From ccf506a68b67857dbf1193a578df0d176c022dde Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sun, 21 Jun 2020 18:45:59 -0600 Subject: [PATCH 01/18] Callback function is now an optional parameter across the various action creation functions, because some times actions are created expressely to be merged into another action to create a 2 action trigger, and thus the standalone action gets discarded. Entering a callback parameter just for the sake of requiring it is not very nice or clean. --- code/REDALERT/MapScript.cpp | 423 +++++++++++++++++++++++++++--------- 1 file changed, 324 insertions(+), 99 deletions(-) diff --git a/code/REDALERT/MapScript.cpp b/code/REDALERT/MapScript.cpp index 4c74838..268528f 100644 --- a/code/REDALERT/MapScript.cpp +++ b/code/REDALERT/MapScript.cpp @@ -1,6 +1,3 @@ -// MapScript.cpp -// - #include "FUNCTION.H" #include "MapScript.h" @@ -1069,7 +1066,13 @@ int houseType = lua_tointeger(L, 1); int in_x = lua_tointeger(L, 2); int in_y = lua_tointeger(L, 3); - const char* in_function = lua_tostring(L, 4); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 4) { + in_function = (char*)lua_tostring(L, 4); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1087,8 +1090,10 @@ // Create instance of trigger type TriggerClass * this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } // Get the cell and set the trigger this_trigger->Cell = XY_Cell(in_x, in_y); @@ -1126,7 +1131,13 @@ int objectID = lua_tointeger(L, 1); int houseType = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } BuildingClass* this_building = Buildings.Ptr(objectID); @@ -1146,8 +1157,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } // Set the building this_building->Trigger = this_trigger; @@ -1184,7 +1197,13 @@ static int Script_ObjectDiscoveryCallback(lua_State* L) { int objectID = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (objectID >= 0 ) { @@ -1201,8 +1220,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } Script_SetObjectTrigger(objectID, this_trigger); @@ -1238,7 +1259,13 @@ static int Script_HouseDiscoveredCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1258,8 +1285,10 @@ TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); @@ -1293,7 +1322,13 @@ static int Script_ObjectAttackedCallback(lua_State* L) { int objectID = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // An object must be specified if (objectID >= 0) { @@ -1310,8 +1345,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } Script_SetObjectTrigger(objectID, this_trigger); @@ -1345,7 +1382,13 @@ static int Script_ObjectDestroyedCallback(lua_State* L) { int objectID = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // An object must be specified if (objectID >= 0) { @@ -1362,8 +1405,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } Script_SetObjectTrigger(objectID, this_trigger); @@ -1399,8 +1444,12 @@ *=============================================================================================*/ static int Script_AnyEventCallback(lua_State* L) { - const char* in_function = lua_tostring(L, 1); + char* in_function = ""; + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -1414,8 +1463,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -1444,7 +1495,13 @@ static int Script_AllUnitsDestroyedCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1462,11 +1519,12 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - - HouseTriggers[(HousesType)houseType].Add(this_trigger); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + HouseTriggers[(HousesType)houseType].Add(this_trigger); // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1498,7 +1556,13 @@ static int Script_AllBuildingsDestroyedCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1516,12 +1580,13 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); - // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1552,7 +1617,13 @@ static int Script_AllDestroyedCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1570,12 +1641,13 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); - // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1608,7 +1680,13 @@ int houseType = lua_tointeger(L, 1); int in_credits = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1626,12 +1704,13 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); - // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1662,7 +1741,13 @@ static int Script_TimeReachedCallback(lua_State* L) { int in_time = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -1677,8 +1762,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -1705,7 +1792,12 @@ *=============================================================================================*/ static int Script_MissionTimerCallback(lua_State* L) { - const char* in_function = lua_tostring(L, 1); + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -1719,8 +1811,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -1751,7 +1845,13 @@ int houseType = lua_tointeger(L, 1); int in_number = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1769,8 +1869,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); @@ -1807,7 +1909,13 @@ int houseType = lua_tointeger(L, 1); int in_number = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1825,8 +1933,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); @@ -1860,7 +1970,13 @@ static int Script_NoFactoriesCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1878,8 +1994,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); @@ -1914,8 +2032,14 @@ static int Script_CivilianEscapeCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); - + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + if (houseType >= 0 && houseType < HouseTypes.Count()) { // Create trigger type @@ -1931,8 +2055,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -1968,7 +2094,13 @@ int houseType = lua_tointeger(L, 1); int objectType = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { @@ -1985,8 +2117,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[houseType].Add(this_trigger); @@ -2025,7 +2159,13 @@ int houseType = lua_tointeger(L, 1); int objectType = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2042,8 +2182,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[houseType].Add(this_trigger); @@ -2082,7 +2224,13 @@ int houseType = lua_tointeger(L, 1); int objectType = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2099,8 +2247,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[houseType].Add(this_trigger); @@ -2139,7 +2289,13 @@ int houseType = lua_tointeger(L, 1); int objectType = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2156,8 +2312,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[houseType].Add(this_trigger); @@ -2192,8 +2350,13 @@ static int Script_TeamLeavesMapCallback(lua_State* L) { int in_team = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } TeamTypeClass* this_team = TeamTypes.Ptr(in_team); if (this_team != NULL) { @@ -2213,8 +2376,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -2257,7 +2422,12 @@ int in_x = lua_tointeger(L, 2); int in_y = lua_tointeger(L, 3); - const char* in_function = lua_tostring(L, 4); + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 4) { + in_function = (char*)lua_tostring(L, 4); + } if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2274,8 +2444,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } // Get the cell and set the trigger this_trigger->Cell = XY_Cell(in_x, in_y); @@ -2318,7 +2490,12 @@ int houseType = lua_tointeger(L, 1); int in_x = lua_tointeger(L,2); - const char* in_function = lua_tostring(L, 3); + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2335,8 +2512,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } // Find a free spot and set the trigger int addY = 0; @@ -2385,7 +2564,12 @@ int houseType = lua_tointeger(L, 1); int in_y = lua_tointeger(L, 2); - const char* in_function = lua_tostring(L, 3); + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2402,8 +2586,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } // Find a free spot and set the trigger int addX = 0; @@ -2447,7 +2633,13 @@ static int Script_GlobalSetCallback(lua_State* L) { int in_global = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -2462,8 +2654,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -2491,7 +2685,13 @@ static int Script_GlobalClearedCallback(lua_State* L) { int in_global = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -2506,8 +2706,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -2535,7 +2737,13 @@ static int Script_LowPowerCallback(lua_State* L) { int houseType = lua_tointeger(L, 1); - const char* in_function = lua_tostring(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } // A house must be specified if (houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2555,8 +2763,10 @@ TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); @@ -2588,7 +2798,12 @@ *=============================================================================================*/ static int Script_AllBridgesDestroyedCallback(lua_State* L) { - const char* in_function = lua_tostring(L, 1); + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -2602,8 +2817,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } LogicTriggers.Add(this_trigger); @@ -2634,7 +2851,13 @@ int buildingType = lua_tointeger(L, 1); int houseType = lua_tointeger(L,2); - const char* in_function = lua_tostring(L,3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } // A house must be specified if (buildingType>=0 && houseType >= 0 && houseType < HouseTypes.Count()) { @@ -2651,8 +2874,10 @@ // Create instance of trigger type TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } HouseTriggers[(HousesType)houseType].Add(this_trigger); From 136b5e2c700680d3aee05d4f2827342ae3de37a5 Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Tue, 23 Jun 2020 23:40:53 +0200 Subject: [PATCH 02/18] Start with EventQueue --- code/CMakeLists.txt | 2 + code/REDALERT/EXTERNS.H | 1 + code/REDALERT/FUNCTION.H | 1 + code/REDALERT/GADGET.CPP | 91 ++++----------------- code/REDALERT/GLOBALS.CPP | 1 + code/REDALERT/KEY.CPP | 7 +- code/REDALERT/KEY.H | 1 + code/REDALERT/SCENARIO.CPP | 2 + code/REDALERT/io/EventQueue.cpp | 140 ++++++++++++++++++++++++++++++++ code/REDALERT/io/EventQueue.h | 92 +++++++++++++++++++++ code/REDALERT/io/UserInput.cpp | 73 ++++++++++++++++- code/REDALERT/io/UserInput.h | 43 ++++++---- 12 files changed, 353 insertions(+), 101 deletions(-) create mode 100644 code/REDALERT/io/EventQueue.cpp create mode 100644 code/REDALERT/io/EventQueue.h diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index ee219fd..e470dad 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -11,6 +11,8 @@ set(CMAKE_VERBOSE_MAKEFILE ON) set(src_io ./REDALERT/io/UserInput.cpp ./REDALERT/io/UserInput.h + ./REDALERT/io/EventQueue.h + ./REDALERT/io/EventQueue.cpp ) set(src_win32lib diff --git a/code/REDALERT/EXTERNS.H b/code/REDALERT/EXTERNS.H index 4b91e44..2a26b6b 100644 --- a/code/REDALERT/EXTERNS.H +++ b/code/REDALERT/EXTERNS.H @@ -161,6 +161,7 @@ extern PKey SlowKey; extern RulesClass Rule; extern KeyboardClass * Keyboard; extern UserInputClass UserInput; +extern EventQueueClass EventQueue; extern RandomStraw CryptRandom; extern RandomClass NonCriticalRandomNumber; extern CarryoverClass * Carryover; diff --git a/code/REDALERT/FUNCTION.H b/code/REDALERT/FUNCTION.H index b226903..e00d0d6 100644 --- a/code/REDALERT/FUNCTION.H +++ b/code/REDALERT/FUNCTION.H @@ -201,6 +201,7 @@ struct NoInitClass { #include "key.h" #include "io/UserInput.h" +#include "io/EventQueue.h" #include #include "mpu.h" diff --git a/code/REDALERT/GADGET.CPP b/code/REDALERT/GADGET.CPP index adfaabd..eb2ce84 100644 --- a/code/REDALERT/GADGET.CPP +++ b/code/REDALERT/GADGET.CPP @@ -471,7 +471,9 @@ void GadgetClass::Draw_All(bool forced) *=========================================================================*/ KeyNumType GadgetClass::Input(void) { - KeyNumType key; + EventMessage* eventMessage = NULL; + KeyNumType key = KN_NONE; + int flags = 0; int forced = false; /* @@ -488,78 +490,13 @@ KeyNumType GadgetClass::Input(void) /* ** Fetch any pending keyboard input. */ - key = Keyboard->Check(); - if (key != 0) { - key = Keyboard->Get(); + UserInput.Process_Input(); // key, flags); + + if ( EventQueue.Check() ) { + eventMessage = EventQueue.Get(); + eventMessage->Key_Flags( key, flags ); } - /* - ** For mouse button clicks, the mouse position is actually held in the MouseQ... - ** globals rather than their normal Mouse... globals. This is because we need to - ** know the position of the mouse at the exact instant when the click occurred - ** rather the the mouse position at the time we get around to this function. - */ -// jmarshall - int flags = g_globalKeyFlags; - UserInput.Process_Input( g_globalKeyNumType, flags ); - -// jmarshall end - - /* - ** Set the mouse button state flags. These will be passed to the individual - ** buttons so that they can determine what action to perform (if any). - */ - //if (key) { - // if (key == KN_LMOUSE) { - // flags |= LEFTPRESS; - // } - // if (key == KN_RMOUSE) { - // flags |= RIGHTPRESS; - // } - // if (key == (KN_LMOUSE | KN_RLSE_BIT)) { - // flags |= LEFTRELEASE; - // } - // if (key == (KN_RMOUSE | KN_RLSE_BIT)) { - // flags |= RIGHTRELEASE; - // } - //} - - /* - ** If the mouse wasn't responsible for this key code, then it must be from - ** the keyboard. Flag this fact. - */ - if (key && !flags) { - flags |= KEYBOARD; - } - - /* - ** Mouse button up or down action is ignored if there is a keyboard event. This - ** allows keyboard events to fall through normally even if the mouse is over a - ** gadget that is flagged for LEFTUP or RIGHTUP. - */ -// jmarshall - //if (!key) { - // - // /* - // ** Check for the mouse being held down. We can't use the normal input system - // ** for this, so we must examine the actual current state of the mouse - // ** buttons. As a side note, if we determine that the mouse button isn't being - // ** held down, then we automatically know that it must be up -- set the flag - // ** accordingly. - // */ - // if (Keyboard->Down(KN_LMOUSE)) { - // flags |= LEFTHELD; - // } else { - // flags |= LEFTUP; - // } - // if (Keyboard->Down(KN_RMOUSE)) { - // flags |= RIGHTHELD; - // } else { - // flags |= RIGHTUP; - // } - //} -// jmarshall end - /* ** If "sticky" processing is active, then only process the stuck gadget. */ @@ -567,18 +504,21 @@ KeyNumType GadgetClass::Input(void) StuckOn->Draw_Me(false); GadgetClass * oldstuck = StuckOn; StuckOn->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); - if (StuckOn) { + + if (StuckOn) + { StuckOn->Draw_Me(false); } else { oldstuck->Draw_Me(false); } - } else { - + } + else + { /* ** If there is a gadget that has the keyboard focus, then route all keyboard ** events to it. */ - if (Focused && (flags & KEYBOARD)) { + if ( Focused && eventMessage != NULL && eventMessage->EventType == EventMessageType::EVENT_KEYBOARD ) { Focused->Draw_Me(false); Focused->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); if (Focused) { @@ -622,6 +562,7 @@ KeyNumType GadgetClass::Input(void) } } } + return(key); } diff --git a/code/REDALERT/GLOBALS.CPP b/code/REDALERT/GLOBALS.CPP index 2e9c522..83acebf 100644 --- a/code/REDALERT/GLOBALS.CPP +++ b/code/REDALERT/GLOBALS.CPP @@ -257,6 +257,7 @@ RulesClass Rule; ** user input class pointer. */ UserInputClass UserInput; +EventQueueClass EventQueue; /*************************************************************************** ** All keyboard input is routed through the object pointed to by this diff --git a/code/REDALERT/KEY.CPP b/code/REDALERT/KEY.CPP index 5424cc2..9ff74b4 100644 --- a/code/REDALERT/KEY.CPP +++ b/code/REDALERT/KEY.CPP @@ -103,12 +103,7 @@ unsigned short WWKeyboardClass::Buff_Get(void) while (!Check()) {} // wait for key in buffer unsigned short temp = Fetch_Element(); - /* - if (Is_Mouse_Key(temp)) { - MouseQX = Fetch_Element(); - MouseQY = Fetch_Element(); - } - */ + return(temp); } diff --git a/code/REDALERT/KEY.H b/code/REDALERT/KEY.H index 3a09594..194d86e 100644 --- a/code/REDALERT/KEY.H +++ b/code/REDALERT/KEY.H @@ -638,6 +638,7 @@ typedef enum KeyNumType { KN_ALT_BIT = WWKEY_ALT_BIT, KN_RLSE_BIT = WWKEY_RLS_BIT, KN_BUTTON = WWKEY_BTN_BIT, + KN_CAPSLOCK_BIT = WWKEY_SHIFT_BIT, } KeyNumType; diff --git a/code/REDALERT/SCENARIO.CPP b/code/REDALERT/SCENARIO.CPP index 79d37cd..f6943e9 100644 --- a/code/REDALERT/SCENARIO.CPP +++ b/code/REDALERT/SCENARIO.CPP @@ -1056,6 +1056,8 @@ void Do_Win(void) } Keyboard->Clear(); + + UserInput.Process_Input(); } Scen.CarryOverMoney = PlayerPtr->Credits; diff --git a/code/REDALERT/io/EventQueue.cpp b/code/REDALERT/io/EventQueue.cpp new file mode 100644 index 0000000..94919a1 --- /dev/null +++ b/code/REDALERT/io/EventQueue.cpp @@ -0,0 +1,140 @@ +#include "../FUNCTION.H" +#include "EventQueue.h" + +/* + Clears the event queue +*/ +void EventQueueClass::Clear() +{ + while (this->eventQueue.size() > 0) this->eventQueue.pop(); +} + +/* + Inserts a new KeyNumType event into the FIFO buffer. KeyNumType represents a key press or mouse button + Input: KeyNumType + optional flag bits for SHIFT/CTRL/etc. cast as unsigned short +*/ +void EventQueueClass::Put(EventMessage* input) +{ + this->eventQueue.push(input); +} + +/* + Check if the buffer contains an element +*/ +bool EventQueueClass::Check() const +{ + return !this->Is_Buffer_Empty(); +} + +/* + Get the first (unprocessed) element in the event queue +*/ +EventMessage* EventQueueClass::Get() +{ + // wait for buffer + while( !this->Check() ) { } + + EventMessage* returnVal = this->eventQueue.front(); + this->eventQueue.pop(); + + return returnVal; +} + +/* + Returns ASCII value of the key in the buffer +*/ +char EventQueueClass::Get_ASCII() +{ + // get the last key from the buffer + KeyboardEventMessage* keyMessage = dynamic_cast( this->Get() ); + unsigned short key = keyMessage->Key; + + // check if it is a keyboard + if (key == KN_LMOUSE || key == KN_RMOUSE) return 0x0; + + // convert to ascii + char ascii; + unsigned short key_raw = (UINT)(key & 0xFF); + + // find the SDL keycode + auto sdl_Key = sdl_keyMapping_reverse.find( (KeyNumType)key_raw ); + if (sdl_Key != sdl_keyMapping_reverse.end()) + { + // convert to ascii + ascii = SDL_GetKeyName( sdl_Key->first )[0]; + + // convert to uppercase + if (keyMessage->Shift || keyMessage->CapsLock) ascii = toupper(ascii); + } + + return 0x0; +} + +KeyboardEventMessage::KeyboardEventMessage(KeyNumType key, bool shift, bool alt, bool control, bool capsLock) { + this->EventType = EventMessageType::EVENT_KEYBOARD; + this->Key = key; + this->Shift = shift; + this->Alt = alt; + this->Control = control; + this->CapsLock = capsLock; + + this->Flags = 0; + if (this->Shift) this->Flags |= KN_SHIFT_BIT; + if (this->Alt) this->Flags |= KN_ALT_BIT; + if (this->Control) this->Flags |= KN_CTRL_BIT; +} + +void KeyboardEventMessage::Key_Flags(KeyNumType& key, int& flags) +{ + flags = this->Flags; + flags |= GadgetClass::KEYBOARD; + + key = (KeyNumType)this->Key; +} + +MouseEventMessage::MouseEventMessage(int x, int y, unsigned short button, UserInputClass::MouseButtonState buttonState) +{ + this->EventType = EventMessageType::EVENT_MOUSE; + this->x = x; + this->y = y; + + this->Button = button; + this->ButtonState = buttonState; + + this->Flags = 0; +} + +void MouseEventMessage::Key_Flags(KeyNumType& key, int& flags) +{ + key = (KeyNumType)this->Button; + + if (key == KN_LMOUSE) { + switch (this->ButtonState) + { + case UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::LEFTPRESS; + break; + case UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::LEFTRELEASE; + break; + case UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::LEFTHELD; + break; + } + } + + if (key == KN_RMOUSE) { + switch (this->ButtonState) + { + case UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::RIGHTPRESS; + break; + case UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::RIGHTRELEASE; + break; + case UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::RIGHTHELD; + break; + } + } +} \ No newline at end of file diff --git a/code/REDALERT/io/EventQueue.h b/code/REDALERT/io/EventQueue.h new file mode 100644 index 0000000..0767b4a --- /dev/null +++ b/code/REDALERT/io/EventQueue.h @@ -0,0 +1,92 @@ +#pragma once + +/* +* WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer* +* WWKeyboardClass::Check -- Checks to see if a key is in the buffer* +* WWKeyboardClass::Down -- Checks to see if the specified key is being held down.* +* WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer.* +* WWKeyboardClass::Get -- Logic to get a metakey from the buffer* +* WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboar* +* WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer.* +* WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation.* +* WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keybo* +* ----------------------------------------------*/ + +/** +* EVENT QUEUE class +* Supports pumping of event messages using a FIFO buffer. +*/ + +extern UserInputClass UserInput; + +/* + Event type definitions +*/ +typedef enum { + EVENT_KEYBOARD, + EVENT_MOUSE +} EventMessageType; + +/* + Event types +*/ +class EventMessage { + public: + EventMessageType EventType; + + virtual void Key_Flags(KeyNumType& key, int& flags) = 0; + virtual ~EventMessage() = default; +}; + +class KeyboardEventMessage : public EventMessage { + public: + KeyNumType Key; + bool Shift; + bool Alt; + bool Control; + bool CapsLock; + + // for cascading functions + unsigned short Flags; + + KeyboardEventMessage(KeyNumType key, bool shift = false, bool alt = false, bool control = false, bool capsLock = false); + virtual void Key_Flags(KeyNumType& key, int& flags); + + virtual ~KeyboardEventMessage() = default; +}; + +class MouseEventMessage : public EventMessage { + public: + int x; + int y; + unsigned short Button; + UserInputClass::MouseButtonState ButtonState; + + // for cascading functions + unsigned short Flags; + + MouseEventMessage(int x, int y, unsigned short button = KN_LMOUSE, UserInputClass::MouseButtonState buttonState = UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN); + virtual void Key_Flags(KeyNumType& key, int& flags); + + virtual ~MouseEventMessage() = default; +}; + +class EventQueueClass { + public: + int Get_Mouse_X() const { return UserInput.Mouse.X; } + int Get_Mouse_Y() const { return UserInput.Mouse.Y; } + + void Clear(); + void Put(EventMessage* input); + bool Check() const; + EventMessage* Get(); + char EventQueueClass::Get_ASCII(); + + private: + const int MaxCapacity = 100; + + std::queue eventQueue; + + bool Is_Buffer_Full() const { return this->eventQueue.size() == MaxCapacity; } + bool Is_Buffer_Empty() const { return this->eventQueue.size() == 0; } +}; diff --git a/code/REDALERT/io/UserInput.cpp b/code/REDALERT/io/UserInput.cpp index 7c602f3..818f64d 100644 --- a/code/REDALERT/io/UserInput.cpp +++ b/code/REDALERT/io/UserInput.cpp @@ -3,10 +3,51 @@ extern KeyboardClass * Keyboard; +std::map sdl_keyMapping_reverse; + +UserInputClass::UserInputClass() +{ + LastAction = InputAction::INPUT_ACTION_NONE; + + // generate reverse keymapping + for ( auto it = sdl_keyMapping.begin(); it != sdl_keyMapping.end(); it++ ) + { + sdl_keyMapping_reverse[it->second] = it->first; + } +} + +bool UserInputClass::KeyboardState::Key_Down(KeyNumType key) const +{ + auto sdl_Key = sdl_keyMapping_reverse.find( key ); + if ( sdl_Key != sdl_keyMapping_reverse.end() ) + { + const Uint8* keyState = SDL_GetKeyboardState(NULL); + return keyState[ sdl_Key->second ]; + } + return false; +} + +bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const +{ + // get mouse state, we are only interested in the buttons + const Uint32 MouseState = SDL_GetMouseState(NULL,NULL); + + switch (key) + { + case KN_LMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_LEFT); + break; + case KN_RMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_RIGHT); + break; + } +} + void UserInputClass::Process_Input(KeyNumType& key, int& flags) { bool leftMouseProcessed = false; - SDL_GetMouseState(&Mouse.X, &Mouse.Y); + + const Uint32 MouseState = SDL_GetMouseState(&Mouse.X, &Mouse.Y); Keyboard->MouseQX = Mouse.X; Keyboard->MouseQY = Mouse.Y; @@ -42,8 +83,12 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) break; + /* Mouse is moving */ case SDL_MOUSEMOTION: - flags = GadgetClass::LEFTUP; + if ( !( MouseState & SDL_BUTTON(SDL_BUTTON_LEFT) ) ) + { + flags = GadgetClass::LEFTUP; + } break; case SDL_MOUSEBUTTONDOWN: @@ -52,15 +97,23 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) key = KN_LMOUSE; flags = GadgetClass::LEFTPRESS; + + EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE) ); Keyboard->Put_Element(KN_LMOUSE); leftMouseProcessed = true; numFramesLMouse++; + + // after first left-mouse down, abort handling any other SDL events + // because game might first need to switch into rubber band mode + if (numFramesLMouse == 1) return; } else if (event.button.button == SDL_BUTTON_RIGHT) { Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_DOWN; + EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_RMOUSE) ); + Keyboard->Put_Element(KN_RMOUSE); key = KN_RMOUSE; @@ -74,12 +127,16 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) if (event.button.button == SDL_BUTTON_LEFT) { Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_RELEASE; + EventQueue.Put(new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE)); + flags = GadgetClass::LEFTRELEASE; numFramesLMouse = 0; } else if (event.button.button == SDL_BUTTON_RIGHT) { Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_RELEASE; + EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_RMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE) ); + flags = GadgetClass::RIGHTRELEASE; numFramesLMouse = 0; } @@ -114,12 +171,20 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) keyFlags |= KN_CTRL_BIT; KeyB.Control = true; } + if (state[SDL_SCANCODE_CAPSLOCK]) { + keyFlags |= KN_CAPSLOCK_BIT; + KeyB.CapsLock = true; + } // handle match between SDL and internals (non-text keys) auto element = sdl_keyMapping.find((SDL_KeyCode)event.key.keysym.sym); if (element != sdl_keyMapping.end()) { - Keyboard->Put_Element(element->second | keyFlags); + //EventQueue.Put( element->second | keyFlags ); + EventQueue.Put( new KeyboardEventMessage( (KeyNumType)element->second, KeyB.Shift, KeyB.Alt, KeyB.Control, KeyB.CapsLock ) ); + + Keyboard->Put_Element( element->second | keyFlags); + KeyB.ASCII = element->second; } @@ -143,5 +208,7 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) { Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_HOLD; flags = GadgetClass::LEFTHELD; + + EventQueue.Put(new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD)); } } \ No newline at end of file diff --git a/code/REDALERT/io/UserInput.h b/code/REDALERT/io/UserInput.h index 2ef72f3..41f37c9 100644 --- a/code/REDALERT/io/UserInput.h +++ b/code/REDALERT/io/UserInput.h @@ -1,9 +1,9 @@ -#ifndef USERINPUT_H -#define USERINPUT_H +#pragma once #include #include #include +#include #include "examples/imgui_impl_sdl.h" @@ -87,6 +87,11 @@ std::map const sdl_keyMapping = { { SDLK_LEFT, KN_LEFT }, { SDLK_RIGHT, KN_RIGHT }, }; +extern std::map sdl_keyMapping_reverse; + +const int USERINPUT_SHIFT_BIT = 0x100; +const int USERINPUT_CTRL_BIT = 0x200; +const int USERINPUT_ALT_BIT = 0x400; class UserInputClass { @@ -104,24 +109,28 @@ class UserInputClass INPUT_ACTION_MOUSE, } InputAction; - void UserInputClass::Process_Input(KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags); + UserInputClass(); + void UserInputClass::Process_Input( KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags ); InputAction LastAction; class KeyboardState { - public: - SDL_KeyCode LastKey; - char ASCII; + public: + SDL_KeyCode LastKey; + char ASCII; - bool Shift; - bool Control; - bool Alt; + bool Shift; + bool Control; + bool Alt; + bool CapsLock; - void ResetState() { - this->Shift = false; - this->Control = false; - this->Alt = false; - } + bool Key_Down(KeyNumType key) const; + + void ResetState() { + this->Shift = false; + this->Control = false; + this->Alt = false; + } }; KeyboardState KeyB; @@ -135,6 +144,8 @@ class UserInputClass MouseButtonState Button_Middle; MouseButtonState Button_Right; + bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const; + void ResetState() { this->Button_Left = MouseButtonState::MOUSE_BUTTON_UP; this->Button_Middle = MouseButtonState::MOUSE_BUTTON_UP; @@ -145,6 +156,4 @@ class UserInputClass MouseState Mouse; private: int numFramesLMouse = 0; -}; - -#endif \ No newline at end of file +}; \ No newline at end of file From 0ef858bf86651112cc7fa00fa1d7f34b19a68494 Mon Sep 17 00:00:00 2001 From: nev6502 Date: Fri, 26 Jun 2020 00:19:20 -0600 Subject: [PATCH 03/18] More scripting functions (ability to various change object / objectType attributes) and globals for use with them --- code/REDALERT/MapScript.cpp | 424 +++++++++++++++++++++++++++++++++++- code/REDALERT/MapScript.h | 31 +++ scripts/_Global.script | 82 +++++++ 3 files changed, 534 insertions(+), 3 deletions(-) diff --git a/code/REDALERT/MapScript.cpp b/code/REDALERT/MapScript.cpp index 268528f..fa5feb8 100644 --- a/code/REDALERT/MapScript.cpp +++ b/code/REDALERT/MapScript.cpp @@ -969,7 +969,7 @@ } /*********************************************************************************************** - * Script_DesignatePreferredTarget - Gives specified house a repeating special weapon * + * Script_DesignatePreferredTarget - Gives specified house a prefferable type of target * * * * SCRIPT INPUT: houseType (int) - The player (house) index to set targetting for (or -1) * * targetType (int) - The target type index to set to * @@ -3271,7 +3271,415 @@ return -1; } + +/********************************************************************************************** +* Modification Utility Functions * +*=============================================================================================*/ + /********************************************************************************************** + * Script_SetObjectAttribute - Sets attributes for specific objects * + * * + * SCRIPT INPUT: objectID (int) - The ID of the object * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetObjectAttribute(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + + + // Get the appropriate technotype + if (this_object != NULL) { + + + + + switch ((MapScriptAttributeType)attribute_type) { + case ATTRIBUTE_STRENGTH: { + + int attribute_value = lua_tointeger(L, 3); + this_object->Strength = attribute_value; + + }break; + default: { + + TechnoTypeClass* this_type_class; + + switch (this_object->RTTI) { + case RTTI_BUILDING: + + this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_UNIT: + + this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_INFANTRY: + + this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_VESSEL: + + this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_AIRCRAFT: + + this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); + + break; + } + + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + }break; + + } + + } + + + return -1; + } + + /********************************************************************************************** + * Script_SetBuildingTypeAttribute - Sets attributes for a building types * + * * + * SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetBuildingTypeAttribute(lua_State* L) { + + int buildingTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_SetVehicleTypeAttribute - Sets attributes for vehicle types * + * * + * SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetVehicleTypeAttribute(lua_State* L) { + + int vehicleTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_SetInfantryTypeAttribute - Sets attributes for infantry types * + * * + * SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetInfantryTypeAttribute(lua_State* L) { + + int infantryTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_SetVesselTypeAttribute - Sets attributes for vessel types * + * * + * SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetVesselTypeAttribute(lua_State* L) { + + int vesselTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_SetAircraftTypeAttribute - Sets attributes for aircraft types * + * * + * SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetAircraftTypeAttribute(lua_State* L) { + + int aircraftTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_SetObjectTypeAttribute - Sets attributes for specific object types globally * + * * + * Used internally by MapScript to retrieve input and set attributes for TechnoTypeClass * + * * + * SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * + * attribute_type (MapScriptAttributeType) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptAttributeType attribute_type) { + + switch (attribute_type) { + case ATTRIBUTE_MAX_STRENGTH: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->MaxStrength = attribute_value; + + }break; + + case ATTRIBUTE_COST: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->Cost = attribute_value; + + }break; + case ATTRIBUTE_SIGHT_RANGE: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->SightRange = attribute_value; + + }break; + case ATTRIBUTE_TECH_LEVEL: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->Level = attribute_value; + + }break; + case ATTRIBUTE_ARMOR: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->Armor = (ArmorType)attribute_value; + + }break; + case ATTRIBUTE_PRIMARY_WEAPON: { + + int attribute_value = lua_tointeger(L, 3); + + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->PrimaryWeapon = this_weapon; + } + + }break; + case ATTRIBUTE_SECONDARY_WEAPON: { + + int attribute_value = lua_tointeger(L, 3); + + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->SecondaryWeapon = this_weapon; + } + + }break; + case ATTRIBUTE_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->Speed = (SpeedType)attribute_value; + + }break; + case ATTRIBUTE_MAX_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->MaxSpeed = (MPHType)attribute_value; + + }break; + case ATTRIBUTE_MAX_AMMO: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->MaxAmmo = attribute_value; + + }break; + case ATTRIBUTE_ROTATION_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->ROT = attribute_value; + + }break; + case ATTRIBUTE_IS_CRUSHABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsCrushable = attribute_value; + + }break; + case ATTRIBUTE_IS_STEALTHY: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsStealthy = attribute_value; + + }break; + case ATTRIBUTE_IS_SELECTABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsSelectable = attribute_value; + + }break; + case ATTRIBUTE_IS_LEGAL_TARGET: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsLegalTarget = attribute_value; + + }break; + case ATTRIBUTE_IS_REPAIRABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsRepairable = attribute_value; + + }break; + case ATTRIBUTE_IS_SELF_HEALING: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsSelfHealing = attribute_value; + + }break; + case ATTRIBUTE_IS_EXPLODING: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsExploding = attribute_value; + + }break; + case ATTRIBUTE_HAS_CREW: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + this_type_class->IsCrew = attribute_value; + + }break; + case ATTRIBUTE_MAX_PASSENGERS: { + + int attribute_value = lua_tointeger(L, 3); + + this_type_class->MaxPassengers = attribute_value; + + }break; + + } + + + return -1; + } /********************************************************************************************** * Mission Utility Functions * @@ -4273,7 +4681,7 @@ } return -1; - } + } /*********************************************************************************************** * MapScript::CallFunction - Calls a given function within the current script * @@ -4472,11 +4880,21 @@ lua_register(L, "SetTriggerCallback", Script_SetTriggerCallback); // Initiates a given [callback] on an existing [trigger] // TODO: Make this work via name OR ID lua_register(L, "TriggerAddCell", Script_TriggerAddCell); // Initiates a given [callback] on an existing [trigger] - lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] lua_register(L, "MergeTriggers", Script_MergeTriggers); // Combines the events of two triggers (for multi-event triggers :) lua_register(L, "GetTriggerByName", Script_GetTriggerByName); // Gets the ID of the trigger with a given name lua_register(L, "TriggerSetPersistence", Script_TriggerSetPersistence); // Does a trigger go on and on or just run once? + /********************************************************************************************** + * Object Functions * + *=============================================================================================*/ + lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] + lua_register(L, "SetObjectAttribute", Script_SetObjectAttribute); // Sets an object's attributes (strength, etc.) + lua_register(L, "SetBuildingTypeAttribute", Script_SetBuildingTypeAttribute); // Sets the attribute of a Building type + lua_register(L, "SetVehicleTypeAttribute", Script_SetVehicleTypeAttribute); // Sets the attribute of a Vehicle type + lua_register(L, "SetInfantryTypeAttribute", Script_SetInfantryTypeAttribute); // Sets the attribute of an Infantry type + lua_register(L, "SetVesselTypeAttribute", Script_SetVesselTypeAttribute); // Sets the attribute of a Vessel type + lua_register(L, "SetAircraftTypeAttribute", Script_SetAircraftTypeAttribute); // Sets the attribute of an Aircraft type + /********************************************************************************************** * Mission Utility Functions * *=============================================================================================*/ diff --git a/code/REDALERT/MapScript.h b/code/REDALERT/MapScript.h index b3bf2d4..532a61b 100644 --- a/code/REDALERT/MapScript.h +++ b/code/REDALERT/MapScript.h @@ -43,6 +43,34 @@ private: lua_State* L; }; + + +// Enums +typedef enum MapScriptAttributeType : char { + ATTRIBUTE_NONE=0, + ATTRIBUTE_STRENGTH, + ATTRIBUTE_MAX_STRENGTH, + ATTRIBUTE_COST, + ATTRIBUTE_SIGHT_RANGE, + ATTRIBUTE_TECH_LEVEL, + ATTRIBUTE_ARMOR, + ATTRIBUTE_PRIMARY_WEAPON, + ATTRIBUTE_SECONDARY_WEAPON, + ATTRIBUTE_SPEED, + ATTRIBUTE_MAX_SPEED, + ATTRIBUTE_MAX_AMMO, + ATTRIBUTE_ROTATION_SPEED, + ATTRIBUTE_IS_CRUSHABLE, + ATTRIBUTE_IS_STEALTHY, + ATTRIBUTE_IS_SELECTABLE, + ATTRIBUTE_IS_LEGAL_TARGET, + ATTRIBUTE_IS_REPAIRABLE, + ATTRIBUTE_IS_SELF_HEALING, + ATTRIBUTE_IS_EXPLODING, + ATTRIBUTE_HAS_CREW, + ATTRIBUTE_MAX_PASSENGERS +} MapScriptAttributeType; + // Non-Class-Functions bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger); ObjectClass* Script_GetCacheObject(int input_object_id); @@ -53,4 +81,7 @@ int Script_AircraftIndexFromID(int input_object_id); int Script_InfantryIndexFromID(int input_object_id); int Script_VesselIndexFromID(int input_object_id); +static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptAttributeType attribute_type); + + #endif \ No newline at end of file diff --git a/scripts/_Global.script b/scripts/_Global.script index e266642..230c731 100644 --- a/scripts/_Global.script +++ b/scripts/_Global.script @@ -194,3 +194,85 @@ TRIGGER_VOLATILE=0 TRIGGER_SEMI_PERSISTENT=1 TRIGGER_PERSISTENT=2 + + -- ********************************************************* + -- * * + -- * Attribite Types * + -- * * + -- ********************************************************* + ATTRIBUTE_NONE=0 + ATTRIBUTE_STRENGTH=1 -- Object attribute + ATTRIBUTE_MAX_STRENGTH=2 -- Definition attribute + ATTRIBUTE_COST=3 -- Definition attribute + ATTRIBUTE_SIGHT_RANGE=4 -- Definition attribute + ATTRIBUTE_TECH_LEVEL=5 -- Definition attribute + ATTRIBUTE_ARMOR=6 + ATTRIBUTE_PRIMARY_WEAPON=7 + ATTRIBUTE_SECONDARY_WEAPON=8 + ATTRIBUTE_SPEED=9 + ATTRIBUTE_MAX_SPEED=10 + ATTRIBUTE_MAX_AMMO=11 + ATTRIBUTE_ROTATION_SPEED=12 + ATTRIBUTE_IS_CRUSHABLE=13 + ATTRIBUTE_IS_STEALTHY=14 + ATTRIBUTE_IS_SELECTABLE=15 + ATTRIBUTE_IS_LEGAL_TARGET=16 + ATTRIBUTE_IS_REPAIRABLE=17 + ATTRIBUTE_IS_SELF_HEALING=18 + ATTRIBUTE_IS_EXPLODING=19 + ATTRIBUTE_HAS_CREW=20 + ATTRIBUTE_MAX_PASSENGERS=21 + + -- ********************************************************* + -- * * + -- * Armor Types * + -- * * + -- ********************************************************* + ARMOR_NONE=0 + ARMOR_WOOD=1 + ARMOR_ALUMINUM=2 + ARMOR_STEEL=3 + ARMOR_CONCRETE=4 + + -- ********************************************************* + -- * * + -- * Weapon Types * + -- * * + -- ********************************************************* + WEAPON_COLT45=0 + WEAPON_ACK_ACK=1 + WEAPON_VULCAN=2 + WEAPON_MAVERICK=3 + WEAPON_CAMERA=4 + WEAPON_FIREBALL=5 + WEAPON_RIFLE=6 + WEAPON_CHAIN_GUN=7 + WEAPON_PISTOL=8 + WEAPON_M16=9 + WEAPON_DRAGON=10 + WEAPON_HELLFIRE=11 + WEAPON_GRENADE=12 + WEAPON_75MM=13 + WEAPON_90MM=14 + WEAPON_105MM=15 + WEAPON_120MM=16 + WEAPON_TURRET_GUN=17 + WEAPON_MAMMOTH_TUSK=18 + WEAPON_155MM=19 + WEAPON_M60MG=20 + WEAPON_NAPALM=21 + WEAPON_TESLA_ZAP=22 + WEAPON_NIKE=23 + WEAPON_8INCH=24 + WEAPON_STINGER=25 + WEAPON_TORPEDO=26 + WEAPON_2INCH=27 + WEAPON_DEPTH_CHARGE=28 + WEAPON_PARA_BOMB=29 + WEAPON_DOGJAW=30 + WEAPON_HEAL=31 + WEAPON_SCUD=32 + WEAPON_FLAMER=33 + WEAPON_REDEYE=34 + + \ No newline at end of file From 28f5f86f91985929a1e16762134bc34cb6ff9789 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 26 Jun 2020 15:01:11 -0700 Subject: [PATCH 04/18] Missing file from previous checkin. --- code/redalert/vector.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/redalert/vector.h b/code/redalert/vector.h index 135db04..2cd4ca2 100644 --- a/code/redalert/vector.h +++ b/code/redalert/vector.h @@ -187,6 +187,11 @@ public: Active = active; } + int GetActive() + { + return Active; + } + void Clear_All() { for (int i = FIRST; i < COUNT; ++i) From 37b47a8cfe0718d1cd1042349db66382290bcfe9 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 26 Jun 2020 15:04:01 -0700 Subject: [PATCH 05/18] Missing file from previous checkin. --- code/redalert/vector.h | 368 ++++++++++++++++++++--------------------- 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/code/redalert/vector.h b/code/redalert/vector.h index 2cd4ca2..9085235 100644 --- a/code/redalert/vector.h +++ b/code/redalert/vector.h @@ -52,57 +52,57 @@ #include #include -// ST - 5/8/1029 -//inline void * operator new(size_t , void * pointer) {return(pointer);} -//inline void * operator new[](size_t , void * pointer) {return(pointer);} + // ST - 5/8/1029 + //inline void * operator new(size_t , void * pointer) {return(pointer);} + //inline void * operator new[](size_t , void * pointer) {return(pointer);} -/************************************************************************** -** This is a general purpose vector class. A vector is defined by this -** class, as an array of arbitrary objects where the array can be dynamically -** sized. Because is deals with arbitrary object types, it can handle everything. -** As a result of this, it is not terribly efficient for integral objects (such -** as char or int). It will function correctly, but the copy constructor and -** equality operator could be highly optimized if the integral type were known. -** This efficiency can be implemented by deriving an integral vector template -** from this one in order to supply more efficient routines. -*/ + /************************************************************************** + ** This is a general purpose vector class. A vector is defined by this + ** class, as an array of arbitrary objects where the array can be dynamically + ** sized. Because is deals with arbitrary object types, it can handle everything. + ** As a result of this, it is not terribly efficient for integral objects (such + ** as char or int). It will function correctly, but the copy constructor and + ** equality operator could be highly optimized if the integral type were known. + ** This efficiency can be implemented by deriving an integral vector template + ** from this one in order to supply more efficient routines. + */ template class VectorClass { - public: - VectorClass(NoInitClass const & ) {}; - VectorClass(unsigned size=0, T const * array=0); - VectorClass(VectorClass const &); // Copy constructor. - virtual ~VectorClass(void); +public: + VectorClass(NoInitClass const&) {}; + VectorClass(unsigned size = 0, T const* array = 0); + VectorClass(VectorClass const&); // Copy constructor. + virtual ~VectorClass(void); - T & operator[](unsigned index) {return(Vector[index]);}; - T const & operator[](unsigned index) const {return(Vector[index]);}; - virtual VectorClass & operator =(VectorClass const &); // Assignment operator. - virtual int operator == (VectorClass const &) const; // Equality operator. - virtual int Resize(unsigned newsize, T const * array=0); - virtual void Clear(void); - unsigned Length(void) const {return VectorMax;}; - virtual int ID(T const * ptr); // Pointer based identification. - virtual int ID(T const & ptr); // Value based identification. + T& operator[](unsigned index) { return(Vector[index]); }; + T const& operator[](unsigned index) const { return(Vector[index]); }; + virtual VectorClass& operator =(VectorClass const&); // Assignment operator. + virtual int operator == (VectorClass const&) const; // Equality operator. + virtual int Resize(unsigned newsize, T const* array = 0); + virtual void Clear(void); + unsigned Length(void) const { return VectorMax; }; + virtual int ID(T const* ptr); // Pointer based identification. + virtual int ID(T const& ptr); // Value based identification. - protected: +protected: - /* - ** This is a pointer to the allocated vector array of elements. - */ - T * Vector; + /* + ** This is a pointer to the allocated vector array of elements. + */ + T* Vector; - /* - ** This is the maximum number of elements allowed in this vector. - */ - unsigned VectorMax; + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; - /* - ** Does the vector data pointer refer to memory that this class has manually - ** allocated? If so, then this class is responsible for deleting it. - */ - unsigned IsAllocated:1; + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated : 1; }; @@ -118,56 +118,56 @@ class VectorClass template class DynamicVectorClass : public VectorClass { - public: - DynamicVectorClass(unsigned size=0, T const * array=0); +public: + DynamicVectorClass(unsigned size = 0, T const* array = 0); - // Change maximum size of vector. - virtual int Resize(unsigned newsize, T const * array=0); + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const* array = 0); - // Resets and frees the vector array. - virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + // Resets and frees the vector array. + virtual void Clear(void) { ActiveCount = 0;VectorClass::Clear(); }; - // Fetch number of "allocated" vector objects. - int Count(void) const {return(ActiveCount);}; + // Fetch number of "allocated" vector objects. + int Count(void) const { return(ActiveCount); }; - // Add object to vector (growing as necessary). - int Add(T const & object); - int Add_Head(T const & object); + // Add object to vector (growing as necessary). + int Add(T const& object); + int Add_Head(T const& object); - // Delete object just like this from vector. - int Delete(T const & object); + // Delete object just like this from vector. + int Delete(T const& object); - // Delete object at this vector index. - int Delete(int index); + // Delete object at this vector index. + int Delete(int index); - // Deletes all objects in the vector. - void Delete_All(void) {ActiveCount = 0;}; + // Deletes all objects in the vector. + void Delete_All(void) { ActiveCount = 0; }; - // Set amount that vector grows by. - int Set_Growth_Step(int step) {return(GrowthStep = step);}; + // Set amount that vector grows by. + int Set_Growth_Step(int step) { return(GrowthStep = step); }; - // Fetch current growth step rate. - int Growth_Step(void) {return GrowthStep;}; + // Fetch current growth step rate. + int Growth_Step(void) { return GrowthStep; }; - virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; - virtual int ID(T const & ptr); + virtual int ID(T const* ptr) { return(VectorClass::ID(ptr)); }; + virtual int ID(T const& ptr); - protected: +protected: - /* - ** This is a count of the number of active objects in this - ** vector. The memory array often times is bigger than this - ** value. - */ - int ActiveCount; + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; - /* - ** If there is insufficient room in the vector array for a new - ** object to be added, then the vector will grow by the number - ** of objects specified by this value. This is controlled by - ** the Set_Growth_Step() function. - */ - int GrowthStep; + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; }; @@ -210,22 +210,22 @@ public: return Count(Active); } - int Add(T const & object) + int Add(T const& object) { return Add(Active, object); } - int Add_Head(T const & object) + int Add_Head(T const& object) { return Add_Head(Active, object); } - int Delete(T const & object) + int Delete(T const& object) { return Delete(Active, object); } - int Delete_All(T const & object) + int Delete_All(T const& object) { int count = 0; for (int i = FIRST; i < COUNT; ++i) @@ -235,7 +235,7 @@ public: return count; } - int Delete_All_Except(T const & object, int except) + int Delete_All_Except(T const& object, int except) { int count = 0; for (int i = FIRST; i < COUNT; ++i) @@ -253,12 +253,12 @@ public: return Delete(Active, index); } - T & operator[](unsigned index) + T& operator[](unsigned index) { return Collection[Active][index]; } - T const & operator[](unsigned index) const + T const& operator[](unsigned index) const { return Collection[Active][index]; } @@ -273,17 +273,17 @@ public: return Collection[context].Count(); } - int Add(int context, T const & object) + int Add(int context, T const& object) { return Collection[context].Add(object); } - int Add_Head(int context, T const & object) + int Add_Head(int context, T const& object) { return Collection[context].Add(object); } - int Delete(int context, T const & object) + int Delete(int context, T const& object) { return Collection[context].Delete(object); } @@ -293,12 +293,12 @@ public: return Collection[context].Delete(index); } - DynamicVectorClass & Raw() + DynamicVectorClass& Raw() { return Collection[Active]; } - DynamicVectorClass & Raw(int context) + DynamicVectorClass& Raw(int context) { return Collection[context]; } @@ -321,104 +321,104 @@ private: */ class BooleanVectorClass { - public: - BooleanVectorClass(unsigned size=0, unsigned char * array=0); - BooleanVectorClass(BooleanVectorClass const & vector); +public: + BooleanVectorClass(unsigned size = 0, unsigned char* array = 0); + BooleanVectorClass(BooleanVectorClass const& vector); - // Assignment operator. - BooleanVectorClass & operator =(BooleanVectorClass const & vector); + // Assignment operator. + BooleanVectorClass& operator =(BooleanVectorClass const& vector); - // Equivalency operator. - int operator == (BooleanVectorClass const & vector); + // Equivalency operator. + int operator == (BooleanVectorClass const& vector); - // Fetch number of boolean objects in vector. - int Length(void) {return BitCount;}; + // Fetch number of boolean objects in vector. + int Length(void) { return BitCount; }; - // Set all boolean values to false; - void Reset(void); + // Set all boolean values to false; + void Reset(void); - // Set all boolean values to true. - void Set(void); + // Set all boolean values to true. + void Set(void); - // Resets vector to zero length (frees memory). - void Clear(void); + // Resets vector to zero length (frees memory). + void Clear(void); - // Change size of this boolean vector. - int Resize(unsigned size); + // Change size of this boolean vector. + int Resize(unsigned size); - // Fetch reference to specified index. - bool const & operator[](int index) const { - if (LastIndex != index) Fixup(index); - return(Copy); - }; - bool & operator[](int index) { - if (LastIndex != index) Fixup(index); - return(Copy); - }; + // Fetch reference to specified index. + bool const& operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool& operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; - // Quick check on boolean state. - bool Is_True(int index) const { - if (index == LastIndex) return(Copy); - return(Get_Bit(&BitArray[0], index)); - }; + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index)); + }; - // Find first index that is false. - int First_False(void) const { - if (LastIndex != -1) Fixup(-1); + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); - int retval = First_False_Bit(&BitArray[0], BitArray.Length()); - if (retval < BitCount) return(retval); - - /* - ** Failure to find a false boolean value in the vector. Return this - ** fact in the form of an invalid index number. - */ - return(-1); - } - - // Find first index that is true. - int First_True(void) const { - if (LastIndex != -1) Fixup(-1); - - int retval = First_True_Bit(&BitArray[0], BitArray.Length()); - if (retval < BitCount) return(retval); - - /* - ** Failure to find a true boolean value in the vector. Return this - ** fact in the form of an invalid index number. - */ - return(-1); - } - - private: - void Fixup(int index=-1) const; + int retval = First_False_Bit(&BitArray[0], BitArray.Length()); + if (retval < BitCount) return(retval); /* - ** This is the number of boolean values in the vector. This value is - ** not necessarily a multiple of 8, even though the underlying character - ** vector contains a multiple of 8 bits. + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. */ - int BitCount; + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0], BitArray.Length()); + if (retval < BitCount) return(retval); /* - ** This is a referential copy of an element in the bit vector. The - ** purpose of this copy is to allow normal reference access to this - ** object (for speed reasons). This hides the bit packing scheme from - ** the user of this class. + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. */ - bool Copy; + return(-1); + } - /* - ** This records the index of the value last fetched into the reference - ** boolean variable. This index is used to properly restore the value - ** when the reference copy needs updating. - */ - int LastIndex; +private: + void Fixup(int index = -1) const; - /* - ** This points to the allocated bitfield array. - */ - VectorClass BitArray; + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; }; @@ -447,7 +447,7 @@ class BooleanVectorClass * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) +DynamicVectorClass::DynamicVectorClass(unsigned size, T const* array) : VectorClass(size, array) { GrowthStep = 10; @@ -475,7 +475,7 @@ DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -int DynamicVectorClass::Resize(unsigned newsize, T const * array) +int DynamicVectorClass::Resize(unsigned newsize, T const* array) { if (VectorClass::Resize(newsize, array)) { if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); @@ -504,7 +504,7 @@ int DynamicVectorClass::Resize(unsigned newsize, T const * array) * 03/13/1995 JLB : Created. * *=============================================================================================*/ template -int DynamicVectorClass::ID(T const & object) +int DynamicVectorClass::ID(T const& object) { for (int index = 0; index < Count(); index++) { if ((*this)[index] == object) return(index); @@ -531,7 +531,7 @@ int DynamicVectorClass::ID(T const & object) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -int DynamicVectorClass::Add(T const & object) +int DynamicVectorClass::Add(T const& object) { if (ActiveCount >= (int)Length()) { if ((IsAllocated || !VectorMax) && GrowthStep > 0) { @@ -578,7 +578,7 @@ int DynamicVectorClass::Add(T const & object) * 09/21/1995 JLB : Created. * *=============================================================================================*/ template -int DynamicVectorClass::Add_Head(T const & object) +int DynamicVectorClass::Add_Head(T const& object) { if (ActiveCount >= (int)Length()) { if ((IsAllocated || !VectorMax) && GrowthStep > 0) { @@ -631,7 +631,7 @@ int DynamicVectorClass::Add_Head(T const & object) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -int DynamicVectorClass::Delete(T const & object) +int DynamicVectorClass::Delete(T const& object) { return(Delete(ID(object))); } @@ -700,7 +700,7 @@ int DynamicVectorClass::Delete(int index) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -VectorClass::VectorClass(unsigned size, T const * array) : +VectorClass::VectorClass(unsigned size, T const* array) : Vector(0), VectorMax(size), IsAllocated(false) @@ -759,7 +759,7 @@ VectorClass::~VectorClass(void) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -VectorClass::VectorClass(VectorClass const & vector) : +VectorClass::VectorClass(VectorClass const& vector) : Vector(0), VectorMax(0), IsAllocated(false) @@ -784,7 +784,7 @@ VectorClass::VectorClass(VectorClass const & vector) : * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -VectorClass & VectorClass::operator =(VectorClass const & vector) +VectorClass& VectorClass::operator =(VectorClass const& vector) { if (this != &vector) { Clear(); @@ -824,7 +824,7 @@ VectorClass & VectorClass::operator =(VectorClass const & vector) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -int VectorClass::operator == (VectorClass const & vector) const +int VectorClass::operator == (VectorClass const& vector) const { if (VectorMax == vector.Length()) { for (int index = 0; index < (int)VectorMax; index++) { @@ -857,9 +857,9 @@ int VectorClass::operator == (VectorClass const & vector) const * 03/13/1995 JLB : Created. * *=============================================================================================*/ template -inline int VectorClass::ID(T const * ptr) +inline int VectorClass::ID(T const* ptr) { - return(((INT_PTR)ptr - (INT_PTR)&(*this)[0]) / sizeof(T)); + return(((INT_PTR)ptr - (INT_PTR) & (*this)[0]) / sizeof(T)); } @@ -880,7 +880,7 @@ inline int VectorClass::ID(T const * ptr) * 03/13/1995 JLB : Created. * *=============================================================================================*/ template -int VectorClass::ID(T const & object) +int VectorClass::ID(T const& object) { for (int index = 0; index < (int)VectorMax; index++) { if ((*this)[index] == object) { @@ -939,7 +939,7 @@ void VectorClass::Clear(void) * 03/10/1995 JLB : Created. * *=============================================================================================*/ template -int VectorClass::Resize(unsigned newsize, T const * array) +int VectorClass::Resize(unsigned newsize, T const* array) { if (newsize) { @@ -947,7 +947,7 @@ int VectorClass::Resize(unsigned newsize, T const * array) ** Allocate a new vector of the size specified. The default constructor ** will be called for every object in this vector. */ - T * newptr; + T* newptr; if (!array) { newptr = new T[newsize]; } From 54aeb104fba32b0a8d3d91c5ba6e2a9045a7f266 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Fri, 26 Jun 2020 15:07:15 -0700 Subject: [PATCH 06/18] Added emptytxt for cache. --- cache/empty.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cache/empty.txt diff --git a/cache/empty.txt b/cache/empty.txt new file mode 100644 index 0000000..e69de29 From 196decf9bd092f2c36bb1774a1e3cc3a8de3d74f Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Sat, 27 Jun 2020 22:49:32 +0200 Subject: [PATCH 07/18] Simplifying I/O flow --- code/REDALERT/EDIT.CPP | 47 ++------ code/REDALERT/EDIT.H | 2 +- code/REDALERT/GADGET.CPP | 8 +- code/REDALERT/io/EventQueue.cpp | 183 +++++++++++++++++--------------- code/REDALERT/io/EventQueue.h | 91 ++++++++++------ code/REDALERT/io/UserInput.cpp | 40 ++++--- code/REDALERT/io/UserInput.h | 26 ++--- 7 files changed, 213 insertions(+), 184 deletions(-) diff --git a/code/REDALERT/EDIT.CPP b/code/REDALERT/EDIT.CPP index 45783a9..1684c36 100644 --- a/code/REDALERT/EDIT.CPP +++ b/code/REDALERT/EDIT.CPP @@ -253,39 +253,8 @@ int EditClass::Action(unsigned flags, KeyNumType & key) } else { #ifdef WIN32 - KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); - - /* - ** Allow numeric keypad presses to map to ascii numbers - */ - if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { - - key = (KeyNumType)(key & ~WWKEY_VK_BIT); - if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { - if (Handle_Key (ascii) ) { - flags &= ~KEYBOARD; - key = KN_NONE; - } - } - } else { - /* - ** Filter out all special keys except return and backspace - */ if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) - || key == KN_RETURN || key == KN_BACKSPACE) { - - - - if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { - if (Handle_Key(Keyboard->To_ASCII(key))) { - flags &= ~KEYBOARD; - key = KN_NONE; - } - } - } else { - flags &= ~KEYBOARD; - key = KN_NONE; - } - } + //KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + Handle_Key(key, flags); } #else //WIN32 @@ -374,11 +343,12 @@ void EditClass::Draw_Text(char const * text) * HISTORY: * * 01/21/1995 JLB : Created. * *=============================================================================================*/ -bool EditClass::Handle_Key(KeyASCIIType ascii) +bool EditClass::Handle_Key(KeyNumType key, unsigned flags) { - unsigned char testAscii = (unsigned char)ascii; + unsigned char testAscii = UserInput.KeyB.ASCII; + //if (key & KN_SHIFT_BIT || key & KN_CAPSLOCK_BIT) testAscii = toupper(testAscii); - switch (ascii) { + switch (key & 0xff) { /* ** Handle the special case of a non-keyboard event. It is possible that this ** key code might be passed to this routine if this routine has been overridden @@ -394,14 +364,14 @@ bool EditClass::Handle_Key(KeyASCIIType ascii) ** so that the controlling program will know that the text can be ** processed. */ - case KA_RETURN: + case KN_RETURN: Clear_Focus(); return(false); /* ** When the BACKSPACE key is pressed, remove the last character in the edit string. */ - case KA_BACKSPACE: + case KN_BACKSPACE: if (Length) { Length--; String[Length] = '\0'; @@ -457,6 +427,7 @@ bool EditClass::Handle_Key(KeyASCIIType ascii) */ String[Length++] = testAscii; String[Length] = '\0'; + Flag_To_Redraw(); break; } diff --git a/code/REDALERT/EDIT.H b/code/REDALERT/EDIT.H index 92168d9..5d861ad 100644 --- a/code/REDALERT/EDIT.H +++ b/code/REDALERT/EDIT.H @@ -91,7 +91,7 @@ class EditClass : public ControlClass virtual int Action (unsigned flags, KeyNumType &key); virtual void Draw_Background(void); virtual void Draw_Text(char const * text); - virtual bool Handle_Key(KeyASCIIType ascii); + virtual bool Handle_Key(KeyNumType ascii, unsigned flags); private: int IsReadOnly; diff --git a/code/REDALERT/GADGET.CPP b/code/REDALERT/GADGET.CPP index eb2ce84..4d3b60c 100644 --- a/code/REDALERT/GADGET.CPP +++ b/code/REDALERT/GADGET.CPP @@ -471,7 +471,7 @@ void GadgetClass::Draw_All(bool forced) *=========================================================================*/ KeyNumType GadgetClass::Input(void) { - EventMessage* eventMessage = NULL; + EventMessage eventMessage; KeyNumType key = KN_NONE; int flags = 0; int forced = false; @@ -490,11 +490,11 @@ KeyNumType GadgetClass::Input(void) /* ** Fetch any pending keyboard input. */ - UserInput.Process_Input(); // key, flags); + UserInput.Process_Input(key, flags); if ( EventQueue.Check() ) { eventMessage = EventQueue.Get(); - eventMessage->Key_Flags( key, flags ); + eventMessage.Key_Flags( key, flags ); } /* @@ -518,7 +518,7 @@ KeyNumType GadgetClass::Input(void) ** If there is a gadget that has the keyboard focus, then route all keyboard ** events to it. */ - if ( Focused && eventMessage != NULL && eventMessage->EventType == EventMessageType::EVENT_KEYBOARD ) { + if ( Focused && eventMessage.EventType == EventMessageType::EVENT_KEYBOARD ) { Focused->Draw_Me(false); Focused->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); if (Focused) { diff --git a/code/REDALERT/io/EventQueue.cpp b/code/REDALERT/io/EventQueue.cpp index 94919a1..eef7a05 100644 --- a/code/REDALERT/io/EventQueue.cpp +++ b/code/REDALERT/io/EventQueue.cpp @@ -2,20 +2,22 @@ #include "EventQueue.h" /* - Clears the event queue + Clears the event queue and frees memory */ void EventQueueClass::Clear() { - while (this->eventQueue.size() > 0) this->eventQueue.pop(); + while (this->eventQueue.size() > 0) { + this->eventQueue.pop(); + } } /* - Inserts a new KeyNumType event into the FIFO buffer. KeyNumType represents a key press or mouse button - Input: KeyNumType + optional flag bits for SHIFT/CTRL/etc. cast as unsigned short + Inserts a new event message event into the FIFO buffer. */ -void EventQueueClass::Put(EventMessage* input) +void EventQueueClass::Put(EventMessage input) { - this->eventQueue.push(input); + if ( Is_Buffer_Full() ) return; + this->eventQueue.push( input ); } /* @@ -29,12 +31,12 @@ bool EventQueueClass::Check() const /* Get the first (unprocessed) element in the event queue */ -EventMessage* EventQueueClass::Get() +EventMessage EventQueueClass::Get() { // wait for buffer while( !this->Check() ) { } - - EventMessage* returnVal = this->eventQueue.front(); + + EventMessage returnVal = this->eventQueue.front(); this->eventQueue.pop(); return returnVal; @@ -46,95 +48,110 @@ EventMessage* EventQueueClass::Get() char EventQueueClass::Get_ASCII() { // get the last key from the buffer - KeyboardEventMessage* keyMessage = dynamic_cast( this->Get() ); - unsigned short key = keyMessage->Key; - - // check if it is a keyboard - if (key == KN_LMOUSE || key == KN_RMOUSE) return 0x0; - - // convert to ascii - char ascii; - unsigned short key_raw = (UINT)(key & 0xFF); - - // find the SDL keycode - auto sdl_Key = sdl_keyMapping_reverse.find( (KeyNumType)key_raw ); - if (sdl_Key != sdl_keyMapping_reverse.end()) - { - // convert to ascii - ascii = SDL_GetKeyName( sdl_Key->first )[0]; - - // convert to uppercase - if (keyMessage->Shift || keyMessage->CapsLock) ascii = toupper(ascii); - } - - return 0x0; + EventMessage eventMessage = this->Get(); + return eventMessage.Keyboard.ASCII; } -KeyboardEventMessage::KeyboardEventMessage(KeyNumType key, bool shift, bool alt, bool control, bool capsLock) { +EventMessage::EventMessage(KeyNumType key, char ascii, bool shift, bool alt, bool control, bool capsLock) { + this->Clear(); + + this->Valid = true; this->EventType = EventMessageType::EVENT_KEYBOARD; - this->Key = key; - this->Shift = shift; - this->Alt = alt; - this->Control = control; - this->CapsLock = capsLock; - this->Flags = 0; - if (this->Shift) this->Flags |= KN_SHIFT_BIT; - if (this->Alt) this->Flags |= KN_ALT_BIT; - if (this->Control) this->Flags |= KN_CTRL_BIT; + this->Keyboard.Key = key; + this->Keyboard.Shift = shift; + this->Keyboard.Alt = alt; + this->Keyboard.Control = control; + this->Keyboard.CapsLock = capsLock; + this->Keyboard.ASCII = ascii; } -void KeyboardEventMessage::Key_Flags(KeyNumType& key, int& flags) +EventMessage::EventMessage(int x, int y, unsigned short button, MouseButtonState buttonState) { - flags = this->Flags; - flags |= GadgetClass::KEYBOARD; + this->Clear(); - key = (KeyNumType)this->Key; -} - -MouseEventMessage::MouseEventMessage(int x, int y, unsigned short button, UserInputClass::MouseButtonState buttonState) -{ + this->Valid = true; this->EventType = EventMessageType::EVENT_MOUSE; - this->x = x; - this->y = y; - this->Button = button; - this->ButtonState = buttonState; + this->Mouse.x = x; + this->Mouse.y = y; + this->Mouse.Button = button; + this->Mouse.ButtonState = buttonState; this->Flags = 0; -} -void MouseEventMessage::Key_Flags(KeyNumType& key, int& flags) -{ - key = (KeyNumType)this->Button; - - if (key == KN_LMOUSE) { - switch (this->ButtonState) - { - case UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN: - flags = GadgetClass::LEFTPRESS; - break; - case UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE: - flags = GadgetClass::LEFTRELEASE; - break; - case UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD: - flags = GadgetClass::LEFTHELD; - break; - } + if (buttonState == MouseButtonState::MOUSE_BUTTON_UP) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTUP : GadgetClass::RIGHTUP; } - if (key == KN_RMOUSE) { - switch (this->ButtonState) - { - case UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN: - flags = GadgetClass::RIGHTPRESS; - break; - case UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE: - flags = GadgetClass::RIGHTRELEASE; - break; - case UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD: - flags = GadgetClass::RIGHTHELD; - break; + if (buttonState == MouseButtonState::MOUSE_BUTTON_DOWN) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTPRESS : GadgetClass::RIGHTPRESS; + } + + if (buttonState == MouseButtonState::MOUSE_BUTTON_HOLD) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTHELD : GadgetClass::RIGHTHELD; + } + + if (buttonState == MouseButtonState::MOUSE_BUTTON_RELEASE) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTRELEASE : GadgetClass::RIGHTRELEASE; + } +} + + +void EventMessage::Key_Flags(KeyNumType& key, int& flags) +{ + if (this->EventType == EventMessageType::EVENT_KEYBOARD) + { + flags = this->Flags; + flags |= GadgetClass::KEYBOARD; + + unsigned short outputKey = this->Keyboard.Key; + + if (this->Keyboard.Shift) outputKey |= KN_SHIFT_BIT; + if (this->Keyboard.Alt) outputKey |= KN_ALT_BIT; + if (this->Keyboard.Control) outputKey |= KN_CTRL_BIT; + + key = (KeyNumType)outputKey; + } + + if (this->EventType == EventMessageType::EVENT_MOUSE) + { + key = (KeyNumType)this->Mouse.Button; + + if (key == KN_LMOUSE) { + switch (this->Mouse.ButtonState) + { + case MouseButtonState::MOUSE_BUTTON_UP: + flags = GadgetClass::LEFTUP; + break; + case MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::LEFTPRESS; + break; + case MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::LEFTRELEASE; + break; + case MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::LEFTHELD; + break; + } + } + + if (key == KN_RMOUSE) { + switch (this->Mouse.ButtonState) + { + case MouseButtonState::MOUSE_BUTTON_UP: + flags = GadgetClass::LEFTUP; + break; + case MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::RIGHTPRESS; + break; + case MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::RIGHTRELEASE; + break; + case MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::RIGHTHELD; + break; + } } } } \ No newline at end of file diff --git a/code/REDALERT/io/EventQueue.h b/code/REDALERT/io/EventQueue.h index 0767b4a..be92699 100644 --- a/code/REDALERT/io/EventQueue.h +++ b/code/REDALERT/io/EventQueue.h @@ -1,4 +1,5 @@ #pragma once +#include /* * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer* @@ -22,10 +23,11 @@ extern UserInputClass UserInput; /* Event type definitions */ -typedef enum { +typedef enum EventMessageType { + EVENT_NONE, EVENT_KEYBOARD, EVENT_MOUSE -} EventMessageType; +}; /* Event types @@ -33,42 +35,69 @@ typedef enum { class EventMessage { public: EventMessageType EventType; + bool Valid; - virtual void Key_Flags(KeyNumType& key, int& flags) = 0; - virtual ~EventMessage() = default; -}; + EventMessage() { + this->EventType == EVENT_NONE; -class KeyboardEventMessage : public EventMessage { - public: - KeyNumType Key; - bool Shift; - bool Alt; - bool Control; - bool CapsLock; + this->Keyboard.Key = KN_NONE; + this->Keyboard.Shift = false; + this->Keyboard.Alt = false; + this->Keyboard.Control = false; + this->Keyboard.CapsLock = false; + + this->Mouse.x = -1; + this->Mouse.y = -1; + this->Mouse.Button = 0; + this->Mouse.ButtonState = MouseButtonState::MOUSE_BUTTON_UP; + + this->Flags = 0; + + this->Valid = false; + } + + // for keyboard + struct { + KeyNumType Key; + char ASCII; + bool Shift; + bool Alt; + bool Control; + bool CapsLock; + } Keyboard; + + // for mouse + struct { + int x; + int y; + unsigned short Button; + MouseButtonState ButtonState; + } Mouse; // for cascading functions unsigned short Flags; - KeyboardEventMessage(KeyNumType key, bool shift = false, bool alt = false, bool control = false, bool capsLock = false); - virtual void Key_Flags(KeyNumType& key, int& flags); + EventMessage( KeyNumType key, char asciis, bool shift, bool alt, bool control, bool capsLock ); + EventMessage( int x, int y, unsigned short button = KN_LMOUSE, MouseButtonState buttonState = MouseButtonState::MOUSE_BUTTON_DOWN ); - virtual ~KeyboardEventMessage() = default; -}; + void Key_Flags(KeyNumType& key, int& flags); + private: + void Clear() { + this->Keyboard.Key = KN_NONE; + this->Keyboard.Shift = false; + this->Keyboard.Alt = false; + this->Keyboard.Control = false; + this->Keyboard.CapsLock = false; -class MouseEventMessage : public EventMessage { - public: - int x; - int y; - unsigned short Button; - UserInputClass::MouseButtonState ButtonState; + this->Mouse.x = -1; + this->Mouse.y = -1; + this->Mouse.Button = 0; + this->Mouse.ButtonState = MouseButtonState::MOUSE_BUTTON_UP; - // for cascading functions - unsigned short Flags; + this->Flags = 0; - MouseEventMessage(int x, int y, unsigned short button = KN_LMOUSE, UserInputClass::MouseButtonState buttonState = UserInputClass::MouseButtonState::MOUSE_BUTTON_DOWN); - virtual void Key_Flags(KeyNumType& key, int& flags); - - virtual ~MouseEventMessage() = default; + this->Valid = false; + } }; class EventQueueClass { @@ -77,15 +106,15 @@ class EventQueueClass { int Get_Mouse_Y() const { return UserInput.Mouse.Y; } void Clear(); - void Put(EventMessage* input); + void Put(EventMessage input); bool Check() const; - EventMessage* Get(); + EventMessage Get(); char EventQueueClass::Get_ASCII(); private: const int MaxCapacity = 100; - std::queue eventQueue; + std::queue< EventMessage > eventQueue; bool Is_Buffer_Full() const { return this->eventQueue.size() == MaxCapacity; } bool Is_Buffer_Empty() const { return this->eventQueue.size() == 0; } diff --git a/code/REDALERT/io/UserInput.cpp b/code/REDALERT/io/UserInput.cpp index 818f64d..cab49c4 100644 --- a/code/REDALERT/io/UserInput.cpp +++ b/code/REDALERT/io/UserInput.cpp @@ -49,6 +49,8 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) const Uint32 MouseState = SDL_GetMouseState(&Mouse.X, &Mouse.Y); + flags = 0; + Keyboard->MouseQX = Mouse.X; Keyboard->MouseQY = Mouse.Y; @@ -87,8 +89,9 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) case SDL_MOUSEMOTION: if ( !( MouseState & SDL_BUTTON(SDL_BUTTON_LEFT) ) ) { - flags = GadgetClass::LEFTUP; + flags |= GadgetClass::LEFTUP; } + break; case SDL_MOUSEBUTTONDOWN: @@ -96,9 +99,9 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_DOWN; key = KN_LMOUSE; - flags = GadgetClass::LEFTPRESS; + flags |= GadgetClass::LEFTPRESS; - EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE) ); + EventQueue.Put( EventMessage( Mouse.X, Mouse.Y, KN_LMOUSE ) ); Keyboard->Put_Element(KN_LMOUSE); @@ -112,12 +115,12 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) else if (event.button.button == SDL_BUTTON_RIGHT) { Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_DOWN; - EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_RMOUSE) ); + EventQueue.Put( EventMessage( Mouse.X, Mouse.Y, KN_RMOUSE ) ); Keyboard->Put_Element(KN_RMOUSE); key = KN_RMOUSE; - flags = GadgetClass::RIGHTPRESS; + flags |= GadgetClass::RIGHTPRESS; numFramesLMouse = 0; } @@ -127,17 +130,17 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) if (event.button.button == SDL_BUTTON_LEFT) { Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_RELEASE; - EventQueue.Put(new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE)); + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE) ); - flags = GadgetClass::LEFTRELEASE; + flags |= GadgetClass::LEFTRELEASE; numFramesLMouse = 0; } else if (event.button.button == SDL_BUTTON_RIGHT) { Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_RELEASE; - EventQueue.Put( new MouseEventMessage(Mouse.X, Mouse.Y, KN_RMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_RELEASE) ); + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_RMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE) ); - flags = GadgetClass::RIGHTRELEASE; + flags |= GadgetClass::RIGHTRELEASE; numFramesLMouse = 0; } break; @@ -159,6 +162,11 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) } int keyFlags = 0x00; + KeyB.Shift = false; + KeyB.Alt = false; + KeyB.Control = false; + KeyB.CapsLock = false; + if (state[SDL_SCANCODE_RSHIFT] || state[SDL_SCANCODE_LSHIFT]) { KeyB.Shift = true; keyFlags |= KN_SHIFT_BIT; @@ -176,16 +184,20 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) KeyB.CapsLock = true; } - // handle match between SDL and internals (non-text keys) + // handle match between SDL and internals ( non-text keys ) auto element = sdl_keyMapping.find((SDL_KeyCode)event.key.keysym.sym); if (element != sdl_keyMapping.end()) { //EventQueue.Put( element->second | keyFlags ); - EventQueue.Put( new KeyboardEventMessage( (KeyNumType)element->second, KeyB.Shift, KeyB.Alt, KeyB.Control, KeyB.CapsLock ) ); + EventQueue.Put( EventMessage( (KeyNumType)element->second, element->second, KeyB.Shift, KeyB.Alt, KeyB.Control, KeyB.CapsLock ) ); Keyboard->Put_Element( element->second | keyFlags); - KeyB.ASCII = element->second; + KeyB.LastKey = element->first; + if (event.text.text[0] == (event.text.text[0] & 0xFF)) + { + KeyB.ASCII = element->second; + } } break; @@ -207,8 +219,8 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) if (!leftMouseProcessed && numFramesLMouse != 0) { Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_HOLD; - flags = GadgetClass::LEFTHELD; + flags |= GadgetClass::LEFTHELD; - EventQueue.Put(new MouseEventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, UserInputClass::MouseButtonState::MOUSE_BUTTON_HOLD)); + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_HOLD) ); } } \ No newline at end of file diff --git a/code/REDALERT/io/UserInput.h b/code/REDALERT/io/UserInput.h index 41f37c9..6d9db2e 100644 --- a/code/REDALERT/io/UserInput.h +++ b/code/REDALERT/io/UserInput.h @@ -93,22 +93,22 @@ const int USERINPUT_SHIFT_BIT = 0x100; const int USERINPUT_CTRL_BIT = 0x200; const int USERINPUT_ALT_BIT = 0x400; +typedef enum { + MOUSE_BUTTON_UP, + MOUSE_BUTTON_DOWN, + MOUSE_BUTTON_HOLD, + MOUSE_BUTTON_RELEASE, +} MouseButtonState; + +typedef enum { + INPUT_ACTION_NONE, + INPUT_ACTION_KEYBOARD, + INPUT_ACTION_MOUSE, +} InputAction; + class UserInputClass { public: - typedef enum { - MOUSE_BUTTON_UP, - MOUSE_BUTTON_DOWN, - MOUSE_BUTTON_HOLD, - MOUSE_BUTTON_RELEASE, - } MouseButtonState; - - typedef enum { - INPUT_ACTION_NONE, - INPUT_ACTION_KEYBOARD, - INPUT_ACTION_MOUSE, - } InputAction; - UserInputClass(); void UserInputClass::Process_Input( KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags ); From 19569faf2be061562bfa5b9c523d73266adea805 Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sat, 27 Jun 2020 18:16:29 -0600 Subject: [PATCH 08/18] Added ability to change attributes for buildings, units, infantry, aircraft and veseels. Values are restored to default values by cacheing values upon first usage and cleaning up with Deinit(). Revised global header to encapsulate the values within sections, drastically reducing variable counts Mouse frequent click/drag fix --- code/redalert/io/userinput.cpp | 6 + code/redalert/mapscript.cpp | 552 +++++++++++++++++++++++++++++++-- code/redalert/mapscript.h | 57 +++- scripts/_Global.script | 519 +++++++++++++++++-------------- 4 files changed, 875 insertions(+), 259 deletions(-) diff --git a/code/redalert/io/userinput.cpp b/code/redalert/io/userinput.cpp index aadd59c..2f0ea73 100644 --- a/code/redalert/io/userinput.cpp +++ b/code/redalert/io/userinput.cpp @@ -58,6 +58,12 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) Keyboard->Put_Element(KN_LMOUSE); leftMouseProcessed = true; + + // Seems to fix "missed mouse click" bug + if (numFramesLMouse == 0) { + numFramesLMouse++; + return; + } numFramesLMouse++; } else if (event.button.button == SDL_BUTTON_RIGHT) { diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp index fa5feb8..807fe15 100644 --- a/code/redalert/mapscript.cpp +++ b/code/redalert/mapscript.cpp @@ -1,5 +1,5 @@ -#include "FUNCTION.H" -#include "MapScript.h" +#include "function.H" +#include "mapscript.h" /********************************************************************************************** * Red Alert Vanilla Actions * @@ -3306,14 +3306,39 @@ - + // Global attributes / Multi object attributes switch ((MapScriptAttributeType)attribute_type) { + case ATTRIBUTE_TYPE: { + + }break; case ATTRIBUTE_STRENGTH: { int attribute_value = lua_tointeger(L, 3); this_object->Strength = attribute_value; }break; + + case ATTRIBUTE_IS_REPAIRING: { + + int attribute_value = lua_tointeger(L, 3); + + switch (this_object->RTTI) { + case RTTI_BUILDING: { + + BuildingClass* this_building = (BuildingClass*)this_object; + this_building->IsRepairing = attribute_value; + + }break; + case RTTI_VESSEL: { + + VesselClass* this_vessel = (VesselClass*)this_object; + this_vessel->IsSelfRepairing = attribute_value; + + }break; + } + }break; + + // Object Class specific default: { TechnoTypeClass* this_type_class; @@ -3344,7 +3369,7 @@ this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); break; - } + } if (this_type_class != NULL) { @@ -3531,6 +3556,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxStrength); + + // Set the attribute this_type_class->MaxStrength = attribute_value; }break; @@ -3539,6 +3568,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Cost); + + // Set the attribute this_type_class->Cost = attribute_value; }break; @@ -3546,6 +3579,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SightRange); + + // Set the attribute this_type_class->SightRange = attribute_value; }break; @@ -3553,6 +3590,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Level); + + // Set the attribute this_type_class->Level = attribute_value; }break; @@ -3560,6 +3601,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Armor); + + // Set the attribute this_type_class->Armor = (ArmorType)attribute_value; }break; @@ -3567,9 +3612,24 @@ int attribute_value = lua_tointeger(L, 3); - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->PrimaryWeapon = this_weapon; + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->PrimaryWeapon->ID); + }else { + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } + + // Set the attribute + if (attribute_value < 0) { + this_type_class->PrimaryWeapon = NULL; + }else { + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->PrimaryWeapon = this_weapon; + }else { + this_type_class->PrimaryWeapon = NULL; + } } }break; @@ -3577,9 +3637,24 @@ int attribute_value = lua_tointeger(L, 3); - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->SecondaryWeapon = this_weapon; + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SecondaryWeapon->ID); + }else { + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } + + // Set the attribute + if (attribute_value < 0) { + this_type_class->SecondaryWeapon = NULL; + }else{ + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->SecondaryWeapon = this_weapon; + }else { + this_type_class->SecondaryWeapon = NULL; + } } }break; @@ -3587,6 +3662,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Speed); + + // Set the attribute this_type_class->Speed = (SpeedType)attribute_value; }break; @@ -3594,6 +3673,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxSpeed); + + // Set the attribute this_type_class->MaxSpeed = (MPHType)attribute_value; }break; @@ -3601,6 +3684,10 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxAmmo); + + // Set the attribute this_type_class->MaxAmmo = attribute_value; }break; @@ -3608,55 +3695,87 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->ROT); + + // Set the attribute this_type_class->ROT = attribute_value; }break; - case ATTRIBUTE_IS_CRUSHABLE: { + case ATTRIBUTE_CRUSHABLE: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrushable); + + // Set the attribute this_type_class->IsCrushable = attribute_value; }break; - case ATTRIBUTE_IS_STEALTHY: { + case ATTRIBUTE_STEALTHY: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsStealthy); + + // Set the attribute this_type_class->IsStealthy = attribute_value; }break; - case ATTRIBUTE_IS_SELECTABLE: { + case ATTRIBUTE_SELECTABLE: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelectable); + + // Set the attribute this_type_class->IsSelectable = attribute_value; }break; - case ATTRIBUTE_IS_LEGAL_TARGET: { + case ATTRIBUTE_LEGAL_TARGET: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsLegalTarget); + + // Set the attribute this_type_class->IsLegalTarget = attribute_value; }break; - case ATTRIBUTE_IS_REPAIRABLE: { + case ATTRIBUTE_REPAIRABLE: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsRepairable); + + // Set the attribute this_type_class->IsRepairable = attribute_value; }break; - case ATTRIBUTE_IS_SELF_HEALING: { + case ATTRIBUTE_SELF_HEALING: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelfHealing); + + // Set the attribute this_type_class->IsSelfHealing = attribute_value; }break; - case ATTRIBUTE_IS_EXPLODING: { + case ATTRIBUTE_EXPLODES: { unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsExploding); + + // Set the attribute this_type_class->IsExploding = attribute_value; }break; @@ -3664,6 +3783,10 @@ unsigned int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrew); + + // Set the attribute this_type_class->IsCrew = attribute_value; }break; @@ -3671,14 +3794,312 @@ int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxPassengers); + + // Set the attribute this_type_class->MaxPassengers = attribute_value; }break; + default:{ + + // Object class specific attributes + // Welcome to the web of switches] + + // Type of object + switch (this_type_class->RTTI) { + + // Building Specific + case RTTI_BUILDINGTYPE:{ + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CAPACITY: { + + int attribute_value = lua_tointeger(L, 3); + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Capacity); + + // Set the attribute + this_building_class->Capacity = attribute_value; + + }break; + case ATTRIBUTE_POWER: { + + int attribute_value = lua_tointeger(L, 3); + + // Both power and drain are different variables + // And so we do some trickery here to fill + // the right values based on postivie/negative input + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Power + this_building_class->Drain); + + // Set the attribute + if (attribute_value > 0) { + this_building_class->Power = attribute_value; + this_building_class->Drain = 0; + }else { + this_building_class->Power = 0; + this_building_class->Drain = attribute_value * -1; // Flip negative power input + } + + }break; + case ATTRIBUTE_SELLABLE: { + + int attribute_value = lua_tointeger(L, 3); + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->IsUnsellable *-1); + + // Set the attribute + // Personal opinion; sellable makes more sense than unsellable + // This replacement could be seen as IsUnNonSellable, or... IsSellable + if (attribute_value == 1) { + this_building_class->IsUnsellable = 0; + } + else { + this_building_class->IsUnsellable = 1; + } + + }break; + } + + }break; + + + // Unit Specific + case RTTI_UNITTYPE:{ + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CRATE_GOODIE: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrateGoodie); + + // Set the attribute + this_unit_class->IsCrateGoodie = attribute_value; + + }break; + case ATTRIBUTE_CRUSHER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrusher); + + // Set the attribute + this_unit_class->IsCrusher = attribute_value; + + }break; + case ATTRIBUTE_HARVEST: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsToHarvest); + + // Set the attribute + this_unit_class->IsToHarvest = attribute_value; + + }break; + case ATTRIBUTE_RADAR_EQUIPPED: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsRadarEquipped); + + // Set the attribute + this_unit_class->IsRadarEquipped = attribute_value; + + }break; + case ATTRIBUTE_JAMMER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsJammer); + + // Set the attribute + this_unit_class->IsJammer = attribute_value; + + }break; + case ATTRIBUTE_GAPPER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsGapper); + + // Set the attribute + this_unit_class->IsGapper = attribute_value; + + }break; + } + + }break; + + + // Infantry Specific + case RTTI_INFANTRYTYPE:{ + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_FEMALE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFemale); + + // Set the attribute + this_infantry_class->IsFemale = attribute_value; + + }break; + case ATTRIBUTE_CAPTURE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCapture); + + // Set the attribute + this_infantry_class->IsCapture = attribute_value; + + }break; + case ATTRIBUTE_FRAIDY_CAT: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFraidyCat); + + // Set the attribute + this_infantry_class->IsFraidyCat = attribute_value; + + }break; + case ATTRIBUTE_CIVILIAN: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCivilian); + + // Set the attribute + this_infantry_class->IsCivilian = attribute_value; + + }break; + case ATTRIBUTE_BOMBER: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsBomber); + + // Set the attribute + this_infantry_class->IsBomber = attribute_value; + + }break; + case ATTRIBUTE_DOG: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsDog); + + // Set the attribute + this_infantry_class->IsDog = attribute_value; + + }break; + + } + + }break; + + } // End of object specific switch + + }break; + + } // End of attribute switch + + return -1; + } + + + /*********************************************************************************************** + * Script_CacheDefaultAttribute - Caches an attribute value upon first usage for restoration * + * * + * SCRIPT INPUT: input_rtti (RTTIType) - The RTTI type for the attrtibute * + * input_id (int) - The ID of the typeclass * + * input_attribute (int) - The attribute to cache * + * input_value (int) - The value if the cache item doesn't exist * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + void Script_CacheDefaultAttribute(RTTIType input_rtti, int input_id, MapScriptAttributeType input_attribute, int input_value) { + + bool cache_item_found = false; + for (std::vector::iterator it = Scen.mapScript->DefaultTypeAttributeCache.begin(); it != Scen.mapScript->DefaultTypeAttributeCache.end(); ++it) {//Error 2-4 + + MapScriptDefaultAttribute* cachedAttribute = (MapScriptDefaultAttribute*)(*it); + + if (input_rtti == cachedAttribute->RTTI && input_id == cachedAttribute->ID && input_attribute == cachedAttribute->attribute) { + cache_item_found = true; + break; + } } + // Cached attribute does not exist; let's cache! + if (!cache_item_found) { + + MapScriptDefaultAttribute* cachedAttribute = new MapScriptDefaultAttribute; + cachedAttribute->RTTI = input_rtti; + cachedAttribute->ID = input_id; + cachedAttribute->attribute = input_attribute; + cachedAttribute->value = input_value; + Scen.mapScript->DefaultTypeAttributeCache.push_back(cachedAttribute); + + } - return -1; } /********************************************************************************************** @@ -4744,6 +5165,7 @@ *=============================================================================================*/ void MapScript::Deinit() { + // Remove any cached objects for (int i = 0; i < ObjectCache.size(); ++i) { delete ObjectCache[i]; @@ -4751,6 +5173,92 @@ ObjectCache.clear(); + // Welcome back to the web of switches where we will restore any default + // attributes that were changed in the script + for (std::vector::iterator it = Scen.mapScript->DefaultTypeAttributeCache.begin(); it != Scen.mapScript->DefaultTypeAttributeCache.end(); ++it) {//Error 2-4 + + MapScriptDefaultAttribute* cachedAttribute = (MapScriptDefaultAttribute*)(*it); + + + + + + switch (cachedAttribute->RTTI) { + + case RTTI_BUILDINGTYPE: { + lua_getglobal(L, "SetBuildingTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_UNITTYPE: { + lua_getglobal(L, "SetUnitTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_INFANTRYTYPE: { + lua_getglobal(L, "SetInfantryTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_AIRCRAFTTYPE: { + lua_getglobal(L, "SetAircraftTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_VESSELTYPE: { + lua_getglobal(L, "SetVesselTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + + } + + + } + + + for (int index = 0; index < Warheads.Count(); index++) { + Warheads.Ptr(index)->Read_INI(RuleINI); + } + + for (int proj = 0; proj < BulletTypes.Count(); proj++) { + BulletTypes.Ptr(proj)->Read_INI(RuleINI); + } + + for (int windex = 0; windex < Weapons.Count(); windex++) { + Weapons.Ptr(windex)->Read_INI(RuleINI); + } + + for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { + UnitTypes.Ptr(uindex)->Read_INI(RuleINI); + } + + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypes.Ptr(iindex)->Read_INI(RuleINI); + } + + for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { + VesselTypes.Ptr(vindex)->Read_INI(RuleINI); + } + + for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { + AircraftTypes.Ptr(aindex)->Read_INI(RuleINI); + } + + for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { + BuildingTypes.Ptr(bindex)->Read_INI(RuleINI); + } + } /*********************************************************************************************** @@ -4776,6 +5284,7 @@ // Load file if (luaL_loadfile(L, scriptName)){ + Console_Printf(lua_tostring(L, -1)); L = NULL; return false; } @@ -4789,6 +5298,7 @@ SetLuaPath(";scripts/?.script;"); if (lua_pcall(L, 0, 0, 0)) { + Console_Printf(lua_tostring(L, -1)); L = NULL; return false; } @@ -4796,6 +5306,9 @@ // Reserve object cache memory ObjectCache.reserve(2000); + // Reserve type attribute cache memory (to restore rules after script) + DefaultTypeAttributeCache.reserve(1000); + /********************************************************************************************** * Red Alert Vanilla Actions * *=============================================================================================*/ @@ -4894,6 +5407,9 @@ lua_register(L, "SetInfantryTypeAttribute", Script_SetInfantryTypeAttribute); // Sets the attribute of an Infantry type lua_register(L, "SetVesselTypeAttribute", Script_SetVesselTypeAttribute); // Sets the attribute of a Vessel type lua_register(L, "SetAircraftTypeAttribute", Script_SetAircraftTypeAttribute); // Sets the attribute of an Aircraft type + + + //lua_register(L, "GetObjectAttribute", Script_SetAircraftTypeAttribute); // Gets the attribute of an object /********************************************************************************************** * Mission Utility Functions * diff --git a/code/redalert/mapscript.h b/code/redalert/mapscript.h index 532a61b..5548a95 100644 --- a/code/redalert/mapscript.h +++ b/code/redalert/mapscript.h @@ -1,4 +1,4 @@ -// MapScript.h +// mapscript.h // #ifndef MAPSCRIPT_H @@ -20,6 +20,15 @@ struct MapScriptObject { RTTIType RTTI; // The type of object }; +// Stores defaults of rules from before +// variables were changed within +struct MapScriptDefaultAttribute { + RTTIType RTTI; // The type of object + int ID=-1; // The index of the object in its heap + int attribute; + int value; +}; + // // MapScript Class @@ -38,6 +47,7 @@ public: void SetLuaPath(const char* input_path); std::vector ObjectCache; + std::vector DefaultTypeAttributeCache; private: lua_State* L; @@ -47,7 +57,9 @@ private: // Enums typedef enum MapScriptAttributeType : char { - ATTRIBUTE_NONE=0, + + // Techno types + ATTRIBUTE_TYPE=0, ATTRIBUTE_STRENGTH, ATTRIBUTE_MAX_STRENGTH, ATTRIBUTE_COST, @@ -60,15 +72,39 @@ typedef enum MapScriptAttributeType : char { ATTRIBUTE_MAX_SPEED, ATTRIBUTE_MAX_AMMO, ATTRIBUTE_ROTATION_SPEED, - ATTRIBUTE_IS_CRUSHABLE, - ATTRIBUTE_IS_STEALTHY, - ATTRIBUTE_IS_SELECTABLE, - ATTRIBUTE_IS_LEGAL_TARGET, - ATTRIBUTE_IS_REPAIRABLE, - ATTRIBUTE_IS_SELF_HEALING, - ATTRIBUTE_IS_EXPLODING, + ATTRIBUTE_CRUSHABLE, + ATTRIBUTE_STEALTHY, + ATTRIBUTE_SELECTABLE, + ATTRIBUTE_LEGAL_TARGET, + ATTRIBUTE_REPAIRABLE, + ATTRIBUTE_SELF_HEALING, + ATTRIBUTE_EXPLODES, ATTRIBUTE_HAS_CREW, - ATTRIBUTE_MAX_PASSENGERS + ATTRIBUTE_MAX_PASSENGERS, + ATTRIBUTE_IS_REPAIRING, + + // Building Specific + ATTRIBUTE_CAPACITY, + ATTRIBUTE_POWER, + ATTRIBUTE_SELLABLE, + + // Unit Specific + ATTRIBUTE_CRATE_GOODIE, + ATTRIBUTE_CRUSHER, + ATTRIBUTE_HARVEST, + ATTRIBUTE_RADAR_EQUIPPED, + ATTRIBUTE_JAMMER, + ATTRIBUTE_GAPPER, + + // Infantry Specific + ATTRIBUTE_FEMALE, + ATTRIBUTE_CAPTURE, + ATTRIBUTE_FRAIDY_CAT, + ATTRIBUTE_CIVILIAN, + ATTRIBUTE_BOMBER, + ATTRIBUTE_DOG + + } MapScriptAttributeType; // Non-Class-Functions @@ -83,5 +119,6 @@ int Script_VesselIndexFromID(int input_object_id); static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptAttributeType attribute_type); +void Script_CacheDefaultAttribute(RTTIType input_rtti, int input_id, MapScriptAttributeType input_attribute, int input_value); #endif \ No newline at end of file diff --git a/scripts/_Global.script b/scripts/_Global.script index 230c731..f848203 100644 --- a/scripts/_Global.script +++ b/scripts/_Global.script @@ -4,115 +4,148 @@ -- * * -- *=======================================================* + -- ********************************************************* + -- * * + -- * Houses (players) * + -- * * + -- ********************************************************* + Houses={ + SPAIN=0, -- YELLOW/GOLD + GREECE=1, -- BLUE-GREY + USSR=2, -- RED + ENGLAND=3, -- GREEN + UKRAINE=4, -- ORANGE + GERMANY=5, -- KHAKI/LIGHT BROWN + FRANCE=6, -- AQUA + TURKEY=7, -- RED-OCHRE + GOODGUY=8, -- BLUE-GREY + BADGUY=9, -- RED + NEUTRAL=10, -- YELLOW/GOLD + SPECIAL=11, -- YELLOW/GOLD + MULTI1=12, -- YELLOW/GOLD + MULTI2=13, -- BLUE-GREY + MULTI3=14, -- RED + MULTI4=15, -- GREEN + MULTI5=16, -- ORANGE + MULTI6=17, -- KHAKI/LIGHT BROWN + MULTI7=18, -- AQUA + MULTI8=19 -- RED-OCHRE + } -- ********************************************************* -- * * -- * Buildings * -- * * -- ********************************************************* - - BUILDING_ATEK=0 -- Allied Technology Centre - BUILDING_IRON=1 -- Iron Curtain - BUILDING_WEAP=2 -- Weapons Factory - BUILDING_PDOX=3 -- Chronosphere - BUILDING_PBOX=4 -- Pillbox - BUILDING_HBOX=5 -- Camouflaged Pillbox - BUILDING_DOME=6 -- Radar Dome - BUILDING_GAP =7 -- Gap Generator - BUILDING_GUN =8 -- Gun Turret - BUILDING_AGUN=9 -- Anti-Aircraft Gun - BUILDING_FTUR=10 -- Flame Turret - BUILDING_FACT=11 -- Construction Yard - BUILDING_PROC=12 -- Ore Refinery - BUILDING_SILO=13 -- Ore Silo - BUILDING_HPAD=14 -- Helicopter Pad - BUILDING_SAM =15 -- SAM Site - BUILDING_AFLD=16 -- Airfield - BUILDING_POWR=17 -- Power Plant - BUILDING_APWR=18 -- Advanced Power Plant - BUILDING_STEK=19 -- Soviet Technology Centre - BUILDING_HOSP=20 -- Hospital - BUILDING_BARR=21 -- Barracks (Allied) - BUILDING_TENT=22 -- Barracks (Soviet) - BUILDING_KENN=23 -- Dog Kennel - BUILDING_FIX =24 -- Service Depot - BUILDING_BIO =25 -- Bio-Research Laboratory - BUILDING_MISS=26 -- Technology Centre/Prison - BUILDING_SYRD=27 -- Ship Yard - BUILDING_SPEN=28 -- Sub Pen - BUILDING_MSLO=29 -- Missile Silo - BUILDING_FCOM=30 -- Forward Command Post - BUILDING_TSLA=31 -- Tesla Coil - BUILDING_WEAF=32 -- Fake Weapons Factory - BUILDING_FACF=33 -- Fake Construction Yard - BUILDING_SYRF=34 -- Fake Ship Yard - BUILDING_SPEF=35 -- Fake Sub Pen - BUILDING_DOMF=36 -- Fake Radar Dome - BUILDING_MINV=43 -- Anti-vehicle mine - BUILDING_MINP=44 -- Anti-personnel mine + + BuildingTypes={ + ATEK=0, -- Allied Technology Centre + IRON=1, -- Iron Curtain + WEAP=2, -- Weapons Factory + PDOX=3, -- Chronosphere + PBOX=4, -- Pillbox + HBOX=5, -- Camouflaged Pillbox + DOME=6, -- Radar Dome + GAP =7, -- Gap Generator + GUN =8, -- Gun Turret + AGUN=9, -- Anti-Aircraft Gun + FTUR=10, -- Flame Turret + FACT=11, -- Construction Yard + PROC=12, -- Ore Refinery + SILO=13, -- Ore Silo + HPAD=14, -- Helicopter Pad + SAM =15, -- SAM Site + AFLD=16, -- Airfield + POWR=17, -- Power Plant + APWR=18, -- Advanced Power Plant + STEK=19, -- Soviet Technology Centre + HOSP=20, -- Hospital + BARR=21, -- Barracks (Allied) + TENT=22, -- Barracks (Soviet) + KENN=23, -- Dog Kennel + FIX =24, -- Service Depot + BIO =25, -- Bio-Research Laboratory + MISS=26, -- Technology Centre/Prison + SYRD=27, -- Ship Yard + SPEN=28, -- Sub Pen + MSLO=29, -- Missile Silo + FCOM=30, -- Forward Command Post + TSLA=31, -- Tesla Coil + WEAF=32, -- Fake Weapons Factory + FACF=33, -- Fake Construction Yard + SYRF=34, -- Fake Ship Yard + SPEF=35, -- Fake Sub Pen + DOMF=36, -- Fake Radar Dome + MINV=43, -- Anti-vehicle mine + MINP=44, -- Anti-personnel mine -- Counterstrike - BUILDING_QUEE=84 -- Queen ant structure - BUILDING_LAR1=85 -- Ant - single ant larva - BUILDING_LAR2=86 -- Ant - two ant larvae - + QUEE=84, -- Queen ant structure + LAR1=85, -- Ant - single ant larva + LAR2=86 -- Ant - two ant larvae + } + -- ********************************************************* -- * * -- * Infantry * -- * * -- ********************************************************* - INFANTRY_E1=0 -- Rifle Infantry - INFANTRY_E2=1 -- Grenadier - INFANTRY_E3=2 -- Rocket Soldier - INFANTRY_E4=3 -- Flamethrower - INFANTRY_E6=4 -- Engineer - INFANTRY_E7=5 -- Tanya - INFANTRY_SPY=6 -- Spy - INFANTRY_THF=7 -- Thief - INFANTRY_MEDI=8 -- Field Medic - INFANTRY_GNRL=9 -- General - INFANTRY_DOG=10 -- Attack Dog - INFANTRY_C1=11 -- Joe - INFANTRY_C2=12 -- Barry - INFANTRY_C3=13 -- Shelly - INFANTRY_C4=14 -- Maria - INFANTRY_C5=15 -- Karen - INFANTRY_C6=16 -- Steve - INFANTRY_C7=17 -- Phil - INFANTRY_C8=18 -- Dwight - INFANTRY_C9=19 -- Erik - INFANTRY_C10=20 -- Scientist - INFANTRY_EINSTEIN=21 -- Prof. Einstein - INFANTRY_DELPHI=22 -- Special 1 - INFANTRY_CHAN=23 -- Special 2 - + InfantryTypes={ + E1=0, -- Rifle Infantry + E2=1, -- Grenadier + E3=2, -- Rocket Soldier + E4=3, -- Flamethrower + E6=4, -- Engineer + E7=5, -- Tanya + SPY=6, -- Spy + THF=7, -- Thief + MEDI=8, -- Field Medic + GNRL=9, -- General + DOG=10, -- Attack Dog + C1=11, -- Joe + C2=12, -- Barry + C3=13, -- Shelly + C4=14, -- Maria + C5=15, -- Karen + C6=16, -- Steve + C7=17, -- Phil + C8=18, -- Dwight + C9=19, -- Erik + C10=20, -- Scientist + EINSTEIN=21, -- Prof. Einstein + DELPHI=22, -- Special 1 + CHAN=23 -- Special 2 + } + -- ********************************************************* -- * * -- * Vehicle * -- * * -- ********************************************************* - VEHICLE_4TNK=0 -- Mammoth Tank - VEHICLE_3TNK=1 -- Heavy Tank - VEHICLE_2TNK=2 -- Medium Tank - VEHICLE_1TNK=3 -- Light Tank - VEHICLE_APC=4 -- Armoured Personnel Carrier - VEHICLE_MNLY=5 -- Mine Layer - VEHICLE_JEEP=6 -- Ranger - VEHICLE_HARV=7 -- Ore Truck - VEHICLE_ARTY=8 -- Artillery - VEHICLE_MRJ=9 -- Mobile Radar Jammer - VEHICLE_MGG=10 -- Mobile Gap Generator - VEHICLE_MCV=11 -- Mobile Construction Vehicle - VEHICLE_2VRL=12 -- V2 Rocket Launcher - VEHICLE_TRUK=13 -- Convoy Truck + VehicleTypes={ + _4TNK=0, -- Mammoth Tank + _3TNK=1, -- Heavy Tank + _2TNK=2, -- Medium Tank + _1TNK=3, -- Light Tank + APC=4, -- Armoured Personnel Carrier + MNLY=5, -- Mine Layer + JEEP=6, -- Ranger + HARV=7, -- Ore Truck + ARTY=8, -- Artillery + MRJ=9, -- Mobile Radar Jammer + MGG=10, -- Mobile Gap Generator + MCV=11, -- Mobile Construction Vehicle + _2VRL=12, -- V2 Rocket Launcher + TRUK=13, -- Convoy Truck -- Counterstrike only: - VEHICLE_ANT1=14 -- First ant type - VEHICLE_ANT2=15 -- Second ant type - VEHICLE_ANT3=16 -- Third ant type + ANT1=14, -- First ant type + ANT2=15, -- Second ant type + ANT3=16 -- Third ant type + } -- ********************************************************* -- * * @@ -120,159 +153,183 @@ -- * * -- ********************************************************* - AIRCRAFT_TRAN=0 -- Chinook Helicopter - AIRCRAFT_BADR=1 -- Badger Bomber - AIRCRAFT_U2=2 -- Spy Plane - AIRCRAFT_MIG=3 -- MIG Attack Plane - AIRCRAFT_YAK=4 -- Yak Attack Plane - AIRCRAFT_HELI=5 -- Longbow Helicopter - AIRCRAFT_HIND=6 -- Hind Helicopter - - -- ********************************************************* - -- * * - -- * Houses (players) * - -- * * - -- ********************************************************* - - HOUSE_SPAIN= 0 -- YELLOW/GOLD - HOUSE_GREECE=1 -- BLUE-GREY - HOUSE_USSR=2 -- RED - HOUSE_ENGLAND=3 -- GREEN - HOUSE_UKRAINE=4 -- ORANGE - HOUSE_GERMANY=5 -- KHAKI/LIGHT BROWN - HOUSE_FRANCE=6 -- AQUA - HOUSE_TURKEY=7 -- RED-OCHRE - HOUSE_GOODGUY=8 -- BLUE-GREY - HOUSE_BADGUY=9 -- RED - HOUSE_NEUTRAL=10 -- YELLOW/GOLD - HOUSE_SPECIAL=11 -- YELLOW/GOLD - HOUSE_MULTI1=12 -- YELLOW/GOLD - HOUSE_MULTI2=13 -- BLUE-GREY - HOUSE_MULTI3=14 -- RED - HOUSE_MULTI4=15 -- GREEN - HOUSE_MULTI5=16 -- ORANGE - HOUSE_MULTI6=17 -- KHAKI/LIGHT BROWN - HOUSE_MULTI7=18 -- AQUA - HOUSE_MULTI8=19 -- RED-OCHRE - - -- ********************************************************* - -- * * - -- * Special weapons * - -- * * - -- ********************************************************* - SPECIAL_WEAPON_SONAR=0 - SPECIAL_WEAPON_NUKE=1 - SPECIAL_WEAPON_CHRONO=2 - SPECIAL_WEAPON_PARABOMB=3 - SPECIAL_WEAPON_PARATROOPS=4 - SPECIAL_WEAPON_SPY_PLANE=5 - SPECIAL_WEAPON_IRON_CURTAIN=6 - SPECIAL_WEAPON_GPS=7 - - -- ********************************************************* - -- * * - -- * Target types (for use with DesignatePreferredTarget) * - -- * * - -- ********************************************************* - TARGET_TYPE_ANYTHING=1 - TARGET_TYPE_BUILDINGS=2 - TARGET_TYPE_HARVESTERS=3 - TARGET_TYPE_INFANTRY=4 - TARGET_TYPE_VEHICLES=5 - TARGET_TYPE_SHIPS=6 - TARGET_TYPE_FACTORIES=7 - TARGET_TYPE_BASE_DEFENCES=8 - TARGET_TYPE_BASE_THREATS=9 - TARGET_TYPE_POWER=10 - TARGET_TYPE_FAKE_BUILDINGS=11 - - -- ********************************************************* - -- * * - -- * Trigger persistance * - -- * * - -- ********************************************************* - TRIGGER_VOLATILE=0 - TRIGGER_SEMI_PERSISTENT=1 - TRIGGER_PERSISTENT=2 - - -- ********************************************************* - -- * * - -- * Attribite Types * - -- * * - -- ********************************************************* - ATTRIBUTE_NONE=0 - ATTRIBUTE_STRENGTH=1 -- Object attribute - ATTRIBUTE_MAX_STRENGTH=2 -- Definition attribute - ATTRIBUTE_COST=3 -- Definition attribute - ATTRIBUTE_SIGHT_RANGE=4 -- Definition attribute - ATTRIBUTE_TECH_LEVEL=5 -- Definition attribute - ATTRIBUTE_ARMOR=6 - ATTRIBUTE_PRIMARY_WEAPON=7 - ATTRIBUTE_SECONDARY_WEAPON=8 - ATTRIBUTE_SPEED=9 - ATTRIBUTE_MAX_SPEED=10 - ATTRIBUTE_MAX_AMMO=11 - ATTRIBUTE_ROTATION_SPEED=12 - ATTRIBUTE_IS_CRUSHABLE=13 - ATTRIBUTE_IS_STEALTHY=14 - ATTRIBUTE_IS_SELECTABLE=15 - ATTRIBUTE_IS_LEGAL_TARGET=16 - ATTRIBUTE_IS_REPAIRABLE=17 - ATTRIBUTE_IS_SELF_HEALING=18 - ATTRIBUTE_IS_EXPLODING=19 - ATTRIBUTE_HAS_CREW=20 - ATTRIBUTE_MAX_PASSENGERS=21 - + AircraftTypes={ + TRAN=0, -- Chinook Helicopter + BADR=1, -- Badger Bomber + U2=2, -- Spy Plane + MIG=3, -- MIG Attack Plane + YAK=4, -- Yak Attack Plane + HELI=5, -- Longbow Helicopter + HIND=6, -- Hind Helicopter + } + -- ********************************************************* -- * * -- * Armor Types * -- * * -- ********************************************************* - ARMOR_NONE=0 - ARMOR_WOOD=1 - ARMOR_ALUMINUM=2 - ARMOR_STEEL=3 - ARMOR_CONCRETE=4 - + ArmorTypes={ + NONE=0, + WOOD=1, + ALUMINUM=2, + STEEL=3, + CONCRETE=4 + } + -- ********************************************************* -- * * -- * Weapon Types * -- * * -- ********************************************************* - WEAPON_COLT45=0 - WEAPON_ACK_ACK=1 - WEAPON_VULCAN=2 - WEAPON_MAVERICK=3 - WEAPON_CAMERA=4 - WEAPON_FIREBALL=5 - WEAPON_RIFLE=6 - WEAPON_CHAIN_GUN=7 - WEAPON_PISTOL=8 - WEAPON_M16=9 - WEAPON_DRAGON=10 - WEAPON_HELLFIRE=11 - WEAPON_GRENADE=12 - WEAPON_75MM=13 - WEAPON_90MM=14 - WEAPON_105MM=15 - WEAPON_120MM=16 - WEAPON_TURRET_GUN=17 - WEAPON_MAMMOTH_TUSK=18 - WEAPON_155MM=19 - WEAPON_M60MG=20 - WEAPON_NAPALM=21 - WEAPON_TESLA_ZAP=22 - WEAPON_NIKE=23 - WEAPON_8INCH=24 - WEAPON_STINGER=25 - WEAPON_TORPEDO=26 - WEAPON_2INCH=27 - WEAPON_DEPTH_CHARGE=28 - WEAPON_PARA_BOMB=29 - WEAPON_DOGJAW=30 - WEAPON_HEAL=31 - WEAPON_SCUD=32 - WEAPON_FLAMER=33 - WEAPON_REDEYE=34 + WeaponTypes={ + NONE=-1, + COLT45=0, + ACK_ACK=1, + VULCAN=2, + MAVERICK=3, + CAMERA=4, + FIREBALL=5, + RIFLE=6, + CHAIN_GUN=7, + PISTOL=8, + M16=9, + DRAGON=10, + HELLFIRE=11, + GRENADE=12, + _75MM=13, + _90MM=14, + _105MM=15, + _120MM=16, + TURRET_GUN=17, + MAMMOTH_TUSK=18, + _155MM=19, + M60MG=20, + NAPALM=21, + TESLA_ZAP=22, + NIKE=23, + _8INCH=24, + STINGER=25, + TORPEDO=26, + _2INCH=27, + DEPTH_CHARGE=28, + PARA_BOMB=29, + DOGJAW=30, + HEAL=31, + SCUD=32, + FLAMER=33, + REDEYE=34 + } + + + + -- ********************************************************* + -- * * + -- * Special weapons * + -- * * + -- ********************************************************* + SpecialWeapons={ + Sonar=0, + Nuke=1, + Chrono=2, + Parabomb=3, + Paratroops=4, + SpyPlane=5, + IronCurtain=6, + GPS=7 + } + + -- ********************************************************* + -- * * + -- * Target types (for use with DesignatePreferredTarget) * + -- * * + -- ********************************************************* + TargetTypes={ + Anything=1, + Buildings=2, + Harvesters=3, + Infantry=4, + Vehicles=5, + Ships=6, + Factories=7, + BaseDefences=8, + BaseThreats=9, + Power=10, + FakeBuildings=11 + } + + -- ********************************************************* + -- * * + -- * Trigger persistance * + -- * * + -- ********************************************************* + PersistenceTypes={ + Volatile=0, + SemiPersistent=1, + Persistent=2 + } + + -- ********************************************************* + -- * * + -- * Attribite Types * + -- * * + -- ********************************************************* + Attributes = { + Type=0, + Strength=1, -- Object attribute + MaxStrength=2, -- Definition attribute + Cost=3, -- Definition attribute + SightRange=4, -- Definition attribute + TechLevel=5, -- Definition attribute + Armor=6, + PrimaryWeapon=7, + SecondaryWeapon=8, + Speed=9, + MaxSpeed=10, + MaxAmmo=11, + RotationSpeed=12, + IsCrushable=13, + IsStealthy=14, + IsSelectable=15, + IsLegalTarget=16, + IsRepairable=17, + IsSelfHealing=18, + Explodes=19, + HasCrew=20, + MaxPassengers=21, + IsRepairing=22, + + -- Building Specific + Capacity=23, + Power=24, + Sellable=25, - \ No newline at end of file + -- Unit Specific + IsCrateGoodie=26, + IsCrusher=27, + IsHarvest=28, + IsRadarEquipped=29, + IsJammer=30, + IsGapper=31, + + -- Infantry Specific + IsFemale=32, + Capture=33, + IsFraidyCat=34, + IsCivilian=35, + IsBomber=36, + IsDog=37 + + } + + -- ********************************************************* + -- * * + -- * Fear Type Attribute Values * + -- * * + -- ********************************************************* + FearTypes={ + None=0, -- No fear at all (default state). + Anxious=10, -- Something makes them scared. + Scared=100, -- Scared enough to take cover. + Panic=200, -- Run away! Run away! + Maximum=255 -- Scared to death. + } \ No newline at end of file From 678d78400424c3a99468defeca5c136035755979 Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sun, 28 Jun 2020 15:35:10 -0600 Subject: [PATCH 09/18] Added To_Float to fixed point class. This is to help with certain house values to/from Lua but may have other uses. Added ability to create objects out of thin air, and force factories to produce units. Added ability to issue movement commands to units. Updated object cache system to use an incremental UID for retrieval / passing ID back to the script, as ID collisions were occuring. --- code/redalert/fixed.cpp | 29 + code/redalert/fixed.h | 5 + code/redalert/mapscript.cpp | 2493 ++++++++++++++++++++++++++++++----- code/redalert/mapscript.h | 66 +- scripts/_Global.script | 64 +- 5 files changed, 2302 insertions(+), 355 deletions(-) diff --git a/code/redalert/fixed.cpp b/code/redalert/fixed.cpp index e8add29..abf18cd 100644 --- a/code/redalert/fixed.cpp +++ b/code/redalert/fixed.cpp @@ -32,6 +32,7 @@ * Functions: * * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * fixed::To_Float -- Convert a fixed point number into a float value * * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ @@ -40,6 +41,7 @@ #include #include #include +#include /* @@ -148,6 +150,33 @@ fixed::fixed(char const * ascii) } +/*********************************************************************************************** + * fixed::To_Float -- Convert a fixed point number into a float * + * * + * Use this routine to convert this fixed point number into a float. This can be lossy for * + * fine values. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the float value of this fixed point number * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/2020 JJ : Created. * + *=============================================================================================*/ +float fixed::To_Float() const +{ + + unsigned int whole = Data.Composite.Whole; + unsigned int frac = ((unsigned int)Data.Composite.Fraction * 1000U) / PRECISION; + + float this_raw_value = (whole + (frac * .001)); + + return floor(this_raw_value * 100.0 + 0.5) / 100.0; + +} + /*********************************************************************************************** * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * * * diff --git a/code/redalert/fixed.h b/code/redalert/fixed.h index b141508..3b2ec81 100644 --- a/code/redalert/fixed.h +++ b/code/redalert/fixed.h @@ -206,6 +206,11 @@ class fixed int To_ASCII(char * buffer, int maxlen=-1) const; char const * As_ASCII(void) const; + /* + ** Helper function to convert fixed point number to float (JJ) + */ + float To_Float() const; + /* ** Helper constants that provide some convenient fixed point values. */ diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp index 807fe15..1489e7f 100644 --- a/code/redalert/mapscript.cpp +++ b/code/redalert/mapscript.cpp @@ -3024,7 +3024,7 @@ return 1; } - MapScriptObject* CacheObject = new MapScriptObject; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); CacheObject->ID = this_object->ID; CacheObject->RTTI = this_object->RTTI; @@ -3054,7 +3054,7 @@ } else { Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->ID); // Return index for ultrafast lookup + lua_pushnumber(L, CacheObject->UID); // Return UID for faster lookup } return 1; @@ -3277,7 +3277,7 @@ *=============================================================================================*/ /********************************************************************************************** - * Script_SetObjectAttribute - Sets attributes for specific objects * + * Script_SetObjectAttribute - Sets attributes for specific objects or object types * * * * SCRIPT INPUT: objectID (int) - The ID of the object * * attribute_type (int) - The attribute to change * @@ -3299,15 +3299,11 @@ ObjectClass* this_object = Script_GetCacheObject(objectID); - - // Get the appropriate technotype if (this_object != NULL) { - - // Global attributes / Multi object attributes - switch ((MapScriptAttributeType)attribute_type) { + switch ((MapScriptObjectAttributeType)attribute_type) { case ATTRIBUTE_TYPE: { }break; @@ -3373,7 +3369,7 @@ if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } }break; @@ -3410,7 +3406,7 @@ TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } return -1; @@ -3440,7 +3436,7 @@ TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } return -1; @@ -3470,7 +3466,7 @@ TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } return -1; @@ -3500,7 +3496,7 @@ TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } return -1; @@ -3530,343 +3526,1025 @@ TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptAttributeType)attribute_type); + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); } return -1; } - + /********************************************************************************************** * Script_SetObjectTypeAttribute - Sets attributes for specific object types globally * * * * Used internally by MapScript to retrieve input and set attributes for TechnoTypeClass * * * * SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * - * attribute_type (MapScriptAttributeType) - The attribute to change * + * attribute_type (MapScriptObjectAttributeType) - The attribute to change * * * * SCRIPT OUTPUT: void * * * * WARNINGS: ? * * * *=============================================================================================*/ - static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptAttributeType attribute_type) { + static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { + + switch (attribute_type) { + case ATTRIBUTE_MAX_STRENGTH: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxStrength); + + // Set the attribute + this_type_class->MaxStrength = attribute_value; + + }break; + + case ATTRIBUTE_COST: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Cost); + + // Set the attribute + this_type_class->Cost = attribute_value; + + }break; + case ATTRIBUTE_SIGHT_RANGE: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SightRange); + + // Set the attribute + this_type_class->SightRange = attribute_value; + + }break; + case ATTRIBUTE_TECH_LEVEL: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Level); + + // Set the attribute + this_type_class->Level = attribute_value; + + }break; + case ATTRIBUTE_ARMOR: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Armor); + + // Set the attribute + this_type_class->Armor = (ArmorType)attribute_value; + + }break; + case ATTRIBUTE_PRIMARY_WEAPON: { + + int attribute_value = lua_tointeger(L, 3); + + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->PrimaryWeapon->ID); + } + else { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } + + // Set the attribute + if (attribute_value < 0) { + this_type_class->PrimaryWeapon = NULL; + } + else { + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->PrimaryWeapon = this_weapon; + } + else { + this_type_class->PrimaryWeapon = NULL; + } + } + + }break; + case ATTRIBUTE_SECONDARY_WEAPON: { + + int attribute_value = lua_tointeger(L, 3); + + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SecondaryWeapon->ID); + } + else { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } + + // Set the attribute + if (attribute_value < 0) { + this_type_class->SecondaryWeapon = NULL; + } + else { + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->SecondaryWeapon = this_weapon; + } + else { + this_type_class->SecondaryWeapon = NULL; + } + } + + }break; + case ATTRIBUTE_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Speed); + + // Set the attribute + this_type_class->Speed = (SpeedType)attribute_value; + + }break; + case ATTRIBUTE_MAX_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxSpeed); + + // Set the attribute + this_type_class->MaxSpeed = (MPHType)attribute_value; + + }break; + case ATTRIBUTE_MAX_AMMO: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxAmmo); + + // Set the attribute + this_type_class->MaxAmmo = attribute_value; + + }break; + case ATTRIBUTE_ROTATION_SPEED: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->ROT); + + // Set the attribute + this_type_class->ROT = attribute_value; + + }break; + case ATTRIBUTE_CRUSHABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrushable); + + // Set the attribute + this_type_class->IsCrushable = attribute_value; + + }break; + case ATTRIBUTE_STEALTHY: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsStealthy); + + // Set the attribute + this_type_class->IsStealthy = attribute_value; + + }break; + case ATTRIBUTE_SELECTABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelectable); + + // Set the attribute + this_type_class->IsSelectable = attribute_value; + + }break; + case ATTRIBUTE_LEGAL_TARGET: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsLegalTarget); + + // Set the attribute + this_type_class->IsLegalTarget = attribute_value; + + }break; + case ATTRIBUTE_REPAIRABLE: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsRepairable); + + // Set the attribute + this_type_class->IsRepairable = attribute_value; + + }break; + case ATTRIBUTE_SELF_HEALING: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelfHealing); + + // Set the attribute + this_type_class->IsSelfHealing = attribute_value; + + }break; + case ATTRIBUTE_EXPLODES: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsExploding); + + // Set the attribute + this_type_class->IsExploding = attribute_value; + + }break; + case ATTRIBUTE_HAS_CREW: { + + unsigned int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrew); + + // Set the attribute + this_type_class->IsCrew = attribute_value; + + }break; + case ATTRIBUTE_MAX_PASSENGERS: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxPassengers); + + // Set the attribute + this_type_class->MaxPassengers = attribute_value; + + }break; + default: { + + // Object class specific attributes + // Welcome to the web of switches] + + // Type of object + switch (this_type_class->RTTI) { + + // Building Specific + case RTTI_BUILDINGTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CAPACITY: { + + int attribute_value = lua_tointeger(L, 3); + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Capacity); + + // Set the attribute + this_building_class->Capacity = attribute_value; + + }break; + case ATTRIBUTE_POWER: { + + int attribute_value = lua_tointeger(L, 3); + + // Both power and drain are different variables + // And so we do some trickery here to fill + // the right values based on postivie/negative input + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Power - this_building_class->Drain); + + // Set the attribute + if (attribute_value > 0) { + this_building_class->Power = attribute_value; + this_building_class->Drain = 0; + } + else { + this_building_class->Power = 0; + this_building_class->Drain = attribute_value * -1; // Flip negative power input + } + + }break; + case ATTRIBUTE_SELLABLE: { + + int attribute_value = lua_tointeger(L, 3); + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->IsUnsellable * -1); + + // Set the attribute + // Personal opinion; sellable makes more sense than unsellable + // This replacement could be seen as IsUnNonSellable, or... IsSellable + if (attribute_value == 1) { + this_building_class->IsUnsellable = 0; + } + else { + this_building_class->IsUnsellable = 1; + } + + }break; + } + + }break; + + + // Unit Specific + case RTTI_UNITTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CRATE_GOODIE: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrateGoodie); + + // Set the attribute + this_unit_class->IsCrateGoodie = attribute_value; + + }break; + case ATTRIBUTE_CRUSHER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrusher); + + // Set the attribute + this_unit_class->IsCrusher = attribute_value; + + }break; + case ATTRIBUTE_HARVEST: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsToHarvest); + + // Set the attribute + this_unit_class->IsToHarvest = attribute_value; + + }break; + case ATTRIBUTE_RADAR_EQUIPPED: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsRadarEquipped); + + // Set the attribute + this_unit_class->IsRadarEquipped = attribute_value; + + }break; + case ATTRIBUTE_JAMMER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsJammer); + + // Set the attribute + this_unit_class->IsJammer = attribute_value; + + }break; + case ATTRIBUTE_GAPPER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsGapper); + + // Set the attribute + this_unit_class->IsGapper = attribute_value; + + }break; + } + + }break; + + + // Infantry Specific + case RTTI_INFANTRYTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_FEMALE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFemale); + + // Set the attribute + this_infantry_class->IsFemale = attribute_value; + + }break; + case ATTRIBUTE_CAPTURE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCapture); + + // Set the attribute + this_infantry_class->IsCapture = attribute_value; + + }break; + case ATTRIBUTE_FRAIDY_CAT: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFraidyCat); + + // Set the attribute + this_infantry_class->IsFraidyCat = attribute_value; + + }break; + case ATTRIBUTE_CIVILIAN: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCivilian); + + // Set the attribute + this_infantry_class->IsCivilian = attribute_value; + + }break; + case ATTRIBUTE_BOMBER: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsBomber); + + // Set the attribute + this_infantry_class->IsBomber = attribute_value; + + }break; + case ATTRIBUTE_DOG: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsDog); + + // Set the attribute + this_infantry_class->IsDog = attribute_value; + + }break; + + } + + }break; + + } // End of object specific switch + + }break; + + } // End of attribute switch + + return -1; + } + + /********************************************************************************************** + * Script_GetObjectAttribute - Gets attributes for specific objects or object types * + * * + * SCRIPT INPUT: objectID (int) - The ID of the object * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetObjectAttribute(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + // Get the appropriate technotype + if (this_object != NULL) { + + // Global attributes / Multi object attributes + switch ((MapScriptObjectAttributeType)attribute_type) { + case ATTRIBUTE_TYPE: { + + // Send the attribute + lua_pushinteger(L, this_object->RTTI); + return 1; + + }break; + case ATTRIBUTE_STRENGTH: { + + // Send the attribute + lua_pushinteger(L, this_object->Strength); + return 1; + + }break; + + case ATTRIBUTE_IS_REPAIRING: { + + switch (this_object->RTTI) { + case RTTI_BUILDING: { + + BuildingClass* this_building = (BuildingClass*)this_object; + + // Send the attribute + lua_pushinteger(L, this_building->IsRepairing); + return 1; + + }break; + case RTTI_VESSEL: { + + VesselClass* this_vessel = (VesselClass*)this_object; + + // Send the attribute + lua_pushinteger(L, this_vessel->IsSelfRepairing); + return 1; + + }break; + } + }break; + + // Object Class specific + default: { + + TechnoTypeClass* this_type_class = NULL; + + switch (this_object->RTTI) { + case RTTI_BUILDING: + + this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_UNIT: + + this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_INFANTRY: + + this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_VESSEL: + + this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_AIRCRAFT: + + this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); + + break; + } + + // Type attribute retrieval + if (this_type_class != NULL) { + return Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + }break; + + } + + } + + + return -1; + } + + /********************************************************************************************** + * Script_GetBuildingTypeAttribute - Gets attributes for a building types * + * * + * SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetBuildingTypeAttribute(lua_State* L) { + + int buildingTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_GetVehicleTypeAttribute - Gets attributes for vehicle types * + * * + * SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetVehicleTypeAttribute(lua_State* L) { + + int vehicleTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_GetInfantryTypeAttribute - Gets attributes for infantry types * + * * + * SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetInfantryTypeAttribute(lua_State* L) { + + int infantryTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_GetVesselTypeAttribute - Gets attributes for vessel types * + * * + * SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetVesselTypeAttribute(lua_State* L) { + + int vesselTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_GetAircraftTypeAttribute - Gets attributes for aircraft types * + * * + * SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetAircraftTypeAttribute(lua_State* L) { + + int aircraftTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; + } + + /********************************************************************************************** + * Script_GetObjectTypeAttribute - Gets attributes for specific object types globally * + * * + * Used internally by MapScript to send input and get attributes for TechnoTypeClass * + * * + * SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * + * attribute_type (MapScriptObjectAttributeType) - The attribute to change * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { switch (attribute_type) { case ATTRIBUTE_MAX_STRENGTH: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxStrength); - - // Set the attribute - this_type_class->MaxStrength = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->MaxStrength); + return 1; }break; case ATTRIBUTE_COST: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Cost); - - // Set the attribute - this_type_class->Cost = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->Cost); + return 1; }break; case ATTRIBUTE_SIGHT_RANGE: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SightRange); - - // Set the attribute - this_type_class->SightRange = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->SightRange); + return 1; }break; case ATTRIBUTE_TECH_LEVEL: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Level); - - // Set the attribute - this_type_class->Level = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->Level); + return 1; }break; case ATTRIBUTE_ARMOR: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Armor); - - // Set the attribute - this_type_class->Armor = (ArmorType)attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->Armor); + return 1; }break; case ATTRIBUTE_PRIMARY_WEAPON: { - int attribute_value = lua_tointeger(L, 3); - if (this_type_class->PrimaryWeapon != NULL) { - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->PrimaryWeapon->ID); - }else { - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + // Send the attribute + lua_pushinteger(L, this_type_class->PrimaryWeapon->ID); + return 1; } - - // Set the attribute - if (attribute_value < 0) { - this_type_class->PrimaryWeapon = NULL; - }else { - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->PrimaryWeapon = this_weapon; - }else { - this_type_class->PrimaryWeapon = NULL; - } + else { + // Send the attribute + lua_pushinteger(L, -1); + return 1; } }break; case ATTRIBUTE_SECONDARY_WEAPON: { - int attribute_value = lua_tointeger(L, 3); - - if (this_type_class->PrimaryWeapon != NULL) { - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SecondaryWeapon->ID); - }else { - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + if (this_type_class->SecondaryWeapon != NULL) { + // Send the attribute + lua_pushinteger(L, this_type_class->SecondaryWeapon->ID); + return 1; } - - // Set the attribute - if (attribute_value < 0) { - this_type_class->SecondaryWeapon = NULL; - }else{ - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->SecondaryWeapon = this_weapon; - }else { - this_type_class->SecondaryWeapon = NULL; - } + else { + // Send the attribute + lua_pushinteger(L, -1); + return 1; } }break; case ATTRIBUTE_SPEED: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Speed); - - // Set the attribute - this_type_class->Speed = (SpeedType)attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->Speed); + return 1; }break; case ATTRIBUTE_MAX_SPEED: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxSpeed); - - // Set the attribute - this_type_class->MaxSpeed = (MPHType)attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->MaxSpeed); + return 1; }break; case ATTRIBUTE_MAX_AMMO: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxAmmo); - - // Set the attribute - this_type_class->MaxAmmo = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->MaxAmmo); + return 1; }break; case ATTRIBUTE_ROTATION_SPEED: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->ROT); - - // Set the attribute - this_type_class->ROT = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->ROT); + return 1; }break; case ATTRIBUTE_CRUSHABLE: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrushable); - - // Set the attribute - this_type_class->IsCrushable = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsCrushable); + return 1; }break; case ATTRIBUTE_STEALTHY: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsStealthy); - - // Set the attribute - this_type_class->IsStealthy = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsStealthy); + return 1; }break; case ATTRIBUTE_SELECTABLE: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelectable); - - // Set the attribute - this_type_class->IsSelectable = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsSelectable); + return 1; }break; case ATTRIBUTE_LEGAL_TARGET: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsLegalTarget); - - // Set the attribute - this_type_class->IsLegalTarget = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsLegalTarget); + return 1; }break; case ATTRIBUTE_REPAIRABLE: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsRepairable); - - // Set the attribute - this_type_class->IsRepairable = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsRepairable); + return 1; }break; case ATTRIBUTE_SELF_HEALING: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelfHealing); - - // Set the attribute - this_type_class->IsSelfHealing = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsSelfHealing); + return 1; }break; case ATTRIBUTE_EXPLODES: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsExploding); - - // Set the attribute - this_type_class->IsExploding = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsExploding); + return 1; }break; case ATTRIBUTE_HAS_CREW: { - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrew); - - // Set the attribute - this_type_class->IsCrew = attribute_value; + // Send the attribute + lua_pushboolean(L, this_type_class->IsCrew); + return 1; }break; case ATTRIBUTE_MAX_PASSENGERS: { - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxPassengers); - - // Set the attribute - this_type_class->MaxPassengers = attribute_value; + // Send the attribute + lua_pushinteger(L, this_type_class->MaxPassengers); + return 1; }break; - default:{ + default: { // Object class specific attributes // Welcome to the web of switches] - + // Type of object switch (this_type_class->RTTI) { - // Building Specific - case RTTI_BUILDINGTYPE:{ + // Building Specific + case RTTI_BUILDINGTYPE: { // What type of attribute? switch (attribute_type) { case ATTRIBUTE_CAPACITY: { - int attribute_value = lua_tointeger(L, 3); - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Capacity); - - // Set the attribute - this_building_class->Capacity = attribute_value; + // Send the attribute + lua_pushinteger(L, this_building_class->Capacity); + return 1; }break; case ATTRIBUTE_POWER: { - int attribute_value = lua_tointeger(L, 3); - // Both power and drain are different variables // And so we do some trickery here to fill // the right values based on postivie/negative input BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Power + this_building_class->Drain); + int this_value = this_building_class->Power - this_building_class->Drain; - // Set the attribute - if (attribute_value > 0) { - this_building_class->Power = attribute_value; - this_building_class->Drain = 0; - }else { - this_building_class->Power = 0; - this_building_class->Drain = attribute_value * -1; // Flip negative power input - } + // Send the attribute + lua_pushinteger(L, this_value); + return 1; }break; case ATTRIBUTE_SELLABLE: { - int attribute_value = lua_tointeger(L, 3); - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->IsUnsellable *-1); - // Set the attribute // Personal opinion; sellable makes more sense than unsellable // This replacement could be seen as IsUnNonSellable, or... IsSellable - if (attribute_value == 1) { - this_building_class->IsUnsellable = 0; - } - else { - this_building_class->IsUnsellable = 1; + if (this_building_class->IsUnsellable == 1) { + // Send the attribute + lua_pushinteger(L, 0); + return 1; + } else { + // Send the attribute + lua_pushinteger(L, 1); + return 1; } }break; @@ -3876,86 +4554,62 @@ // Unit Specific - case RTTI_UNITTYPE:{ + case RTTI_UNITTYPE: { // What type of attribute? switch (attribute_type) { case ATTRIBUTE_CRATE_GOODIE: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrateGoodie); - - // Set the attribute - this_unit_class->IsCrateGoodie = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsCrateGoodie); + return 1; }break; case ATTRIBUTE_CRUSHER: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrusher); - - // Set the attribute - this_unit_class->IsCrusher = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsCrusher); + return 1; }break; case ATTRIBUTE_HARVEST: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsToHarvest); - - // Set the attribute - this_unit_class->IsToHarvest = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsToHarvest); + return 1; }break; case ATTRIBUTE_RADAR_EQUIPPED: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsRadarEquipped); - - // Set the attribute - this_unit_class->IsRadarEquipped = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsRadarEquipped); + return 1; }break; case ATTRIBUTE_JAMMER: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsJammer); - - // Set the attribute - this_unit_class->IsJammer = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsJammer); + return 1; }break; case ATTRIBUTE_GAPPER: { - int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsGapper); - - // Set the attribute - this_unit_class->IsGapper = attribute_value; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsGapper); + return 1; }break; } @@ -3963,87 +4617,63 @@ }break; - // Infantry Specific - case RTTI_INFANTRYTYPE:{ + // Infantry Specific + case RTTI_INFANTRYTYPE: { // What type of attribute? switch (attribute_type) { case ATTRIBUTE_FEMALE: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFemale); - - // Set the attribute - this_infantry_class->IsFemale = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsFemale); + return 1; }break; case ATTRIBUTE_CAPTURE: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCapture); - - // Set the attribute - this_infantry_class->IsCapture = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsCapture); + return 1; }break; case ATTRIBUTE_FRAIDY_CAT: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFraidyCat); - - // Set the attribute - this_infantry_class->IsFraidyCat = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsFraidyCat); + return 1; }break; case ATTRIBUTE_CIVILIAN: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCivilian); - - // Set the attribute - this_infantry_class->IsCivilian = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsCivilian); + return 1; }break; case ATTRIBUTE_BOMBER: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsBomber); - - // Set the attribute - this_infantry_class->IsBomber = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsBomber); + return 1; }break; case ATTRIBUTE_DOG: { - int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - // Cache default value if first time changing this attribute - Script_CacheDefaultAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsDog); - - // Set the attribute - this_infantry_class->IsDog = attribute_value; + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsDog); + return 1; }break; @@ -4053,55 +4683,1110 @@ } // End of object specific switch - }break; + }break; } // End of attribute switch + + // Didn't find result return -1; + } + /********************************************************************************************** + * Script_SetHouseAttribute - Sets attributes for specific houses * + * * + * SCRIPT INPUT: houseID (int) - The ID of the house * + * attribute_type (int) - The attribute to change * + * attribute_value (int) - The value to set the attribute to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetHouseAttribute(lua_State* L) { - /*********************************************************************************************** - * Script_CacheDefaultAttribute - Caches an attribute value upon first usage for restoration * - * * - * SCRIPT INPUT: input_rtti (RTTIType) - The RTTI type for the attrtibute * - * input_id (int) - The ID of the typeclass * - * input_attribute (int) - The attribute to cache * - * input_value (int) - The value if the cache item doesn't exist * - * * - * SCRIPT OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - void Script_CacheDefaultAttribute(RTTIType input_rtti, int input_id, MapScriptAttributeType input_attribute, int input_value) { + int houseID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); - bool cache_item_found = false; - for (std::vector::iterator it = Scen.mapScript->DefaultTypeAttributeCache.begin(); it != Scen.mapScript->DefaultTypeAttributeCache.end(); ++it) {//Error 2-4 + HouseClass* this_house = Houses.Ptr(houseID); - MapScriptDefaultAttribute* cachedAttribute = (MapScriptDefaultAttribute*)(*it); + // Get the appropriate technotype + if (this_house != NULL) { - if (input_rtti == cachedAttribute->RTTI && input_id == cachedAttribute->ID && input_attribute == cachedAttribute->attribute) { - cache_item_found = true; - break; + switch (attribute_type) { + case HOUSE_ATTRIBUTE_IQ: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Control.IQ = attribute_value; + this_house->IQ = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_TECH_LEVEL: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Control.TechLevel = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_IS_HUMAN: { + + int attribute_value = lua_tointeger(L, 3); + + if (attribute_value == 1) { + this_house->IsHuman = 1; + this_house->WasHuman = 1; + }else { + this_house->IsHuman = 0; + } + + + }break; + case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->FirepowerBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].FirepowerBias; + + }break; + case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->GroundspeedBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].GroundspeedBias; + + }break; + case HOUSE_ATTRIBUTE_ARMOR_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->ArmorBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ArmorBias; + + }break; + case HOUSE_ATTRIBUTE_ROF_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->ROFBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ROFBias; + + }break; + case HOUSE_ATTRIBUTE_COST_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->CostBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].CostBias; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_DELAY: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->RepairDelay = (fixed)attribute_value; + + }break; + case HOUSE_ATTRIBUTE_BUILD_DELAY: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->BuildDelay = (fixed)attribute_value; + + }break; + case HOUSE_ATTRIBUTE_STARTED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsStarted = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_ALERTED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsAlerted = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_BASE_BUILDING: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsBaseBuilding = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DISCOVERED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsDiscovered = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_CAPACITY: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Capacity = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_TIBERIUM: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Tiberium = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_CREDITS: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Credits = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_ALERT_TIMER: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->AlertTime = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_TIMER: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->RepairTimer = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_POINTS : { + + int attribute_value = lua_tointeger(L, 3); + + this_house->PointTotal = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_CRATES: { + + // Non writable + + }break; + + case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->BuildingFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->UnitFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->InfantryFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->AircraftFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->VesselFactories = attribute_value; + + }break; + + case HOUSE_ATTRIBUTE_POWER: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Power = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DRAIN: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Drain = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_LAST_ATTACKER: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->WhoLastHurtMe = (HousesType)attribute_value; + + }break; } } - // Cached attribute does not exist; let's cache! - if (!cache_item_found) { - MapScriptDefaultAttribute* cachedAttribute = new MapScriptDefaultAttribute; - cachedAttribute->RTTI = input_rtti; - cachedAttribute->ID = input_id; - cachedAttribute->attribute = input_attribute; - cachedAttribute->value = input_value; - Scen.mapScript->DefaultTypeAttributeCache.push_back(cachedAttribute); + return -1; + } + + /********************************************************************************************** + * Script_GetHouseAttribute - Sets attributes for specific houses * + * * + * SCRIPT INPUT: houseID (int) - The ID of the house * + * attribute_type (int) - The attribute to change * + * * + * SCRIPT OUTPUT: (mixed) - The value of the attribute * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetHouseAttribute(lua_State* L) { + + int houseID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + HouseClass* this_house = Houses.Ptr(houseID); + + // Get the appropriate technotype + if (this_house != NULL) { + + switch (attribute_type) { + case HOUSE_ATTRIBUTE_IQ: { + + lua_pushnumber(L, this_house->IQ); + return 1; + + }break; + case HOUSE_ATTRIBUTE_TECH_LEVEL: { + + lua_pushnumber(L, this_house->Control.TechLevel); + return 1; + + }break; + case HOUSE_ATTRIBUTE_IS_HUMAN: { + + lua_pushboolean(L, this_house->IsHuman); + return 1; + + }break; + case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { + + lua_pushnumber(L, (float)this_house->FirepowerBias.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { + + lua_pushnumber(L, (float)this_house->GroundspeedBias.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_ARMOR_BIAS: { + + lua_pushnumber(L, (float)this_house->ArmorBias.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_ROF_BIAS: { + + lua_pushnumber(L, this_house->ROFBias.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_COST_BIAS: { + + lua_pushnumber(L, (float)this_house->CostBias.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_DELAY: { + + lua_pushnumber(L, (float)this_house->RepairDelay.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_BUILD_DELAY: { + + lua_pushnumber(L, (float)this_house->BuildDelay.To_Float()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_STARTED: { + + lua_pushboolean(L, this_house->IsStarted); + return 1; + + }break; + case HOUSE_ATTRIBUTE_ALERTED: { + + lua_pushboolean(L, this_house->IsAlerted); + return 1; + + }break; + case HOUSE_ATTRIBUTE_BASE_BUILDING: { + + lua_pushboolean(L, this_house->IsBaseBuilding); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DISCOVERED: { + + lua_pushboolean(L, this_house->IsDiscovered); + return 1; + + }break; + case HOUSE_ATTRIBUTE_CAPACITY: { + + lua_pushinteger(L, this_house->Capacity); + return 1; + + }break; + case HOUSE_ATTRIBUTE_TIBERIUM: { + + lua_pushinteger(L, this_house->Tiberium); + return 1; + + }break; + case HOUSE_ATTRIBUTE_CREDITS: { + + lua_pushinteger(L, this_house->Credits); + return 1; + + }break; + case HOUSE_ATTRIBUTE_ALERT_TIMER: { + + lua_pushnumber(L, this_house->AlertTime); + return 1; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_TIMER: { + + lua_pushnumber(L, this_house->RepairTimer); + return 1; + + }break; + case HOUSE_ATTRIBUTE_POINTS: { + + lua_pushinteger(L, this_house->PointTotal); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { + + lua_pushinteger(L, (int)this_house->DestroyedBuildings->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { + + lua_pushinteger(L, (int)this_house->DestroyedInfantry->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { + + lua_pushinteger(L, (int)this_house->DestroyedUnits->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { + + lua_pushinteger(L, (int)this_house->DestroyedAircraft->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { + + lua_pushinteger(L, (int)this_house->DestroyedVessels->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { + + lua_pushinteger(L, (int)this_house->CapturedBuildings->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_CRATES: { + + lua_pushinteger(L, (int)this_house->TotalCrates->Get_All_Totals()); + return 1; + + }break; + case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { + + lua_pushinteger(L, this_house->AircraftFactories); + return 1; + + }break; + case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { + + lua_pushinteger(L, this_house->InfantryFactories); + return 1; + + }break; + case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { + + lua_pushinteger(L, this_house->UnitFactories); + return 1; + + }break; + case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { + + lua_pushinteger(L, this_house->VesselFactories); + return 1; + + }break; + case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { + + lua_pushinteger(L, this_house->BuildingFactories); + return 1; + + }break; + case HOUSE_ATTRIBUTE_POWER: { + + lua_pushnumber(L, this_house->Power); + return 1; + + }break; + case HOUSE_ATTRIBUTE_DRAIN: { + + lua_pushnumber(L, this_house->Drain); + return 1; + + }break; + case HOUSE_ATTRIBUTE_LAST_ATTACKER: { + + lua_pushnumber(L, this_house->WhoLastHurtMe); + return 1; + + }break; + } } + lua_pushnumber(L, -1); + + return -1; } + /*********************************************************************************************** + * Script_BuildUnit - Builds a given unit in a given factory * + * * + * SCRIPT INPUT: factoryObject (int) - The factory object id * + * * + * unitType (int) - The unit type index for the unit to make (unit in a * + * general sense, not a vehicle) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_BuildUnit(lua_State* L) { + + int factoryObject = lua_tointeger(L, 1); + int unitType = lua_tointeger(L, 2); + + BuildingClass* this_factory = (BuildingClass *)Script_GetCacheObject(factoryObject); + + // Get the appropriate technotype + if (this_factory != NULL && this_factory->IsActive) { + + int result = true; + + switch (this_factory->Class->ID) { + case STRUCT_WEAP: { + + UnitTypeClass* unit_to_place = UnitTypes.Ptr(unitType); + UnitClass *new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_UnitIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + }else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_TENT: + case STRUCT_BARRACKS: { + + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + }else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_KENNEL: { + + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + }else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_HELIPAD: + case STRUCT_AIRSTRIP: { + + AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(unitType); + AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_AircraftIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + }else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_SUB_PEN: + case STRUCT_SHIP_YARD: { + + VesselTypeClass* unit_to_place = VesselTypes.Ptr(unitType); + VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_VesselIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + }else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + } + + } + + + lua_pushnumber(L, -1); + return 1; + } + + /*********************************************************************************************** + * Script_CreateVehicle - Creates a given unit out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The unit type to create * + * * + * houseType (int) - The player (house) index to create the unit for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreateVehicle(lua_State* L) { + + int objectIndex = lua_tointeger(L,1); + int houseType = lua_tointeger(L,2); + int in_x = lua_tointeger(L,3); + int in_y = lua_tointeger(L,4); + + HouseClass *this_house = Houses.Ptr(houseType); + + UnitTypeClass *unit_to_place = UnitTypes.Ptr(objectIndex); + + if (this_house != NULL && unit_to_place != NULL) { + UnitClass* new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { + + bool new_unit_placed = false; + + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if(!new_unit_placed){ + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { + new_unit_placed = true; + } + } + + + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_UnitIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + } + } + } + + lua_pushnumber(L, -1); + return 1; + } + + + /*********************************************************************************************** + * Script_CreateInfantry - Creates a given Infantry out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Infantry type to create * + * * + * houseType (int) - The player (house) index to create the Infantry for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreateInfantry(lua_State* L) { + + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); + + HouseClass* this_house = Houses.Ptr(houseType); + + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(objectIndex); + + if (this_house != NULL && unit_to_place != NULL) { + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { + + bool new_unit_placed = false; + + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)),DIR_S)) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell),DIR_S)) { + new_unit_placed = true; + } + } + + + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + } + } + } + + lua_pushnumber(L, -1); + return 1; + } + + /*********************************************************************************************** + * Script_CreateAircraft - Creates a given Aircraft out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Aircraft type to create * + * * + * houseType (int) - The player (house) index to create the Aircraft for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreateAircraft(lua_State* L) { + + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); + + HouseClass* this_house = Houses.Ptr(houseType); + + AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(objectIndex); + + if (this_house != NULL && unit_to_place != NULL) { + AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { + + bool new_unit_placed = false; + + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { + new_unit_placed = true; + } + } + + + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_AircraftIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + } + } + } + + lua_pushnumber(L, -1); + return 1; + } + + /*********************************************************************************************** + * Script_CreateVessel - Creates a given Vessel out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Vessel type to create * + * * + * houseType (int) - The player (house) index to create the Vessel for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreateVessel(lua_State* L) { + + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); + + HouseClass* this_house = Houses.Ptr(houseType); + + VesselTypeClass* unit_to_place = VesselTypes.Ptr(objectIndex); + + if (this_house != NULL && unit_to_place != NULL) { + VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { + + bool new_unit_placed = false; + + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { + new_unit_placed = true; + } + } + + + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_VesselIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + } + } + } + + lua_pushnumber(L, -1); + return 1; + } + + +/********************************************************************************************** +* Object Orders Functions * +*=============================================================================================*/ + +/*********************************************************************************************** + * Script_MoveObjectTo - Assigns a movement order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (int) - X location to move object to * + * * + * in_y (int) - Y location to move object to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_MoveObjectTo(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + switch (this_object->RTTI) { + case RTTI_UNIT: { + + UnitClass* thisUnit = (UnitClass*)this_object; + + Queue_Mission(TargetClass(thisUnit), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + + lua_pushnumber(L, 1); + return 1; + + }break; + case RTTI_INFANTRY: { + + InfantryClass* thisInfantry = (InfantryClass*)this_object; + + Queue_Mission(TargetClass(thisInfantry), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + + lua_pushnumber(L, 1); + return 1; + + }break; + case RTTI_AIRCRAFT: { + + AircraftClass* thisAircraft = (AircraftClass*)this_object; + + Queue_Mission(TargetClass(thisAircraft), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + + lua_pushnumber(L, 1); + return 1; + + }break; + case RTTI_VESSEL: { + + VesselClass* thisVessel = (VesselClass*)this_object; + + Queue_Mission(TargetClass(thisVessel), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + + lua_pushnumber(L, 1); + return 1; + + }break; + } + } + + lua_pushnumber(L, -1); + return 1; + + } + + /********************************************************************************************** * Mission Utility Functions * *=============================================================================================*/ @@ -4754,6 +6439,34 @@ return -1; } +/********************************************************************************************** +* Special / Debug Functions * +*=============================================================================================*/ + +/*********************************************************************************************** + * Script_Console_Print - Prints text to the console * + * * + * SCRIPT INPUT: inputText (string) - The text to print to the console * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_Console_Print(lua_State* L) { + + const char* inputText = lua_tostring(L, -1); // grab path string from top of stack + + Console_Printf(inputText); + + return -1; + } + + /********************************************************************************************** * MapScript Internal Functions * *=============================================================================================*/ @@ -4780,7 +6493,7 @@ MapScriptObject* thisCacheObject = (MapScriptObject*)(*it); // Here it is - if (thisCacheObject->ID == input_object_id) { + if (thisCacheObject->UID == input_object_id) { // Determine type of object and return it from cache. If it doesn't exist any longer, remove it from cache switch (thisCacheObject->RTTI) { @@ -4842,7 +6555,7 @@ // Not found in cache; let's find the object by ID the hard way... If scripting is done well, this doesn't get hit int _objectIndex = -1; - MapScriptObject* CacheObject = new MapScriptObject; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); CacheObject->ID = -1; _objectIndex = Script_BuildingIndexFromID(input_object_id); @@ -5104,6 +6817,47 @@ return -1; } + /*********************************************************************************************** + * Script_CacheDefaultAttribute - Caches an attribute value upon first usage for restoration * + * * + * SCRIPT INPUT: input_rtti (RTTIType) - The RTTI type for the attrtibute * + * input_id (int) - The ID of the typeclass * + * input_attribute (int) - The attribute to cache * + * input_value (int) - The value if the cache item doesn't exist * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + void Script_CacheDefaultObjectTypeAttribute(RTTIType input_rtti, int input_id, MapScriptObjectAttributeType input_attribute, int input_value) { + + bool cache_item_found = false; + for (std::vector::iterator it = Scen.mapScript->DefaultObjectTypeAttributeCache.begin(); it != Scen.mapScript->DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 + + MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); + + if (input_rtti == cachedAttribute->RTTI && input_id == cachedAttribute->ID && input_attribute == cachedAttribute->attribute) { + cache_item_found = true; + break; + } + + } + + // Cached attribute does not exist; let's cache! + if (!cache_item_found) { + + MapScriptDefaultObjectAttribute* cachedAttribute = new MapScriptDefaultObjectAttribute; + cachedAttribute->RTTI = input_rtti; + cachedAttribute->ID = input_id; + cachedAttribute->attribute = input_attribute; + cachedAttribute->value = input_value; + Scen.mapScript->DefaultObjectTypeAttributeCache.push_back(cachedAttribute); + + } + + } + /*********************************************************************************************** * MapScript::CallFunction - Calls a given function within the current script * * * @@ -5153,6 +6907,27 @@ lua_pop(L, 1); } + /*********************************************************************************************** + * MapScript::CreateCacheObject -- Initializes a cached object for a new cache entry * + * * + * INPUT: mapName - The script path * + * * + * OUTPUT: bool; did the map script load and initialize? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + MapScriptObject* MapScript::CreateCacheObject() { + + MapScriptObject* CacheObject = new MapScriptObject; + + cUID++; + CacheObject->UID = cUID; + + return CacheObject; + + } + /*********************************************************************************************** * MapScript::Deinit - Deinitialize/free any data used by MapScript (used within destructor) * * * @@ -5173,15 +6948,10 @@ ObjectCache.clear(); - // Welcome back to the web of switches where we will restore any default - // attributes that were changed in the script - for (std::vector::iterator it = Scen.mapScript->DefaultTypeAttributeCache.begin(); it != Scen.mapScript->DefaultTypeAttributeCache.end(); ++it) {//Error 2-4 - - MapScriptDefaultAttribute* cachedAttribute = (MapScriptDefaultAttribute*)(*it); - - - + // We will now restore any default Object attributes that were changed in the script + for (std::vector::iterator it = DefaultObjectTypeAttributeCache.begin(); it != DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 + MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); switch (cachedAttribute->RTTI) { @@ -5223,9 +6993,16 @@ } - } + // Remove any cached Object Attributes + for (int i = 0; i < DefaultObjectTypeAttributeCache.size(); ++i) + { + delete DefaultObjectTypeAttributeCache[i]; + } + + DefaultObjectTypeAttributeCache.clear(); + for (int index = 0; index < Warheads.Count(); index++) { Warheads.Ptr(index)->Read_INI(RuleINI); @@ -5261,6 +7038,7 @@ } + /*********************************************************************************************** * MapScript::Init -- Loads and initializes script for a given map * * * @@ -5274,6 +7052,9 @@ * * *=============================================================================================*/ bool MapScript::Init(const char* mapName) { + + this->cUID = 0; + char scriptName[256]; // Build map path @@ -5307,7 +7088,7 @@ ObjectCache.reserve(2000); // Reserve type attribute cache memory (to restore rules after script) - DefaultTypeAttributeCache.reserve(1000); + DefaultObjectTypeAttributeCache.reserve(1000); /********************************************************************************************** * Red Alert Vanilla Actions * @@ -5401,6 +7182,7 @@ * Object Functions * *=============================================================================================*/ lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] + lua_register(L, "SetObjectAttribute", Script_SetObjectAttribute); // Sets an object's attributes (strength, etc.) lua_register(L, "SetBuildingTypeAttribute", Script_SetBuildingTypeAttribute); // Sets the attribute of a Building type lua_register(L, "SetVehicleTypeAttribute", Script_SetVehicleTypeAttribute); // Sets the attribute of a Vehicle type @@ -5408,8 +7190,27 @@ lua_register(L, "SetVesselTypeAttribute", Script_SetVesselTypeAttribute); // Sets the attribute of a Vessel type lua_register(L, "SetAircraftTypeAttribute", Script_SetAircraftTypeAttribute); // Sets the attribute of an Aircraft type + lua_register(L, "GetObjectAttribute", Script_GetObjectAttribute); // Sets an object's attributes (strength, etc.) + lua_register(L, "GetBuildingTypeAttribute", Script_GetBuildingTypeAttribute); // Sets the attribute of a Building type + lua_register(L, "GetVehicleTypeAttribute", Script_GetVehicleTypeAttribute); // Sets the attribute of a Vehicle type + lua_register(L, "GetInfantryTypeAttribute", Script_GetInfantryTypeAttribute); // Sets the attribute of an Infantry type + lua_register(L, "GetVesselTypeAttribute", Script_GetVesselTypeAttribute); // Sets the attribute of a Vessel type + lua_register(L, "GetAircraftTypeAttribute", Script_GetAircraftTypeAttribute); // Sets the attribute of an Aircraft type - //lua_register(L, "GetObjectAttribute", Script_SetAircraftTypeAttribute); // Gets the attribute of an object + lua_register(L, "SetHouseAttribute", Script_SetHouseAttribute); // Sets a house's attributes + lua_register(L, "GetHouseAttribute", Script_GetHouseAttribute); // Gets a house's attributes + + lua_register(L, "BuildUnit", Script_BuildUnit); // Builds a unit in a given factory / barracks / naval yard / etc + lua_register(L, "CreateVehicle", Script_CreateVehicle); // Creates a unit out of thin air + lua_register(L, "CreateInfantry", Script_CreateInfantry); // Creates a unit out of thin air + lua_register(L, "CreateAircraft", Script_CreateAircraft); // Creates a unit out of thin air + lua_register(L, "CreateVessel", Script_CreateVessel); // Creates a unit out of thin air + + /********************************************************************************************** + * Object Orders Functions * + *=============================================================================================*/ + + lua_register(L, "MoveObjectTo", Script_MoveObjectTo); // Assigns a movement order to the object /********************************************************************************************** * Mission Utility Functions * @@ -5432,7 +7233,13 @@ lua_register(L, "GetWaypointX", Script_GetWaypointX); // Get Cell/X of waypoint lua_register(L, "GetWaypointY", Script_GetWaypointY); // Get Cell/Y of waypoint - + + /********************************************************************************************** + * Special / Debug Functions * + *=============================================================================================*/ + + lua_register(L, "PrintConsole", Script_Console_Print); // Print text to console + /********************************************************************************************** * Script Globals * *=============================================================================================*/ diff --git a/code/redalert/mapscript.h b/code/redalert/mapscript.h index 5548a95..3b914f5 100644 --- a/code/redalert/mapscript.h +++ b/code/redalert/mapscript.h @@ -15,27 +15,30 @@ extern "C" { // Object Cache Item // Stores an id lookup table struct MapScriptObject { + + int UID; // The UID of the object (to prevent ID collisions) + int ID=-1; // The object's "Global ID" int classIndex=-1; // The object's "Global ID" RTTIType RTTI; // The type of object + }; -// Stores defaults of rules from before -// variables were changed within -struct MapScriptDefaultAttribute { +// Stores defaults of object attributes whenever they are changed for the first time (for later restoration) +struct MapScriptDefaultObjectAttribute { RTTIType RTTI; // The type of object int ID=-1; // The index of the object in its heap int attribute; int value; }; - // // MapScript Class // class MapScript { public: + int cUID = 0; ~MapScript() { Deinit(); }; @@ -43,11 +46,13 @@ public: bool Init(const char* mapName); void Deinit(); + MapScriptObject* CreateCacheObject(); + void CallFunction(const char* functionName); void SetLuaPath(const char* input_path); std::vector ObjectCache; - std::vector DefaultTypeAttributeCache; + std::vector DefaultObjectTypeAttributeCache; private: lua_State* L; @@ -56,7 +61,7 @@ private: // Enums -typedef enum MapScriptAttributeType : char { +typedef enum MapScriptObjectAttributeType : char { // Techno types ATTRIBUTE_TYPE=0, @@ -105,7 +110,49 @@ typedef enum MapScriptAttributeType : char { ATTRIBUTE_DOG -} MapScriptAttributeType; +} MapScriptObjectAttributeType; + +// Enums +typedef enum MapScriptHouseAttributeType : char { + + // Techno types + HOUSE_ATTRIBUTE_IQ = 0, + HOUSE_ATTRIBUTE_TECH_LEVEL, + HOUSE_ATTRIBUTE_IS_HUMAN, + HOUSE_ATTRIBUTE_FIREPOWER_BIAS, + HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS, + HOUSE_ATTRIBUTE_ARMOR_BIAS, + HOUSE_ATTRIBUTE_ROF_BIAS, + HOUSE_ATTRIBUTE_COST_BIAS, + HOUSE_ATTRIBUTE_REPAIR_DELAY, + HOUSE_ATTRIBUTE_BUILD_DELAY, + HOUSE_ATTRIBUTE_STARTED, + HOUSE_ATTRIBUTE_ALERTED, + HOUSE_ATTRIBUTE_BASE_BUILDING, + HOUSE_ATTRIBUTE_DISCOVERED, + HOUSE_ATTRIBUTE_CAPACITY, + HOUSE_ATTRIBUTE_TIBERIUM, + HOUSE_ATTRIBUTE_CREDITS, + HOUSE_ATTRIBUTE_ALERT_TIMER, + HOUSE_ATTRIBUTE_REPAIR_TIMER, + HOUSE_ATTRIBUTE_POINTS, + HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS, + HOUSE_ATTRIBUTE_DESTROYED_VEHICLES, + HOUSE_ATTRIBUTE_DESTROYED_INFANTRY, + HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT, + HOUSE_ATTRIBUTE_DESTROYED_VESSELS, + HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS, + HOUSE_ATTRIBUTE_CRATES, + HOUSE_ATTRIBUTE_BUILDING_FACTORIES, + HOUSE_ATTRIBUTE_UNIT_FACTORIES, + HOUSE_ATTRIBUTE_INFANTRY_FACTORIES, + HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES, + HOUSE_ATTRIBUTE_VESSEL_FACTORIES, + HOUSE_ATTRIBUTE_POWER, + HOUSE_ATTRIBUTE_DRAIN, + HOUSE_ATTRIBUTE_LAST_ATTACKER + +} MapScriptHouseAttributeType; // Non-Class-Functions bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger); @@ -117,8 +164,9 @@ int Script_AircraftIndexFromID(int input_object_id); int Script_InfantryIndexFromID(int input_object_id); int Script_VesselIndexFromID(int input_object_id); -static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptAttributeType attribute_type); +static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type); +static int Script_GetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type); -void Script_CacheDefaultAttribute(RTTIType input_rtti, int input_id, MapScriptAttributeType input_attribute, int input_value); +void Script_CacheDefaultObjectTypeAttribute(RTTIType input_rtti, int input_id, MapScriptObjectAttributeType input_attribute, int input_value); #endif \ No newline at end of file diff --git a/scripts/_Global.script b/scripts/_Global.script index f848203..6b4a09b 100644 --- a/scripts/_Global.script +++ b/scripts/_Global.script @@ -160,7 +160,21 @@ MIG=3, -- MIG Attack Plane YAK=4, -- Yak Attack Plane HELI=5, -- Longbow Helicopter - HIND=6, -- Hind Helicopter + HIND=6 -- Hind Helicopter + } + + -- ********************************************************* + -- * * + -- * Vessels * + -- * * + -- ********************************************************* + + VesselTypes={ + SS=0, -- Submarine + DD=1, -- Destroyer + CA=2, -- Cruiser + LST=3, -- Land Sea Tank (transport) + PT=4 -- Gun Boat } -- ********************************************************* @@ -270,10 +284,10 @@ -- ********************************************************* -- * * - -- * Attribite Types * + -- * Obejct Attributes * -- * * -- ********************************************************* - Attributes = { + ObjectAttributes = { Type=0, Strength=1, -- Object attribute MaxStrength=2, -- Definition attribute @@ -320,6 +334,50 @@ IsDog=37 } + + -- ********************************************************* + -- * * + -- * House Attributes * + -- * * + -- ********************************************************* + HouseAttributes = { + IQ = 0, + TechLevel =1, + IsHuman=2, + FirepowerBias=3, + GroundspeedBias=4, + ArmorBias=5, + ROFBias=6, + CostBias=7, + RepairDelay=8, + BuildDelay=9, + Started=10, + Alerted=11, + BaseBuilding=12, + Discovered=13, + Capacity=14, + Tiberium=15, + Credits=16, + AlertTimer=17, + RepairTimer=18, + Points=19, + DestroyedBuildings=20, + DestroyedVehicles=21, + DestroyedInfantry=22, + DestroyedAircraft=23, + DestroyedVessels=24, + CapturedBuildings=25, + Crates=26, + BuildingFactories=27, + VehicleFactories=28, + InfantryFactories=29, + AircraftFactories=30, + VesselFactories=31, + + Power=32, + Drain=33, + LastAttacker=34 + } -- ********************************************************* -- * * From 9b237d8fdad3b2796af954a0a866c55c2141cc76 Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sun, 28 Jun 2020 17:57:57 -0600 Subject: [PATCH 10/18] Added several unit orders --- code/redalert/mapscript.cpp | 388 ++++++++++++++++++++++++++++++++---- 1 file changed, 351 insertions(+), 37 deletions(-) diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp index 1489e7f..1253edb 100644 --- a/code/redalert/mapscript.cpp +++ b/code/redalert/mapscript.cpp @@ -5711,7 +5711,7 @@ *=============================================================================================*/ /*********************************************************************************************** - * Script_MoveObjectTo - Assigns a movement order to an object * + * Script_OrderMove - Assigns a movement order to an object * * * * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * * * @@ -5728,7 +5728,7 @@ * WARNINGS: ? * * * *=============================================================================================*/ - static int Script_MoveObjectTo(lua_State* L) { + static int Script_OrderMove(lua_State* L) { int objectID = lua_tointeger(L, 1); int in_x = lua_tointeger(L, 2); @@ -5737,48 +5737,108 @@ ObjectClass* this_object = Script_GetCacheObject(objectID); if (this_object != NULL) { - switch (this_object->RTTI) { - case RTTI_UNIT: { - UnitClass* thisUnit = (UnitClass*)this_object; + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, As_Target(XY_Cell(in_x, in_y))); - Queue_Mission(TargetClass(thisUnit), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + lua_pushnumber(L, 1); + return 1; - lua_pushnumber(L, 1); - return 1; + } - }break; - case RTTI_INFANTRY: { + lua_pushnumber(L, -1); + return 1; - InfantryClass* thisInfantry = (InfantryClass*)this_object; + } - Queue_Mission(TargetClass(thisInfantry), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + /*********************************************************************************************** + * Script_OrderAttackCell - Assigns an attack order to an object for a specific cell * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (int) - X location to attack * + * * + * in_y (int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderAttackCell(lua_State* L) { - lua_pushnumber(L, 1); - return 1; + int objectID = lua_tointeger(L, 1); - }break; - case RTTI_AIRCRAFT: { + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); - AircraftClass* thisAircraft = (AircraftClass*)this_object; + ObjectClass* this_object = Script_GetCacheObject(objectID); - Queue_Mission(TargetClass(thisAircraft), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + if (this_object != NULL) { - lua_pushnumber(L, 1); - return 1; - - }break; - case RTTI_VESSEL: { - - VesselClass* thisVessel = (VesselClass*)this_object; - - Queue_Mission(TargetClass(thisVessel), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); - - lua_pushnumber(L, 1); - return 1; - - }break; + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, this_target, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } + + /*********************************************************************************************** + * Script_OrderAttack - Assigns an attack order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (int) - X location to attack * + * * + * in_y (int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderAttack(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + } lua_pushnumber(L, -1); @@ -5786,6 +5846,255 @@ } + /*********************************************************************************************** + * Script_OrderHunt - Assigns a hunt order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderHunt(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } + + /*********************************************************************************************** + * Script_OrderGuard - Assigns a guard order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderGuard(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } + + /*********************************************************************************************** + * Script_OrderRetreat - Assigns a retreat order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderRetreat(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } + + /*********************************************************************************************** + * Script_OrderEnter - Assigns an enter order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to enter (building / unit) * + * * + * in_y (opt)(int) - Y location to enter (building / unit) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderEnter(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ENTER, this_target, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } + + /*********************************************************************************************** + * Script_OrderCapture - Assigns a capture order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to capture (building) * + * * + * in_y (opt)(int) - Y location to capture (building) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_OrderCapture(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_CAPTURE, this_target, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + + } /********************************************************************************************** * Mission Utility Functions * @@ -7201,16 +7510,21 @@ lua_register(L, "GetHouseAttribute", Script_GetHouseAttribute); // Gets a house's attributes lua_register(L, "BuildUnit", Script_BuildUnit); // Builds a unit in a given factory / barracks / naval yard / etc - lua_register(L, "CreateVehicle", Script_CreateVehicle); // Creates a unit out of thin air - lua_register(L, "CreateInfantry", Script_CreateInfantry); // Creates a unit out of thin air - lua_register(L, "CreateAircraft", Script_CreateAircraft); // Creates a unit out of thin air - lua_register(L, "CreateVessel", Script_CreateVessel); // Creates a unit out of thin air + lua_register(L, "CreateVehicle", Script_CreateVehicle); // Creates a unit out of thin air + lua_register(L, "CreateInfantry", Script_CreateInfantry); // Creates a unit out of thin air + lua_register(L, "CreateAircraft", Script_CreateAircraft); // Creates a unit out of thin air + lua_register(L, "CreateVessel", Script_CreateVessel); // Creates a unit out of thin air /********************************************************************************************** * Object Orders Functions * *=============================================================================================*/ - lua_register(L, "MoveObjectTo", Script_MoveObjectTo); // Assigns a movement order to the object + lua_register(L, "OrderMove", Script_OrderMove); // Assigns a movement order to the object + lua_register(L, "OrderAttack", Script_OrderAttack); // Assigns an attack order to the object + lua_register(L, "OrderHunt", Script_OrderHunt); // Assigns a hunt order to the object + lua_register(L, "OrderGuard", Script_OrderGuard); // Assigns a guard order to the object + lua_register(L, "OrderEnter", Script_OrderEnter); // Assigns a enter order to the object + lua_register(L, "OrderCapture", Script_OrderCapture); // Assigns a capture order to the object /********************************************************************************************** * Mission Utility Functions * From 9770771f5c8f5b9e852b43b50f9945f8e1a75f1a Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sun, 28 Jun 2020 19:50:53 -0600 Subject: [PATCH 11/18] Fixes several small issues --- code/redalert/mapscript.cpp | 13461 +++++++++++++++++----------------- 1 file changed, 6734 insertions(+), 6727 deletions(-) diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp index 1253edb..8e1a841 100644 --- a/code/redalert/mapscript.cpp +++ b/code/redalert/mapscript.cpp @@ -5,1451 +5,1208 @@ * Red Alert Vanilla Actions * *=============================================================================================*/ - /*********************************************************************************************** - * Script_Win - The specified player wins * - * * - * SCRIPT INPUT: houseType (int) - The player (house that wins) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_Win(lua_State* L) { - - int houseType = lua_tointeger(L, -1); +/*********************************************************************************************** + * Script_Win - The specified player wins * + * * + * SCRIPT INPUT: houseType (int) - The player (house that wins) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_Win(lua_State* L) { - if (houseType != HOUSE_NONE) { + int houseType = lua_tointeger(L, -1); - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + if (houseType != HOUSE_NONE) { - if (this_house != NULL) { + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - if (this_house->ID == PlayerPtr->Class->House) { - PlayerPtr->Flag_To_Win(); - } - else { - PlayerPtr->Flag_To_Lose(); - } + if (this_house != NULL) { - bool success = this_house->Fire_Sale(); - lua_pushboolean(L, int(success)); + if (this_house->ID == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); } - } - - return 1; - } - - /*********************************************************************************************** - * Script_Lose - The specified player loses * - * * - * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_Lose(lua_State* L) { - - int houseType = lua_tointeger(L, -1); - - if (houseType != HOUSE_NONE) { - - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - - if (this_house != NULL) { - - if (this_house->ID == PlayerPtr->Class->House) { - PlayerPtr->Flag_To_Lose(); - } - else { - PlayerPtr->Flag_To_Win(); - } - - lua_pushboolean(L, 1); - } - } - - return 1; - } - - /*********************************************************************************************** - * Script_BeginProduction - This will enable production to begin for the house specified * - * * - * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_BeginProduction(lua_State* L) { - - int houseType = lua_tointeger(L, -1); - - if (houseType != HOUSE_NONE) { - - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - - if (this_house != NULL) { - this_house->Begin_Production(); - } - } - - return 1; - } - - /*********************************************************************************************** - * Script_CreateTeam - Attempts to create team (as defined in map) * - * * - * SCRIPT INPUT: teamType (int) - The team index to attempt to create * - * * - * SCRIPT OUTPUT: success (bool) - Did a team get created? * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreateTeam(lua_State* L) { - - const char* teamType = lua_tostring(L, 1); - - TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); - - if (teamPtr != NULL) { - ScenarioInit++; - - bool success = false; - - TeamClass* new_team = teamPtr->Create_One_Of(); - - if (new_team != NULL) { - success = true; + else { + PlayerPtr->Flag_To_Lose(); } - lua_pushboolean(L, int(success)); - - ScenarioInit--; - } - - return 1; - } - - /*********************************************************************************************** - * Script_DestroyTeam - Destroy all teams of the type specified * - * * - * SCRIPT INPUT: teamType (int) - The team index to attempt to destroy * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DestroyTeam(lua_State* L) { - - const char* teamType = lua_tostring(L, 1); - - TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); - - if (teamPtr != NULL) { - - teamPtr->Destroy_All_Of(); - - } - - return 1; - } - - /*********************************************************************************************** - * Script_AllHunt - Force all units of specified house to go into hunt mode * - * * - * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllHunt(lua_State* L) { - - int houseType = lua_tointeger(L, -1); - - if (houseType != HOUSE_NONE) { - - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - - if (this_house != NULL) { - this_house->Do_All_To_Hunt(); - } - } - - return 1; - } - - /*********************************************************************************************** - * Script_Reinforcements - Attempts to create reinforcements as defined by team * - * * - * SCRIPT INPUT: teamType (int) - The team index to attempt to create * - * * - * SCRIPT OUTPUT: success (bool) - Did Do_Reinforcements return true? * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_Reinforcements(lua_State* L) { - - const char* teamType = lua_tostring(L, 1); - - TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); - - if (teamPtr != NULL) { - bool success = Do_Reinforcements(teamPtr); + bool success = this_house->Fire_Sale(); lua_pushboolean(L, int(success)); } - else { - lua_pushboolean(L, 0); - } - - return 1; } - /*********************************************************************************************** - * Script_DropZoneFlare - Places drop down smoke at specified waypoint location * - * * - * SCRIPT INPUT: in_x (int) - The Cell/X location to drop the smoke * - * in_y (int) - The Cell/Y location to drop the smoke * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DropZoneFlare(lua_State* L) { + return 1; +} - int in_x = lua_tointeger(L, 1); - int in_y = lua_tointeger(L, 2); +/*********************************************************************************************** + * Script_Lose - The specified player loses * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_Lose(lua_State* L) { - new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(XY_Cell(in_x,in_y))); + int houseType = lua_tointeger(L, -1); - return 1; - } + if (houseType != HOUSE_NONE) { - /*********************************************************************************************** - * Script_FireSale - Make AI house give up, selling everything and going all in on attack * - * * - * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * - * * - * SCRIPT OUTPUT: success (bool) - Did the action get performed? * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_FireSale(lua_State* L) { + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - int houseType = lua_tointeger(L, -1); + if (this_house != NULL) { - if (houseType != HOUSE_NONE) { - - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - - if (this_house != NULL) { - bool success = this_house->Fire_Sale(); - lua_pushboolean(L, int(success)); + if (this_house->ID == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Lose(); } - } - - return 1; - } - - /*********************************************************************************************** - * Script_PlayMovie - Plays the given movie * - * * - * SCRIPT INPUT: ret (int) - The movie index of which to play * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_PlayMovie(lua_State* L) { - int ret = lua_tointeger(L, -1); - Play_Movie((VQType)ret); - return 1; - } - - /*********************************************************************************************** - * Script_TriggerText - Triggers text to display in-game * - * * - * SCRIPT INPUT: textIndex (int) - The text index to display (from tutorial.ini) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_TriggerText(lua_State* L) { - int textIndex = lua_tointeger(L, -1); - - // TODO: Implement colors / flags - - // JJQUESTION: This appears right but doesn't do anything. Is text broken at the moment? - if (textIndex > 0 && textIndex < sizeof(TutorialText)) { - - Session.Messages.Add_Message(NULL, textIndex, (char*)TutorialText[textIndex], PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); - } - - return 1; - } - - /*********************************************************************************************** - * Script_DestroyTrigger - Destroys a given trigger * - * * - * SCRIPT INPUT: ret (int) - The index/id of the trigger of which to destroy * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DestroyTrigger(lua_State* L) { - - int triggerIndex = lua_tointeger(L, -1); - - TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); - - if (this_trigger != NULL) { - Detach_This_From_All(this_trigger->As_Target()); - delete Triggers.Ptr(triggerIndex); - } - - return 1; - } - - /*********************************************************************************************** - * Script_AutoCreate - Allows house AI to begin creating autocreate teams * - * * - * Autocreation means that instead of a teamtype being * - * manually created by a Create Team trigger action, that * - * it will be subject to the autocreate function. Each * - * computer country can have its autocreation function * - * turned on through a trigger action, after which the * - * creation of the teamtypes that have autocreation turned * - * on will be subject to being created, at the whim of the * - * autocreate function. See [TeamTypes] Section for more * - * details. * - * - The Red Alert Single Player Mission Creation Guide * - * * - * * - * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow autocreate for * - * createEnables (bool, default yes) - Allow autocreate on/off * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AutoCreate(lua_State* L) { - - int houseType = 0; - bool autoCreateEnabled = true; - - houseType = lua_tointeger(L, 1); - - // If there is a second argument given, process it - if (lua_gettop(L) == 2) { - autoCreateEnabled = lua_toboolean(L, 2); - } - - if (houseType != HOUSE_NONE) { - - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); - - if (this_house != NULL) { - - // If we are enabling auto creation by the house, clear alert - if (autoCreateEnabled) { - this_house->AlertTime = 0; // TODO - don't assume successful pointer - } - - // Now set the parameter - this_house->IsAlerted = autoCreateEnabled; - + else { + PlayerPtr->Flag_To_Win(); } + lua_pushboolean(L, 1); } - - return 1; } - /*********************************************************************************************** - * Script_AllowWin - Allows a win * - * * - * Used when you want a specific objective * - * to be met before the mission is accomplished. In other words, if the * - * trigger with this action assigned to it hasn't been fired, but the * - * actual WIN trigger has been fired, the mission will not end until this * - * "allow win" trigger is fired. * - * * - * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllowWin(lua_State* L) { + return 1; +} - int houseType = lua_tointeger(L, -1); +/*********************************************************************************************** + * Script_BeginProduction - This will enable production to begin for the house specified * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_BeginProduction(lua_State* L) { - if (houseType != HOUSE_NONE) { + int houseType = lua_tointeger(L, -1); - HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + if (houseType != HOUSE_NONE) { - if (this_house != NULL) { - this_house->Blockage++; + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Begin_Production(); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_CreateTeam - Attempts to create team (as defined in map) * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to create * + * * + * SCRIPT OUTPUT: success (bool) - Did a team get created? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateTeam(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + ScenarioInit++; + + bool success = false; + + TeamClass* new_team = teamPtr->Create_One_Of(); + + if (new_team != NULL) { + success = true; + } + + lua_pushboolean(L, int(success)); + + ScenarioInit--; + } + + return 1; +} + +/*********************************************************************************************** + * Script_DestroyTeam - Destroy all teams of the type specified * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to destroy * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DestroyTeam(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + + teamPtr->Destroy_All_Of(); + + } + + return 1; +} + +/*********************************************************************************************** + * Script_AllHunt - Force all units of specified house to go into hunt mode * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllHunt(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Do_All_To_Hunt(); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_Reinforcements - Attempts to create reinforcements as defined by team * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to create * + * * + * SCRIPT OUTPUT: success (bool) - Did Do_Reinforcements return true? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_Reinforcements(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + bool success = Do_Reinforcements(teamPtr); + lua_pushboolean(L, int(success)); + } + else { + lua_pushboolean(L, 0); + } + + return 1; +} + +/*********************************************************************************************** + * Script_DropZoneFlare - Places drop down smoke at specified waypoint location * + * * + * SCRIPT INPUT: in_x (int) - The Cell/X location to drop the smoke * + * in_y (int) - The Cell/Y location to drop the smoke * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DropZoneFlare(lua_State* L) { + + int in_x = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(XY_Cell(in_x, in_y))); + + return 1; +} + +/*********************************************************************************************** + * Script_FireSale - Make AI house give up, selling everything and going all in on attack * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: success (bool) - Did the action get performed? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_FireSale(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + bool success = this_house->Fire_Sale(); + lua_pushboolean(L, int(success)); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_PlayMovie - Plays the given movie * + * * + * SCRIPT INPUT: ret (int) - The movie index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlayMovie(lua_State* L) { + int ret = lua_tointeger(L, -1); + Play_Movie((VQType)ret); + return 1; +} + +/*********************************************************************************************** + * Script_TriggerText - Triggers text to display in-game * + * * + * SCRIPT INPUT: textIndex (int) - The text index to display (from tutorial.ini) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TriggerText(lua_State* L) { + int textIndex = lua_tointeger(L, -1); + + // TODO: Implement colors / flags + + // JJQUESTION: This appears right but doesn't do anything. Is text broken at the moment? + if (textIndex > 0 && textIndex < sizeof(TutorialText)) { + + Session.Messages.Add_Message(NULL, textIndex, (char*)TutorialText[textIndex], PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return 1; +} + +/*********************************************************************************************** + * Script_DestroyTrigger - Destroys a given trigger * + * * + * SCRIPT INPUT: ret (int) - The index/id of the trigger of which to destroy * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DestroyTrigger(lua_State* L) { + + int triggerIndex = lua_tointeger(L, -1); + + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); + + if (this_trigger != NULL) { + Detach_This_From_All(this_trigger->As_Target()); + delete Triggers.Ptr(triggerIndex); + } + + return 1; +} + +/*********************************************************************************************** + * Script_AutoCreate - Allows house AI to begin creating autocreate teams * + * * + * Autocreation means that instead of a teamtype being * + * manually created by a Create Team trigger action, that * + * it will be subject to the autocreate function. Each * + * computer country can have its autocreation function * + * turned on through a trigger action, after which the * + * creation of the teamtypes that have autocreation turned * + * on will be subject to being created, at the whim of the * + * autocreate function. See [TeamTypes] Section for more * + * details. * + * - The Red Alert Single Player Mission Creation Guide * + * * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow autocreate for * + * createEnables (bool, default yes) - Allow autocreate on/off * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AutoCreate(lua_State* L) { + + int houseType = 0; + bool autoCreateEnabled = true; + + houseType = lua_tointeger(L, 1); + + // If there is a second argument given, process it + if (lua_gettop(L) == 2) { + autoCreateEnabled = lua_toboolean(L, 2); + } + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + + // If we are enabling auto creation by the house, clear alert + if (autoCreateEnabled) { + this_house->AlertTime = 0; // TODO - don't assume successful pointer } + + // Now set the parameter + this_house->IsAlerted = autoCreateEnabled; + } - return 1; } - /*********************************************************************************************** - * Script_RevealAll - Reveals the entire map TODO: input player * - * * - * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_RevealAll(lua_State* L) { - if (!PlayerPtr->IsVisionary) { - PlayerPtr->IsVisionary = true; - for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + return 1; +} + +/*********************************************************************************************** + * Script_AllowWin - Allows a win * + * * + * Used when you want a specific objective * + * to be met before the mission is accomplished. In other words, if the * + * trigger with this action assigned to it hasn't been fired, but the * + * actual WIN trigger has been fired, the mission will not end until this * + * "allow win" trigger is fired. * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllowWin(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Blockage++; + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_RevealAll - Reveals the entire map TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealAll(lua_State* L) { + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_RevealCell - Reveals map around given waypoint TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealCell(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[ret], Rule.GapShroudRadius, PlayerPtr, false); + } + + return 1; +} + +/*********************************************************************************************** + * Script_RevealZone - Reveals area of map around given waypoint TODO: input player * + * * + * Reveal all cells of the zone that the specified waypoint is located in. This can be * + * used to reveal whole islands or bodies of water * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_RevealZone(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[ret]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { Map.Map_Cell(cell, PlayerPtr); } } - return 1; } - /*********************************************************************************************** - * Script_RevealCell - Reveals map around given waypoint TODO: input player * - * * - * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_RevealCell(lua_State* L) { - int ret = lua_tointeger(L, -1); + return 1; +} - if (!PlayerPtr->IsVisionary) { - Map.Sight_From(Scen.Waypoint[ret], Rule.GapShroudRadius, PlayerPtr, false); - } +/*********************************************************************************************** + * Script_PlaySound - Plays the given Sound * + * * + * SCRIPT INPUT: ret (int) - The sound index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlaySound(lua_State* L) { + int ret = lua_tointeger(L, -1); - return 1; + Sound_Effect((VocType)ret); + + return 1; +} + +/*********************************************************************************************** + * Script_PlayMusic - Plays the given Music score. * + * * + * Plays music score at given index. Negative numbers stop any music playing. * + * * + * SCRIPT INPUT: ret (int) - The music index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlayMusic(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (ret < 0) { + Theme.Stop(); + } + else { + Theme.Play_Song((ThemeType)ret); } - /*********************************************************************************************** - * Script_RevealZone - Reveals area of map around given waypoint TODO: input player * - * * - * Reveal all cells of the zone that the specified waypoint is located in. This can be * - * used to reveal whole islands or bodies of water * - * * - * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_RevealZone(lua_State* L) { - int ret = lua_tointeger(L, -1); + return 1; +} - if (!PlayerPtr->IsVisionary) { - int zone = Map[Scen.Waypoint[ret]].Zones[MZONE_CRUSHER]; +/*********************************************************************************************** + * Script_PlaySpeech - Plays the given Speech audio * + * * + * SCRIPT INPUT: ret (int) - The speech index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_PlaySpeech(lua_State* L) { + int ret = lua_tointeger(L, -1); + Speak((VoxType)ret); + return 1; +} - for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { - if (Map[cell].Zones[MZONE_CRUSHER] == zone) { - Map.Map_Cell(cell, PlayerPtr); - } - } +/*********************************************************************************************** + * Script_StartMissionTimer - Starts the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_StartMissionTimer(lua_State* L) { + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + return 1; +} - } +/*********************************************************************************************** + * Script_StopMissionTimer - Stops the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_StopMissionTimer(lua_State* L) { + Scen.MissionTimer.Stop(); + return 1; +} - return 1; +/*********************************************************************************************** + * Script_IncreaseMissionTimer - Increases the mission timer by the given amount * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to increase by * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_IncreaseMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); + Map.Redraw_Tab(); + return 1; +} + +/*********************************************************************************************** + * Script_DecreaseMissionTimer - Decreases the mission timer by the given amount * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to decrease by * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DecreaseMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer - (ret * (TICKS_PER_MINUTE / 10)); + if (Scen.MissionTimer < 0) { Scen.MissionTimer = 0; } + Map.Redraw_Tab(); + return 1; +} + +/*********************************************************************************************** + * Script_SetMissionTimer - Sets and starts the in-game mission timer * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_SetMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + return 1; +} + +/*********************************************************************************************** + * Script_SetGlobalValue - Sets the global value to "set" position (on) * + * * + * SCRIPT INPUT: global_value (int) - The global to set to "set" * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_SetGlobalValue(lua_State* L) { + int global_value = lua_tointeger(L, -1); + Scen.Set_Global_To(global_value, true); + return 1; +} + +/*********************************************************************************************** + * Script_ClearGlobalValue - Sets the global value to "clear" position (off) * + * * + * SCRIPT INPUT: global_value (int) - The global to set to "off" * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_ClearGlobalValue(lua_State* L) { + int global_value = lua_tointeger(L, -1); + Scen.Set_Global_To(global_value, false); + return 1; +} + +/*********************************************************************************************** + * Script_AutoBaseBuilding - Sets AI basebuilding on/off for given house * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index of the AI * + * on_off (bool) - Enable / disable auto base building * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AutoBaseBuilding(lua_State* L) { + int houseType = lua_tointeger(L, 1); + bool on_off = lua_toboolean(L, 2); + + HouseClass* this_house = Houses.Ptr(houseType); + if (this_house != NULL) { + this_house->IsBaseBuilding = on_off; } - /*********************************************************************************************** - * Script_PlaySound - Plays the given Sound * - * * - * SCRIPT INPUT: ret (int) - The sound index of which to play * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_PlaySound(lua_State* L) { - int ret = lua_tointeger(L, -1); + return 1; +} - Sound_Effect((VocType)ret); +/*********************************************************************************************** + * Script_StopMissionTimer - Stops the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreepShadow(lua_State* L) { + Map.Encroach_Shadow(PlayerPtr); + return 1; +} - return 1; - } +/*********************************************************************************************** + * Script_DestroyTriggerBuilding - Stops the in-game mission timer * + * * + * SCRIPT INPUT: triggerIndex (int) - The index of the trigger previously created * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DestroyTriggerBuilding(lua_State* L) { + int triggerIndex = lua_tointeger(L, -1); - /*********************************************************************************************** - * Script_PlayMusic - Plays the given Music score. * - * * - * Plays music score at given index. Negative numbers stop any music playing. * - * * - * SCRIPT INPUT: ret (int) - The music index of which to play * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_PlayMusic(lua_State* L) { - int ret = lua_tointeger(L, -1); + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); - if (ret < 0) { - Theme.Stop(); - } - else { - Theme.Play_Song((ThemeType)ret); - } - - return 1; - } - - /*********************************************************************************************** - * Script_PlaySpeech - Plays the given Speech audio * - * * - * SCRIPT INPUT: ret (int) - The speech index of which to play * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_PlaySpeech(lua_State* L) { - int ret = lua_tointeger(L, -1); - Speak((VoxType)ret); - return 1; - } - - /*********************************************************************************************** - * Script_StartMissionTimer - Starts the in-game mission timer * - * * - * SCRIPT INPUT: none * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_StartMissionTimer(lua_State* L) { - Scen.MissionTimer.Start(); - Map.Redraw_Tab(); - return 1; - } - - /*********************************************************************************************** - * Script_StopMissionTimer - Stops the in-game mission timer * - * * - * SCRIPT INPUT: none * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_StopMissionTimer(lua_State* L) { - Scen.MissionTimer.Stop(); - return 1; - } - - /*********************************************************************************************** - * Script_IncreaseMissionTimer - Increases the mission timer by the given amount * - * * - * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to increase by * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_IncreaseMissionTimer(lua_State* L) { - int ret = lua_tointeger(L, -1); - Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); - Map.Redraw_Tab(); - return 1; - } - - /*********************************************************************************************** - * Script_DecreaseMissionTimer - Decreases the mission timer by the given amount * - * * - * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to decrease by * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DecreaseMissionTimer(lua_State* L) { - int ret = lua_tointeger(L, -1); - Scen.MissionTimer = Scen.MissionTimer - (ret * (TICKS_PER_MINUTE / 10)); - if (Scen.MissionTimer < 0) { Scen.MissionTimer = 0; } - Map.Redraw_Tab(); - return 1; - } - - /*********************************************************************************************** - * Script_SetMissionTimer - Sets and starts the in-game mission timer * - * * - * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetMissionTimer(lua_State* L) { - int ret = lua_tointeger(L, -1); - Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); - Scen.MissionTimer.Start(); - Map.Redraw_Tab(); - return 1; - } - - /*********************************************************************************************** - * Script_SetGlobalValue - Sets the global value to "set" position (on) * - * * - * SCRIPT INPUT: global_value (int) - The global to set to "set" * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetGlobalValue(lua_State* L) { - int global_value = lua_tointeger(L, -1); - Scen.Set_Global_To(global_value, true); - return 1; - } - - /*********************************************************************************************** - * Script_ClearGlobalValue - Sets the global value to "clear" position (off) * - * * - * SCRIPT INPUT: global_value (int) - The global to set to "off" * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_ClearGlobalValue(lua_State* L) { - int global_value = lua_tointeger(L, -1); - Scen.Set_Global_To(global_value, false); - return 1; - } - - /*********************************************************************************************** - * Script_AutoBaseBuilding - Sets AI basebuilding on/off for given house * - * * - * SCRIPT INPUT: houseType (int) - The player (house) index of the AI * - * on_off (bool) - Enable / disable auto base building * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AutoBaseBuilding(lua_State* L) { - int houseType = lua_tointeger(L, 1); - bool on_off = lua_toboolean(L, 2); - - HouseClass* this_house = Houses.Ptr(houseType); - if (this_house != NULL) { - this_house->IsBaseBuilding = on_off; - } - - return 1; - } - - /*********************************************************************************************** - * Script_StopMissionTimer - Stops the in-game mission timer * - * * - * SCRIPT INPUT: none * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreepShadow(lua_State* L) { - Map.Encroach_Shadow(PlayerPtr); - return 1; - } - - /*********************************************************************************************** - * Script_DestroyTriggerBuilding - Stops the in-game mission timer * - * * - * SCRIPT INPUT: triggerIndex (int) - The index of the trigger previously created * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DestroyTriggerBuilding(lua_State* L) { - int triggerIndex = lua_tointeger(L, -1); - - TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); - - if(this_trigger != NULL){ - for (int index = 0; index < Buildings.Count(); index++) { - - BuildingClass* this_building = Buildings.Ptr(index); - - if (this_building->Trigger == this_trigger && this_building->Strength > 0) { - int damage = this_building->Strength; - this_building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); - } - - } - } - - return 1; - } - - /*********************************************************************************************** - * Script_GiveOneTimeSpecialWeapon - Gives specified house a one time special weapon * - * * - * SCRIPT INPUT: weaponType (int) - The special weapon type to give * - * houseType (int) - The player (house) index to give this to (-1 for all) * - * immediate(bool) - should the weapon be fully charged? * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GiveOneTimeSpecialWeapon(lua_State* L) { - - int weaponType = lua_tointeger(L, 1); - - int houseType = -1; - bool immediate = -1; - - // Optional houseType parameter - if (lua_gettop(L) >= 2) { - houseType = lua_tointeger(L, 2); - } - - // Optional immediate parameter - if (lua_gettop(L) >= 2) { - immediate = lua_toboolean(L, 3); - } - - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - - HouseClass* house = Houses.Ptr(h_index); - - if (house->ID == houseType || houseType == -1) { - - house->SuperWeapon[weaponType].Enable(TACTION_1_SPECIAL, true); - - if (immediate) { - house->SuperWeapon[weaponType].Forced_Charge(true); - } - - if (PlayerPtr == house) { - Map.Add(RTTI_SPECIAL, weaponType); - Map.Column[1].Flag_To_Redraw(); - } - - if (houseType >= 0) { - break; - } - } - - } - - return 1; - } - - /*********************************************************************************************** - * Script_GiveSpecialWeapon - Gives specified house a repeating special weapon * - * * - * SCRIPT INPUT: weaponType (int) - The special weapon type to give * - * houseType (int) - The player (house) index to give this to (-1 for all) * - * immediate(bool) - should the weapon be fully charged? * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GiveSpecialWeapon(lua_State* L) { - - int weaponType = lua_tointeger(L, 1); - - int houseType = -1; - bool immediate = -1; - - // Optional houseType parameter - if (lua_gettop(L) >= 2) { - houseType = lua_tointeger(L, 2); - } - - // Optional immediate parameter - if (lua_gettop(L) ==3) { - immediate = lua_toboolean(L, 3); - } - - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - - HouseClass* house = Houses.Ptr(h_index); - - if (house->ID == houseType || houseType == -1) { - - house->SuperWeapon[weaponType].Enable(TACTION_FULL_SPECIAL, true); - - if (immediate) { - house->SuperWeapon[weaponType].Forced_Charge(true); - } - - if (PlayerPtr == house) { - Map.Add(RTTI_SPECIAL, weaponType); - Map.Column[1].Flag_To_Redraw(); - } - - if (houseType >= 0) { - break; - } - } - - } - - return 1; - } - - /*********************************************************************************************** - * Script_DesignatePreferredTarget - Gives specified house a prefferable type of target * - * * - * SCRIPT INPUT: houseType (int) - The player (house) index to set targetting for (or -1) * - * targetType (int) - The target type index to set to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_DesignatePreferredTarget(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - unsigned char targetType = lua_tointeger(L, 2); - - if(targetType >= 1 && targetType <= 11){ - - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - - HouseClass* house = Houses.Ptr(h_index); - - if (house->ID == houseType || houseType == -1) { - - house->PreferredTarget = (QuarryType)targetType; - - // Discontinue giving out free cash if a specific house was specified - if (houseType >= 0) { - break; - } - - } - - } - - } - - return 1; - } - - /*********************************************************************************************** - * Script_LaunchFakeNukes - All nuke silos begin launching duds * - * * - * SCRIPT INPUT: none * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_LaunchFakeNukes(lua_State* L) { - + if (this_trigger != NULL) { for (int index = 0; index < Buildings.Count(); index++) { - BuildingClass* bldg = Buildings.Ptr(index); - if (*bldg == STRUCT_MSLO) { - bldg->Assign_Mission(MISSION_MISSILE); + + BuildingClass* this_building = Buildings.Ptr(index); + + if (this_building->Trigger == this_trigger && this_building->Strength > 0) { + int damage = this_building->Strength; + this_building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + + } + } + + return 1; +} + +/*********************************************************************************************** + * Script_GiveOneTimeSpecialWeapon - Gives specified house a one time special weapon * + * * + * SCRIPT INPUT: weaponType (int) - The special weapon type to give * + * houseType (int) - The player (house) index to give this to (-1 for all) * + * immediate(bool) - should the weapon be fully charged? * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GiveOneTimeSpecialWeapon(lua_State* L) { + + int weaponType = lua_tointeger(L, 1); + + int houseType = -1; + bool immediate = -1; + + // Optional houseType parameter + if (lua_gettop(L) >= 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional immediate parameter + if (lua_gettop(L) >= 2) { + immediate = lua_toboolean(L, 3); + } + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->SuperWeapon[weaponType].Enable(TACTION_1_SPECIAL, true); + + if (immediate) { + house->SuperWeapon[weaponType].Forced_Charge(true); + } + + if (PlayerPtr == house) { + Map.Add(RTTI_SPECIAL, weaponType); + Map.Column[1].Flag_To_Redraw(); + } + + if (houseType >= 0) { + break; } } - return 1; } + return 1; +} + +/*********************************************************************************************** + * Script_GiveSpecialWeapon - Gives specified house a repeating special weapon * + * * + * SCRIPT INPUT: weaponType (int) - The special weapon type to give * + * houseType (int) - The player (house) index to give this to (-1 for all) * + * immediate(bool) - should the weapon be fully charged? * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GiveSpecialWeapon(lua_State* L) { + + int weaponType = lua_tointeger(L, 1); + + int houseType = -1; + bool immediate = -1; + + // Optional houseType parameter + if (lua_gettop(L) >= 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional immediate parameter + if (lua_gettop(L) == 3) { + immediate = lua_toboolean(L, 3); + } + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->SuperWeapon[weaponType].Enable(TACTION_FULL_SPECIAL, true); + + if (immediate) { + house->SuperWeapon[weaponType].Forced_Charge(true); + } + + if (PlayerPtr == house) { + Map.Add(RTTI_SPECIAL, weaponType); + Map.Column[1].Flag_To_Redraw(); + } + + if (houseType >= 0) { + break; + } + } + + } + + return 1; +} + +/*********************************************************************************************** + * Script_DesignatePreferredTarget - Gives specified house a prefferable type of target * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to set targetting for (or -1) * + * targetType (int) - The target type index to set to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_DesignatePreferredTarget(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + unsigned char targetType = lua_tointeger(L, 2); + + if (targetType >= 1 && targetType <= 11) { + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->PreferredTarget = (QuarryType)targetType; + + // Discontinue giving out free cash if a specific house was specified + if (houseType >= 0) { + break; + } + + } + + } + + } + + return 1; +} + +/*********************************************************************************************** + * Script_LaunchFakeNukes - All nuke silos begin launching duds * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_LaunchFakeNukes(lua_State* L) { + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass* bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_MSLO) { + bldg->Assign_Mission(MISSION_MISSILE); + } + } + + return 1; +} + /********************************************************************************************** * Red Alert Vanilla Events * *=============================================================================================*/ - /*********************************************************************************************** - * Script_CreateCellCallback - Creates an ENTERED_BY trigger and attaches a callback * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index to test for entry * - * in_x (int) - The cell/X location of the cell to test * - * in_y (int) - The cell/Y location of the cell to test * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CellCallback(lua_State* L) { +/*********************************************************************************************** + * Script_CreateCellCallback - Creates an ENTERED_BY trigger and attaches a callback * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index to test for entry * + * in_x (int) - The cell/X location of the cell to test * + * in_y (int) - The cell/Y location of the cell to test * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CellCallback(lua_State* L) { - // TODO: option 6 arguments to allow x2,y2 (for a rect) + // TODO: option 6 arguments to allow x2,y2 (for a rect) - int houseType = lua_tointeger(L, 1); - int in_x = lua_tointeger(L, 2); - int in_y = lua_tointeger(L, 3); + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); - char* in_function = ""; + char* in_function = ""; - // Optional callback function - if (lua_gettop(L) >= 4) { - in_function = (char*)lua_tostring(L, 4); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_PLAYER_ENTERED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass * this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - // Get the cell and set the trigger - this_trigger->Cell = XY_Cell(in_x, in_y); - Map[XY_Cell(in_x, in_y)].Trigger = this_trigger; - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; + // Optional callback function + if (lua_gettop(L) >= 4) { + in_function = (char*)lua_tostring(L, 4); } - /*********************************************************************************************** - * Script_CreateSpiedByCallback - Creates a SPIED_BY trigger and attaches a callback * - * * - * SCRIPT INPUT: objectID (int) - The building ID being spied * - * houseType (int) - The house (player) index doing the spying * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SpiedByCallback(lua_State* L) { + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { - int objectID = lua_tointeger(L, 1); - int houseType = lua_tointeger(L, 2); - - char* in_function = ""; + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); + this_trigger_type->Event1 = TEVENT_PLAYER_ENTERED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } - BuildingClass* this_building = Buildings.Ptr(objectID); + // Get the cell and set the trigger + this_trigger->Cell = XY_Cell(in_x, in_y); + Map[XY_Cell(in_x, in_y)].Trigger = this_trigger; - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count() && this_building != NULL) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)this_building->House->ID; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_SPIED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - // Set the building - this_building->Trigger = this_trigger; - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); return 1; + } - /*********************************************************************************************** - * Script_CreateDiscoveryCallback - Creates a TEVENT_DISCOVERED trigger and attaches a callback* - * * - * Trigger is sprung when the given object is discovered * - * * - * SCRIPT INPUT: objectID (int) - The object's ID being discovered * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_ObjectDiscoveryCallback(lua_State* L) { + lua_pushnumber(L, -1); - int objectID = lua_tointeger(L, 1); + return 1; +} - char* in_function = ""; +/*********************************************************************************************** + * Script_CreateSpiedByCallback - Creates a SPIED_BY trigger and attaches a callback * + * * + * SCRIPT INPUT: objectID (int) - The building ID being spied * + * houseType (int) - The house (player) index doing the spying * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_SpiedByCallback(lua_State* L) { - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); + int objectID = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + BuildingClass* this_building = Buildings.Ptr(objectID); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count() && this_building != NULL) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)this_building->House->ID; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_SPIED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } - // A house must be specified - if (objectID >= 0 ) { + // Set the building + this_building->Trigger = this_trigger; - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_DISCOVERED; - this_trigger_type->EventControl = MULTI_ONLY; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - Script_SetObjectTrigger(objectID, this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); return 1; + } - /*********************************************************************************************** - * Script_HouseDiscoveredCallback - Initiates when given house is discovered * - * * - * Trigger is sprung when an object belonging to the given house is discovered * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_HouseDiscoveredCallback(lua_State* L) { + lua_pushnumber(L, -1); - int houseType = lua_tointeger(L, 1); + return 1; +} - char* in_function = ""; +/*********************************************************************************************** + * Script_CreateDiscoveryCallback - Creates a TEVENT_DISCOVERED trigger and attaches a callback* + * * + * Trigger is sprung when the given object is discovered * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_ObjectDiscoveryCallback(lua_State* L) { - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } + int objectID = lua_tointeger(L, 1); - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { + char* in_function = ""; - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; - - this_trigger_type->Event1 = TEVENT_HOUSE_DISCOVERED; - this_trigger_type->EventControl = MULTI_ONLY; - - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); } - /*********************************************************************************************** - * Script_ObjectAttackedCallback - Trigger that springs when object is attacked by anyone * - * * - * SCRIPT INPUT: objectID (int) - The object's ID being discovered * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_ObjectAttackedCallback(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // An object must be specified - if (objectID >= 0) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_ATTACKED; - this_trigger_type->EventControl = MULTI_ONLY; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - Script_SetObjectTrigger(objectID, this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_ObjectDestroyedCallback - Trigger that springs when object is destroyed by anyone * - * * - * SCRIPT INPUT: objectID (int) - The object's ID being discovered * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_ObjectDestroyedCallback(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // An object must be specified - if (objectID >= 0) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - Script_SetObjectTrigger(objectID, this_trigger); - - - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_AnyEventCallback - * - * * - * SCRIPT INPUT: objectID (int) - The object's ID being discovered * - * houseType (int) - The house (player) index * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AnyEventCallback(lua_State* L) { - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 1) { - in_function = (char*)lua_tostring(L, 1); - } + // A house must be specified + if (objectID >= 0) { // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -1457,7 +1214,7 @@ this_trigger_type->IsActive = 1; this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - this_trigger_type->Event1 = TEVENT_ANY; + this_trigger_type->Event1 = TEVENT_DISCOVERED; this_trigger_type->EventControl = MULTI_ONLY; // Create instance of trigger type @@ -1468,7 +1225,7 @@ strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } - LogicTriggers.Add(this_trigger); + Script_SetObjectTrigger(objectID, this_trigger); // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1477,289 +1234,55 @@ } - /*********************************************************************************************** - * Script_AllUnitsDestroyedCallback - Called when all of a house's units are destroyed * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllUnitsDestroyedCallback(lua_State* L) { + lua_pushnumber(L, -1); - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; + return 1; +} - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } +/*********************************************************************************************** + * Script_HouseDiscoveredCallback - Initiates when given house is discovered * + * * + * Trigger is sprung when an object belonging to the given house is discovered * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_HouseDiscoveredCallback(lua_State* L) { - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { + int houseType = lua_tointeger(L, 1); - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + char* in_function = ""; - this_trigger_type->Event1 = TEVENT_UNITS_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); } - /*********************************************************************************************** - * Script_AllBuildingsDestroyedCallback - Called when all of a house's buildings are destroyed* - * * - * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllBuildingsDestroyedCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_BUILDINGS_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_AllDestroyedCallback - Called when everything belonging to a house is destyroyed * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllDestroyedCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HousesType::HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_ALL_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_CreditsReachedCallback - Called when given house meets given amount of credits * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index to test against * - * in_credits (int) - The amount of credits * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreditsReachedCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_credits = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_CREDITS; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value= in_credits; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_TimeReachedCallback - Called when the given amount of time has elapsed * - * * - * SCRIPT INPUT: in_time (int) - The time (in tenths of a second) that was reached * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_TimeReachedCallback(lua_State* L) { - - int in_time = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); this_trigger_type->House = HOUSE_NONE; this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; - this_trigger_type->Event1 = TEVENT_TIME; + this_trigger_type->Event1 = TEVENT_HOUSE_DISCOVERED; this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = in_time; + + this_trigger_type->Event1.Data.House = (HousesType)houseType; // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); if (in_function[0] != '\0') { @@ -1767,7 +1290,7 @@ strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } - LogicTriggers.Add(this_trigger); + HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); // Output the new trigger's ID lua_pushnumber(L, this_trigger->ID); @@ -1776,1034 +1299,39 @@ } - /*********************************************************************************************** - * Script_MissionTimerCallback - Called when the mission timer has expired * - * * - * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_MissionTimerCallback(lua_State* L) { + lua_pushnumber(L, -1); - char* in_function = ""; + return 1; +} - // Optional callback function - if (lua_gettop(L) >= 1) { - in_function = (char*)lua_tostring(L, 1); - } +/*********************************************************************************************** + * Script_ObjectAttackedCallback - Trigger that springs when object is attacked by anyone * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_ObjectAttackedCallback(lua_State* L) { - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + int objectID = lua_tointeger(L, 1); - this_trigger_type->Event1 = TEVENT_MISSION_TIMER_EXPIRED; - this_trigger_type->EventControl = MULTI_ONLY; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - LogicTriggers.Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; + char* in_function = ""; + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); } - /*********************************************************************************************** - * Script_UnitsDestroyedCallback - Called when given number of a house's units are destroyed * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * - * in_number (int) - The number of units that have must be destroyed * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_UnitsDestroyedCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_number = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_NUNITS_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = in_number; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /************************************************************************************************ - * Script_BuildingsDestroyedCallback - Called when number of a house's buildings are destroyed * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * - * in_number (int) - The number of buildings that have must be destroyed * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *==============================================================================================*/ - static int Script_BuildingsDestroyedCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_number = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_NBUILDINGS_DESTROYED; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = in_number; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_NoFactoriesCallback - Called when no more factories exist for a house * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_NoFactoriesCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_NOFACTORIES; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = -1; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_CivilianEscapeCallback - Called when civilians get evacuated from map * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner of civilians escaped * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CivilianEscapeCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_EVAC_CIVILIAN; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = -1; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - LogicTriggers.Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_BuildingBuiltCallback - Called when a specified house builds a specified building * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that built the building * - * objectType (int) - The index / building type to check for * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_BuildingBuiltCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int objectType = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_BUILD; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = objectType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_UnitBuiltCallback - Called when a specified house builds a specified unit * - * * - * Does not trigger for units that spawn with buildings (ie. ore truck) * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * - * objectType (int) - The index / unit type to check for * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_UnitBuiltCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int objectType = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_BUILD_UNIT; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = objectType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_InfantryBuiltCallback - Called when a specified house builds a specified infantry * - * * - * Does not trigger for infantry gained by selling a building * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that built the infantry * - * objectType (int) - The index / infantry type to check for * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_InfantryBuiltCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int objectType = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_BUILD_INFANTRY; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = objectType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_AircraftBuiltCallback - Called when a specified house builds a specified aircraft * - * * - * Does not trigger for units that spawn with buildings * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * - * objectType (int) - The index / unit type to check for * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AircraftBuiltCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int objectType = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_BUILD_AIRCRAFT; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = objectType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_TeamLeavesMapCallback - Called when the specified team leaves the map * - * * - * SCRIPT INPUT: in_team (int) - The team index that has left the map * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_TeamLeavesMapCallback(lua_State* L) { - - int in_team = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - TeamTypeClass* this_team = TeamTypes.Ptr(in_team); - - if (this_team != NULL) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = this_team->House; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_LEAVES_MAP; - this_trigger_type->EventControl = MULTI_ONLY; - - - this_trigger_type->Event1.Team = this_team; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - LogicTriggers.Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - // Output the new trigger's ID - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_ZoneEntryCallback - Called when a specified house enters zone of given position * - * * - * Does not trigger for units that spawn with buildings * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * - * in_x (int) - The cell/X location of the cell to test * - * in_y (int) - The cell/Y location of the cell to test * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_ZoneEntryCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_x = lua_tointeger(L, 2); - int in_y = lua_tointeger(L, 3); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 4) { - in_function = (char*)lua_tostring(L, 4); - } - - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_ENTERS_ZONE; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - // Get the cell and set the trigger - this_trigger->Cell = XY_Cell(in_x, in_y); - MapTriggers.Add(this_trigger); - - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_HorizontalCrossCallback - Called when a specified house's units cross the given X * - * * - * Does not trigger for units that spawn with buildings * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * - * in_x (int) - The cell/X location that must be crossed * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_HorizontalCrossCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_x = lua_tointeger(L,2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_CROSS_VERTICAL; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - // Find a free spot and set the trigger - int addY = 0; - for (addY = 0; addY < Map.MapCellHeight; addY++) { - TriggerClass* trigger = Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger; - if (trigger == NULL) { - this_trigger->Cell = XY_Cell(in_x, Map.MapCellY + addY); - Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger = this_trigger; - break; - } - } - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_VerticalCrossCallback - Called when a specified house's units cross the given Y * - * * - * Does not trigger for units that spawn with buildings * - * * - * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * - * in_y (int) - The cell/Y location that must be crossed * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_VerticalCrossCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - int in_y = lua_tointeger(L, 2); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); - } - - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_CROSS_HORIZONTAL; // <- Their description doesn't make sense, not mine - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - // Find a free spot and set the trigger - int addX = 0; - for (addX = 0; addX < Map.MapCellWidth; addX++) { - TriggerClass* trigger = Map[XY_Cell(Map.MapCellX + addX, in_y)].Trigger; - if (trigger == NULL) { - this_trigger->Cell = XY_Cell(Map.MapCellX+ addX, in_y); - Map[XY_Cell(Map.MapCellX+ addX, in_y)].Trigger = this_trigger; - break; - } - } - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - - } - - /*********************************************************************************************** - * Script_GlobalSetCallback - activated when the global value is in the "set" poisition * - * * - * SCRIPT INPUT: in_global - The global value to set * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GlobalSetCallback(lua_State* L) { - - int in_global = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_GLOBAL_SET; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = in_global; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - LogicTriggers.Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - } - - /*********************************************************************************************** - * Script_GlobalSetCallback - activated when the global value is in the "cleared" poisition * - * * - * SCRIPT INPUT: in_global - The global value to set to "cleared" * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GlobalClearedCallback(lua_State* L) { - - int in_global = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - - this_trigger_type->Event1 = TEVENT_GLOBAL_CLEAR; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = in_global; - - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - LogicTriggers.Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - } - - /*********************************************************************************************** - * Script_LowPowerCallback - Initiates when given house becomes low on power * - * * - * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_LowPowerCallback(lua_State* L) { - - int houseType = lua_tointeger(L, 1); - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 2) { - in_function = (char*)lua_tostring(L, 2); - } - - // A house must be specified - if (houseType >= 0 && houseType < HouseTypes.Count()) { - - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = HOUSE_NONE; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; - - this_trigger_type->Event1 = TEVENT_LOW_POWER; - this_trigger_type->EventControl = MULTI_ONLY; - - this_trigger_type->Event1.Data.House = (HousesType)houseType; - - // Create instance of trigger type - - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); - - return 1; - } - - /*********************************************************************************************** - * Script_AllBridgesDestroyedCallback - Called when all map's bridges have been destroyed * - * * - * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_AllBridgesDestroyedCallback(lua_State* L) { - - char* in_function = ""; - - // Optional callback function - if (lua_gettop(L) >= 1) { - in_function = (char*)lua_tostring(L, 1); - } + // An object must be specified + if (objectID >= 0) { // Create trigger type TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); @@ -2811,7 +1339,7 @@ this_trigger_type->IsActive = 1; this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; - this_trigger_type->Event1 = TEVENT_ALL_BRIDGES_DESTROYED; + this_trigger_type->Event1 = TEVENT_ATTACKED; this_trigger_type->EventControl = MULTI_ONLY; // Create instance of trigger type @@ -2822,6 +1350,1037 @@ strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } + Script_SetObjectTrigger(objectID, this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_ObjectDestroyedCallback - Trigger that springs when object is destroyed by anyone * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_ObjectDestroyedCallback(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // An object must be specified + if (objectID >= 0) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + Script_SetObjectTrigger(objectID, this_trigger); + + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_AnyEventCallback - * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * houseType (int) - The house (player) index * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AnyEventCallback(lua_State* L) { + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ANY; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + +} + +/*********************************************************************************************** + * Script_AllUnitsDestroyedCallback - Called when all of a house's units are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllUnitsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_UNITS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_AllBuildingsDestroyedCallback - Called when all of a house's buildings are destroyed* + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllBuildingsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILDINGS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_AllDestroyedCallback - Called when everything belonging to a house is destyroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ALL_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_CreditsReachedCallback - Called when given house meets given amount of credits * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index to test against * + * in_credits (int) - The amount of credits * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreditsReachedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_credits = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CREDITS; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_credits; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_TimeReachedCallback - Called when the given amount of time has elapsed * + * * + * SCRIPT INPUT: in_time (int) - The time (in tenths of a second) that was reached * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TimeReachedCallback(lua_State* L) { + + int in_time = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_TIME; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_time; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + +} + +/*********************************************************************************************** + * Script_MissionTimerCallback - Called when the mission timer has expired * + * * + * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_MissionTimerCallback(lua_State* L) { + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_MISSION_TIMER_EXPIRED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + +} + +/*********************************************************************************************** + * Script_UnitsDestroyedCallback - Called when given number of a house's units are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * + * in_number (int) - The number of units that have must be destroyed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_UnitsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_number = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NUNITS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_number; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/************************************************************************************************ + * Script_BuildingsDestroyedCallback - Called when number of a house's buildings are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * + * in_number (int) - The number of buildings that have must be destroyed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *==============================================================================================*/ +static int Script_BuildingsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_number = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NBUILDINGS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_number; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_NoFactoriesCallback - Called when no more factories exist for a house * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_NoFactoriesCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NOFACTORIES; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = -1; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_CivilianEscapeCallback - Called when civilians get evacuated from map * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner of civilians escaped * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CivilianEscapeCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_EVAC_CIVILIAN; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = -1; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_BuildingBuiltCallback - Called when a specified house builds a specified building * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the building * + * objectType (int) - The index / building type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_BuildingBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_UnitBuiltCallback - Called when a specified house builds a specified unit * + * * + * Does not trigger for units that spawn with buildings (ie. ore truck) * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * objectType (int) - The index / unit type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_UnitBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_UNIT; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_InfantryBuiltCallback - Called when a specified house builds a specified infantry * + * * + * Does not trigger for infantry gained by selling a building * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the infantry * + * objectType (int) - The index / infantry type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_InfantryBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_INFANTRY; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_AircraftBuiltCallback - Called when a specified house builds a specified aircraft * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * objectType (int) - The index / unit type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AircraftBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_AIRCRAFT; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_TeamLeavesMapCallback - Called when the specified team leaves the map * + * * + * SCRIPT INPUT: in_team (int) - The team index that has left the map * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TeamLeavesMapCallback(lua_State* L) { + + int in_team = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + TeamTypeClass* this_team = TeamTypes.Ptr(in_team); + + if (this_team != NULL) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = this_team->House; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_LEAVES_MAP; + this_trigger_type->EventControl = MULTI_ONLY; + + + this_trigger_type->Event1.Team = this_team; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + LogicTriggers.Add(this_trigger); // Output the new trigger's ID @@ -2831,315 +2390,784 @@ } - /*********************************************************************************************** - * Script_BuildingExistsCallback - Called if a given building exists for a given house * - * * - * SCRIPT INPUT: buildingType (int) - The building type to check for * - * houseType (int) - The house (player) index owner of said building * - * in_function (string) - The callback function to execute upon trigger * - * * - * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_BuildingExistsCallback(lua_State* L) { + // Output the new trigger's ID + lua_pushnumber(L, -1); - int buildingType = lua_tointeger(L, 1); - int houseType = lua_tointeger(L,2); + return 1; - char* in_function = ""; +} - // Optional callback function - if (lua_gettop(L) >= 3) { - in_function = (char*)lua_tostring(L, 3); +/*********************************************************************************************** + * Script_ZoneEntryCallback - Called when a specified house enters zone of given position * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * in_x (int) - The cell/X location of the cell to test * + * in_y (int) - The cell/Y location of the cell to test * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_ZoneEntryCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 4) { + in_function = (char*)lua_tostring(L, 4); + } + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ENTERS_ZONE; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); } - // A house must be specified - if (buildingType>=0 && houseType >= 0 && houseType < HouseTypes.Count()) { + // Get the cell and set the trigger + this_trigger->Cell = XY_Cell(in_x, in_y); + MapTriggers.Add(this_trigger); - // Create trigger type - TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); - this_trigger_type->House = (HousesType)houseType; - this_trigger_type->IsActive = 1; - this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; - this_trigger_type->Event1 = TEVENT_BUILDING_EXISTS; - this_trigger_type->EventControl = MULTI_ONLY; - this_trigger_type->Event1.Data.Value = buildingType; - // Create instance of trigger type - TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); - - if (in_function[0] != '\0') { - // Copy the callback function into the trigger - strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); - } - - HouseTriggers[(HousesType)houseType].Add(this_trigger); - - // Output the new trigger's ID - lua_pushnumber(L, this_trigger->ID); - - return 1; - - } - - lua_pushnumber(L, -1); + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); return 1; + } + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_HorizontalCrossCallback - Called when a specified house's units cross the given X * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * + * in_x (int) - The cell/X location that must be crossed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_HorizontalCrossCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CROSS_VERTICAL; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + // Find a free spot and set the trigger + int addY = 0; + for (addY = 0; addY < Map.MapCellHeight; addY++) { + TriggerClass* trigger = Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger; + if (trigger == NULL) { + this_trigger->Cell = XY_Cell(in_x, Map.MapCellY + addY); + Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger = this_trigger; + break; + } + } + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_VerticalCrossCallback - Called when a specified house's units cross the given Y * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * + * in_y (int) - The cell/Y location that must be crossed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_VerticalCrossCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CROSS_HORIZONTAL; // <- Their description doesn't make sense, not mine + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + // Find a free spot and set the trigger + int addX = 0; + for (addX = 0; addX < Map.MapCellWidth; addX++) { + TriggerClass* trigger = Map[XY_Cell(Map.MapCellX + addX, in_y)].Trigger; + if (trigger == NULL) { + this_trigger->Cell = XY_Cell(Map.MapCellX + addX, in_y); + Map[XY_Cell(Map.MapCellX + addX, in_y)].Trigger = this_trigger; + break; + } + } + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + +} + +/*********************************************************************************************** + * Script_GlobalSetCallback - activated when the global value is in the "set" poisition * + * * + * SCRIPT INPUT: in_global - The global value to set * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GlobalSetCallback(lua_State* L) { + + int in_global = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_GLOBAL_SET; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_global; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; +} + +/*********************************************************************************************** + * Script_GlobalSetCallback - activated when the global value is in the "cleared" poisition * + * * + * SCRIPT INPUT: in_global - The global value to set to "cleared" * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GlobalClearedCallback(lua_State* L) { + + int in_global = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_GLOBAL_CLEAR; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_global; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; +} + +/*********************************************************************************************** + * Script_LowPowerCallback - Initiates when given house becomes low on power * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_LowPowerCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 2) { + in_function = (char*)lua_tostring(L, 2); + } + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; + + this_trigger_type->Event1 = TEVENT_LOW_POWER; + this_trigger_type->EventControl = MULTI_ONLY; + + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + +/*********************************************************************************************** + * Script_AllBridgesDestroyedCallback - Called when all map's bridges have been destroyed * + * * + * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_AllBridgesDestroyedCallback(lua_State* L) { + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 1) { + in_function = (char*)lua_tostring(L, 1); + } + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ALL_BRIDGES_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + +} + +/*********************************************************************************************** + * Script_BuildingExistsCallback - Called if a given building exists for a given house * + * * + * SCRIPT INPUT: buildingType (int) - The building type to check for * + * houseType (int) - The house (player) index owner of said building * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_BuildingExistsCallback(lua_State* L) { + + int buildingType = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + + char* in_function = ""; + + // Optional callback function + if (lua_gettop(L) >= 3) { + in_function = (char*)lua_tostring(L, 3); + } + + // A house must be specified + if (buildingType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; + this_trigger_type->Event1 = TEVENT_BUILDING_EXISTS; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = buildingType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + if (in_function[0] != '\0') { + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + } + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; +} + /********************************************************************************************** * Trigger Utility Functions * *=============================================================================================*/ - /*********************************************************************************************** - * Script_Script_TriggerAddCell - Adds a cell to any cell based callback * - * * - * The map editor will always be the fastest way to do this, but here in case it's needed * - * * - * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which to add a cell * - * in_x (int) - The Cell/X location of the new cell * - * in_y (int) - The Cell/Y location of the new cell * - * in_x2 (int) (opt. gr. 2) - The second Cell/X location (for bounding box) * - * in_y2 (int) (opt. gr. 2) - The second Cell/Y location (for bounding box) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_TriggerAddCell(lua_State* L) { +/*********************************************************************************************** + * Script_Script_TriggerAddCell - Adds a cell to any cell based callback * + * * + * The map editor will always be the fastest way to do this, but here in case it's needed * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which to add a cell * + * in_x (int) - The Cell/X location of the new cell * + * in_y (int) - The Cell/Y location of the new cell * + * in_x2 (int) (opt. gr. 2) - The second Cell/X location (for bounding box) * + * in_y2 (int) (opt. gr. 2) - The second Cell/Y location (for bounding box) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TriggerAddCell(lua_State* L) { - int triggerIndex = lua_tointeger(L, 1); + int triggerIndex = lua_tointeger(L, 1); - int in_x = lua_tointeger(L, 2); - int in_y = lua_tointeger(L, 3); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); - int in_x2=in_x; - int in_y2=in_y; + int in_x2 = in_x; + int in_y2 = in_y; - // Optional secondary group bounding box parameter - if (lua_gettop(L) == 5) { - in_x2 = lua_tointeger(L, 4); - in_y2 = lua_tointeger(L, 5); - } + // Optional secondary group bounding box parameter + if (lua_gettop(L) == 5) { + in_x2 = lua_tointeger(L, 4); + in_y2 = lua_tointeger(L, 5); + } - TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); - if(this_trigger != NULL){ - for (int _x = in_x; _x <= in_x2; _x++) { - for (int _y = in_y; _y <= in_y2; _y++) { - Map[XY_Cell(_x, _y)].Trigger = this_trigger; - } + if (this_trigger != NULL) { + for (int _x = in_x; _x <= in_x2; _x++) { + for (int _y = in_y; _y <= in_y2; _y++) { + Map[XY_Cell(_x, _y)].Trigger = this_trigger; } } - - + } + + + return 1; +} + +/*********************************************************************************************** + * Script_SetTriggerCallback - Adds a lua callback to an existing trigger * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger of which to get * + * * + * callbackName (string) - The function name to be called when the * + * actions have been met * + * * + * actionIndex (int) (opt.) - 0: always callback on any of the given paths * + * of the trigger * + * 1: only callback on first action * + * 2: only callback on second action * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_SetTriggerCallback(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + + const char* callbackName = lua_tostring(L, 2); + char actionIndex = 0; + + // Optional actionIndex parameter + if (lua_gettop(L) == 3) { + actionIndex = lua_tointeger(L, 2); + } + + TriggerClass* trigger = Triggers.Ptr(triggerIndex); + + if (trigger != NULL) { + + strncpy(trigger->MapScriptCallback, callbackName, sizeof(trigger->MapScriptCallback) - 1); + trigger->MapScriptActionIndex = actionIndex; + + } + + return 1; +} + +/*********************************************************************************************** + * Script_GetCellObject - Gets an object (if any) on a cell and returns the ID * + * * + * SCRIPT INPUT: in_x (int) - The cell/X location of the cell to pick from * + * in_y (int) - The cell/Y location of the cell to pick from * + * * + * SCRIPT OUTPUT: result (number) - The ID of the building on the cell, or -1 if none * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GetCellObject(lua_State* L) { + + int in_x = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + long result = -1; + + ObjectClass* this_object = Map[XY_Cell(in_x, in_y)].Cell_Object(); + + if (this_object == NULL) { + + lua_pushnumber(L, -1); return 1; } - /*********************************************************************************************** - * Script_SetTriggerCallback - Adds a lua callback to an existing trigger * - * * - * SCRIPT INPUT: triggerIndex (int) - The trigger of which to get * - * * - * callbackName (string) - The function name to be called when the * - * actions have been met * - * * - * actionIndex (int) (opt.) - 0: always callback on any of the given paths * - * of the trigger * - * 1: only callback on first action * - * 2: only callback on second action * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetTriggerCallback(lua_State* L) { + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - int triggerIndex = lua_tointeger(L, 1); + CacheObject->ID = this_object->ID; + CacheObject->RTTI = this_object->RTTI; + CacheObject->classIndex = -1; - const char* callbackName = lua_tostring(L, 2); - char actionIndex = 0; - - // Optional actionIndex parameter - if (lua_gettop(L) == 3) { - actionIndex = lua_tointeger(L, 2); - } - - TriggerClass* trigger = Triggers.Ptr(triggerIndex); - - if (trigger != NULL) { - - strncpy(trigger->MapScriptCallback, callbackName, sizeof(trigger->MapScriptCallback) - 1); - trigger->MapScriptActionIndex = actionIndex; - - } - - return 1; + switch (this_object->RTTI) { + case RTTI_BUILDING: { + CacheObject->classIndex = Script_BuildingIndexFromID(this_object->ID); + }break; + case RTTI_UNIT: { + CacheObject->classIndex = Script_UnitIndexFromID(this_object->ID); + }break; + case RTTI_AIRCRAFT: { + CacheObject->classIndex = Script_AircraftIndexFromID(this_object->ID); + }break; + case RTTI_INFANTRY: { + CacheObject->classIndex = Script_InfantryIndexFromID(this_object->ID); + }break; + case RTTI_VESSEL: { + CacheObject->classIndex = Script_VesselIndexFromID(this_object->ID); + }break; } - /*********************************************************************************************** - * Script_GetCellObject - Gets an object (if any) on a cell and returns the ID * - * * - * SCRIPT INPUT: in_x (int) - The cell/X location of the cell to pick from * - * in_y (int) - The cell/Y location of the cell to pick from * - * * - * SCRIPT OUTPUT: result (number) - The ID of the building on the cell, or -1 if none * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetCellObject(lua_State* L) { - - int in_x = lua_tointeger(L, 1); - int in_y = lua_tointeger(L, 2); - - long result = -1; - - ObjectClass* this_object = Map[XY_Cell(in_x, in_y)].Cell_Object(); - - if (this_object == NULL) { - - lua_pushnumber(L, -1); - return 1; - } - - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - - CacheObject->ID = this_object->ID; - CacheObject->RTTI = this_object->RTTI; - CacheObject->classIndex = -1; - - switch (this_object->RTTI) { - case RTTI_BUILDING: { - CacheObject->classIndex = Script_BuildingIndexFromID(this_object->ID); - }break; - case RTTI_UNIT: { - CacheObject->classIndex = Script_UnitIndexFromID(this_object->ID); - }break; - case RTTI_AIRCRAFT: { - CacheObject->classIndex = Script_AircraftIndexFromID(this_object->ID); - }break; - case RTTI_INFANTRY: { - CacheObject->classIndex = Script_InfantryIndexFromID(this_object->ID); - }break; - case RTTI_VESSEL: { - CacheObject->classIndex = Script_VesselIndexFromID(this_object->ID); - }break; - } - - if (CacheObject->classIndex == -1 || CacheObject->ID < 0) { - delete CacheObject; - lua_pushnumber(L, -1); - } - else { - Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->UID); // Return UID for faster lookup - } - - return 1; + if (CacheObject->classIndex == -1 || CacheObject->ID < 0) { + delete CacheObject; + lua_pushnumber(L, -1); + } + else { + Scen.mapScript->ObjectCache.push_back(CacheObject); + lua_pushnumber(L, CacheObject->UID); // Return UID for faster lookup } - /*********************************************************************************************** - * Script_Merge_Triggers - Adds the event from trigger 2 to trigger 1 and discards trigger 2 * - * * - * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which will be the result * - * mergeTriggerIndex (int)- The trigger ID of which is being merged * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_MergeTriggers(lua_State* L) { + return 1; +} - int triggerIndex = lua_tointeger(L, 1); - int mergeTriggerIndex = lua_tointeger(L, 2); +/*********************************************************************************************** + * Script_Merge_Triggers - Adds the event from trigger 2 to trigger 1 and discards trigger 2 * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which will be the result * + * mergeTriggerIndex (int)- The trigger ID of which is being merged * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_MergeTriggers(lua_State* L) { - TriggerClass* resulting_trigger = Triggers.Ptr(triggerIndex); - TriggerTypeClass* resulting_trigger_type = resulting_trigger->Class; - TriggerClass* merge_trigger = Triggers.Ptr(mergeTriggerIndex); + int triggerIndex = lua_tointeger(L, 1); + int mergeTriggerIndex = lua_tointeger(L, 2); - if (resulting_trigger != NULL && merge_trigger != NULL) { + TriggerClass* resulting_trigger = Triggers.Ptr(triggerIndex); + TriggerTypeClass* resulting_trigger_type = resulting_trigger->Class; + TriggerClass* merge_trigger = Triggers.Ptr(mergeTriggerIndex); - // Set event 2 to event 1 of the merge trigger - resulting_trigger_type->Event2.Event = (TEventType)merge_trigger->Class->Event1.Event; - resulting_trigger_type->Event2.Data.Value = (long)merge_trigger->Class->Event1.Data.Value; - resulting_trigger_type->Event2.Team = (TeamTypeClass*)merge_trigger->Class->Event1.Team; + if (resulting_trigger != NULL && merge_trigger != NULL) { - // Make this an "AND" coniditional trigger (event 1 AND event 2) - resulting_trigger->Class->EventControl = MULTI_AND; - resulting_trigger->Class->ActionControl = MULTI_ONLY; + // Set event 2 to event 1 of the merge trigger + resulting_trigger_type->Event2.Event = (TEventType)merge_trigger->Class->Event1.Event; + resulting_trigger_type->Event2.Data.Value = (long)merge_trigger->Class->Event1.Data.Value; + resulting_trigger_type->Event2.Team = (TeamTypeClass*)merge_trigger->Class->Event1.Team; - // TODO: only do these individual cleanups for actions that they represent (for efficiency) + // Make this an "AND" coniditional trigger (event 1 AND event 2) + resulting_trigger->Class->EventControl = MULTI_AND; + resulting_trigger->Class->ActionControl = MULTI_ONLY; - // Now switch over any LogicTrigger references (we're getting rid of the second trigger) - for (int t_index = 0; t_index < LogicTriggers.Count(); t_index++) { + // TODO: only do these individual cleanups for actions that they represent (for efficiency) - TriggerClass* _trigger = LogicTriggers[t_index]; + // Now switch over any LogicTrigger references (we're getting rid of the second trigger) + for (int t_index = 0; t_index < LogicTriggers.Count(); t_index++) { + TriggerClass* _trigger = LogicTriggers[t_index]; + + if (_trigger == merge_trigger) { + + LogicTriggers.Delete(t_index); + + bool find_existing_result; + for (int t2_index = 0; t2_index < LogicTriggers.Count(); t2_index++) { + if (LogicTriggers[t2_index] == resulting_trigger) { + find_existing_result = true; + break; + } + } + if (!find_existing_result) { + LogicTriggers.Add(resulting_trigger); + } + + break; + } + + } + + // Now switch over any MapTrigger references (we're getting rid of the second trigger) + for (int t_index = 0; t_index < MapTriggers.Count(); t_index++) { + TriggerClass* _trigger = MapTriggers[t_index]; + + if (_trigger != NULL) { if (_trigger == merge_trigger) { - LogicTriggers.Delete(t_index); + MapTriggers.Delete(t_index); bool find_existing_result; - for (int t2_index = 0; t2_index < LogicTriggers.Count(); t2_index++) { - if (LogicTriggers[t2_index] == resulting_trigger) { + for (int t2_index = 0; t2_index < MapTriggers.Count(); t2_index++) { + if (MapTriggers[t2_index] == resulting_trigger) { find_existing_result = true; break; } } if (!find_existing_result) { - LogicTriggers.Add(resulting_trigger); + MapTriggers.Add(resulting_trigger); } break; } - } - // Now switch over any MapTrigger references (we're getting rid of the second trigger) - for (int t_index = 0; t_index < MapTriggers.Count(); t_index++) { - TriggerClass* _trigger = MapTriggers[t_index]; + } + + // Now switch over any HouseTrigger references (we're getting rid of the second trigger) + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + for (int t_index = 0; t_index < HouseTriggers[h_index].Count(); t_index++) { + + TriggerClass* _trigger = HouseTriggers[h_index][t_index]; if (_trigger != NULL) { if (_trigger == merge_trigger) { - MapTriggers.Delete(t_index); + HouseTriggers[h_index].Delete(t_index); bool find_existing_result; - for (int t2_index = 0; t2_index < MapTriggers.Count(); t2_index++) { - if (MapTriggers[t2_index] == resulting_trigger) { + for (int t2_index = 0; t2_index < HouseTriggers[h_index].Count(); t2_index++) { + if (HouseTriggers[h_index][t2_index] == resulting_trigger) { find_existing_result = true; break; } } if (!find_existing_result) { - MapTriggers.Add(resulting_trigger); + HouseTriggers[h_index].Add(resulting_trigger); } break; @@ -3147,2564 +3175,2543 @@ } } - - // Now switch over any HouseTrigger references (we're getting rid of the second trigger) - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - for (int t_index = 0; t_index < HouseTriggers[h_index].Count(); t_index++) { - - TriggerClass* _trigger = HouseTriggers[h_index][t_index]; - - if(_trigger != NULL){ - if (_trigger == merge_trigger) { - - HouseTriggers[h_index].Delete(t_index); - - bool find_existing_result; - for (int t2_index = 0; t2_index < HouseTriggers[h_index].Count(); t2_index++) { - if (HouseTriggers[h_index][t2_index] == resulting_trigger) { - find_existing_result = true; - break; - } - } - if (!find_existing_result) { - HouseTriggers[h_index].Add(resulting_trigger); - } - - break; - } - } - - } - } - - // Go through the map and replace any celltriggers - for (int _x = Map.MapCellX; _x <= Map.MapCellX + Map.MapCellWidth; _x++) { - for (int _y = Map.MapCellY; _y <= Map.MapCellY + Map.MapCellHeight; _y++) { - if (Map[XY_Cell(_x, _y)].Trigger == merge_trigger) { - Map[XY_Cell(_x, _y)].Trigger = resulting_trigger; - } - } - } - - // Now that we've merged the contents, and it's no longer being referred to, we can safely delete the merge trigger - Detach_This_From_All(merge_trigger->As_Target()); - delete merge_trigger; } - return 1; - } - - /*********************************************************************************************** - * Script_GetTriggerByName - Gets the trigger with a given name * - * * - * SCRIPT INPUT: triggerName (string); The input trigger name * - * * - * SCRIPT OUTPUT: triggerID (int) - The index of the trigger with this name * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetTriggerByName(lua_State* L) { - - const char* triggerName= lua_tostring(L, 1); - - // Find the trigger and set up callback - for (int t_index = 0; t_index < Triggers.Count(); t_index++) { - TriggerClass* trigger = Triggers.Ptr(t_index); - - if (trigger != NULL) { - - if (strcmp(trigger->Name(), triggerName) == 0) { - - TriggerTypeClass* this_type = trigger->Class; - lua_pushnumber(L, t_index); - - return 1; - - break; + // Go through the map and replace any celltriggers + for (int _x = Map.MapCellX; _x <= Map.MapCellX + Map.MapCellWidth; _x++) { + for (int _y = Map.MapCellY; _y <= Map.MapCellY + Map.MapCellHeight; _y++) { + if (Map[XY_Cell(_x, _y)].Trigger == merge_trigger) { + Map[XY_Cell(_x, _y)].Trigger = resulting_trigger; } - } - } - lua_pushnumber(L, -1); - - return -1; + // Now that we've merged the contents, and it's no longer being referred to, we can safely delete the merge trigger + Detach_This_From_All(merge_trigger->As_Target()); + delete merge_trigger; } - /*********************************************************************************************** - * Script_TriggerSetPersistence - Sets whether a trigger runs once or indefinitely * - * * - * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which this will apply * - * isPersistent (int) - The persistence value * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_TriggerSetPersistence(lua_State* L) { + return 1; +} - int triggerIndex = lua_tointeger(L, 1); - unsigned char isPersistent = lua_tointeger(L, 2); +/*********************************************************************************************** + * Script_GetTriggerByName - Gets the trigger with a given name * + * * + * SCRIPT INPUT: triggerName (string); The input trigger name * + * * + * SCRIPT OUTPUT: triggerID (int) - The index of the trigger with this name * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GetTriggerByName(lua_State* L) { - TriggerClass* trigger = Triggers.Ptr(triggerIndex); + const char* triggerName = lua_tostring(L, 1); + + // Find the trigger and set up callback + for (int t_index = 0; t_index < Triggers.Count(); t_index++) { + TriggerClass* trigger = Triggers.Ptr(t_index); if (trigger != NULL) { - TriggerTypeClass* trigger_type = trigger->Class; + if (strcmp(trigger->Name(), triggerName) == 0) { - trigger_type->IsPersistant = (TriggerTypeClass::PersistantType)isPersistent; + TriggerTypeClass* this_type = trigger->Class; + lua_pushnumber(L, t_index); + return 1; + + break; + } } - return -1; } + lua_pushnumber(L, -1); + + return -1; +} + +/*********************************************************************************************** + * Script_TriggerSetPersistence - Sets whether a trigger runs once or indefinitely * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which this will apply * + * isPersistent (int) - The persistence value * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_TriggerSetPersistence(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + unsigned char isPersistent = lua_tointeger(L, 2); + + TriggerClass* trigger = Triggers.Ptr(triggerIndex); + + if (trigger != NULL) { + + TriggerTypeClass* trigger_type = trigger->Class; + + trigger_type->IsPersistant = (TriggerTypeClass::PersistantType)isPersistent; + + + } + + return -1; +} + /********************************************************************************************** * Modification Utility Functions * *=============================================================================================*/ - - /********************************************************************************************** - * Script_SetObjectAttribute - Sets attributes for specific objects or object types * - * * - * SCRIPT INPUT: objectID (int) - The ID of the object * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetObjectAttribute(lua_State* L) { - int objectID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); +/********************************************************************************************** +* Script_SetObjectAttribute - Sets attributes for specific objects or object types * +* * +* SCRIPT INPUT: objectID (int) - The ID of the object * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetObjectAttribute(lua_State* L) { - ObjectClass* this_object = Script_GetCacheObject(objectID); + int objectID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); - // Get the appropriate technotype - if (this_object != NULL) { + ObjectClass* this_object = Script_GetCacheObject(objectID); - // Global attributes / Multi object attributes - switch ((MapScriptObjectAttributeType)attribute_type) { - case ATTRIBUTE_TYPE: { + // Get the appropriate technotype + if (this_object != NULL) { - }break; - case ATTRIBUTE_STRENGTH: { + // Global attributes / Multi object attributes + switch ((MapScriptObjectAttributeType)attribute_type) { + case ATTRIBUTE_TYPE: { - int attribute_value = lua_tointeger(L, 3); - this_object->Strength = attribute_value; + }break; + case ATTRIBUTE_STRENGTH: { - }break; + int attribute_value = lua_tointeger(L, 3); + this_object->Strength = attribute_value; - case ATTRIBUTE_IS_REPAIRING: { + }break; - int attribute_value = lua_tointeger(L, 3); + case ATTRIBUTE_IS_REPAIRING: { - switch (this_object->RTTI) { - case RTTI_BUILDING: { + int attribute_value = lua_tointeger(L, 3); - BuildingClass* this_building = (BuildingClass*)this_object; - this_building->IsRepairing = attribute_value; + switch (this_object->RTTI) { + case RTTI_BUILDING: { - }break; - case RTTI_VESSEL: { + BuildingClass* this_building = (BuildingClass*)this_object; + this_building->IsRepairing = attribute_value; - VesselClass* this_vessel = (VesselClass*)this_object; - this_vessel->IsSelfRepairing = attribute_value; + }break; + case RTTI_VESSEL: { - }break; - } - }break; - - // Object Class specific - default: { - - TechnoTypeClass* this_type_class; - - switch (this_object->RTTI) { - case RTTI_BUILDING: - - this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); - - break; - case RTTI_UNIT: - - this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); - - break; - case RTTI_INFANTRY: - - this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); - - break; - case RTTI_VESSEL: - - this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); - - break; - case RTTI_AIRCRAFT: - - this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); - - break; - } - - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - }break; + VesselClass* this_vessel = (VesselClass*)this_object; + this_vessel->IsSelfRepairing = attribute_value; + }break; } - - } - - - return -1; - } - - /********************************************************************************************** - * Script_SetBuildingTypeAttribute - Sets attributes for a building types * - * * - * SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetBuildingTypeAttribute(lua_State* L) { - - int buildingTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_SetVehicleTypeAttribute - Sets attributes for vehicle types * - * * - * SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetVehicleTypeAttribute(lua_State* L) { - - int vehicleTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_SetInfantryTypeAttribute - Sets attributes for infantry types * - * * - * SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetInfantryTypeAttribute(lua_State* L) { - - int infantryTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_SetVesselTypeAttribute - Sets attributes for vessel types * - * * - * SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetVesselTypeAttribute(lua_State* L) { - - int vesselTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_SetAircraftTypeAttribute - Sets attributes for aircraft types * - * * - * SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetAircraftTypeAttribute(lua_State* L) { - - int aircraftTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); - - if (this_type_class != NULL) { - Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_SetObjectTypeAttribute - Sets attributes for specific object types globally * - * * - * Used internally by MapScript to retrieve input and set attributes for TechnoTypeClass * - * * - * SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * - * attribute_type (MapScriptObjectAttributeType) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { - - switch (attribute_type) { - case ATTRIBUTE_MAX_STRENGTH: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxStrength); - - // Set the attribute - this_type_class->MaxStrength = attribute_value; - }break; - case ATTRIBUTE_COST: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Cost); - - // Set the attribute - this_type_class->Cost = attribute_value; - - }break; - case ATTRIBUTE_SIGHT_RANGE: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SightRange); - - // Set the attribute - this_type_class->SightRange = attribute_value; - - }break; - case ATTRIBUTE_TECH_LEVEL: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Level); - - // Set the attribute - this_type_class->Level = attribute_value; - - }break; - case ATTRIBUTE_ARMOR: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Armor); - - // Set the attribute - this_type_class->Armor = (ArmorType)attribute_value; - - }break; - case ATTRIBUTE_PRIMARY_WEAPON: { - - int attribute_value = lua_tointeger(L, 3); - - if (this_type_class->PrimaryWeapon != NULL) { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->PrimaryWeapon->ID); - } - else { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); - } - - // Set the attribute - if (attribute_value < 0) { - this_type_class->PrimaryWeapon = NULL; - } - else { - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->PrimaryWeapon = this_weapon; - } - else { - this_type_class->PrimaryWeapon = NULL; - } - } - - }break; - case ATTRIBUTE_SECONDARY_WEAPON: { - - int attribute_value = lua_tointeger(L, 3); - - if (this_type_class->PrimaryWeapon != NULL) { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SecondaryWeapon->ID); - } - else { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); - } - - // Set the attribute - if (attribute_value < 0) { - this_type_class->SecondaryWeapon = NULL; - } - else { - WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); - if (this_weapon != NULL) { - this_type_class->SecondaryWeapon = this_weapon; - } - else { - this_type_class->SecondaryWeapon = NULL; - } - } - - }break; - case ATTRIBUTE_SPEED: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Speed); - - // Set the attribute - this_type_class->Speed = (SpeedType)attribute_value; - - }break; - case ATTRIBUTE_MAX_SPEED: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxSpeed); - - // Set the attribute - this_type_class->MaxSpeed = (MPHType)attribute_value; - - }break; - case ATTRIBUTE_MAX_AMMO: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxAmmo); - - // Set the attribute - this_type_class->MaxAmmo = attribute_value; - - }break; - case ATTRIBUTE_ROTATION_SPEED: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->ROT); - - // Set the attribute - this_type_class->ROT = attribute_value; - - }break; - case ATTRIBUTE_CRUSHABLE: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrushable); - - // Set the attribute - this_type_class->IsCrushable = attribute_value; - - }break; - case ATTRIBUTE_STEALTHY: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsStealthy); - - // Set the attribute - this_type_class->IsStealthy = attribute_value; - - }break; - case ATTRIBUTE_SELECTABLE: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelectable); - - // Set the attribute - this_type_class->IsSelectable = attribute_value; - - }break; - case ATTRIBUTE_LEGAL_TARGET: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsLegalTarget); - - // Set the attribute - this_type_class->IsLegalTarget = attribute_value; - - }break; - case ATTRIBUTE_REPAIRABLE: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsRepairable); - - // Set the attribute - this_type_class->IsRepairable = attribute_value; - - }break; - case ATTRIBUTE_SELF_HEALING: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelfHealing); - - // Set the attribute - this_type_class->IsSelfHealing = attribute_value; - - }break; - case ATTRIBUTE_EXPLODES: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsExploding); - - // Set the attribute - this_type_class->IsExploding = attribute_value; - - }break; - case ATTRIBUTE_HAS_CREW: { - - unsigned int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrew); - - // Set the attribute - this_type_class->IsCrew = attribute_value; - - }break; - case ATTRIBUTE_MAX_PASSENGERS: { - - int attribute_value = lua_tointeger(L, 3); - - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxPassengers); - - // Set the attribute - this_type_class->MaxPassengers = attribute_value; - - }break; + // Object Class specific default: { - // Object class specific attributes - // Welcome to the web of switches] + TechnoTypeClass* this_type_class; - // Type of object - switch (this_type_class->RTTI) { + switch (this_object->RTTI) { + case RTTI_BUILDING: - // Building Specific - case RTTI_BUILDINGTYPE: { + this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_CAPACITY: { + break; + case RTTI_UNIT: - int attribute_value = lua_tointeger(L, 3); + this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + break; + case RTTI_INFANTRY: - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Capacity); + this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); - // Set the attribute - this_building_class->Capacity = attribute_value; + break; + case RTTI_VESSEL: + + this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_AIRCRAFT: + + this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); - }break; - case ATTRIBUTE_POWER: { + break; + } - int attribute_value = lua_tointeger(L, 3); - // Both power and drain are different variables - // And so we do some trickery here to fill - // the right values based on postivie/negative input + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + }break; - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Power - this_building_class->Drain); + } - // Set the attribute - if (attribute_value > 0) { - this_building_class->Power = attribute_value; - this_building_class->Drain = 0; - } - else { - this_building_class->Power = 0; - this_building_class->Drain = attribute_value * -1; // Flip negative power input - } + } + + + return -1; +} + +/********************************************************************************************** +* Script_SetBuildingTypeAttribute - Sets attributes for a building types * +* * +* SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetBuildingTypeAttribute(lua_State* L) { + + int buildingTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_SetVehicleTypeAttribute - Sets attributes for vehicle types * +* * +* SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetVehicleTypeAttribute(lua_State* L) { + + int vehicleTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_SetInfantryTypeAttribute - Sets attributes for infantry types * +* * +* SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetInfantryTypeAttribute(lua_State* L) { + + int infantryTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_SetVesselTypeAttribute - Sets attributes for vessel types * +* * +* SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetVesselTypeAttribute(lua_State* L) { + + int vesselTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_SetAircraftTypeAttribute - Sets attributes for aircraft types * +* * +* SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetAircraftTypeAttribute(lua_State* L) { + + int aircraftTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); + + if (this_type_class != NULL) { + Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_SetObjectTypeAttribute - Sets attributes for specific object types globally * +* * +* Used internally by MapScript to retrieve input and set attributes for TechnoTypeClass * +* * +* SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * +* attribute_type (MapScriptObjectAttributeType) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { + + switch (attribute_type) { + case ATTRIBUTE_MAX_STRENGTH: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxStrength); + + // Set the attribute + this_type_class->MaxStrength = attribute_value; + + }break; + + case ATTRIBUTE_COST: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Cost); + + // Set the attribute + this_type_class->Cost = attribute_value; + + }break; + case ATTRIBUTE_SIGHT_RANGE: { + + int attribute_value = lua_tointeger(L, 3); + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SightRange); + + // Set the attribute + this_type_class->SightRange = attribute_value; + + }break; + case ATTRIBUTE_TECH_LEVEL: { + + int attribute_value = lua_tointeger(L, 3); - }break; - case ATTRIBUTE_SELLABLE: { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Level); - int attribute_value = lua_tointeger(L, 3); + // Set the attribute + this_type_class->Level = attribute_value; - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + }break; + case ATTRIBUTE_ARMOR: { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->IsUnsellable * -1); + int attribute_value = lua_tointeger(L, 3); - // Set the attribute - // Personal opinion; sellable makes more sense than unsellable - // This replacement could be seen as IsUnNonSellable, or... IsSellable - if (attribute_value == 1) { - this_building_class->IsUnsellable = 0; - } - else { - this_building_class->IsUnsellable = 1; - } + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Armor); - }break; - } + // Set the attribute + this_type_class->Armor = (ArmorType)attribute_value; - }break; + }break; + case ATTRIBUTE_PRIMARY_WEAPON: { + int attribute_value = lua_tointeger(L, 3); - // Unit Specific - case RTTI_UNITTYPE: { + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->PrimaryWeapon->ID); + } + else { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_CRATE_GOODIE: { + // Set the attribute + if (attribute_value < 0) { + this_type_class->PrimaryWeapon = NULL; + } + else { + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->PrimaryWeapon = this_weapon; + } + else { + this_type_class->PrimaryWeapon = NULL; + } + } - int attribute_value = lua_tointeger(L, 3); + }break; + case ATTRIBUTE_SECONDARY_WEAPON: { - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + int attribute_value = lua_tointeger(L, 3); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrateGoodie); + if (this_type_class->PrimaryWeapon != NULL) { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->SecondaryWeapon->ID); + } + else { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, -1); + } - // Set the attribute - this_unit_class->IsCrateGoodie = attribute_value; + // Set the attribute + if (attribute_value < 0) { + this_type_class->SecondaryWeapon = NULL; + } + else { + WeaponTypeClass* this_weapon = Weapons.Ptr(attribute_value); + if (this_weapon != NULL) { + this_type_class->SecondaryWeapon = this_weapon; + } + else { + this_type_class->SecondaryWeapon = NULL; + } + } - }break; - case ATTRIBUTE_CRUSHER: { + }break; + case ATTRIBUTE_SPEED: { - int attribute_value = lua_tointeger(L, 3); + int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->Speed); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrusher); + // Set the attribute + this_type_class->Speed = (SpeedType)attribute_value; - // Set the attribute - this_unit_class->IsCrusher = attribute_value; + }break; + case ATTRIBUTE_MAX_SPEED: { - }break; - case ATTRIBUTE_HARVEST: { + int attribute_value = lua_tointeger(L, 3); - int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxSpeed); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + // Set the attribute + this_type_class->MaxSpeed = (MPHType)attribute_value; - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsToHarvest); + }break; + case ATTRIBUTE_MAX_AMMO: { - // Set the attribute - this_unit_class->IsToHarvest = attribute_value; + int attribute_value = lua_tointeger(L, 3); - }break; - case ATTRIBUTE_RADAR_EQUIPPED: { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxAmmo); - int attribute_value = lua_tointeger(L, 3); + // Set the attribute + this_type_class->MaxAmmo = attribute_value; - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + }break; + case ATTRIBUTE_ROTATION_SPEED: { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsRadarEquipped); + int attribute_value = lua_tointeger(L, 3); - // Set the attribute - this_unit_class->IsRadarEquipped = attribute_value; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->ROT); - }break; - case ATTRIBUTE_JAMMER: { + // Set the attribute + this_type_class->ROT = attribute_value; - int attribute_value = lua_tointeger(L, 3); + }break; + case ATTRIBUTE_CRUSHABLE: { - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + unsigned int attribute_value = lua_tointeger(L, 3); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsJammer); + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrushable); - // Set the attribute - this_unit_class->IsJammer = attribute_value; + // Set the attribute + this_type_class->IsCrushable = attribute_value; - }break; - case ATTRIBUTE_GAPPER: { + }break; + case ATTRIBUTE_STEALTHY: { - int attribute_value = lua_tointeger(L, 3); + unsigned int attribute_value = lua_tointeger(L, 3); - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsStealthy); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsGapper); + // Set the attribute + this_type_class->IsStealthy = attribute_value; - // Set the attribute - this_unit_class->IsGapper = attribute_value; + }break; + case ATTRIBUTE_SELECTABLE: { - }break; - } + unsigned int attribute_value = lua_tointeger(L, 3); - }break; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelectable); + // Set the attribute + this_type_class->IsSelectable = attribute_value; - // Infantry Specific - case RTTI_INFANTRYTYPE: { + }break; + case ATTRIBUTE_LEGAL_TARGET: { - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_FEMALE: { + unsigned int attribute_value = lua_tointeger(L, 3); - int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsLegalTarget); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + // Set the attribute + this_type_class->IsLegalTarget = attribute_value; - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFemale); + }break; + case ATTRIBUTE_REPAIRABLE: { - // Set the attribute - this_infantry_class->IsFemale = attribute_value; + unsigned int attribute_value = lua_tointeger(L, 3); - }break; - case ATTRIBUTE_CAPTURE: { + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsRepairable); - int attribute_value = lua_tointeger(L, 3); + // Set the attribute + this_type_class->IsRepairable = attribute_value; - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + }break; + case ATTRIBUTE_SELF_HEALING: { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCapture); + unsigned int attribute_value = lua_tointeger(L, 3); - // Set the attribute - this_infantry_class->IsCapture = attribute_value; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsSelfHealing); - }break; - case ATTRIBUTE_FRAIDY_CAT: { + // Set the attribute + this_type_class->IsSelfHealing = attribute_value; - int attribute_value = lua_tointeger(L, 3); + }break; + case ATTRIBUTE_EXPLODES: { - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + unsigned int attribute_value = lua_tointeger(L, 3); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFraidyCat); + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsExploding); - // Set the attribute - this_infantry_class->IsFraidyCat = attribute_value; + // Set the attribute + this_type_class->IsExploding = attribute_value; - }break; - case ATTRIBUTE_CIVILIAN: { + }break; + case ATTRIBUTE_HAS_CREW: { - int attribute_value = lua_tointeger(L, 3); + unsigned int attribute_value = lua_tointeger(L, 3); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->IsCrew); - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCivilian); + // Set the attribute + this_type_class->IsCrew = attribute_value; - // Set the attribute - this_infantry_class->IsCivilian = attribute_value; + }break; + case ATTRIBUTE_MAX_PASSENGERS: { - }break; - case ATTRIBUTE_BOMBER: { + int attribute_value = lua_tointeger(L, 3); - int attribute_value = lua_tointeger(L, 3); + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_type_class->MaxPassengers); - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + // Set the attribute + this_type_class->MaxPassengers = attribute_value; - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsBomber); + }break; + default: { - // Set the attribute - this_infantry_class->IsBomber = attribute_value; + // Object class specific attributes + // Welcome to the web of switches] - }break; - case ATTRIBUTE_DOG: { + // Type of object + switch (this_type_class->RTTI) { - int attribute_value = lua_tointeger(L, 3); + // Building Specific + case RTTI_BUILDINGTYPE: { - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CAPACITY: { - // Cache default value if first time changing this attribute - Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsDog); + int attribute_value = lua_tointeger(L, 3); - // Set the attribute - this_infantry_class->IsDog = attribute_value; + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - }break; + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Capacity); - } + // Set the attribute + this_building_class->Capacity = attribute_value; - }break; + }break; + case ATTRIBUTE_POWER: { - } // End of object specific switch + int attribute_value = lua_tointeger(L, 3); + + // Both power and drain are different variables + // And so we do some trickery here to fill + // the right values based on postivie/negative input + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->Power - this_building_class->Drain); + + // Set the attribute + if (attribute_value > 0) { + this_building_class->Power = attribute_value; + this_building_class->Drain = 0; + } + else { + this_building_class->Power = 0; + this_building_class->Drain = attribute_value * -1; // Flip negative power input + } + + }break; + case ATTRIBUTE_SELLABLE: { + + int attribute_value = lua_tointeger(L, 3); + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_building_class->IsUnsellable * -1); + + // Set the attribute + // Personal opinion; sellable makes more sense than unsellable + // This replacement could be seen as IsUnNonSellable, or... IsSellable + if (attribute_value == 1) { + this_building_class->IsUnsellable = 0; + } + else { + this_building_class->IsUnsellable = 1; + } + + }break; + } + + }break; + + + // Unit Specific + case RTTI_UNITTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CRATE_GOODIE: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrateGoodie); + + // Set the attribute + this_unit_class->IsCrateGoodie = attribute_value; + + }break; + case ATTRIBUTE_CRUSHER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsCrusher); + + // Set the attribute + this_unit_class->IsCrusher = attribute_value; + + }break; + case ATTRIBUTE_HARVEST: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsToHarvest); + + // Set the attribute + this_unit_class->IsToHarvest = attribute_value; + + }break; + case ATTRIBUTE_RADAR_EQUIPPED: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsRadarEquipped); + + // Set the attribute + this_unit_class->IsRadarEquipped = attribute_value; + + }break; + case ATTRIBUTE_JAMMER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsJammer); + + // Set the attribute + this_unit_class->IsJammer = attribute_value; + + }break; + case ATTRIBUTE_GAPPER: { + + int attribute_value = lua_tointeger(L, 3); + + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_unit_class->IsGapper); + + // Set the attribute + this_unit_class->IsGapper = attribute_value; + + }break; + } + + }break; + + + // Infantry Specific + case RTTI_INFANTRYTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_FEMALE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFemale); + + // Set the attribute + this_infantry_class->IsFemale = attribute_value; + + }break; + case ATTRIBUTE_CAPTURE: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCapture); + + // Set the attribute + this_infantry_class->IsCapture = attribute_value; + + }break; + case ATTRIBUTE_FRAIDY_CAT: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsFraidyCat); + + // Set the attribute + this_infantry_class->IsFraidyCat = attribute_value; + + }break; + case ATTRIBUTE_CIVILIAN: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsCivilian); + + // Set the attribute + this_infantry_class->IsCivilian = attribute_value; + + }break; + case ATTRIBUTE_BOMBER: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsBomber); + + // Set the attribute + this_infantry_class->IsBomber = attribute_value; + + }break; + case ATTRIBUTE_DOG: { + + int attribute_value = lua_tointeger(L, 3); + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Cache default value if first time changing this attribute + Script_CacheDefaultObjectTypeAttribute(this_type_class->RTTI, this_type_class->ID, attribute_type, this_infantry_class->IsDog); + + // Set the attribute + this_infantry_class->IsDog = attribute_value; }break; - } // End of attribute switch + } + + }break; + + } // End of object specific switch + + }break; + + } // End of attribute switch + + return -1; +} + +/********************************************************************************************** +* Script_GetObjectAttribute - Gets attributes for specific objects or object types * +* * +* SCRIPT INPUT: objectID (int) - The ID of the object * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetObjectAttribute(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + // Get the appropriate technotype + if (this_object != NULL) { + + // Global attributes / Multi object attributes + switch ((MapScriptObjectAttributeType)attribute_type) { + case ATTRIBUTE_TYPE: { + + // Send the attribute + lua_pushinteger(L, this_object->RTTI); + return 1; + + }break; + case ATTRIBUTE_STRENGTH: { + + // Send the attribute + lua_pushinteger(L, this_object->Strength); + return 1; + + }break; + + case ATTRIBUTE_IS_REPAIRING: { + + switch (this_object->RTTI) { + case RTTI_BUILDING: { + + BuildingClass* this_building = (BuildingClass*)this_object; + + // Send the attribute + lua_pushinteger(L, this_building->IsRepairing); + return 1; + + }break; + case RTTI_VESSEL: { + + VesselClass* this_vessel = (VesselClass*)this_object; + + // Send the attribute + lua_pushinteger(L, this_vessel->IsSelfRepairing); + return 1; + + }break; + } + }break; + + // Object Class specific + default: { + + TechnoTypeClass* this_type_class = NULL; + + switch (this_object->RTTI) { + case RTTI_BUILDING: + + this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_UNIT: + + this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_INFANTRY: + + this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_VESSEL: + + this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); + + break; + case RTTI_AIRCRAFT: + + this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); + + break; + } + + // Type attribute retrieval + if (this_type_class != NULL) { + return Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + }break; + + } - return -1; } - /********************************************************************************************** - * Script_GetObjectAttribute - Gets attributes for specific objects or object types * - * * - * SCRIPT INPUT: objectID (int) - The ID of the object * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetObjectAttribute(lua_State* L) { - int objectID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); + return -1; +} - ObjectClass* this_object = Script_GetCacheObject(objectID); +/********************************************************************************************** +* Script_GetBuildingTypeAttribute - Gets attributes for a building types * +* * +* SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetBuildingTypeAttribute(lua_State* L) { - // Get the appropriate technotype - if (this_object != NULL) { + int buildingTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); - // Global attributes / Multi object attributes - switch ((MapScriptObjectAttributeType)attribute_type) { - case ATTRIBUTE_TYPE: { + TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_GetVehicleTypeAttribute - Gets attributes for vehicle types * +* * +* SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetVehicleTypeAttribute(lua_State* L) { + + int vehicleTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_GetInfantryTypeAttribute - Gets attributes for infantry types * +* * +* SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetInfantryTypeAttribute(lua_State* L) { + + int infantryTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_GetVesselTypeAttribute - Gets attributes for vessel types * +* * +* SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetVesselTypeAttribute(lua_State* L) { + + int vesselTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_GetAircraftTypeAttribute - Gets attributes for aircraft types * +* * +* SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetAircraftTypeAttribute(lua_State* L) { + + int aircraftTypeID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); + + TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); + + if (this_type_class != NULL) { + return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); + } + + return -1; +} + +/********************************************************************************************** +* Script_GetObjectTypeAttribute - Gets attributes for specific object types globally * +* * +* Used internally by MapScript to send input and get attributes for TechnoTypeClass * +* * +* SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * +* attribute_type (MapScriptObjectAttributeType) - The attribute to change * +* * +* SCRIPT OUTPUT: void * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { + + switch (attribute_type) { + case ATTRIBUTE_MAX_STRENGTH: { + + // Send the attribute + lua_pushinteger(L, this_type_class->MaxStrength); + return 1; + + }break; + + case ATTRIBUTE_COST: { + + // Send the attribute + lua_pushinteger(L, this_type_class->Cost); + return 1; + + }break; + case ATTRIBUTE_SIGHT_RANGE: { + + // Send the attribute + lua_pushinteger(L, this_type_class->SightRange); + return 1; + + }break; + case ATTRIBUTE_TECH_LEVEL: { + + // Send the attribute + lua_pushinteger(L, this_type_class->Level); + return 1; + + }break; + case ATTRIBUTE_ARMOR: { + + // Send the attribute + lua_pushinteger(L, this_type_class->Armor); + return 1; + + }break; + case ATTRIBUTE_PRIMARY_WEAPON: { + + if (this_type_class->PrimaryWeapon != NULL) { + // Send the attribute + lua_pushinteger(L, this_type_class->PrimaryWeapon->ID); + return 1; + } + else { + // Send the attribute + lua_pushinteger(L, -1); + return 1; + } + + }break; + case ATTRIBUTE_SECONDARY_WEAPON: { + + if (this_type_class->SecondaryWeapon != NULL) { + // Send the attribute + lua_pushinteger(L, this_type_class->SecondaryWeapon->ID); + return 1; + } + else { + // Send the attribute + lua_pushinteger(L, -1); + return 1; + } + + }break; + case ATTRIBUTE_SPEED: { + + // Send the attribute + lua_pushinteger(L, this_type_class->Speed); + return 1; + + }break; + case ATTRIBUTE_MAX_SPEED: { + + // Send the attribute + lua_pushinteger(L, this_type_class->MaxSpeed); + return 1; + + }break; + case ATTRIBUTE_MAX_AMMO: { + + // Send the attribute + lua_pushinteger(L, this_type_class->MaxAmmo); + return 1; + + }break; + case ATTRIBUTE_ROTATION_SPEED: { + + // Send the attribute + lua_pushboolean(L, this_type_class->ROT); + return 1; + + }break; + case ATTRIBUTE_CRUSHABLE: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsCrushable); + return 1; + + }break; + case ATTRIBUTE_STEALTHY: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsStealthy); + return 1; + + }break; + case ATTRIBUTE_SELECTABLE: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsSelectable); + return 1; + + }break; + case ATTRIBUTE_LEGAL_TARGET: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsLegalTarget); + return 1; + + }break; + case ATTRIBUTE_REPAIRABLE: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsRepairable); + return 1; + + }break; + case ATTRIBUTE_SELF_HEALING: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsSelfHealing); + return 1; + + }break; + case ATTRIBUTE_EXPLODES: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsExploding); + return 1; + + }break; + case ATTRIBUTE_HAS_CREW: { + + // Send the attribute + lua_pushboolean(L, this_type_class->IsCrew); + return 1; + + }break; + case ATTRIBUTE_MAX_PASSENGERS: { + + // Send the attribute + lua_pushinteger(L, this_type_class->MaxPassengers); + return 1; + + }break; + default: { + + // Object class specific attributes + // Welcome to the web of switches] + + // Type of object + switch (this_type_class->RTTI) { + + // Building Specific + case RTTI_BUILDINGTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CAPACITY: { + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Send the attribute + lua_pushinteger(L, this_building_class->Capacity); + return 1; + + }break; + case ATTRIBUTE_POWER: { + + // Both power and drain are different variables + // And so we do some trickery here to fill + // the right values based on postivie/negative input + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + int this_value = this_building_class->Power - this_building_class->Drain; + + // Send the attribute + lua_pushinteger(L, this_value); + return 1; + + }break; + case ATTRIBUTE_SELLABLE: { + + BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; + + // Set the attribute + // Personal opinion; sellable makes more sense than unsellable + // This replacement could be seen as IsUnNonSellable, or... IsSellable + if (this_building_class->IsUnsellable == 1) { // Send the attribute - lua_pushinteger(L, this_object->RTTI); + lua_pushinteger(L, 0); return 1; - - }break; - case ATTRIBUTE_STRENGTH: { - + } + else { // Send the attribute - lua_pushinteger(L, this_object->Strength); + lua_pushinteger(L, 1); return 1; + } - }break; + }break; + } - case ATTRIBUTE_IS_REPAIRING: { + }break; - switch (this_object->RTTI) { - case RTTI_BUILDING: { - BuildingClass* this_building = (BuildingClass*)this_object; + // Unit Specific + case RTTI_UNITTYPE: { - // Send the attribute - lua_pushinteger(L, this_building->IsRepairing); - return 1; + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_CRATE_GOODIE: { - }break; - case RTTI_VESSEL: { + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - VesselClass* this_vessel = (VesselClass*)this_object; - - // Send the attribute - lua_pushinteger(L, this_vessel->IsSelfRepairing); - return 1; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsCrateGoodie); + return 1; - }break; - } - }break; + }break; + case ATTRIBUTE_CRUSHER: { - // Object Class specific - default: { + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - TechnoTypeClass* this_type_class = NULL; + // Send the attribute + lua_pushboolean(L, this_unit_class->IsCrusher); + return 1; - switch (this_object->RTTI) { - case RTTI_BUILDING: + }break; + case ATTRIBUTE_HARVEST: { - this_type_class = BuildingTypes.Ptr(this_object->Class_Of().ID); + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - break; - case RTTI_UNIT: + // Send the attribute + lua_pushboolean(L, this_unit_class->IsToHarvest); + return 1; - this_type_class = UnitTypes.Ptr(this_object->Class_Of().ID); + }break; + case ATTRIBUTE_RADAR_EQUIPPED: { - break; - case RTTI_INFANTRY: + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - this_type_class = InfantryTypes.Ptr(this_object->Class_Of().ID); + // Send the attribute + lua_pushboolean(L, this_unit_class->IsRadarEquipped); + return 1; - break; - case RTTI_VESSEL: + }break; + case ATTRIBUTE_JAMMER: { - this_type_class = VesselTypes.Ptr(this_object->Class_Of().ID); + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - break; - case RTTI_AIRCRAFT: + // Send the attribute + lua_pushboolean(L, this_unit_class->IsJammer); + return 1; - this_type_class = AircraftTypes.Ptr(this_object->Class_Of().ID); + }break; + case ATTRIBUTE_GAPPER: { - break; - } + UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - // Type attribute retrieval - if (this_type_class != NULL) { - return Script_SetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } + // Send the attribute + lua_pushboolean(L, this_unit_class->IsGapper); + return 1; - }break; + }break; + } + + }break; + + + // Infantry Specific + case RTTI_INFANTRYTYPE: { + + // What type of attribute? + switch (attribute_type) { + case ATTRIBUTE_FEMALE: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsFemale); + return 1; + + }break; + case ATTRIBUTE_CAPTURE: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsCapture); + return 1; + + }break; + case ATTRIBUTE_FRAIDY_CAT: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsFraidyCat); + return 1; + + }break; + case ATTRIBUTE_CIVILIAN: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsCivilian); + return 1; + + }break; + case ATTRIBUTE_BOMBER: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsBomber); + return 1; + + }break; + case ATTRIBUTE_DOG: { + + InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; + + // Send the attribute + lua_pushboolean(L, this_infantry_class->IsDog); + return 1; + + }break; } - } + }break; + + } // End of object specific switch + + }break; + + } // End of attribute switch - return -1; - } + // Didn't find result + return -1; - /********************************************************************************************** - * Script_GetBuildingTypeAttribute - Gets attributes for a building types * - * * - * SCRIPT INPUT: buildingTypeID (int) - The ID of the building type class * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetBuildingTypeAttribute(lua_State* L) { +} - int buildingTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); +/********************************************************************************************** +* Script_SetHouseAttribute - Sets attributes for specific houses * +* * +* SCRIPT INPUT: houseID (int) - The ID of the house * +* attribute_type (int) - The attribute to change * +* attribute_value (int) - The value to set the attribute to * +* * +* SCRIPT OUTPUT: void * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_SetHouseAttribute(lua_State* L) { - TechnoTypeClass* this_type_class = BuildingTypes.Ptr(buildingTypeID); + int houseID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); - if (this_type_class != NULL) { - return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } + HouseClass* this_house = Houses.Ptr(houseID); - return -1; - } - - /********************************************************************************************** - * Script_GetVehicleTypeAttribute - Gets attributes for vehicle types * - * * - * SCRIPT INPUT: vehicleTypeID (int) - The ID of the vehicle type class * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetVehicleTypeAttribute(lua_State* L) { - - int vehicleTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = UnitTypes.Ptr(vehicleTypeID); - - if (this_type_class != NULL) { - return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_GetInfantryTypeAttribute - Gets attributes for infantry types * - * * - * SCRIPT INPUT: infantryTypeID (int) - The ID of the infantry type class * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetInfantryTypeAttribute(lua_State* L) { - - int infantryTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = InfantryTypes.Ptr(infantryTypeID); - - if (this_type_class != NULL) { - return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_GetVesselTypeAttribute - Gets attributes for vessel types * - * * - * SCRIPT INPUT: vesselTypeID (int) - The ID of the vessel type class * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetVesselTypeAttribute(lua_State* L) { - - int vesselTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = VesselTypes.Ptr(vesselTypeID); - - if (this_type_class != NULL) { - return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_GetAircraftTypeAttribute - Gets attributes for aircraft types * - * * - * SCRIPT INPUT: aircraftTypeID (int) - The ID of the aircraft type class * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetAircraftTypeAttribute(lua_State* L) { - - int aircraftTypeID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - TechnoTypeClass* this_type_class = AircraftTypes.Ptr(aircraftTypeID); - - if (this_type_class != NULL) { - return Script_GetObjectTypeAttribute(L, this_type_class, (MapScriptObjectAttributeType)attribute_type); - } - - return -1; - } - - /********************************************************************************************** - * Script_GetObjectTypeAttribute - Gets attributes for specific object types globally * - * * - * Used internally by MapScript to send input and get attributes for TechnoTypeClass * - * * - * SCRIPT INPUT: this_type_class (TechnoTypeClass*) - The techno type * - * attribute_type (MapScriptObjectAttributeType) - The attribute to change * - * * - * SCRIPT OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetObjectTypeAttribute(lua_State* L, TechnoTypeClass* this_type_class, MapScriptObjectAttributeType attribute_type) { + // Get the appropriate technotype + if (this_house != NULL) { switch (attribute_type) { - case ATTRIBUTE_MAX_STRENGTH: { + case HOUSE_ATTRIBUTE_IQ: { - // Send the attribute - lua_pushinteger(L, this_type_class->MaxStrength); - return 1; + int attribute_value = lua_tointeger(L, 3); - }break; + this_house->Control.IQ = attribute_value; + this_house->IQ = attribute_value; - case ATTRIBUTE_COST: { + }break; + case HOUSE_ATTRIBUTE_TECH_LEVEL: { - // Send the attribute - lua_pushinteger(L, this_type_class->Cost); - return 1; + int attribute_value = lua_tointeger(L, 3); - }break; - case ATTRIBUTE_SIGHT_RANGE: { + this_house->Control.TechLevel = attribute_value; - // Send the attribute - lua_pushinteger(L, this_type_class->SightRange); - return 1; + }break; + case HOUSE_ATTRIBUTE_IS_HUMAN: { - }break; - case ATTRIBUTE_TECH_LEVEL: { + int attribute_value = lua_tointeger(L, 3); - // Send the attribute - lua_pushinteger(L, this_type_class->Level); - return 1; - - }break; - case ATTRIBUTE_ARMOR: { - - // Send the attribute - lua_pushinteger(L, this_type_class->Armor); - return 1; - - }break; - case ATTRIBUTE_PRIMARY_WEAPON: { - - if (this_type_class->PrimaryWeapon != NULL) { - // Send the attribute - lua_pushinteger(L, this_type_class->PrimaryWeapon->ID); - return 1; - } - else { - // Send the attribute - lua_pushinteger(L, -1); - return 1; - } - - }break; - case ATTRIBUTE_SECONDARY_WEAPON: { - - if (this_type_class->SecondaryWeapon != NULL) { - // Send the attribute - lua_pushinteger(L, this_type_class->SecondaryWeapon->ID); - return 1; - } - else { - // Send the attribute - lua_pushinteger(L, -1); - return 1; - } - - }break; - case ATTRIBUTE_SPEED: { - - // Send the attribute - lua_pushinteger(L, this_type_class->Speed); - return 1; - - }break; - case ATTRIBUTE_MAX_SPEED: { - - // Send the attribute - lua_pushinteger(L, this_type_class->MaxSpeed); - return 1; - - }break; - case ATTRIBUTE_MAX_AMMO: { - - // Send the attribute - lua_pushinteger(L, this_type_class->MaxAmmo); - return 1; - - }break; - case ATTRIBUTE_ROTATION_SPEED: { - - // Send the attribute - lua_pushboolean(L, this_type_class->ROT); - return 1; - - }break; - case ATTRIBUTE_CRUSHABLE: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsCrushable); - return 1; - - }break; - case ATTRIBUTE_STEALTHY: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsStealthy); - return 1; - - }break; - case ATTRIBUTE_SELECTABLE: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsSelectable); - return 1; - - }break; - case ATTRIBUTE_LEGAL_TARGET: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsLegalTarget); - return 1; - - }break; - case ATTRIBUTE_REPAIRABLE: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsRepairable); - return 1; - - }break; - case ATTRIBUTE_SELF_HEALING: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsSelfHealing); - return 1; - - }break; - case ATTRIBUTE_EXPLODES: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsExploding); - return 1; - - }break; - case ATTRIBUTE_HAS_CREW: { - - // Send the attribute - lua_pushboolean(L, this_type_class->IsCrew); - return 1; - - }break; - case ATTRIBUTE_MAX_PASSENGERS: { - - // Send the attribute - lua_pushinteger(L, this_type_class->MaxPassengers); - return 1; - - }break; - default: { - - // Object class specific attributes - // Welcome to the web of switches] - - // Type of object - switch (this_type_class->RTTI) { - - // Building Specific - case RTTI_BUILDINGTYPE: { - - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_CAPACITY: { - - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - - // Send the attribute - lua_pushinteger(L, this_building_class->Capacity); - return 1; - - }break; - case ATTRIBUTE_POWER: { - - // Both power and drain are different variables - // And so we do some trickery here to fill - // the right values based on postivie/negative input - - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - - int this_value = this_building_class->Power - this_building_class->Drain; - - // Send the attribute - lua_pushinteger(L, this_value); - return 1; - - }break; - case ATTRIBUTE_SELLABLE: { - - BuildingTypeClass* this_building_class = (BuildingTypeClass*)this_type_class; - - // Set the attribute - // Personal opinion; sellable makes more sense than unsellable - // This replacement could be seen as IsUnNonSellable, or... IsSellable - if (this_building_class->IsUnsellable == 1) { - // Send the attribute - lua_pushinteger(L, 0); - return 1; - } else { - // Send the attribute - lua_pushinteger(L, 1); - return 1; - } - - }break; - } - - }break; - - - // Unit Specific - case RTTI_UNITTYPE: { - - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_CRATE_GOODIE: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsCrateGoodie); - return 1; - - }break; - case ATTRIBUTE_CRUSHER: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsCrusher); - return 1; - - }break; - case ATTRIBUTE_HARVEST: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsToHarvest); - return 1; - - }break; - case ATTRIBUTE_RADAR_EQUIPPED: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsRadarEquipped); - return 1; - - }break; - case ATTRIBUTE_JAMMER: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsJammer); - return 1; - - }break; - case ATTRIBUTE_GAPPER: { - - UnitTypeClass* this_unit_class = (UnitTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_unit_class->IsGapper); - return 1; - - }break; - } - - }break; - - - // Infantry Specific - case RTTI_INFANTRYTYPE: { - - // What type of attribute? - switch (attribute_type) { - case ATTRIBUTE_FEMALE: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsFemale); - return 1; - - }break; - case ATTRIBUTE_CAPTURE: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsCapture); - return 1; - - }break; - case ATTRIBUTE_FRAIDY_CAT: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsFraidyCat); - return 1; - - }break; - case ATTRIBUTE_CIVILIAN: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsCivilian); - return 1; - - }break; - case ATTRIBUTE_BOMBER: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsBomber); - return 1; - - }break; - case ATTRIBUTE_DOG: { - - InfantryTypeClass* this_infantry_class = (InfantryTypeClass*)this_type_class; - - // Send the attribute - lua_pushboolean(L, this_infantry_class->IsDog); - return 1; - - }break; - - } - - }break; - - } // End of object specific switch - - }break; - - } // End of attribute switch - - - // Didn't find result - return -1; - - } - - /********************************************************************************************** - * Script_SetHouseAttribute - Sets attributes for specific houses * - * * - * SCRIPT INPUT: houseID (int) - The ID of the house * - * attribute_type (int) - The attribute to change * - * attribute_value (int) - The value to set the attribute to * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetHouseAttribute(lua_State* L) { - - int houseID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); - - HouseClass* this_house = Houses.Ptr(houseID); - - // Get the appropriate technotype - if (this_house != NULL) { - - switch (attribute_type) { - case HOUSE_ATTRIBUTE_IQ: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Control.IQ = attribute_value; - this_house->IQ = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_TECH_LEVEL: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Control.TechLevel = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_IS_HUMAN: { - - int attribute_value = lua_tointeger(L, 3); - - if (attribute_value == 1) { - this_house->IsHuman = 1; - this_house->WasHuman = 1; - }else { - this_house->IsHuman = 0; - } - - - }break; - case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->FirepowerBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].FirepowerBias; - - }break; - case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->GroundspeedBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].GroundspeedBias; - - }break; - case HOUSE_ATTRIBUTE_ARMOR_BIAS: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->ArmorBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ArmorBias; - - }break; - case HOUSE_ATTRIBUTE_ROF_BIAS: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->ROFBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ROFBias; - - }break; - case HOUSE_ATTRIBUTE_COST_BIAS: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->CostBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].CostBias; - - }break; - case HOUSE_ATTRIBUTE_REPAIR_DELAY: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->RepairDelay = (fixed)attribute_value; - - }break; - case HOUSE_ATTRIBUTE_BUILD_DELAY: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->BuildDelay = (fixed)attribute_value; - - }break; - case HOUSE_ATTRIBUTE_STARTED: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->IsStarted = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_ALERTED: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->IsAlerted = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_BASE_BUILDING: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->IsBaseBuilding = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_DISCOVERED: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->IsDiscovered = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_CAPACITY: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Capacity = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_TIBERIUM: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Tiberium = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_CREDITS: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Credits = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_ALERT_TIMER: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->AlertTime = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_REPAIR_TIMER: { - - float attribute_value = lua_tonumber(L, 3); - - this_house->RepairTimer = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_POINTS : { - - int attribute_value = lua_tointeger(L, 3); - - this_house->PointTotal = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { - - // Non writable - - }break; - case HOUSE_ATTRIBUTE_CRATES: { - - // Non writable - - }break; - - case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->BuildingFactories = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->UnitFactories = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->InfantryFactories = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->AircraftFactories = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->VesselFactories = attribute_value; - - }break; - - case HOUSE_ATTRIBUTE_POWER: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Power = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_DRAIN: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->Drain = attribute_value; - - }break; - case HOUSE_ATTRIBUTE_LAST_ATTACKER: { - - int attribute_value = lua_tointeger(L, 3); - - this_house->WhoLastHurtMe = (HousesType)attribute_value; - - }break; + if (attribute_value == 1) { + this_house->IsHuman = 1; + this_house->WasHuman = 1; + } + else { + this_house->IsHuman = 0; } + + }break; + case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->FirepowerBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].FirepowerBias; + + }break; + case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->GroundspeedBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].GroundspeedBias; + + }break; + case HOUSE_ATTRIBUTE_ARMOR_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->ArmorBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ArmorBias; + + }break; + case HOUSE_ATTRIBUTE_ROF_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->ROFBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].ROFBias; + + }break; + case HOUSE_ATTRIBUTE_COST_BIAS: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->CostBias = (fixed)attribute_value * Rule.Diff[this_house->Difficulty].CostBias; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_DELAY: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->RepairDelay = (fixed)attribute_value; + + }break; + case HOUSE_ATTRIBUTE_BUILD_DELAY: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->BuildDelay = (fixed)attribute_value; + + }break; + case HOUSE_ATTRIBUTE_STARTED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsStarted = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_ALERTED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsAlerted = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_BASE_BUILDING: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsBaseBuilding = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DISCOVERED: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->IsDiscovered = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_CAPACITY: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Capacity = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_TIBERIUM: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Tiberium = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_CREDITS: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Credits = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_ALERT_TIMER: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->AlertTime = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_REPAIR_TIMER: { + + float attribute_value = lua_tonumber(L, 3); + + this_house->RepairTimer = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_POINTS: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->PointTotal = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { + + // Non writable + + }break; + case HOUSE_ATTRIBUTE_CRATES: { + + // Non writable + + }break; + + case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->BuildingFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->UnitFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->InfantryFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->AircraftFactories = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->VesselFactories = attribute_value; + + }break; + + case HOUSE_ATTRIBUTE_POWER: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Power = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_DRAIN: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->Drain = attribute_value; + + }break; + case HOUSE_ATTRIBUTE_LAST_ATTACKER: { + + int attribute_value = lua_tointeger(L, 3); + + this_house->WhoLastHurtMe = (HousesType)attribute_value; + + }break; } - - return -1; } - /********************************************************************************************** - * Script_GetHouseAttribute - Sets attributes for specific houses * - * * - * SCRIPT INPUT: houseID (int) - The ID of the house * - * attribute_type (int) - The attribute to change * - * * - * SCRIPT OUTPUT: (mixed) - The value of the attribute * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetHouseAttribute(lua_State* L) { - int houseID = lua_tointeger(L, 1); - int attribute_type = lua_tointeger(L, 2); + return -1; +} - HouseClass* this_house = Houses.Ptr(houseID); +/********************************************************************************************** +* Script_GetHouseAttribute - Sets attributes for specific houses * +* * +* SCRIPT INPUT: houseID (int) - The ID of the house * +* attribute_type (int) - The attribute to change * +* * +* SCRIPT OUTPUT: (mixed) - The value of the attribute * +* * +* INPUT: lua_State - The current Lua state * +* * +* OUTPUT: int; Did the function run successfully? Return 1 * +* * +* WARNINGS: ? * +* * +*=============================================================================================*/ +static int Script_GetHouseAttribute(lua_State* L) { - // Get the appropriate technotype - if (this_house != NULL) { + int houseID = lua_tointeger(L, 1); + int attribute_type = lua_tointeger(L, 2); - switch (attribute_type) { - case HOUSE_ATTRIBUTE_IQ: { + HouseClass* this_house = Houses.Ptr(houseID); - lua_pushnumber(L, this_house->IQ); - return 1; + // Get the appropriate technotype + if (this_house != NULL) { - }break; - case HOUSE_ATTRIBUTE_TECH_LEVEL: { + switch (attribute_type) { + case HOUSE_ATTRIBUTE_IQ: { - lua_pushnumber(L, this_house->Control.TechLevel); - return 1; + lua_pushnumber(L, this_house->IQ); + return 1; - }break; - case HOUSE_ATTRIBUTE_IS_HUMAN: { + }break; + case HOUSE_ATTRIBUTE_TECH_LEVEL: { - lua_pushboolean(L, this_house->IsHuman); - return 1; + lua_pushnumber(L, this_house->Control.TechLevel); + return 1; - }break; - case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { + }break; + case HOUSE_ATTRIBUTE_IS_HUMAN: { - lua_pushnumber(L, (float)this_house->FirepowerBias.To_Float()); - return 1; + lua_pushboolean(L, this_house->IsHuman); + return 1; - }break; - case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { - - lua_pushnumber(L, (float)this_house->GroundspeedBias.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_FIREPOWER_BIAS: { - }break; - case HOUSE_ATTRIBUTE_ARMOR_BIAS: { + lua_pushnumber(L, (float)this_house->FirepowerBias.To_Float()); + return 1; - lua_pushnumber(L, (float)this_house->ArmorBias.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_GROUNDSPEED_BIAS: { - }break; - case HOUSE_ATTRIBUTE_ROF_BIAS: { + lua_pushnumber(L, (float)this_house->GroundspeedBias.To_Float()); + return 1; - lua_pushnumber(L, this_house->ROFBias.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_ARMOR_BIAS: { - }break; - case HOUSE_ATTRIBUTE_COST_BIAS: { + lua_pushnumber(L, (float)this_house->ArmorBias.To_Float()); + return 1; - lua_pushnumber(L, (float)this_house->CostBias.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_ROF_BIAS: { - }break; - case HOUSE_ATTRIBUTE_REPAIR_DELAY: { + lua_pushnumber(L, this_house->ROFBias.To_Float()); + return 1; - lua_pushnumber(L, (float)this_house->RepairDelay.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_COST_BIAS: { - }break; - case HOUSE_ATTRIBUTE_BUILD_DELAY: { + lua_pushnumber(L, (float)this_house->CostBias.To_Float()); + return 1; - lua_pushnumber(L, (float)this_house->BuildDelay.To_Float()); - return 1; + }break; + case HOUSE_ATTRIBUTE_REPAIR_DELAY: { - }break; - case HOUSE_ATTRIBUTE_STARTED: { + lua_pushnumber(L, (float)this_house->RepairDelay.To_Float()); + return 1; - lua_pushboolean(L, this_house->IsStarted); - return 1; + }break; + case HOUSE_ATTRIBUTE_BUILD_DELAY: { - }break; - case HOUSE_ATTRIBUTE_ALERTED: { + lua_pushnumber(L, (float)this_house->BuildDelay.To_Float()); + return 1; - lua_pushboolean(L, this_house->IsAlerted); - return 1; + }break; + case HOUSE_ATTRIBUTE_STARTED: { - }break; - case HOUSE_ATTRIBUTE_BASE_BUILDING: { + lua_pushboolean(L, this_house->IsStarted); + return 1; - lua_pushboolean(L, this_house->IsBaseBuilding); - return 1; + }break; + case HOUSE_ATTRIBUTE_ALERTED: { - }break; - case HOUSE_ATTRIBUTE_DISCOVERED: { + lua_pushboolean(L, this_house->IsAlerted); + return 1; - lua_pushboolean(L, this_house->IsDiscovered); - return 1; + }break; + case HOUSE_ATTRIBUTE_BASE_BUILDING: { - }break; - case HOUSE_ATTRIBUTE_CAPACITY: { + lua_pushboolean(L, this_house->IsBaseBuilding); + return 1; - lua_pushinteger(L, this_house->Capacity); - return 1; + }break; + case HOUSE_ATTRIBUTE_DISCOVERED: { - }break; - case HOUSE_ATTRIBUTE_TIBERIUM: { + lua_pushboolean(L, this_house->IsDiscovered); + return 1; - lua_pushinteger(L, this_house->Tiberium); - return 1; + }break; + case HOUSE_ATTRIBUTE_CAPACITY: { - }break; - case HOUSE_ATTRIBUTE_CREDITS: { + lua_pushinteger(L, this_house->Capacity); + return 1; - lua_pushinteger(L, this_house->Credits); - return 1; + }break; + case HOUSE_ATTRIBUTE_TIBERIUM: { - }break; - case HOUSE_ATTRIBUTE_ALERT_TIMER: { + lua_pushinteger(L, this_house->Tiberium); + return 1; - lua_pushnumber(L, this_house->AlertTime); - return 1; + }break; + case HOUSE_ATTRIBUTE_CREDITS: { - }break; - case HOUSE_ATTRIBUTE_REPAIR_TIMER: { + lua_pushinteger(L, this_house->Credits); + return 1; - lua_pushnumber(L, this_house->RepairTimer); - return 1; + }break; + case HOUSE_ATTRIBUTE_ALERT_TIMER: { - }break; - case HOUSE_ATTRIBUTE_POINTS: { + lua_pushnumber(L, this_house->AlertTime); + return 1; - lua_pushinteger(L, this_house->PointTotal); - return 1; + }break; + case HOUSE_ATTRIBUTE_REPAIR_TIMER: { - }break; - case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { + lua_pushnumber(L, this_house->RepairTimer); + return 1; - lua_pushinteger(L, (int)this_house->DestroyedBuildings->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_POINTS: { - }break; - case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { + lua_pushinteger(L, this_house->PointTotal); + return 1; - lua_pushinteger(L, (int)this_house->DestroyedInfantry->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_DESTROYED_BUILDINGS: { - }break; - case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { + lua_pushinteger(L, (int)this_house->DestroyedBuildings->Get_All_Totals()); + return 1; - lua_pushinteger(L, (int)this_house->DestroyedUnits->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_DESTROYED_INFANTRY: { - }break; - case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { + lua_pushinteger(L, (int)this_house->DestroyedInfantry->Get_All_Totals()); + return 1; - lua_pushinteger(L, (int)this_house->DestroyedAircraft->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VEHICLES: { - }break; - case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { + lua_pushinteger(L, (int)this_house->DestroyedUnits->Get_All_Totals()); + return 1; - lua_pushinteger(L, (int)this_house->DestroyedVessels->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_DESTROYED_AIRCRAFT: { - }break; - case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { + lua_pushinteger(L, (int)this_house->DestroyedAircraft->Get_All_Totals()); + return 1; - lua_pushinteger(L, (int)this_house->CapturedBuildings->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_DESTROYED_VESSELS: { - }break; - case HOUSE_ATTRIBUTE_CRATES: { + lua_pushinteger(L, (int)this_house->DestroyedVessels->Get_All_Totals()); + return 1; - lua_pushinteger(L, (int)this_house->TotalCrates->Get_All_Totals()); - return 1; + }break; + case HOUSE_ATTRIBUTE_CAPTURED_BUILDINGS: { - }break; - case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { + lua_pushinteger(L, (int)this_house->CapturedBuildings->Get_All_Totals()); + return 1; - lua_pushinteger(L, this_house->AircraftFactories); - return 1; + }break; + case HOUSE_ATTRIBUTE_CRATES: { - }break; - case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { + lua_pushinteger(L, (int)this_house->TotalCrates->Get_All_Totals()); + return 1; - lua_pushinteger(L, this_house->InfantryFactories); - return 1; + }break; + case HOUSE_ATTRIBUTE_AIRCRAFT_FACTORIES: { - }break; - case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { + lua_pushinteger(L, this_house->AircraftFactories); + return 1; - lua_pushinteger(L, this_house->UnitFactories); - return 1; + }break; + case HOUSE_ATTRIBUTE_INFANTRY_FACTORIES: { - }break; - case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { + lua_pushinteger(L, this_house->InfantryFactories); + return 1; - lua_pushinteger(L, this_house->VesselFactories); - return 1; + }break; + case HOUSE_ATTRIBUTE_UNIT_FACTORIES: { - }break; - case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { + lua_pushinteger(L, this_house->UnitFactories); + return 1; - lua_pushinteger(L, this_house->BuildingFactories); - return 1; + }break; + case HOUSE_ATTRIBUTE_VESSEL_FACTORIES: { - }break; - case HOUSE_ATTRIBUTE_POWER: { + lua_pushinteger(L, this_house->VesselFactories); + return 1; - lua_pushnumber(L, this_house->Power); - return 1; + }break; + case HOUSE_ATTRIBUTE_BUILDING_FACTORIES: { - }break; - case HOUSE_ATTRIBUTE_DRAIN: { + lua_pushinteger(L, this_house->BuildingFactories); + return 1; - lua_pushnumber(L, this_house->Drain); - return 1; + }break; + case HOUSE_ATTRIBUTE_POWER: { - }break; - case HOUSE_ATTRIBUTE_LAST_ATTACKER: { + lua_pushnumber(L, this_house->Power); + return 1; - lua_pushnumber(L, this_house->WhoLastHurtMe); - return 1; + }break; + case HOUSE_ATTRIBUTE_DRAIN: { - }break; + lua_pushnumber(L, this_house->Drain); + return 1; + + }break; + case HOUSE_ATTRIBUTE_LAST_ATTACKER: { + + lua_pushnumber(L, this_house->WhoLastHurtMe); + return 1; + + }break; + } + + } + + lua_pushnumber(L, -1); + + return -1; +} + +/*********************************************************************************************** + * Script_BuildUnit - Builds a given unit in a given factory * + * * + * SCRIPT INPUT: factoryObject (int) - The factory object id * + * * + * unitType (int) - The unit type index for the unit to make (unit in a * + * general sense, not a vehicle) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_BuildUnit(lua_State* L) { + + int factoryObject = lua_tointeger(L, 1); + int unitType = lua_tointeger(L, 2); + + BuildingClass* this_factory = (BuildingClass*)Script_GetCacheObject(factoryObject); + + // Get the appropriate technotype + if (this_factory != NULL && this_factory->IsActive) { + + int result = true; + + switch (this_factory->Class->ID) { + case STRUCT_WEAP: { + + UnitTypeClass* unit_to_place = UnitTypes.Ptr(unitType); + UnitClass* new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_UnitIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + } + else { + delete new_unit; } - } + lua_pushnumber(L, -1); + return 1; - lua_pushnumber(L, -1); + }break; + case STRUCT_TENT: + case STRUCT_BARRACKS: { - return -1; - } + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); - /*********************************************************************************************** - * Script_BuildUnit - Builds a given unit in a given factory * - * * - * SCRIPT INPUT: factoryObject (int) - The factory object id * - * * - * unitType (int) - The unit type index for the unit to make (unit in a * - * general sense, not a vehicle) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_BuildUnit(lua_State* L) { + if (this_factory->Exit_Object(new_unit)) { - int factoryObject = lua_tointeger(L, 1); - int unitType = lua_tointeger(L, 2); + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - BuildingClass* this_factory = (BuildingClass *)Script_GetCacheObject(factoryObject); - - // Get the appropriate technotype - if (this_factory != NULL && this_factory->IsActive) { + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); - int result = true; + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); - switch (this_factory->Class->ID) { - case STRUCT_WEAP: { + lua_pushnumber(L, CacheObject->UID); + return 1; - UnitTypeClass* unit_to_place = UnitTypes.Ptr(unitType); - UnitClass *new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_factory->House); - - if (this_factory->Exit_Object(new_unit)) { - - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_UnitIndexFromID(new_unit->ID); - - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - lua_pushnumber(L, CacheObject->UID); - return 1; - - }else { - delete new_unit; - } - - lua_pushnumber(L, -1); - return 1; - - }break; - case STRUCT_TENT: - case STRUCT_BARRACKS: { - - InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); - InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); - - if (this_factory->Exit_Object(new_unit)) { - - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_InfantryIndexFromID(new_unit->ID); - - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - lua_pushnumber(L, CacheObject->UID); - return 1; - - }else { - delete new_unit; - } - - lua_pushnumber(L, -1); - return 1; - - }break; - case STRUCT_KENNEL: { - - InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); - InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); - - if (this_factory->Exit_Object(new_unit)) { - - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_InfantryIndexFromID(new_unit->ID); - - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - lua_pushnumber(L, CacheObject->UID); - return 1; - - }else { - delete new_unit; - } - - lua_pushnumber(L, -1); - return 1; - - }break; - case STRUCT_HELIPAD: - case STRUCT_AIRSTRIP: { - - AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(unitType); - AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_factory->House); - - if (this_factory->Exit_Object(new_unit)) { - - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_AircraftIndexFromID(new_unit->ID); - - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - lua_pushnumber(L, CacheObject->UID); - return 1; - - }else { - delete new_unit; - } - - lua_pushnumber(L, -1); - return 1; - - }break; - case STRUCT_SUB_PEN: - case STRUCT_SHIP_YARD: { - - VesselTypeClass* unit_to_place = VesselTypes.Ptr(unitType); - VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_factory->House); - - if (this_factory->Exit_Object(new_unit)) { - - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_VesselIndexFromID(new_unit->ID); - - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - lua_pushnumber(L, CacheObject->UID); - return 1; - - }else { - delete new_unit; - } - - lua_pushnumber(L, -1); - return 1; - - }break; + } + else { + delete new_unit; } + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_KENNEL: { + + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(unitType); + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + } + else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_HELIPAD: + case STRUCT_AIRSTRIP: { + + AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(unitType); + AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_AircraftIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + } + else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; + case STRUCT_SUB_PEN: + case STRUCT_SHIP_YARD: { + + VesselTypeClass* unit_to_place = VesselTypes.Ptr(unitType); + VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_factory->House); + + if (this_factory->Exit_Object(new_unit)) { + + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; + + _objectIndex = Script_VesselIndexFromID(new_unit->ID); + + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + lua_pushnumber(L, CacheObject->UID); + return 1; + + } + else { + delete new_unit; + } + + lua_pushnumber(L, -1); + return 1; + + }break; } - - lua_pushnumber(L, -1); - return 1; } - /*********************************************************************************************** - * Script_CreateVehicle - Creates a given unit out of thin air * - * * - * SCRIPT INPUT: objectIndex(int) - The unit type to create * - * * - * houseType (int) - The player (house) index to create the unit for * - * * - * in_x (int) - X location to create object * - * * - * in_y (int) - Y location to create object * - * * - * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreateVehicle(lua_State* L) { - int objectIndex = lua_tointeger(L,1); - int houseType = lua_tointeger(L,2); - int in_x = lua_tointeger(L,3); - int in_y = lua_tointeger(L,4); + lua_pushnumber(L, -1); + return 1; +} - HouseClass *this_house = Houses.Ptr(houseType); +/*********************************************************************************************** + * Script_CreateVehicle - Creates a given unit out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The unit type to create * + * * + * houseType (int) - The player (house) index to create the unit for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateVehicle(lua_State* L) { - UnitTypeClass *unit_to_place = UnitTypes.Ptr(objectIndex); + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); - if (this_house != NULL && unit_to_place != NULL) { - UnitClass* new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_house); - if (new_unit != NULL) { - - bool new_unit_placed = false; + HouseClass* this_house = Houses.Ptr(houseType); - if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + UnitTypeClass* unit_to_place = UnitTypes.Ptr(objectIndex); + if (this_house != NULL && unit_to_place != NULL) { + UnitClass* new_unit = (UnitClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { + + bool new_unit_placed = false; + + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { new_unit_placed = true; } - - /* - ** Try to place the object into a nearby cell if something is preventing - ** placement at the given location. - */ - if(!new_unit_placed){ - CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); - if (new_unit->Unlimbo(::Cell_Coord(cell))) { - new_unit_placed = true; - } - } + } - if (new_unit_placed) { - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - _objectIndex = Script_UnitIndexFromID(new_unit->ID); + _objectIndex = Script_UnitIndexFromID(new_unit->ID); - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->UID); - return 1; - } + lua_pushnumber(L, CacheObject->UID); + return 1; } } - - lua_pushnumber(L, -1); - return 1; } + lua_pushnumber(L, -1); + return 1; +} - /*********************************************************************************************** - * Script_CreateInfantry - Creates a given Infantry out of thin air * - * * - * SCRIPT INPUT: objectIndex(int) - The Infantry type to create * - * * - * houseType (int) - The player (house) index to create the Infantry for * - * * - * in_x (int) - X location to create object * - * * - * in_y (int) - Y location to create object * - * * - * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreateInfantry(lua_State* L) { - int objectIndex = lua_tointeger(L, 1); - int houseType = lua_tointeger(L, 2); - int in_x = lua_tointeger(L, 3); - int in_y = lua_tointeger(L, 4); +/*********************************************************************************************** + * Script_CreateInfantry - Creates a given Infantry out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Infantry type to create * + * * + * houseType (int) - The player (house) index to create the Infantry for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateInfantry(lua_State* L) { - HouseClass* this_house = Houses.Ptr(houseType); + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); - InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(objectIndex); + HouseClass* this_house = Houses.Ptr(houseType); - if (this_house != NULL && unit_to_place != NULL) { - InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_house); - if (new_unit != NULL) { + InfantryTypeClass* unit_to_place = InfantryTypes.Ptr(objectIndex); - bool new_unit_placed = false; + if (this_house != NULL && unit_to_place != NULL) { + InfantryClass* new_unit = (InfantryClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { - if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)),DIR_S)) { + bool new_unit_placed = false; + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)), DIR_S)) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell), DIR_S)) { new_unit_placed = true; } - - /* - ** Try to place the object into a nearby cell if something is preventing - ** placement at the given location. - */ - if (!new_unit_placed) { - CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); - if (new_unit->Unlimbo(::Cell_Coord(cell),DIR_S)) { - new_unit_placed = true; - } - } + } - if (new_unit_placed) { - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - _objectIndex = Script_InfantryIndexFromID(new_unit->ID); + _objectIndex = Script_InfantryIndexFromID(new_unit->ID); - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->UID); - return 1; - } + lua_pushnumber(L, CacheObject->UID); + return 1; } } - - lua_pushnumber(L, -1); - return 1; } - /*********************************************************************************************** - * Script_CreateAircraft - Creates a given Aircraft out of thin air * - * * - * SCRIPT INPUT: objectIndex(int) - The Aircraft type to create * - * * - * houseType (int) - The player (house) index to create the Aircraft for * - * * - * in_x (int) - X location to create object * - * * - * in_y (int) - Y location to create object * - * * - * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreateAircraft(lua_State* L) { + lua_pushnumber(L, -1); + return 1; +} - int objectIndex = lua_tointeger(L, 1); - int houseType = lua_tointeger(L, 2); - int in_x = lua_tointeger(L, 3); - int in_y = lua_tointeger(L, 4); +/*********************************************************************************************** + * Script_CreateAircraft - Creates a given Aircraft out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Aircraft type to create * + * * + * houseType (int) - The player (house) index to create the Aircraft for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateAircraft(lua_State* L) { - HouseClass* this_house = Houses.Ptr(houseType); + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); - AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(objectIndex); + HouseClass* this_house = Houses.Ptr(houseType); - if (this_house != NULL && unit_to_place != NULL) { - AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_house); - if (new_unit != NULL) { + AircraftTypeClass* unit_to_place = AircraftTypes.Ptr(objectIndex); - bool new_unit_placed = false; + if (this_house != NULL && unit_to_place != NULL) { + AircraftClass* new_unit = (AircraftClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { - if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + bool new_unit_placed = false; + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { new_unit_placed = true; } - - /* - ** Try to place the object into a nearby cell if something is preventing - ** placement at the given location. - */ - if (!new_unit_placed) { - CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); - if (new_unit->Unlimbo(::Cell_Coord(cell))) { - new_unit_placed = true; - } - } + } - if (new_unit_placed) { - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - _objectIndex = Script_AircraftIndexFromID(new_unit->ID); + _objectIndex = Script_AircraftIndexFromID(new_unit->ID); - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->UID); - return 1; - } + lua_pushnumber(L, CacheObject->UID); + return 1; } } - - lua_pushnumber(L, -1); - return 1; } - /*********************************************************************************************** - * Script_CreateVessel - Creates a given Vessel out of thin air * - * * - * SCRIPT INPUT: objectIndex(int) - The Vessel type to create * - * * - * houseType (int) - The player (house) index to create the Vessel for * - * * - * in_x (int) - X location to create object * - * * - * in_y (int) - Y location to create object * - * * - * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CreateVessel(lua_State* L) { + lua_pushnumber(L, -1); + return 1; +} - int objectIndex = lua_tointeger(L, 1); - int houseType = lua_tointeger(L, 2); - int in_x = lua_tointeger(L, 3); - int in_y = lua_tointeger(L, 4); +/*********************************************************************************************** + * Script_CreateVessel - Creates a given Vessel out of thin air * + * * + * SCRIPT INPUT: objectIndex(int) - The Vessel type to create * + * * + * houseType (int) - The player (house) index to create the Vessel for * + * * + * in_x (int) - X location to create object * + * * + * in_y (int) - Y location to create object * + * * + * SCRIPT OUTPUT: (int) the cached object ID for use with Mapscript Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CreateVessel(lua_State* L) { - HouseClass* this_house = Houses.Ptr(houseType); + int objectIndex = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + int in_x = lua_tointeger(L, 3); + int in_y = lua_tointeger(L, 4); - VesselTypeClass* unit_to_place = VesselTypes.Ptr(objectIndex); + HouseClass* this_house = Houses.Ptr(houseType); - if (this_house != NULL && unit_to_place != NULL) { - VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_house); - if (new_unit != NULL) { + VesselTypeClass* unit_to_place = VesselTypes.Ptr(objectIndex); - bool new_unit_placed = false; + if (this_house != NULL && unit_to_place != NULL) { + VesselClass* new_unit = (VesselClass*)unit_to_place->Create_One_Of(this_house); + if (new_unit != NULL) { - if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + bool new_unit_placed = false; + if (new_unit->Unlimbo(Cell_Coord(XY_Cell(in_x, in_y)))) { + + new_unit_placed = true; + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the given location. + */ + if (!new_unit_placed) { + CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); + if (new_unit->Unlimbo(::Cell_Coord(cell))) { new_unit_placed = true; } - - /* - ** Try to place the object into a nearby cell if something is preventing - ** placement at the given location. - */ - if (!new_unit_placed) { - CELL cell = Map.Nearby_Location(Cell_Coord(XY_Cell(in_x, in_y)), new_unit->Class->Speed); - if (new_unit->Unlimbo(::Cell_Coord(cell))) { - new_unit_placed = true; - } - } + } - if (new_unit_placed) { - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; + if (new_unit_placed) { + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - _objectIndex = Script_VesselIndexFromID(new_unit->ID); + _objectIndex = Script_VesselIndexFromID(new_unit->ID); - CacheObject->ID = new_unit->ID; - CacheObject->RTTI = new_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); + CacheObject->ID = new_unit->ID; + CacheObject->RTTI = new_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); - lua_pushnumber(L, CacheObject->UID); - return 1; - } + lua_pushnumber(L, CacheObject->UID); + return 1; } } - - lua_pushnumber(L, -1); - return 1; } - + + lua_pushnumber(L, -1); + return 1; +} + /********************************************************************************************** * Object Orders Functions * @@ -5713,7 +5720,7 @@ /*********************************************************************************************** * Script_OrderMove - Assigns a movement order to an object * * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * * * * in_x (int) - X location to move object to * * * @@ -5728,1025 +5735,1025 @@ * WARNINGS: ? * * * *=============================================================================================*/ - static int Script_OrderMove(lua_State* L) { +static int Script_OrderMove(lua_State* L) { - int objectID = lua_tointeger(L, 1); - int in_x = lua_tointeger(L, 2); - int in_y = lua_tointeger(L, 3); + int objectID = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); - ObjectClass* this_object = Script_GetCacheObject(objectID); + ObjectClass* this_object = Script_GetCacheObject(objectID); - if (this_object != NULL) { + if (this_object != NULL) { - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, As_Target(XY_Cell(in_x, in_y))); + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, As_Target(XY_Cell(in_x, in_y))); - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); + lua_pushnumber(L, 1); return 1; } - /*********************************************************************************************** - * Script_OrderAttackCell - Assigns an attack order to an object for a specific cell * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (int) - X location to attack * - * * - * in_y (int) - Y location to attack * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderAttackCell(lua_State* L) { + lua_pushnumber(L, -1); + return 1; - int objectID = lua_tointeger(L, 1); +} - int in_x = lua_tointeger(L, 2); - int in_y = lua_tointeger(L, 3); +/*********************************************************************************************** + * Script_OrderAttackCell - Assigns an attack order to an object for a specific cell * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (int) - X location to attack * + * * + * in_y (int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderAttackCell(lua_State* L) { - ObjectClass* this_object = Script_GetCacheObject(objectID); + int objectID = lua_tointeger(L, 1); - if (this_object != NULL) { + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } + ObjectClass* this_object = Script_GetCacheObject(objectID); - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, this_target, this_target); - - lua_pushnumber(L, 1); - return 1; + if (this_object != NULL) { + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); } - lua_pushnumber(L, -1); - return 1; + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, this_target, this_target); - } - - /*********************************************************************************************** - * Script_OrderAttack - Assigns an attack order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (int) - X location to attack * - * * - * in_y (int) - Y location to attack * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderAttack(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); - } - - ObjectClass* this_object = Script_GetCacheObject(objectID); - - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); - return 1; - - } - - /*********************************************************************************************** - * Script_OrderHunt - Assigns a hunt order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (opt)(int) - X location to attack * - * * - * in_y (opt)(int) - Y location to attack * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderHunt(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); - } - - ObjectClass* this_object = Script_GetCacheObject(objectID); - - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); - return 1; - - } - - /*********************************************************************************************** - * Script_OrderGuard - Assigns a guard order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (opt)(int) - X location to attack * - * * - * in_y (opt)(int) - Y location to attack * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderGuard(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); - } - - ObjectClass* this_object = Script_GetCacheObject(objectID); - - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); - return 1; - - } - - /*********************************************************************************************** - * Script_OrderRetreat - Assigns a retreat order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (opt)(int) - X location to attack * - * * - * in_y (opt)(int) - Y location to attack * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderRetreat(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); - } - - ObjectClass* this_object = Script_GetCacheObject(objectID); - - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); - return 1; - - } - - /*********************************************************************************************** - * Script_OrderEnter - Assigns an enter order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (opt)(int) - X location to enter (building / unit) * - * * - * in_y (opt)(int) - Y location to enter (building / unit) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderEnter(lua_State* L) { - - int objectID = lua_tointeger(L, 1); - - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); - } - - ObjectClass* this_object = Script_GetCacheObject(objectID); - - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_ENTER, this_target, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); + lua_pushnumber(L, 1); return 1; } - /*********************************************************************************************** - * Script_OrderCapture - Assigns a capture order to an object * - * * - * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * - * * - * in_x (opt)(int) - X location to capture (building) * - * * - * in_y (opt)(int) - Y location to capture (building) * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_OrderCapture(lua_State* L) { + lua_pushnumber(L, -1); + return 1; - int objectID = lua_tointeger(L, 1); +} - int in_x = -1; - int in_y = -1; - if (lua_gettop(L) == 3) { - in_x = lua_tointeger(L, 2); - in_y = lua_tointeger(L, 3); +/*********************************************************************************************** + * Script_OrderAttack - Assigns an attack order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (int) - X location to attack * + * * + * in_y (int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderAttack(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); } - ObjectClass* this_object = Script_GetCacheObject(objectID); + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); - if (this_object != NULL) { - - TARGET this_target = 0; - if (in_x >= 0 && in_y >= 0) { - this_target = As_Target(XY_Cell(in_x, in_y)); - } - - Queue_Mission(TargetClass(this_object), MISSION_CAPTURE, this_target, this_target); - - lua_pushnumber(L, 1); - return 1; - - } - - lua_pushnumber(L, -1); + lua_pushnumber(L, 1); return 1; } + lua_pushnumber(L, -1); + return 1; + +} + +/*********************************************************************************************** + * Script_OrderHunt - Assigns a hunt order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderHunt(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + +} + +/*********************************************************************************************** + * Script_OrderGuard - Assigns a guard order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderGuard(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + +} + +/*********************************************************************************************** + * Script_OrderRetreat - Assigns a retreat order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to attack * + * * + * in_y (opt)(int) - Y location to attack * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderRetreat(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + +} + +/*********************************************************************************************** + * Script_OrderEnter - Assigns an enter order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to enter (building / unit) * + * * + * in_y (opt)(int) - Y location to enter (building / unit) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderEnter(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_ENTER, this_target, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + +} + +/*********************************************************************************************** + * Script_OrderCapture - Assigns a capture order to an object * + * * + * SCRIPT INPUT: objectID (int) - The object ID (retrieved via MS functions) * + * * + * in_x (opt)(int) - X location to capture (building) * + * * + * in_y (opt)(int) - Y location to capture (building) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_OrderCapture(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + + int in_x = -1; + int in_y = -1; + if (lua_gettop(L) == 3) { + in_x = lua_tointeger(L, 2); + in_y = lua_tointeger(L, 3); + } + + ObjectClass* this_object = Script_GetCacheObject(objectID); + + if (this_object != NULL) { + + TARGET this_target = 0; + if (in_x >= 0 && in_y >= 0) { + this_target = As_Target(XY_Cell(in_x, in_y)); + } + + Queue_Mission(TargetClass(this_object), MISSION_CAPTURE, this_target, this_target); + + lua_pushnumber(L, 1); + return 1; + + } + + lua_pushnumber(L, -1); + return 1; + +} + /********************************************************************************************** * Mission Utility Functions * *=============================================================================================*/ - /*********************************************************************************************** - * Script_SetBriefingText - Sets the text on the mission briefing screen * - * * - * SCRIPT INPUT: (char*); The input text * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_SetBriefingText(lua_State* L) { - strcpy(Scen.BriefingText, lua_tostring(L, -1)); - return -1; - } +/*********************************************************************************************** + * Script_SetBriefingText - Sets the text on the mission briefing screen * + * * + * SCRIPT INPUT: (char*); The input text * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_SetBriefingText(lua_State* L) { + strcpy(Scen.BriefingText, lua_tostring(L, -1)); + return -1; +} /********************************************************************************************** * Status / Location Utility Functions * *=============================================================================================*/ - /*********************************************************************************************** - * Script_GiveCredits - Gives or takes a given amount of credits to / from a player * - * * - * SCRIPT INPUT: cashToGive (int) - The amount of cold hard credits to give the player. * - * Negative numbers may be used to *take* money * - * * - * houseType (int) - The player (house) index to complete this transaction * - * for, or -1 to give to all players * - * * - * SCRIPT OUTPUT: void * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - * HISTORY: * - * 6/11/2020 - JM Created * - * 6/12/2020 - JJ Added player (house) index input * - *=============================================================================================*/ - static int Script_GiveCredits(lua_State* L) { +/*********************************************************************************************** + * Script_GiveCredits - Gives or takes a given amount of credits to / from a player * + * * + * SCRIPT INPUT: cashToGive (int) - The amount of cold hard credits to give the player. * + * Negative numbers may be used to *take* money * + * * + * houseType (int) - The player (house) index to complete this transaction * + * for, or -1 to give to all players * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + * HISTORY: * + * 6/11/2020 - JM Created * + * 6/12/2020 - JJ Added player (house) index input * + *=============================================================================================*/ +static int Script_GiveCredits(lua_State* L) { - int cashToGive = lua_tointeger(L, 1); + int cashToGive = lua_tointeger(L, 1); - int houseType = -1; + int houseType = -1; - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); - } - - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - - HouseClass* house = Houses.Ptr(h_index); - - if (house->ID == houseType || houseType == -1) { - - if (cashToGive < 0) { - house->Spend_Money(max(0, cashToGive * -1)); - } - else { - house->Refund_Money(cashToGive); - } - - // Discontinue giving out free cash if a specific house was specified - if (houseType >= 0) { - break; - } - } - - } - - return 1; + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); } - /*********************************************************************************************** - * Script_GetCredits - Gets how many credits the given player has * - * * - * SCRIPT INPUT: houseType (int) - The player (house) index to get credits for * - * * - * SCRIPT OUTPUT: result (number) - The amount of credits the player has * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetCredits(lua_State* L) { + for (int h_index = 0; h_index < Houses.Count(); h_index++) { - int houseType = lua_tointeger(L, -1); + HouseClass* house = Houses.Ptr(h_index); - long result = -1; + if (house->ID == houseType || houseType == -1) { - for (int h_index = 0; h_index < Houses.Count(); h_index++) { - - HouseClass* house = Houses.Ptr(h_index); - - if (house->ID == houseType || houseType == -1) { - result = house->Credits; + if (cashToGive < 0) { + house->Spend_Money(max(0, cashToGive * -1)); + } + else { + house->Refund_Money(cashToGive); + } + // Discontinue giving out free cash if a specific house was specified + if (houseType >= 0) { break; } - } - lua_pushnumber(L, result); - - return 1; } - /*********************************************************************************************** - * Script_CountBuildings - Returns the amount of specific buildings for a player * - * * - * Optionally, you can return only building at a particular cell or in a rectangular area * - * using the two groups of optional coordinate inputs * - * * - * SCRIPT INPUT: structType (int) - The structure index to count buildings for * - * or -1 for all structures * - * * - * houseType (int) - The player (house) index to count buildings for * - * or -1 to for all players * - * * - * NOTE: -1 for either index input acts as ALL * - * * - * in_x1 (int) (optional A) - Only include buildings at or beginning at this * - * Cell/X location * - * * - * in_y1 (int) (optional A) - Only include buildings at or beginning at this * - * Cell/Y location * - * * - * in_x2 (int) (optional B) - Only include buildings at or before this Cell/X * - * location * - * * - * in_y2 (int) (optional B) - Only include buildings at or before this Cell/Y * - * location * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CountBuildings(lua_State* L) { - int structType = lua_tointeger(L, 1); + return 1; +} - int houseType = -1; +/*********************************************************************************************** + * Script_GetCredits - Gets how many credits the given player has * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to get credits for * + * * + * SCRIPT OUTPUT: result (number) - The amount of credits the player has * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GetCredits(lua_State* L) { - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); + int houseType = lua_tointeger(L, -1); + + long result = -1; + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + result = house->Credits; + + break; } - // Optional search area - bool within_area = false; - int in_x1, in_x2, in_y1, in_y2; - if (lua_gettop(L) == 4) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = in_x1; - in_y2 = in_y1; - within_area = true; - } - else if (lua_gettop(L) == 6) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = lua_tointeger(L, 5); - in_y2 = lua_tointeger(L, 6); - within_area = true; - } + } - int result = 0; - for (int b_index = 0; b_index < Buildings.Count(); b_index++) { - BuildingClass* building = Buildings.Ptr(b_index); + lua_pushnumber(L, result); - if (building->Owner() == houseType || houseType == -1) { - if (building->Class->Type == structType || structType == -1) { + return 1; +} - // Count, in general - if (within_area == false) { - result++; +/*********************************************************************************************** + * Script_CountBuildings - Returns the amount of specific buildings for a player * + * * + * Optionally, you can return only building at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: structType (int) - The structure index to count buildings for * + * or -1 for all structures * + * * + * houseType (int) - The player (house) index to count buildings for * + * or -1 to for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include buildings at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include buildings at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include buildings at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include buildings at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CountBuildings(lua_State* L) { + int structType = lua_tointeger(L, 1); - // Within search area - } - else { + int houseType = -1; - int this_x = Cell_X(Coord_Cell(building->Coord)); - int this_y = Cell_Y(Coord_Cell(building->Coord)); + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } - if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { - result++; - } - } + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + int result = 0; + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass* building = Buildings.Ptr(b_index); + + if (building->Owner() == houseType || houseType == -1) { + if (building->Class->Type == structType || structType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area } + else { + + int this_x = Cell_X(Coord_Cell(building->Coord)); + int this_y = Cell_Y(Coord_Cell(building->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + } } - - lua_pushnumber(L, result); - return 1; } - /*********************************************************************************************** - * Script_CountAircraft - Returns the amount of specific aircraft for a player * - * * - * Optionally, you can return only aircraft at a particular cell or in a rectangular area * - * using the two groups of optional coordinate inputs * - * * - * SCRIPT INPUT: aircraftType (int) - The aircraft index to count aircraft for * - * or -1 for all aircraft * - * * - * houseType (int) - The player (house) index to count aircraft for * - * or -1 for all players * - * * - * NOTE: -1 for either index input acts as ALL * - * * - * in_x1 (int) (optional A) - Only include aircraft at or beginning at this * - * Cell/X location * - * * - * in_y1 (int) (optional A) - Only include aircraft at or beginning at this * - * Cell/Y location * - * * - * in_x2 (int) (optional B) - Only include aircraft at or before this Cell/X * - * location * - * * - * in_y2 (int) (optional B) - Only include aircraft at or before this Cell/Y * - * location * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching aircraft for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CountAircraft(lua_State* L) { - int aircraftType = lua_tointeger(L, 1); + lua_pushnumber(L, result); + return 1; +} - int houseType = -1; +/*********************************************************************************************** + * Script_CountAircraft - Returns the amount of specific aircraft for a player * + * * + * Optionally, you can return only aircraft at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: aircraftType (int) - The aircraft index to count aircraft for * + * or -1 for all aircraft * + * * + * houseType (int) - The player (house) index to count aircraft for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include aircraft at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include aircraft at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include aircraft at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include aircraft at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching aircraft for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CountAircraft(lua_State* L) { + int aircraftType = lua_tointeger(L, 1); - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); - } + int houseType = -1; - // Optional search area - bool within_area = false; - int in_x1, in_x2, in_y1, in_y2; - if (lua_gettop(L) == 4) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = in_x1; - in_y2 = in_y1; - within_area = true; - } - else if (lua_gettop(L) == 6) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = lua_tointeger(L, 5); - in_y2 = lua_tointeger(L, 6); - within_area = true; - } + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } - int result = 0; - for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { - AircraftClass* aircraft = Aircraft.Ptr(a_index); + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } - if (aircraft->Owner() == houseType || houseType == -1) { - if (aircraft->Class->Type == aircraftType || aircraftType == -1) { + int result = 0; + for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { + AircraftClass* aircraft = Aircraft.Ptr(a_index); - // Count, in general - if (within_area == false) { - result++; + if (aircraft->Owner() == houseType || houseType == -1) { + if (aircraft->Class->Type == aircraftType || aircraftType == -1) { - // Within search area - } - else { - - int this_x = Cell_X(Coord_Cell(aircraft->Coord)); - int this_y = Cell_Y(Coord_Cell(aircraft->Coord)); - - if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { - result++; - } - } + // Count, in general + if (within_area == false) { + result++; + // Within search area } + else { + + int this_x = Cell_X(Coord_Cell(aircraft->Coord)); + int this_y = Cell_Y(Coord_Cell(aircraft->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + } } - - lua_pushnumber(L, result); - return 1; } - /*********************************************************************************************** - * Script_CountUnits - Returns the amount of specific units for a player * - * * - * Optionally, you can return only units at a particular cell or in a rectangular area * - * using the two groups of optional coordinate inputs * - * * - * SCRIPT INPUT: unitType (int) - The unit index to count units for * - * or -1 for all units * - * * - * houseType (int) - The player (house) index to count units for * - * or -1 for all players * - * * - * NOTE: -1 for either index input acts as ALL * - * * - * in_x1 (int) (optional A) - Only include units at or beginning at this * - * Cell/X location * - * * - * in_y1 (int) (optional A) - Only include units at or beginning at this * - * Cell/Y location * - * * - * in_x2 (int) (optional B) - Only include units at or before this Cell/X * - * location * - * * - * in_y2 (int) (optional B) - Only include units at or before this Cell/Y * - * location * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching units for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CountUnits(lua_State* L) { - int unitType = lua_tointeger(L, 1); + lua_pushnumber(L, result); + return 1; +} - int houseType = -1; +/*********************************************************************************************** + * Script_CountUnits - Returns the amount of specific units for a player * + * * + * Optionally, you can return only units at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: unitType (int) - The unit index to count units for * + * or -1 for all units * + * * + * houseType (int) - The player (house) index to count units for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include units at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include units at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include units at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include units at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching units for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CountUnits(lua_State* L) { + int unitType = lua_tointeger(L, 1); - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); - } + int houseType = -1; - // Optional search area - bool within_area = false; - int in_x1, in_x2, in_y1, in_y2; - if (lua_gettop(L) == 4) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = in_x1; - in_y2 = in_y1; - within_area = true; - } - else if (lua_gettop(L) == 6) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = lua_tointeger(L, 5); - in_y2 = lua_tointeger(L, 6); - within_area = true; - } + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } - int result = 0; - for (int u_index = 0; u_index < Units.Count(); u_index++) { - UnitClass* unit = Units.Ptr(u_index); + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } - if (unit->Owner() == houseType || houseType == -1) { - if (unit->Class->Type == unitType || unitType == -1) { + int result = 0; + for (int u_index = 0; u_index < Units.Count(); u_index++) { + UnitClass* unit = Units.Ptr(u_index); - // Count, in general - if (within_area == false) { - result++; + if (unit->Owner() == houseType || houseType == -1) { + if (unit->Class->Type == unitType || unitType == -1) { - // Within search area - } - else { - - int this_x = Cell_X(Coord_Cell(unit->Coord)); - int this_y = Cell_Y(Coord_Cell(unit->Coord)); - - if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { - result++; - } - } + // Count, in general + if (within_area == false) { + result++; + // Within search area } + else { + + int this_x = Cell_X(Coord_Cell(unit->Coord)); + int this_y = Cell_Y(Coord_Cell(unit->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + } } - - lua_pushnumber(L, result); - return 1; } - /*********************************************************************************************** - * Script_CountInfantry - Returns the amount of specific infantry for a player * - * * - * Optionally, you can return only infantry at a particular cell or in a rectangular area * - * using the two groups of optional coordinate inputs * - * * - * SCRIPT INPUT: infantryType (int) - The infantry index to count infantry for * - * or -1 for all infantry * - * * - * houseType (int) - The player (house) index to count infantry for * - * or -1 for all players * - * * - * NOTE: -1 for either index input acts as ALL * - * * - * in_x1 (int) (optional A) - Only include infantry at or beginning at this * - * Cell/X location * - * * - * in_y1 (int) (optional A) - Only include infantry at or beginning at this * - * Cell/Y location * - * * - * in_x2 (int) (optional B) - Only include infantry at or before this Cell/X * - * location * - * * - * in_y2 (int) (optional B) - Only include infantry at or before this Cell/Y * - * location * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching infantry for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CountInfantry(lua_State* L) { - int infantryType = lua_tointeger(L, 1); + lua_pushnumber(L, result); + return 1; +} - int houseType = -1; +/*********************************************************************************************** + * Script_CountInfantry - Returns the amount of specific infantry for a player * + * * + * Optionally, you can return only infantry at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: infantryType (int) - The infantry index to count infantry for * + * or -1 for all infantry * + * * + * houseType (int) - The player (house) index to count infantry for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include infantry at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include infantry at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include infantry at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include infantry at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching infantry for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CountInfantry(lua_State* L) { + int infantryType = lua_tointeger(L, 1); - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); - } + int houseType = -1; - // Optional search area - bool within_area = false; - int in_x1, in_x2, in_y1, in_y2; - if (lua_gettop(L) == 4) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = in_x1; - in_y2 = in_y1; - within_area = true; - } - else if (lua_gettop(L) == 6) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = lua_tointeger(L, 5); - in_y2 = lua_tointeger(L, 6); - within_area = true; - } + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } - int result = 0; - for (int u_index = 0; u_index < Infantry.Count(); u_index++) { - InfantryClass* infantry = Infantry.Ptr(u_index); + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } - if (infantry->Owner() == houseType || houseType == -1) { - if (infantry->Class->Type == infantryType || infantryType == -1) { + int result = 0; + for (int u_index = 0; u_index < Infantry.Count(); u_index++) { + InfantryClass* infantry = Infantry.Ptr(u_index); - // Count, in general - if (within_area == false) { - result++; + if (infantry->Owner() == houseType || houseType == -1) { + if (infantry->Class->Type == infantryType || infantryType == -1) { - // Within search area - } - else { - - int this_x = Cell_X(Coord_Cell(infantry->Coord)); - int this_y = Cell_Y(Coord_Cell(infantry->Coord)); - - if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { - result++; - } - } + // Count, in general + if (within_area == false) { + result++; + // Within search area } + else { + + int this_x = Cell_X(Coord_Cell(infantry->Coord)); + int this_y = Cell_Y(Coord_Cell(infantry->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + } } - - lua_pushnumber(L, result); - return 1; } - /*********************************************************************************************** - * Script_CountVessels - Returns the amount of specific vessels for a player * - * * - * Optionally, you can return only vessels at a particular cell or in a rectangular area * - * using the two groups of optional coordinate inputs * - * * - * SCRIPT INPUT: vesselType (int) - The vessel index to count vessels for * - * or -1 for all vessels * - * * - * houseType (int) - The player (house) index to count vessels for * - * or -1 for all players * - * * - * NOTE: -1 for either index input acts as ALL * - * * - * in_x1 (int) (optional A) - Only include vessels at or beginning at this * - * Cell/X location * - * * - * in_y1 (int) (optional A) - Only include vessels at or beginning at this * - * Cell/Y location * - * * - * in_x2 (int) (optional B) - Only include vessels at or before this Cell/X * - * location * - * * - * in_y2 (int) (optional B) - Only include vessels at or before this Cell/X * - * location * - * * - * SCRIPT OUTPUT: result (number) - The amount of matching vessels for that player * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_CountVessels(lua_State* L) { - int vesselType = lua_tointeger(L, 1); + lua_pushnumber(L, result); + return 1; +} - int houseType = -1; +/*********************************************************************************************** + * Script_CountVessels - Returns the amount of specific vessels for a player * + * * + * Optionally, you can return only vessels at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: vesselType (int) - The vessel index to count vessels for * + * or -1 for all vessels * + * * + * houseType (int) - The player (house) index to count vessels for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include vessels at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include vessels at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include vessels at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include vessels at or before this Cell/X * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching vessels for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_CountVessels(lua_State* L) { + int vesselType = lua_tointeger(L, 1); - // Optional houseType parameter - if (lua_gettop(L) == 2) { - houseType = lua_tointeger(L, 2); - } + int houseType = -1; - // Optional search area - bool within_area = false; - int in_x1, in_x2, in_y1, in_y2; - if (lua_gettop(L) == 4) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = in_x1; - in_y2 = in_y1; - within_area = true; - } - else if (lua_gettop(L) == 6) { - in_x1 = lua_tointeger(L, 3); - in_y1 = lua_tointeger(L, 4); - in_x2 = lua_tointeger(L, 5); - in_y2 = lua_tointeger(L, 6); - within_area = true; - } + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } - int result = 0; - for (int u_index = 0; u_index < Vessels.Count(); u_index++) { - VesselClass* vessel = Vessels.Ptr(u_index); + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } - if (vessel->Owner() == houseType || houseType == -1) { - if (vessel->Class->Type == vesselType || vesselType == -1) { + int result = 0; + for (int u_index = 0; u_index < Vessels.Count(); u_index++) { + VesselClass* vessel = Vessels.Ptr(u_index); - // Count, in general - if (within_area == false) { - result++; + if (vessel->Owner() == houseType || houseType == -1) { + if (vessel->Class->Type == vesselType || vesselType == -1) { - // Within search area - } - else { - - int this_x = Cell_X(Coord_Cell(vessel->Coord)); - int this_y = Cell_Y(Coord_Cell(vessel->Coord)); - - if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { - result++; - } - } + // Count, in general + if (within_area == false) { + result++; + // Within search area } + else { + + int this_x = Cell_X(Coord_Cell(vessel->Coord)); + int this_y = Cell_Y(Coord_Cell(vessel->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + } } + } + + lua_pushnumber(L, result); + return 1; +} + +/*********************************************************************************************** + * Script_GetWaypointX - Gets the Cell/X Coordinate of Waypoint * + * * + * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the X coordinate for * + * * + * SCRIPT OUTPUT: outX (int) - The Cell/X coordinate of the waypoint * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GetWaypointX(lua_State* L) { + + int ret = lua_tointeger(L, -1); + + if (ret > 0 && ret < sizeof(Scen.Waypoint)) { + + lua_pushnumber(L, Cell_X(Scen.Waypoint[ret])); - lua_pushnumber(L, result); return 1; + } - /*********************************************************************************************** - * Script_GetWaypointX - Gets the Cell/X Coordinate of Waypoint * - * * - * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the X coordinate for * - * * - * SCRIPT OUTPUT: outX (int) - The Cell/X coordinate of the waypoint * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetWaypointX(lua_State* L) { + lua_pushnumber(L, -1); - int ret = lua_tointeger(L, -1); + return -1; +} - if (ret > 0 && ret < sizeof(Scen.Waypoint)) { +/*********************************************************************************************** + * Script_GetWaypointY - Gets the Cell/Y Coordinate of Waypoint * + * * + * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the Y coordinate for * + * * + * SCRIPT OUTPUT: outX (int) - The Cell/Y coordinate of the waypoint * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +static int Script_GetWaypointY(lua_State* L) { - lua_pushnumber(L, Cell_X(Scen.Waypoint[ret])); + int ret = lua_tointeger(L, -1); - return 1; + if (ret > 0 && ret < sizeof(Scen.Waypoint)) { - } + lua_pushnumber(L, Cell_Y(Scen.Waypoint[ret])); - lua_pushnumber(L, -1); + return 1; - return -1; } - /*********************************************************************************************** - * Script_GetWaypointY - Gets the Cell/Y Coordinate of Waypoint * - * * - * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the Y coordinate for * - * * - * SCRIPT OUTPUT: outX (int) - The Cell/Y coordinate of the waypoint * - * * - * INPUT: lua_State - The current Lua state * - * * - * OUTPUT: int; Did the function run successfully? Return 1 * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - static int Script_GetWaypointY(lua_State* L) { + lua_pushnumber(L, -1); - int ret = lua_tointeger(L, -1); - - if (ret > 0 && ret < sizeof(Scen.Waypoint)) { - - lua_pushnumber(L, Cell_Y(Scen.Waypoint[ret])); - - return 1; - - } - - lua_pushnumber(L, -1); - - return -1; - } + return -1; +} /********************************************************************************************** * Special / Debug Functions * @@ -6766,801 +6773,801 @@ * WARNINGS: ? * * * *=============================================================================================*/ - static int Script_Console_Print(lua_State* L) { +static int Script_Console_Print(lua_State* L) { - const char* inputText = lua_tostring(L, -1); // grab path string from top of stack + const char* inputText = lua_tostring(L, -1); // grab path string from top of stack - Console_Printf(inputText); + Console_Printf(inputText); + + return -1; +} - return -1; - } - /********************************************************************************************** * MapScript Internal Functions * *=============================================================================================*/ - /*********************************************************************************************** - * MapScript::GetCacheObject -- Returns ObjectClass from cache with given ID * - * * - * INPUT: input_object_id (int) - Input object ID * - * * - * OUTPUT: this_object (ObjectClass*) - The object with the ID or NULL * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - ObjectClass* Script_GetCacheObject(int input_object_id) { - - if (input_object_id < 0) { - return false; - } - - // Look through cache for object with ID, and return it - for (std::vector::iterator it = Scen.mapScript->ObjectCache.begin(); it != Scen.mapScript->ObjectCache.end(); ++it) {//Error 2-4 - - MapScriptObject* thisCacheObject = (MapScriptObject*)(*it); - - // Here it is - if (thisCacheObject->UID == input_object_id) { - - // Determine type of object and return it from cache. If it doesn't exist any longer, remove it from cache - switch (thisCacheObject->RTTI) { - case RTTI_BUILDING: { - BuildingClass* this_building = Buildings.Ptr(thisCacheObject->classIndex); - if (this_building != NULL) { - return this_building; - } - else { - it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache - return NULL; - } - }break; - case RTTI_UNIT: { - UnitClass* this_unit = Units.Ptr(thisCacheObject->classIndex); - if (this_unit != NULL) { - return this_unit; - } - else { - it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache - return NULL; - } - }break; - case RTTI_AIRCRAFT: { - AircraftClass* this_aircraft = Aircraft.Ptr(thisCacheObject->classIndex); - if (this_aircraft != NULL) { - return this_aircraft; - } - else { - it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache - return NULL; - } - }break; - case RTTI_INFANTRY: { - InfantryClass* this_infantry = Infantry.Ptr(thisCacheObject->classIndex); - if (this_infantry != NULL) { - return this_infantry; - } - else { - it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache - return NULL; - } - }break; - case RTTI_VESSEL: { - VesselClass* this_vessel = Vessels.Ptr(thisCacheObject->classIndex); - if (this_vessel != NULL) { - return this_vessel; - } - else { - it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache - return NULL; - } - }break; - } - - } - - } - - // Not found in cache; let's find the object by ID the hard way... If scripting is done well, this doesn't get hit - int _objectIndex = -1; - MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); - CacheObject->ID = -1; - - _objectIndex = Script_BuildingIndexFromID(input_object_id); - BuildingClass* this_building = Buildings.Ptr(_objectIndex); - if (this_building != NULL) { - - CacheObject->ID = this_building->ID; - CacheObject->RTTI = this_building->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - return this_building; - - } - else { - _objectIndex = Script_UnitIndexFromID(input_object_id); - UnitClass* this_unit = Units.Ptr(_objectIndex); - if (this_unit != NULL) { - - CacheObject->ID = this_unit->ID; - CacheObject->RTTI = this_unit->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - return this_unit; - - } - else { - _objectIndex = Script_AircraftIndexFromID(input_object_id); - AircraftClass* this_aircraft = Aircraft.Ptr(_objectIndex); - if (this_aircraft != NULL) { - - CacheObject->ID = this_aircraft->ID; - CacheObject->RTTI = this_aircraft->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - return this_aircraft; - - } - else { - _objectIndex = Script_VesselIndexFromID(input_object_id); - VesselClass* this_vessel = Vessels.Ptr(_objectIndex); - if (this_vessel != NULL) { - - CacheObject->ID = this_vessel->ID; - CacheObject->RTTI = this_vessel->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - return this_vessel; - - } - else { - _objectIndex = Script_InfantryIndexFromID(input_object_id); - InfantryClass* this_infantry = Infantry.Ptr(_objectIndex); - if (this_infantry != NULL) { - - CacheObject->ID = this_infantry->ID; - CacheObject->RTTI = this_infantry->RTTI; - CacheObject->classIndex = _objectIndex; - Scen.mapScript->ObjectCache.push_back(CacheObject); - - return this_infantry; - - } - } - } - } - } - - // Looks like we didn't find anything. They probably shouldn't have gotten this far in the first place so it was worth a try. - delete CacheObject; - - return NULL; - } - - /*********************************************************************************************** - * Script_SetObjectTrigger -- Figures out what kind of object is given and sets up trigger * - * * - * INPUT: input_object (ObjectClass*) - Input object * - * input_trigger (TriggerClass*) - Input Trigger * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger) { - - if (input_object_id < 0) { - return false; - } - - if (input_trigger == NULL) { - return false; - } - - ObjectClass* this_object = Script_GetCacheObject(input_object_id); - - if (this_object != NULL) { - - switch (this_object->RTTI) { - case RTTI_BUILDING:{ - input_trigger->Class->Event1.Data.Structure = dynamic_cast(this_object)->Class->Type; - dynamic_cast(this_object)->Trigger = input_trigger; - }break; - case RTTI_UNIT:{ - input_trigger->Class->Event1.Data.Unit = dynamic_cast(this_object)->Class->Type; - dynamic_cast(this_object)->Trigger = input_trigger; - }break; - case RTTI_AIRCRAFT:{ - input_trigger->Class->Event1.Data.Aircraft = dynamic_cast(this_object)->Class->Type; - dynamic_cast(this_object)->Trigger = input_trigger; - }break; - case RTTI_INFANTRY:{ - input_trigger->Class->Event1.Data.Infantry = dynamic_cast(this_object)->Class->Type; - dynamic_cast(this_object)->Trigger = input_trigger; - }break; - } - - } +/*********************************************************************************************** + * MapScript::GetCacheObject -- Returns ObjectClass from cache with given ID * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: this_object (ObjectClass*) - The object with the ID or NULL * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +ObjectClass* Script_GetCacheObject(int input_object_id) { + if (input_object_id < 0) { return false; } - /*********************************************************************************************** - * Script_BuildingIndexFromID -- Returns BuildingClass or NULL depending on the input * - * * - * INPUT: input_object_id (int) - Input object ID * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - int Script_BuildingIndexFromID(int input_object_id) { + // Look through cache for object with ID, and return it + for (std::vector::iterator it = Scen.mapScript->ObjectCache.begin(); it != Scen.mapScript->ObjectCache.end(); ++it) {//Error 2-4 - if (input_object_id < 0) { - return false; - } + MapScriptObject* thisCacheObject = (MapScriptObject*)(*it); - for (int _index = 0; _index < Buildings.Count(); _index++) { - BuildingClass* _object = Buildings.Ptr(_index); + // Here it is + if (thisCacheObject->UID == input_object_id) { - if (_object->ID == input_object_id) { - return _index; + // Determine type of object and return it from cache. If it doesn't exist any longer, remove it from cache + switch (thisCacheObject->RTTI) { + case RTTI_BUILDING: { + BuildingClass* this_building = Buildings.Ptr(thisCacheObject->classIndex); + if (this_building != NULL) { + return this_building; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_UNIT: { + UnitClass* this_unit = Units.Ptr(thisCacheObject->classIndex); + if (this_unit != NULL) { + return this_unit; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_AIRCRAFT: { + AircraftClass* this_aircraft = Aircraft.Ptr(thisCacheObject->classIndex); + if (this_aircraft != NULL) { + return this_aircraft; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_INFANTRY: { + InfantryClass* this_infantry = Infantry.Ptr(thisCacheObject->classIndex); + if (this_infantry != NULL) { + return this_infantry; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_VESSEL: { + VesselClass* this_vessel = Vessels.Ptr(thisCacheObject->classIndex); + if (this_vessel != NULL) { + return this_vessel; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; } - } - - return -1; - } - - /*********************************************************************************************** - * Script_UnitIndexFromID -- Returns UnitClass* or NULL depending on the input * - * * - * INPUT: input_unit_id (int) - Input Unit ID * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - int Script_UnitIndexFromID(int input_object_id) { - - if (input_object_id < 0) { - return false; - } - - for (int _index = 0; _index < Units.Count(); _index++) { - UnitClass* _object = Units.Ptr(_index); - - if (_object->ID == input_object_id) { - return _index; - } - } - - return -1; - } - - /*********************************************************************************************** - * Script_AircraftIndexFromID -- Returns AircraftClass* or NULL depending on the input * - * * - * INPUT: input_object_id (int) - Input object ID * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - int Script_AircraftIndexFromID(int input_object_id) { - - if (input_object_id < 0) { - return false; - } - - for (int _index = 0; _index < Aircraft.Count(); _index++) { - AircraftClass* _object = Aircraft.Ptr(_index); - - if (_object->ID == input_object_id) { - return _index; - } - } - - return -1; - } - - /*********************************************************************************************** - * Script_InfantryIndexFromID -- Returns InfantryClass* or NULL depending on the input * - * * - * INPUT: input_unit_id (int) - Input Infantry ID * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - int Script_InfantryIndexFromID(int input_object_id) { - - if (input_object_id < 0) { - return false; - } - - for (int _index = 0; _index < Infantry.Count(); _index++) { - InfantryClass* _object = Infantry.Ptr(_index); - - if (_object->ID == input_object_id) { - return _index; - } - } - - return -1; - } - - /*********************************************************************************************** - * Script_VesselIndexFromID -- Returns VesselClass* or NULL depending on the input * - * * - * INPUT: input_unit_id (int) - Input vessel ID * - * * - * OUTPUT: bool; Was the object found? Does the trigger exist? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - int Script_VesselIndexFromID(int input_object_id) { - - if (input_object_id < 0) { - return false; - } - - for (int _index = 0; _index < Vessels.Count(); _index++) { - VesselClass* _object = Vessels.Ptr(_index); - - if (_object->ID == input_object_id) { - return _index; - } - } - - return -1; - } - - /*********************************************************************************************** - * Script_CacheDefaultAttribute - Caches an attribute value upon first usage for restoration * - * * - * SCRIPT INPUT: input_rtti (RTTIType) - The RTTI type for the attrtibute * - * input_id (int) - The ID of the typeclass * - * input_attribute (int) - The attribute to cache * - * input_value (int) - The value if the cache item doesn't exist * - * * - * SCRIPT OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - void Script_CacheDefaultObjectTypeAttribute(RTTIType input_rtti, int input_id, MapScriptObjectAttributeType input_attribute, int input_value) { - - bool cache_item_found = false; - for (std::vector::iterator it = Scen.mapScript->DefaultObjectTypeAttributeCache.begin(); it != Scen.mapScript->DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 - - MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); - - if (input_rtti == cachedAttribute->RTTI && input_id == cachedAttribute->ID && input_attribute == cachedAttribute->attribute) { - cache_item_found = true; - break; - } - - } - - // Cached attribute does not exist; let's cache! - if (!cache_item_found) { - - MapScriptDefaultObjectAttribute* cachedAttribute = new MapScriptDefaultObjectAttribute; - cachedAttribute->RTTI = input_rtti; - cachedAttribute->ID = input_id; - cachedAttribute->attribute = input_attribute; - cachedAttribute->value = input_value; - Scen.mapScript->DefaultObjectTypeAttributeCache.push_back(cachedAttribute); } } - /*********************************************************************************************** - * MapScript::CallFunction - Calls a given function within the current script * - * * - * INPUT: functionName - The name of the function to call * - * * - * OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - void MapScript::CallFunction(const char* functionName) { + // Not found in cache; let's find the object by ID the hard way... If scripting is done well, this doesn't get hit + int _objectIndex = -1; + MapScriptObject* CacheObject = Scen.mapScript->CreateCacheObject(); + CacheObject->ID = -1; - lua_getglobal(L, functionName); - lua_pcall(L, 0, 0, 0); - - } + _objectIndex = Script_BuildingIndexFromID(input_object_id); + BuildingClass* this_building = Buildings.Ptr(_objectIndex); + if (this_building != NULL) { - /*********************************************************************************************** - * MapScript::setLuaPath - Sets Lua's path / extensions to search for scripts * - * * - * This helps Lua know where/how to look for files when using require(), otherwise we could * - * hard code path/extension as we did previously * - * * - * INPUT: pathvalue - See lua documentation RE: LUA_PATH to know how to deal with this * - * * - * OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - void MapScript::SetLuaPath( const char* input_path) + CacheObject->ID = this_building->ID; + CacheObject->RTTI = this_building->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_building; + + } + else { + _objectIndex = Script_UnitIndexFromID(input_object_id); + UnitClass* this_unit = Units.Ptr(_objectIndex); + if (this_unit != NULL) { + + CacheObject->ID = this_unit->ID; + CacheObject->RTTI = this_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_unit; + + } + else { + _objectIndex = Script_AircraftIndexFromID(input_object_id); + AircraftClass* this_aircraft = Aircraft.Ptr(_objectIndex); + if (this_aircraft != NULL) { + + CacheObject->ID = this_aircraft->ID; + CacheObject->RTTI = this_aircraft->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_aircraft; + + } + else { + _objectIndex = Script_VesselIndexFromID(input_object_id); + VesselClass* this_vessel = Vessels.Ptr(_objectIndex); + if (this_vessel != NULL) { + + CacheObject->ID = this_vessel->ID; + CacheObject->RTTI = this_vessel->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_vessel; + + } + else { + _objectIndex = Script_InfantryIndexFromID(input_object_id); + InfantryClass* this_infantry = Infantry.Ptr(_objectIndex); + if (this_infantry != NULL) { + + CacheObject->ID = this_infantry->ID; + CacheObject->RTTI = this_infantry->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_infantry; + + } + } + } + } + } + + // Looks like we didn't find anything. They probably shouldn't have gotten this far in the first place so it was worth a try. + delete CacheObject; + + return NULL; +} + +/*********************************************************************************************** + * Script_SetObjectTrigger -- Figures out what kind of object is given and sets up trigger * + * * + * INPUT: input_object (ObjectClass*) - Input object * + * input_trigger (TriggerClass*) - Input Trigger * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger) { + + if (input_object_id < 0) { + return false; + } + + if (input_trigger == NULL) { + return false; + } + + ObjectClass* this_object = Script_GetCacheObject(input_object_id); + + if (this_object != NULL) { + + switch (this_object->RTTI) { + case RTTI_BUILDING: { + input_trigger->Class->Event1.Data.Structure = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_UNIT: { + input_trigger->Class->Event1.Data.Unit = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_AIRCRAFT: { + input_trigger->Class->Event1.Data.Aircraft = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_INFANTRY: { + input_trigger->Class->Event1.Data.Infantry = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + } + + } + + return false; +} + +/*********************************************************************************************** + * Script_BuildingIndexFromID -- Returns BuildingClass or NULL depending on the input * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +int Script_BuildingIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Buildings.Count(); _index++) { + BuildingClass* _object = Buildings.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; +} + +/*********************************************************************************************** + * Script_UnitIndexFromID -- Returns UnitClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input Unit ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +int Script_UnitIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Units.Count(); _index++) { + UnitClass* _object = Units.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; +} + +/*********************************************************************************************** + * Script_AircraftIndexFromID -- Returns AircraftClass* or NULL depending on the input * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +int Script_AircraftIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Aircraft.Count(); _index++) { + AircraftClass* _object = Aircraft.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; +} + +/*********************************************************************************************** + * Script_InfantryIndexFromID -- Returns InfantryClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input Infantry ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +int Script_InfantryIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Infantry.Count(); _index++) { + InfantryClass* _object = Infantry.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; +} + +/*********************************************************************************************** + * Script_VesselIndexFromID -- Returns VesselClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input vessel ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +int Script_VesselIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Vessels.Count(); _index++) { + VesselClass* _object = Vessels.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; +} + +/*********************************************************************************************** + * Script_CacheDefaultAttribute - Caches an attribute value upon first usage for restoration * + * * + * SCRIPT INPUT: input_rtti (RTTIType) - The RTTI type for the attrtibute * + * input_id (int) - The ID of the typeclass * + * input_attribute (int) - The attribute to cache * + * input_value (int) - The value if the cache item doesn't exist * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +void Script_CacheDefaultObjectTypeAttribute(RTTIType input_rtti, int input_id, MapScriptObjectAttributeType input_attribute, int input_value) { + + bool cache_item_found = false; + for (std::vector::iterator it = Scen.mapScript->DefaultObjectTypeAttributeCache.begin(); it != Scen.mapScript->DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 + + MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); + + if (input_rtti == cachedAttribute->RTTI && input_id == cachedAttribute->ID && input_attribute == cachedAttribute->attribute) { + cache_item_found = true; + break; + } + + } + + // Cached attribute does not exist; let's cache! + if (!cache_item_found) { + + MapScriptDefaultObjectAttribute* cachedAttribute = new MapScriptDefaultObjectAttribute; + cachedAttribute->RTTI = input_rtti; + cachedAttribute->ID = input_id; + cachedAttribute->attribute = input_attribute; + cachedAttribute->value = input_value; + Scen.mapScript->DefaultObjectTypeAttributeCache.push_back(cachedAttribute); + + } + +} + +/*********************************************************************************************** + * MapScript::CallFunction - Calls a given function within the current script * + * * + * INPUT: functionName - The name of the function to call * + * * + * OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +void MapScript::CallFunction(const char* functionName) { + + lua_getglobal(L, functionName); + lua_pcall(L, 0, 0, 0); + +} + +/*********************************************************************************************** + * MapScript::setLuaPath - Sets Lua's path / extensions to search for scripts * + * * + * This helps Lua know where/how to look for files when using require(), otherwise we could * + * hard code path/extension as we did previously * + * * + * INPUT: pathvalue - See lua documentation RE: LUA_PATH to know how to deal with this * + * * + * OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +void MapScript::SetLuaPath(const char* input_path) +{ + + // Needs space to keep the initial paths + char new_path[1024]; + + // Set new GLOBALS.package.path + lua_getglobal(L, "package"); + lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1) + const char* cur_path = lua_tostring(L, -1); // grab path string from top of stack + + sprintf(new_path, "%s;%s", cur_path, input_path); + + lua_pop(L, 1); + lua_pushstring(L, new_path); + lua_setfield(L, -2, "path"); + lua_pop(L, 1); +} + +/*********************************************************************************************** + * MapScript::CreateCacheObject -- Initializes a cached object for a new cache entry * + * * + * INPUT: mapName - The script path * + * * + * OUTPUT: bool; did the map script load and initialize? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +MapScriptObject* MapScript::CreateCacheObject() { + + MapScriptObject* CacheObject = new MapScriptObject; + + cUID++; + CacheObject->UID = cUID; + + return CacheObject; + +} + +/*********************************************************************************************** + * MapScript::Deinit - Deinitialize/free any data used by MapScript (used within destructor) * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ +void MapScript::Deinit() { + + // Remove any cached objects + for (int i = 0; i < ObjectCache.size(); ++i) { - - // Needs space to keep the initial paths - char new_path[1024]; - - // Set new GLOBALS.package.path - lua_getglobal(L, "package"); - lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1) - const char* cur_path = lua_tostring(L, -1); // grab path string from top of stack - - sprintf(new_path, "%s;%s", cur_path, input_path); - - lua_pop(L, 1); - lua_pushstring(L, new_path); - lua_setfield(L, -2, "path"); - lua_pop(L, 1); + delete ObjectCache[i]; } - /*********************************************************************************************** - * MapScript::CreateCacheObject -- Initializes a cached object for a new cache entry * - * * - * INPUT: mapName - The script path * - * * - * OUTPUT: bool; did the map script load and initialize? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - MapScriptObject* MapScript::CreateCacheObject() { + ObjectCache.clear(); - MapScriptObject* CacheObject = new MapScriptObject; + // We will now restore any default Object attributes that were changed in the script + for (std::vector::iterator it = DefaultObjectTypeAttributeCache.begin(); it != DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 - cUID++; - CacheObject->UID = cUID; + MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); - return CacheObject; + switch (cachedAttribute->RTTI) { - } + case RTTI_BUILDINGTYPE: { + lua_getglobal(L, "SetBuildingTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_UNITTYPE: { + lua_getglobal(L, "SetUnitTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_INFANTRYTYPE: { + lua_getglobal(L, "SetInfantryTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_AIRCRAFTTYPE: { + lua_getglobal(L, "SetAircraftTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; + case RTTI_VESSELTYPE: { + lua_getglobal(L, "SetVesselTypeAttribute"); + lua_pushnumber(L, cachedAttribute->ID); + lua_pushnumber(L, cachedAttribute->attribute); + lua_pushnumber(L, cachedAttribute->value); + lua_pcall(L, 3, 1, 0); + }break; - /*********************************************************************************************** - * MapScript::Deinit - Deinitialize/free any data used by MapScript (used within destructor) * - * * - * SCRIPT INPUT: none * - * * - * SCRIPT OUTPUT: void * - * * - * WARNINGS: ? * - * * - *=============================================================================================*/ - void MapScript::Deinit() { - - // Remove any cached objects - for (int i = 0; i < ObjectCache.size(); ++i) - { - delete ObjectCache[i]; - } - - ObjectCache.clear(); - - // We will now restore any default Object attributes that were changed in the script - for (std::vector::iterator it = DefaultObjectTypeAttributeCache.begin(); it != DefaultObjectTypeAttributeCache.end(); ++it) {//Error 2-4 - - MapScriptDefaultObjectAttribute* cachedAttribute = (MapScriptDefaultObjectAttribute*)(*it); - - switch (cachedAttribute->RTTI) { - - case RTTI_BUILDINGTYPE: { - lua_getglobal(L, "SetBuildingTypeAttribute"); - lua_pushnumber(L, cachedAttribute->ID); - lua_pushnumber(L, cachedAttribute->attribute); - lua_pushnumber(L, cachedAttribute->value); - lua_pcall(L, 3, 1, 0); - }break; - case RTTI_UNITTYPE: { - lua_getglobal(L, "SetUnitTypeAttribute"); - lua_pushnumber(L, cachedAttribute->ID); - lua_pushnumber(L, cachedAttribute->attribute); - lua_pushnumber(L, cachedAttribute->value); - lua_pcall(L, 3, 1, 0); - }break; - case RTTI_INFANTRYTYPE: { - lua_getglobal(L, "SetInfantryTypeAttribute"); - lua_pushnumber(L, cachedAttribute->ID); - lua_pushnumber(L, cachedAttribute->attribute); - lua_pushnumber(L, cachedAttribute->value); - lua_pcall(L, 3, 1, 0); - }break; - case RTTI_AIRCRAFTTYPE: { - lua_getglobal(L, "SetAircraftTypeAttribute"); - lua_pushnumber(L, cachedAttribute->ID); - lua_pushnumber(L, cachedAttribute->attribute); - lua_pushnumber(L, cachedAttribute->value); - lua_pcall(L, 3, 1, 0); - }break; - case RTTI_VESSELTYPE: { - lua_getglobal(L, "SetVesselTypeAttribute"); - lua_pushnumber(L, cachedAttribute->ID); - lua_pushnumber(L, cachedAttribute->attribute); - lua_pushnumber(L, cachedAttribute->value); - lua_pcall(L, 3, 1, 0); - }break; - - } - - } - - // Remove any cached Object Attributes - for (int i = 0; i < DefaultObjectTypeAttributeCache.size(); ++i) - { - delete DefaultObjectTypeAttributeCache[i]; - } - - DefaultObjectTypeAttributeCache.clear(); - - - for (int index = 0; index < Warheads.Count(); index++) { - Warheads.Ptr(index)->Read_INI(RuleINI); - } - - for (int proj = 0; proj < BulletTypes.Count(); proj++) { - BulletTypes.Ptr(proj)->Read_INI(RuleINI); - } - - for (int windex = 0; windex < Weapons.Count(); windex++) { - Weapons.Ptr(windex)->Read_INI(RuleINI); - } - - for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { - UnitTypes.Ptr(uindex)->Read_INI(RuleINI); - } - - for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { - InfantryTypes.Ptr(iindex)->Read_INI(RuleINI); - } - - for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { - VesselTypes.Ptr(vindex)->Read_INI(RuleINI); - } - - for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { - AircraftTypes.Ptr(aindex)->Read_INI(RuleINI); - } - - for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { - BuildingTypes.Ptr(bindex)->Read_INI(RuleINI); } } + // Remove any cached Object Attributes + for (int i = 0; i < DefaultObjectTypeAttributeCache.size(); ++i) + { + delete DefaultObjectTypeAttributeCache[i]; + } - /*********************************************************************************************** - * MapScript::Init -- Loads and initializes script for a given map * - * * - * This routine is called at the beginning of a mission * - * * - * INPUT: mapName - The script path * - * * - * OUTPUT: bool; did the map script load and initialize? * - * * - * WARNINGS: none * - * * - *=============================================================================================*/ - bool MapScript::Init(const char* mapName) { - - this->cUID = 0; - - char scriptName[256]; - - // Build map path - sprintf(scriptName, "scripts/%s.script", mapName); - L = luaL_newstate(); - - luaL_openlibs(L); - - // Load file - if (luaL_loadfile(L, scriptName)){ - Console_Printf(lua_tostring(L, -1)); - L = NULL; - return false; - } - - // TODO: I've tried several combinations of paths (including "scripts/?"), - // but I can only get "require 'basefilename'" to work within a - // script, not "require 'basefilename.script'". This may be completely - // normal within lua, but it seems odd. - - // This allows require() to work - SetLuaPath(";scripts/?.script;"); - - if (lua_pcall(L, 0, 0, 0)) { - Console_Printf(lua_tostring(L, -1)); - L = NULL; - return false; - } - - // Reserve object cache memory - ObjectCache.reserve(2000); - - // Reserve type attribute cache memory (to restore rules after script) - DefaultObjectTypeAttributeCache.reserve(1000); - - /********************************************************************************************** - * Red Alert Vanilla Actions * - *=============================================================================================*/ - - lua_register(L, "Win", Script_Win); // player wins! - lua_register(L, "Lose", Script_Lose); // player loses. - lua_register(L, "BeginProduction", Script_BeginProduction); // computer begins factory production. - lua_register(L, "CreateTeam", Script_CreateTeam); // computer creates a certain type of team - lua_register(L, "DestroyTeam", Script_DestroyTeam); // destroy team (comment was redacted. was something offensive originally written?) - lua_register(L, "AllHunt", Script_AllHunt); // all enemy units go into hunt mode (teams destroyed). - lua_register(L, "Reinforcements", Script_Reinforcements); // player gets reinforcements (house that gets them is determined by the Reinforcement instance) - lua_register(L, "DropZoneFlare", Script_DropZoneFlare); // Deploy drop zone smoke. - lua_register(L, "FireSale", Script_FireSale); // Sell all buildings and go on rampage. - lua_register(L, "PlayMovie", Script_PlayMovie); // Play movie (temporarily suspend game). - lua_register(L, "TriggerText", Script_TriggerText); // Triggers a text message display. - lua_register(L, "DestroyTrigger", Script_DestroyTrigger); // Destroy specified trigger. - lua_register(L, "AutoCreate", Script_AutoCreate); // Computer to autocreate teams. - // Win if captured, lose if destroyed // function does not exist - lua_register(L, "AllowWin", Script_AllowWin); // Allows winning if triggered. - lua_register(L, "RevealAll", Script_RevealAll); // Reveal the entire map. - lua_register(L, "RevealCell", Script_RevealCell); // Reveal map around cell #. - lua_register(L, "RevealZone", Script_RevealZone); // Reveal all of specified zone. - lua_register(L, "PlaySound", Script_PlaySound); // Play sound effect. - lua_register(L, "PlayMusic", Script_PlayMusic); // Play musical score. - lua_register(L, "PlaySpeech", Script_PlaySpeech); // Play EVA speech. - // TODO: Force trigger to activate. - lua_register(L, "StartMissionTimer", Script_StartMissionTimer); // Start mission timer. - lua_register(L, "StopMissionTimer", Script_StopMissionTimer); // Stop mission timer. - lua_register(L, "IncreaseMissionTimer", Script_IncreaseMissionTimer); // Increase mission timer time. - lua_register(L, "DecreaseMissionTimer", Script_DecreaseMissionTimer); // Decrease mission timer time. - lua_register(L, "SetMissionTimer", Script_SetMissionTimer); // Set and start the mission timer. - lua_register(L, "SetGlobalValue", Script_SetGlobalValue); // Set global variable. - lua_register(L, "ClearGlobalValue", Script_ClearGlobalValue); // Clear global variable. - lua_register(L, "AutoBaseBuilding", Script_AutoBaseBuilding); // Automated base building. - lua_register(L, "CreepShadow", Script_CreepShadow); // Shadow grows back one 'step'. - lua_register(L, "DestroyTriggerBuilding", Script_DestroyTriggerBuilding); // Destroys the building this trigger is attached to. (in the case of scripging, we can supply the trigger) - lua_register(L, "GiveOneTimeSpecialWeapon", Script_GiveOneTimeSpecialWeapon); // Add a one-time special weapon ability to house. - lua_register(L, "GiveSpecialWeapon", Script_GiveSpecialWeapon); // Add a repeating special weapon ability to house. - lua_register(L, "DesignatePreferredTarget", Script_DesignatePreferredTarget); // Designates preferred target for house. - lua_register(L, "LaunchFakeNukes", Script_LaunchFakeNukes); // Launch fake nuclear missiles from all silos - - /********************************************************************************************** - * Red Alert Vanilla Events * - *=============================================================================================*/ - - lua_register(L, "CellCallback", Script_CellCallback); // player enters this square (or group of squares) - lua_register(L, "SpiedByCallback", Script_SpiedByCallback); // Spied by. - // Thieved by (raided or stolen vehicle). // This doesn't appear to be implemented in the engine (?) - lua_register(L, "ObjectDiscoveryCallback", Script_ObjectDiscoveryCallback); // player discovers this object - lua_register(L, "HouseDiscoveredCallback", Script_HouseDiscoveredCallback); // House has been discovered. - lua_register(L, "ObjectAttackedCallback", Script_ObjectAttackedCallback); // player attacks this object - lua_register(L, "ObjectDestroyedCallback", Script_ObjectDestroyedCallback); // player destroys this object - lua_register(L, "AnyEventCallback", Script_AnyEventCallback); // Any object event will cause the trigger. - lua_register(L, "AllUnitsDestroyedCallback", Script_AllUnitsDestroyedCallback); // all house's units destroyed - lua_register(L, "AllBuildingsDestroyedCallback", Script_AllBuildingsDestroyedCallback); // all house's buildings destroyed - lua_register(L, "AllDestroyedCallback", Script_AllDestroyedCallback); // all house's units & buildings destroyed - lua_register(L, "CreditsReachedCallback", Script_CreditsReachedCallback); // house reaches this many credits - lua_register(L, "TimeReachedCallback", Script_TimeReachedCallback); // Scenario elapsed time from start. - lua_register(L, "MissionTimerCallback", Script_MissionTimerCallback); // Pre expired mission timer. - lua_register(L, "BuildingsDestroyedCallback", Script_BuildingsDestroyedCallback); // Number of buildings destroyed. - lua_register(L, "UnitsDestroyedCallback", Script_UnitsDestroyedCallback); // Number of units destroyed. - lua_register(L, "NoFactoriesCallback", Script_NoFactoriesCallback); // No factories left. - lua_register(L, "CivilianEscapeCallback", Script_CivilianEscapeCallback); // Civilian has been evacuated. - lua_register(L, "BuildingBuiltCallback", Script_BuildingBuiltCallback); // Specified building has been built. - lua_register(L, "UnitBuiltCallback", Script_UnitBuiltCallback); // Specified unit has been built. - lua_register(L, "InfantryBuiltCallback", Script_InfantryBuiltCallback); // Specified infantry has been built. - lua_register(L, "AircraftBuiltCallback", Script_AircraftBuiltCallback); // Specified aircraft has been built. - lua_register(L, "TeamLeavesMapCallback", Script_TeamLeavesMapCallback); // Specified team member leaves map. - lua_register(L, "ZoneEntryCallback", Script_ZoneEntryCallback); // Enters same zone as waypoint 'x'. - lua_register(L, "HorizontalCrossCallback", Script_HorizontalCrossCallback); // Crosses horizontal trigger line. - lua_register(L, "VerticalCrossCallback", Script_VerticalCrossCallback); // Crosses vertical trigger line. - lua_register(L, "GlobalSetCallback", Script_GlobalSetCallback); // If specified global has been set. (somewhat useless with Scripting) - lua_register(L, "GlobalClearedCallback", Script_GlobalClearedCallback); // If specified global has been cleared. (somewhat useless with Scripting) - // If all fake structures are gone. // This doesn't appear to be implemented in the engine (?) - lua_register(L, "LowPowerCallback", Script_LowPowerCallback); // When power drops below 100%. - lua_register(L, "AllBridgesDestroyedCallback", Script_AllBridgesDestroyedCallback); // All bridges destroyed. - lua_register(L, "BuildingExistsCallback", Script_BuildingExistsCallback); // Check for building existing. - - /********************************************************************************************** - * Trigger Utility Functions * - *=============================================================================================*/ - - lua_register(L, "SetTriggerCallback", Script_SetTriggerCallback); // Initiates a given [callback] on an existing [trigger] // TODO: Make this work via name OR ID - lua_register(L, "TriggerAddCell", Script_TriggerAddCell); // Initiates a given [callback] on an existing [trigger] - lua_register(L, "MergeTriggers", Script_MergeTriggers); // Combines the events of two triggers (for multi-event triggers :) - lua_register(L, "GetTriggerByName", Script_GetTriggerByName); // Gets the ID of the trigger with a given name - lua_register(L, "TriggerSetPersistence", Script_TriggerSetPersistence); // Does a trigger go on and on or just run once? - - /********************************************************************************************** - * Object Functions * - *=============================================================================================*/ - lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] - - lua_register(L, "SetObjectAttribute", Script_SetObjectAttribute); // Sets an object's attributes (strength, etc.) - lua_register(L, "SetBuildingTypeAttribute", Script_SetBuildingTypeAttribute); // Sets the attribute of a Building type - lua_register(L, "SetVehicleTypeAttribute", Script_SetVehicleTypeAttribute); // Sets the attribute of a Vehicle type - lua_register(L, "SetInfantryTypeAttribute", Script_SetInfantryTypeAttribute); // Sets the attribute of an Infantry type - lua_register(L, "SetVesselTypeAttribute", Script_SetVesselTypeAttribute); // Sets the attribute of a Vessel type - lua_register(L, "SetAircraftTypeAttribute", Script_SetAircraftTypeAttribute); // Sets the attribute of an Aircraft type - - lua_register(L, "GetObjectAttribute", Script_GetObjectAttribute); // Sets an object's attributes (strength, etc.) - lua_register(L, "GetBuildingTypeAttribute", Script_GetBuildingTypeAttribute); // Sets the attribute of a Building type - lua_register(L, "GetVehicleTypeAttribute", Script_GetVehicleTypeAttribute); // Sets the attribute of a Vehicle type - lua_register(L, "GetInfantryTypeAttribute", Script_GetInfantryTypeAttribute); // Sets the attribute of an Infantry type - lua_register(L, "GetVesselTypeAttribute", Script_GetVesselTypeAttribute); // Sets the attribute of a Vessel type - lua_register(L, "GetAircraftTypeAttribute", Script_GetAircraftTypeAttribute); // Sets the attribute of an Aircraft type - - lua_register(L, "SetHouseAttribute", Script_SetHouseAttribute); // Sets a house's attributes - lua_register(L, "GetHouseAttribute", Script_GetHouseAttribute); // Gets a house's attributes - - lua_register(L, "BuildUnit", Script_BuildUnit); // Builds a unit in a given factory / barracks / naval yard / etc - lua_register(L, "CreateVehicle", Script_CreateVehicle); // Creates a unit out of thin air - lua_register(L, "CreateInfantry", Script_CreateInfantry); // Creates a unit out of thin air - lua_register(L, "CreateAircraft", Script_CreateAircraft); // Creates a unit out of thin air - lua_register(L, "CreateVessel", Script_CreateVessel); // Creates a unit out of thin air - - /********************************************************************************************** - * Object Orders Functions * - *=============================================================================================*/ - - lua_register(L, "OrderMove", Script_OrderMove); // Assigns a movement order to the object - lua_register(L, "OrderAttack", Script_OrderAttack); // Assigns an attack order to the object - lua_register(L, "OrderHunt", Script_OrderHunt); // Assigns a hunt order to the object - lua_register(L, "OrderGuard", Script_OrderGuard); // Assigns a guard order to the object - lua_register(L, "OrderEnter", Script_OrderEnter); // Assigns a enter order to the object - lua_register(L, "OrderCapture", Script_OrderCapture); // Assigns a capture order to the object - - /********************************************************************************************** - * Mission Utility Functions * - *=============================================================================================*/ - - lua_register(L, "SetBriefingText", Script_SetBriefingText); // Sets the [text] on the mission briefing screen - - /********************************************************************************************** - * Status / Location Utility Functions * - *=============================================================================================*/ - - lua_register(L, "GiveCredits", Script_GiveCredits); // Give [credits] to [player] - lua_register(L, "GetCredits", Script_GetCredits); // Get [player]'s [credits] - - lua_register(L, "CountBuildings", Script_CountBuildings); // Number of buildings of [type] for given [player] - lua_register(L, "CountAircraft", Script_CountAircraft); // Number of units of [type] for given [player] - lua_register(L, "CountUnits", Script_CountUnits); // Number of units of [type] for given [player] - lua_register(L, "CountInfantry", Script_CountInfantry); // Number of infantry of [type] for given [player] - lua_register(L, "CountVessels", Script_CountVessels); // Number of vessels of [type] for given [player] - - lua_register(L, "GetWaypointX", Script_GetWaypointX); // Get Cell/X of waypoint - lua_register(L, "GetWaypointY", Script_GetWaypointY); // Get Cell/Y of waypoint - - /********************************************************************************************** - * Special / Debug Functions * - *=============================================================================================*/ - - lua_register(L, "PrintConsole", Script_Console_Print); // Print text to console - - /********************************************************************************************** - * Script Globals * - *=============================================================================================*/ - - lua_pushnumber(L, PlayerPtr->ID); // Local player (house) index - lua_setglobal(L, "_localPlayer"); + DefaultObjectTypeAttributeCache.clear(); - return true; - } \ No newline at end of file + for (int index = 0; index < Warheads.Count(); index++) { + Warheads.Ptr(index)->Read_INI(RuleINI); + } + + for (int proj = 0; proj < BulletTypes.Count(); proj++) { + BulletTypes.Ptr(proj)->Read_INI(RuleINI); + } + + for (int windex = 0; windex < Weapons.Count(); windex++) { + Weapons.Ptr(windex)->Read_INI(RuleINI); + } + + for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { + UnitTypes.Ptr(uindex)->Read_INI(RuleINI); + } + + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypes.Ptr(iindex)->Read_INI(RuleINI); + } + + for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { + VesselTypes.Ptr(vindex)->Read_INI(RuleINI); + } + + for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { + AircraftTypes.Ptr(aindex)->Read_INI(RuleINI); + } + + for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { + BuildingTypes.Ptr(bindex)->Read_INI(RuleINI); + } + +} + + +/*********************************************************************************************** + * MapScript::Init -- Loads and initializes script for a given map * + * * + * This routine is called at the beginning of a mission * + * * + * INPUT: mapName - The script path * + * * + * OUTPUT: bool; did the map script load and initialize? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ +bool MapScript::Init(const char* mapName) { + + this->cUID = 0; + + char scriptName[256]; + + // Build map path + sprintf(scriptName, "scripts/%s.script", mapName); + L = luaL_newstate(); + + luaL_openlibs(L); + + // Load file + if (luaL_loadfile(L, scriptName)) { + Console_Printf(lua_tostring(L, -1)); + L = NULL; + return false; + } + + // TODO: I've tried several combinations of paths (including "scripts/?"), + // but I can only get "require 'basefilename'" to work within a + // script, not "require 'basefilename.script'". This may be completely + // normal within lua, but it seems odd. + + // This allows require() to work + SetLuaPath(";scripts/?.script;"); + + if (lua_pcall(L, 0, 0, 0)) { + Console_Printf(lua_tostring(L, -1)); + L = NULL; + return false; + } + + // Reserve object cache memory + ObjectCache.reserve(2000); + + // Reserve type attribute cache memory (to restore rules after script) + DefaultObjectTypeAttributeCache.reserve(1000); + + /********************************************************************************************** + * Red Alert Vanilla Actions * + *=============================================================================================*/ + + lua_register(L, "Win", Script_Win); // player wins! + lua_register(L, "Lose", Script_Lose); // player loses. + lua_register(L, "BeginProduction", Script_BeginProduction); // computer begins factory production. + lua_register(L, "CreateTeam", Script_CreateTeam); // computer creates a certain type of team + lua_register(L, "DestroyTeam", Script_DestroyTeam); // destroy team (comment was redacted. was something offensive originally written?) + lua_register(L, "AllHunt", Script_AllHunt); // all enemy units go into hunt mode (teams destroyed). + lua_register(L, "Reinforcements", Script_Reinforcements); // player gets reinforcements (house that gets them is determined by the Reinforcement instance) + lua_register(L, "DropZoneFlare", Script_DropZoneFlare); // Deploy drop zone smoke. + lua_register(L, "FireSale", Script_FireSale); // Sell all buildings and go on rampage. + lua_register(L, "PlayMovie", Script_PlayMovie); // Play movie (temporarily suspend game). + lua_register(L, "TriggerText", Script_TriggerText); // Triggers a text message display. + lua_register(L, "DestroyTrigger", Script_DestroyTrigger); // Destroy specified trigger. + lua_register(L, "AutoCreate", Script_AutoCreate); // Computer to autocreate teams. + // Win if captured, lose if destroyed // function does not exist + lua_register(L, "AllowWin", Script_AllowWin); // Allows winning if triggered. + lua_register(L, "RevealAll", Script_RevealAll); // Reveal the entire map. + lua_register(L, "RevealCell", Script_RevealCell); // Reveal map around cell #. + lua_register(L, "RevealZone", Script_RevealZone); // Reveal all of specified zone. + lua_register(L, "PlaySound", Script_PlaySound); // Play sound effect. + lua_register(L, "PlayMusic", Script_PlayMusic); // Play musical score. + lua_register(L, "PlaySpeech", Script_PlaySpeech); // Play EVA speech. + // TODO: Force trigger to activate. + lua_register(L, "StartMissionTimer", Script_StartMissionTimer); // Start mission timer. + lua_register(L, "StopMissionTimer", Script_StopMissionTimer); // Stop mission timer. + lua_register(L, "IncreaseMissionTimer", Script_IncreaseMissionTimer); // Increase mission timer time. + lua_register(L, "DecreaseMissionTimer", Script_DecreaseMissionTimer); // Decrease mission timer time. + lua_register(L, "SetMissionTimer", Script_SetMissionTimer); // Set and start the mission timer. + lua_register(L, "SetGlobalValue", Script_SetGlobalValue); // Set global variable. + lua_register(L, "ClearGlobalValue", Script_ClearGlobalValue); // Clear global variable. + lua_register(L, "AutoBaseBuilding", Script_AutoBaseBuilding); // Automated base building. + lua_register(L, "CreepShadow", Script_CreepShadow); // Shadow grows back one 'step'. + lua_register(L, "DestroyTriggerBuilding", Script_DestroyTriggerBuilding); // Destroys the building this trigger is attached to. (in the case of scripging, we can supply the trigger) + lua_register(L, "GiveOneTimeSpecialWeapon", Script_GiveOneTimeSpecialWeapon); // Add a one-time special weapon ability to house. + lua_register(L, "GiveSpecialWeapon", Script_GiveSpecialWeapon); // Add a repeating special weapon ability to house. + lua_register(L, "DesignatePreferredTarget", Script_DesignatePreferredTarget); // Designates preferred target for house. + lua_register(L, "LaunchFakeNukes", Script_LaunchFakeNukes); // Launch fake nuclear missiles from all silos + +/********************************************************************************************** +* Red Alert Vanilla Events * +*=============================================================================================*/ + + lua_register(L, "CellCallback", Script_CellCallback); // player enters this square (or group of squares) + lua_register(L, "SpiedByCallback", Script_SpiedByCallback); // Spied by. + // Thieved by (raided or stolen vehicle). // This doesn't appear to be implemented in the engine (?) + lua_register(L, "ObjectDiscoveryCallback", Script_ObjectDiscoveryCallback); // player discovers this object + lua_register(L, "HouseDiscoveredCallback", Script_HouseDiscoveredCallback); // House has been discovered. + lua_register(L, "ObjectAttackedCallback", Script_ObjectAttackedCallback); // player attacks this object + lua_register(L, "ObjectDestroyedCallback", Script_ObjectDestroyedCallback); // player destroys this object + lua_register(L, "AnyEventCallback", Script_AnyEventCallback); // Any object event will cause the trigger. + lua_register(L, "AllUnitsDestroyedCallback", Script_AllUnitsDestroyedCallback); // all house's units destroyed + lua_register(L, "AllBuildingsDestroyedCallback", Script_AllBuildingsDestroyedCallback); // all house's buildings destroyed + lua_register(L, "AllDestroyedCallback", Script_AllDestroyedCallback); // all house's units & buildings destroyed + lua_register(L, "CreditsReachedCallback", Script_CreditsReachedCallback); // house reaches this many credits + lua_register(L, "TimeReachedCallback", Script_TimeReachedCallback); // Scenario elapsed time from start. + lua_register(L, "MissionTimerCallback", Script_MissionTimerCallback); // Pre expired mission timer. + lua_register(L, "BuildingsDestroyedCallback", Script_BuildingsDestroyedCallback); // Number of buildings destroyed. + lua_register(L, "UnitsDestroyedCallback", Script_UnitsDestroyedCallback); // Number of units destroyed. + lua_register(L, "NoFactoriesCallback", Script_NoFactoriesCallback); // No factories left. + lua_register(L, "CivilianEscapeCallback", Script_CivilianEscapeCallback); // Civilian has been evacuated. + lua_register(L, "BuildingBuiltCallback", Script_BuildingBuiltCallback); // Specified building has been built. + lua_register(L, "UnitBuiltCallback", Script_UnitBuiltCallback); // Specified unit has been built. + lua_register(L, "InfantryBuiltCallback", Script_InfantryBuiltCallback); // Specified infantry has been built. + lua_register(L, "AircraftBuiltCallback", Script_AircraftBuiltCallback); // Specified aircraft has been built. + lua_register(L, "TeamLeavesMapCallback", Script_TeamLeavesMapCallback); // Specified team member leaves map. + lua_register(L, "ZoneEntryCallback", Script_ZoneEntryCallback); // Enters same zone as waypoint 'x'. + lua_register(L, "HorizontalCrossCallback", Script_HorizontalCrossCallback); // Crosses horizontal trigger line. + lua_register(L, "VerticalCrossCallback", Script_VerticalCrossCallback); // Crosses vertical trigger line. + lua_register(L, "GlobalSetCallback", Script_GlobalSetCallback); // If specified global has been set. (somewhat useless with Scripting) + lua_register(L, "GlobalClearedCallback", Script_GlobalClearedCallback); // If specified global has been cleared. (somewhat useless with Scripting) + // If all fake structures are gone. // This doesn't appear to be implemented in the engine (?) + lua_register(L, "LowPowerCallback", Script_LowPowerCallback); // When power drops below 100%. + lua_register(L, "AllBridgesDestroyedCallback", Script_AllBridgesDestroyedCallback); // All bridges destroyed. + lua_register(L, "BuildingExistsCallback", Script_BuildingExistsCallback); // Check for building existing. + +/********************************************************************************************** +* Trigger Utility Functions * +*=============================================================================================*/ + + lua_register(L, "SetTriggerCallback", Script_SetTriggerCallback); // Initiates a given [callback] on an existing [trigger] // TODO: Make this work via name OR ID + lua_register(L, "TriggerAddCell", Script_TriggerAddCell); // Initiates a given [callback] on an existing [trigger] + lua_register(L, "MergeTriggers", Script_MergeTriggers); // Combines the events of two triggers (for multi-event triggers :) + lua_register(L, "GetTriggerByName", Script_GetTriggerByName); // Gets the ID of the trigger with a given name + lua_register(L, "TriggerSetPersistence", Script_TriggerSetPersistence); // Does a trigger go on and on or just run once? + +/********************************************************************************************** +* Object Functions * +*=============================================================================================*/ + lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] + + lua_register(L, "SetObjectAttribute", Script_SetObjectAttribute); // Sets an object's attributes (strength, etc.) + lua_register(L, "SetBuildingTypeAttribute", Script_SetBuildingTypeAttribute); // Sets the attribute of a Building type + lua_register(L, "SetVehicleTypeAttribute", Script_SetVehicleTypeAttribute); // Sets the attribute of a Vehicle type + lua_register(L, "SetInfantryTypeAttribute", Script_SetInfantryTypeAttribute); // Sets the attribute of an Infantry type + lua_register(L, "SetVesselTypeAttribute", Script_SetVesselTypeAttribute); // Sets the attribute of a Vessel type + lua_register(L, "SetAircraftTypeAttribute", Script_SetAircraftTypeAttribute); // Sets the attribute of an Aircraft type + + lua_register(L, "GetObjectAttribute", Script_GetObjectAttribute); // Sets an object's attributes (strength, etc.) + lua_register(L, "GetBuildingTypeAttribute", Script_GetBuildingTypeAttribute); // Sets the attribute of a Building type + lua_register(L, "GetVehicleTypeAttribute", Script_GetVehicleTypeAttribute); // Sets the attribute of a Vehicle type + lua_register(L, "GetInfantryTypeAttribute", Script_GetInfantryTypeAttribute); // Sets the attribute of an Infantry type + lua_register(L, "GetVesselTypeAttribute", Script_GetVesselTypeAttribute); // Sets the attribute of a Vessel type + lua_register(L, "GetAircraftTypeAttribute", Script_GetAircraftTypeAttribute); // Sets the attribute of an Aircraft type + + lua_register(L, "SetHouseAttribute", Script_SetHouseAttribute); // Sets a house's attributes + lua_register(L, "GetHouseAttribute", Script_GetHouseAttribute); // Gets a house's attributes + + lua_register(L, "BuildUnit", Script_BuildUnit); // Builds a unit in a given factory / barracks / naval yard / etc + lua_register(L, "CreateVehicle", Script_CreateVehicle); // Creates a unit out of thin air + lua_register(L, "CreateInfantry", Script_CreateInfantry); // Creates a unit out of thin air + lua_register(L, "CreateAircraft", Script_CreateAircraft); // Creates a unit out of thin air + lua_register(L, "CreateVessel", Script_CreateVessel); // Creates a unit out of thin air + +/********************************************************************************************** +* Object Orders Functions * +*=============================================================================================*/ + + lua_register(L, "OrderMove", Script_OrderMove); // Assigns a movement order to the object + lua_register(L, "OrderAttack", Script_OrderAttack); // Assigns an attack order to the object + lua_register(L, "OrderHunt", Script_OrderHunt); // Assigns a hunt order to the object + lua_register(L, "OrderGuard", Script_OrderGuard); // Assigns a guard order to the object + lua_register(L, "OrderEnter", Script_OrderEnter); // Assigns a enter order to the object + lua_register(L, "OrderCapture", Script_OrderCapture); // Assigns a capture order to the object + +/********************************************************************************************** +* Mission Utility Functions * +*=============================================================================================*/ + + lua_register(L, "SetBriefingText", Script_SetBriefingText); // Sets the [text] on the mission briefing screen + +/********************************************************************************************** +* Status / Location Utility Functions * +*=============================================================================================*/ + + lua_register(L, "GiveCredits", Script_GiveCredits); // Give [credits] to [player] + lua_register(L, "GetCredits", Script_GetCredits); // Get [player]'s [credits] + + lua_register(L, "CountBuildings", Script_CountBuildings); // Number of buildings of [type] for given [player] + lua_register(L, "CountAircraft", Script_CountAircraft); // Number of units of [type] for given [player] + lua_register(L, "CountUnits", Script_CountUnits); // Number of units of [type] for given [player] + lua_register(L, "CountInfantry", Script_CountInfantry); // Number of infantry of [type] for given [player] + lua_register(L, "CountVessels", Script_CountVessels); // Number of vessels of [type] for given [player] + + lua_register(L, "GetWaypointX", Script_GetWaypointX); // Get Cell/X of waypoint + lua_register(L, "GetWaypointY", Script_GetWaypointY); // Get Cell/Y of waypoint + +/********************************************************************************************** +* Special / Debug Functions * +*=============================================================================================*/ + + lua_register(L, "PrintConsole", Script_Console_Print); // Print text to console + +/********************************************************************************************** +* Script Globals * +*=============================================================================================*/ + + lua_pushnumber(L, PlayerPtr->ID); // Local player (house) index + lua_setglobal(L, "_localPlayer"); + + + return true; +} \ No newline at end of file From 2f4eee66dffa9d636446280f55b7bef2aed0e1cc Mon Sep 17 00:00:00 2001 From: nev6502 Date: Sun, 28 Jun 2020 19:56:53 -0600 Subject: [PATCH 12/18] Several more small fixes; everything should be where it should be now --- code/redalert/mapscript.cpp | 40 +++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp index 8e1a841..6608868 100644 --- a/code/redalert/mapscript.cpp +++ b/code/redalert/mapscript.cpp @@ -5745,7 +5745,11 @@ static int Script_OrderMove(lua_State* L) { if (this_object != NULL) { - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, As_Target(XY_Cell(in_x, in_y))); + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); + } + + Queue_Mission(TargetClass(this_object), MISSION_QMOVE, 0, As_Target(XY_Cell(in_x, in_y))); lua_pushnumber(L, 1); return 1; @@ -5791,6 +5795,10 @@ static int Script_OrderAttackCell(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, this_target, this_target); + } + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, this_target, this_target); lua_pushnumber(L, 1); @@ -5841,6 +5849,10 @@ static int Script_OrderAttack(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + } + Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); lua_pushnumber(L, 1); @@ -5891,7 +5903,11 @@ static int Script_OrderHunt(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_HUNT, 0, this_target); + } + + Queue_Mission(TargetClass(this_object), MISSION_HUNT, 0, this_target); lua_pushnumber(L, 1); return 1; @@ -5941,7 +5957,11 @@ static int Script_OrderGuard(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_GUARD, 0, this_target); + } + + Queue_Mission(TargetClass(this_object), MISSION_GUARD, 0, this_target); lua_pushnumber(L, 1); return 1; @@ -5991,7 +6011,11 @@ static int Script_OrderRetreat(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } - Queue_Mission(TargetClass(this_object), MISSION_ATTACK, 0, this_target); + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_RETREAT, 0, this_target); + } + + Queue_Mission(TargetClass(this_object), MISSION_RETREAT, 0, this_target); lua_pushnumber(L, 1); return 1; @@ -6041,6 +6065,10 @@ static int Script_OrderEnter(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_ENTER, this_target, this_target); + } + Queue_Mission(TargetClass(this_object), MISSION_ENTER, this_target, this_target); lua_pushnumber(L, 1); @@ -6091,6 +6119,10 @@ static int Script_OrderCapture(lua_State* L) { this_target = As_Target(XY_Cell(in_x, in_y)); } + if (this_object->IsRecentlyCreated) { + Queue_Mission(TargetClass(this_object), MISSION_CAPTURE, this_target, this_target); + } + Queue_Mission(TargetClass(this_object), MISSION_CAPTURE, this_target, this_target); lua_pushnumber(L, 1); From 8337ad667208750e3a81eb227d27c6d9e0a5a696 Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 21:39:30 +0200 Subject: [PATCH 13/18] User Input changes --- code/redalert/gadget.cpp | 778 +++++++++++++++++++++++++++++++++ code/redalert/io/userinput.cpp | 236 ++++++++++ code/redalert/io/userinput.h | 166 +++++++ code/redalert/key.cpp | 749 +++++++++++++++++++++++++++++++ code/redalert/key.h | 651 +++++++++++++++++++++++++++ 5 files changed, 2580 insertions(+) create mode 100644 code/redalert/gadget.cpp create mode 100644 code/redalert/io/userinput.cpp create mode 100644 code/redalert/io/userinput.h create mode 100644 code/redalert/key.cpp create mode 100644 code/redalert/key.h diff --git a/code/redalert/gadget.cpp b/code/redalert/gadget.cpp new file mode 100644 index 0000000..1a65352 --- /dev/null +++ b/code/redalert/gadget.cpp @@ -0,0 +1,778 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GADGET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : 01/03/95 * + * * + * Last Update : August 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GadgetClass::Action -- Base action for gadget. * + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * GadgetClass::Disable -- Disables the gadget from input processing. * + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * GadgetClass::Enable -- Enables the gadget. * + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * GadgetClass::GadgetClass -- Default constructor for a gadget class object. * + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include + + +/* +** This records the current gadget the the gadget system is "stuck on". Such a +** gadget will be processed to the exclusion of all others until the mouse button +** is no longer pressed. +*/ +GadgetClass * GadgetClass::StuckOn = 0; + +/* +** This is a copy of a pointer to the last list used by the gadget input system. +** If a change of list is detected, then all gadgets are forced to be redrawn. +*/ +GadgetClass * GadgetClass::LastList = 0; + + +/* +** This points to the gadget that is intercepting all keyboard events. +*/ +GadgetClass * GadgetClass::Focused = 0; + + +/* +** This points to the current color scheme for drawing all gadgets. +*/ +static RemapControlType _GreyScheme = {15}; +RemapControlType * GadgetClass::ColorScheme = &_GreyScheme; + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for gadget object. * + * * + * This is the normal constructor for gadget objects. A gadget object is only concerned * + * with the region on the screen to considered "its own" as well as the flags that tell * + * what mouse action should be recognized when the mouse is over this screen area. * + * * + * INPUT: x,y -- Coordinates (in pixels) of the upper left hand corner of the region that * + * will be "owned" by this gadget. * + * * + * w,h -- Width and height (in pixels) of this gadget's region. * + * * + * flags -- The flags (mouse conditions) that will cause this gadget's action * + * function to be called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) : + X(x), Y(y), Width(w), Height(h), IsToRepaint(false), IsSticky(sticky), IsDisabled(false), Flags(flags) +{ + if (IsSticky) { + Flags |= LEFTPRESS|LEFTRELEASE; + } +} + + +/*********************************************************************************************** + * GadgetClass::GadgetClass -- Constructor for the gadget object. * + * * + * This is the copy constructor for the gadget object. It will try to duplicate the * + * righthand gadget. * + * * + * INPUT: gadget -- Reference to the initilization gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +GadgetClass::GadgetClass(GadgetClass const & gadget) : + X(gadget.X), + Y(gadget.Y), + Width(gadget.Width), + Height(gadget.Height), + IsToRepaint(gadget.IsToRepaint), + IsSticky(gadget.IsSticky), + IsDisabled(gadget.IsDisabled), + Flags(gadget.Flags) +{ +} + + +/*********************************************************************************************** + * GadgetClass::~GadgetClass -- Destructor for gadget object. * + * * + * This is the destructor for the gadget object. It will clear the focus from this gadget * + * if this gadget currently has the focus. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass::~GadgetClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } + + if (this == StuckOn) { + StuckOn = NULL; + } + + if (this == LastList) { + LastList = NULL; + } + + if (this == Focused) { + Focused = NULL; + } +} + + +/*************************************************************************** + * GADGETCLASS::CLICKEDON -- If a mouse click is detected within gadget's * + * area and the appropriate flag is set, then call Action(). * + * * + * INPUT: int key, int mousex, int mousey * + * * + * OUTPUT: true or false * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +{ + /* + ** Set flags to match only those events that occur AND are being looked for. If + ** the result is NULL, then we know that this button should be ignored. + */ + flags &= Flags; + + if (mousex - X < 0 || mousey - Y < 0) + return false; + + /* + ** If keyboard input should be processed by this "gadget" and keyboard input is + ** detected, then always call the action function. It is up to the action function + ** in this case to either ignore the keyboard input or not. + ** + ** For mouse actions, check to see if the mouse is in the region of the button + ** before calling the associated action function. This is the typical action for + ** buttons. + */ + if (this == StuckOn || + (flags & KEYBOARD) || + (flags && (mousex - X) < Width && (mousey - Y) < Height)) { + + return(Action(flags, key)); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Enable -- Enables the gadget. * + * * + * This function enables the gadget. An enabled gadget will be processed for input * + * purposes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Enable(void) +{ + IsDisabled = false; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Disable -- Disables the gadget from input processing. * + * * + * This routine will disable the gadget. A disabled gadget might be rendered, but is * + * ignored for input processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Disable(void) +{ + IsDisabled = true; + IsToRepaint = true; + Clear_Focus(); +} + + +/*********************************************************************************************** + * GadgetClass::Remove -- Removes the specified gadget from the list. * + * * + * Use this routine if an individual gadget needs to be removed from the list of gadgets. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the specified gadget found and removed? A false indicates that the * + * gadget wasn't in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Remove(void) +{ + Clear_Focus(); + return(GadgetClass *)LinkClass::Remove(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Next -- Returns a pointer to the next gadget in the chain. * + * * + * This returns with the next gadget's pointer. It is identical to the base Get_Next() * + * function, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the next gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Next(void) const +{ + return(GadgetClass*)LinkClass::Get_Next(); +} + + +/*********************************************************************************************** + * GadgetClass::Get_Prev -- Fetches a pointer to the previous gadget. * + * * + * This routine will return the previous gadget in the list. It is identical to the base * + * function Get_Prev, but returns a pointer to a GadgetClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous gadget in the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * GadgetClass::Get_Prev(void) const +{ + return(GadgetClass*)LinkClass::Get_Prev(); +} + + +/*********************************************************************************************** + * GadgetClass::Delete_List -- Deletes all gadget objects in list. * + * * + * This function will delete all gadgets in the list. It is the counterpart to the * + * Create_One_Of functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Any references to these gadget become invalidated by this routine. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Delete_List(void) +{ + GadgetClass * g = this; + + /* + ** Move to head of the list. + */ + while (g->Get_Prev()) { + g = g->Get_Prev(); + } + + /* + ** First delete all the gadgets following the first one. The reason the first one + ** is kept around is that sometimes deleting one gadget will result in related gadgets + ** in the same list also being deleted. The first gadget will always contain the + ** correct gadget pointer. + */ + while (g) { + g->Clear_Focus(); + + GadgetClass * temp = g; + g = g->Get_Next(); + delete temp; + } +} + + +/*********************************************************************************************** + * GadgetClass::Action -- Base action for gadget. * + * * + * This handles the base level action that a gadget performs when a qualifying input event * + * is detected. This sets the redraw flag and returns true (to stop further processing). * + * If no qualifying input event was detected, but this routine was called anyway, then * + * don't perform any action. The call to this routine, in that case, must have been forced * + * for some other reason. * + * * + * INPUT: flag -- The input event bits that qualify for this gadget. A NULL indicates that * + * no qualifying event occurred. * + * * + * OUTPUT: bool; Should further gadget list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Action(unsigned flags, KeyNumType &) +{ + /* + ** If any of the event flags are active, then this indicates that something probably + ** has changed the gadget. Flag the gadget to be redrawn. Also, make sure that + ** any sticky flags are cleared up. + */ + if (flags) { + IsToRepaint = true; + Sticky_Process(flags); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_Me -- Gadget redraw action (flag control). * + * * + * At this level, there is no actual rendering taking place with the call to Draw_Me, but * + * the IsToRepaint flag must be cleared. Derived objects will call this routine and if it * + * returns true, they will perform their custom rendering. * + * * + * INPUT: forced -- Is this redraw forced by outside circumstances? * + * * + * OUTPUT: bool; Should the gadget imagery be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + *=============================================================================================*/ +int GadgetClass::Draw_Me(int forced) +{ + if (forced || IsToRepaint) { + IsToRepaint = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GadgetClass::Draw_All -- Forces all gadgets in list to be redrawn. * + * * + * Use this function to cause all gadget in the list to be redrawn regardless of the state * + * of the IsToRepaint flag. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +void GadgetClass::Draw_All(bool forced) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + gadget->Draw_Me(forced); + gadget = gadget->Get_Next(); + } +} + +/*************************************************************************** + * GADGETCLASS::PROCESSLIST -- Check list for a mouse click within a gadget* + * * + * INPUT: none. * + * * + * OUTPUT: key pressed. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +KeyNumType GadgetClass::Input(void) +{ + EventMessage eventMessage; + KeyNumType key = KN_NONE; + int flags = 0; + int forced = false; + + /* + ** Record this list so that a forced redraw only occurs the FIRST time the + ** gadget list is passed to this routine. + */ + if (LastList != this) { + LastList = this; + forced = true; + StuckOn = NULL; + Focused = NULL; + } + + /* + ** Fetch any pending keyboard input. + */ + UserInput.Process_Input(key, flags); + + if ( EventQueue.Check() ) { + eventMessage = EventQueue.Get(); + eventMessage.Key_Flags( key, flags ); + } + + /* + ** If "sticky" processing is active, then only process the stuck gadget. + */ + if (StuckOn) { + StuckOn->Draw_Me(false); + GadgetClass * oldstuck = StuckOn; + StuckOn->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); + + if (StuckOn) + { + StuckOn->Draw_Me(false); + } else { + oldstuck->Draw_Me(false); + } + } + else + { + /* + ** If there is a gadget that has the keyboard focus, then route all keyboard + ** events to it. + */ + if ( Focused && eventMessage.EventType == EventMessageType::EVENT_KEYBOARD ) { + Focused->Draw_Me(false); + Focused->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); + if (Focused) { + Focused->Draw_Me(false); + } + } else { + + /* + ** Sweep through all the buttons in the chain and pass the current button state + ** and keyboard input data to them. These routines will detect whether they should + ** perform some action and return a flag to this effect. They also have the option + ** of changing the key value so that an appropriate return value is used for this + ** processing routine. + */ + GadgetClass * next_button = this; + while (next_button != NULL) { + + /* + ** Maybe redraw the button if it needs to or is being forced to redraw. + */ + next_button->Draw_Me(forced); + + if (!next_button->IsDisabled) { + + /* + ** Process this button. If the button was recognized and action was + ** performed, then bail from further processing (speed reasons?). + */ + if (next_button->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y)) { + + /* + ** Some buttons will require repainting when they perform some action. + ** Do so at this time. + */ + next_button->Draw_Me(false); + break; + } + } + + next_button = next_button->Get_Next(); + } + } + } + + return(key); +} + + +/*********************************************************************************************** + * GadgetClass::Extract_Gadget -- Sweeps through the gadget chain to find gadget specified. * + * * + * This examines the gadget list looking for on that has the same ID as specified. If that * + * gadget was found, then a pointer to it is returned. Since only ControlClass gadgets * + * or ones derived from it can have an ID value, we know that the returned pointer is at * + * least of the ControlClass type. * + * * + * INPUT: id -- The ID number to scan for. Zero is not a legal ID number and if passed in, * + * a NULL will always be returned. * + * * + * OUTPUT: Returns with a pointer to the ControlClass gadget that has the matching ID number. * + * If no matching gadget was found, then NULL is returned. * + * * + * WARNINGS: If there happens to be more than one gadget with a matching ID, this routine * + * will return a pointer to the first one only. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass * GadgetClass::Extract_Gadget(unsigned id) +{ + GadgetClass * g = this; + + if (id != 0) { + while (g != NULL) { + if (g->Get_ID() == id) { + return((ControlClass *)g); + } + g = g->Get_Next(); + } + } + return(0); +} + + +/*********************************************************************************************** + * GadgetClass::Flag_To_Redraw -- Flags this gadget to be redrawn. * + * * + * Use this routine to flag the gadget to be redrawn. A gadget so flagged will have its * + * Draw_Me function called at the next available opportunity. Usually, this is the next * + * time the Input() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Flag_To_Redraw(void) +{ + IsToRepaint = true; +} + + +/*********************************************************************************************** + * GadgetClass::Sticky_Process -- Handles the sticky flag processing. * + * * + * This function examines the event flags and handles any "sticky" processing required. * + * Sticky processing is when the button is flagged with the "IsSticky" bit and it will * + * be processed to the exclusion of all other gadgets while the mouse button is held * + * down. * + * * + * INPUT: flags -- The event flags that triggered the call to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Sticky_Process(unsigned flags) +{ + if (IsSticky && (flags & LEFTPRESS)) { + StuckOn = this; + } + if (StuckOn == this && (flags & LEFTRELEASE)) { + StuckOn = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Set_Focus -- Sets focus to this gadget. * + * * + * This will set the focus to this gadget regardless of any current focus setting. If there * + * is another gadget that has focus, it will have its focus cleared before this gadget will * + * get the focus. A focused gadget is one that has all keyboard input routed to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Focus(void) +{ + if (Focused) { + Focused->Flag_To_Redraw(); + Focused->Clear_Focus(); + } + Flags |= KEYBOARD; + Focused = this; +} + + +/*********************************************************************************************** + * GadgetClass::Clear_Focus -- Clears the focus if this gadget has it. * + * * + * Use this function to clear the focus for the gadget. If the gadget doesn't currently * + * have focus, then this routine will do nothing. For added functionality, overload this * + * virtual function so that gadget specific actions may be take when focus is lost. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Clear_Focus(void) +{ + if (Focused == this) { + Flags &= ~KEYBOARD; + Focused = 0; + } +} + + +/*********************************************************************************************** + * GadgetClass::Has_Focus -- Checks if this object currently has the keyboard focus. * + * * + * If this object has the keyboard focus, then this routine will return true. When the * + * gadget has keyboard focus, all keyboard events get routed to the gadget. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this gadget have the keyboard focus? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool GadgetClass::Has_Focus(void) +{ + return(this == Focused); +} + + +/*********************************************************************************************** + * GadgetClass::Is_List_To_Redraw -- tells if any gadget in the list needs redrawing * + * * + * This function is mostly for supporting HidPage drawing. If it returns true, it means * + * the application needs to re-blit the HidPage forward, after calling the list's Input(). * + * * + * INPUT: none * + * * + * OUTPUT: true = an item needs redrawing, false = no items need redrawing * + * * + * WARNINGS: It is assumed 'this' is the head of the list. * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=============================================================================================*/ +int GadgetClass::Is_List_To_Redraw(void) +{ + GadgetClass * gadget = this; + + while (gadget != NULL) { + if (gadget->IsToRepaint) { + return (true); + } + gadget = gadget->Get_Next(); + } + return (false); +} + + +/*********************************************************************************************** + * GadgetClass::Set_Position -- Set the coordinate position of this gadget. * + * * + * This routine helps with moving a gadget's location. It will set the gadgets upper * + * left corner to the pixel location specified. * + * * + * INPUT: x,y -- The X and Y position to put the gadget's upper left corner to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1996 JLB : Created. * + *=============================================================================================*/ +void GadgetClass::Set_Position(int x, int y) +{ + X = x; + Y = y; +} + diff --git a/code/redalert/io/userinput.cpp b/code/redalert/io/userinput.cpp new file mode 100644 index 0000000..39322e8 --- /dev/null +++ b/code/redalert/io/userinput.cpp @@ -0,0 +1,236 @@ +#include "../FUNCTION.H" +#include "UserInput.h" + +extern KeyboardClass * Keyboard; + +std::map sdl_keyMapping_reverse; + +UserInputClass::UserInputClass() +{ + LastAction = InputAction::INPUT_ACTION_NONE; + + // generate reverse keymapping + for ( auto it = sdl_keyMapping.begin(); it != sdl_keyMapping.end(); it++ ) + { + sdl_keyMapping_reverse[it->second] = it->first; + } +} + +bool UserInputClass::KeyboardState::Key_Down(KeyNumType key) const +{ + auto sdl_Key = sdl_keyMapping_reverse.find( key ); + if ( sdl_Key != sdl_keyMapping_reverse.end() ) + { + const SDL_Scancode scanCode = SDL_GetScancodeFromKey(sdl_Key->second); + const Uint8* keyState = SDL_GetKeyboardState(NULL); + return ( keyState[scanCode] ); + } + return false; +} + +bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const +{ + // get mouse state, we are only interested in the buttons + const Uint32 MouseState = SDL_GetMouseState(NULL,NULL); + + switch (key) + { + case KN_LMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_LEFT); + break; + case KN_RMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_RIGHT); + break; + } +} + +void UserInputClass::Process_Input(KeyNumType& key, int& flags) +{ + bool leftMouseProcessed = false; + + const Uint32 MouseState = SDL_GetMouseState(&Mouse.X, &Mouse.Y); + + flags = 0; + + Keyboard->MouseQX = Mouse.X; + Keyboard->MouseQY = Mouse.Y; + + static SDL_Event event; + memset(&event, 0, sizeof(SDL_Event)); + + while (SDL_PollEvent(&event)) { + const Uint8* state = SDL_GetKeyboardState(NULL); + + switch (event.type) + { + case SDL_SYSWMEVENT: + if (event.syswm.msg->msg.win.msg == WM_DESTROY) + { + Invalidate_Cached_Icons(); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger) Reset_Video_Mode(); + + PostQuitMessage(0); + + switch (ReadyToQuit) { + case 1: + ReadyToQuit = 2; + break; + case 0: + ExitProcess(0); + break; + } + } + + break; + + /* Mouse is moving */ + case SDL_MOUSEMOTION: + if ( !( MouseState & SDL_BUTTON(SDL_BUTTON_LEFT) ) ) + { + flags |= GadgetClass::LEFTUP; + } + + break; + + case SDL_MOUSEBUTTONDOWN: + if (event.button.button == SDL_BUTTON_LEFT) { + Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_DOWN; + + key = KN_LMOUSE; + flags |= GadgetClass::LEFTPRESS; + + EventQueue.Put( EventMessage( Mouse.X, Mouse.Y, KN_LMOUSE ) ); + + Keyboard->Put_Element(KN_LMOUSE); + + leftMouseProcessed = true; + numFramesLMouse++; + + // after first left-mouse down, abort handling any other SDL events + // because game might first need to switch into rubber band mode + if (numFramesLMouse == 1) return; + } + else if (event.button.button == SDL_BUTTON_RIGHT) { + Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_DOWN; + + EventQueue.Put( EventMessage( Mouse.X, Mouse.Y, KN_RMOUSE ) ); + + Keyboard->Put_Element(KN_RMOUSE); + + key = KN_RMOUSE; + flags |= GadgetClass::RIGHTPRESS; + + numFramesLMouse = 0; + } + break; + + case SDL_MOUSEBUTTONUP: + if (event.button.button == SDL_BUTTON_LEFT) { + Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_RELEASE; + + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE) ); + + flags |= GadgetClass::LEFTRELEASE; + numFramesLMouse = 0; + } + else if (event.button.button == SDL_BUTTON_RIGHT) { + Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_RELEASE; + + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_RMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE) ); + + flags |= GadgetClass::RIGHTRELEASE; + numFramesLMouse = 0; + } + break; + + // Keyboard key lifted up + case SDL_KEYUP: + flags |= GadgetClass::KEYBOARD; + + break; + + // Keyboard key pressed down + case SDL_KEYDOWN: + { + if (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) { + renderConsole = !renderConsole; + + if (renderConsole) { + ShowCursor(TRUE); + Hide_Mouse(); + } + else { + ShowCursor(FALSE); + Show_Mouse(); + } + break; + } + + flags |= GadgetClass::KEYBOARD; + + int keyFlags = 0x00; + KeyB.Shift = false; + KeyB.Alt = false; + KeyB.Control = false; + KeyB.CapsLock = false; + + if (state[SDL_SCANCODE_RSHIFT] || state[SDL_SCANCODE_LSHIFT]) { + KeyB.Shift = true; + keyFlags |= KN_SHIFT_BIT; + } + if (state[SDL_SCANCODE_RALT] || state[SDL_SCANCODE_LALT]) { + keyFlags |= KN_ALT_BIT; + KeyB.Alt = true; + } + if (state[SDL_SCANCODE_RCTRL] || state[SDL_SCANCODE_LCTRL]) { + keyFlags |= KN_CTRL_BIT; + KeyB.Control = true; + } + if (state[SDL_SCANCODE_CAPSLOCK]) { + keyFlags |= KN_CAPSLOCK_BIT; + KeyB.CapsLock = true; + } + + // handle match between SDL and internals ( non-text keys ) + auto element = sdl_keyMapping.find((SDL_KeyCode)event.key.keysym.sym); + if (element != sdl_keyMapping.end()) + { + //EventQueue.Put( element->second | keyFlags ); + EventQueue.Put( EventMessage( (KeyNumType)element->second, element->second, KeyB.Shift, KeyB.Alt, KeyB.Control, KeyB.CapsLock ) ); + + Keyboard->Put_Element( element->second | keyFlags); + + KeyB.LastKey = element->first; + if (event.text.text[0] == (event.text.text[0] & 0xFF)) + { + KeyB.ASCII = element->second; + } + } + + break; + } + case SDL_TEXTINPUT: + // limit to range 0-255 + if (event.text.text[0] == ( event.text.text[0] & 0xFF ) ) + { + KeyB.ASCII = (char)event.text.text[0]; + } + } + + if (Debug_Map || renderConsole) + { + ImGui_ImplSDL2_ProcessEvent(&event); + } + } + + if (!leftMouseProcessed && numFramesLMouse != 0) + { + Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_HOLD; + flags |= GadgetClass::LEFTHELD; + + EventQueue.Put( EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_HOLD) ); + } +} \ No newline at end of file diff --git a/code/redalert/io/userinput.h b/code/redalert/io/userinput.h new file mode 100644 index 0000000..d6cc690 --- /dev/null +++ b/code/redalert/io/userinput.h @@ -0,0 +1,166 @@ +#pragma once + +#include +#include +#include +#include + +#include "examples/imgui_impl_sdl.h" + +extern KeyNumType g_globalKeyNumType; +extern int g_globalKeyFlags; + +// Mapping of SDL KeyCode values to internal KeyNumType +std::map const sdl_keyMapping = { + { SDLK_a, KN_A }, + { SDLK_b, KN_B }, + { SDLK_c, KN_C }, + { SDLK_d, KN_D }, + { SDLK_e, KN_E }, + { SDLK_f, KN_F }, + { SDLK_g, KN_G }, + { SDLK_h, KN_H }, + { SDLK_i, KN_I }, + { SDLK_j, KN_J }, + { SDLK_k, KN_K }, + { SDLK_l, KN_N }, + { SDLK_m, KN_M }, + { SDLK_n, KN_N }, + { SDLK_o, KN_O }, + { SDLK_p, KN_P }, + { SDLK_q, KN_Q }, + { SDLK_r, KN_R }, + { SDLK_s, KN_S }, + { SDLK_t, KN_T }, + { SDLK_u, KN_U }, + { SDLK_v, KN_V }, + { SDLK_w, KN_W }, + { SDLK_x, KN_X }, + { SDLK_y, KN_Y }, + { SDLK_z, KN_Z }, + + { SDLK_0, KN_0 }, + { SDLK_1, KN_1 }, + { SDLK_2, KN_2 }, + { SDLK_3, KN_3 }, + { SDLK_4, KN_4 }, + { SDLK_5, KN_5 }, + { SDLK_6, KN_6 }, + { SDLK_7, KN_7 }, + { SDLK_8, KN_8 }, + { SDLK_9, KN_9 }, + + { SDLK_KP_MULTIPLY, KN_KEYPAD_ASTERISK }, + { SDLK_KP_DIVIDE, KN_KEYPAD_SLASH }, + { SDLK_MINUS, KN_MINUS }, + { SDLK_EQUALS, KN_EQUAL }, + + { SDLK_RIGHTBRACKET, KN_RBRACKET }, + { SDLK_LEFTBRACKET, KN_LBRACKET }, + { SDLK_SEMICOLON, KN_SEMICOLON }, + { SDLK_COMMA, KN_COMMA }, + { SDLK_PERIOD, KN_PERIOD }, + { SDLK_SLASH, KN_SLASH }, + { SDLK_BACKSLASH, KN_BACKSLASH }, + + { SDLK_F1, KN_F1 }, + { SDLK_F2, KN_F2 }, + { SDLK_F3, KN_F3 }, + { SDLK_F4, KN_F4 }, + { SDLK_F5, KN_F5 }, + { SDLK_F6, KN_F6 }, + { SDLK_F7, KN_F7 }, + { SDLK_F8, KN_F8 }, + { SDLK_F9, KN_F9 }, + { SDLK_F10, KN_F10 }, + { SDLK_F11, KN_F12 }, + { SDLK_F12, KN_F12 }, + + { SDLK_BACKSPACE, KN_BACKSPACE }, + { SDLK_SPACE, KN_SPACE }, + { SDLK_KP_ENTER, KN_RETURN }, + { SDLK_RETURN, KN_RETURN }, + { SDLK_ESCAPE, KN_ESC }, + + { SDLK_UP, KN_UP }, + { SDLK_DOWN, KN_DOWN }, + { SDLK_LEFT, KN_LEFT }, + { SDLK_RIGHT, KN_RIGHT }, + + { SDLK_LALT, KN_LALT }, + { SDLK_RALT, KN_RALT }, + { SDLK_LCTRL, KN_LCTRL }, + { SDLK_RCTRL, KN_RCTRL }, + { SDLK_LSHIFT, KN_LSHIFT }, + { SDLK_RSHIFT, KN_RSHIFT }, +}; +extern std::map sdl_keyMapping_reverse; + +const int USERINPUT_SHIFT_BIT = 0x100; +const int USERINPUT_CTRL_BIT = 0x200; +const int USERINPUT_ALT_BIT = 0x400; + +typedef enum { + MOUSE_BUTTON_UP, + MOUSE_BUTTON_DOWN, + MOUSE_BUTTON_HOLD, + MOUSE_BUTTON_RELEASE, +} MouseButtonState; + +typedef enum { + INPUT_ACTION_NONE, + INPUT_ACTION_KEYBOARD, + INPUT_ACTION_MOUSE, +} InputAction; + +class UserInputClass +{ + public: + UserInputClass(); + void UserInputClass::Process_Input( KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags ); + + InputAction LastAction; + + class KeyboardState { + public: + SDL_KeyCode LastKey; + char ASCII; + + bool Shift; + bool Control; + bool Alt; + bool CapsLock; + + bool Key_Down(KeyNumType key) const; + + void ResetState() { + this->Shift = false; + this->Control = false; + this->Alt = false; + } + }; + + KeyboardState KeyB; + + class MouseState { + public: + int X; + int Y; + + MouseButtonState Button_Left; + MouseButtonState Button_Middle; + MouseButtonState Button_Right; + + bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const; + + void ResetState() { + this->Button_Left = MouseButtonState::MOUSE_BUTTON_UP; + this->Button_Middle = MouseButtonState::MOUSE_BUTTON_UP; + this->Button_Right = MouseButtonState::MOUSE_BUTTON_UP; + } + }; + + MouseState Mouse; + private: + int numFramesLMouse = 0; +}; \ No newline at end of file diff --git a/code/redalert/key.cpp b/code/redalert/key.cpp new file mode 100644 index 0000000..354ce01 --- /dev/null +++ b/code/redalert/key.cpp @@ -0,0 +1,749 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/KEY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages* + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboar* + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keybo* + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "key.h" +#include "FUNCTION.H" +#include "SDL.h" + +#include "monoc.h" + +//void Message_Loop(void); + +//WWKeyboardClass * _Kbd = NULL; + + +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + +int WWKeyboardClass::Head = 0; +int WWKeyboardClass::Tail = 0; + +/*********************************************************************************************** + * WWKeyboardClass::WWKeyBoardClass -- Construction for Westwood Keyboard Class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +WWKeyboardClass::WWKeyboardClass(void) : + MouseQX(0), + MouseQY(0) +{ +// _Kbd = this; + + memset(KeyState, '\0', sizeof(KeyState)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the key value that was pulled from buffer (includes bits) * * + * * + * WARNINGS: If the key was a mouse event MouseQX and MouseQY will be updated * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Buff_Get(void) +{ + while (!Check()) {} // wait for key in buffer + + unsigned short temp = Fetch_Element(); + + return(temp); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Mouse_Key -- Checks to see if specified key refers to the mouse. * + * * + * This checks the specified key code to see if it refers to the mouse buttons. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the key a mouse button key? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Mouse_Key(unsigned short key) +{ + key &= 0xFF; + return (key == VK_LBUTTON || key == VK_MBUTTON || key == VK_RBUTTON); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Check -- Checks to see if a key is in the buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + * 09/24/1996 JLB : Converted to new style keyboard system. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Check(void) const +{ + ((WWKeyboardClass *)this)->Fill_Buffer_From_System(); + if (Is_Buffer_Empty()) return(false); + return(Peek_Element()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get -- Logic to get a metakey from the buffer * + * * + * INPUT: none * + * * + * OUTPUT: int - the meta key taken from the buffer. * + * * + * WARNINGS: This routine will not return until a keypress is received * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Get(void) +{ + while (!Check()) {} // wait for key in buffer + return (Buff_Get()); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put -- Logic to insert a key into the keybuffer] * + * * + * INPUT: int - the key to insert into the buffer * + * * + * OUTPUT: bool - true if key is sucessfuly inserted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put(unsigned short key) +{ + if (!Is_Buffer_Full()) { + Put_Element(key); + return(true); + } + return(false); +} + +/*********************************************************************************************** + * WWKeyboardClass::Put_Key_Message -- Translates and inserts wParam into Keyboard Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/16/1995 PWG : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Key_Message(unsigned short vk_key, bool release) +{ + /* + ** Get the status of all of the different keyboard modifiers. Note, only pay attention + ** to numlock and caps lock if we are dealing with a key that is affected by them. Note + ** that we do not want to set the shift, ctrl and alt bits for Mouse keypresses as this + ** would be incompatible with the dos version. + */ + if (!Is_Mouse_Key(vk_key)) { + if (((GetKeyState(VK_SHIFT) & 0x8000) != 0) || + ((GetKeyState(VK_CAPITAL) & 0x0008) != 0) || + ((GetKeyState(VK_NUMLOCK) & 0x0008) != 0)) { + + vk_key |= WWKEY_SHIFT_BIT; + } + if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) { + vk_key |= WWKEY_CTRL_BIT; + } + if ((GetKeyState(VK_MENU) & 0x8000) != 0) { + vk_key |= WWKEY_ALT_BIT; + } + } + + if (release) { + vk_key |= WWKEY_RLS_BIT; + } + + /* + ** Finally use the put command to enter the key into the keyboard + ** system. + */ + return(Put(vk_key)); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Mouse_Message -- Stores a mouse type message into the keyboard buffer. * + * * + * This routine will store the mouse type event into the keyboard buffer. It also checks * + * to ensure that there is enough room in the buffer so that partial mouse events won't * + * be recorded. * + * * + * INPUT: vk_key -- The mouse key message itself. * + * * + * x,y -- The mouse coordinates at the time of the event. * + * * + * release -- Is this a mouse button release? * + * * + * OUTPUT: bool; Was the event stored sucessfully into the keyboard buffer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release) +{ + if (Available_Buffer_Room() >= 3 && Is_Mouse_Key(vk_key)) { + Put_Key_Message(vk_key, release); + Put((unsigned short)x); + Put((unsigned short)y); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation. * + * * + * This routine will convert the key code specified into an ASCII value. This takes into * + * consideration the language and keyboard mapping of the host Windows system. * + * * + * INPUT: key -- The key code to convert into ASCII. * + * * + * OUTPUT: Returns with the key converted into ASCII. If the key has no ASCII equivalent, * + * then '\0' is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +char WWKeyboardClass::To_ASCII(unsigned short key) +{ + /* + ** Released keys never translate into an ASCII value. + */ + if (key & WWKEY_RLS_BIT) { + return('\0'); + } + + /* + ** Set the KeyState buffer to reflect the shift bits stored in the key value. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0x80; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0x80; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0x80; + } + + /* + ** Ask windows to translate the key into an ASCII equivalent. + */ + char buffer[10]; + int result = 1; + int scancode = 0; + + //scancode = MapVirtualKey(key & 0xFF, 0); + //result = ToAscii((UINT)(key & 0xFF), (UINT)scancode, (PBYTE)KeyState, (LPWORD)buffer, (UINT)0); + buffer[0] = UserInput.KeyB.ASCII; + + /* + ** Restore the KeyState buffer back to pristine condition. + */ + if (key & WWKEY_SHIFT_BIT) { + KeyState[VK_SHIFT] = 0; + } + if (key & WWKEY_CTRL_BIT) { + KeyState[VK_CONTROL] = 0; + } + if (key & WWKEY_ALT_BIT) { + KeyState[VK_MENU] = 0; + } + + /* + ** If Windows could not perform the translation as expected, then + ** return with a null ASCII value. + */ + if (result != 1) { + return('\0'); + } + + return(buffer[0]); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Down -- Checks to see if the specified key is being held down. * + * * + * This routine will examine the key specified to see if it is currently being held down. * + * * + * INPUT: key -- The key to check. * + * * + * OUTPUT: bool; Is the specified key currently being held down? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Down(unsigned short key) +{ + //return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); + return UserInput.KeyB.Key_Down( (KeyNumType)key ); +} + + +extern "C" { + void __cdecl Stop_Execution (void); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer. * + * * + * This routine will extract the next pending element in the keyboard queue. If there is * + * no element available, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the element extracted from the queue. An empty queue is signified * + * by a 0 return value. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Fetch_Element(void) +{ + unsigned short val = 0; + if (Head != Tail) { + val = Buffer[Head]; + + Head = (Head + 1) % ARRAY_SIZE(Buffer); + } + return(val); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer. * + * * + * This routine will examine and return with the next element in the keyboard buffer but * + * it will not alter or remove that element. Use this routine to see what is pending in * + * the keyboard queue. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next element in the keyboard queue. If the keyboard buffer is * + * empty, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short WWKeyboardClass::Peek_Element(void) const +{ + if (!Is_Buffer_Empty()) { + return(Buffer[Head]); + } + return(0); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Put_Element -- Put a keyboard data element into the buffer. * + * * + * This will put one keyboard data element into the keyboard buffer. Typically, this data * + * is a key code, but it might be mouse coordinates. * + * * + * INPUT: val -- The data element to add to the keyboard buffer. * + * * + * OUTPUT: bool; Was the keyboard element added successfully? A failure would indicate that * + * the keyboard buffer is full. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Put_Element(unsigned short val) +{ + if (!Is_Buffer_Full()) { + int temp = (Tail+1) % ARRAY_SIZE(Buffer); + Buffer[Tail] = val; + Tail = temp; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Full -- Determines if the keyboard buffer is full. * + * * + * This routine will examine the keyboard buffer to determine if it is completely * + * full of queued keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer completely full? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Full(void) const +{ + if ((Tail + 1) % ARRAY_SIZE(Buffer) == Head) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Is_Buffer_Empty -- Checks to see if the keyboard buffer is empty. * + * * + * This routine will examine the keyboard buffer to see if it contains no events at all. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the keyboard buffer currently without any pending events queued? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Is_Buffer_Empty(void) const +{ + if (Head == Tail) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Fill_Buffer_From_Syste -- Extract and process any queued windows messages. * + * * + * This routine will extract and process any windows messages in the windows message * + * queue. It is presumed that the normal message handler will call the keyboard * + * message processing function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Fill_Buffer_From_System(void) +{ + +} + + +/*********************************************************************************************** + * WWKeyboardClass::Clear -- Clears the keyboard buffer. * + * * + * This routine will clear the keyboard buffer of all pending keyboard events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +void WWKeyboardClass::Clear(void) +{ + /* + ** Extract any windows pending keyboard message events and then clear out the keyboard + ** buffer. + */ + Fill_Buffer_From_System(); + Head = Tail; + + /* + ** Perform a second clear to handle the rare case of the keyboard buffer being full and there + ** still remains keyboard related events in the windows message queue. + */ + Fill_Buffer_From_System(); + Head = Tail; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboard * + * * + * This routine will examine the Windows message specified. If the message relates to an * + * event that the keyboard input system needs to process, then it will be processed * + * accordingly. * + * * + * INPUT: window -- Handle to the window receiving the message. * + * * + * message -- The message number of this event. * + * * + * wParam -- The windows specific word parameter (meaning depends on message). * + * * + * lParam -- The windows specific long word parameter (meaning is message dependant)* + * * + * OUTPUT: bool; Was this keyboard message recognized and processed? A 'false' return value * + * means that the message should be processed normally. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool WWKeyboardClass::Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam) +{ +// ST - 5/13/2019 +#if (0) + bool processed = false; + + /* + ** Examine the message to see if it is one that should be processed. Only keyboard and + ** pertinant mouse messages are processed. + */ + switch (message) { + + /* + ** System key has been pressed. This is the normal keyboard event message. + */ + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if (wParam == VK_SCROLL) { + Stop_Execution(); + } else { + Put_Key_Message((unsigned short)wParam); + } + processed = true; + break; + + /* + ** The key has been released. This is the normal key release message. + */ + case WM_SYSKEYUP: + case WM_KEYUP: + Put_Key_Message((unsigned short)wParam, true); + processed = true; + break; + + /* + ** Press of the left mouse button. + */ + case WM_LBUTTONDOWN: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the left mouse button. + */ + case WM_LBUTTONUP: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Double click of the left mouse button. Fake this into being + ** just a rapid click of the left button twice. + */ + case WM_LBUTTONDBLCLK: + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_LBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Press of the middle mouse button. + */ + case WM_MBUTTONDOWN: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Release of the middle mouse button. + */ + case WM_MBUTTONUP: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Middle button double click gets translated into two + ** regular middle button clicks. + */ + case WM_MBUTTONDBLCLK: + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_MBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Right mouse button press. + */ + case WM_RBUTTONDOWN: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + processed = true; + break; + + /* + ** Right mouse button release. + */ + case WM_RBUTTONUP: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** Translate a double click of the right button + ** into being just two regular right button clicks. + */ + case WM_RBUTTONDBLCLK: + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam)); + Put_Mouse_Message(VK_RBUTTON, LOWORD(lParam), HIWORD(lParam), true); + processed = true; + break; + + /* + ** If the message is not pertinant to the keyboard system, + ** then do nothing. + */ + default: + break; + } + + /* + ** If this message has been processed, then pass it on to the system + ** directly. + */ + if (processed) { + DefWindowProc(window, message, wParam, lParam); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keyboa * + * * + * This examines the keyboard buffer queue and determine how many elements are available * + * for use before the buffer becomes full. Typical use of this would be when inserting * + * mouse events that require more than one element. Such an event must detect when there * + * would be insufficient room in the buffer and bail accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of elements that may be stored in to the keyboard buffer * + * before it becomes full and cannot accept any more elements. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +int WWKeyboardClass::Available_Buffer_Room(void) const +{ + int avail; + if (Head == Tail) { + avail = ARRAY_SIZE(Buffer); + } + if (Head < Tail) { + avail = Tail - Head; + } + if (Head > Tail) { + avail = (Tail + ARRAY_SIZE(Buffer)) - Head; + } + return(avail); +} diff --git a/code/redalert/key.h b/code/redalert/key.h new file mode 100644 index 0000000..263c1ae --- /dev/null +++ b/code/redalert/key.h @@ -0,0 +1,651 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/KEY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#pragma warning(disable : 4005) + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + bool Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + bool Put_Element(unsigned short val); + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + bool Put_Mouse_Message(unsigned short vk_key, int x, int y, bool release = false); + int Available_Buffer_Room(void) const; + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + static int Head; + static int Tail; +}; + +#define RA_SCANCODE_MASK (1<<30) +#define RA_SCANCODE_TO_KEYCODE(X) (X | RA_SCANCODE_MASK) + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK '\b' +#define VK_TAB '\t' +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE '\033' +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 80 +#define VK_UP 82 +#define VK_RIGHT 79 +#define VK_DOWN 81 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 'a' +#define VK_B 'b' +#define VK_C 'c' +#define VK_D 'd' +#define VK_E 'e' +#define VK_F 'f' +#define VK_G 'g' +#define VK_H 'h' +#define VK_I 'i' +#define VK_J 'j' +#define VK_K 'k' +#define VK_L 'l' +#define VK_M 'm' +#define VK_N 'n' +#define VK_O 'o' +#define VK_P 'p' +#define VK_Q 'q' +#define VK_R 'r' +#define VK_S 's' +#define VK_T 't' +#define VK_U 'u' +#define VK_V 'v' +#define VK_W 'w' +#define VK_X 'x' +#define VK_Y 'y' +#define VK_Z 'z' +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +typedef enum KeyASCIIType { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +} KeyASCIIType; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0, + KN_1, + KN_2, + KN_3, + KN_4, + KN_5, + KN_6, + KN_7, + KN_8, + KN_9, + KN_A, + KN_B, + KN_C, + KN_D, + KN_E, + KN_F, + KN_G, + KN_H, + KN_I, + KN_J, + KN_K, + KN_L, + KN_M, + KN_N, + KN_O, + KN_P, + KN_Q, + KN_R, + KN_S, + KN_T, + KN_U, + KN_V, + KN_W, + KN_X, + KN_Y, + KN_Z, + + KN_BACKSLASH, + KN_BACKSPACE, + KN_CAPSLOCK, + KN_CENTER, + KN_COMMA, + KN_DELETE, + KN_DOWN, + KN_DOWNLEFT, + KN_DOWNRIGHT, + KN_END, + KN_EQUAL, + KN_ESC, + KN_E_DELETE, + KN_E_DOWN, + KN_E_END, + KN_E_HOME, + KN_E_INSERT, + KN_E_LEFT, + KN_E_PGDN, + KN_E_PGUP, + KN_E_RIGHT, + KN_E_UP, + KN_GRAVE, + KN_HOME, + KN_INSERT, + KN_KEYPAD_ASTERISK, + KN_KEYPAD_MINUS, + KN_KEYPAD_PLUS, + KN_KEYPAD_RETURN, + KN_KEYPAD_SLASH, + KN_MINUS, + KN_NUMLOCK, + KN_PAUSE, + KN_PERIOD, + KN_PGDN, + KN_PGUP, + KN_PRNTSCRN, + KN_SCROLLLOCK, + KN_SEMICOLON, + KN_SLASH, + KN_SPACE, + KN_SQUOTE, + KN_TAB, + KN_UP, + KN_UPLEFT, + KN_UPRIGHT, + + KN_LALT, + KN_LBRACKET, + KN_LCTRL, + KN_LEFT, + KN_LMOUSE, + KN_LSHIFT, + + KN_RALT, + KN_RBRACKET, + KN_RCTRL, + KN_RETURN, + KN_RIGHT, + KN_RMOUSE, + KN_RSHIFT, + + KN_F1, + KN_F2, + KN_F3, + KN_F4, + KN_F5, + KN_F6, + KN_F7, + KN_F8, + KN_F9, + KN_F10, + KN_F11, + KN_F12, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, + KN_CAPSLOCK_BIT = WWKEY_SHIFT_BIT, +} KeyNumType; + + +//extern WWKeyboardClass *_Kbd; +#endif + From 7a18490fa266a37c720f58127fa02b2c0742a7f8 Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 21:40:14 +0200 Subject: [PATCH 14/18] * All lowercase * Fixed radar --- .gitignore | 11 +- code/CMakeLists.txt | 1238 ++-- code/REDALERT/CONQUER.CPP | 11 +- code/REDALERT/DISPLAY.CPP | 2 + code/REDALERT/DISPLAY.H | 2 +- code/REDALERT/GADGET.CPP | 2 +- code/REDALERT/KEY.CPP | 4 +- code/REDALERT/KEY.H | 220 +- code/REDALERT/POWER.CPP | 11 +- code/REDALERT/RADAR.CPP | 1066 +-- code/REDALERT/SHAPEBTN.CPP | 1 + code/REDALERT/SIDEBAR.CPP | 33 +- code/REDALERT/io/UserInput.cpp | 12 +- code/REDALERT/io/UserInput.h | 7 + code/redalert/2keyfram.cpp | 588 ++ code/redalert/_wsproto.cpp | 37 + code/redalert/_wsproto.h | 42 + code/redalert/aadata.cpp | 655 ++ code/redalert/abstract.cpp | 213 + code/redalert/abstract.h | 144 + code/redalert/adata.cpp | 2553 +++++++ code/redalert/adpcm.cpp | 80 + code/redalert/aircraft.cpp | 4437 ++++++++++++ code/redalert/aircraft.h | 252 + code/redalert/anim.cpp | 1221 ++++ code/redalert/anim.h | 189 + code/redalert/audio.cpp | 840 +++ code/redalert/audio.h | 96 + code/redalert/audiomix.h | 14 + code/redalert/audmix.cpp | 396 + code/redalert/b64pipe.cpp | 162 + code/redalert/b64pipe.h | 89 + code/redalert/b64straw.cpp | 114 + code/redalert/b64straw.h | 88 + code/redalert/bar.cpp | 236 + code/redalert/bar.h | 108 + code/redalert/base.cpp | 545 ++ code/redalert/base.h | 127 + code/redalert/base64.cpp | 434 ++ code/redalert/base64.h | 37 + code/redalert/bbdata.cpp | 297 + code/redalert/bdata.cpp | 3941 ++++++++++ code/redalert/bdata_af.h | 34 + code/redalert/bdataxml.cpp | 98 + code/redalert/bench.cpp | 165 + code/redalert/bench.h | 121 + code/redalert/bfiofile.cpp | 984 +++ code/redalert/bfiofile.h | 92 + code/redalert/bigcheck.cpp | 81 + code/redalert/bigcheck.h | 71 + code/redalert/blowfish.cpp | 595 ++ code/redalert/blowfish.h | 114 + code/redalert/blowpipe.cpp | 199 + code/redalert/blowpipe.h | 82 + code/redalert/blwstraw.cpp | 158 + code/redalert/blwstraw.h | 84 + code/redalert/bmp8.cpp | 186 + code/redalert/bmp8.h | 40 + code/redalert/buff.cpp | 217 + code/redalert/buff.h | 96 + code/redalert/bufferx.h | 98 + code/redalert/building.cpp | 6069 ++++++++++++++++ code/redalert/building.h | 364 + code/redalert/bullet.cpp | 1110 +++ code/redalert/bullet.h | 150 + code/redalert/cargo.cpp | 180 + code/redalert/cargo.h | 90 + code/redalert/carry.cpp | 150 + code/redalert/carry.h | 81 + code/redalert/cbn_.h | 153 + code/redalert/ccdde.cpp | 428 ++ code/redalert/ccdde.h | 86 + code/redalert/ccfile.cpp | 692 ++ code/redalert/ccfile.h | 104 + code/redalert/ccini.cpp | 1484 ++++ code/redalert/ccini.h | 117 + code/redalert/ccmpath.cpp | 418 ++ code/redalert/ccptr.cpp | 111 + code/redalert/ccptr.h | 114 + code/redalert/ccten.cpp | 600 ++ code/redalert/cdata.cpp | 3340 +++++++++ code/redalert/cdfile.cpp | 746 ++ code/redalert/cdfile.h | 116 + code/redalert/cell.cpp | 3310 +++++++++ code/redalert/cell.h | 332 + code/redalert/checkbox.cpp | 99 + code/redalert/checkbox.h | 53 + code/redalert/cheklist.cpp | 362 + code/redalert/cheklist.h | 102 + code/redalert/class.cpp | 16 + code/redalert/colrlist.cpp | 275 + code/redalert/colrlist.h | 84 + code/redalert/combat.cpp | 422 ++ code/redalert/combuf.cpp | 1161 +++ code/redalert/combuf.h | 190 + code/redalert/cominit.cpp | 48 + code/redalert/cominit.h | 31 + code/redalert/compat.h | 214 + code/redalert/comqueue.cpp | 1002 +++ code/redalert/comqueue.h | 190 + code/redalert/confdlg.cpp | 244 + code/redalert/confdlg.h | 53 + code/redalert/connect.cpp | 835 +++ code/redalert/connect.h | 278 + code/redalert/connmgr.h | 151 + code/redalert/conquer.cpp | 5894 +++++++++++++++ code/redalert/conquer.h | 571 ++ code/redalert/console.cpp | 305 + code/redalert/const.cpp | 845 +++ code/redalert/control.cpp | 223 + code/redalert/control.h | 87 + code/redalert/coord.cpp | 565 ++ code/redalert/coorda.h | 141 + code/redalert/crate.cpp | 182 + code/redalert/crate.h | 76 + code/redalert/crc.cpp | 133 + code/redalert/crc.h | 118 + code/redalert/crcpipe.cpp | 87 + code/redalert/crcpipe.h | 63 + code/redalert/crcstraw.cpp | 91 + code/redalert/crcstraw.h | 64 + code/redalert/credits.cpp | 236 + code/redalert/credits.h | 72 + code/redalert/crew.cpp | 36 + code/redalert/crew.h | 67 + code/redalert/cstraw.cpp | 97 + code/redalert/cstraw.h | 68 + code/redalert/dde.cpp | 462 ++ code/redalert/dde.h | 172 + code/redalert/debug.cpp | 558 ++ code/redalert/debug.h | 15 + code/redalert/defines.h | 3631 ++++++++++ code/redalert/descdlg.cpp | 160 + code/redalert/descdlg.h | 66 + code/redalert/dial8.cpp | 316 + code/redalert/dial8.h | 73 + code/redalert/dialog.cpp | 1050 +++ code/redalert/dibapi.h | 150 + code/redalert/dibfile.cpp | 702 ++ code/redalert/dibutil.cpp | 1314 ++++ code/redalert/dibutil.h | 38 + code/redalert/display.cpp | 5109 +++++++++++++ code/redalert/display.h | 342 + code/redalert/dllinterface.cpp | 8470 ++++++++++++++++++++++ code/redalert/dllinterface.h | 924 +++ code/redalert/dllinterfaceeditor.cpp | 830 +++ code/redalert/door.cpp | 199 + code/redalert/door.h | 86 + code/redalert/dpmi.cpp | 167 + code/redalert/dpmi.h | 173 + code/redalert/drive.cpp | 2338 ++++++ code/redalert/drive.h | 208 + code/redalert/drop.cpp | 216 + code/redalert/drop.h | 350 + code/redalert/dtable.cpp | 1443 ++++ code/redalert/dynavec.cpp | 300 + code/redalert/edit.cpp | 445 ++ code/redalert/edit.h | 120 + code/redalert/egos.cpp | 921 +++ code/redalert/egos.h | 57 + code/redalert/ending.cpp | 160 + code/redalert/ending.h | 40 + code/redalert/event.cpp | 1067 +++ code/redalert/event.h | 257 + code/redalert/expand.cpp | 542 ++ code/redalert/externs.h | 514 ++ code/redalert/face.cpp | 224 + code/redalert/face.h | 78 + code/redalert/facing.cpp | 180 + code/redalert/facing.h | 77 + code/redalert/factory.cpp | 697 ++ code/redalert/factory.h | 161 + code/redalert/fakesock.h | 50 + code/redalert/field.cpp | 211 + code/redalert/field.h | 82 + code/redalert/filepcx.h | 74 + code/redalert/findpath.cpp | 1307 ++++ code/redalert/fixed.cpp | 245 + code/redalert/fixed.h | 234 + code/redalert/flasher.cpp | 130 + code/redalert/flasher.h | 78 + code/redalert/fly.cpp | 131 + code/redalert/fly.h | 73 + code/redalert/foot.cpp | 2597 +++++++ code/redalert/foot.h | 389 + code/redalert/ftimer.h | 716 ++ code/redalert/function.h | 1199 +++ code/redalert/fuse.cpp | 194 + code/redalert/fuse.h | 89 + code/redalert/gadget.h | 273 + code/redalert/gamedlg.cpp | 517 ++ code/redalert/gamedlg.h | 49 + code/redalert/gauge.cpp | 536 ++ code/redalert/gauge.h | 106 + code/redalert/getcpu.cpp | 103 + code/redalert/globals.cpp | 826 +++ code/redalert/goptions.cpp | 610 ++ code/redalert/goptions.h | 96 + code/redalert/gscreen.cpp | 487 ++ code/redalert/gscreen.h | 136 + code/redalert/hdata.cpp | 523 ++ code/redalert/heap.cpp | 663 ++ code/redalert/heap.h | 195 + code/redalert/help.cpp | 414 ++ code/redalert/help.h | 140 + code/redalert/house.cpp | 8175 +++++++++++++++++++++ code/redalert/house.h | 958 +++ code/redalert/housecolor.h | 58 + code/redalert/hsv.cpp | 208 + code/redalert/hsv.h | 78 + code/redalert/iconlist.cpp | 907 +++ code/redalert/iconlist.h | 189 + code/redalert/idata.cpp | 1530 ++++ code/redalert/image.h | 43 + code/redalert/imgutil.h | 206 + code/redalert/infantry.cpp | 4335 +++++++++++ code/redalert/infantry.h | 239 + code/redalert/ini.cpp | 1287 ++++ code/redalert/ini.h | 159 + code/redalert/inibin.cpp | 36 + code/redalert/inicode.cpp | 363 + code/redalert/init.cpp | 3778 ++++++++++ code/redalert/inline.h | 1066 +++ code/redalert/int.cpp | 43 + code/redalert/int.h | 301 + code/redalert/internet.cpp | 975 +++ code/redalert/internet.h | 53 + code/redalert/interpal.cpp | 465 ++ code/redalert/intro.cpp | 70 + code/redalert/intro.h | 40 + code/redalert/io/eventqueue.cpp | 157 + code/redalert/io/eventqueue.h | 121 + code/redalert/iomap.cpp | 517 ++ code/redalert/ioobj.cpp | 898 +++ code/redalert/ipx.cpp | 1164 +++ code/redalert/ipx.h | 188 + code/redalert/ipx95.cpp | 231 + code/redalert/ipx95.h | 101 + code/redalert/ipxaddr.cpp | 506 ++ code/redalert/ipxaddr.h | 104 + code/redalert/ipxconn.cpp | 876 +++ code/redalert/ipxconn.h | 203 + code/redalert/ipxgconn.cpp | 531 ++ code/redalert/ipxgconn.h | 203 + code/redalert/ipxmgr.cpp | 2066 ++++++ code/redalert/ipxmgr.h | 394 + code/redalert/itable.cpp | 1443 ++++ code/redalert/jshell.cpp | 659 ++ code/redalert/jshell.h | 478 ++ code/redalert/keyboard.h | 649 ++ code/redalert/language.h | 117 + code/redalert/layer.cpp | 161 + code/redalert/layer.h | 64 + code/redalert/lcw.cpp | 159 + code/redalert/lcw.h | 46 + code/redalert/lcwpipe.cpp | 310 + code/redalert/lcwpipe.h | 102 + code/redalert/lcwstraw.cpp | 176 + code/redalert/lcwstraw.h | 103 + code/redalert/lcwuncmp.cpp | 163 + code/redalert/led.h | 43 + code/redalert/license.txt | 712 ++ code/redalert/link.cpp | 398 + code/redalert/link.h | 72 + code/redalert/lint.h | 185 + code/redalert/list.cpp | 969 +++ code/redalert/list.h | 799 ++ code/redalert/listnode.h | 170 + code/redalert/loaddlg.cpp | 787 ++ code/redalert/loaddlg.h | 90 + code/redalert/logic.cpp | 474 ++ code/redalert/logic.h | 58 + code/redalert/lzo.h | 57 + code/redalert/lzo1x.h | 107 + code/redalert/lzo1x_c.cpp | 348 + code/redalert/lzo1x_d.cpp | 205 + code/redalert/lzo_conf.h | 283 + code/redalert/lzoconf.h | 214 + code/redalert/lzopipe.cpp | 318 + code/redalert/lzopipe.h | 101 + code/redalert/lzostraw.cpp | 181 + code/redalert/lzostraw.h | 102 + code/redalert/lzw.cpp | 709 ++ code/redalert/lzw.h | 86 + code/redalert/lzwotraw.cpp | 181 + code/redalert/lzwpipe.cpp | 312 + code/redalert/lzwpipe.h | 102 + code/redalert/lzwstraw.cpp | 177 + code/redalert/lzwstraw.h | 103 + code/redalert/map.cpp | 2284 ++++++ code/redalert/map.h | 183 + code/redalert/mapeddlg.cpp | 2620 +++++++ code/redalert/mapedit.cpp | 2223 ++++++ code/redalert/mapedit.h | 319 + code/redalert/mapedplc.cpp | 1952 +++++ code/redalert/mapedsel.cpp | 585 ++ code/redalert/mapedtm.cpp | 948 +++ code/redalert/mapscript.cpp | 4286 +++++++++++ code/redalert/mapscript.h | 56 + code/redalert/mapsel.cpp | 403 ++ code/redalert/mci.cpp | 362 + code/redalert/mci.h | 105 + code/redalert/mcimovie.cpp | 349 + code/redalert/mcimovie.h | 66 + code/redalert/memcheck.h | 2602 +++++++ code/redalert/menus.cpp | 808 +++ code/redalert/message.h | 44 + code/redalert/miscasm.cpp | 1572 ++++ code/redalert/mission.cpp | 570 ++ code/redalert/mission.h | 199 + code/redalert/mixfile.cpp | 596 ++ code/redalert/mixfile.h | 131 + code/redalert/monoc.cpp | 1132 +++ code/redalert/monoc.h | 244 + code/redalert/mouse.cpp | 389 + code/redalert/mouse.h | 123 + code/redalert/movie.h | 70 + code/redalert/mp.cpp | 2729 +++++++ code/redalert/mp.h | 173 + code/redalert/mpgset.cpp | 606 ++ code/redalert/mpgset.h | 48 + code/redalert/mplayer.cpp | 1174 +++ code/redalert/mplib.cpp | 127 + code/redalert/mpmgrd.cpp | 363 + code/redalert/mpmgrd.h | 80 + code/redalert/mpmgrw.cpp | 297 + code/redalert/mpmgrw.h | 85 + code/redalert/mpu.cpp | 45 + code/redalert/mpu.h | 68 + code/redalert/msgbox.cpp | 530 ++ code/redalert/msgbox.h | 52 + code/redalert/msglist.cpp | 1408 ++++ code/redalert/msglist.h | 211 + code/redalert/netdlg.cpp | 7849 ++++++++++++++++++++ code/redalert/newblit.cpp | 62 + code/redalert/newblit.h | 10 + code/redalert/noseqcon.cpp | 688 ++ code/redalert/noseqcon.h | 126 + code/redalert/nullconn.cpp | 284 + code/redalert/nullconn.h | 147 + code/redalert/nulldlg.cpp | 9642 +++++++++++++++++++++++++ code/redalert/nullmgr.cpp | 2618 +++++++ code/redalert/nullmgr.h | 223 + code/redalert/object.cpp | 2494 +++++++ code/redalert/object.h | 273 + code/redalert/ocidl.h | 8026 ++++++++++++++++++++ code/redalert/odata.cpp | 951 +++ code/redalert/options.cpp | 920 +++ code/redalert/options.h | 157 + code/redalert/overlay.cpp | 381 + code/redalert/overlay.h | 98 + code/redalert/packet.cpp | 463 ++ code/redalert/packet.h | 98 + code/redalert/palettec.cpp | 436 ++ code/redalert/palettec.h | 81 + code/redalert/pipe.cpp | 163 + code/redalert/pipe.h | 89 + code/redalert/pk.cpp | 360 + code/redalert/pk.h | 90 + code/redalert/pkpipe.cpp | 284 + code/redalert/pkpipe.h | 133 + code/redalert/pkstraw.cpp | 301 + code/redalert/pkstraw.h | 122 + code/redalert/power.cpp | 485 ++ code/redalert/power.h | 118 + code/redalert/profile.cpp | 867 +++ code/redalert/queue.cpp | 4561 ++++++++++++ code/redalert/queue.h | 292 + code/redalert/radar.cpp | 2735 +++++++ code/redalert/radar.h | 250 + code/redalert/radio.cpp | 290 + code/redalert/radio.h | 104 + code/redalert/ramfile.cpp | 472 ++ code/redalert/ramfile.h | 110 + code/redalert/rand.cpp | 170 + code/redalert/random.cpp | 180 + code/redalert/random.h | 83 + code/redalert/rawfile.cpp | 1345 ++++ code/redalert/rawfile.h | 332 + code/redalert/rawolapi.cpp | 2099 ++++++ code/redalert/rawolapi.h | 339 + code/redalert/readline.cpp | 87 + code/redalert/readline.h | 23 + code/redalert/rect.cpp | 197 + code/redalert/rect.h | 74 + code/redalert/region.h | 56 + code/redalert/reinf.cpp | 748 ++ code/redalert/resource/redalert.rc | 100 + code/redalert/resource/resource.h | 30 + code/redalert/rgb.cpp | 246 + code/redalert/rgb.h | 140 + code/redalert/rndstraw.cpp | 317 + code/redalert/rndstraw.h | 89 + code/redalert/rng.h | 51 + code/redalert/rotbmp.cpp | 414 ++ code/redalert/rotbmp.h | 112 + code/redalert/rules.cpp | 1072 +++ code/redalert/rules.h | 896 +++ code/redalert/savedlg.h | 68 + code/redalert/saveload.cpp | 1654 +++++ code/redalert/scenario.cpp | 3770 ++++++++++ code/redalert/scenario.h | 342 + code/redalert/score.cpp | 1949 +++++ code/redalert/score.h | 145 + code/redalert/screen.h | 65 + code/redalert/scroll.cpp | 266 + code/redalert/scroll.h | 71 + code/redalert/sdata.cpp | 568 ++ code/redalert/search.h | 686 ++ code/redalert/seditdlg.cpp | 383 + code/redalert/seditdlg.h | 53 + code/redalert/sendfile.cpp | 877 +++ code/redalert/seqconn.cpp | 560 ++ code/redalert/seqconn.h | 108 + code/redalert/session.cpp | 1875 +++++ code/redalert/session.h | 703 ++ code/redalert/sha.cpp | 314 + code/redalert/sha.h | 201 + code/redalert/shape.cpp | 1588 ++++ code/redalert/shapebtn.cpp | 180 + code/redalert/shapebtn.h | 65 + code/redalert/shapipe.cpp | 87 + code/redalert/shapipe.h | 63 + code/redalert/shastraw.cpp | 93 + code/redalert/shastraw.h | 65 + code/redalert/sidebar.cpp | 2474 +++++++ code/redalert/sidebar.h | 398 + code/redalert/sidebarglyphx.cpp | 821 +++ code/redalert/sidebarglyphx.h | 209 + code/redalert/slider.cpp | 413 ++ code/redalert/slider.h | 107 + code/redalert/smudge.cpp | 361 + code/redalert/smudge.h | 96 + code/redalert/sounddlg.cpp | 455 ++ code/redalert/sounddlg.h | 112 + code/redalert/special.cpp | 602 ++ code/redalert/special.h | 115 + code/redalert/sprite.cpp | 326 + code/redalert/stage.h | 80 + code/redalert/startup.cpp | 1011 +++ code/redalert/statbtn.cpp | 270 + code/redalert/statbtn.h | 72 + code/redalert/stats.cpp | 911 +++ code/redalert/straw.cpp | 138 + code/redalert/straw.h | 87 + code/redalert/stub.cpp | 22 + code/redalert/style.h | 81 + code/redalert/super.cpp | 388 + code/redalert/super.h | 89 + code/redalert/surface.cpp | 160 + code/redalert/surface.h | 113 + code/redalert/tab.cpp | 295 + code/redalert/tab.h | 76 + code/redalert/taction.cpp | 902 +++ code/redalert/taction.h | 154 + code/redalert/target.cpp | 941 +++ code/redalert/target.h | 173 + code/redalert/tcpip.cpp | 906 +++ code/redalert/tcpip.h | 197 + code/redalert/tdata.cpp | 931 +++ code/redalert/team.cpp | 3073 ++++++++ code/redalert/team.h | 272 + code/redalert/teamtype.cpp | 1879 +++++ code/redalert/teamtype.h | 282 + code/redalert/techno.cpp | 6911 ++++++++++++++++++ code/redalert/techno.h | 421 ++ code/redalert/template.cpp | 238 + code/redalert/template.h | 93 + code/redalert/tenmgr.cpp | 1118 +++ code/redalert/tenmgr.h | 115 + code/redalert/terrain.cpp | 789 ++ code/redalert/terrain.h | 154 + code/redalert/tevent.cpp | 766 ++ code/redalert/tevent.h | 180 + code/redalert/textbtn.cpp | 353 + code/redalert/textbtn.h | 73 + code/redalert/theme.cpp | 600 ++ code/redalert/theme.h | 89 + code/redalert/tileset.cpp | 436 ++ code/redalert/tilesetxml.cpp | 89 + code/redalert/toggle.cpp | 198 + code/redalert/toggle.h | 79 + code/redalert/tooltip.cpp | 294 + code/redalert/tooltip.h | 74 + code/redalert/tracker.cpp | 131 + code/redalert/trigger.cpp | 529 ++ code/redalert/trigger.h | 129 + code/redalert/trigtype.cpp | 2600 +++++++ code/redalert/trigtype.h | 149 + code/redalert/turret.cpp | 111 + code/redalert/turret.h | 57 + code/redalert/txtlabel.cpp | 98 + code/redalert/txtlabel.h | 69 + code/redalert/txtprnt.cpp | 292 + code/redalert/type.h | 2061 ++++++ code/redalert/udata.cpp | 1416 ++++ code/redalert/udpaddr.cpp | 234 + code/redalert/unit.cpp | 5141 +++++++++++++ code/redalert/unit.h | 250 + code/redalert/unitxml.cpp | 99 + code/redalert/utracker.cpp | 247 + code/redalert/utracker.h | 83 + code/redalert/vdata.cpp | 708 ++ code/redalert/vector.cpp | 672 ++ code/redalert/vector.h | 1000 +++ code/redalert/vectormath.h | 2020 ++++++ code/redalert/version.cpp | 848 +++ code/redalert/version.h | 199 + code/redalert/vessel.cpp | 2425 +++++++ code/redalert/vessel.h | 154 + code/redalert/visudlg.cpp | 413 ++ code/redalert/visudlg.h | 73 + code/redalert/vortex.cpp | 1259 ++++ code/redalert/vortex.h | 270 + code/redalert/vqamovie.cpp | 162 + code/redalert/vqamovie.h | 43 + code/redalert/w95trace.cpp | 126 + code/redalert/w95trace.h | 55 + code/redalert/warhead.cpp | 189 + code/redalert/warhead.h | 117 + code/redalert/watcom.h | 90 + code/redalert/weapon.cpp | 333 + code/redalert/weapon.h | 162 + code/redalert/win32lib/_diptabl.cpp | 55 + code/redalert/win32lib/alloc.cpp | 509 ++ code/redalert/win32lib/audio.h | 159 + code/redalert/win32lib/buffer.cpp | 128 + code/redalert/win32lib/buffer.h | 121 + code/redalert/win32lib/buffglbl.cpp | 80 + code/redalert/win32lib/defines.h | 36 + code/redalert/win32lib/delay.cpp | 59 + code/redalert/win32lib/descmgmt.h | 90 + code/redalert/win32lib/difftb.inc | 1445 ++++ code/redalert/win32lib/dipthong.cpp | 325 + code/redalert/win32lib/dipthong.h | 21 + code/redalert/win32lib/dplay.h | 323 + code/redalert/win32lib/drawbuff.h | 72 + code/redalert/win32lib/drawbuff.inc | 90 + code/redalert/win32lib/drawmisc.cpp | 3533 +++++++++ code/redalert/win32lib/drawrect.cpp | 69 + code/redalert/win32lib/dsetup.h | 71 + code/redalert/win32lib/dsound.h | 380 + code/redalert/win32lib/externs.h | 37 + code/redalert/win32lib/fastfile.h | 39 + code/redalert/win32lib/file.h | 257 + code/redalert/win32lib/filepcx.h | 74 + code/redalert/win32lib/filetemp.h | 57 + code/redalert/win32lib/font.h | 115 + code/redalert/win32lib/function.h | 44 + code/redalert/win32lib/gbuffer.cpp | 555 ++ code/redalert/win32lib/gbuffer.h | 1358 ++++ code/redalert/win32lib/gbuffer.inc | 48 + code/redalert/win32lib/getshape.cpp | 359 + code/redalert/win32lib/iconcach.h | 150 + code/redalert/win32lib/iconset.cpp | 341 + code/redalert/win32lib/iff.cpp | 325 + code/redalert/win32lib/iff.h | 164 + code/redalert/win32lib/indextb.inc | 1445 ++++ code/redalert/win32lib/irandom.cpp | 183 + code/redalert/win32lib/keyboard.h | 675 ++ code/redalert/win32lib/keyboard.inc | 126 + code/redalert/win32lib/keystruc.inc | 159 + code/redalert/win32lib/load.cpp | 375 + code/redalert/win32lib/loadfont.cpp | 137 + code/redalert/win32lib/mcgaprim.inc | 119 + code/redalert/win32lib/memflag.h | 106 + code/redalert/win32lib/misc.h | 264 + code/redalert/win32lib/modemreg.h | 69 + code/redalert/win32lib/mono.h | 182 + code/redalert/win32lib/mouse.h | 150 + code/redalert/win32lib/mouse.inc | 64 + code/redalert/win32lib/mouseshape.cpp | 304 + code/redalert/win32lib/mouseshape.h | 67 + code/redalert/win32lib/mouseww.cpp | 587 ++ code/redalert/win32lib/nybbtb.inc | 56 + code/redalert/win32lib/palette.cpp | 381 + code/redalert/win32lib/palette.h | 82 + code/redalert/win32lib/playcd.h | 306 + code/redalert/win32lib/profile.h | 102 + code/redalert/win32lib/profile.inc | 41 + code/redalert/win32lib/rawfile.h | 254 + code/redalert/win32lib/shape.h | 184 + code/redalert/win32lib/shape.inc | 212 + code/redalert/win32lib/sos.h | 562 ++ code/redalert/win32lib/soscomp.h | 80 + code/redalert/win32lib/sosdata.h | 125 + code/redalert/win32lib/sosdefs.h | 80 + code/redalert/win32lib/sosfnct.h | 216 + code/redalert/win32lib/sosres.h | 126 + code/redalert/win32lib/sound.h | 52 + code/redalert/win32lib/soundint.h | 289 + code/redalert/win32lib/stamp.inc | 56 + code/redalert/win32lib/structs.h | 34 + code/redalert/win32lib/svgaprim.inc | 74 + code/redalert/win32lib/tile.h | 91 + code/redalert/win32lib/timer.cpp | 200 + code/redalert/win32lib/timer.h | 198 + code/redalert/win32lib/timerdwn.cpp | 123 + code/redalert/win32lib/timerini.cpp | 314 + code/redalert/win32lib/tobuff.cpp | 70 + code/redalert/win32lib/video.h | 216 + code/redalert/win32lib/wincomm.h | 454 ++ code/redalert/win32lib/windows.cpp | 973 +++ code/redalert/win32lib/winhide.cpp | 94 + code/redalert/win32lib/writepcx.cpp | 177 + code/redalert/win32lib/wsa.cpp | 1153 +++ code/redalert/win32lib/wsa.h | 160 + code/redalert/win32lib/ww_win.h | 93 + code/redalert/win32lib/wwfile.h | 68 + code/redalert/win32lib/wwlib32.h | 65 + code/redalert/win32lib/wwmem.h | 67 + code/redalert/win32lib/wwmem.inc | 40 + code/redalert/win32lib/wwstd.h | 342 + code/redalert/winstub.cpp | 1231 ++++ code/redalert/wol_cgam.cpp | 386 + code/redalert/wol_chat.cpp | 1578 ++++ code/redalert/wol_dnld.cpp | 260 + code/redalert/wol_gsup.cpp | 3500 +++++++++ code/redalert/wol_gsup.h | 361 + code/redalert/wol_logn.cpp | 662 ++ code/redalert/wol_main.cpp | 284 + code/redalert/wol_opt.cpp | 254 + code/redalert/wolapiob.cpp | 3432 +++++++++ code/redalert/wolapiob.h | 417 ++ code/redalert/woldebug.h | 32 + code/redalert/woledit.cpp | 164 + code/redalert/woledit.h | 40 + code/redalert/wolstrng.cpp | 804 +++ code/redalert/wolstrng.h | 209 + code/redalert/wsnwlink.h | 294 + code/redalert/wspipx.cpp | 446 ++ code/redalert/wspipx.h | 103 + code/redalert/wsproto.cpp | 591 ++ code/redalert/wsproto.h | 189 + code/redalert/wspudp.cpp | 428 ++ code/redalert/wspudp.h | 83 + code/redalert/wwalloc.h | 65 + code/redalert/wwfile.h | 93 + code/redalert/xpipe.cpp | 163 + code/redalert/xpipe.h | 92 + code/redalert/xstraw.cpp | 148 + code/redalert/xstraw.h | 88 + imgui.ini | 15 + 643 files changed, 360913 insertions(+), 1271 deletions(-) create mode 100644 code/redalert/2keyfram.cpp create mode 100644 code/redalert/_wsproto.cpp create mode 100644 code/redalert/_wsproto.h create mode 100644 code/redalert/aadata.cpp create mode 100644 code/redalert/abstract.cpp create mode 100644 code/redalert/abstract.h create mode 100644 code/redalert/adata.cpp create mode 100644 code/redalert/adpcm.cpp create mode 100644 code/redalert/aircraft.cpp create mode 100644 code/redalert/aircraft.h create mode 100644 code/redalert/anim.cpp create mode 100644 code/redalert/anim.h create mode 100644 code/redalert/audio.cpp create mode 100644 code/redalert/audio.h create mode 100644 code/redalert/audiomix.h create mode 100644 code/redalert/audmix.cpp create mode 100644 code/redalert/b64pipe.cpp create mode 100644 code/redalert/b64pipe.h create mode 100644 code/redalert/b64straw.cpp create mode 100644 code/redalert/b64straw.h create mode 100644 code/redalert/bar.cpp create mode 100644 code/redalert/bar.h create mode 100644 code/redalert/base.cpp create mode 100644 code/redalert/base.h create mode 100644 code/redalert/base64.cpp create mode 100644 code/redalert/base64.h create mode 100644 code/redalert/bbdata.cpp create mode 100644 code/redalert/bdata.cpp create mode 100644 code/redalert/bdata_af.h create mode 100644 code/redalert/bdataxml.cpp create mode 100644 code/redalert/bench.cpp create mode 100644 code/redalert/bench.h create mode 100644 code/redalert/bfiofile.cpp create mode 100644 code/redalert/bfiofile.h create mode 100644 code/redalert/bigcheck.cpp create mode 100644 code/redalert/bigcheck.h create mode 100644 code/redalert/blowfish.cpp create mode 100644 code/redalert/blowfish.h create mode 100644 code/redalert/blowpipe.cpp create mode 100644 code/redalert/blowpipe.h create mode 100644 code/redalert/blwstraw.cpp create mode 100644 code/redalert/blwstraw.h create mode 100644 code/redalert/bmp8.cpp create mode 100644 code/redalert/bmp8.h create mode 100644 code/redalert/buff.cpp create mode 100644 code/redalert/buff.h create mode 100644 code/redalert/bufferx.h create mode 100644 code/redalert/building.cpp create mode 100644 code/redalert/building.h create mode 100644 code/redalert/bullet.cpp create mode 100644 code/redalert/bullet.h create mode 100644 code/redalert/cargo.cpp create mode 100644 code/redalert/cargo.h create mode 100644 code/redalert/carry.cpp create mode 100644 code/redalert/carry.h create mode 100644 code/redalert/cbn_.h create mode 100644 code/redalert/ccdde.cpp create mode 100644 code/redalert/ccdde.h create mode 100644 code/redalert/ccfile.cpp create mode 100644 code/redalert/ccfile.h create mode 100644 code/redalert/ccini.cpp create mode 100644 code/redalert/ccini.h create mode 100644 code/redalert/ccmpath.cpp create mode 100644 code/redalert/ccptr.cpp create mode 100644 code/redalert/ccptr.h create mode 100644 code/redalert/ccten.cpp create mode 100644 code/redalert/cdata.cpp create mode 100644 code/redalert/cdfile.cpp create mode 100644 code/redalert/cdfile.h create mode 100644 code/redalert/cell.cpp create mode 100644 code/redalert/cell.h create mode 100644 code/redalert/checkbox.cpp create mode 100644 code/redalert/checkbox.h create mode 100644 code/redalert/cheklist.cpp create mode 100644 code/redalert/cheklist.h create mode 100644 code/redalert/class.cpp create mode 100644 code/redalert/colrlist.cpp create mode 100644 code/redalert/colrlist.h create mode 100644 code/redalert/combat.cpp create mode 100644 code/redalert/combuf.cpp create mode 100644 code/redalert/combuf.h create mode 100644 code/redalert/cominit.cpp create mode 100644 code/redalert/cominit.h create mode 100644 code/redalert/compat.h create mode 100644 code/redalert/comqueue.cpp create mode 100644 code/redalert/comqueue.h create mode 100644 code/redalert/confdlg.cpp create mode 100644 code/redalert/confdlg.h create mode 100644 code/redalert/connect.cpp create mode 100644 code/redalert/connect.h create mode 100644 code/redalert/connmgr.h create mode 100644 code/redalert/conquer.cpp create mode 100644 code/redalert/conquer.h create mode 100644 code/redalert/console.cpp create mode 100644 code/redalert/const.cpp create mode 100644 code/redalert/control.cpp create mode 100644 code/redalert/control.h create mode 100644 code/redalert/coord.cpp create mode 100644 code/redalert/coorda.h create mode 100644 code/redalert/crate.cpp create mode 100644 code/redalert/crate.h create mode 100644 code/redalert/crc.cpp create mode 100644 code/redalert/crc.h create mode 100644 code/redalert/crcpipe.cpp create mode 100644 code/redalert/crcpipe.h create mode 100644 code/redalert/crcstraw.cpp create mode 100644 code/redalert/crcstraw.h create mode 100644 code/redalert/credits.cpp create mode 100644 code/redalert/credits.h create mode 100644 code/redalert/crew.cpp create mode 100644 code/redalert/crew.h create mode 100644 code/redalert/cstraw.cpp create mode 100644 code/redalert/cstraw.h create mode 100644 code/redalert/dde.cpp create mode 100644 code/redalert/dde.h create mode 100644 code/redalert/debug.cpp create mode 100644 code/redalert/debug.h create mode 100644 code/redalert/defines.h create mode 100644 code/redalert/descdlg.cpp create mode 100644 code/redalert/descdlg.h create mode 100644 code/redalert/dial8.cpp create mode 100644 code/redalert/dial8.h create mode 100644 code/redalert/dialog.cpp create mode 100644 code/redalert/dibapi.h create mode 100644 code/redalert/dibfile.cpp create mode 100644 code/redalert/dibutil.cpp create mode 100644 code/redalert/dibutil.h create mode 100644 code/redalert/display.cpp create mode 100644 code/redalert/display.h create mode 100644 code/redalert/dllinterface.cpp create mode 100644 code/redalert/dllinterface.h create mode 100644 code/redalert/dllinterfaceeditor.cpp create mode 100644 code/redalert/door.cpp create mode 100644 code/redalert/door.h create mode 100644 code/redalert/dpmi.cpp create mode 100644 code/redalert/dpmi.h create mode 100644 code/redalert/drive.cpp create mode 100644 code/redalert/drive.h create mode 100644 code/redalert/drop.cpp create mode 100644 code/redalert/drop.h create mode 100644 code/redalert/dtable.cpp create mode 100644 code/redalert/dynavec.cpp create mode 100644 code/redalert/edit.cpp create mode 100644 code/redalert/edit.h create mode 100644 code/redalert/egos.cpp create mode 100644 code/redalert/egos.h create mode 100644 code/redalert/ending.cpp create mode 100644 code/redalert/ending.h create mode 100644 code/redalert/event.cpp create mode 100644 code/redalert/event.h create mode 100644 code/redalert/expand.cpp create mode 100644 code/redalert/externs.h create mode 100644 code/redalert/face.cpp create mode 100644 code/redalert/face.h create mode 100644 code/redalert/facing.cpp create mode 100644 code/redalert/facing.h create mode 100644 code/redalert/factory.cpp create mode 100644 code/redalert/factory.h create mode 100644 code/redalert/fakesock.h create mode 100644 code/redalert/field.cpp create mode 100644 code/redalert/field.h create mode 100644 code/redalert/filepcx.h create mode 100644 code/redalert/findpath.cpp create mode 100644 code/redalert/fixed.cpp create mode 100644 code/redalert/fixed.h create mode 100644 code/redalert/flasher.cpp create mode 100644 code/redalert/flasher.h create mode 100644 code/redalert/fly.cpp create mode 100644 code/redalert/fly.h create mode 100644 code/redalert/foot.cpp create mode 100644 code/redalert/foot.h create mode 100644 code/redalert/ftimer.h create mode 100644 code/redalert/function.h create mode 100644 code/redalert/fuse.cpp create mode 100644 code/redalert/fuse.h create mode 100644 code/redalert/gadget.h create mode 100644 code/redalert/gamedlg.cpp create mode 100644 code/redalert/gamedlg.h create mode 100644 code/redalert/gauge.cpp create mode 100644 code/redalert/gauge.h create mode 100644 code/redalert/getcpu.cpp create mode 100644 code/redalert/globals.cpp create mode 100644 code/redalert/goptions.cpp create mode 100644 code/redalert/goptions.h create mode 100644 code/redalert/gscreen.cpp create mode 100644 code/redalert/gscreen.h create mode 100644 code/redalert/hdata.cpp create mode 100644 code/redalert/heap.cpp create mode 100644 code/redalert/heap.h create mode 100644 code/redalert/help.cpp create mode 100644 code/redalert/help.h create mode 100644 code/redalert/house.cpp create mode 100644 code/redalert/house.h create mode 100644 code/redalert/housecolor.h create mode 100644 code/redalert/hsv.cpp create mode 100644 code/redalert/hsv.h create mode 100644 code/redalert/iconlist.cpp create mode 100644 code/redalert/iconlist.h create mode 100644 code/redalert/idata.cpp create mode 100644 code/redalert/image.h create mode 100644 code/redalert/imgutil.h create mode 100644 code/redalert/infantry.cpp create mode 100644 code/redalert/infantry.h create mode 100644 code/redalert/ini.cpp create mode 100644 code/redalert/ini.h create mode 100644 code/redalert/inibin.cpp create mode 100644 code/redalert/inicode.cpp create mode 100644 code/redalert/init.cpp create mode 100644 code/redalert/inline.h create mode 100644 code/redalert/int.cpp create mode 100644 code/redalert/int.h create mode 100644 code/redalert/internet.cpp create mode 100644 code/redalert/internet.h create mode 100644 code/redalert/interpal.cpp create mode 100644 code/redalert/intro.cpp create mode 100644 code/redalert/intro.h create mode 100644 code/redalert/io/eventqueue.cpp create mode 100644 code/redalert/io/eventqueue.h create mode 100644 code/redalert/iomap.cpp create mode 100644 code/redalert/ioobj.cpp create mode 100644 code/redalert/ipx.cpp create mode 100644 code/redalert/ipx.h create mode 100644 code/redalert/ipx95.cpp create mode 100644 code/redalert/ipx95.h create mode 100644 code/redalert/ipxaddr.cpp create mode 100644 code/redalert/ipxaddr.h create mode 100644 code/redalert/ipxconn.cpp create mode 100644 code/redalert/ipxconn.h create mode 100644 code/redalert/ipxgconn.cpp create mode 100644 code/redalert/ipxgconn.h create mode 100644 code/redalert/ipxmgr.cpp create mode 100644 code/redalert/ipxmgr.h create mode 100644 code/redalert/itable.cpp create mode 100644 code/redalert/jshell.cpp create mode 100644 code/redalert/jshell.h create mode 100644 code/redalert/keyboard.h create mode 100644 code/redalert/language.h create mode 100644 code/redalert/layer.cpp create mode 100644 code/redalert/layer.h create mode 100644 code/redalert/lcw.cpp create mode 100644 code/redalert/lcw.h create mode 100644 code/redalert/lcwpipe.cpp create mode 100644 code/redalert/lcwpipe.h create mode 100644 code/redalert/lcwstraw.cpp create mode 100644 code/redalert/lcwstraw.h create mode 100644 code/redalert/lcwuncmp.cpp create mode 100644 code/redalert/led.h create mode 100644 code/redalert/license.txt create mode 100644 code/redalert/link.cpp create mode 100644 code/redalert/link.h create mode 100644 code/redalert/lint.h create mode 100644 code/redalert/list.cpp create mode 100644 code/redalert/list.h create mode 100644 code/redalert/listnode.h create mode 100644 code/redalert/loaddlg.cpp create mode 100644 code/redalert/loaddlg.h create mode 100644 code/redalert/logic.cpp create mode 100644 code/redalert/logic.h create mode 100644 code/redalert/lzo.h create mode 100644 code/redalert/lzo1x.h create mode 100644 code/redalert/lzo1x_c.cpp create mode 100644 code/redalert/lzo1x_d.cpp create mode 100644 code/redalert/lzo_conf.h create mode 100644 code/redalert/lzoconf.h create mode 100644 code/redalert/lzopipe.cpp create mode 100644 code/redalert/lzopipe.h create mode 100644 code/redalert/lzostraw.cpp create mode 100644 code/redalert/lzostraw.h create mode 100644 code/redalert/lzw.cpp create mode 100644 code/redalert/lzw.h create mode 100644 code/redalert/lzwotraw.cpp create mode 100644 code/redalert/lzwpipe.cpp create mode 100644 code/redalert/lzwpipe.h create mode 100644 code/redalert/lzwstraw.cpp create mode 100644 code/redalert/lzwstraw.h create mode 100644 code/redalert/map.cpp create mode 100644 code/redalert/map.h create mode 100644 code/redalert/mapeddlg.cpp create mode 100644 code/redalert/mapedit.cpp create mode 100644 code/redalert/mapedit.h create mode 100644 code/redalert/mapedplc.cpp create mode 100644 code/redalert/mapedsel.cpp create mode 100644 code/redalert/mapedtm.cpp create mode 100644 code/redalert/mapscript.cpp create mode 100644 code/redalert/mapscript.h create mode 100644 code/redalert/mapsel.cpp create mode 100644 code/redalert/mci.cpp create mode 100644 code/redalert/mci.h create mode 100644 code/redalert/mcimovie.cpp create mode 100644 code/redalert/mcimovie.h create mode 100644 code/redalert/memcheck.h create mode 100644 code/redalert/menus.cpp create mode 100644 code/redalert/message.h create mode 100644 code/redalert/miscasm.cpp create mode 100644 code/redalert/mission.cpp create mode 100644 code/redalert/mission.h create mode 100644 code/redalert/mixfile.cpp create mode 100644 code/redalert/mixfile.h create mode 100644 code/redalert/monoc.cpp create mode 100644 code/redalert/monoc.h create mode 100644 code/redalert/mouse.cpp create mode 100644 code/redalert/mouse.h create mode 100644 code/redalert/movie.h create mode 100644 code/redalert/mp.cpp create mode 100644 code/redalert/mp.h create mode 100644 code/redalert/mpgset.cpp create mode 100644 code/redalert/mpgset.h create mode 100644 code/redalert/mplayer.cpp create mode 100644 code/redalert/mplib.cpp create mode 100644 code/redalert/mpmgrd.cpp create mode 100644 code/redalert/mpmgrd.h create mode 100644 code/redalert/mpmgrw.cpp create mode 100644 code/redalert/mpmgrw.h create mode 100644 code/redalert/mpu.cpp create mode 100644 code/redalert/mpu.h create mode 100644 code/redalert/msgbox.cpp create mode 100644 code/redalert/msgbox.h create mode 100644 code/redalert/msglist.cpp create mode 100644 code/redalert/msglist.h create mode 100644 code/redalert/netdlg.cpp create mode 100644 code/redalert/newblit.cpp create mode 100644 code/redalert/newblit.h create mode 100644 code/redalert/noseqcon.cpp create mode 100644 code/redalert/noseqcon.h create mode 100644 code/redalert/nullconn.cpp create mode 100644 code/redalert/nullconn.h create mode 100644 code/redalert/nulldlg.cpp create mode 100644 code/redalert/nullmgr.cpp create mode 100644 code/redalert/nullmgr.h create mode 100644 code/redalert/object.cpp create mode 100644 code/redalert/object.h create mode 100644 code/redalert/ocidl.h create mode 100644 code/redalert/odata.cpp create mode 100644 code/redalert/options.cpp create mode 100644 code/redalert/options.h create mode 100644 code/redalert/overlay.cpp create mode 100644 code/redalert/overlay.h create mode 100644 code/redalert/packet.cpp create mode 100644 code/redalert/packet.h create mode 100644 code/redalert/palettec.cpp create mode 100644 code/redalert/palettec.h create mode 100644 code/redalert/pipe.cpp create mode 100644 code/redalert/pipe.h create mode 100644 code/redalert/pk.cpp create mode 100644 code/redalert/pk.h create mode 100644 code/redalert/pkpipe.cpp create mode 100644 code/redalert/pkpipe.h create mode 100644 code/redalert/pkstraw.cpp create mode 100644 code/redalert/pkstraw.h create mode 100644 code/redalert/power.cpp create mode 100644 code/redalert/power.h create mode 100644 code/redalert/profile.cpp create mode 100644 code/redalert/queue.cpp create mode 100644 code/redalert/queue.h create mode 100644 code/redalert/radar.cpp create mode 100644 code/redalert/radar.h create mode 100644 code/redalert/radio.cpp create mode 100644 code/redalert/radio.h create mode 100644 code/redalert/ramfile.cpp create mode 100644 code/redalert/ramfile.h create mode 100644 code/redalert/rand.cpp create mode 100644 code/redalert/random.cpp create mode 100644 code/redalert/random.h create mode 100644 code/redalert/rawfile.cpp create mode 100644 code/redalert/rawfile.h create mode 100644 code/redalert/rawolapi.cpp create mode 100644 code/redalert/rawolapi.h create mode 100644 code/redalert/readline.cpp create mode 100644 code/redalert/readline.h create mode 100644 code/redalert/rect.cpp create mode 100644 code/redalert/rect.h create mode 100644 code/redalert/region.h create mode 100644 code/redalert/reinf.cpp create mode 100644 code/redalert/resource/redalert.rc create mode 100644 code/redalert/resource/resource.h create mode 100644 code/redalert/rgb.cpp create mode 100644 code/redalert/rgb.h create mode 100644 code/redalert/rndstraw.cpp create mode 100644 code/redalert/rndstraw.h create mode 100644 code/redalert/rng.h create mode 100644 code/redalert/rotbmp.cpp create mode 100644 code/redalert/rotbmp.h create mode 100644 code/redalert/rules.cpp create mode 100644 code/redalert/rules.h create mode 100644 code/redalert/savedlg.h create mode 100644 code/redalert/saveload.cpp create mode 100644 code/redalert/scenario.cpp create mode 100644 code/redalert/scenario.h create mode 100644 code/redalert/score.cpp create mode 100644 code/redalert/score.h create mode 100644 code/redalert/screen.h create mode 100644 code/redalert/scroll.cpp create mode 100644 code/redalert/scroll.h create mode 100644 code/redalert/sdata.cpp create mode 100644 code/redalert/search.h create mode 100644 code/redalert/seditdlg.cpp create mode 100644 code/redalert/seditdlg.h create mode 100644 code/redalert/sendfile.cpp create mode 100644 code/redalert/seqconn.cpp create mode 100644 code/redalert/seqconn.h create mode 100644 code/redalert/session.cpp create mode 100644 code/redalert/session.h create mode 100644 code/redalert/sha.cpp create mode 100644 code/redalert/sha.h create mode 100644 code/redalert/shape.cpp create mode 100644 code/redalert/shapebtn.cpp create mode 100644 code/redalert/shapebtn.h create mode 100644 code/redalert/shapipe.cpp create mode 100644 code/redalert/shapipe.h create mode 100644 code/redalert/shastraw.cpp create mode 100644 code/redalert/shastraw.h create mode 100644 code/redalert/sidebar.cpp create mode 100644 code/redalert/sidebar.h create mode 100644 code/redalert/sidebarglyphx.cpp create mode 100644 code/redalert/sidebarglyphx.h create mode 100644 code/redalert/slider.cpp create mode 100644 code/redalert/slider.h create mode 100644 code/redalert/smudge.cpp create mode 100644 code/redalert/smudge.h create mode 100644 code/redalert/sounddlg.cpp create mode 100644 code/redalert/sounddlg.h create mode 100644 code/redalert/special.cpp create mode 100644 code/redalert/special.h create mode 100644 code/redalert/sprite.cpp create mode 100644 code/redalert/stage.h create mode 100644 code/redalert/startup.cpp create mode 100644 code/redalert/statbtn.cpp create mode 100644 code/redalert/statbtn.h create mode 100644 code/redalert/stats.cpp create mode 100644 code/redalert/straw.cpp create mode 100644 code/redalert/straw.h create mode 100644 code/redalert/stub.cpp create mode 100644 code/redalert/style.h create mode 100644 code/redalert/super.cpp create mode 100644 code/redalert/super.h create mode 100644 code/redalert/surface.cpp create mode 100644 code/redalert/surface.h create mode 100644 code/redalert/tab.cpp create mode 100644 code/redalert/tab.h create mode 100644 code/redalert/taction.cpp create mode 100644 code/redalert/taction.h create mode 100644 code/redalert/target.cpp create mode 100644 code/redalert/target.h create mode 100644 code/redalert/tcpip.cpp create mode 100644 code/redalert/tcpip.h create mode 100644 code/redalert/tdata.cpp create mode 100644 code/redalert/team.cpp create mode 100644 code/redalert/team.h create mode 100644 code/redalert/teamtype.cpp create mode 100644 code/redalert/teamtype.h create mode 100644 code/redalert/techno.cpp create mode 100644 code/redalert/techno.h create mode 100644 code/redalert/template.cpp create mode 100644 code/redalert/template.h create mode 100644 code/redalert/tenmgr.cpp create mode 100644 code/redalert/tenmgr.h create mode 100644 code/redalert/terrain.cpp create mode 100644 code/redalert/terrain.h create mode 100644 code/redalert/tevent.cpp create mode 100644 code/redalert/tevent.h create mode 100644 code/redalert/textbtn.cpp create mode 100644 code/redalert/textbtn.h create mode 100644 code/redalert/theme.cpp create mode 100644 code/redalert/theme.h create mode 100644 code/redalert/tileset.cpp create mode 100644 code/redalert/tilesetxml.cpp create mode 100644 code/redalert/toggle.cpp create mode 100644 code/redalert/toggle.h create mode 100644 code/redalert/tooltip.cpp create mode 100644 code/redalert/tooltip.h create mode 100644 code/redalert/tracker.cpp create mode 100644 code/redalert/trigger.cpp create mode 100644 code/redalert/trigger.h create mode 100644 code/redalert/trigtype.cpp create mode 100644 code/redalert/trigtype.h create mode 100644 code/redalert/turret.cpp create mode 100644 code/redalert/turret.h create mode 100644 code/redalert/txtlabel.cpp create mode 100644 code/redalert/txtlabel.h create mode 100644 code/redalert/txtprnt.cpp create mode 100644 code/redalert/type.h create mode 100644 code/redalert/udata.cpp create mode 100644 code/redalert/udpaddr.cpp create mode 100644 code/redalert/unit.cpp create mode 100644 code/redalert/unit.h create mode 100644 code/redalert/unitxml.cpp create mode 100644 code/redalert/utracker.cpp create mode 100644 code/redalert/utracker.h create mode 100644 code/redalert/vdata.cpp create mode 100644 code/redalert/vector.cpp create mode 100644 code/redalert/vector.h create mode 100644 code/redalert/vectormath.h create mode 100644 code/redalert/version.cpp create mode 100644 code/redalert/version.h create mode 100644 code/redalert/vessel.cpp create mode 100644 code/redalert/vessel.h create mode 100644 code/redalert/visudlg.cpp create mode 100644 code/redalert/visudlg.h create mode 100644 code/redalert/vortex.cpp create mode 100644 code/redalert/vortex.h create mode 100644 code/redalert/vqamovie.cpp create mode 100644 code/redalert/vqamovie.h create mode 100644 code/redalert/w95trace.cpp create mode 100644 code/redalert/w95trace.h create mode 100644 code/redalert/warhead.cpp create mode 100644 code/redalert/warhead.h create mode 100644 code/redalert/watcom.h create mode 100644 code/redalert/weapon.cpp create mode 100644 code/redalert/weapon.h create mode 100644 code/redalert/win32lib/_diptabl.cpp create mode 100644 code/redalert/win32lib/alloc.cpp create mode 100644 code/redalert/win32lib/audio.h create mode 100644 code/redalert/win32lib/buffer.cpp create mode 100644 code/redalert/win32lib/buffer.h create mode 100644 code/redalert/win32lib/buffglbl.cpp create mode 100644 code/redalert/win32lib/defines.h create mode 100644 code/redalert/win32lib/delay.cpp create mode 100644 code/redalert/win32lib/descmgmt.h create mode 100644 code/redalert/win32lib/difftb.inc create mode 100644 code/redalert/win32lib/dipthong.cpp create mode 100644 code/redalert/win32lib/dipthong.h create mode 100644 code/redalert/win32lib/dplay.h create mode 100644 code/redalert/win32lib/drawbuff.h create mode 100644 code/redalert/win32lib/drawbuff.inc create mode 100644 code/redalert/win32lib/drawmisc.cpp create mode 100644 code/redalert/win32lib/drawrect.cpp create mode 100644 code/redalert/win32lib/dsetup.h create mode 100644 code/redalert/win32lib/dsound.h create mode 100644 code/redalert/win32lib/externs.h create mode 100644 code/redalert/win32lib/fastfile.h create mode 100644 code/redalert/win32lib/file.h create mode 100644 code/redalert/win32lib/filepcx.h create mode 100644 code/redalert/win32lib/filetemp.h create mode 100644 code/redalert/win32lib/font.h create mode 100644 code/redalert/win32lib/function.h create mode 100644 code/redalert/win32lib/gbuffer.cpp create mode 100644 code/redalert/win32lib/gbuffer.h create mode 100644 code/redalert/win32lib/gbuffer.inc create mode 100644 code/redalert/win32lib/getshape.cpp create mode 100644 code/redalert/win32lib/iconcach.h create mode 100644 code/redalert/win32lib/iconset.cpp create mode 100644 code/redalert/win32lib/iff.cpp create mode 100644 code/redalert/win32lib/iff.h create mode 100644 code/redalert/win32lib/indextb.inc create mode 100644 code/redalert/win32lib/irandom.cpp create mode 100644 code/redalert/win32lib/keyboard.h create mode 100644 code/redalert/win32lib/keyboard.inc create mode 100644 code/redalert/win32lib/keystruc.inc create mode 100644 code/redalert/win32lib/load.cpp create mode 100644 code/redalert/win32lib/loadfont.cpp create mode 100644 code/redalert/win32lib/mcgaprim.inc create mode 100644 code/redalert/win32lib/memflag.h create mode 100644 code/redalert/win32lib/misc.h create mode 100644 code/redalert/win32lib/modemreg.h create mode 100644 code/redalert/win32lib/mono.h create mode 100644 code/redalert/win32lib/mouse.h create mode 100644 code/redalert/win32lib/mouse.inc create mode 100644 code/redalert/win32lib/mouseshape.cpp create mode 100644 code/redalert/win32lib/mouseshape.h create mode 100644 code/redalert/win32lib/mouseww.cpp create mode 100644 code/redalert/win32lib/nybbtb.inc create mode 100644 code/redalert/win32lib/palette.cpp create mode 100644 code/redalert/win32lib/palette.h create mode 100644 code/redalert/win32lib/playcd.h create mode 100644 code/redalert/win32lib/profile.h create mode 100644 code/redalert/win32lib/profile.inc create mode 100644 code/redalert/win32lib/rawfile.h create mode 100644 code/redalert/win32lib/shape.h create mode 100644 code/redalert/win32lib/shape.inc create mode 100644 code/redalert/win32lib/sos.h create mode 100644 code/redalert/win32lib/soscomp.h create mode 100644 code/redalert/win32lib/sosdata.h create mode 100644 code/redalert/win32lib/sosdefs.h create mode 100644 code/redalert/win32lib/sosfnct.h create mode 100644 code/redalert/win32lib/sosres.h create mode 100644 code/redalert/win32lib/sound.h create mode 100644 code/redalert/win32lib/soundint.h create mode 100644 code/redalert/win32lib/stamp.inc create mode 100644 code/redalert/win32lib/structs.h create mode 100644 code/redalert/win32lib/svgaprim.inc create mode 100644 code/redalert/win32lib/tile.h create mode 100644 code/redalert/win32lib/timer.cpp create mode 100644 code/redalert/win32lib/timer.h create mode 100644 code/redalert/win32lib/timerdwn.cpp create mode 100644 code/redalert/win32lib/timerini.cpp create mode 100644 code/redalert/win32lib/tobuff.cpp create mode 100644 code/redalert/win32lib/video.h create mode 100644 code/redalert/win32lib/wincomm.h create mode 100644 code/redalert/win32lib/windows.cpp create mode 100644 code/redalert/win32lib/winhide.cpp create mode 100644 code/redalert/win32lib/writepcx.cpp create mode 100644 code/redalert/win32lib/wsa.cpp create mode 100644 code/redalert/win32lib/wsa.h create mode 100644 code/redalert/win32lib/ww_win.h create mode 100644 code/redalert/win32lib/wwfile.h create mode 100644 code/redalert/win32lib/wwlib32.h create mode 100644 code/redalert/win32lib/wwmem.h create mode 100644 code/redalert/win32lib/wwmem.inc create mode 100644 code/redalert/win32lib/wwstd.h create mode 100644 code/redalert/winstub.cpp create mode 100644 code/redalert/wol_cgam.cpp create mode 100644 code/redalert/wol_chat.cpp create mode 100644 code/redalert/wol_dnld.cpp create mode 100644 code/redalert/wol_gsup.cpp create mode 100644 code/redalert/wol_gsup.h create mode 100644 code/redalert/wol_logn.cpp create mode 100644 code/redalert/wol_main.cpp create mode 100644 code/redalert/wol_opt.cpp create mode 100644 code/redalert/wolapiob.cpp create mode 100644 code/redalert/wolapiob.h create mode 100644 code/redalert/woldebug.h create mode 100644 code/redalert/woledit.cpp create mode 100644 code/redalert/woledit.h create mode 100644 code/redalert/wolstrng.cpp create mode 100644 code/redalert/wolstrng.h create mode 100644 code/redalert/wsnwlink.h create mode 100644 code/redalert/wspipx.cpp create mode 100644 code/redalert/wspipx.h create mode 100644 code/redalert/wsproto.cpp create mode 100644 code/redalert/wsproto.h create mode 100644 code/redalert/wspudp.cpp create mode 100644 code/redalert/wspudp.h create mode 100644 code/redalert/wwalloc.h create mode 100644 code/redalert/wwfile.h create mode 100644 code/redalert/xpipe.cpp create mode 100644 code/redalert/xpipe.h create mode 100644 code/redalert/xstraw.cpp create mode 100644 code/redalert/xstraw.h create mode 100644 imgui.ini diff --git a/.gitignore b/.gitignore index 4b893ed..f713aac 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,10 @@ /fonts /sound /ui -/*.mix -/*.pal -/*.ini /*.exe /*.ilk -/*.dat -/savegame* - +/*.MIX +/*.PAL +/*.INI +/*.DAT +/SAVEGAME* \ No newline at end of file diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e470dad..c6b8543 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1,7 +1,7 @@ # CMakeList.txt : CMake project for Red Alert cmake_minimum_required (VERSION 3.8) -project(RedAlert ASM CXX C) +project(redalert ASM CXX C) enable_language(ASM) set(CMAKE_CXX_FLAGS "") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../) @@ -9,626 +9,626 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../) set(CMAKE_VERBOSE_MAKEFILE ON) set(src_io - ./REDALERT/io/UserInput.cpp - ./REDALERT/io/UserInput.h - ./REDALERT/io/EventQueue.h - ./REDALERT/io/EventQueue.cpp + ./redalert/io/userinput.cpp + ./redalert/io/userinput.h + ./redalert/io/eventqueue.h + ./redalert/io/eventqueue.cpp ) set(src_win32lib - ./REDALERT/WIN32LIB/TOBUFF.cpp - ./RedAlert/WIN32LIB/ALLOC.CPP - ./RedAlert/WIN32LIB/AUDIO.H - ./RedAlert/WIN32LIB/BUFFER.CPP - ./RedAlert/WIN32LIB/BUFFER.H - ./RedAlert/WIN32LIB/BUFFGLBL.CPP - ./RedAlert/WIN32LIB/DEFINES.H - ./RedAlert/WIN32LIB/DELAY.CPP - ./RedAlert/WIN32LIB/DESCMGMT.H - ./RedAlert/WIN32LIB/DIFFTB.INC - ./RedAlert/WIN32LIB/DIPTHONG.CPP - ./RedAlert/WIN32LIB/DIPTHONG.H - ./RedAlert/WIN32LIB/DPLAY.H - ./RedAlert/WIN32LIB/DRAWBUFF.H - ./RedAlert/WIN32LIB/DRAWBUFF.INC - ./RedAlert/WIN32LIB/DrawMisc.cpp - ./RedAlert/WIN32LIB/DRAWRECT.CPP - ./RedAlert/WIN32LIB/DSETUP.H - ./RedAlert/WIN32LIB/DSOUND.H - ./RedAlert/WIN32LIB/EXTERNS.H - ./RedAlert/WIN32LIB/FASTFILE.H - ./RedAlert/WIN32LIB/FILE.H - ./RedAlert/WIN32LIB/FILEPCX.H - ./RedAlert/WIN32LIB/FILETEMP.H - ./RedAlert/WIN32LIB/FONT.H - ./RedAlert/WIN32LIB/FUNCTION.H - ./RedAlert/WIN32LIB/GBUFFER.CPP - ./RedAlert/WIN32LIB/GBUFFER.H - ./RedAlert/WIN32LIB/GBUFFER.INC - ./RedAlert/WIN32LIB/GETSHAPE.CPP - ./RedAlert/WIN32LIB/ICONCACH.H - ./RedAlert/WIN32LIB/ICONSET.CPP - ./RedAlert/WIN32LIB/IFF.CPP - ./RedAlert/WIN32LIB/IFF.H - ./RedAlert/WIN32LIB/INDEXTB.INC - ./RedAlert/WIN32LIB/IRANDOM.CPP - ./RedAlert/WIN32LIB/KEYBOARD.H - ./RedAlert/WIN32LIB/KEYBOARD.INC - ./RedAlert/WIN32LIB/KEYSTRUC.INC - ./RedAlert/WIN32LIB/LOAD.CPP - ./RedAlert/WIN32LIB/LOADFONT.CPP - ./RedAlert/WIN32LIB/MCGAPRIM.INC - ./RedAlert/WIN32LIB/MEMFLAG.H - ./RedAlert/WIN32LIB/MISC.H - ./RedAlert/WIN32LIB/MODEMREG.H - ./RedAlert/WIN32LIB/MONO.H - ./RedAlert/WIN32LIB/MOUSE.H - ./RedAlert/WIN32LIB/MOUSE.INC - ./RedAlert/WIN32LIB/MouseShape.cpp - ./RedAlert/WIN32LIB/MouseShape.h - ./RedAlert/WIN32LIB/MOUSEWW.CPP - ./RedAlert/WIN32LIB/NYBBTB.INC - ./RedAlert/WIN32LIB/PALETTE.CPP - ./RedAlert/WIN32LIB/PALETTE.H - ./RedAlert/WIN32LIB/PLAYCD.H - ./RedAlert/WIN32LIB/PROFILE.H - ./RedAlert/WIN32LIB/PROFILE.INC - ./RedAlert/WIN32LIB/RAWFILE.H - ./RedAlert/WIN32LIB/SHAPE.H - ./RedAlert/WIN32LIB/SHAPE.INC - ./RedAlert/WIN32LIB/SOS.H - ./RedAlert/WIN32LIB/SOSCOMP.H - ./RedAlert/WIN32LIB/SOSDATA.H - ./RedAlert/WIN32LIB/SOSDEFS.H - ./RedAlert/WIN32LIB/SOSFNCT.H - ./RedAlert/WIN32LIB/SOSRES.H - ./RedAlert/WIN32LIB/SOUND.H - ./RedAlert/WIN32LIB/SOUNDINT.H - ./RedAlert/WIN32LIB/STAMP.INC - ./RedAlert/WIN32LIB/STRUCTS.H - ./RedAlert/WIN32LIB/SVGAPRIM.INC - ./RedAlert/WIN32LIB/TILE.H - ./RedAlert/WIN32LIB/TIMER.CPP - ./RedAlert/WIN32LIB/TIMER.H - ./RedAlert/WIN32LIB/TIMERDWN.CPP - ./RedAlert/WIN32LIB/TIMERINI.CPP - ./RedAlert/WIN32LIB/VIDEO.H - ./RedAlert/WIN32LIB/WINCOMM.H - ./RedAlert/WIN32LIB/WINDOWS.CPP - ./RedAlert/WIN32LIB/WINHIDE.CPP - ./RedAlert/WIN32LIB/WRITEPCX.CPP - ./RedAlert/WIN32LIB/WSA.CPP - ./RedAlert/WIN32LIB/WSA.H - ./RedAlert/WIN32LIB/WWFILE.H - ./RedAlert/WIN32LIB/WWLIB32.H - ./RedAlert/WIN32LIB/WWMEM.H - ./RedAlert/WIN32LIB/WWMEM.INC - ./RedAlert/WIN32LIB/wwstd.h - ./RedAlert/WIN32LIB/WW_WIN.H - ./RedAlert/WIN32LIB/_DIPTABL.CPP + ./redalert/win32lib/TOBUFF.cpp + ./redalert/win32lib/ALLOC.CPP + ./redalert/win32lib/AUDIO.H + ./redalert/win32lib/BUFFER.CPP + ./redalert/win32lib/BUFFER.H + ./redalert/win32lib/BUFFGLBL.CPP + ./redalert/win32lib/DEFINES.H + ./redalert/win32lib/DELAY.CPP + ./redalert/win32lib/DESCMGMT.H + ./redalert/win32lib/DIFFTB.INC + ./redalert/win32lib/DIPTHONG.CPP + ./redalert/win32lib/DIPTHONG.H + ./redalert/win32lib/DPLAY.H + ./redalert/win32lib/DRAWBUFF.H + ./redalert/win32lib/DRAWBUFF.INC + ./redalert/win32lib/DrawMisc.cpp + ./redalert/win32lib/DRAWRECT.CPP + ./redalert/win32lib/DSETUP.H + ./redalert/win32lib/DSOUND.H + ./redalert/win32lib/EXTERNS.H + ./redalert/win32lib/FASTFILE.H + ./redalert/win32lib/FILE.H + ./redalert/win32lib/FILEPCX.H + ./redalert/win32lib/FILETEMP.H + ./redalert/win32lib/FONT.H + ./redalert/win32lib/FUNCTION.H + ./redalert/win32lib/GBUFFER.CPP + ./redalert/win32lib/GBUFFER.H + ./redalert/win32lib/GBUFFER.INC + ./redalert/win32lib/GETSHAPE.CPP + ./redalert/win32lib/ICONCACH.H + ./redalert/win32lib/ICONSET.CPP + ./redalert/win32lib/IFF.CPP + ./redalert/win32lib/IFF.H + ./redalert/win32lib/INDEXTB.INC + ./redalert/win32lib/IRANDOM.CPP + ./redalert/win32lib/KEYBOARD.H + ./redalert/win32lib/KEYBOARD.INC + ./redalert/win32lib/KEYSTRUC.INC + ./redalert/win32lib/LOAD.CPP + ./redalert/win32lib/LOADFONT.CPP + ./redalert/win32lib/MCGAPRIM.INC + ./redalert/win32lib/MEMFLAG.H + ./redalert/win32lib/MISC.H + ./redalert/win32lib/MODEMREG.H + ./redalert/win32lib/MONO.H + ./redalert/win32lib/MOUSE.H + ./redalert/win32lib/MOUSE.INC + ./redalert/win32lib/MouseShape.cpp + ./redalert/win32lib/MouseShape.h + ./redalert/win32lib/MOUSEWW.CPP + ./redalert/win32lib/NYBBTB.INC + ./redalert/win32lib/PALETTE.CPP + ./redalert/win32lib/PALETTE.H + ./redalert/win32lib/PLAYCD.H + ./redalert/win32lib/PROFILE.H + ./redalert/win32lib/PROFILE.INC + ./redalert/win32lib/RAWFILE.H + ./redalert/win32lib/SHAPE.H + ./redalert/win32lib/SHAPE.INC + ./redalert/win32lib/SOS.H + ./redalert/win32lib/SOSCOMP.H + ./redalert/win32lib/SOSDATA.H + ./redalert/win32lib/SOSDEFS.H + ./redalert/win32lib/SOSFNCT.H + ./redalert/win32lib/SOSRES.H + ./redalert/win32lib/SOUND.H + ./redalert/win32lib/SOUNDINT.H + ./redalert/win32lib/STAMP.INC + ./redalert/win32lib/STRUCTS.H + ./redalert/win32lib/SVGAPRIM.INC + ./redalert/win32lib/TILE.H + ./redalert/win32lib/TIMER.CPP + ./redalert/win32lib/TIMER.H + ./redalert/win32lib/TIMERDWN.CPP + ./redalert/win32lib/TIMERINI.CPP + ./redalert/win32lib/VIDEO.H + ./redalert/win32lib/WINCOMM.H + ./redalert/win32lib/WINDOWS.CPP + ./redalert/win32lib/WINHIDE.CPP + ./redalert/win32lib/WRITEPCX.CPP + ./redalert/win32lib/WSA.CPP + ./redalert/win32lib/WSA.H + ./redalert/win32lib/WWFILE.H + ./redalert/win32lib/WWLIB32.H + ./redalert/win32lib/WWMEM.H + ./redalert/win32lib/WWMEM.INC + ./redalert/win32lib/wwstd.h + ./redalert/win32lib/WW_WIN.H + ./redalert/win32lib/_DIPTABL.CPP ) #Red Alert files set(src_redalert - ./RedAlert/2KEYFRAM.CPP - ./RedAlert/AADATA.CPP - ./RedAlert/ABSTRACT.CPP - ./RedAlert/ABSTRACT.H - ./RedAlert/ADATA.CPP - ./RedAlert/ADPCM.CPP - ./RedAlert/AIRCRAFT.CPP - ./RedAlert/AIRCRAFT.H - ./RedAlert/ANIM.CPP - ./RedAlert/CONSOLE.CPP - ./RedAlert/ANIM.H - ./RedAlert/AUDIO.CPP - ./RedAlert/AUDIO.H - ./RedAlert/AUDIOMIX.H - ./RedAlert/AUDMIX.CPP - ./RedAlert/B64PIPE.CPP - ./RedAlert/B64PIPE.H - ./RedAlert/B64STRAW.CPP - ./RedAlert/B64STRAW.H - ./RedAlert/BAR.CPP - ./RedAlert/BAR.H - ./RedAlert/BASE.CPP - ./RedAlert/BASE.H - ./RedAlert/BASE64.CPP - ./RedAlert/BASE64.H - ./RedAlert/BBDATA.CPP - ./RedAlert/BDATAXML.CPP - ./RedAlert/BDATA.CPP - ./RedAlert/BENCH.CPP - ./RedAlert/BENCH.H - ./RedAlert/BFIOFILE.CPP - ./RedAlert/BFIOFILE.H - ./RedAlert/BIGCHECK.CPP - ./RedAlert/BIGCHECK.H - ./RedAlert/BLOWFISH.CPP - ./RedAlert/BLOWFISH.H - ./RedAlert/BLOWPIPE.CPP - ./RedAlert/BLOWPIPE.H - ./RedAlert/BLWSTRAW.CPP - ./RedAlert/BLWSTRAW.H - ./RedAlert/BUFF.CPP - ./RedAlert/BUFF.H - ./RedAlert/BUFFERX.H - ./RedAlert/BUILDING.CPP - ./RedAlert/BUILDING.H - ./RedAlert/BULLET.CPP - ./RedAlert/BULLET.H - ./RedAlert/CARGO.CPP - ./RedAlert/CARGO.H - ./RedAlert/CARRY.CPP - ./RedAlert/CARRY.H - ./RedAlert/CBN_.H - ./RedAlert/CCDDE.CPP - ./RedAlert/CCDDE.H - ./RedAlert/CCFILE.CPP - ./RedAlert/CCFILE.H - ./RedAlert/CCINI.CPP - ./RedAlert/CCINI.H - ./RedAlert/CCMPATH.CPP - ./RedAlert/CCPTR.CPP - ./RedAlert/CCPTR.H - ./RedAlert/CCTEN.CPP - ./RedAlert/CDATA.CPP - ./RedAlert/CDFILE.CPP - ./RedAlert/CDFILE.H - ./RedAlert/CELL.CPP - ./RedAlert/CELL.H - ./RedAlert/CHECKBOX.CPP - ./RedAlert/CHECKBOX.H - ./RedAlert/CHEKLIST.CPP - ./RedAlert/CHEKLIST.H - ./RedAlert/CLASS.CPP - ./RedAlert/COLRLIST.CPP - ./RedAlert/COLRLIST.H - ./RedAlert/COMBAT.CPP - ./RedAlert/COMBUF.CPP - ./RedAlert/COMBUF.H - ./RedAlert/COMINIT.CPP - ./RedAlert/COMINIT.H - ./RedAlert/COMPAT.H - ./RedAlert/COMQUEUE.CPP - ./RedAlert/COMQUEUE.H - ./RedAlert/CONFDLG.CPP - ./RedAlert/CONFDLG.H - ./RedAlert/CONNECT.CPP - ./RedAlert/CONNECT.H - ./RedAlert/CONNMGR.H - ./RedAlert/CONQUER.CPP - ./RedAlert/CONQUER.H - ./RedAlert/CONST.CPP - ./RedAlert/CONTROL.CPP - ./RedAlert/CONTROL.H - ./RedAlert/COORD.CPP - ./RedAlert/COORDA.h - ./RedAlert/CRATE.CPP - ./RedAlert/CRATE.H - ./RedAlert/CRC.CPP - ./RedAlert/CRC.H - ./RedAlert/CRCPIPE.CPP - ./RedAlert/CRCPIPE.H - ./RedAlert/CRCSTRAW.CPP - ./RedAlert/CRCSTRAW.H - ./RedAlert/CREDITS.CPP - ./RedAlert/CREDITS.H - ./RedAlert/CREW.CPP - ./RedAlert/CREW.H - ./RedAlert/CSTRAW.CPP - ./RedAlert/CSTRAW.H - ./RedAlert/DDE.CPP - ./RedAlert/DDE.H - ./RedAlert/DEBUG.CPP - ./RedAlert/DEBUG.H - ./RedAlert/DEFINES.H - ./RedAlert/DESCDLG.CPP - ./RedAlert/DESCDLG.H - ./RedAlert/DIAL8.CPP - ./RedAlert/DIAL8.H - ./RedAlert/DIALOG.CPP - ./RedAlert/DIBAPI.H - ./RedAlert/DIBFILE.CPP - ./RedAlert/DIBUTIL.CPP - ./RedAlert/DIBUTIL.H - ./RedAlert/DISPLAY.CPP - ./RedAlert/DISPLAY.H - ./RedAlert/DLLInterface.cpp - ./RedAlert/DLLInterface.h - ./RedAlert/DLLInterfaceEditor.cpp - ./RedAlert/DOOR.CPP - ./RedAlert/DOOR.H - ./RedAlert/DPMI.CPP - ./RedAlert/DPMI.H - ./RedAlert/DRIVE.CPP - ./RedAlert/DRIVE.H - ./RedAlert/DROP.CPP - ./RedAlert/DROP.H - ./RedAlert/DTABLE.CPP - ./RedAlert/DYNAVEC.CPP - ./RedAlert/EDIT.CPP - ./RedAlert/EDIT.H - ./RedAlert/EGOS.CPP - ./RedAlert/EGOS.H - ./RedAlert/ENDING.CPP - ./RedAlert/ENDING.H - ./RedAlert/EVENT.CPP - ./RedAlert/EVENT.H - ./RedAlert/EXPAND.CPP - ./RedAlert/EXTERNS.H - ./RedAlert/FACE.CPP - ./RedAlert/FACE.H - ./RedAlert/FACING.CPP - ./RedAlert/FACING.H - ./RedAlert/FACTORY.CPP - ./RedAlert/FACTORY.H - ./RedAlert/FAKESOCK.H - ./RedAlert/FIELD.CPP - ./RedAlert/FIELD.H - ./RedAlert/FILEPCX.H - ./RedAlert/FINDPATH.CPP - ./RedAlert/FIXED.CPP - ./RedAlert/FIXED.H - ./RedAlert/FLASHER.CPP - ./RedAlert/FLASHER.H - ./RedAlert/FLY.CPP - ./RedAlert/FLY.H - ./RedAlert/FOOT.CPP - ./RedAlert/FOOT.H - ./RedAlert/FTIMER.H - ./RedAlert/FUNCTION.H - ./RedAlert/FUSE.CPP - ./RedAlert/FUSE.H - ./RedAlert/GADGET.CPP - ./RedAlert/GADGET.H - ./RedAlert/GAMEDLG.CPP - ./RedAlert/GAMEDLG.H - ./RedAlert/GAUGE.CPP - ./RedAlert/GAUGE.H - ./RedAlert/GETCPU.CPP - ./RedAlert/GLOBALS.CPP - ./RedAlert/GOPTIONS.CPP - ./RedAlert/GOPTIONS.H - ./RedAlert/GSCREEN.CPP - ./RedAlert/GSCREEN.H - ./RedAlert/HDATA.CPP - ./RedAlert/HEAP.CPP - ./RedAlert/HEAP.H - ./RedAlert/HELP.CPP - ./RedAlert/HELP.H - ./RedAlert/HOUSE.CPP - ./RedAlert/HOUSE.H - ./RedAlert/HSV.CPP - ./RedAlert/HSV.H - ./RedAlert/ICONLIST.CPP - ./RedAlert/ICONLIST.H - ./RedAlert/IDATA.CPP - ./RedAlert/INFANTRY.CPP - ./RedAlert/INFANTRY.H - ./RedAlert/INI.CPP - ./RedAlert/INI.H - ./RedAlert/INIBIN.CPP - ./RedAlert/INICODE.CPP - ./RedAlert/INIT.CPP - ./RedAlert/INLINE.H - ./RedAlert/INT.CPP - ./RedAlert/INT.H - ./RedAlert/INTERNET.CPP - ./RedAlert/INTERNET.H - ./RedAlert/INTERPAL.CPP - ./RedAlert/INTRO.CPP - ./RedAlert/INTRO.H - ./RedAlert/IOMAP.CPP - ./RedAlert/IOOBJ.CPP - ./RedAlert/IPX.CPP - ./RedAlert/IPX.H - ./RedAlert/IPX95.CPP - ./RedAlert/IPX95.H - ./RedAlert/IPXADDR.CPP - ./RedAlert/IPXADDR.H - ./RedAlert/IPXCONN.CPP - ./RedAlert/IPXCONN.H - ./RedAlert/IPXGCONN.CPP - ./RedAlert/IPXGCONN.H - ./RedAlert/IPXMGR.CPP - ./RedAlert/IPXMGR.H - ./RedAlert/ITABLE.CPP - ./RedAlert/JSHELL.CPP - ./RedAlert/JSHELL.H - ./RedAlert/KEY.CPP - ./RedAlert/KEY.H - ./RedAlert/KEYBOARD.H - ./RedAlert/LANGUAGE.H - ./RedAlert/LAYER.CPP - ./RedAlert/LAYER.H - ./RedAlert/LCW.CPP - ./RedAlert/LCW.H - ./RedAlert/LCWPIPE.CPP - ./RedAlert/LCWPIPE.H - ./RedAlert/LCWSTRAW.CPP - ./RedAlert/LCWSTRAW.H - ./RedAlert/LCWUNCMP.CPP - ./RedAlert/LED.H - ./RedAlert/License.txt - ./RedAlert/LINK.CPP - ./RedAlert/LINK.H - ./RedAlert/LINT.H - ./RedAlert/LIST.CPP - ./RedAlert/LIST.H - ./RedAlert/LISTNODE.H - ./RedAlert/LOADDLG.CPP - ./RedAlert/LOADDLG.H - ./RedAlert/LOGIC.CPP - ./RedAlert/LOGIC.H - ./RedAlert/LZO.H - ./RedAlert/LZO1X.H - ./RedAlert/LZO1X_C.CPP - ./RedAlert/LZO1X_D.CPP - ./RedAlert/LZOCONF.H - ./RedAlert/LZOPIPE.CPP - ./RedAlert/LZOPIPE.H - ./RedAlert/LZOSTRAW.CPP - ./RedAlert/LZOSTRAW.H - ./RedAlert/LZO_CONF.H - ./RedAlert/LZW.CPP - ./RedAlert/LZW.H - ./RedAlert/LZWPIPE.CPP - ./RedAlert/LZWPIPE.H - ./RedAlert/LZWSTRAW.CPP - ./RedAlert/LZWSTRAW.H - ./RedAlert/MAP.CPP - ./RedAlert/MAP.H - ./RedAlert/MAPEDDLG.CPP - ./RedAlert/MAPEDIT.CPP - ./RedAlert/MAPEDIT.H - ./RedAlert/MAPEDPLC.CPP - ./RedAlert/MAPEDTM.CPP - ./RedAlert/MAPSEL.CPP - ./RedAlert/MCI.CPP - ./RedAlert/MCI.H - ./RedAlert/MCIMOVIE.CPP - ./RedAlert/MCIMOVIE.H - ./RedAlert/MEMCHECK.H - ./RedAlert/MENUS.CPP - ./RedAlert/MESSAGE.H - ./RedAlert/MiscAsm.cpp - ./RedAlert/MISSION.CPP - ./RedAlert/MISSION.H - ./RedAlert/MIXFILE.CPP - ./RedAlert/MIXFILE.H - ./RedAlert/MONOC.CPP - ./RedAlert/MONOC.H - ./RedAlert/MOUSE.CPP - ./RedAlert/MOUSE.H - ./RedAlert/MOVIE.H - ./RedAlert/MP.CPP - ./RedAlert/MP.H - ./RedAlert/MPGSET.CPP - ./RedAlert/MPGSET.H - ./RedAlert/MPLAYER.CPP - ./RedAlert/MPMGRW.CPP - ./RedAlert/MPMGRW.H - ./RedAlert/MPU.CPP - ./RedAlert/MPU.H - ./RedAlert/MSGBOX.CPP - ./RedAlert/MSGBOX.H - ./RedAlert/MSGLIST.CPP - ./RedAlert/MSGLIST.H - ./RedAlert/NETDLG.CPP - ./RedAlert/NULLCONN.CPP - ./RedAlert/NULLCONN.H - ./RedAlert/NULLDLG.CPP - ./RedAlert/NULLMGR.CPP - ./RedAlert/NULLMGR.H - ./RedAlert/OBJECT.CPP - ./RedAlert/OBJECT.H - ./RedAlert/OCIDL.H - ./RedAlert/ODATA.CPP - ./RedAlert/OPTIONS.CPP - ./RedAlert/OPTIONS.H - ./RedAlert/OVERLAY.CPP - ./RedAlert/OVERLAY.H - ./RedAlert/UNITXML.CPP - ./RedAlert/PACKET.CPP - ./RedAlert/PACKET.H - ./RedAlert/PALETTEC.CPP - ./RedAlert/PALETTEC.H - ./RedAlert/PIPE.CPP - ./RedAlert/PIPE.H - ./RedAlert/PK.CPP - ./RedAlert/PK.H - ./RedAlert/PKPIPE.CPP - ./RedAlert/PKPIPE.H - ./RedAlert/PKSTRAW.CPP - ./RedAlert/PKSTRAW.H - ./RedAlert/POWER.CPP - ./RedAlert/POWER.H - ./RedAlert/PROFILE.CPP - ./RedAlert/QUEUE.CPP - ./RedAlert/QUEUE.H - ./RedAlert/RADAR.CPP - ./RedAlert/RADAR.H - ./RedAlert/RADIO.CPP - ./RedAlert/RADIO.H - ./RedAlert/RAMFILE.CPP - ./RedAlert/RAMFILE.H - ./RedAlert/RAND.CPP - ./RedAlert/RANDOM.CPP - ./RedAlert/RANDOM.H - ./RedAlert/RAWFILE.CPP - ./RedAlert/RAWFILE.H - ./RedAlert/RAWOLAPI.CPP - ./RedAlert/RAWOLAPI.H - ./RedAlert/READLINE.CPP - ./RedAlert/READLINE.H - ./RedAlert/RECT.CPP - ./RedAlert/RECT.H - ./RedAlert/REGION.H - ./RedAlert/REINF.CPP - ./RedAlert/RGB.CPP - ./RedAlert/RGB.H - ./RedAlert/RNDSTRAW.CPP - ./RedAlert/RNDSTRAW.H - ./RedAlert/RNG.H - ./RedAlert/ROTBMP.CPP - ./RedAlert/ROTBMP.H - ./RedAlert/RULES.CPP - ./RedAlert/RULES.H - ./RedAlert/SAVEDLG.H - ./RedAlert/SAVELOAD.CPP - ./RedAlert/SCENARIO.CPP - ./RedAlert/SCENARIO.H - ./RedAlert/SCORE.CPP - ./RedAlert/SCORE.H - ./RedAlert/SCREEN.H - ./RedAlert/SCROLL.CPP - ./RedAlert/SCROLL.H - ./RedAlert/SDATA.CPP - ./RedAlert/SEARCH.H - ./RedAlert/SEDITDLG.CPP - ./RedAlert/SEDITDLG.H - ./RedAlert/SENDFILE.CPP - ./RedAlert/SEQCONN.CPP - ./RedAlert/SEQCONN.H - ./RedAlert/SESSION.CPP - ./RedAlert/SESSION.H - ./RedAlert/SHA.CPP - ./RedAlert/SHA.H - ./RedAlert/Shape.cpp - ./RedAlert/SHAPEBTN.CPP - ./RedAlert/SHAPEBTN.H - ./RedAlert/SHAPIPE.CPP - ./RedAlert/SHAPIPE.H - ./RedAlert/SHASTRAW.CPP - ./RedAlert/SHASTRAW.H - ./RedAlert/SIDEBAR.CPP - ./RedAlert/SIDEBAR.H - ./RedAlert/SIDEBARGlyphx.CPP - ./RedAlert/SIDEBARGlyphx.H - ./RedAlert/SLIDER.CPP - ./RedAlert/SLIDER.H - ./RedAlert/SMUDGE.CPP - ./RedAlert/SMUDGE.H - ./RedAlert/SOUNDDLG.CPP - ./RedAlert/SOUNDDLG.H - ./RedAlert/SPECIAL.CPP - ./RedAlert/SPECIAL.H - ./RedAlert/NewBlit.cpp - ./RedAlert/SPRITE.CPP - ./RedAlert/STAGE.H - ./RedAlert/STARTUP.CPP - ./RedAlert/STATBTN.CPP - ./RedAlert/STATBTN.H - ./RedAlert/STATS.CPP - ./RedAlert/STRAW.CPP - ./RedAlert/STRAW.H - ./RedAlert/STUB.CPP - ./RedAlert/STYLE.H - ./RedAlert/SUPER.CPP - ./RedAlert/SUPER.H - ./RedAlert/SURFACE.CPP - ./RedAlert/SURFACE.H - ./RedAlert/TAB.CPP - ./RedAlert/TAB.H - ./RedAlert/TACTION.CPP - ./RedAlert/TACTION.H - ./RedAlert/TARGET.CPP - ./RedAlert/TARGET.H - ./RedAlert/TCPIP.CPP - ./RedAlert/TCPIP.H - ./RedAlert/TDATA.CPP - ./RedAlert/TEAM.CPP - ./RedAlert/TEAM.H - ./RedAlert/TEAMTYPE.CPP - ./RedAlert/TEAMTYPE.H - ./RedAlert/TECHNO.CPP - ./RedAlert/TECHNO.H - ./RedAlert/TEMPLATE.CPP - ./RedAlert/TEMPLATE.H - ./RedAlert/TENMGR.CPP - ./RedAlert/TENMGR.H - ./RedAlert/TERRAIN.CPP - ./RedAlert/TERRAIN.H - ./RedAlert/TEVENT.CPP - ./RedAlert/TEVENT.H - ./RedAlert/TEXTBTN.CPP - ./RedAlert/TEXTBTN.H - ./RedAlert/THEME.CPP - ./RedAlert/THEME.H - ./RedAlert/TILESET.CPP - ./RedAlert/TOGGLE.CPP - ./RedAlert/TOGGLE.H - ./RedAlert/TOOLTIP.CPP - ./RedAlert/TOOLTIP.H - ./RedAlert/TRACKER.CPP - ./RedAlert/TRIGGER.CPP - ./RedAlert/TRIGGER.H - ./RedAlert/TRIGTYPE.CPP - ./RedAlert/TRIGTYPE.H - ./RedAlert/TXTLABEL.CPP - ./RedAlert/TXTLABEL.H - ./RedAlert/TYPE.H - ./RedAlert/UDATA.CPP - ./RedAlert/UDPADDR.CPP - ./RedAlert/UNIT.CPP - ./RedAlert/UNIT.H - ./RedAlert/UTRACKER.CPP - ./RedAlert/UTRACKER.H - ./RedAlert/VDATA.CPP - ./RedAlert/VECTOR.CPP - ./RedAlert/VECTOR.H - ./RedAlert/VERSION.CPP - ./RedAlert/VERSION.H - ./RedAlert/VESSEL.CPP - ./RedAlert/VESSEL.H - ./RedAlert/VISUDLG.CPP - ./RedAlert/VISUDLG.H - ./RedAlert/VORTEX.CPP - ./RedAlert/VORTEX.H - ./RedAlert/VQAMOVIE.CPP - ./RedAlert/VQAMOVIE.H - ./RedAlert/W95TRACE.CPP - ./RedAlert/W95TRACE.H - ./RedAlert/WARHEAD.CPP - ./RedAlert/WARHEAD.H - ./RedAlert/WATCOM.H - ./RedAlert/WEAPON.CPP - ./RedAlert/WEAPON.H - ./RedAlert/WINSTUB.CPP - ./RedAlert/WOLAPIOB.CPP - ./RedAlert/WOLAPIOB.H - ./RedAlert/WOLDEBUG.H - ./RedAlert/WOLEDIT.CPP - ./RedAlert/WOLEDIT.H - ./RedAlert/WOLSTRNG.CPP - ./RedAlert/WOLSTRNG.H - ./RedAlert/WOL_CGAM.CPP - ./RedAlert/WOL_CHAT.CPP - ./RedAlert/WOL_DNLD.CPP - ./RedAlert/WOL_GSUP.CPP - ./RedAlert/WOL_GSUP.H - ./RedAlert/WOL_LOGN.CPP - ./RedAlert/WOL_MAIN.CPP - ./RedAlert/WOL_OPT.CPP - ./RedAlert/WSNWLINK.H - ./RedAlert/WSPIPX.CPP - ./RedAlert/WSPIPX.H - ./RedAlert/WSPROTO.CPP - ./RedAlert/WSPROTO.H - ./RedAlert/WSPUDP.CPP - ./RedAlert/WSPUDP.H - ./RedAlert/WWALLOC.H - ./RedAlert/WWFILE.H - ./RedAlert/XPIPE.CPP - ./RedAlert/XPIPE.H - ./RedAlert/XSTRAW.CPP - ./RedAlert/XSTRAW.H - ./RedAlert/_WSPROTO.CPP - ./RedAlert/_WSPROTO.H - ./REDALERT/MapScript.cpp - ./REDALERT/TXTPRNT.cpp - ./REDALERT/TILESETXML.cpp + ./redalert/2KEYFRAM.CPP + ./redalert/AADATA.CPP + ./redalert/ABSTRACT.CPP + ./redalert/ABSTRACT.H + ./redalert/ADATA.CPP + ./redalert/ADPCM.CPP + ./redalert/AIRCRAFT.CPP + ./redalert/AIRCRAFT.H + ./redalert/ANIM.CPP + ./redalert/CONSOLE.CPP + ./redalert/ANIM.H + ./redalert/AUDIO.CPP + ./redalert/AUDIO.H + ./redalert/AUDIOMIX.H + ./redalert/AUDMIX.CPP + ./redalert/B64PIPE.CPP + ./redalert/B64PIPE.H + ./redalert/B64STRAW.CPP + ./redalert/B64STRAW.H + ./redalert/BAR.CPP + ./redalert/BAR.H + ./redalert/BASE.CPP + ./redalert/BASE.H + ./redalert/BASE64.CPP + ./redalert/BASE64.H + ./redalert/BBDATA.CPP + ./redalert/BDATAXML.CPP + ./redalert/BDATA.CPP + ./redalert/BENCH.CPP + ./redalert/BENCH.H + ./redalert/BFIOFILE.CPP + ./redalert/BFIOFILE.H + ./redalert/BIGCHECK.CPP + ./redalert/BIGCHECK.H + ./redalert/BLOWFISH.CPP + ./redalert/BLOWFISH.H + ./redalert/BLOWPIPE.CPP + ./redalert/BLOWPIPE.H + ./redalert/BLWSTRAW.CPP + ./redalert/BLWSTRAW.H + ./redalert/BUFF.CPP + ./redalert/BUFF.H + ./redalert/BUFFERX.H + ./redalert/BUILDING.CPP + ./redalert/BUILDING.H + ./redalert/BULLET.CPP + ./redalert/BULLET.H + ./redalert/CARGO.CPP + ./redalert/CARGO.H + ./redalert/CARRY.CPP + ./redalert/CARRY.H + ./redalert/CBN_.H + ./redalert/CCDDE.CPP + ./redalert/CCDDE.H + ./redalert/CCFILE.CPP + ./redalert/CCFILE.H + ./redalert/CCINI.CPP + ./redalert/CCINI.H + ./redalert/CCMPATH.CPP + ./redalert/CCPTR.CPP + ./redalert/CCPTR.H + ./redalert/CCTEN.CPP + ./redalert/CDATA.CPP + ./redalert/CDFILE.CPP + ./redalert/CDFILE.H + ./redalert/CELL.CPP + ./redalert/CELL.H + ./redalert/CHECKBOX.CPP + ./redalert/CHECKBOX.H + ./redalert/CHEKLIST.CPP + ./redalert/CHEKLIST.H + ./redalert/CLASS.CPP + ./redalert/COLRLIST.CPP + ./redalert/COLRLIST.H + ./redalert/COMBAT.CPP + ./redalert/COMBUF.CPP + ./redalert/COMBUF.H + ./redalert/COMINIT.CPP + ./redalert/COMINIT.H + ./redalert/COMPAT.H + ./redalert/COMQUEUE.CPP + ./redalert/COMQUEUE.H + ./redalert/CONFDLG.CPP + ./redalert/CONFDLG.H + ./redalert/CONNECT.CPP + ./redalert/CONNECT.H + ./redalert/CONNMGR.H + ./redalert/CONQUER.CPP + ./redalert/CONQUER.H + ./redalert/CONST.CPP + ./redalert/CONTROL.CPP + ./redalert/CONTROL.H + ./redalert/COORD.CPP + ./redalert/COORDA.h + ./redalert/CRATE.CPP + ./redalert/CRATE.H + ./redalert/CRC.CPP + ./redalert/CRC.H + ./redalert/CRCPIPE.CPP + ./redalert/CRCPIPE.H + ./redalert/CRCSTRAW.CPP + ./redalert/CRCSTRAW.H + ./redalert/CREDITS.CPP + ./redalert/CREDITS.H + ./redalert/CREW.CPP + ./redalert/CREW.H + ./redalert/CSTRAW.CPP + ./redalert/CSTRAW.H + ./redalert/DDE.CPP + ./redalert/DDE.H + ./redalert/DEBUG.CPP + ./redalert/DEBUG.H + ./redalert/DEFINES.H + ./redalert/DESCDLG.CPP + ./redalert/DESCDLG.H + ./redalert/DIAL8.CPP + ./redalert/DIAL8.H + ./redalert/DIALOG.CPP + ./redalert/DIBAPI.H + ./redalert/DIBFILE.CPP + ./redalert/DIBUTIL.CPP + ./redalert/DIBUTIL.H + ./redalert/DISPLAY.CPP + ./redalert/DISPLAY.H + ./redalert/DLLInterface.cpp + ./redalert/DLLInterface.h + ./redalert/DLLInterfaceEditor.cpp + ./redalert/DOOR.CPP + ./redalert/DOOR.H + ./redalert/DPMI.CPP + ./redalert/DPMI.H + ./redalert/DRIVE.CPP + ./redalert/DRIVE.H + ./redalert/DROP.CPP + ./redalert/DROP.H + ./redalert/DTABLE.CPP + ./redalert/DYNAVEC.CPP + ./redalert/EDIT.CPP + ./redalert/EDIT.H + ./redalert/EGOS.CPP + ./redalert/EGOS.H + ./redalert/ENDING.CPP + ./redalert/ENDING.H + ./redalert/EVENT.CPP + ./redalert/EVENT.H + ./redalert/EXPAND.CPP + ./redalert/EXTERNS.H + ./redalert/FACE.CPP + ./redalert/FACE.H + ./redalert/FACING.CPP + ./redalert/FACING.H + ./redalert/FACTORY.CPP + ./redalert/FACTORY.H + ./redalert/FAKESOCK.H + ./redalert/FIELD.CPP + ./redalert/FIELD.H + ./redalert/FILEPCX.H + ./redalert/FINDPATH.CPP + ./redalert/FIXED.CPP + ./redalert/FIXED.H + ./redalert/FLASHER.CPP + ./redalert/FLASHER.H + ./redalert/FLY.CPP + ./redalert/FLY.H + ./redalert/FOOT.CPP + ./redalert/FOOT.H + ./redalert/FTIMER.H + ./redalert/FUNCTION.H + ./redalert/FUSE.CPP + ./redalert/FUSE.H + ./redalert/GADGET.CPP + ./redalert/GADGET.H + ./redalert/GAMEDLG.CPP + ./redalert/GAMEDLG.H + ./redalert/GAUGE.CPP + ./redalert/GAUGE.H + ./redalert/GETCPU.CPP + ./redalert/GLOBALS.CPP + ./redalert/GOPTIONS.CPP + ./redalert/GOPTIONS.H + ./redalert/GSCREEN.CPP + ./redalert/GSCREEN.H + ./redalert/HDATA.CPP + ./redalert/HEAP.CPP + ./redalert/HEAP.H + ./redalert/HELP.CPP + ./redalert/HELP.H + ./redalert/HOUSE.CPP + ./redalert/HOUSE.H + ./redalert/HSV.CPP + ./redalert/HSV.H + ./redalert/ICONLIST.CPP + ./redalert/ICONLIST.H + ./redalert/IDATA.CPP + ./redalert/INFANTRY.CPP + ./redalert/INFANTRY.H + ./redalert/INI.CPP + ./redalert/INI.H + ./redalert/INIBIN.CPP + ./redalert/INICODE.CPP + ./redalert/INIT.CPP + ./redalert/INLINE.H + ./redalert/INT.CPP + ./redalert/INT.H + ./redalert/INTERNET.CPP + ./redalert/INTERNET.H + ./redalert/INTERPAL.CPP + ./redalert/INTRO.CPP + ./redalert/INTRO.H + ./redalert/IOMAP.CPP + ./redalert/IOOBJ.CPP + ./redalert/IPX.CPP + ./redalert/IPX.H + ./redalert/IPX95.CPP + ./redalert/IPX95.H + ./redalert/IPXADDR.CPP + ./redalert/IPXADDR.H + ./redalert/IPXCONN.CPP + ./redalert/IPXCONN.H + ./redalert/IPXGCONN.CPP + ./redalert/IPXGCONN.H + ./redalert/IPXMGR.CPP + ./redalert/IPXMGR.H + ./redalert/ITABLE.CPP + ./redalert/JSHELL.CPP + ./redalert/JSHELL.H + ./redalert/KEY.CPP + ./redalert/KEY.H + ./redalert/KEYBOARD.H + ./redalert/LANGUAGE.H + ./redalert/LAYER.CPP + ./redalert/LAYER.H + ./redalert/LCW.CPP + ./redalert/LCW.H + ./redalert/LCWPIPE.CPP + ./redalert/LCWPIPE.H + ./redalert/LCWSTRAW.CPP + ./redalert/LCWSTRAW.H + ./redalert/LCWUNCMP.CPP + ./redalert/LED.H + ./redalert/License.txt + ./redalert/LINK.CPP + ./redalert/LINK.H + ./redalert/LINT.H + ./redalert/LIST.CPP + ./redalert/LIST.H + ./redalert/LISTNODE.H + ./redalert/LOADDLG.CPP + ./redalert/LOADDLG.H + ./redalert/LOGIC.CPP + ./redalert/LOGIC.H + ./redalert/LZO.H + ./redalert/LZO1X.H + ./redalert/LZO1X_C.CPP + ./redalert/LZO1X_D.CPP + ./redalert/LZOCONF.H + ./redalert/LZOPIPE.CPP + ./redalert/LZOPIPE.H + ./redalert/LZOSTRAW.CPP + ./redalert/LZOSTRAW.H + ./redalert/LZO_CONF.H + ./redalert/LZW.CPP + ./redalert/LZW.H + ./redalert/LZWPIPE.CPP + ./redalert/LZWPIPE.H + ./redalert/LZWSTRAW.CPP + ./redalert/LZWSTRAW.H + ./redalert/MAP.CPP + ./redalert/MAP.H + ./redalert/MAPEDDLG.CPP + ./redalert/MAPEDIT.CPP + ./redalert/MAPEDIT.H + ./redalert/MAPEDPLC.CPP + ./redalert/MAPEDTM.CPP + ./redalert/MAPSEL.CPP + ./redalert/MCI.CPP + ./redalert/MCI.H + ./redalert/MCIMOVIE.CPP + ./redalert/MCIMOVIE.H + ./redalert/MEMCHECK.H + ./redalert/MENUS.CPP + ./redalert/MESSAGE.H + ./redalert/MiscAsm.cpp + ./redalert/MISSION.CPP + ./redalert/MISSION.H + ./redalert/MIXFILE.CPP + ./redalert/MIXFILE.H + ./redalert/MONOC.CPP + ./redalert/MONOC.H + ./redalert/MOUSE.CPP + ./redalert/MOUSE.H + ./redalert/MOVIE.H + ./redalert/MP.CPP + ./redalert/MP.H + ./redalert/MPGSET.CPP + ./redalert/MPGSET.H + ./redalert/MPLAYER.CPP + ./redalert/MPMGRW.CPP + ./redalert/MPMGRW.H + ./redalert/MPU.CPP + ./redalert/MPU.H + ./redalert/MSGBOX.CPP + ./redalert/MSGBOX.H + ./redalert/MSGLIST.CPP + ./redalert/MSGLIST.H + ./redalert/NETDLG.CPP + ./redalert/NULLCONN.CPP + ./redalert/NULLCONN.H + ./redalert/NULLDLG.CPP + ./redalert/NULLMGR.CPP + ./redalert/NULLMGR.H + ./redalert/OBJECT.CPP + ./redalert/OBJECT.H + ./redalert/OCIDL.H + ./redalert/ODATA.CPP + ./redalert/OPTIONS.CPP + ./redalert/OPTIONS.H + ./redalert/OVERLAY.CPP + ./redalert/OVERLAY.H + ./redalert/UNITXML.CPP + ./redalert/PACKET.CPP + ./redalert/PACKET.H + ./redalert/PALETTEC.CPP + ./redalert/PALETTEC.H + ./redalert/PIPE.CPP + ./redalert/PIPE.H + ./redalert/PK.CPP + ./redalert/PK.H + ./redalert/PKPIPE.CPP + ./redalert/PKPIPE.H + ./redalert/PKSTRAW.CPP + ./redalert/PKSTRAW.H + ./redalert/POWER.CPP + ./redalert/POWER.H + ./redalert/PROFILE.CPP + ./redalert/QUEUE.CPP + ./redalert/QUEUE.H + ./redalert/RADAR.CPP + ./redalert/RADAR.H + ./redalert/RADIO.CPP + ./redalert/RADIO.H + ./redalert/RAMFILE.CPP + ./redalert/RAMFILE.H + ./redalert/RAND.CPP + ./redalert/RANDOM.CPP + ./redalert/RANDOM.H + ./redalert/RAWFILE.CPP + ./redalert/RAWFILE.H + ./redalert/RAWOLAPI.CPP + ./redalert/RAWOLAPI.H + ./redalert/READLINE.CPP + ./redalert/READLINE.H + ./redalert/RECT.CPP + ./redalert/RECT.H + ./redalert/REGION.H + ./redalert/REINF.CPP + ./redalert/RGB.CPP + ./redalert/RGB.H + ./redalert/RNDSTRAW.CPP + ./redalert/RNDSTRAW.H + ./redalert/RNG.H + ./redalert/ROTBMP.CPP + ./redalert/ROTBMP.H + ./redalert/RULES.CPP + ./redalert/RULES.H + ./redalert/SAVEDLG.H + ./redalert/SAVELOAD.CPP + ./redalert/SCENARIO.CPP + ./redalert/SCENARIO.H + ./redalert/SCORE.CPP + ./redalert/SCORE.H + ./redalert/SCREEN.H + ./redalert/SCROLL.CPP + ./redalert/SCROLL.H + ./redalert/SDATA.CPP + ./redalert/SEARCH.H + ./redalert/SEDITDLG.CPP + ./redalert/SEDITDLG.H + ./redalert/SENDFILE.CPP + ./redalert/SEQCONN.CPP + ./redalert/SEQCONN.H + ./redalert/SESSION.CPP + ./redalert/SESSION.H + ./redalert/SHA.CPP + ./redalert/SHA.H + ./redalert/Shape.cpp + ./redalert/SHAPEBTN.CPP + ./redalert/SHAPEBTN.H + ./redalert/SHAPIPE.CPP + ./redalert/SHAPIPE.H + ./redalert/SHASTRAW.CPP + ./redalert/SHASTRAW.H + ./redalert/SIDEBAR.CPP + ./redalert/SIDEBAR.H + ./redalert/SIDEBARGlyphx.CPP + ./redalert/SIDEBARGlyphx.H + ./redalert/SLIDER.CPP + ./redalert/SLIDER.H + ./redalert/SMUDGE.CPP + ./redalert/SMUDGE.H + ./redalert/SOUNDDLG.CPP + ./redalert/SOUNDDLG.H + ./redalert/SPECIAL.CPP + ./redalert/SPECIAL.H + ./redalert/NewBlit.cpp + ./redalert/SPRITE.CPP + ./redalert/STAGE.H + ./redalert/STARTUP.CPP + ./redalert/STATBTN.CPP + ./redalert/STATBTN.H + ./redalert/STATS.CPP + ./redalert/STRAW.CPP + ./redalert/STRAW.H + ./redalert/STUB.CPP + ./redalert/STYLE.H + ./redalert/SUPER.CPP + ./redalert/SUPER.H + ./redalert/SURFACE.CPP + ./redalert/SURFACE.H + ./redalert/TAB.CPP + ./redalert/TAB.H + ./redalert/TACTION.CPP + ./redalert/TACTION.H + ./redalert/TARGET.CPP + ./redalert/TARGET.H + ./redalert/TCPIP.CPP + ./redalert/TCPIP.H + ./redalert/TDATA.CPP + ./redalert/TEAM.CPP + ./redalert/TEAM.H + ./redalert/TEAMTYPE.CPP + ./redalert/TEAMTYPE.H + ./redalert/TECHNO.CPP + ./redalert/TECHNO.H + ./redalert/TEMPLATE.CPP + ./redalert/TEMPLATE.H + ./redalert/TENMGR.CPP + ./redalert/TENMGR.H + ./redalert/TERRAIN.CPP + ./redalert/TERRAIN.H + ./redalert/TEVENT.CPP + ./redalert/TEVENT.H + ./redalert/TEXTBTN.CPP + ./redalert/TEXTBTN.H + ./redalert/THEME.CPP + ./redalert/THEME.H + ./redalert/TILESET.CPP + ./redalert/TOGGLE.CPP + ./redalert/TOGGLE.H + ./redalert/TOOLTIP.CPP + ./redalert/TOOLTIP.H + ./redalert/TRACKER.CPP + ./redalert/TRIGGER.CPP + ./redalert/TRIGGER.H + ./redalert/TRIGTYPE.CPP + ./redalert/TRIGTYPE.H + ./redalert/TXTLABEL.CPP + ./redalert/TXTLABEL.H + ./redalert/TYPE.H + ./redalert/UDATA.CPP + ./redalert/UDPADDR.CPP + ./redalert/UNIT.CPP + ./redalert/UNIT.H + ./redalert/UTRACKER.CPP + ./redalert/UTRACKER.H + ./redalert/VDATA.CPP + ./redalert/VECTOR.CPP + ./redalert/VECTOR.H + ./redalert/VERSION.CPP + ./redalert/VERSION.H + ./redalert/VESSEL.CPP + ./redalert/VESSEL.H + ./redalert/VISUDLG.CPP + ./redalert/VISUDLG.H + ./redalert/VORTEX.CPP + ./redalert/VORTEX.H + ./redalert/VQAMOVIE.CPP + ./redalert/VQAMOVIE.H + ./redalert/W95TRACE.CPP + ./redalert/W95TRACE.H + ./redalert/WARHEAD.CPP + ./redalert/WARHEAD.H + ./redalert/WATCOM.H + ./redalert/WEAPON.CPP + ./redalert/WEAPON.H + ./redalert/WINSTUB.CPP + ./redalert/WOLAPIOB.CPP + ./redalert/WOLAPIOB.H + ./redalert/WOLDEBUG.H + ./redalert/WOLEDIT.CPP + ./redalert/WOLEDIT.H + ./redalert/WOLSTRNG.CPP + ./redalert/WOLSTRNG.H + ./redalert/WOL_CGAM.CPP + ./redalert/WOL_CHAT.CPP + ./redalert/WOL_DNLD.CPP + ./redalert/WOL_GSUP.CPP + ./redalert/WOL_GSUP.H + ./redalert/WOL_LOGN.CPP + ./redalert/WOL_MAIN.CPP + ./redalert/WOL_OPT.CPP + ./redalert/WSNWLINK.H + ./redalert/WSPIPX.CPP + ./redalert/WSPIPX.H + ./redalert/WSPROTO.CPP + ./redalert/WSPROTO.H + ./redalert/WSPUDP.CPP + ./redalert/WSPUDP.H + ./redalert/WWALLOC.H + ./redalert/WWFILE.H + ./redalert/XPIPE.CPP + ./redalert/XPIPE.H + ./redalert/XSTRAW.CPP + ./redalert/XSTRAW.H + ./redalert/_WSPROTO.CPP + ./redalert/_WSPROTO.H + ./redalert/MapScript.cpp + ./redalert/TXTPRNT.cpp + ./redalert/TILESETXML.cpp ) set(src_external @@ -720,10 +720,10 @@ set(src_external ) -add_definitions(-D RA_AF -DCHEAT_KEYS -DIMGUI_IMPL_OPENGL_LOADER_GLEW -DSCENARIO_EDITOR -DWINDOWS_IGNORE_PACKING_MISMATCH -DTRUE_FALSE_DEFINED -DWIN32 -DWINDOWS -DENGLISH -D_USRDLL -DREDALERT_EXPORTS) +add_definitions(-D RA_AF -DCHEAT_KEYS -DIMGUI_IMPL_OPENGL_LOADER_GLEW -DSCENARIO_EDITOR -DWINDOWS_IGNORE_PACKING_MISMATCH -DTRUE_FALSE_DEFINED -DWIN32 -DWINDOWS -DENGLISH -D_USRDLL -Dredalert_EXPORTS) add_compile_options(/permissive+ /Zc:forScope- /Zp1) -add_executable(RedAlert ${src_io} ${src_win32lib} ${src_redalert} ${src_external}) +add_executable( redalert ${src_io} ${src_win32lib} ${src_redalert} ${src_external}) -set_target_properties(RedAlert PROPERTIES OUTPUT_NAME "RedAlert" LINK_FLAGS "/PDB:\"RedAlert.pdb\" /SUBSYSTEM:WINDOWS" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../" ) -target_include_directories(RedAlert PRIVATE ./external/xml/;./external/devil/;./external/;./redalert/win32lib;./external/lua/;./external/imgui;./external/dxsdk/Include;./external/ffmpeg-win32/include;./external/sdl2/include;./win32lib;./external/openal/include) -target_link_libraries(RedAlert "opengl32.lib" "winmm.lib" "Ws2_32.lib" "${CMAKE_SOURCE_DIR}/external/devil/ilu.lib" "${CMAKE_SOURCE_DIR}/external/devil/DevIL.lib" "${CMAKE_SOURCE_DIR}/external/sdl2/lib/x86/SDL2.lib" "${CMAKE_SOURCE_DIR}/external/openal/out/build/x86-Release/OpenAL32.lib") +set_target_properties(redalert PROPERTIES OUTPUT_NAME "redalert" LINK_FLAGS "/PDB:\"redalert.pdb\" /SUBSYSTEM:WINDOWS" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../" ) +target_include_directories(redalert PRIVATE ./external/xml/;./external/devil/;./external/;./redalert/win32lib;./external/lua/;./external/imgui;./external/dxsdk/Include;./external/ffmpeg-win32/include;./external/sdl2/include;./win32lib;./external/openal/include) +target_link_libraries(redalert "opengl32.lib" "winmm.lib" "Ws2_32.lib" "${CMAKE_SOURCE_DIR}/external/devil/ilu.lib" "${CMAKE_SOURCE_DIR}/external/devil/DevIL.lib" "${CMAKE_SOURCE_DIR}/external/sdl2/lib/x86/SDL2.lib" "${CMAKE_SOURCE_DIR}/external/openal/out/build/x86-Release/OpenAL32.lib") diff --git a/code/REDALERT/CONQUER.CPP b/code/REDALERT/CONQUER.CPP index d2ca0b7..b047664 100644 --- a/code/REDALERT/CONQUER.CPP +++ b/code/REDALERT/CONQUER.CPP @@ -743,8 +743,9 @@ void Keyboard_Process(KeyNumType & input) ** Center the map about the construction yard or construction vehicle ** if one is present. */ - if (key != 0 && key == VK_H) { + if (key != 0 && key == Options.KeyBase) { Unselect_All(); + if (PlayerPtr->CurBuildings) { for (index = 0; index < Buildings.Count(); index++) { BuildingClass * building = Buildings.Ptr(index); @@ -875,19 +876,19 @@ void Keyboard_Process(KeyNumType & input) ** Scrolls the tactical map in the direction specified. */ int distance = CELL_LEPTON_W; - if (key != 0 && key == VK_LEFT) { + if (key != 0 && key == KN_LEFT) { Map.Scroll_Map(DIR_W, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_RIGHT) { + if (key != 0 && key == KN_RIGHT) { Map.Scroll_Map(DIR_E, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_UP) { + if (key != 0 && key == KN_UP) { Map.Scroll_Map(DIR_N, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_DOWN) { + if (key != 0 && key == KN_DOWN) { Map.Scroll_Map(DIR_S, distance, true); input = KN_NONE; } diff --git a/code/REDALERT/DISPLAY.CPP b/code/REDALERT/DISPLAY.CPP index c162eea..0687890 100644 --- a/code/REDALERT/DISPLAY.CPP +++ b/code/REDALERT/DISPLAY.CPP @@ -3139,6 +3139,8 @@ int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) action = Best_Object_Action(cell); } + // this updates the mouse cursor + Map.Mouse_Left_Up(cell, shadow, object, action); } else { if (object && object->Class_Of().IsSelectable) { diff --git a/code/REDALERT/DISPLAY.H b/code/REDALERT/DISPLAY.H index f47315a..ddd2cb0 100644 --- a/code/REDALERT/DISPLAY.H +++ b/code/REDALERT/DISPLAY.H @@ -275,7 +275,7 @@ class DisplayClass: public MapClass */ class TacticalClass : public GadgetClass { public: - TacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + TacticalClass(void) : GadgetClass(0,0,0,0,KEYBOARD|LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; int Selection_At_Mouse(unsigned flags, KeyNumType & key); int Command_Object(unsigned flags, KeyNumType & key); diff --git a/code/REDALERT/GADGET.CPP b/code/REDALERT/GADGET.CPP index 4d3b60c..1a65352 100644 --- a/code/REDALERT/GADGET.CPP +++ b/code/REDALERT/GADGET.CPP @@ -530,7 +530,7 @@ KeyNumType GadgetClass::Input(void) ** Sweep through all the buttons in the chain and pass the current button state ** and keyboard input data to them. These routines will detect whether they should ** perform some action and return a flag to this effect. They also have the option - ** of changing the key value so that an appropriate return value is use for this + ** of changing the key value so that an appropriate return value is used for this ** processing routine. */ GadgetClass * next_button = this; diff --git a/code/REDALERT/KEY.CPP b/code/REDALERT/KEY.CPP index 9ff74b4..354ce01 100644 --- a/code/REDALERT/KEY.CPP +++ b/code/REDALERT/KEY.CPP @@ -53,6 +53,7 @@ //#include "key.h" #include "FUNCTION.H" +#include "SDL.h" #include "monoc.h" @@ -359,7 +360,8 @@ char WWKeyboardClass::To_ASCII(unsigned short key) *=============================================================================================*/ bool WWKeyboardClass::Down(unsigned short key) { - return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); + //return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); + return UserInput.KeyB.Key_Down( (KeyNumType)key ); } diff --git a/code/REDALERT/KEY.H b/code/REDALERT/KEY.H index 194d86e..263c1ae 100644 --- a/code/REDALERT/KEY.H +++ b/code/REDALERT/KEY.H @@ -523,115 +523,119 @@ typedef enum KeyASCIIType { typedef enum KeyNumType { - KN_NONE = 0, + KN_NONE = 0, - KN_0 = VK_0, - KN_1 = VK_1, - KN_2 = VK_2, - KN_3 = VK_3, - KN_4 = VK_4, - KN_5 = VK_5, - KN_6 = VK_6, - KN_7 = VK_7, - KN_8 = VK_8, - KN_9 = VK_9, - KN_A = VK_A, - KN_B = VK_B, - KN_BACKSLASH = VK_NONE_DC, - KN_BACKSPACE = VK_BACK, - KN_C = VK_C, - KN_CAPSLOCK = VK_CAPITAL, - KN_CENTER = VK_CLEAR, - KN_COMMA = VK_NONE_BC, - KN_D = VK_D, - KN_DELETE = VK_DELETE, - KN_DOWN = VK_DOWN, - KN_DOWNLEFT = VK_END, - KN_DOWNRIGHT = VK_NEXT, - KN_E = VK_E, - KN_END = VK_END, - KN_EQUAL = VK_NONE_BB, - KN_ESC = VK_ESCAPE, - KN_E_DELETE = VK_DELETE, - KN_E_DOWN = VK_NUMPAD2, - KN_E_END = VK_NUMPAD1, - KN_E_HOME = VK_NUMPAD7, - KN_E_INSERT = VK_INSERT, - KN_E_LEFT = VK_NUMPAD4, - KN_E_PGDN = VK_NUMPAD3, - KN_E_PGUP = VK_NUMPAD9, - KN_E_RIGHT = VK_NUMPAD6, - KN_E_UP = VK_NUMPAD8, - KN_F = VK_F, - KN_F1 = VK_F1, - KN_F10 = 67, - KN_F11 = 68, - KN_F12 = 69, - KN_F2 = 59, - KN_F3 = 60, - KN_F4 = 61, - KN_F5 = 62, - KN_F6 = 63, - KN_F7 = 64, - KN_F8 = 65, - KN_F9 = 66, - KN_G = VK_G, - KN_GRAVE = VK_NONE_C0, - KN_H = VK_H, - KN_HOME = VK_HOME, - KN_I = VK_I, - KN_INSERT = VK_INSERT, - KN_J = VK_J, - KN_K = VK_K, - KN_KEYPAD_ASTERISK= VK_MULTIPLY, - KN_KEYPAD_MINUS = VK_SUBTRACT, - KN_KEYPAD_PLUS = VK_ADD, - KN_KEYPAD_RETURN = VK_RETURN, - KN_KEYPAD_SLASH = VK_DIVIDE, - KN_L = VK_L, - KN_LALT = VK_MENU, - KN_LBRACKET = VK_NONE_DB, - KN_LCTRL = VK_CONTROL, - KN_LEFT = VK_LEFT, - KN_LMOUSE = VK_LBUTTON, - KN_LSHIFT = VK_SHIFT, - KN_M = VK_M, - KN_MINUS = VK_NONE_BD, - KN_N = VK_N, - KN_NUMLOCK = VK_NUMLOCK, - KN_O = VK_O, - KN_P = VK_P, - KN_PAUSE = VK_PAUSE, - KN_PERIOD = VK_NONE_BE, - KN_PGDN = VK_NEXT, - KN_PGUP = VK_PRIOR, - KN_PRNTSCRN = VK_PRINT, - KN_Q = VK_Q, - KN_R = VK_R, - KN_RALT = VK_MENU, - KN_RBRACKET = VK_NONE_DD, - KN_RCTRL = VK_CONTROL, - KN_RETURN = VK_RETURN, - KN_RIGHT = VK_RIGHT, - KN_RMOUSE = VK_RBUTTON, - KN_RSHIFT = VK_SHIFT, - KN_S = VK_S, - KN_SCROLLLOCK = VK_SCROLL, - KN_SEMICOLON = VK_NONE_BA, - KN_SLASH = VK_NONE_BF, - KN_SPACE = VK_SPACE, - KN_SQUOTE = VK_NONE_DE, - KN_T = VK_T, - KN_TAB = VK_TAB, - KN_U = VK_U, - KN_UP = VK_UP, - KN_UPLEFT = VK_HOME, - KN_UPRIGHT = VK_PRIOR, - KN_V = VK_V, - KN_W = VK_W, - KN_X = VK_X, - KN_Y = VK_Y, - KN_Z = VK_Z, + KN_0, + KN_1, + KN_2, + KN_3, + KN_4, + KN_5, + KN_6, + KN_7, + KN_8, + KN_9, + KN_A, + KN_B, + KN_C, + KN_D, + KN_E, + KN_F, + KN_G, + KN_H, + KN_I, + KN_J, + KN_K, + KN_L, + KN_M, + KN_N, + KN_O, + KN_P, + KN_Q, + KN_R, + KN_S, + KN_T, + KN_U, + KN_V, + KN_W, + KN_X, + KN_Y, + KN_Z, + + KN_BACKSLASH, + KN_BACKSPACE, + KN_CAPSLOCK, + KN_CENTER, + KN_COMMA, + KN_DELETE, + KN_DOWN, + KN_DOWNLEFT, + KN_DOWNRIGHT, + KN_END, + KN_EQUAL, + KN_ESC, + KN_E_DELETE, + KN_E_DOWN, + KN_E_END, + KN_E_HOME, + KN_E_INSERT, + KN_E_LEFT, + KN_E_PGDN, + KN_E_PGUP, + KN_E_RIGHT, + KN_E_UP, + KN_GRAVE, + KN_HOME, + KN_INSERT, + KN_KEYPAD_ASTERISK, + KN_KEYPAD_MINUS, + KN_KEYPAD_PLUS, + KN_KEYPAD_RETURN, + KN_KEYPAD_SLASH, + KN_MINUS, + KN_NUMLOCK, + KN_PAUSE, + KN_PERIOD, + KN_PGDN, + KN_PGUP, + KN_PRNTSCRN, + KN_SCROLLLOCK, + KN_SEMICOLON, + KN_SLASH, + KN_SPACE, + KN_SQUOTE, + KN_TAB, + KN_UP, + KN_UPLEFT, + KN_UPRIGHT, + + KN_LALT, + KN_LBRACKET, + KN_LCTRL, + KN_LEFT, + KN_LMOUSE, + KN_LSHIFT, + + KN_RALT, + KN_RBRACKET, + KN_RCTRL, + KN_RETURN, + KN_RIGHT, + KN_RMOUSE, + KN_RSHIFT, + + KN_F1, + KN_F2, + KN_F3, + KN_F4, + KN_F5, + KN_F6, + KN_F7, + KN_F8, + KN_F9, + KN_F10, + KN_F11, + KN_F12, KN_SHIFT_BIT = WWKEY_SHIFT_BIT, KN_CTRL_BIT = WWKEY_CTRL_BIT, diff --git a/code/REDALERT/POWER.CPP b/code/REDALERT/POWER.CPP index 7499cb5..8c1b18f 100644 --- a/code/REDALERT/POWER.CPP +++ b/code/REDALERT/POWER.CPP @@ -191,13 +191,13 @@ void PowerClass::Draw_It(bool complete) int bottomOfBar = (88 * RESFACTOR) + 56; - while(bottomOfBar < PowerButton.Height + 34 ) + while(bottomOfBar < PowerButton.Height + 42 ) { CC_Draw_Shape(PowerBarShape, 1, ScreenWidth - (640 - (240 * RESFACTOR)), bottomOfBar, WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); bottomOfBar += 8 * RESFACTOR; } - int bottom = ( PowerButton.Y * RESFACTOR ) + PowerButton.Height; + int bottom = bottomOfBar + ( 28 * RESFACTOR ); /* ** Determine how much the power production exceeds or falls short @@ -208,8 +208,6 @@ void PowerClass::Draw_It(bool complete) power_height = Bound(power_height, 0, PowerButton.Height - 2); drain_height = Bound(drain_height, 0, PowerButton.Height - 2); - bottom -= (15 * RESFACTOR); - /* ** Draw the power output graphic on top of the power bar framework. */ @@ -245,7 +243,10 @@ void PowerClass::Draw_It(bool complete) /* ** Draw the power drain threshold marker. */ - CC_Draw_Shape(PowerShape, 0, ScreenWidth - (640 - (((POWER_X * RESFACTOR)) +RESFACTOR)), bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + if (PlayerPtr->Drain > 0) + { + CC_Draw_Shape(PowerShape, 0, ScreenWidth - (640 - (((POWER_X * RESFACTOR)) + RESFACTOR)), bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + } } LogicPage->Unlock(); } diff --git a/code/REDALERT/RADAR.CPP b/code/REDALERT/RADAR.CPP index 2c40ad3..1680711 100644 --- a/code/REDALERT/RADAR.CPP +++ b/code/REDALERT/RADAR.CPP @@ -69,17 +69,17 @@ #include "function.h" #include "Image.h" -//void const * RadarClass::CoverShape; + //void const * RadarClass::CoverShape; RadarClass::RTacticalClass RadarClass::RadarButton; -void const * RadarClass::RadarAnim = NULL; -void const * RadarClass::RadarPulse = NULL; -void const * RadarClass::RadarFrame = NULL; +void const* RadarClass::RadarAnim = NULL; +void const* RadarClass::RadarPulse = NULL; +void const* RadarClass::RadarFrame = NULL; static bool FullRedraw = false; -static GraphicBufferClass _IconStage(3,3); -static GraphicBufferClass _TileStage(24,24); +static GraphicBufferClass _IconStage(3, 3); +static GraphicBufferClass _TileStage(24, 24); /*********************************************************************************************** @@ -147,24 +147,36 @@ RadarClass::RadarClass(void) : *=============================================================================================*/ void RadarClass::One_Time(void) { - RadWidth = 80 * RESFACTOR; - RadHeight = 70 * RESFACTOR; - RadX = SeenBuff.Get_Width() - RadWidth; - RadY = 7 * RESFACTOR; - RadPWidth = 64 * RESFACTOR; - RadPHeight = 64 * RESFACTOR; - - RadOffX = 6; - RadOffY = 7; - RadIWidth = 128+18;//************ - RadIHeight = 128+2;//************ + RadWidth = 80 * RESFACTOR; + RadHeight = 70 * RESFACTOR; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = 7 * RESFACTOR; + RadPWidth = 64 * RESFACTOR; + RadPHeight = 64 * RESFACTOR; +#ifdef WIN32 + RadOffX = 6; + RadOffY = 7; + RadIWidth = 128 + 18;//************ + RadIHeight = 128 + 2;//************ +#else + RadOffX = 4; + RadOffY = 1; + RadIWidth = 72; + RadIHeight = 69; +#endif DisplayClass::One_Time(); - - RadarButton.X = RadX; - RadarButton.Y = RadY; - RadarButton.Width = RadWidth; - RadarButton.Height = RadHeight; +#ifdef OBSOLETE + RadarButton.X = RadX + RadOffX; + RadarButton.Y = RadY + RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +#else + RadarButton.X = RadX; + RadarButton.Y = RadY; + RadarButton.Width = RadWidth; + RadarButton.Height = RadHeight; +#endif } @@ -186,14 +198,14 @@ void RadarClass::One_Time(void) void RadarClass::Init_Clear(void) { DisplayClass::Init_Clear(); - IsRadarActive = false; - IsToRedraw = true; - RadarCursorRedraw = true; - IsRadarActivating = false; - IsRadarDeactivating = false; - DoesRadarExist = false; - PixelPtr = 0; - IsPlayerNames = false; + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; /* ** If we have a valid map lets make sure that we set it correctly @@ -238,88 +250,95 @@ bool RadarClass::Radar_Activate(int control) /* ** Toggle the state of the radar map on or off. */ - case -1: - { - int temp = (IsRadarActive == false); - if (temp) { - Radar_Activate(1); - } else { - Radar_Activate(0); + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } + else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } + else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; } } - break; + } + else { + Radar_Activate(2); + } + return(old); - /* - ** Turn the radar map off properly. - */ - case 0: - if (Map.IsSidebarActive) { - if (IsRadarActive && !IsRadarDeactivating) { - Sound_Effect(VOC_RADAR_OFF); - IsRadarDeactivating = true; - IsRadarActive = false; - if (IsRadarActivating == true) { - IsRadarActivating = false; - } else { - RadarAnimFrame = RADAR_ACTIVATED_FRAME; + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } + else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } + else { + RadarAnimFrame = 0; } } - } else { - Radar_Activate(2); } - return(old); + } + else { + Radar_Activate(3); + } + return(old); - case 1: - if (Map.IsSidebarActive) { - if (!IsRadarActivating && !IsRadarActive) { - Sound_Effect(VOC_RADAR_ON); - IsRadarActivating = true; - if (IsRadarDeactivating == true) { - IsRadarDeactivating = false; - } else { - if (DoesRadarExist) { - RadarAnimFrame = MAX_RADAR_FRAMES; - } else { - RadarAnimFrame = 0; - } - } - } - } else { - Radar_Activate(3); - } - return(old); + case 2: + if (Session.Type == GAME_NORMAL) { + SidebarClass::Zoom.Disable(); + } + else { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; - case 2: - if (Session.Type==GAME_NORMAL) { - SidebarClass::Zoom.Disable(); - } else { - SidebarClass::Zoom.Enable(); - } - IsRadarActive = false; - IsRadarActivating = false; - IsRadarDeactivating = false; - break; + case 3: + if (Session.Type == GAME_NORMAL && Is_Zoomable()) { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; - case 3: - if (Session.Type == GAME_NORMAL && Is_Zoomable()) { - SidebarClass::Zoom.Enable(); - } - IsRadarActive = true; - IsRadarActivating = false; - IsRadarDeactivating = false; - break; + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; - case 4: - IsRadarActive = false; - IsRadarActivating = false; - IsRadarDeactivating = false; - DoesRadarExist = false; - Flag_To_Redraw(false); - IsToRedraw = true; - break; - - default: - break; + default: + break; } if (IsRadarActive != old) { @@ -352,7 +371,7 @@ void RadarClass::Draw_It(bool forced) { DisplayClass::Draw_It(forced); - static char * _hiresradarnames[]={ + static char* _hiresradarnames[] = { "natoradr.shp", //HOUSE_SPAIN, "natoradr.shp", //HOUSE_GREECE, "ussrradr.shp", //HOUSE_USSR, @@ -364,7 +383,7 @@ void RadarClass::Draw_It(bool forced) "natoradr.shp", //HOUSE_GOOD "ussrradr.shp", //HOUSE_BAD }; - static char * _frames[]={ + static char* _frames[] = { "nradrfrm.shp", //HOUSE_SPAIN, "nradrfrm.shp", //HOUSE_GREECE, "uradrfrm.shp", //HOUSE_USSR, @@ -391,42 +410,46 @@ void RadarClass::Draw_It(bool forced) if (PlayerPtr->ActLike != _house) { char name[_MAX_FNAME + _MAX_EXT]; -// strcpy(name, "NATORADR.SHP" ); -// if (Session.Type == GAME_NORMAL) { - strcpy(name, _hiresradarnames[PlayerPtr->ActLike]); -// } - #ifndef NDEBUG - RawFileClass file(name); - if (file.Is_Available()) { - RadarAnim = Load_Alloc_Data(file); - } else { - RadarAnim = MFCD::Retrieve(name); - } - strcpy(name, "PULSE.SHP"); - RawFileClass file2(name); - if (file2.Is_Available()) { - RadarPulse = Load_Alloc_Data(file2); - } else { - RadarPulse = MFCD::Retrieve(name); - } - strcpy(name, _frames[PlayerPtr->ActLike]); - RawFileClass file3(name); - if (file3.Is_Available()) { - RadarFrame = Load_Alloc_Data(file3); - } else { - RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); - } - #else + // strcpy(name, "NATORADR.SHP" ); + // if (Session.Type == GAME_NORMAL) { + strcpy(name, _hiresradarnames[PlayerPtr->ActLike]); + // } +#ifndef NDEBUG + RawFileClass file(name); + if (file.Is_Available()) { + RadarAnim = Load_Alloc_Data(file); + } + else { RadarAnim = MFCD::Retrieve(name); - strcpy(name, "PULSE.SHP"); - RawFileClass file3(name); - if (file3.Is_Available()) { - RadarPulse = Load_Alloc_Data(file3); - } else { - RadarPulse = MFCD::Retrieve(name); - } + } + strcpy(name, "PULSE.SHP"); + RawFileClass file2(name); + if (file2.Is_Available()) { + RadarPulse = Load_Alloc_Data(file2); + } + else { + RadarPulse = MFCD::Retrieve(name); + } + strcpy(name, _frames[PlayerPtr->ActLike]); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarFrame = Load_Alloc_Data(file3); + } + else { + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + } +#else + RadarAnim = MFCD::Retrieve(name); + strcpy(name, "PULSE.SHP"); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarPulse = Load_Alloc_Data(file3); + } + else { + RadarPulse = MFCD::Retrieve(name); + } RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); - #endif +#endif _house = PlayerPtr->ActLike; } @@ -435,7 +458,7 @@ void RadarClass::Draw_It(bool forced) */ if (IsPlayerNames) { Draw_Names(); - // IsToRedraw = false; + // IsToRedraw = false; BEnd(BENCH_RADAR); return; } @@ -463,7 +486,7 @@ void RadarClass::Draw_It(bool forced) if (Map.IsSidebarActive) { if (IsRadarActive) { - + if (RunningAsDLL) { BEnd(BENCH_RADAR); return; @@ -484,7 +507,7 @@ void RadarClass::Draw_It(bool forced) if (LogicPage->Lock()) { for (int index = 0; index < (int)PixelPtr; index++) { CELL cell = PixelStack[index]; - if (Cell_On_Radar(cell)) { + if (Cell_On_Radar(cell)) { (*this)[cell].IsPlot = false; Plot_Radar_Pixel(cell); RadarCursorRedraw |= (*this)[cell].IsRadarCursor; @@ -503,52 +526,55 @@ void RadarClass::Draw_It(bool forced) for (int y = 0; y < MapCellHeight; y++) { for (int x = 0; x < MapCellWidth; x++) { CELL cell = XY_Cell(MapCellX + x, MapCellY + y); - if (Cell_On_Radar(cell)) { + if (Cell_On_Radar(cell)) { if ((*this)[cell].IsPlot) { PixelStack[PixelPtr++] = cell; IsToRedraw = true; - if (PixelPtr == PIXELSTACK) break; + if (PixelPtr == PIXELSTACK) break; } } } - if (PixelPtr == PIXELSTACK) break; + if (PixelPtr == PIXELSTACK) break; } - } else { + } + else { PixelPtr = 0; } } Radar_Cursor(RadarCursorRedraw); - } else { + } + else { #ifdef WIN32 - GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass* oldpage = Set_Logic_Page(HidPage); #else - GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); + GraphicBufferClass* oldpage = Set_Logic_Page(HidPage); #endif - CC_Draw_Shape(RadarFrame, 1, RadX, RadY+(1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); if (BaseX || BaseY) { - if (!IsZoomed && BaseX && BaseY && (int)RadarWidth< (RadIWidth-1) && (int)RadarHeight < (RadIHeight-1)) { + if (!IsZoomed && BaseX && BaseY && (int)RadarWidth < (RadIWidth - 1) && (int)RadarHeight < (RadIHeight - 1)) { #ifdef WIN32 - LogicPage->Draw_Rect(RadX + RadOffX + BaseX -1, - RadY + RadOffY + BaseY -1, - RadX + RadOffX + BaseX + RadarWidth, -// RadX + RadOffX + BaseX + RadarWidth +1, - RadY + RadOffY + BaseY + RadarHeight, -// RadY + RadOffY + BaseY + RadarHeight +1, - WHITE); + LogicPage->Draw_Rect(RadX + RadOffX + BaseX - 1, + RadY + RadOffY + BaseY - 1, + RadX + RadOffX + BaseX + RadarWidth, + // RadX + RadOffX + BaseX + RadarWidth +1, + RadY + RadOffY + BaseY + RadarHeight, + // RadY + RadOffY + BaseY + RadarHeight +1, + WHITE); #endif } - } else { - LogicPage->Fill_Rect( RadX + RadOffX, - RadY + RadOffY, - RadX + RadOffX + RadIWidth - 1, - RadY + RadOffY + RadIHeight - 1, - BLACK); + } + else { + LogicPage->Fill_Rect(RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); } /* @@ -561,7 +587,7 @@ void RadarClass::Draw_It(bool forced) } } if (IsPulseActive) { - CC_Draw_Shape(RadarPulse, RadarPulseFrame++, RadX + RadOffX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarPulse, RadarPulseFrame++, RadX + RadOffX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); } LogicPage->Unlock(); } @@ -583,12 +609,13 @@ void RadarClass::Draw_It(bool forced) Set_Logic_Page(oldpage); } - } else { + } + else { /* ** If the radar is not active, then only draw the cover plate if forced to do so. */ - int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; CC_Draw_Shape(RadarAnim, val, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); //FullRedraw = false; //IsToRedraw = false; @@ -602,7 +629,7 @@ void RadarClass::Draw_It(bool forced) ** Display the country name on the cover plate when in multi play only. */ if (Session.Type != GAME_NORMAL) { - Fancy_Text_Print(Text_String(HouseTypeClass::As_Reference(PlayerPtr->ActLike).Full_Name()), RadX+RadWidth/2, RadY+RadHeight-10*RESFACTOR, &ColorRemaps[PlayerPtr->RemapColor], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + Fancy_Text_Print(Text_String(HouseTypeClass::As_Reference(PlayerPtr->ActLike).Full_Name()), RadX + RadWidth / 2, RadY + RadHeight - 10 * RESFACTOR, &ColorRemaps[PlayerPtr->RemapColor], TBLACK, TPF_CENTER | TPF_TEXT | TPF_DROPSHADOW); } Map.Repair.Draw_Me(true); @@ -630,28 +657,28 @@ void RadarClass::Draw_It(bool forced) *=========================================================================*/ void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) { - TerrainClass * list[4] = {0,0,0,0}; + TerrainClass* list[4] = { 0,0,0,0 }; int listidx = 0; - int lp,lp2; + int lp, lp2; - ObjectClass * obj = Map[cell].Cell_Occupier(); + ObjectClass* obj = Map[cell].Cell_Occupier(); /* ** If the cell is occupied by a terrain type, add it to the sortable ** list. */ if (obj && obj->What_Am_I() == RTTI_TERRAIN) - list[listidx++] = (TerrainClass *)obj; + list[listidx++] = (TerrainClass*)obj; /* ** Now loop through all the occupiers and add them to the list if they ** are terrain type. */ - for (lp = 0; lp < ARRAY_SIZE(Map[cell].Overlapper); lp ++) { + for (lp = 0; lp < ARRAY_SIZE(Map[cell].Overlapper); lp++) { obj = Map[cell].Overlapper[lp]; if (obj && obj->What_Am_I() == RTTI_TERRAIN) - list[listidx++] = (TerrainClass *)obj; + list[listidx++] = (TerrainClass*)obj; } /* @@ -665,7 +692,7 @@ void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) */ if (size == 1) { LogicPage->Put_Pixel(x, y, 21); -// LogicPage->Put_Pixel(x, y, 60); + // LogicPage->Put_Pixel(x, y, 60); return; } @@ -673,10 +700,10 @@ void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) ** Sort the list by its sort Y value so that we can render in the proper ** order. */ - for (lp = 0; lp < listidx - 1; lp ++) { + for (lp = 0; lp < listidx - 1; lp++) { for (lp2 = lp + 1; lp2 < listidx; lp2++) { if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { - TerrainClass * terrain = list[lp]; + TerrainClass* terrain = list[lp]; list[lp] = list[lp2]; list[lp2] = terrain; } @@ -686,12 +713,12 @@ void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) /* ** loop through the list and take care of rendering the correct icon. */ - for (lp = 0; lp < listidx; lp ++) { - unsigned char * icon = list[lp]->Radar_Icon(cell); + for (lp = 0; lp < listidx; lp++) { + unsigned char* icon = list[lp]->Radar_Icon(cell); if (!icon) continue; #ifdef WIN32 Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); - _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char *)&FadingBrighten[0]); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char*)&FadingBrighten[0]); #else for (int lpy = 0; lpy < 3; lpy++) { for (int lpx = 0; lpx < 3; lpx++) { @@ -727,44 +754,44 @@ void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) *=============================================================================================*/ void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) { - ObjectClass * obj; + ObjectClass* obj; - obj = (ObjectClass *)Map[cell].Cell_Occupier(); + obj = (ObjectClass*)Map[cell].Cell_Occupier(); while (obj) { - if (obj->Is_Techno() && ((TechnoClass *)obj)->Is_Visible_On_Radar()) { - int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].Bar; -// int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].BrightColor; + if (obj->Is_Techno() && ((TechnoClass*)obj)->Is_Visible_On_Radar()) { + int color = ColorRemaps[((InfantryClass*)obj)->House->RemapColor].Bar; + // int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].BrightColor; int xoff; int yoff; - int subsize = max(1, size/3); + int subsize = max(1, size / 3); switch (obj->What_Am_I()) { - case RTTI_INFANTRY: - xoff = (Coord_XLepton(obj->Coord) / (CELL_LEPTON_W/(size+1))) - subsize/2; - xoff = max(xoff, 0); - xoff = min(xoff, size-subsize); - yoff = (Coord_YLepton(obj->Coord) / (CELL_LEPTON_H/(size+1))) - subsize/2; - yoff = max(yoff, 0); - yoff = min(yoff, size-subsize); + case RTTI_INFANTRY: + xoff = (Coord_XLepton(obj->Coord) / (CELL_LEPTON_W / (size + 1))) - subsize / 2; + xoff = max(xoff, 0); + xoff = min(xoff, size - subsize); + yoff = (Coord_YLepton(obj->Coord) / (CELL_LEPTON_H / (size + 1))) - subsize / 2; + yoff = max(yoff, 0); + yoff = min(yoff, size - subsize); - /* - ** Draw the infantryman's pixel. If he's a spy, draw in my house color - */ - if (*(InfantryClass *)obj == INFANTRY_SPY) { - color = ColorRemaps[PlayerPtr->RemapColor].Bar; -// color = ColorRemaps[PlayerPtr->RemapColor].BrightColor; - } - LogicPage->Fill_Rect(x+xoff, y+yoff, x+xoff+(subsize-1), y+yoff+(subsize-1), color); - break; + /* + ** Draw the infantryman's pixel. If he's a spy, draw in my house color + */ + if (*(InfantryClass*)obj == INFANTRY_SPY) { + color = ColorRemaps[PlayerPtr->RemapColor].Bar; + // color = ColorRemaps[PlayerPtr->RemapColor].BrightColor; + } + LogicPage->Fill_Rect(x + xoff, y + yoff, x + xoff + (subsize - 1), y + yoff + (subsize - 1), color); + break; - case RTTI_UNIT: - case RTTI_VESSEL: - case RTTI_AIRCRAFT: - LogicPage->Fill_Rect(x, y, x+size-1, y+size-1, color); - break; + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + LogicPage->Fill_Rect(x, y, x + size - 1, y + size - 1, color); + break; - default: - break; + default: + break; } } obj = obj->Next; @@ -790,11 +817,11 @@ void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) //int lpx,lpy; OverlayType overlay = (*this)[cell].Overlay; - if (overlay != OVERLAY_NONE) { - OverlayTypeClass const * otype = &OverlayTypeClass::As_Reference(overlay); + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const* otype = &OverlayTypeClass::As_Reference(overlay); if (otype->IsRadarVisible) { - unsigned char * icon = otype->Radar_Icon((*this)[cell].OverlayData); + unsigned char* icon = otype->Radar_Icon((*this)[cell].OverlayData); if (!icon) return; #ifdef WIN32 Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); @@ -802,35 +829,38 @@ void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) if (size == 1) { LogicPage->Put_Pixel(x, y, DKGREY); -// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingShade[0]); - } else { - _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingYellow[0]); + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingShade[0]); } -// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); -// } else { -// _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char*)&FadingYellow[0]); + } + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); + // } else { + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); } #else for (int lpy = 0; lpy < size; lpy++) { for (int lpx = 0; lpx < size; lpx++) { - if (size == 1) icon+=4; + if (size == 1) icon += 4; if (*icon) { if (otype->IsTiberium) { if (size == 1) { LogicPage->Put_Pixel(x + lpx, y + lpy, DKGREY); -// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingShade[*icon]); - } else { + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingShade[*icon]); + } + else { LogicPage->Put_Pixel(x + lpx, y + lpy, FadingYellow[*icon]); } -// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingGreen[*icon]); -// } else { -// LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingGreen[*icon]); + // } else { + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); } } if (size == 1) { - icon+=5; - } else { + icon += 5; + } + else { icon++; } icon++; @@ -863,30 +893,32 @@ void RadarClass::Zoom_Mode(CELL cell) */ #ifdef WIN32 if (Is_Zoomable()) { - IsZoomed = !IsZoomed; - } else { + IsZoomed = !IsZoomed; + } + else { IsZoomed = true; } #else - IsZoomed = false; + IsZoomed = false; #endif - BaseX = 0; - BaseY = 0; + BaseX = 0; + BaseY = 0; /* ** Figure out exactly what size we need to zoom the map to. */ if (!IsZoomed) { - int xfactor = RadIWidth / MapCellWidth; - int yfactor = RadIHeight / MapCellHeight; - ZoomFactor = max(min(xfactor, yfactor) , 1); - map_c_width = MapCellWidth; - map_c_height = MapCellHeight; - } else { - ZoomFactor = 3; -// ZoomFactor = 6; - map_c_width = RadIWidth / ZoomFactor; - map_c_height = RadIHeight / ZoomFactor; + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = max(min(xfactor, yfactor), 1); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } + else { + ZoomFactor = 3; + // ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; } /* @@ -894,40 +926,40 @@ void RadarClass::Zoom_Mode(CELL cell) */ #ifdef FIXIT_CSII // checked - ajw 9/28/98 #ifdef WIN32 - map_c_width = min(map_c_width, RadIWidth); - map_c_width = min(map_c_width, MapCellWidth); - map_c_height = min(map_c_height, RadIHeight); - map_c_height = min(map_c_height, MapCellHeight); + map_c_width = min(map_c_width, RadIWidth); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, RadIHeight); + map_c_height = min(map_c_height, MapCellHeight); #else - map_c_width = min(map_c_width, 62 * RESFACTOR); - map_c_width = min(map_c_width, MapCellWidth); - map_c_height = min(map_c_height, 62 * RESFACTOR); - map_c_height = min(map_c_height, MapCellHeight); + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); #endif #else - map_c_width = min(map_c_width, 62 * RESFACTOR); - map_c_width = min(map_c_width, MapCellWidth); - map_c_height = min(map_c_height, 62 * RESFACTOR); - map_c_height = min(map_c_height, MapCellHeight); + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); #endif /* ** Find the amount of remainder because this will let us calculate ** how to center the thing. */ - int rem_x = RadIWidth - (map_c_width * ZoomFactor); - int rem_y = RadIHeight - (map_c_height * ZoomFactor); + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); /* ** Finally mark the map so it shows just as much as it is supposed ** to. */ - BaseX = rem_x / 2; - BaseY = rem_y / 2; - RadarCellWidth = map_c_width; - RadarCellHeight = map_c_height; - RadarWidth = RadIWidth - rem_x; - RadarHeight = RadIHeight - rem_y; + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIHeight - rem_y; /* ** Set the radar position to the current cell. @@ -972,7 +1004,7 @@ bool RadarClass::Is_Zoomable(void) const { int xfactor = RadIWidth / MapCellWidth; int yfactor = RadIHeight / MapCellHeight; - int factor = max(min(xfactor, yfactor) , 1); + int factor = max(min(xfactor, yfactor), 1); if (factor == 3) { return(false); } @@ -1008,7 +1040,7 @@ void RadarClass::Plot_Radar_Pixel(CELL cell) { if (cell == -1) cell = 1; - int x,y; // Coordinate of cell location. + int x, y; // Coordinate of cell location. /* ** Perform any clipping on the cell coordinate. @@ -1033,32 +1065,33 @@ void RadarClass::Plot_Radar_Pixel(CELL cell) bool usjamming = false; if (LogicPage->Lock()) { - CellClass * cellptr = &(*this)[cell]; - x = RadX + RadOffX + BaseX + (x * ZoomFactor); - y = RadY + RadOffY + BaseY + (y * ZoomFactor); + CellClass* cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); - /* - ** Determine what (if any) vehicle or unit should be rendered in this blip. - */ - int color=TBLACK; // Color of the pixel to plot. + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color = TBLACK; // Color of the pixel to plot. int housebit = (1 << PlayerPtr->Class->House); int celljammed = (*this)[cell].Jammed; int jammed = celljammed & (0xFFFF - housebit); - if (!jammed && ((*this)[cell].IsMapped || Debug_Unshroud)) { -// if (!jammed && ((*this)[cell].IsVisible || Debug_Unshroud)) { - color = cellptr->Cell_Color(true); - if ( (celljammed & housebit) && (color == TBLACK) ) { + if (!jammed && ((*this)[cell].IsMapped || Debug_Unshroud)) { + // if (!jammed && ((*this)[cell].IsVisible || Debug_Unshroud)) { + color = cellptr->Cell_Color(true); + if ((celljammed & housebit) && (color == TBLACK)) { color = BLACK;//FadingWayDark[color]; usjamming = true; } - } else { - color = BLACK; - } + } + else { + color = BLACK; + } - /* - ** If no color override occurs for this cell, then render the underlying - ** terrain. - */ + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ if (color == TBLACK) { if (ZoomFactor > 1) { const TemplateTypeClass* ptr = NULL; @@ -1082,8 +1115,8 @@ void RadarClass::Plot_Radar_Pixel(CELL cell) icon = cellptr->Clear_Icon(); } - IconsetClass const * iconset = (IconsetClass const *)ptr->Get_Image_Data(); - unsigned char const * icondata = iconset->Icon_Data(); + IconsetClass const* iconset = (IconsetClass const*)ptr->Get_Image_Data(); + unsigned char const* icondata = iconset->Icon_Data(); /* @@ -1091,8 +1124,8 @@ void RadarClass::Plot_Radar_Pixel(CELL cell) */ icon &= 0x00FF; icon = *(iconset->Map_Data() + icon); -// jmarshall - unsigned char * data = (unsigned char *)icondata + icon*(24*24); + // jmarshall + unsigned char* data = (unsigned char*)icondata + icon * (24 * 24); Image_t* image; { char tmp[512]; @@ -1100,23 +1133,26 @@ void RadarClass::Plot_Radar_Pixel(CELL cell) image = Image_CreateImageFrom8Bit(tmp, 24, 24, (unsigned char*)data); } GL_RenderImage(image, x, y, ZoomFactor, ZoomFactor); -// jmarshall end - //Buffer_To_Page(0, 0, 24, 24, data, _TileStage); - //_TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); - } else { -// LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, cellptr->Cell_Color(false)); -/*BG*/ LogicPage->Put_Pixel(x, y, cellptr->Cell_Color(false)); + // jmarshall end + //Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + //_TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); } - } else { - LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, color); -///*BG*/ LogicPage->Put_Pixel(x, y, color); + else { + // LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, cellptr->Cell_Color(false)); + /*BG*/ LogicPage->Put_Pixel(x, y, cellptr->Cell_Color(false)); + } + } + else { + LogicPage->Fill_Rect(x, y, x + ZoomFactor - 1, y + ZoomFactor - 1, color); + ///*BG*/ LogicPage->Put_Pixel(x, y, color); } if (color != BLACK) { Render_Overlay(cell, x, y, ZoomFactor); - Render_Terrain(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); Render_Infantry(cell, x, y, ZoomFactor); - } else { - if(usjamming) { + } + else { + if (usjamming) { Render_Infantry(cell, x, y, ZoomFactor); } } @@ -1175,10 +1211,10 @@ void RadarClass::Radar_Pixel(CELL cell) * 05/30/1995 PWG : Created. * * 07/16/1995 JLB : Recognizes when sidebar is closed now. * *=============================================================================================*/ -int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) const +int RadarClass::Click_In_Radar(int& ptr_x, int& ptr_y, bool change) const { - int x = ptr_x; - int y = ptr_y; + int x = ptr_x; + int y = ptr_y; /* ** If radar is not active the click could have been on a radar point @@ -1191,8 +1227,8 @@ int RadarClass::Click_In_Radar(int &ptr_x, int &ptr_y, bool change) const x -= BaseX; y -= BaseY; - if ((unsigned)x < RadarWidth + (ZoomFactor-1) && (unsigned)y < RadarHeight + (ZoomFactor-1)) { -// if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + if ((unsigned)x < RadarWidth + (ZoomFactor - 1) && (unsigned)y < RadarHeight + (ZoomFactor - 1)) { + // if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { x = RadarX + (x / ZoomFactor); y = RadarY + (y / ZoomFactor); if (change) { @@ -1228,14 +1264,14 @@ CELL RadarClass::Click_Cell_Calc(int x, int y) const { int result = Click_In_Radar(x, y, true); switch (result) { - case 1: - return(XY_Cell(x, y)); + case 1: + return(XY_Cell(x, y)); - case -1: - return(-1); + case -1: + return(-1); - default: - break; + default: + break; } return(DisplayClass::Click_Cell_Calc(x, y)); } @@ -1257,7 +1293,7 @@ CELL RadarClass::Click_Cell_Calc(int x, int y) const * HISTORY: * * 12/22/1994 JLB : Created. * *=============================================================================================*/ -bool RadarClass::Map_Cell(CELL cell, HouseClass * house, bool check_radar_spied, bool and_for_allies) +bool RadarClass::Map_Cell(CELL cell, HouseClass* house, bool check_radar_spied, bool and_for_allies) { if (DisplayClass::Map_Cell(cell, house, check_radar_spied, and_for_allies)) { Radar_Pixel(cell); @@ -1282,12 +1318,12 @@ void RadarClass::Cursor_Cell(CELL cell, int value) /* ** Record the new state of this cell. */ - (*this)[cell].IsRadarCursor = value; + (*this)[cell].IsRadarCursor = value; /* ** If we are erasing then erase the cell. */ - if (value == FALSE) { + if (value == FALSE) { Plot_Radar_Pixel(cell); } } @@ -1364,7 +1400,7 @@ void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barle * 05/22/1991 JLB : Created. * * 11/17/1995 PWG : Created. * *=============================================================================================*/ -void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int& x, int& y) { x = (cellx - RadarX) * ZoomFactor; y = (celly - RadarY) * ZoomFactor; @@ -1387,26 +1423,27 @@ void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y) * HISTORY: * * 11/09/1995 BWG : Created. * *=============================================================================================*/ -bool RadarClass::Jam_Cell(CELL cell, HouseClass * house/*KO, bool shadeit*/) +bool RadarClass::Jam_Cell(CELL cell, HouseClass* house/*KO, bool shadeit*/) { unsigned short jam = 1 << house->Class->House; (*this)[cell].Jammed |= jam; - + /* ** Updated for client/server multiplayer. ST - 8/12/2019 11:00AM */ if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { if (house != PlayerPtr) Shroud_Cell(cell, PlayerPtr/*KO, shadeit*/); - - } else { - + + } + else { + for (int i = 0; i < Session.Players.Count(); i++) { - HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + HouseClass* player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); if (player_house->IsHuman && player_house != house) { Shroud_Cell(cell, player_house); } } - } + } Radar_Pixel(cell); return(true); @@ -1429,7 +1466,7 @@ bool RadarClass::Jam_Cell(CELL cell, HouseClass * house/*KO, bool shadeit*/) * HISTORY: * * 11/09/1995 BWG : Created. * *=============================================================================================*/ -bool RadarClass::UnJam_Cell(CELL cell, HouseClass * house) +bool RadarClass::UnJam_Cell(CELL cell, HouseClass* house) { unsigned short jam = 1 << house->Class->House; (*this)[cell].Redraw_Objects(); @@ -1458,25 +1495,25 @@ bool RadarClass::UnJam_Cell(CELL cell, HouseClass * house) * 05/22/1991 JLB : Created. * * 11/17/1995 PWG : Created. * *=============================================================================================*/ -//#pragma argsused + //#pragma argsused void RadarClass::Radar_Cursor(int forced) { static int _last_pos = -1; static int _last_frame = -1; #ifdef WIN32 - GraphicViewPortClass * oldpage; + GraphicViewPortClass* oldpage; #else - GraphicBufferClass * oldpage; + GraphicBufferClass* oldpage; #endif int x1, y1, x2, y2; /* ** figure out these function calls as we will need to call them multiple times. */ - int tac_cell = Coord_Cell(TacticalCoord); - int tac_cell_x = Cell_X(tac_cell); - int tac_cell_y = Cell_Y(tac_cell); - int barlen = 6; + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; /* ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then @@ -1484,15 +1521,15 @@ void RadarClass::Radar_Cursor(int forced) */ if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; - if ( _last_pos != -1 ) { + if (_last_pos != -1) { /* ** The first thing we need to do is take care of erasing the last radar cell position. We do this ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and ** finally taking care of calling the erase procedure which will convert the pixel coordinates back ** to the cells that need to be redraw. **/ - int last_cell_x = Cell_X(_last_pos); - int last_cell_y = Cell_Y(_last_pos); + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); @@ -1502,10 +1539,10 @@ void RadarClass::Radar_Cursor(int forced) /* ** Adjust the current coordinates based on the last animation frame. */ - x1-= _last_frame; - y1-= _last_frame; - x2+= _last_frame; - y2+= _last_frame; + x1 -= _last_frame; + y1 -= _last_frame; + x2 += _last_frame; + y2 += _last_frame; /* ** Finally mark the map (actually remove the marks that indicate the radar cursor was there @@ -1528,10 +1565,10 @@ void RadarClass::Radar_Cursor(int forced) /* ** Adjust the coordinates based on the current frame of radar animation. */ - x1-= SpecialRadarFrame; - y1-= SpecialRadarFrame; - x2+= SpecialRadarFrame; - y2+= SpecialRadarFrame; + x1 -= SpecialRadarFrame; + y1 -= SpecialRadarFrame; + x2 += SpecialRadarFrame; + y2 += SpecialRadarFrame; Mark_Radar(x1, y1, x2, y2, TRUE, barlen); @@ -1541,25 +1578,25 @@ void RadarClass::Radar_Cursor(int forced) */ oldpage = Set_Logic_Page(HidPage); GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), - RadX + RadOffX + BaseX + LogicPage->Get_XPos(), - RadY + RadOffY + BaseY + LogicPage->Get_YPos(), - RadarWidth, - RadarHeight); + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); - draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); - draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); // Draw upper right hand corner - draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); - draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); // Draw lower left hand corner - draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); - draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); // Draw lower right hand corner - draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); - draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); Set_Logic_Page(oldpage); _last_pos = tac_cell; @@ -1593,26 +1630,26 @@ void RadarClass::Radar_Anim(void) if (!Map.IsSidebarActive) return; #ifdef WIN32 - GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); + GraphicViewPortClass* oldpage = Set_Logic_Page(HidPage); GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), - RadX + RadOffX + LogicPage->Get_XPos(), - RadY + RadOffY + LogicPage->Get_YPos(), - RadIWidth, - RadIHeight); + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); #else - GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + GraphicBufferClass* oldpage = Set_Logic_Page(HidPage); GraphicViewPortClass draw_window(LogicPage, - RadX + RadOffX, - RadY + RadOffY, - RadIWidth, - RadIHeight-2); + RadX + RadOffX, + RadY + RadOffY, + RadIWidth, + RadIHeight - 2); #endif -//Mono_Set_Cursor(0,0); + //Mono_Set_Cursor(0,0); #ifdef WIN32 - Draw_Box(RadX+RadOffX-1, RadY+RadOffY-1, RadIWidth+2, RadIHeight+2, BOXSTYLE_RAISED, true); + Draw_Box(RadX + RadOffX - 1, RadY + RadOffY - 1, RadIWidth + 2, RadIHeight + 2, BOXSTYLE_RAISED, true); #endif draw_window.Clear(); - CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY+1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); Flag_To_Redraw(false); Set_Logic_Page(oldpage); } @@ -1639,7 +1676,7 @@ void RadarClass::Radar_Anim(void) * 12/26/1994 JLB : Moves tactical map with click or drag. * * 12/31/1994 JLB : Uses mouse coordinate parameters. * *=============================================================================================*/ -void RadarClass::AI(KeyNumType & input, int x, int y) +void RadarClass::AI(KeyNumType& input, int x, int y) { /* ** Check to see if we need to animate the radar cursor @@ -1660,16 +1697,19 @@ void RadarClass::AI(KeyNumType & input, int x, int y) if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { IsToRedraw = true; Flag_To_Redraw(false); - } else { + } + else { DoesRadarExist = true; Radar_Activate(3); } - } else { + } + else { RadarAnimFrame--; if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { IsToRedraw = true; Flag_To_Redraw(false); - } else { + } + else { Radar_Activate(3); } } @@ -1682,7 +1722,8 @@ void RadarClass::AI(KeyNumType & input, int x, int y) RadarAnimFrame++; if (RadarAnimFrame == MAX_RADAR_FRAMES) { IsRadarDeactivating = false; - } else { + } + else { IsToRedraw = true; Flag_To_Redraw(false); } @@ -1734,13 +1775,13 @@ void RadarClass::AI(KeyNumType & input, int x, int y) * HISTORY: * * 05/08/1995 JLB : Created. * *=============================================================================================*/ -int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) +int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType& key) { CELL cell; // cell num click happened over - int x,y; // Sub cell pixel coordinates. - int cellx,celly; // Sub cell pixel coordinates. + int x, y; // Sub cell pixel coordinates. + int cellx, celly; // Sub cell pixel coordinates. bool shadow; // is the cell in shadow or not - ObjectClass * object = 0; // what object is in the cell + ObjectClass* object = 0; // what object is in the cell ActionType action = ACTION_NONE; // Action possible with currently selected object. /* @@ -1754,7 +1795,7 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) if (!Map.IsRadarActive) { if (Map.IsSidebarActive) { Map.Override_Mouse_Shape(MOUSE_NORMAL); -// Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + // Map.Override_Mouse_Shape(MOUSE_NORMAL, true); } return(false); } @@ -1772,8 +1813,14 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) ** or release event, special mouse queuing storage variables are used. Other ** events must use the current mouse position globals. */ - x = Get_Mouse_X(); - y = Get_Mouse_Y(); + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } /* ** See if the mouse is over the radar general area, but not yet @@ -1781,7 +1828,7 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) ** mouse is overridden to be the normal cursor and no other ** action is performed. */ - if (x < Map.RadX+Map.RadOffX || x >= Map.RadX+Map.RadIWidth || y < Map.RadY+Map.RadOffY || y >= Map.RadY+Map.RadIHeight) { + if (x < Map.RadX + Map.RadOffX || x >= Map.RadX + Map.RadIWidth || y < Map.RadY + Map.RadOffY || y >= Map.RadY + Map.RadIHeight) { Map.Override_Mouse_Shape(MOUSE_NORMAL); return(false); } @@ -1791,10 +1838,10 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) if (result == 1) { cell = Map.RadarClass::Click_Cell_Calc(x, y); if (cell != -1 && Map.In_Radar(cell)) { - shadow = (!Map[cell].IsMapped && !Debug_Unshroud); -// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); - cellx = 12; - celly = 12; + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + // shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + cellx = 12; + celly = 12; /* ** Determine the object that the mouse is currently over. @@ -1810,7 +1857,8 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) if (CurrentObject.Count()) { if (object) { action = CurrentObject[0]->What_Action(object); - } else { + } + else { action = CurrentObject[0]->What_Action(cell); } @@ -1819,18 +1867,18 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) ** anything. */ switch (action) { - case ACTION_MOVE: - case ACTION_NOMOVE: - case ACTION_ATTACK: - case ACTION_ENTER: - case ACTION_CAPTURE: - case ACTION_SABOTAGE: - break; + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; - default: - action = ACTION_NONE; - object = NULL; - break; + default: + action = ACTION_NONE; + object = NULL; + break; } /* @@ -1841,7 +1889,8 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) if (action == ACTION_NONE) { if (object && object->Is_Selected_By_Player()) { object = NULL; - } else { + } + else { action = ACTION_NOMOVE; } } @@ -1870,7 +1919,8 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); } - } else { + } + else { Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); @@ -1886,7 +1936,7 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) celly = max(celly, Map.MapCellY); cell = XY_Cell(cellx, celly); shadow = (!Map[cell].IsMapped && !Debug_Unshroud); -// shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + // shadow = (!Map[cell].IsVisible && !Debug_Unshroud); Map.Set_Tactical_Position(Cell_Coord(cell)); cell = Coord_Cell(Map.DesiredTacticalCoord); Map.DisplayClass::IsToRedraw = true; @@ -1932,7 +1982,7 @@ int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType & key) * HISTORY: * * 01/01/1995 JLB : Created. * *=============================================================================================*/ -void RadarClass::Refresh_Cells(CELL cell, short const * list) +void RadarClass::Refresh_Cells(CELL cell, short const* list) { if (*list == REFRESH_SIDEBAR) { IsToRedraw = true; @@ -1966,32 +2016,33 @@ void RadarClass::Set_Radar_Position(CELL cell) if (ZoomFactor != 1) { oldx = (Cell_X(cell) - MapCellX); oldy = (Cell_Y(cell) - MapCellY); - } else { + } + else { oldx = 0; oldy = 0; } Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); - newx = oldx + MapCellX; - newy = oldy + MapCellY; - newcell = XY_Cell(newx, newy); + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); if (RadarCell != newcell) { - int forced = FALSE; + int forced = FALSE; int xmod = newx; int ymod = newy; int radx = (Cell_X(RadarCell)) - xmod; int rady = (Cell_Y(RadarCell)) - ymod; - RadarX = newx; - RadarY = newy; - RadarCell = newcell; + RadarX = newx; + RadarY = newy; + RadarCell = newcell; if (Map.IsSidebarActive && Map.IsRadarActive) { - int radw = RadarCellWidth-ABS(radx); // Replicable width. - int radh = RadarCellHeight-ABS(rady); // Replicable height. + int radw = RadarCellWidth - ABS(radx); // Replicable width. + int radh = RadarCellHeight - ABS(rady); // Replicable height. if (radw < 1) forced = true; if (radh < 1) forced = true; @@ -2011,73 +2062,76 @@ void RadarClass::Set_Radar_Position(CELL cell) ** always done in software by the library anyway. */ HidPage.Blit(HidPage, - (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, - (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, - (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, - (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, - radw * ZoomFactor, - radh * ZoomFactor); + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); - } else { + } + else { /* ** Create a temporary intermediate surface */ GraphicBufferClass temp_surface; temp_surface.Init((RadarWidth + 16) & 0xfffffff0, - (RadarHeight + 16) & 0xfffffff0, - NULL, 0, (GBC_Enum) GBC_VIDEOMEM); + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum)GBC_VIDEOMEM); /* ** Do the blit in 2 stages. */ HidPage.Blit(temp_surface, - (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, - (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, - 0, - 0, - RadarWidth, - RadarHeight); + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); temp_surface.Blit(HidPage, - 0, - 0, - (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX+ RadOffX + BaseX, - (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, - radw * ZoomFactor, - radh * ZoomFactor); + 0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); } /* ** Now we need to flag the section of the map that is going to redraw. */ - if ( radx != 0 ) { + if (radx != 0) { int min; int max; - if ( radx < 0 ) { // this mean regen the right edge + if (radx < 0) { // this mean regen the right edge min = radw; - max = radw+ABS(radx); - } else { // this mean regen the left edge + max = radw + ABS(radx); + } + else { // this mean regen the left edge min = 0; max = radx; } - for (int x = min; x < max; x++ ) { - for (int y = 0; y < (int)RadarCellHeight; y++ ) { + for (int x = min; x < max; x++) { + for (int y = 0; y < (int)RadarCellHeight; y++) { Radar_Pixel(XY_Cell(newx + x, newy + y)); } } } - if ( newy != 0 ) { + if (newy != 0) { int min; int max; - if ( rady < 0 ) { // this mean regen the bottom edge + if (rady < 0) { // this mean regen the bottom edge min = radh; - max = radh+ABS(rady); - } else { // this mean regen the top edge + max = radh + ABS(rady); + } + else { // this mean regen the top edge min = 0; max = rady; } - for (int y = min; y < max; y++ ) { - for ( int x = 0; x < (int)RadarCellWidth; x++ ) { + for (int y = min; y < max; y++) { + for (int x = 0; x < (int)RadarCellWidth; x++) { Radar_Pixel(XY_Cell(newx + x, newy + y)); } } @@ -2085,14 +2139,15 @@ void RadarClass::Set_Radar_Position(CELL cell) } } RadarCursorRedraw = IsRadarActive; - IsToRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; Flag_To_Redraw(false); if (ZoomFactor > 4) { FullRedraw = forced; } - } else { + } + else { RadarCursorRedraw = IsRadarActive; - IsToRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; Flag_To_Redraw(false); } #else @@ -2108,24 +2163,27 @@ void RadarClass::Set_Radar_Position(CELL cell) ** If the new radar position is not too close to the edge of the ** current radar display, then don't bother to change the radar position. */ - if ((unsigned)(x - (RadarX+10)) > RadarCellWidth-20) { - oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; - } else { - oldx = Cell_X(RadarCell)-MapCellX; + if ((unsigned)(x - (RadarX + 10)) > RadarCellWidth - 20) { + oldx = (Cell_X(cell) - MapCellX) - RadarCellWidth / 2; } - if ((unsigned)(y - (RadarY+10)) > RadarCellHeight-20) { - oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; - } else { - oldy = Cell_Y(RadarCell)-MapCellY; + else { + oldx = Cell_X(RadarCell) - MapCellX; + } + if ((unsigned)(y - (RadarY + 10)) > RadarCellHeight - 20) { + oldy = (Cell_Y(cell) - MapCellY) - RadarCellHeight / 2; + } + else { + oldy = Cell_Y(RadarCell) - MapCellY; } #ifdef NEVER - if ((unsigned)(x - (RadarX+10)) > RadarWidth-20 || (unsigned)(y - (RadarY+10)) > RadarHeight-20) { - oldx = (Cell_X(cell)-MapCellX)-RadarCellWidth/2; - oldy = (Cell_Y(cell)-MapCellY)-RadarCellHeight/2; - } else { - oldx = Cell_X(RadarCell)-MapCellX; - oldy = Cell_Y(RadarCell)-MapCellY; + if ((unsigned)(x - (RadarX + 10)) > RadarWidth - 20 || (unsigned)(y - (RadarY + 10)) > RadarHeight - 20) { + oldx = (Cell_X(cell) - MapCellX) - RadarCellWidth / 2; + oldy = (Cell_Y(cell) - MapCellY) - RadarCellHeight / 2; + } + else { + oldx = Cell_X(RadarCell) - MapCellX; + oldy = Cell_Y(RadarCell) - MapCellY; } #endif @@ -2258,8 +2316,9 @@ void RadarClass::Player_Names(bool on) IsToRedraw = true; if (on) { Flag_To_Redraw(true); -// Flag_To_Redraw(false); - } else { + // Flag_To_Redraw(false); + } + else { Flag_To_Redraw(true); // force drawing of the plate } } @@ -2282,7 +2341,7 @@ void RadarClass::Player_Names(bool on) bool RadarClass::Spy_Next_House(void) { bool tospy = false; - int spiedby = (1<<(PlayerPtr->Class->House)); + int spiedby = (1 << (PlayerPtr->Class->House)); IsPlayerNames = false; IsToRedraw = true; @@ -2294,21 +2353,23 @@ bool RadarClass::Spy_Next_House(void) if (Session.Type == GAME_NORMAL) { firsthouse = HOUSE_SPAIN; maxhouse = HOUSE_GOOD; - } else { + } + else { firsthouse = HOUSE_MULTI1; maxhouse = HOUSE_COUNT; } if (IsHouseSpy) { - house = (HousesType)(SpyingOn+1); - } else { + house = (HousesType)(SpyingOn + 1); + } + else { house = firsthouse; } if (house < firsthouse) house = firsthouse; while (house < maxhouse && !tospy) { - HouseClass * hptr = HouseClass::As_Pointer(house); + HouseClass* hptr = HouseClass::As_Pointer(house); if (hptr && hptr->IsActive && hptr != PlayerPtr) { if (hptr->RadarSpied & spiedby) { tospy = true; @@ -2351,22 +2412,22 @@ bool RadarClass::Draw_House_Info(void) if (!Map.IsSidebarActive) { return false; } - CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); - y = RadY + RadOffY + (2*RESFACTOR); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + y = RadY + RadOffY + (2 * RESFACTOR); Map.Repair.Draw_Me(true); Map.Upgrade.Draw_Me(true); Map.Zoom.Draw_Me(true); - Fancy_Text_Print (TXT_SPY_INFO, RadX + RadOffX + (6 * RESFACTOR), y, + Fancy_Text_Print(TXT_SPY_INFO, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); - y += 7*RESFACTOR; + y += 7 * RESFACTOR; - HouseClass * ptr = HouseClass::As_Pointer(SpyingOn); - if (ptr && (ptr->RadarSpied & (1<<(PlayerPtr->Class->House))) ) { + HouseClass* ptr = HouseClass::As_Pointer(SpyingOn); + if (ptr && (ptr->RadarSpied & (1 << (PlayerPtr->Class->House)))) { PlayerColorType c_idx; - RemapControlType * color; + RemapControlType* color; TextPrintType style; c_idx = ptr->RemapColor; @@ -2384,49 +2445,51 @@ bool RadarClass::Draw_House_Info(void) txt[9] = '.'; txt[10] = '\0'; } - Fancy_Text_Print (txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); - } else { - strcpy(txt,"________"); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + } + else { + strcpy(txt, "________"); } y += (6 * RESFACTOR) + 1; Fancy_Text_Print(TXT_BUILDNGS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); y += (6 * RESFACTOR) + 1; -// count & print buildings + // count & print buildings itoa(ptr->CurBuildings, txt, 10); Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); y += (6 * RESFACTOR) + 1; Fancy_Text_Print(TXT_UNITS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); y += (6 * RESFACTOR) + 1; -// count & print units + // count & print units itoa(ptr->CurUnits, txt, 10); Fancy_Text_Print(txt, RadX + RadOffX + 6 * RESFACTOR, y, color, BLACK, style); y += (6 * RESFACTOR) + 1; Fancy_Text_Print(TXT_INFANTRY, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); y += (6 * RESFACTOR) + 1; -// count & print infantry + // count & print infantry itoa(ptr->CurInfantry, txt, 10); Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); #if(0) y += (6 * RESFACTOR) + 1; Fancy_Text_Print(TXT_AIRCRAFT, RADAR_X + RADAR_OFF_X + 6, y, - &ColorRemaps[PCOLOR_GREY], TBLACK, - TPF_6PT_GRAD | TPF_NOSHADOW); + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); y += (6 * RESFACTOR) + 1; -// count & print aircraft - for (i = AIRCRAFT_NONE+1, count = 0; i < AIRCRAFT_COUNT; i++) { + // count & print aircraft + for (i = AIRCRAFT_NONE + 1, count = 0; i < AIRCRAFT_COUNT; i++) { count += ptr->AQuantity[i]; } itoa(count, txt, 10); Fancy_Text_Print(txt, RadX + RadOffX, y, - color, BLACK, style); + color, BLACK, style); #endif return(true); - } else { + } + else { return(false); } } @@ -2451,12 +2514,12 @@ void RadarClass::Draw_Names(void) { PlayerColorType c_idx; HousesType house; - HouseClass * ptr; + HouseClass* ptr; int y; char txt[40]; HousesType h; int kills; - RemapControlType * color; + RemapControlType* color; TextPrintType style; /* @@ -2466,18 +2529,18 @@ void RadarClass::Draw_Names(void) return; } -// CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RADAR_X, RADAR_Y+1, -// WINDOW_MAIN, SHAPE_NORMAL); - CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1*RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + // CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RADAR_X, RADAR_Y+1, + // WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); - y = RadY + RadOffY+(2*RESFACTOR); + y = RadY + RadOffY + (2 * RESFACTOR); - Fancy_Text_Print (TXT_NAME_COLON, RadX + RadOffX, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); - Fancy_Text_Print (TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW); - y += 6*RESFACTOR+1; + Fancy_Text_Print(TXT_NAME_COLON, RadX + RadOffX, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Fancy_Text_Print(TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW); + y += 6 * RESFACTOR + 1; LogicPage->Draw_Line(RadX + RadOffX, y, RadX + RadOffX + RadIWidth - 1, y, LTGREY); - y += 2*RESFACTOR; + y += 2 * RESFACTOR; for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { ptr = HouseClass::As_Pointer(house); @@ -2492,7 +2555,8 @@ void RadarClass::Draw_Names(void) if (ptr->IsDefeated) { color = &GreyScheme; style = TPF_6PT_GRAD | TPF_NOSHADOW; - } else { + } + else { color = &ColorRemaps[c_idx]; style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; } @@ -2501,11 +2565,11 @@ void RadarClass::Draw_Names(void) ** Initialize our message */ txt[0] = 0; -// sprintf(txt, "%s", ptr->Name()); + // sprintf(txt, "%s", ptr->Name()); sprintf(txt, "%s", ptr->IsHuman ? ptr->IniName : Text_String(TXT_COMPUTER)); if (strlen(txt) == 0) { - strcpy(txt,"________"); + strcpy(txt, "________"); } /* @@ -2522,7 +2586,7 @@ void RadarClass::Draw_Names(void) txt[9] = '\0'; } #endif - Fancy_Text_Print (txt, RadX + RadOffX, y, color, TBLACK, style); + Fancy_Text_Print(txt, RadX + RadOffX, y, color, TBLACK, style); kills = 0; for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { @@ -2530,9 +2594,9 @@ void RadarClass::Draw_Names(void) kills += ptr->BuildingsKilled[h]; } sprintf(txt, "%2d", kills); - Fancy_Text_Print (txt, RadX + RadOffX + RadIWidth - 2, y, color, TBLACK, style | TPF_RIGHT); + Fancy_Text_Print(txt, RadX + RadOffX + RadIWidth - 2, y, color, TBLACK, style | TPF_RIGHT); - y += 6*RESFACTOR+1; + y += 6 * RESFACTOR + 1; } @@ -2568,7 +2632,7 @@ void RadarClass::Activate_Pulse(void) bool RadarClass::Is_Radar_Active(void) { return(IsRadarActive || PlayerPtr->IsGPSActive); -// return IsRadarActive || PlayerPtr->IsGPSActive; + // return IsRadarActive || PlayerPtr->IsGPSActive; } @@ -2638,7 +2702,7 @@ bool RadarClass::Get_Jammed(HousesType house) const return false; } -bool RadarClass::Get_Jammed(HouseClass *player) const +bool RadarClass::Get_Jammed(HouseClass* player) const { assert(player); if (player->IsGPSActive) return(false); @@ -2657,7 +2721,7 @@ void RadarClass::Set_Jammed(HousesType house, bool jam) } } -void RadarClass::Set_Jammed(HouseClass *player, bool jam) +void RadarClass::Set_Jammed(HouseClass* player, bool jam) { if (player && player->Class) { Set_Jammed(player->Class->House, jam); @@ -2666,6 +2730,6 @@ void RadarClass::Set_Jammed(HouseClass *player, bool jam) void RadarClass::Flag_Cell(CELL cell) { -// Radar_Pixel(cell); + // Radar_Pixel(cell); DisplayClass::Flag_Cell(cell); } \ No newline at end of file diff --git a/code/REDALERT/SHAPEBTN.CPP b/code/REDALERT/SHAPEBTN.CPP index ea07f16..7d1dc59 100644 --- a/code/REDALERT/SHAPEBTN.CPP +++ b/code/REDALERT/SHAPEBTN.CPP @@ -164,6 +164,7 @@ int ShapeButtonClass::Draw_Me(int forced) } CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + /* ** Display the mouse. */ diff --git a/code/REDALERT/SIDEBAR.CPP b/code/REDALERT/SIDEBAR.CPP index f3db0df..2a9912e 100644 --- a/code/REDALERT/SIDEBAR.CPP +++ b/code/REDALERT/SIDEBAR.CPP @@ -759,6 +759,9 @@ bool SidebarClass::Scroll(bool up, int column) *=============================================================================================*/ void SidebarClass::Draw_It(bool complete) { + /* Draw black background */ + GL_FillRect(TBLACK, SIDE_X * RESFACTOR, 8 * RESFACTOR, SIDE_WIDTH * RESFACTOR, ScreenHeight - (8 * RESFACTOR)); + PowerClass::Draw_It(complete); BStart(BENCH_SIDEBAR); @@ -772,19 +775,16 @@ void SidebarClass::Draw_It(bool complete) */ int shape = complete ? 0 : 1; - /* - ** The sidebar shape is too big in 640x400 so it needs to be drawn in three chunks. - */ CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); - // @@ this one repeats CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); - - CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, ScreenHeight - ( 80*RESFACTOR ), WINDOW_MAIN, SHAPE_WIN_REL); + + CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, ScreenHeight - (80 * RESFACTOR) + 3, WINDOW_MAIN, SHAPE_WIN_REL); Repair.Draw_Me(true); Upgrade.Draw_Me(true); Zoom.Draw_Me(true); + LogicPage->Unlock(); } } @@ -802,7 +802,6 @@ void SidebarClass::Draw_It(bool complete) Zoom.Draw_Me(true); } } - //IsToRedraw = false; BEnd(BENCH_SIDEBAR); } @@ -1226,30 +1225,17 @@ void SidebarClass::StripClass::Init_IO(int id) UpButton[ID].ID = BUTTON_UP+id; UpButton[ID].X = X + (UP_X_OFFSET * RESFACTOR); UpButton[ID].Y = HEIGHT - ( ( OBJECT_HEIGHT + 7 ) * RESFACTOR ); - -#if (FRENCH) -#ifdef WIN32 UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); -#else - UpButton[ID].Set_Shape(MFCD::Retrieve("STUP_FIX.SHP")); -#endif -#else //FRENCH - UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); -#endif //FRENCH DownButton[ID].IsSticky = true; DownButton[ID].ID = BUTTON_DOWN+id; DownButton[ID].X = X + (DOWN_X_OFFSET * RESFACTOR); DownButton[ID].Y = HEIGHT - ( (OBJECT_HEIGHT + 7 ) * RESFACTOR ); - - /* - ** Buttons are in a slightly different position in the new sidebar - */ - UpButton[ID].Y--; - DownButton[ID].Y--; - DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); + UpButton[ID].Y += 3; + DownButton[ID].Y += 3; + for (int index = 0; index < MaxButtonsVisible; index++) { SelectClass & g = SelectButton[ID][index]; g.ID = BUTTON_SELECT; @@ -1828,6 +1814,7 @@ void SidebarClass::StripClass::Draw_It(bool complete) } remapper = 0; + /* ** Now that the shape of the object at the current working slot has been found, ** draw it and any graphic overlays as necessary. diff --git a/code/REDALERT/io/UserInput.cpp b/code/REDALERT/io/UserInput.cpp index cab49c4..39322e8 100644 --- a/code/REDALERT/io/UserInput.cpp +++ b/code/REDALERT/io/UserInput.cpp @@ -21,8 +21,9 @@ bool UserInputClass::KeyboardState::Key_Down(KeyNumType key) const auto sdl_Key = sdl_keyMapping_reverse.find( key ); if ( sdl_Key != sdl_keyMapping_reverse.end() ) { + const SDL_Scancode scanCode = SDL_GetScancodeFromKey(sdl_Key->second); const Uint8* keyState = SDL_GetKeyboardState(NULL); - return keyState[ sdl_Key->second ]; + return ( keyState[scanCode] ); } return false; } @@ -145,6 +146,13 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) } break; + // Keyboard key lifted up + case SDL_KEYUP: + flags |= GadgetClass::KEYBOARD; + + break; + + // Keyboard key pressed down case SDL_KEYDOWN: { if (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) { @@ -161,6 +169,8 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) break; } + flags |= GadgetClass::KEYBOARD; + int keyFlags = 0x00; KeyB.Shift = false; KeyB.Alt = false; diff --git a/code/REDALERT/io/UserInput.h b/code/REDALERT/io/UserInput.h index 6d9db2e..d6cc690 100644 --- a/code/REDALERT/io/UserInput.h +++ b/code/REDALERT/io/UserInput.h @@ -86,6 +86,13 @@ std::map const sdl_keyMapping = { { SDLK_DOWN, KN_DOWN }, { SDLK_LEFT, KN_LEFT }, { SDLK_RIGHT, KN_RIGHT }, + + { SDLK_LALT, KN_LALT }, + { SDLK_RALT, KN_RALT }, + { SDLK_LCTRL, KN_LCTRL }, + { SDLK_RCTRL, KN_RCTRL }, + { SDLK_LSHIFT, KN_LSHIFT }, + { SDLK_RSHIFT, KN_RSHIFT }, }; extern std::map sdl_keyMapping_reverse; diff --git a/code/redalert/2keyfram.cpp b/code/redalert/2keyfram.cpp new file mode 100644 index 0000000..18af0a5 --- /dev/null +++ b/code/redalert/2keyfram.cpp @@ -0,0 +1,588 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/2KEYFRAM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : KEYFRAME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/25/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#define SUBFRAMEOFFS 7 // 3 1/2 frame offsets loaded (2 offsets/frame) + + +#define Apply_Delta(buffer, delta) Apply_XOR_Delta((char*)(buffer), (char*)(delta)) + +typedef struct { + unsigned short frames; + unsigned short x; + unsigned short y; + unsigned short width; + unsigned short height; + unsigned short largest_frame_size; + short flags; +} KeyFrameHeaderType; + +#define INITIAL_BIG_SHAPE_BUFFER_SIZE (64 * 1024 * 1024) +#define THEATER_BIG_SHAPE_BUFFER_SIZE 4000000 +#define UNCOMPRESS_MAGIC_NUMBER 56789 + +unsigned BigShapeBufferLength = INITIAL_BIG_SHAPE_BUFFER_SIZE; +unsigned TheaterShapeBufferLength = THEATER_BIG_SHAPE_BUFFER_SIZE; +extern "C"{ + char *BigShapeBufferStart = NULL; + char *TheaterShapeBufferStart = NULL; + BOOL UseBigShapeBuffer = TRUE; + bool IsTheaterShape = false; +} +#ifdef FIXIT_SCORE_CRASH +/* +** Global required to fix the score screen crash bug by allowing disabling of uncompressed shapes. +*/ +bool OriginalUseBigShapeBuffer = false; +#endif //FIXIT +char *BigShapeBufferPtr = NULL; +int TotalBigShapes=0; +BOOL ReallocShapeBufferFlag = FALSE; + +char *TheaterShapeBufferPtr = NULL; +int TotalTheaterShapes = 0; + + +#define MAX_SLOTS 1500 +#define THEATER_SLOT_START 1000 + +char **KeyFrameSlots [MAX_SLOTS]; +int TotalSlotsUsed=0; +int TheaterSlotsUsed = THEATER_SLOT_START; + + + +typedef struct tShapeHeaderType{ + unsigned draw_flags; + char *shape_data; + int shape_buffer; //1 if shape is in theater buffer +} ShapeHeaderType; + +static int Length; + +//void *Get_Shape_Header_Data(void *ptr) +//{ +// if (UseBigShapeBuffer) { +// +// ShapeHeaderType *header = (ShapeHeaderType*) ptr; +// return ((void*) (header->shape_data + (long)(header->shape_buffer ? TheaterShapeBufferStart : BigShapeBufferStart) ) ); +// +// } else { +// return (ptr); +// } +//} +// +//int Get_Last_Frame_Length(void) +//{ +// return(Length); +//} + + +//void Reset_Theater_Shapes (void) +//{ +// /* +// ** Delete any previously allocated slots +// */ +// for (int i=THEATER_SLOT_START ; i 16*1024*1024) ? TRUE : FALSE; +#ifdef FIXIT_SCORE_CRASH + /* + ** Keep track of our original decision about whether to use cached shapes. + ** This is needed for the score screen crash fix. + */ + OriginalUseBigShapeBuffer = UseBigShapeBuffer; +#endif //FIXIT +#endif +} + + + + + +//unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr) +//{ +//#ifdef FIXIT_SCORE_CRASH +// char *ptr; +// unsigned long offcurr, offdiff; +//#else +// char *ptr, *lockptr;//, *uncomp_ptr; +// unsigned long offcurr, off16, offdiff; +//#endif +// unsigned long offset[SUBFRAMEOFFS]; +// KeyFrameHeaderType *keyfr; +// unsigned short buffsize, currframe, subframe; +// unsigned long length = 0; +// char frameflags; +// unsigned long return_value; +// char *temp_shape_ptr; +// +// // +// // valid pointer?? +// // +// Length = 0; +// if ( !dataptr || !buffptr ) { +// return(0); +// } +// +// // +// // look at header then check that frame to build is not greater +// // than total frames +// // +// keyfr = (KeyFrameHeaderType *) dataptr; +// +// if ( framenumber >= keyfr->frames ) { +// return(0); +// } +// +// +// if (UseBigShapeBuffer) { +// /* +// ** If we havnt yet allocated memory for uncompressed shapes then do so now. +// ** +// */ +// if (!BigShapeBufferStart) { +// BigShapeBufferStart = (char*)Alloc(BigShapeBufferLength, MEM_NORMAL); +// BigShapeBufferPtr = BigShapeBufferStart; +// +// /* +// ** Allocate memory for theater specific uncompressed shapes +// */ +// TheaterShapeBufferStart = (char*) Alloc (TheaterShapeBufferLength, MEM_NORMAL); +// TheaterShapeBufferPtr = TheaterShapeBufferStart; +// } +// +// /* +// ** If we are running out of memory (<10k left) for uncompressed shapes +// ** then allocate some more. +// */ +// if (( (unsigned)BigShapeBufferStart + BigShapeBufferLength) - (unsigned)BigShapeBufferPtr < 128000) { +// ReallocShapeBufferFlag = TRUE; +// } +// +// /* +// ** If this animation was not previously uncompressed then +// ** allocate memory to keep the pointers to the uncompressed data +// ** for these animation frames +// */ +// if (keyfr->x != UNCOMPRESS_MAGIC_NUMBER) { +// keyfr->x = UNCOMPRESS_MAGIC_NUMBER; +// if (IsTheaterShape) { +// keyfr->y = TheaterSlotsUsed; +// TheaterSlotsUsed++; +// } else { +// keyfr->y = TotalSlotsUsed; +// TotalSlotsUsed++; +// } +// /* +// ** Allocate and clear the memory for the shape info +// */ +// KeyFrameSlots[keyfr->y]= new char *[keyfr->frames]; +// memset (KeyFrameSlots[keyfr->y] , 0 , keyfr->frames*4); +// } +// +// /* +// ** If this frame was previously uncompressed then just return +// ** a pointer to the raw data +// */ +// if (*(KeyFrameSlots[keyfr->y]+framenumber)) { +// if (IsTheaterShape) { +// return ((unsigned long)TheaterShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); +// } else { +// return ((unsigned long)BigShapeBufferStart + (unsigned long)*(KeyFrameSlots[keyfr->y]+framenumber)); +// } +// } +// } +// +// // calc buff size +// buffsize = keyfr->width * keyfr->height; +// +// // get offset into data +// ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)framenumber << 3) + sizeof(KeyFrameHeaderType)) ); +// Mem_Copy( ptr, &offset[0], 12L ); +// frameflags = (char)(offset[0] >> 24); +// +// +// if ( (frameflags & KF_KEYFRAME) ) { +// +// ptr = (char *)Add_Long_To_Pointer( dataptr, (offset[0] & 0x00FFFFFFL) ); +// +// if (keyfr->flags & 1 ) { +// ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); +// } +// length = LCW_Uncompress( ptr, buffptr, buffsize ); +// } else { // key delta or delta +// +// if ( (frameflags & KF_DELTA) ) { +// currframe = (unsigned short)offset[1]; +// +// ptr = (char *)Add_Long_To_Pointer( dataptr, (((unsigned long)currframe << 3) + sizeof(KeyFrameHeaderType)) ); +// Mem_Copy( ptr, &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); +// } +// +// // key frame +// offcurr = offset[1] & 0x00FFFFFFL; +// +// // key delta +// offdiff = (offset[0] & 0x00FFFFFFL) - offcurr; +// +// ptr = (char *)Add_Long_To_Pointer( dataptr, offcurr ); +// +// if (keyfr->flags & 1 ) { +// ptr = (char *)Add_Long_To_Pointer( ptr, 768L ); +// } +// +//#ifndef FIXIT_SCORE_CRASH +// off16 = (unsigned long)lockptr & 0x00003FFFL; +//#endif +// +// length = LCW_Uncompress( ptr, buffptr, buffsize ); +// +// if (length > buffsize) { +// return(0); +// } +// +//#ifndef FIXIT_SCORE_CRASH +// if ( ((offset[2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { +// +// ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); +// off16 = (unsigned long)ptr & 0x00003FFFL; +// +// offcurr += offdiff; +// offdiff = 0; +// } +//#endif +// +// length = buffsize; +// Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); +// +// if ( (frameflags & KF_DELTA) ) { +// // adjust to delta after the keydelta +// +// currframe++; +// subframe = 2; +// +// while (currframe <= framenumber) { +// offdiff = (offset[subframe] & 0x00FFFFFFL) - offcurr; +// +//#ifndef FIXIT_SCORE_CRASH +// if ( ((offset[subframe+2] & 0x00FFFFFFL) - offcurr) >= (0x00010000L - off16) ) { +// +// ptr = (char *)Add_Long_To_Pointer( ptr, offdiff ); +// off16 = (unsigned long)lockptr & 0x00003FFFL; +// +// offcurr += offdiff; +// offdiff = 0; +// } +//#endif +// +// length = buffsize; +// Apply_Delta(buffptr, Add_Long_To_Pointer(ptr, offdiff)); +// +// currframe++; +// subframe += 2; +// +// if ( subframe >= (SUBFRAMEOFFS - 1) && +// currframe <= framenumber ) { +// Mem_Copy( Add_Long_To_Pointer( dataptr, +// (((unsigned long)currframe << 3) + +// sizeof(KeyFrameHeaderType)) ), +// &offset[0], (long)(SUBFRAMEOFFS * sizeof(unsigned long)) ); +// subframe = 0; +// } +// } +// } +// } +// +// +// if (UseBigShapeBuffer) { +// /* +// ** Save the uncompressed shape data so we dont have to uncompress it +// ** again next time its drawn. +// ** We keep a space free before the raw shape data so we can add line +// ** header info before the shape is drawn for the first time +// */ +// +// if (IsTheaterShape) { +// /* +// ** Shape is a theater specific shape +// */ +// return_value = (unsigned long) TheaterShapeBufferPtr; +// temp_shape_ptr = TheaterShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); +// /* +// ** align the actual shape data +// */ +// if (3 & (unsigned)temp_shape_ptr) { +// temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); +// } +// +// memcpy (temp_shape_ptr , buffptr , length); +// ((ShapeHeaderType *)TheaterShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated +// ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)TheaterShapeBufferStart; //pointer to old raw shape data +// ((ShapeHeaderType *)TheaterShapeBufferPtr)->shape_buffer = 1; //Theater buffer +// *(KeyFrameSlots[keyfr->y]+framenumber) = TheaterShapeBufferPtr - (unsigned)TheaterShapeBufferStart; +// TheaterShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); +// /* +// ** Align the next shape +// */ +// if (3 & (unsigned)TheaterShapeBufferPtr) { +// TheaterShapeBufferPtr = (char *)((unsigned)(TheaterShapeBufferPtr + 3) & 0xfffffffc); +// } +// Length = length; +// return (return_value); +// +// } else { +// +// +// return_value=(unsigned long)BigShapeBufferPtr; +// temp_shape_ptr = BigShapeBufferPtr + keyfr->height+sizeof(ShapeHeaderType); +// /* +// ** align the actual shape data +// */ +// if (3 & (unsigned)temp_shape_ptr) { +// temp_shape_ptr = (char *) ((unsigned)(temp_shape_ptr + 3) & 0xfffffffc); +// } +// memcpy (temp_shape_ptr , buffptr , length); +// ((ShapeHeaderType *)BigShapeBufferPtr)->draw_flags = -1; //Flag that headers need to be generated +// ((ShapeHeaderType *)BigShapeBufferPtr)->shape_data = temp_shape_ptr - (unsigned)BigShapeBufferStart; //pointer to old raw shape data +// ((ShapeHeaderType *)BigShapeBufferPtr)->shape_buffer = 0; //Normal Big Shape Buffer +// *(KeyFrameSlots[keyfr->y]+framenumber) = BigShapeBufferPtr - (unsigned)BigShapeBufferStart; +// BigShapeBufferPtr = (char*)(length + (unsigned)temp_shape_ptr); +// // Align the next shape +// if (3 & (unsigned)BigShapeBufferPtr) { +// BigShapeBufferPtr = (char *)((unsigned)(BigShapeBufferPtr + 3) & 0xfffffffc); +// } +// Length = length; +// return (return_value); +// } +// +// } else { +// return ((unsigned long)buffptr); +// } +//} + + +/*********************************************************************************************** + * Get_Build_Frame_Count -- Fetches the number of frames in data block. * + * * + * Use this routine to determine the number of shapes within the data block. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the number of shapes in the data block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Count(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->frames); + } + return(0); +} + + +unsigned short Get_Build_Frame_X(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->x); + } + return(0); +} + + +unsigned short Get_Build_Frame_Y(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->y); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Width -- Fetches the width of the shape image. * + * * + * Use this routine to fetch the width of the shapes within the keyframe shape data block. * + * All shapes within the block have the same width. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the width of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Width(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->width); + } + return(0); +} + + +/*********************************************************************************************** + * Get_Build_Frame_Height -- Fetches the height of the shape image. * + * * + * Use this routine to fetch the height of the shapes within the keyframe shape data block. * + * All shapes within the block have the same height. * + * * + * INPUT: dataptr -- Pointer to the keyframe shape data block. * + * * + * OUTPUT: Returns with the height of the shapes in the block -- expressed in pixels. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented * + *=============================================================================================*/ +unsigned short Get_Build_Frame_Height(void const *dataptr) +{ + if (dataptr) { + return(((KeyFrameHeaderType const *)dataptr)->height); + } + return(0); +} + + +bool Get_Build_Frame_Palette(void const * dataptr, void * palette) +{ + if (dataptr && (((KeyFrameHeaderType const *)dataptr)->flags & 1)) { + char const * ptr = (char const *)Add_Long_To_Pointer( dataptr, + ( (( (long)sizeof(unsigned long) << 1) * + ((KeyFrameHeaderType *) dataptr)->frames ) + + 16 + sizeof(KeyFrameHeaderType) ) ); + + memcpy(palette, ptr, 768L); + return(true); + } + return(false); +} \ No newline at end of file diff --git a/code/redalert/_wsproto.cpp b/code/redalert/_wsproto.cpp new file mode 100644 index 0000000..a31e465 --- /dev/null +++ b/code/redalert/_wsproto.cpp @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/_WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:47p $* + * * + * $Revision:: 2 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "_WSProto.h" + + +WinsockInterfaceClass *PacketTransport = 0; //The object for interfacing with Winsock diff --git a/code/redalert/_wsproto.h b/code/redalert/_wsproto.h new file mode 100644 index 0000000..ee240f0 --- /dev/null +++ b/code/redalert/_wsproto.h @@ -0,0 +1,42 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/_WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/06/97 12:39p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef _WSPROTO_H +#define _WSPROTO_H + + +class WinsockInterfaceClass; +extern WinsockInterfaceClass *PacketTransport; //The object for interfacing with Winsock + +#endif //_WSPROTO_H diff --git a/code/redalert/aadata.cpp b/code/redalert/aadata.cpp new file mode 100644 index 0000000..473826d --- /dev/null +++ b/code/redalert/aadata.cpp @@ -0,0 +1,655 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AADATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game syste* + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class* + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft obj* + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * AircraftTypeClass::LRotorData = NULL; +void const * AircraftTypeClass::RRotorData = NULL; + +// Badger bomber +static AircraftTypeClass const BadgerPlane( + AIRCRAFT_BADGER, // What kind of aircraft is this. + TXT_BADGER, // Translated text number for aircraft. + "BADR", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Photo recon plane. +static AircraftTypeClass const U2Plane( + AIRCRAFT_U2, // What kind of aircraft is this. + TXT_U2, // Translated text number for aircraft. + "U2", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + false, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Mig attack aircraft. +static AircraftTypeClass const MigPlane( + AIRCRAFT_MIG, // What kind of aircraft is this. + TXT_MIG, // Translated text number for aircraft. + "MIG", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xC0, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Yak attack aircraft. +static AircraftTypeClass const YakPlane( + AIRCRAFT_YAK, // What kind of aircraft is this. + TXT_YAK, // Translated text number for aircraft. + "YAK", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0020, // Primary weapon offset along turret centerline. + 0x0020, // Primary weapon lateral offset along turret centerline. + true, // Fixed wing aircraft? + false, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_AIRSTRIP, // Preferred landing building. + 0xFF, // Landing speed + 16, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Transport helicopter. +static AircraftTypeClass const TransportHeli( + AIRCRAFT_TRANSPORT, // What kind of aircraft is this. + TXT_TRANS, // Translated text number for aircraft. + "TRAN", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + true, // Custom rotor sets for each facing? + true, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_NONE, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + +// Longbow attack helicopter +static AircraftTypeClass const AttackHeli( + AIRCRAFT_LONGBOW, // What kind of aircraft is this. + TXT_HELI, // Translated text number for aircraft. + "HELI", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +// Hind +static AircraftTypeClass const OrcaHeli( + AIRCRAFT_HIND, // What kind of aircraft is this. + TXT_ORCA, // Translated text number for aircraft. + "HIND", // INI name of aircraft. + 0x0000, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Fixed wing aircraft? + true, // Equipped with a rotor? + false, // Custom rotor sets for each facing? + false, // Can this aircraft land on clear terrain? + true, // Is it invisible on radar? + true, // Can the player select it so as to give it orders? + true, // Can it be assigned as a target for attack. + false, // Is it insignificant (won't be announced)? + false, // Is it immune to normal combat damage? + STRUCT_HELIPAD, // Preferred landing building. + 0xFF, // Landing speed + 32, // Number of rotation stages. + MISSION_HUNT // Default mission for aircraft. +); + + +/*********************************************************************************************** + * AircraftTypeClass::AircraftTypeClass -- Constructor for aircraft objects. * + * * + * This is the constructor for the aircraft object. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass::AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder + ) : + TechnoTypeClass(RTTI_AIRCRAFTTYPE, + int(airtype), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + false, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + false, + false, + true, + true, + rotation, + SPEED_WINGED), + IsFixedWing(is_fixedwing), + IsLandable(is_landable), + IsRotorEquipped(is_rotorequipped), + IsRotorCustom(is_rotorcustom), + Type(airtype), + Mission(deforder), + Building(building), + LandingSpeed(landingspeed) +{ + /* + ** Forced aircraft overrides from the default. + */ + Speed = SPEED_WINGED; +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator new -- Allocates an aircraft type object from special pool. * + * * + * This will allocate an aircraft type class object from the memory pool of that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated aircraft type class object. If there * + * was insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AircraftTypeClass::operator new(size_t) +{ + return(AircraftTypes.Alloc()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::operator delete -- Returns aircraft type to special memory pool. * + * * + * This will return the aircraft type class object back to the special memory pool that * + * it was allocated from. * + * * + * INPUT: pointer -- Pointer to the aircraft type class object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::operator delete(void * pointer) +{ + AircraftTypes.Free((AircraftTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Init_Heap -- Initialize the aircraft type class heap. * + * * + * This will initialize the aircraft type class heap by pre-allocating all known aircraft * + * types. It should be called once and before the rules.ini file is processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Init_Heap(void) +{ + /* + ** These aircraft type class objects must be allocated in the exact order that they + ** are specified in the AircraftSmen enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AircraftTypeClass(TransportHeli); + new AircraftTypeClass(BadgerPlane); + new AircraftTypeClass(U2Plane); + new AircraftTypeClass(MigPlane); + new AircraftTypeClass(YakPlane); + new AircraftTypeClass(AttackHeli); + new AircraftTypeClass(OrcaHeli); +} + + +/*********************************************************************************************** + * AircraftTypeClass::From_Name -- Converts an ASCII name into an aircraft type number. * + * * + * This routine is used to convert an ASCII representation of an aircraft into the * + * matching aircraft type number. This is used by the scenario INI reader code. * + * * + * INPUT: name -- Pointer to ASCII name to translate. * + * * + * OUTPUT: Returns the aircraft type number that matches the ASCII name provided. If no * + * match could be found, then AIRCRAFT_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftType AircraftTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int classid = AIRCRAFT_FIRST; classid < AIRCRAFT_COUNT; classid++) { + if (stricmp(As_Reference((AircraftType)classid).IniName, name) == 0) { + return(AircraftType)classid; + } + } + } + return(AIRCRAFT_NONE); +} + + +/*********************************************************************************************** + * AircraftTypeClass::One_Time -- Performs one time initialization of the aircraft type class. * + * * + * This routine is used to perform the onetime initialization of the aircraft type. This * + * includes primarily the shape and other graphic data loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This goes to disk and also must only be called ONCE. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::One_Time(void) +{ + for (int index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + AircraftTypeClass const & uclass = As_Reference((AircraftType)index); + + /* + ** Fetch the supporting data files for the unit. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + } + + LRotorData = MFCD::Retrieve("LROTOR.SHP"); + RRotorData = MFCD::Retrieve("RROTOR.SHP"); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_One_Of -- Creates an aircraft object of the appropriate type. * + * * + * This routine is used to create an aircraft object that matches the aircraft type. It * + * serves as a shortcut to creating an object using the "new" operator and "if" checks. * + * * + * INPUT: house -- The house owner of the aircraft that is to be created. * + * * + * OUTPUT: Returns with a pointer to the aircraft created. If the aircraft could not be * + * created, then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * AircraftTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new AircraftClass(Type, house->Class->House)); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * AircraftTypeClass::Prep_For_Add -- Prepares the scenario editor for adding an aircraft objec* + * * + * This routine is used by the scenario editor to prepare for the adding operation. It * + * builds a list of pointers to object types that can be added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Prep_For_Add(void) +{ + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::Display -- Displays a generic version of the aircraft type. * + * * + * This routine is used by the scenario editor to display a generic version of the object * + * type. This is displayed in the object selection dialog box. * + * * + * INPUT: x,y -- The coordinates to draw the aircraft at (centered). * + * * + * window -- The window to base the coordinates upon. * + * * + * house -- The owner of this generic aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 5; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} +#endif + + +/*********************************************************************************************** + * AircraftTypeClass::Occupy_List -- Returns with occupation list for landed aircraft. * + * * + * This determines the occupation list for the aircraft (if it was landed). * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset occupation list for the aircraft. * + * * + * WARNINGS: This occupation list is only valid if the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Overlap_List -- Determines the overlap list for a landed aircraft. * + * * + * This routine figures out the overlap list for the aircraft as if it were landed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cell offset overlap list for the aircraft. * + * * + * WARNINGS: This overlap list is only valid when the aircraft is landed. * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftTypeClass::Overlap_List(void) const +{ + static short const _list[] = {-(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -1, 1, (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Max_Pips -- Fetches the maximum number of pips allowed. * + * * + * Use this routine to retrieve the maximum pip count allowed for this aircraft. This is * + * the maximum number of passengers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips for this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftTypeClass::Max_Pips(void) const +{ + if (PrimaryWeapon != NULL) { + // Camera weapon (ex. on the Spy plane) doesn't display any pips + if (!PrimaryWeapon->IsCamera) { + return(5); + } + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Create_And_Place -- Creates and places aircraft using normal game system * + * * + * This routine is used to create and place an aircraft through the normal game system. * + * Since creation of aircraft in this fashion is prohibited, this routine does nothing. * + * * + * INPUT: na * + * * + * OUTPUT: Always returns a failure code (false). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftTypeClass::Create_And_Place(CELL, HousesType) const +{ + return(false); +} + + +/*********************************************************************************************** + * AircraftTypeClass::Dimensions -- Fetches the graphic dimensions of the aircraft type. * + * * + * This routine will fetch the pixel dimensions of this aircraft type. These dimensions * + * are used to control map refresh and select box rendering. * + * * + * INPUT: width -- Reference to variable that will be filled in with aircraft width. * + * * + * height -- Reference to variable that will be filled in with aircraft height. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftTypeClass::Dimensions(int &width, int &height) const +{ + if (Type == AIRCRAFT_BADGER) { + width = 56; + height = 56; + } else { + width = 21; + height = 20; + } +} + + +/*********************************************************************************************** + * AircraftTypeClass::As_Reference -- Given an aircraft type, find the matching type object. * + * * + * This routine is used to fetch a reference to the aircraft type class object that matches * + * the aircraft type specified. * + * * + * INPUT: aircraft -- The aircraft type to fetch a reference to the type class object of. * + * * + * OUTPUT: Returns with a reference to the type class object of this aircraft type. * + * * + * WARNINGS: Be sure that the aircraft type specified is legal. Illegal values will result * + * in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +AircraftTypeClass & AircraftTypeClass::As_Reference(AircraftType aircraft) +{ + return(*AircraftTypes.Ptr(aircraft)); +} diff --git a/code/redalert/abstract.cpp b/code/redalert/abstract.cpp new file mode 100644 index 0000000..772ee48 --- /dev/null +++ b/code/redalert/abstract.cpp @@ -0,0 +1,213 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ABSTRACT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * AbstractClass::Distance -- Determines distance to target. * + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * AbstractClass::Debug_Dump -- Display debug information to mono screen. * + * * + * This debug only routine will display various information about this abstract class * + * object to the monochrome screen specified. * + * * + * INPUT: mono -- Pointer to the monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef CHEAT_KEYS +void AbstractClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(11, 5);mono->Printf("%08X", As_Target()); + mono->Set_Cursor(20, 1);mono->Printf("%08X", Coord); + mono->Set_Cursor(29, 1);mono->Printf("%3d", Height); + if (Owner() != HOUSE_NONE) { + mono->Set_Cursor(1, 3); + mono->Printf("%-18s", Text_String(HouseTypeClass::As_Reference(Owner()).FullName)); + } +} +#endif + + +/*********************************************************************************************** + * AbstractClass::Distance -- Determines distance to target. * + * * + * This will determine the distance (direct line) to the target. The distance is in * + * 'leptons'. This routine is typically used for weapon range checks. * + * * + * INPUT: target -- The target to determine range to. * + * * + * OUTPUT: Returns with the range to the specified target (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1994 JLB : Created. * + *=============================================================================================*/ +int AbstractClass::Distance(TARGET target) const +{ + /* + ** Should subtract a fudge-factor distance for building targets. + */ + BuildingClass * obj = As_Building(target); + int dist = Distance(As_Coord(target)); + + /* + ** If the object is a building the adjust it by the average radius + ** of the object. + */ + if (obj && obj->IsActive) { + dist -= ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + if (dist < 0) dist = 0; + } + + /* + ** Return the distance to the target + */ + return(dist); +} + + +/*********************************************************************************************** + * AbstractTypeClass::AbstractTypeClass -- Constructor for abstract type objects. * + * * + * This is the constructor for AbstractTypeClass objects. It initializes the INI name and * + * the text name for this object type. * + * * + * INPUT: name -- Text number for the full name of the object. * + * * + * ini -- The ini name for this object type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +AbstractTypeClass::AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini) : + RTTI(rtti), + ID(id), + FullName(name) +{ + strncpy((char *)IniName, ini, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; +} + + +/*********************************************************************************************** + * AbstractTypeClass::Coord_Fixup -- Performs custom adjustments to location coordinate. * + * * + * This routine is called when the placement coordinate should be fixed up according * + * to any special rules specific to this object type. At this level, no transformation * + * occurs. Derived classes will transform the coordinate as necessary. * + * * + * INPUT: coord -- The proposed coordinate that this object type will be placed down at. * + * * + * OUTPUT: Returns with the adjusted coordinate that the object should be placed down at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE AbstractTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return(coord); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Full_Name -- Returns the full name (number) of this object type. * + * * + * This routine is used to fetch the full name of this object type. The name value * + * returned is actually the index number into the text array. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name index number for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Full_Name(void) const +{ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == ((RTTI+1) * 100) + ID) { + return(-(index+1)); + } + } +#endif + return(FullName); +} + + +/*********************************************************************************************** + * AbstractTypeClass::Get_Ownable -- Fetch the ownable bits for this object. * + * * + * This returns a bit flag that indicates which houses are allowed to own this object * + * type. At this level, all houses have ownership rights. This routine will be overridden * + * by object types that restrict ownership. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a bit flag indicating which houses have ownership rights. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int AbstractTypeClass::Get_Ownable(void) const +{ + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + + diff --git a/code/redalert/abstract.h b/code/redalert/abstract.h new file mode 100644 index 0000000..b85162f --- /dev/null +++ b/code/redalert/abstract.h @@ -0,0 +1,144 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ABSTRACT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ABSTRACT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/26/95 * + * * + * Last Update : January 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ABSTRACT_H +#define ABSTRACT_H + +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +COORDINATE As_Coord(TARGET target); + +class AbstractTypeClass; + +/* +** This class is the base class for all game objects that have an existence on the +** battlefield. +*/ +class AbstractClass +{ + public: + + /* + ** This specifies the type of object and the unique ID number + ** associated with it. The ID number happens to match the index into + ** the object heap appropriate for this object type. + */ + RTTIType RTTI; + int ID; + + /* + ** The coordinate location of the unit. For vehicles, this is the center + ** point. For buildings, it is the upper left corner. + */ + COORDINATE Coord; + + /* + ** This is the height of the object above ground (expressed in leptons). + */ + int Height; + + /* + ** The actual object ram-space is located in arrays in the data segment. This flag + ** is used to indicate which objects are free to be reused and which are currently + ** in use by the game. + */ + unsigned IsActive:1; + + /* + ** A flag to indicate that this object was recently created. Since an object's allocation is just a matter of whether + ** the IsActive flag is set, during a logic frame an object with a given ID could be 'deleted' then reallocated + ** as a different type of object in a different location. This flag lets us know that this happened. ST - 8/19/2019 5:33PM + */ + unsigned IsRecentlyCreated:1; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + AbstractClass(RTTIType rtti, int id) : RTTI(rtti), ID(id), Coord(0xFFFFFFFFL), Height(0) {}; + AbstractClass(NoInitClass const & x) {x();}; + virtual ~AbstractClass(void) {}; + + /* + ** Query functions. + */ + virtual char const * Name(void) const {return("");} + virtual HousesType Owner(void) const {return HOUSE_NONE;}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + RTTIType What_Am_I(void) const {return(RTTI);}; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass * mono) const; + #endif + + /* + ** Coordinate query support functions. + */ + virtual COORDINATE Center_Coord(void) const {return Coord;}; + virtual COORDINATE Target_Coord(void) const {return Coord;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + DirType Direction(AbstractClass const * object) const {return ::Direction(Center_Coord(), object->Target_Coord());}; + DirType Direction(COORDINATE coord) const {return ::Direction(Center_Coord(), coord);}; + DirType Direction(TARGET target) const {return ::Direction(Center_Coord(), As_Coord(target));}; + DirType Direction(CELL cell) const {return ::Direction(Coord_Cell(Center_Coord()), cell);}; + int Distance(TARGET target) const; + int Distance(COORDINATE coord) const {return ::Distance(Center_Coord(), coord);}; + int Distance(AbstractClass const * object) const {return ::Distance(Center_Coord(), object->Target_Coord());}; + + /* + ** Object entry and exit from the game system. + */ + virtual MoveType Can_Enter_Cell(CELL , FacingType = FACING_NONE) const {return MOVE_OK;}; + + /* + ** AI. + */ + virtual void AI(void) {}; + + /* + ** Set the new recently created flag every time the active flag is set. ST - 8/19/2019 5:41PM + */ + void Set_Active(void) {IsActive = true; IsRecentlyCreated = true;} + +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/adata.cpp b/code/redalert/adata.cpp new file mode 100644 index 0000000..0d7f38d --- /dev/null +++ b/code/redalert/adata.cpp @@ -0,0 +1,2553 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ADATA.CPP 3 3/07/97 4:27p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ADATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static AnimTypeClass const AtomBomb( + ANIM_ATOM_BLAST, // Animation number. + "ATOMSFX", // Data name of animation. + 72, // Maximum dimension of animation. + 19, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 75, // Virtual stages + 0x300 // Virtual scale +); + +static AnimTypeClass const SputDoor( + ANIM_SPUTDOOR, // Animation number. + "SPUTDOOR", // Data name of animation. + 42, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +// Electrocution death anim from Tesla coil +static AnimTypeClass const ElectricDie( + ANIM_ELECT_DIE, // Animation number. + "ELECTRO", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +// Electrocution death anim from Tesla coil for dog +static AnimTypeClass const DogElectricDie( + ANIM_DOG_ELECT_DIE, // Animation number. + "ELECTDOG", // Data name of animation. + 17, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 3, // Ending frame of loop back. + -1, // Number of animation stages. + 5, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_FIRE_MED +); + +static AnimTypeClass const SAMN( + ANIM_SAM_N, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNW( + ANIM_SAM_NW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 22, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*1, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMW( + ANIM_SAM_W, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 40, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*2, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSW( + ANIM_SAM_SW, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*3, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMS( + ANIM_SAM_S, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 76, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*4, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMSE( + ANIM_SAM_SE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 94, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*5, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAME( + ANIM_SAM_E, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 112, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*6, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const SAMNE( + ANIM_SAM_NE, // Animation number. + "SAMFIRE", // Data name of animation. + 55, // Maximum dimension of animation. + 130, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18*7, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + 18, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const LZSmoke( + ANIM_LZ_SMOKE, // Animation number. + "SMOKLAND", // Data name of animation. + 32, // Maximum dimension of animation. + 72, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 72, // Loop start frame number. + 91, // Ending frame of loop back. + -1, // Number of animation stages. + 255, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations. Primarily used on trees and buildings. +*/ +static AnimTypeClass const BurnSmall( + ANIM_BURN_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnMed( + ANIM_BURN_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const BurnBig( + ANIM_BURN_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +/* +** Flammable object burning animations that trail into smoke. Used for +** buildings and the gunboat. +*/ +static AnimTypeClass const OnFireSmall( + ANIM_ON_FIRE_SMALL, // Animation number. + "BURN-S", // Data name of animation. + 11, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_SMOKE_M +); +static AnimTypeClass const OnFireMed( + ANIM_ON_FIRE_MED, // Animation number. + "BURN-M", // Data name of animation. + 14, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_SMALL +); +static AnimTypeClass const OnFireBig( + ANIM_ON_FIRE_BIG, // Animation number. + "BURN-L", // Data name of animation. + 23, // Maximum dimension of animation. + 13, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 10), // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 30, // Loop start frame number. + 62, // Ending frame of loop back. + -1, // Number of animation stages. + 4, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_ON_FIRE_MED +); +static AnimTypeClass const Parachute( + ANIM_PARACHUTE, // Animation number. + "PARACH", // Data name of animation. + 32, // Maximum dimension of animation. + 15, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const ParaBomb( + ANIM_PARA_BOMB, // Animation number. + "PARABOMB", // Data name of animation. + 32, // Maximum dimension of animation. + 8, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 7, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 15, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FBall1( + ANIM_FBALL1, // Animation number. + "FBALL1", // Data name of animation. + 67, // Maximum dimension of animation. + 6, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Frag1( + ANIM_FRAG1, // Animation number. + "FRAG1", // Data name of animation. + 45, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM30, // Sound effect to play. + ANIM_NONE, + 29 // Virtual stages +); + +static AnimTypeClass const VehHit1( + ANIM_VEH_HIT1, // Animation number. + "VEH-HIT1", // Data name of animation. + 30, // Maximum dimension of animation. + 4, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM25, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit2( + ANIM_VEH_HIT2, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const VehHit3( + ANIM_VEH_HIT3, // Animation number. + "VEH-HIT3", // Data name of animation. + 19, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + true, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM12, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const ArtExp1( + ANIM_ART_EXP1, // Animation number. + "ART-EXP1", // Data name of animation. + 41, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_KABOOM22, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm1( + ANIM_NAPALM1, // Animation number. + "NAPALM1", // Data name of animation. + 21, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm2( + ANIM_NAPALM2, // Animation number. + "NAPALM2", // Data name of animation. + 41, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_EXPLODE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Napalm3( + ANIM_NAPALM3, // Animation number. + "NAPALM3", // Data name of animation. + 78, // Maximum dimension of animation. + 5, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_FIRE_LAUNCH, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const SmokePuff( + ANIM_SMOKE_PUFF, // Animation number. + "SMOKEY", // Data name of animation. + 24, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const FireBallFade( + ANIM_FBALL_FADE, // Animation number. + "FB2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Piff( + ANIM_PIFF, // Animation number. + "PIFF", // Data name of animation. + 13, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const PiffPiff( + ANIM_PIFFPIFF, // Animation number. + "PIFFPIFF", // Data name of animation. + 20, // Maximum dimension of animation. + 2, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire3( + ANIM_FIRE_SMALL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_SMALL_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire3Virtual( + ANIM_FIRE_SMALL_VIRTUAL, // Animation number. + "FIRE3", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 2, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire1( + ANIM_FIRE_MED2, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_MED2_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire1Virtual( + ANIM_FIRE_MED2_VIRTUAL, // Animation number. + "FIRE1", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire4( + ANIM_FIRE_TINY, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 32), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_TINY_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire4Virtual( + ANIM_FIRE_TINY_VIRTUAL, // Animation number. + "FIRE4", // Data name of animation. + 7, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Fire2( + ANIM_FIRE_MED, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + fixed(1, 16), // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + -1, // Virtual stages + 0x100, // Virtual scale + NULL, // Virtual name + ANIM_FIRE_MED_VIRTUAL // Virtual anim +); + +static AnimTypeClass const Fire2Virtual( + ANIM_FIRE_MED_VIRTUAL, // Animation number. + "FIRE2", // Data name of animation. + 23, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + true, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 10, // Loop start frame number. + 21, // Ending frame of loop back. + 29, // Number of animation stages. + 3, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const OilFieldBurn( + ANIM_OILFIELD_BURN, // Animation number. + "FLMSPT", // Data name of animation. + 42, // Maximum dimension of animation. + 58, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 33, // Loop start frame number. + 99, // Ending frame of loop back. + 66, // Number of animation stages. + 65535, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Gunfire( + ANIM_MUZZLE_FLASH, // Animation number. + "GUNFIRE", // Data name of animation. + 16, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 1, // Number of animation stages. + 1, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 10 // Virtual stages +); + +static AnimTypeClass const SmokeM( + ANIM_SMOKE_M, // Animation number. + "SMOKE_M", // Data name of animation. + 28, // Maximum dimension of animation. + 30, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 67, // Loop start frame number. + -1, // Loopback frame number. + -1, // Number of animation stages. + 6, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 105 // Virtual stages +); + +/* +** Mini-gun fire effect -- used by guard towers. +*/ +static AnimTypeClass const GUNN( + ANIM_GUN_N, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNW( + ANIM_GUN_NW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 6, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNW( + ANIM_GUN_W, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 12, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSW( + ANIM_GUN_SW, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 18, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNS( + ANIM_GUN_S, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 24, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNSE( + ANIM_GUN_SE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 30, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNE( + ANIM_GUN_E, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 36, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const GUNNE( + ANIM_GUN_NE, // Animation number. + "MINIGUN", // Data name of animation. + 18, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + false, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 42, // Starting frame number. + 0, // Loop start frame number. + 0, // Number of times the animation loops. + 6, // Number of animation stages. + 0, // Ending frame of loop back. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const CDeviator( + ANIM_CRATE_DEVIATOR, // Animation number. + "DEVIATOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateArmor( + ANIM_CRATE_ARMOR, // Animation number. + "ARMOR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateSpeed( + ANIM_CRATE_SPEED, // Animation number. + "SPEED", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CrateFPower( + ANIM_CRATE_FPOWER, // Animation number. + "FPOWER", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CrateTQuake( + ANIM_CRATE_TQUAKE, // Animation number. + "TQUAKE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CDollar( + ANIM_CRATE_DOLLAR, // Animation number. + "DOLLAR", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEarth( + ANIM_CRATE_EARTH, // Animation number. + "EARTH", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CEmpulse( + ANIM_CRATE_EMPULSE, // Animation number. + "EMPULSE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CInvun( + ANIM_CRATE_INVUN, // Animation number. + "INVUN", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CMine( + ANIM_CRATE_MINE, // Animation number. + "MINE", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CRapid( + ANIM_CRATE_RAPID, // Animation number. + "RAPID", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const CStealth( + ANIM_CRATE_STEALTH, // Animation number. + "STEALTH2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ChronoBox( + ANIM_CHRONO_BOX, // Animation number. + "CHRONBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const GPSBox( + ANIM_GPS_BOX, // Animation number. + "GPSBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const InvulBox( + ANIM_INVUL_BOX, // Animation number. + "INVULBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const ParaBox( + ANIM_PARA_BOX, // Animation number. + "PARABOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); +static AnimTypeClass const SonarBox( + ANIM_SONAR_BOX, // Animation number. + "SONARBOX", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const CMissile( + ANIM_CRATE_MISSILE, // Animation number. + "MISSILE2", // Data name of animation. + 48, // Maximum dimension of animation. + 0, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 2, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const MoveFlash( + ANIM_MOVE_FLASH, // Animation number. + "MOVEFLSH", // Data name of animation. + 24, // Maximum dimension of animation. + 0, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + true, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE // Follow up animation. +); + +static AnimTypeClass const Corpse1( + ANIM_CORPSE1, // Animation number. + "CORPSE1", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse2( + ANIM_CORPSE2, // Animation number. + "CORPSE2", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Corpse3( + ANIM_CORPSE3, // Animation number. + "CORPSE3", // Data name of animation. + 24, // Maximum dimension of animation. + 1, // Biggest animation stage. + true, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 15, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + 0, // Ending frame of loop back. + -1, // Number of animation stages. + 0, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); + +static AnimTypeClass const Twinkle1( + ANIM_TWINKLE1, // Animation number. + "TWINKLE1", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle2( + ANIM_TWINKLE2, // Animation number. + "TWINKLE2", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Twinkle3( + ANIM_TWINKLE3, // Animation number. + "TWINKLE3", // Data name of animation. + 8, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const Flak( + ANIM_FLAK, // Animation number. + "FLAK", // Data name of animation. + 8, // Maximum dimension of animation. + 7, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_NONE, // Sound effect to play. + ANIM_NONE, + 17 // Virtual stages +); +static AnimTypeClass const WaterExp1( + ANIM_WATER_EXP1, // Animation number. + "H2O_EXP1", // Data name of animation. + 64, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp2( + ANIM_WATER_EXP2, // Animation number. + "H2O_EXP2", // Data name of animation. + 40, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); +static AnimTypeClass const WaterExp3( + ANIM_WATER_EXP3, // Animation number. + "H2O_EXP3", // Data name of animation. + 32, // Maximum dimension of animation. + 3, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_SPLASH, // Sound effect to play. + ANIM_NONE +); + + +static AnimTypeClass const MineExp1( + ANIM_MINE_EXP1, // Animation number. + "VEH-HIT2", // Data name of animation. + 21, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + true, // Forms a crater? + false, // Sticks to unit in square? + false, // Ground level animation? + false, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 1, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_MINEBLOW, // Sound effect to play. + ANIM_NONE +); + +#ifdef FIXIT_ANTS +static AnimTypeClass const Ant1Death( + ANIM_ANT1_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE1" +); + +static AnimTypeClass const Ant2Death( + ANIM_ANT2_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE2" +); + +static AnimTypeClass const Ant3Death( + ANIM_ANT3_DEATH, // Animation number. + "ANTDIE", // Data name of animation. + 28, // Maximum dimension of animation. + 1, // Biggest animation stage. + false, // Theater specific art imagery? + true, // Normalized animation rate? + false, // Uses white translucent table? + false, // Scorches the ground? + false, // Forms a crater? + false, // Sticks to unit in square? + true, // Ground level animation? + true, // Translucent colors in this animation? + false, // Is this a flame thrower animation? + 0, // Damage to apply per tick (fixed point). + 4, // Delay between frames. + 0, // Starting frame number. + 0, // Loop start frame number. + -1, // Ending frame of loop back. + -1, // Number of animation stages. + 1, // Number of times the animation loops. + VOC_ANTDIE, // Sound effect to play. + ANIM_NONE, + -1, + 0x100, + "ANTDIE3" +); +#endif + +/*********************************************************************************************** + * AnimTypeClass::AnimTypeClass -- Constructor for animation types. * + * * + * This is the constructor for static objects that elaborate the various animation types * + * allowed in the game. Each animation in the game is of one of these types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass::AnimTypeClass( + AnimType anim, + char const * name, + int size, + int biggest, + bool istheater, + bool isnormal, + bool iswhitetrans, + bool isscorcher, + bool iscrater, + bool issticky, + bool ground, + bool istrans, + bool isflame, + fixed damage, + int delaytime, + int start, + int loopstart, + int loopend, + int stages, + int loops, + VocType soundid, + AnimType chainto, + int virtualstages, + int virtualscale, + char const * virtualname, + AnimType virtualanim) : + ObjectTypeClass(RTTI_ANIMTYPE, + int(anim), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsNormalized(isnormal), + IsGroundLayer(ground), + IsTranslucent(istrans), + IsWhiteTrans(iswhitetrans), + IsFlameThrower(isflame), + IsScorcher(isscorcher), + IsCraterForming(iscrater), + IsSticky(issticky), + IsTheater(istheater), + Type(anim), + Size(size), + Biggest(biggest), + Damage(damage), + Delay(delaytime), + Start(start), + LoopStart(loopstart), + LoopEnd(loopend), + Stages(stages), + Loops(loops), + Sound(soundid), + ChainTo(chainto), + VirtualStages(virtualstages), + VirtualScale(virtualscale), + VirtualName(virtualname), + VirtualAnim(virtualanim) +{ +} + + +/*********************************************************************************************** + * AnimTypeClass::operator new -- Allocate an animation type object from private pool. * + * * + * This routine will allocate an animation type class object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated anim type object. If no anim type * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * AnimTypeClass::operator new(size_t) +{ + return(AnimTypes.Alloc()); +} + + +/*********************************************************************************************** + * AnimTypeClass::operator delete -- Returns an anim type class object back to the pool. * + * * + * This will return the anim type class object back to the memory pool from whence it was * + * previously allocated. * + * * + * INPUT: pointer -- Pointer to the anim type class object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::operator delete(void * pointer) +{ + AnimTypes.Free((AnimTypeClass *)pointer); +} + + +/*********************************************************************************************** + * AnimTypeClass::Init_Heap -- Initialize the animation type system. * + * * + * This routine is called to initialize the animation type class heap. It allocates all * + * known animation types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init_Heap(void) +{ + /* + ** These anim type class objects must be allocated in the exact order that they + ** are specified in the AnimType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new AnimTypeClass(FBall1); + new AnimTypeClass(FireBallFade); + new AnimTypeClass(Frag1); + new AnimTypeClass(VehHit1); + new AnimTypeClass(VehHit2); + new AnimTypeClass(VehHit3); + new AnimTypeClass(ArtExp1); + new AnimTypeClass(Napalm1); + new AnimTypeClass(Napalm2); + new AnimTypeClass(Napalm3); + new AnimTypeClass(SmokePuff); + new AnimTypeClass(Piff); + new AnimTypeClass(PiffPiff); + new AnimTypeClass(Fire3); + new AnimTypeClass(Fire2); + new AnimTypeClass(Fire1); + new AnimTypeClass(Fire4); + new AnimTypeClass(Gunfire); + new AnimTypeClass(SmokeM); + new AnimTypeClass(BurnSmall); + new AnimTypeClass(BurnMed); + new AnimTypeClass(BurnBig); + new AnimTypeClass(OnFireSmall); + new AnimTypeClass(OnFireMed); + new AnimTypeClass(OnFireBig); + new AnimTypeClass(SAMN); + new AnimTypeClass(SAMNE); + new AnimTypeClass(SAME); + new AnimTypeClass(SAMSE); + new AnimTypeClass(SAMS); + new AnimTypeClass(SAMSW); + new AnimTypeClass(SAMW); + new AnimTypeClass(SAMNW); + new AnimTypeClass(GUNN); + new AnimTypeClass(GUNNE); + new AnimTypeClass(GUNE); + new AnimTypeClass(GUNSE); + new AnimTypeClass(GUNS); + new AnimTypeClass(GUNSW); + new AnimTypeClass(GUNW); + new AnimTypeClass(GUNNW); + new AnimTypeClass(LZSmoke); + new AnimTypeClass(CDeviator); + new AnimTypeClass(CDollar); + new AnimTypeClass(CEarth); + new AnimTypeClass(CEmpulse); + new AnimTypeClass(CInvun); + new AnimTypeClass(CMine); + new AnimTypeClass(CRapid); + new AnimTypeClass(CStealth); + new AnimTypeClass(CMissile); + new AnimTypeClass(MoveFlash); + new AnimTypeClass(OilFieldBurn); + new AnimTypeClass(ElectricDie); + new AnimTypeClass(Parachute); + new AnimTypeClass(DogElectricDie); + new AnimTypeClass(Corpse1); + new AnimTypeClass(Corpse2); + new AnimTypeClass(Corpse3); + new AnimTypeClass(SputDoor); + new AnimTypeClass(AtomBomb); + new AnimTypeClass(ChronoBox); + new AnimTypeClass(GPSBox); + new AnimTypeClass(InvulBox); + new AnimTypeClass(ParaBox); + new AnimTypeClass(SonarBox); + new AnimTypeClass(Twinkle1); + new AnimTypeClass(Twinkle2); + new AnimTypeClass(Twinkle3); + new AnimTypeClass(Flak); + new AnimTypeClass(WaterExp1); + new AnimTypeClass(WaterExp2); + new AnimTypeClass(WaterExp3); + new AnimTypeClass(CrateArmor); + new AnimTypeClass(CrateSpeed); + new AnimTypeClass(CrateFPower); + new AnimTypeClass(CrateTQuake); + new AnimTypeClass(ParaBomb); + new AnimTypeClass(MineExp1); +#ifdef FIXIT_ANTS + new AnimTypeClass(Ant1Death); + new AnimTypeClass(Ant2Death); + new AnimTypeClass(Ant3Death); +#endif + new AnimTypeClass(Fire3Virtual); + new AnimTypeClass(Fire2Virtual); + new AnimTypeClass(Fire1Virtual); + new AnimTypeClass(Fire4Virtual); +} + +/*********************************************************************************************** + * AnimTypeClass::One_Time -- Performs one time action for animation types. * + * * + * This will load the animation shape data. It is called by the game initialization * + * process. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called ONLY once. * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::One_Time(void) +{ + for (int index = ANIM_FIRST; index < ANIM_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + AnimTypeClass const & anim = As_Reference((AnimType)index); + + if (!anim.IsTheater) { + + _makepath(fullname, NULL, NULL, As_Reference((AnimType)index).IniName, ".SHP"); + + #ifndef NDEBUG + RawFileClass file(fullname); + if (file.Is_Available()) { + ((void const *&)As_Reference((AnimType)index).ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)As_Reference((AnimType)index).ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)As_Reference((AnimType)index).ImageData) = MFCD::Retrieve(fullname); + #endif + } + } +} + + +/*********************************************************************************************** + * AnimTypeClass::Init -- Load any animation artwork that is theater specific. * + * * + * This routine will examine all the animation types and for any that are theater * + * specific, it will fetch a pointer to the artwork appropriate for the theater specified. * + * * + * INPUT: theater -- The theater to align the animation artwork with. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine when the theater changes. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (int index = ANIM_FIRST; index < ANIM_COUNT; index++) { + AnimTypeClass const & anim = As_Reference((AnimType)index); + + if (anim.IsTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + _makepath(fullname, NULL, NULL, anim.IniName, Theaters[theater].Suffix); + ((void const *&)anim.ImageData) = MFCD::Retrieve(fullname); + } + } + } +} + + +/*********************************************************************************************** + * Anim_Name -- Fetches the ASCII name of the animation type specified. * + * * + * This will convert the animation type specified into a text name. This name can be used * + * for uniquely identifying the animation. * + * * + * INPUT: anim -- The anim type to convert to a text string. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that identifies this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Anim_Name(AnimType anim) +{ + if (anim == ANIM_NONE) return(""); + + return(AnimTypeClass::As_Reference(anim).IniName); +} + + +/*********************************************************************************************** + * AnimTypeClass::As_Reference -- Fetch a reference to the animation type specified. * + * * + * This routine will convert the animation type specified into a reference to the * + * animation type class object. * + * * + * INPUT: type -- The animation type to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the animation type class object. * + * * + * WARNINGS: Be sure that the animation type specified is legal. If it isn't then the * + * results of this routine are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimTypeClass & AnimTypeClass::As_Reference(AnimType type) +{ + return(* AnimTypes.Ptr(type)); +} + diff --git a/code/redalert/adpcm.cpp b/code/redalert/adpcm.cpp new file mode 100644 index 0000000..763438c --- /dev/null +++ b/code/redalert/adpcm.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "function.h" + +extern "C" { +#include "soscomp.h" +#include "itable.cpp" +#include "dtable.cpp" + + +void sosCODECInitStream(_SOS_COMPRESS_INFO* info) +{ + info->dwSampleIndex = 0; + info->dwPredicted = 0; +} + + +unsigned long sosCODECDecompressData(_SOS_COMPRESS_INFO* info, unsigned long numbytes) +{ + unsigned long token; + long sample; + unsigned int fastindex; + unsigned char *inbuff; + unsigned short *outbuff; + + inbuff = (unsigned char *)info->lpSource; + outbuff = (unsigned short *)info->lpDest; + + // Preload variables before the big loop + fastindex = (unsigned int)info->dwSampleIndex; + sample = info->dwPredicted; + + if (!numbytes) + goto SkipLoop; + + do { + // First nibble + token = *inbuff++; + fastindex += token & 0x0f; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + + // Second nibble + fastindex += token >> 4; + sample += DiffTable[fastindex]; + fastindex = IndexTable[fastindex]; + if (sample > 32767L) + sample = 32767L; + if (sample < -32768L) + sample = -32768L; + *outbuff++ = (unsigned short)sample; + } while(--numbytes); + +SkipLoop: + + // Put local vars back + info->dwSampleIndex = (unsigned long)fastindex; + info->dwPredicted = sample; + return(numbytes << 2); +} + +} diff --git a/code/redalert/aircraft.cpp b/code/redalert/aircraft.cpp new file mode 100644 index 0000000..7722529 --- /dev/null +++ b/code/redalert/aircraft.cpp @@ -0,0 +1,4437 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AIRCRAFT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * AircraftClass::Good_LZ -- Locates a good spot ot land. * + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * AircraftClass::Mission_Move -- Handles movement mission. * + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * AircraftClass::New_LZ -- Find a good landing zone. * + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * AircraftClass::Response_Move -- Gives audio response to move request. * + * AircraftClass::Response_Select -- Gives audio response when selected. * + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::What_Action -- Determines what action to perform. * + * AircraftClass::operator delete -- Deletes the aircraft object. * + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Counts_As_Civ_Evac -- Is the specified object a candidate for civilian evac logic? * + * * + * Examines the specified object to see if it qualifies to be a civilian evacuation. This * + * can only occur if it is a civilian (or Tanya) and the special evacuation flag has been * + * set in the scenario control structure. * + * * + * INPUT: candidate -- Candidate object to examine for civilian evacuation legality. * + * * + * OUTPUT: bool; Is the specified object considered a civilian that must be auto-evacuated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Counts_As_Civ_Evac(ObjectClass const * candidate) +{ + /* + ** If the candidate pointer is missing, then return with failure code. + */ + if (candidate == NULL) return(false); + + /* + ** Only infantry objects can be considered for civilian evacuation action. + */ + if (candidate->What_Am_I() != RTTI_INFANTRY) return(false); + + /* + ** Working infantry object pointer. + */ + InfantryClass const * inf = (InfantryClass const *)candidate; + + /* + ** Certain infantry types will always be considered a civilian evacuation candidate. These + ** include the special one-time infantry that appear in some missions. + */ + if (*inf == INFANTRY_EINSTEIN || *inf == INFANTRY_GENERAL || *inf == INFANTRY_DELPHI || *inf == INFANTRY_CHAN) return(true); + + /* + ** Consider Tanya to be part of the civilian evacuation logic if the scenario is + ** specially flagged for this. + */ + if (Scen.IsTanyaEvac && *inf == INFANTRY_TANYA) return(true); + + /* + ** If the infantry is not a civilian, then it isn't allowed to be a civilian evacuation. + */ + if (!inf->Class->IsCivilian) return(false); + + /* + ** Technicians look like civilians, but are not considered a legal evacuation candidate. + */ + if (inf->IsTechnician) return(false); + + /* + ** All tests pass, so return the success of the infantry as a civilian evacuation candidate. + */ + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::operator new -- Allocates a new aircraft object from the pool * + * * + * This routine will allocate an aircraft object from the free aircraft object pool. If * + * there are no free object available, then this routine will fail (return NULL). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocate aircraft object or NULL if none were * + * available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void * AircraftClass::operator new(size_t) +{ + void * ptr = Aircraft.Allocate(); + if (ptr) { + ((AircraftClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * AircraftClass::operator delete -- Deletes the aircraft object. * + * * + * This routine will return the aircraft object back to the free aircraft object pool. * + * * + * INPUT: ptr -- Pointer to the aircraft object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::operator delete(void * ptr) +{ + if (ptr) { + ((AircraftClass *)ptr)->IsActive = false; + } + Aircraft.Free((AircraftClass *)ptr); +} + + +/*********************************************************************************************** + * AircraftClass::AircraftClass -- The constructor for aircraft objects. * + * * + * This routine is the constructor for aircraft objects. An aircraft object can be * + * created and possibly placed into the game system by this routine. * + * * + * INPUT: classid -- The type of aircraft to create. * + * * + * house -- The owner of this aircraft. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +AircraftClass::AircraftClass(AircraftType classid, HousesType house) : + FootClass(RTTI_AIRCRAFT, Aircraft.ID(this), house), + Class(AircraftTypes.Ptr((int)classid)), + SecondaryFacing(PrimaryFacing), + Passenger(false), + IsLanding(false), + IsTakingOff(false), + IsHovering(false), + Jitter(0), + SightTimer(0), + AttacksRemaining(1) +{ + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + Height = FLIGHT_LEVEL; + Strength = Class->MaxStrength; + NavCom = TARGET_NONE; + + /* + ** Keep count of the number of units created. Dont track cargo planes as they are created + ** automatically, not bought. + */ +// if (/*classid != AIRCRAFT_CARGO && */ Session.Type == GAME_INTERNET) { +// House->AircraftTotals->Increment_Unit_Total((int)classid); +// } + +} + + +/*********************************************************************************************** + * AircraftClass::Unlimbo -- Removes an aircraft from the limbo state. * + * * + * This routine is used to transition the aircraft from the limbo to the non limbo state. * + * It occurs when the aircraft is placed on the map for whatever reason. When it is * + * unlimboed, only then will normal game processing recognize it. * + * * + * INPUT: coord -- The coordinate that the aircraft should appear at. * + * * + * dir -- The direction it should start facing. * + * * + * strength (optional) -- sets initial strength * + * * + * mission (optional) -- sets initial mission * + * * + * OUTPUT: bool; Was the aircraft unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (FootClass::Unlimbo(coord, dir)) { + + if (*this == AIRCRAFT_BADGER || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera)) { + IsALoaner = true; + } + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->AScan |= (1L << Class->Type); + House->ActiveAScan |= (1L << Class->Type); + + /* + ** Hack it so that aircraft that are both passenger and cargo carrying + ** will carry passengers at the expense of ammo. + */ + if (Is_Something_Attached()) { + Ammo = 0; + Passenger = true; + } + + /* + ** Forces the body of the helicopter to face the correct direction. + */ + SecondaryFacing = dir; + + /* + ** Start rotor animation. + */ + if (!Class->IsFixedWing) { + Set_Rate(1); + Set_Stage(0); + } + + /* + ** When starting at flight level, then give it speed. When landed + ** then it must be stationary. + */ + if (Height == FLIGHT_LEVEL) { + Set_Speed(0xFF); + } else { + Set_Speed(0); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Shape_Number -- Fetch the shape number to use for the aircraft. * + * * + * This will determine what shape number to use for the aircraft in its current state. * + * The shape number can be used for drawing or determine shape rectangle size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the aircraft body. * + * * + * WARNINGS: Some aircraft, particularly helicopters, require other shapes attached to it. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Shape_Number(void) const +{ + int shapenum = 0; + + switch (Class->Rotation) { + case 32: + shapenum = UnitClass::BodyShape[Dir_To_32(SecondaryFacing)]; + break; + + case 16: + shapenum = UnitClass::BodyShape[Dir_To_16(SecondaryFacing)*2]/2; + break; + + case 8: + shapenum = UnitClass::BodyShape[Dir_To_8(SecondaryFacing)*4]/4; + break; + + default: + break; + } + + /* + ** If there is a door on this aircraft (Chinook), then adjust the + ** shape number to match the door open state. + */ + if (!Is_Door_Closed()) { + shapenum = Class->Rotation + Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_It -- Renders an aircraft object at the location specified. * + * * + * This routine is used to display the aircraft object at the coordinates specified. * + * The tactical map display uses this routine for all aircraft rendering. * + * * + * INPUT: x,y -- The coordinates to render the aircraft at. * + * * + * window -- The window that the coordinates are based upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (!shapefile) return; + + int shapenum = Shape_Number(); + + /* + ** Certain aircraft use algorithmic rotation for some stages. Set the + ** rotation value accordingly. A rotation of DIR_N means no rotation at all. + */ + DirType rotation = DIR_N; + if (Class->Rotation == 16) { + rotation = DirType(Rotation16[SecondaryFacing]); + } + +#ifdef TOFIX + /* + ** The orca attack helicopter uses a special shape set when it is travelling + ** forward above a certain speed. + */ + if (*this == AIRCRAFT_HIND && Get_Speed() >= MPH_MEDIUM_FAST) { + shapenum += Class->Rotation; + } +#endif + + /* + ** Helicopters that are flying have a "bobbing" effect. + */ + int jitter = 0; + if (Height == FLIGHT_LEVEL && Get_Speed() < 3) { + static int _jitter[] = {0,0,0,0,1,1,1,0,0,0,0,0,-1,-1,-1,0}; + jitter = _jitter[::Frame % 16]; + } + + // Virtual window needs to draw the body first so it's considered the primary object and the shadow is a sub-object + if (window == WINDOW_VIRTUAL) { + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation); + + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL); + } + } else { + /* + ** Special manual shadow draw code. + */ + if (Visual_Character() <= VISUAL_DARKEN) { + CC_Draw_Shape(this, shapefile, shapenum, x+1, y+2, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, DisplayClass::FadingShade, NULL); + } + + /* + ** Draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, shapenum, x, y+jitter, window, rotation); + } + + /* + ** If this aircraft is equipped with rotor blades, then draw them at this time. + */ + if (Class->IsRotorEquipped) { + Draw_Rotors(x, y+jitter, window); + } + + /* + ** This draws any overlay graphics on the aircraft. + */ + FootClass::Draw_It(x, y-Lepton_To_Pixel(Height), window); +} + + +/*********************************************************************************************** + * AircraftClass::Draw_Rotors -- Draw rotor blades on the aircraft. * + * * + * This routine will draw rotor blades on the aircraft. It is presumed that the aircraft * + * has already been drawn at the X and Y pixel coordinates specified. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to draw the rotor blades. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Draw_Rotors(int x, int y, WindowNumberType window) const +{ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + int shapenum; + + /* + ** The rotor shape number depends on whether the helicopter is idling + ** or not. A landed helicopter uses slow moving "idling" blades. + */ + if (Height == 0) { + shapenum = (Fetch_Stage()%8)+4; + flags = flags | SHAPE_GHOST; + } else { + shapenum = Fetch_Stage()%4; + flags = flags | SHAPE_FADING|SHAPE_PREDATOR; + } + + if (*this == AIRCRAFT_TRANSPORT) { + int _stretch[FACING_COUNT] = {8, 9, 10, 9, 8, 9, 10, 9}; + + /* + ** Dual rotors offset along flight axis. + */ + short xx = x; + short yy = y-Lepton_To_Pixel(Height); + FacingType face = Dir_Facing(SecondaryFacing); + Move_Point(xx, yy, SecondaryFacing.Current(), _stretch[face]); + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "RROTOR", AircraftTypeClass::RRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + Move_Point(xx, yy, SecondaryFacing.Current()+DIR_S, _stretch[face]*2); + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "LROTOR", AircraftTypeClass::LRotorData, shapenum, xx, yy-2, window, flags, NULL, DisplayClass::UnitShadow); + + } else { + + /* + ** Single rotor centered about shape. + */ + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, "RROTOR", AircraftTypeClass::RRotorData, shapenum, x, ((y-Lepton_To_Pixel(Height))-2), window, flags, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * AircraftClass::Read_INI -- Reads aircraft object data from an INI file. * + * * + * This routine is used to read the aircraft object data from the INI file buffer * + * specified. This is used by the scenario loader code to interpret the INI file and * + * create the specified objects therein. * + * * + * INPUT: buffer -- Pointer to the INI buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Read_INI(CCINIClass & ini) +{ + AircraftClass * air; // Working unit pointer. + HousesType inhouse; // Unit house. + AircraftType classid; // Unit class. + char buf[128]; + + int counter = ini.Entry_Count(INI_Name()); + for (int index = 0; index < counter; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)-1); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = AircraftTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != AIRCRAFT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + air = new AircraftClass(classid, inhouse); + if (air) { + COORDINATE coord; + int strength; + DirType dir; + + /* + ** Read the raw data. + */ + char * token = strtok(NULL, ","); + if (token) { + strength = atoi(token); + } else { + strength = 0; + } + + token = strtok(NULL, ","); + if (token) { + coord = Cell_Coord((CELL)atoi(token)); + } else { + coord = 0xFFFFFFFFL; + } + + token = strtok(NULL, ","); + if (token) { + dir = (DirType)atoi(token); + } else { + dir = DIR_N; + } + + if (!Map.In_Radar(Coord_Cell(coord))) { + delete air; + } else { + + air->Strength = (int)air->Class->MaxStrength * fixed(strength, 256); // Cast this to (int). ST - 5/8/19 + if (air->Unlimbo(coord, dir)) { + air->Assign_Mission(AircraftClass::Mission_From_Name(strtok(NULL, ",\n\r"))); + } else { + delete air; + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Hunt -- Maintains hunt AI for the aircraft. * + * * + * Hunt AI consists of finding a target and attacking it. If there is no target assigned * + * and this unit doesn't automatically hunt for more targets, then it will change * + * mission to a more passive (land and await further orders) type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of ticks before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Hunt(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + if (TarCom != NavCom) { + Assign_Destination(TarCom); + } + + enum { + LOOK_FOR_TARGET, + TAKE_OFF, + FLY_TO_TARGET, + DROP_BOMBS, + REGROUP + }; + switch (Status) { + + /* + ** Acquiring target stage. + */ + case LOOK_FOR_TARGET: + if (Target_Legal(TarCom)) { + Status = TAKE_OFF; + return(1); + } else { + if (!Team.Is_Valid()) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + + /* + ** If there is no target, then this aircraft should just do its normal thing. + */ + if (!Target_Legal(TarCom) && !Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + } + break; + + /* + ** Make the aircraft take off from the airstrip. + */ + case TAKE_OFF: + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact()/*KO && Map[Coord].Cell_Building() == Contact_With_Whom()*/) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_TARGET; + } + return(1); + + /* + ** Homing in on target stage. + */ + case FLY_TO_TARGET: + switch (Can_Fire(TarCom, 0)) { + case FIRE_FACING: + /* + ** Catch the case where it is tightly circling the target. In that + ** case, increase the delay so that it has a chance to fly away and + ** break the circle cycle. + */ + if (In_Range(TarCom, 0) || Passenger) { + return(TICKS_PER_SECOND * 2); + } + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_AMMO: + Status = REGROUP; + break; + + case FIRE_CANT: + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_OK: + Status = DROP_BOMBS; + return(1); + + default: + if (!PrimaryFacing.Is_Rotating() && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + break; + } + return(TICKS_PER_SECOND/2); + + /* + ** Dropping a stream of bombs phase. + */ + case DROP_BOMBS: + TARGET targ; + switch (Can_Fire(TarCom, 0)) { + case FIRE_OK: + targ = ::As_Target(Coord_Move(Center_Coord(), SecondaryFacing, Weapon_Range(0)-0x0200)); + if (Class->PrimaryWeapon != NULL) { + if (Class->PrimaryWeapon->IsCamera) { + Status = REGROUP; + } else { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** Force the target to be the actual target if this aircraft is + ** equipped with homing projectile. + */ + if (Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->ROT > 0) { + targ = TarCom; + } + } + Fire_At(targ, 0); + if (Class->Is_Two_Shooter()) { + Fire_At(targ, 0); + } + return(Arm); + + case FIRE_RANGE: + case FIRE_FACING: + Status = FLY_TO_TARGET; + return(TICKS_PER_SECOND*4); + + case FIRE_ILLEGAL: + if (Mission == MISSION_ATTACK) { + Status = REGROUP; + } else { + Status = LOOK_FOR_TARGET; + } + break; + + case FIRE_CANT: + Status = REGROUP; + break; + + case FIRE_AMMO: + AttacksRemaining--; + Status = REGROUP; + break; + + default: + break; + } + return(1); + + /* + ** Pull away to regroup for possibly another attack or a retreat. + */ + case REGROUP: + if (Ammo == 0) { + AttacksRemaining = 0; + if (Team.Is_Valid()) Team->Remove(this); + Enter_Idle_Mode(); + } + + if (Mission == MISSION_ATTACK || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) || (!AttacksRemaining && !Is_Something_Attached())) { + + if (IsALoaner) { + if (Team) Team->Remove(this); + Assign_Mission(MISSION_RETREAT); + Commence(); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + Commence(); + } else { + Status = LOOK_FOR_TARGET; + } + break; + + default: + break; + } + } else { + if (!Ammo) { + if (Team) Team->Remove(this); + Enter_Idle_Mode(); + } else { + if (!Target_Legal(TarCom)) { + if (Session.Type != GAME_NORMAL) { + Assign_Target(Greatest_Threat(THREAT_TIBERIUM)); + } + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(THREAT_NORMAL)); + } + if (!Target_Legal(TarCom)) { + Enter_Idle_Mode(); + return(1); + } + } + + Assign_Mission(MISSION_ATTACK); + return(1); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::AI -- Processes the normal non-graphic AI for the aircraft. * + * * + * This handles the non-graphic AI processing for the aircraft. This usually entails * + * maintenance and other AI functions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::AI(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Perform any base class AI processing. If during this process, the aircraft was + ** destroyed, then detect this and bail from this AI routine early. + */ + FootClass::AI(); + if (!IsActive) { + return; + } + + /* + ** A Mission change can always occur if the aircraft is landed or flying. + */ + if (!IsLanding && !IsTakingOff) { + Commence(); + } + + /* + ** Handle any body rotation at this time. Body rotation can occur even if the + ** flying object is not actually moving. + */ + Rotation_AI(); + + /* + ** Handle any aircraft movement at this time. + */ + Movement_AI(); + + /* + ** Any aircraft that is not in the ground layer must be redrawn. This is a + ** performance hit, but there is no other choice. The cells under an aircraft + ** do not know if there is an aircraft above it. Thus, it cannot flag the + ** aircraft to redraw. As a consequence, all aircraft must redraw. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(); + } + + /* + ** Perform sighting every so often as controlled by the sight timer. + */ + //if (IsOwnedByPlayer && Class->SightRange && SightTimer == 0) { // Changed to facilitate client/server multiplayer. ST - 8/2/2019 2:29PM + if (House->IsHuman && Class->SightRange && SightTimer == 0) { + Look(); + SightTimer = TICKS_PER_SECOND; + } + + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Landing_Takeoff_AI()) { + return; + } + + /* + ** Always flag the map draw process to occur if there is an aircraft in the view. + ** This ensures that it will be rendered even if there is nothing else that flagged + ** the map to be redrawn. + */ + if (Map.In_View(Coord_Cell(Coord))) { + Map.Flag_To_Redraw(false); + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** When aircraft leave the edge of the map, they might get destroyed. This occurs if the + ** aircraft is a non-player produced unit and it has completed its mission. A transport + ** helicopter that has already delivered reinforcements is a good example of this. + */ + if (Edge_Of_World_AI()) { + return; + } +} + + +/*********************************************************************************************** + * AircraftClass::Overlap_List -- Returns with list of cells the aircraft overlaps. * + * * + * When aircraft are flying, they can overlap quite a number of cells. These cells can * + * be determined from the coordinate where the aircraft is centered and the size of the * + * aircraft's shape. Landed aircraft are a special case and are usually much smaller * + * than when flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a cell offset list that specifies all cells that * + * the aircraft overlaps given the aircraft's current state. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + *=============================================================================================*/ +short const * AircraftClass::Overlap_List(bool redraw) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static short const _list[] = { + -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), + -1, 0, 1, + (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), + -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), + -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), + REFRESH_EOL + }; + + static short const _listbadger[] = { + -(MAP_CELL_W-2), -(MAP_CELL_W-1), -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W+2), + -2, -1, 0, 1, 2, + (MAP_CELL_W-2), (MAP_CELL_W-1), MAP_CELL_W, (MAP_CELL_W+1), (MAP_CELL_W+2), + -((MAP_CELL_W*2)-2), -((MAP_CELL_W*2)-1), -(MAP_CELL_W*2), -((MAP_CELL_W*2)+1), -((MAP_CELL_W*2)+2), + -((MAP_CELL_W*3)-2), -((MAP_CELL_W*3)-1), -(MAP_CELL_W*3), -((MAP_CELL_W*3)+1), -((MAP_CELL_W*3)+2), + REFRESH_EOL + }; + + if (redraw || Height != 0) { +#ifdef PARTIAL + Rect rect; + if (!IsSelected && Class->DimensionData != NULL && Class->IsFixedWing) { + int shapenum = min(Shape_Number(), Get_Build_Frame_Count(Class->Get_Image_Data())-1); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + + /* + ** Increase the rectangle for the aircraft since the aircraft could + ** have its shape algorithmically rotated. + */ + rect.X -= 5; + rect.Y -= 5; + rect.Width += 10; + rect.Height += 10; + + Rect hrect = rect; + + hrect.Y -= Lepton_To_Pixel(Height); + + return(Coord_Spillage_List(Coord, Union(rect, hrect), true)); + } +#endif + + if (*this == AIRCRAFT_BADGER) { + return(_listbadger); + } else { + return(_list); + } + } + return(Class->Overlap_List()); +} + + +/*********************************************************************************************** + * AircraftClass::Init -- Initialize the aircraft system to an empty state. * + * * + * This routine is used to clear out the aircraft allocation system. It is called in * + * preparation for a scenario load or save game load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Init(void) +{ + Aircraft.Free_All(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Unload -- Handles unloading cargo. * + * * + * This function is used to handle finding, heading toward, landing, and unloading the * + * cargo from the aircraft. Once unloading of cargo has occurred, then the aircraft follows * + * a different mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks to delay before calling this function again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Unload(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + Assign_Target(NavCom); + return(Mission_Hunt()); + + } else { + enum { + SEARCH_FOR_LZ, + FLY_TO_LZ, + LAND_ON_LZ, + UNLOAD_PASSENGERS, + TAKE_OFF + }; + + switch (Status) { + + /* + ** Search for an appropriate destination spot if one isn't already assigned. + */ + case SEARCH_FOR_LZ: + if (Height == 0 && (Target_Legal(NavCom) || Coord == As_Coord(NavCom))) { + Status = UNLOAD_PASSENGERS; + } else { + if (!Is_LZ_Clear(NavCom)) { + + FootClass * foot = Attached_Object(); + if (foot != NULL && foot->Team && foot->Team->Class->Origin != -1) { + Assign_Destination(::As_Target(Scen.Waypoint[foot->Team->Class->Origin])); + } else { + Assign_Destination(New_LZ(::As_Target(Scen.Waypoint[WAYPT_REINF]))); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } + } else { + if (Height == FLIGHT_LEVEL) { + Status = FLY_TO_LZ; + } else { + Status = TAKE_OFF; + } + } + } + break; + + /* + ** Fly to destination. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0100) { + SecondaryFacing.Set_Desired(Pose_Dir()); + + if (distance < 0x0010) { + Status = LAND_ON_LZ; + } + return(1); + } else { + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + return(5); + } + } else { + Status = SEARCH_FOR_LZ; + } + break; + + /* + ** Landing phase. Just delay until landing is complete. At that time, + ** transition to the unloading phase. + */ + case LAND_ON_LZ: + if (IsTakingOff) { + Status = TAKE_OFF; + } else { + if (Process_Landing()) { + Status = UNLOAD_PASSENGERS; + } + } + return(1); + + /* + ** Hold while unloading passengers. When passengers are unloaded the order for this + ** transport gets changed to MISSION_RETREAT. + */ + case UNLOAD_PASSENGERS: + if (!IsTethered) { + if (Is_Something_Attached()) { + FootClass * unit = (FootClass *)Detach_Object(); + + /* + ** First thing is to lift the transport off of the map so that the unlimbo + ** process for the passengers is more likely to succeed. + */ + Map.Pick_Up(Coord_Cell(Coord), this); + + if (!Exit_Object(unit)) { + delete unit; + } + + /* + ** Restore the transport back down on the map. + */ + Map.Place_Down(Coord_Cell(Coord), this); + + if (!Is_Something_Attached()) { + Enter_Idle_Mode(); + } + + } else { + + Enter_Idle_Mode(); + } + } + break; + + /* + ** Aircraft is now taking off. Once the aircraft reaches flying altitude then it + ** will either take off or look for another landing spot to try again. + */ + case TAKE_OFF: { + if (Process_Take_Off()) { + if (Is_Something_Attached()) { + Status = SEARCH_FOR_LZ; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + } else { + Enter_Idle_Mode(); + } + } + return(1); + } + + default: + break; + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Is_LZ_Clear -- Determines if landing zone is free for landing. * + * * + * This routine examines the landing zone (as specified by the target parameter) in order * + * to determine if it is free to be landed upon. Call this routine when it is necessary * + * to double check this. Typically this occurs right before a helicopter lands and also * + * when determining the landing zone in the first place. * + * * + * INPUT: target -- The target that is the "landing zone". * + * * + * OUTPUT: bool; Is the landing zone clear for landing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/94 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Is_LZ_Clear(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(target)) return(false); + CELL cell = ::As_Cell(target); + if (!Map.In_Radar(cell)) return(false); + + /* + ** If the requested landing location is occupied, then only consider that location + ** legal if the occupying object is in radio contact with the aircraft. This presumes that + ** the two objects know what they are doing. + */ + ObjectClass * object = Map[cell].Cell_Object(); + if (object) { + if (object == this) return(true); + + if (In_Radio_Contact() && Contact_With_Whom() == object) { + return(true); + } + return(false); + } + + if (!Map[cell].Is_Clear_To_Move(SPEED_TRACK, false, false)) return(false); + + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Sort_Y -- Figures the sorting coordinate. * + * * + * This routine is used to determine the coordinate to use for sorting the aircraft. This * + * sorting value is used when the aircraft is on the ground. At that time the aircraft * + * must be rendered in proper relationship to the other ground objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting the aircraft with other ground * + * objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE AircraftClass::Sort_Y(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Retreat -- Handles the aircraft logic for leaving the battlefield. * + * * + * This mission will be followed when the aircraft decides that it is time to leave the * + * battle. Typically, this occurs when a loaner transport has dropped off its load or when * + * an attack air vehicle has expended its ordinance. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 08/13/1995 JLB : Handles aircraft altitude gain after takeoff logic. * + *=============================================================================================*/ +int AircraftClass::Mission_Retreat(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + if (Class->IsFixedWing && Height < FLIGHT_LEVEL) { + Height += 1; + return(3); + } + return(TICKS_PER_SECOND*10); + } + + enum { + TAKE_OFF, + FACE_MAP_EDGE, + KEEP_FLYING + }; + switch (Status) { + + /* + ** Take off if landed. + */ + case TAKE_OFF: + if (Process_Take_Off()) { + Status = FACE_MAP_EDGE; + } + return(1); + + /* + ** Set facing and speed toward the friendly map edge. + */ + case FACE_MAP_EDGE: + Set_Speed(0xFF); + + /* + ** Take advantage of the fact that the source map edge enumerations happen to + ** occur in a clockwise order and are the first four enumerations of the map + ** edge default for the house. If this value is masked and then shifted, a + ** normalized direction value results. Use this value to head the aircraft + ** toward the "friendly" map edge. + */ + PrimaryFacing.Set_Desired((DirType)((House->Control.Edge & 0x03) << 6)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Status = KEEP_FLYING; + break; + + /* + ** Just do nothing since we are headed toward the map edge. When the edge is + ** reached, the aircraft should be automatically eliminated. + */ + case KEEP_FLYING: + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Exit_Object -- Unloads passenger from aircraft. * + * * + * This routine is called when the aircraft is to unload a passenger. The passenger must * + * be able to move under its own power. Typical situation is when a transport helicopter * + * is to unload an infantry unit. * + * * + * INPUT: unit -- Pointer to the unit that is to be unloaded from this aircraft. * + * * + * OUTPUT: bool; Was the unit unloaded successfully? * + * * + * WARNINGS: The unload process is merely started by this routine. Radio contact is * + * established with the unloading unit and when the unit is clear of the aircraft * + * the radio contact will be broken and then the aircraft is free to pursue * + * other. * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Exit_Object(TechnoClass * unit) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static FacingType _toface[FACING_COUNT] = {FACING_S, FACING_SW, FACING_SE, FACING_NW, FACING_NE, FACING_N, FACING_W, FACING_E}; + CELL cell; + + /* + ** Find a free cell to drop the unit off at. + */ + FacingType face; + for (face = FACING_N; face < FACING_COUNT; face++) { + cell = Adjacent_Cell(Coord_Cell(Coord), _toface[face]); + if (unit->Can_Enter_Cell(cell) == MOVE_OK) break; + } + + // Should perform a check here to see if no cell could be found. + + /* + ** If the passenger can be placed on the map, then start it moving toward the + ** destination cell and establish radio contact with the transport. This is used + ** to make sure that the transport waits until the passenger is clear before + ** unloading the next passenger or taking off. + */ + if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(::As_Target(cell)); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + unit->Look(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Paradrop_Cargo -- Drop a passenger by parachute. * + * * + * Call this routine when a passenger needs to be dropped off by parachute. One passenger * + * is offloaded by a call to this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay time that it is safe to wait before processing any further * + * paradrop actions. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Paradrop_Cargo(void) +{ + FootClass * passenger = Detach_Object(); + if (passenger) { + if (!passenger->Paradrop(Center_Coord())) { + Attach(passenger); + } else { + + /* + ** Play a sound effect of the parachute opening. + */ + Sound_Effect(VOC_CHUTE1, Coord); + + if (Team.Is_Valid()) { + Team->Remove(passenger); + if (passenger->House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + } +// Arm = Rearm_Delay(IsSecondShot); + Arm = 0; + } + } + return(Arm); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_At -- Handles firing a projectile from an aircraft. * + * * + * Sometimes, aircraft firing needs special handling. Example: for napalm bombs, the * + * bomb travels forward at nearly the speed of the delivery aircraft, not necessarily the * + * default speed defined in the BulletTypeClass structure. * + * * + * INPUT: target -- The target that the projectile is heading for. * + * * + * which -- Which weapon to use in the attack. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with a pointer to the bullet that was created as a result of this attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * AircraftClass::Fire_At(TARGET target, int which) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Passenger aircraft will actually paradrop their cargo instead of + ** firing their weapon. + */ + if (Is_Something_Attached()) { + Paradrop_Cargo(); + return(0); + } + + /* + ** If the weapon is actually a camera, then perform the "snapshot" of the + ** ground instead of normal weapon fire. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera) { + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + + if (House->Is_Ally(PlayerPtr)) { + Map.Sight_From(Coord_Cell(Center_Coord()), 9, House, false); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass *house = HouseClass::As_Pointer(house_type); + + if (house == House || House->Is_Ally(house)) { + Map.Sight_From(Coord_Cell(Center_Coord()), 9, house, false); + } + } + } + + Ammo = 0; + Arm = Rearm_Delay(IsSecondShot); + return(0); + } + + + BulletClass * bullet = FootClass::Fire_At(target, which); + + if (bullet) { + + /* + ** Aircraft reveal when firing + */ + HouseClass *player = HouseClass::As_Pointer(Owner()); + if (player != nullptr && player->IsHuman) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, player, false); + } + + /* + ** Falling bullets move at a speed proportionate to the delivery craft. + */ + if (bullet->Class->IsDropping) { + bullet->Fly_Speed(40, MPH_MEDIUM_SLOW); // TCTC To fix. + } + } + return(bullet); +} + + +/*********************************************************************************************** + * AircraftClass::Take_Damage -- Applies damage to the aircraft. * + * * + * This routine is used to apply damage to the specified aircraft. This is where any * + * special crash animation will be initiated. * + * * + * INPUT: damage -- Reference to the damage that will be applied to the aircraft. * + * This value will be filled in with the actual damage that was * + * applied. * + * * + * distance -- Distance from the source of the explosion to this aircraft. * + * * + * warhead -- The warhead type that the damage occurs from. * + * * + * source -- Pointer to the originator of the damage. This can be used so that * + * proper "thank you" can be delivered. * + * * + * OUTPUT: Returns with the result of the damage as it affects this aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1995 JLB : Created. * + *=============================================================================================*/ +ResultType AircraftClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Flying aircraft take half damage. + */ + if (Height) { + damage /= 2; + } + + /* + ** Apply the damage to the aircraft. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** Special action is performed if the aircraft is killed -- the cargo is destroyed + ** as well. + */ + switch (res) { + case RESULT_DESTROYED: + Kill_Cargo(source); + Death_Announcement(); + new AnimClass(ANIM_FBALL1, Target_Coord()); + + /* + ** Parachute a survivor if possible. + */ + if (Class->IsCrew && Percent_Chance(90) && Map[Center_Coord()].Is_Clear_To_Move(SPEED_FOOT, true, false)) { + InfantryClass * infantry = new InfantryClass(INFANTRY_E1, House->Class->House); + if (infantry != NULL) { + if (!infantry->Paradrop(Center_Coord())) { + delete infantry; + } + } + } + + delete this; + break; + + default: + case RESULT_HALF: + break; + } + + return(res); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Move -- Handles movement mission. * + * * + * This state machine routine is used when an aircraft (usually helicopter) is to move * + * from one location to another. It will handle any necessary take off and landing this * + * may require. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames that should elapse before this routine * + * is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + + enum { + TAKE_OFF, + FLY_TOWARD_TARGET + }; + + switch (Status) { + int distance; + + case TAKE_OFF: + + /* + ** If the aircraft is high enough to begin its mission, then do so. + */ + if (Process_Take_Off()) { + IsTakingOff = false; + Set_Speed(0xFF); + + /* + ** After takeoff is complete, break radio contact. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TOWARD_TARGET; + } + return(1); + + case FLY_TOWARD_TARGET: + PrimaryFacing.Set_Desired(Direction(NavCom)); + distance = Distance(NavCom); + + if (distance < 0x00C0) { + MissionType mission = MISSION_GUARD; + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers() ) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + + /* + ** If this aircraft has nowhere else to go, meaning that + ** there is no airfield available, then it has to crash. + */ + if (Is_Target_Cell(NavCom)) { + + if (Process_Landing()) { + Strength = 1; + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(1); + } + return(500); + } + mission = MISSION_MOVE; + } + Assign_Mission(mission); + Commence(); + } else { + if (!Team.Is_Valid()) { + Enter_Idle_Mode(); + } + } + return(1); + } + break; + + default: + break; + } + + return(5); + } + + enum { + VALIDATE_LZ, + TAKE_OFF, + FLY_TO_LZ, + LAND + }; + switch (Status) { + + /* + ** Double check and change LZ if necessary. + */ + case VALIDATE_LZ: + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } else { + if (!Is_LZ_Clear(NavCom) || !Cell_Seems_Ok(As_Cell(NavCom))) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + } else { + Status = TAKE_OFF; + } + } + break; + + /* + ** Take off if necessary. + */ + case TAKE_OFF: + if (!Target_Legal(NavCom)) { + Status = VALIDATE_LZ; + } else { + if (Process_Take_Off()) { + + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + Status = FLY_TO_LZ; + } + return(1); + } + break; + + /* + ** Fly toward target. + */ + case FLY_TO_LZ: + if (Is_LZ_Clear(NavCom)) { + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + + if (distance < 0x0010) { + Status = LAND; + } + return(1); + } + +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + SecondaryFacing.Set_Desired(Direction(NavCom)); + + } else { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + if (!Target_Legal(NavCom)) { + Status = LAND; + } + } + return(1); + + /* + ** Land on target. + */ + case LAND: + if (IsTakingOff) { + Assign_Destination(New_LZ(NavCom)); + if (Team.Is_Valid()) { + Team->Assign_Mission_Target(NavCom); + } + Status = TAKE_OFF; + } + if (Process_Landing()) { + if (MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + } + return(1); + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::Enter_Idle_Mode -- Gives the aircraft an appropriate mission. * + * * + * Use this routine when the mission for the aircraft is in doubt. This routine will find * + * an appropriate mission for the aircraft and dispatch it. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Enter_Idle_Mode(bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + MissionType mission = (Class->IsFixedWing && IsALoaner && Class->PrimaryWeapon != NULL) ? MISSION_HUNT : MISSION_GUARD; + if (Class->IsFixedWing) { + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + mission = MISSION_RETREAT; + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + if (Team.Is_Valid()) return; + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (Mission != MISSION_ATTACK && IsALoaner && Ammo == 0 && Class->PrimaryWeapon != NULL) { + mission = MISSION_HUNT; + } else { + + if (!IsALoaner) { + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); + Assign_Destination(TARGET_NONE); + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + if (Class->IsFixedWing) { + Status = 0; //BG - reset the mission status to avoid landing on the ground next to the airstrip + if (IsLanding) { + Process_Take_Off(); + } + } + mission = MISSION_ENTER; + } else { + mission = MISSION_RETREAT; + } + } + } + } + + } else { + + if (In_Which_Layer() == LAYER_GROUND) { + if (IsALoaner) { + if (Is_Something_Attached()) { + + /* + ** In the case of a computer controlled helicopter that hold passengers, + ** don't unload when landing. Wait for specific instructions from the + ** controlling team. + */ + if (Team.Is_Valid()) { +// if (Team.Is_Valid() && !House->IsHuman) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + } + } else { + mission = MISSION_RETREAT; + } + } else { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + mission = MISSION_GUARD; + } + } else { + if (Is_Something_Attached()) { + if (IsALoaner) { + if (Team) { + mission = MISSION_GUARD; + } else { + mission = MISSION_UNLOAD; + Assign_Destination(Good_LZ()); + } + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } else { + + /* + ** If this transport is a loaner and part of a team, then remove it from + ** the team it is attached to. + */ + if ((IsALoaner && House->IsHuman) || (!House->IsHuman && !Ammo)) { + if (Team.Is_Valid() && Team->Has_Entered_Map()) { + Team->Remove(this); + } + } + + if (Class->PrimaryWeapon != NULL) { + + /* + ** Weapon equipped helicopters that run out of ammo and were + ** brought in as reinforcements will leave the map. + */ + if (IsALoaner) { + + /* + ** If it has no ammo, then break off of the team and leave the map. + ** If it can fight, then give it fighting orders. + */ + if (Ammo == 0) { + if (Team.Is_Valid()) Team->Remove(this); + mission = MISSION_RETREAT; + } else { + if (!Team.Is_Valid()) { + mission = MISSION_HUNT; + } + } + + } else { + + /* + ** Normal aircraft try to find a good landing spot to rest. + */ + BuildingClass * building = Find_Docking_Bay(Class->Building, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()/* && !ship->In_Radio_Contact()*/) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + Assign_Destination(TARGET_NONE); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building && (Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER || building->What_Am_I() == RTTI_VESSEL) ) { +#else + if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { +#endif + mission = MISSION_ENTER; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (building->What_Am_I() == RTTI_VESSEL) { + Assign_Destination(building->As_Target()); + } +#endif + } else { + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } else { + if (Team) return; + + Assign_Destination(Good_LZ()); + mission = MISSION_MOVE; + } + } + } + } + Assign_Mission(mission); + Commence(); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Fly_To -- Handles state machine for flying to destination. * + * * + * This support routine is used when the helicopter is to fly to the destination. It can * + * optionally slow the helicopter down as it approaches the destination. * + * * + * INPUT: slowdown -- Should the aircraft be slowed down when it approaches the dest? * + * * + * OUTPUT: Returns with the distance remaining between the aircraft and the destination. * + * * + * WARNINGS: Because the aircraft can move at a fast speed, the distance to target value * + * will probably never be zero. The likely case will be that the aircraft * + * overshoots the target. * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + * 03/05/1996 JLB : Specifies destination target value. * + *=============================================================================================*/ +int AircraftClass::Process_Fly_To(bool slowdown, TARGET dest) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) slowdown = false; + + COORDINATE coord; + if (Is_Target_Building(dest)) { + coord = As_Building(dest)->Docking_Coord(); + } else { + coord = As_Coord(dest); + } + int distance = Distance(coord); + + PrimaryFacing.Set_Desired(Direction(coord)); + + if (slowdown) { + int speed = min(distance, 0x0300); + speed = Bound(speed/3, 0x0020, 0x00FF); + if (Speed != speed) { + Set_Speed(speed); + } + } + + if (distance < 0x0010) { + if (slowdown) { + Set_Speed(0); + } + distance = 0; + } + return(distance); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * AircraftClass::Debug_Dump -- Displays the status of the aircraft to the mono monitor. * + * * + * This displays the current status of the aircraft class to the mono monitor. By this * + * display bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Debug_Dump(MonoClass * mono) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_AIRCRAFT)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", AttacksRemaining); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified object. * + * * + * This routine is used when the player clicks over the speicifed object. It will assign * + * the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action that was nominally determined by the What_Action function. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will alter the game sequence and causes an event packet to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_NOMOVE: + return; + + case ACTION_ENTER: + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD, TARGET_NONE, TARGET_NONE); + break; + + default: + break; + } + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * AircraftClass::Active_Click_With -- Handles clicking over specified cell. * + * * + * This routine is used when the player clicks the mouse of the specified cell. It will * + * assign the appropriate mission to the aircraft. * + * * + * INPUT: action -- The action nominally determined by What_Action(). * + * * + * cell -- The cell over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will affect the game sequence and causes an event object to be * + * propagated to all connected machines. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * AircraftClass::Player_Assign_Mission -- Handles player input to assign a mission. * + * * + * This routine is called as a result of player input with the intent to change the * + * mission of the aircraft. * + * * + * INPUT: mission -- The mission requested of the aircraft. * + * * + * target -- The value to assign to the aircraft's targeting computer. * + * * + * dest. -- The value to assign to the aircraft's navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: The mission specified will be executed at an indeterminate future game frame. * + * This is controlled by net/modem propagation delay. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + Queue_Mission(TargetClass(this), mission, target, destination); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine is used to determine what action will likely be performed if the mouse * + * were clicked over the object specified. The display system calls this routine to * + * control the mouse shape. * + * * + * INPUT: target -- Pointer to the object that the mouse is currently over. * + * * + * OUTPUT: Returns with the action that will occur if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(ObjectClass const * target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(target); + + if (action == ACTION_SELF && (!How_Many() || (Height > 0) || IsTethered)) { + action = ACTION_NONE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_BUILDING && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing && House->IsPlayerControl && House->Is_Ally(target) && target->What_Am_I() == RTTI_VESSEL && *(VesselClass *)target == VESSEL_CARRIER && ((AircraftClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)target) == RADIO_ROGER) { + action = ACTION_ENTER; + } +#endif + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_NONE) { + action = What_Action(Coord_Cell(target->Center_Coord())); + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && target->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)target; + if (building->Class->Type == STRUCT_REPAIR && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_ENTER; + } + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::What_Action -- Determines what action to perform. * + * * + * This routine will determine what action would occur if the mouse were clicked over the * + * cell specified. The display system calls this routine to determine what mouse shape * + * to use. * + * * + * INPUT: cell -- The cell over which the mouse is currently positioned. * + * * + * OUTPUT: Returns with the action that will be performed if the mouse were clicked at the * + * specified cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType AircraftClass::What_Action(CELL cell) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (action == ACTION_MOVE && !Map[cell].Is_Visible(PlayerPtr)) { + action = ACTION_NOMOVE; + } + + if (action == ACTION_ATTACK && Class->PrimaryWeapon == NULL) { + action = ACTION_NONE; + } + + if (Class->IsFixedWing && action == ACTION_MOVE) { + action = ACTION_NOMOVE; + } + + return(action); +} + + +/*********************************************************************************************** + * AircraftClass::Pose_Dir -- Fetches the natural landing facing. * + * * + * Use this routine to get the desired facing the aircraft should assume when landing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the normal default facing the aircraft should have when landed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 03/04/1996 JLB : Fixed wing aircraft always face down the runway. * + *=============================================================================================*/ +DirType AircraftClass::Pose_Dir(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (*this == AIRCRAFT_TRANSPORT) { + return(DIR_N); + } + if (Class->IsFixedWing) { + return(DIR_E); + } + return(DIR_NE); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Attack -- Handles the attack mission for aircraft. * + * * + * This routine is the state machine that handles the attack mission for aircraft. It will * + * handling homing in on and firing on the target in the aircraft's targeting computer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to pass before this routine must be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Fixes brain dead helicopter for Nod scen #7. * + *=============================================================================================*/ +int AircraftClass::Mission_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Class->IsFixedWing) { + return(Mission_Hunt()); + } + + enum { + VALIDATE_AZ, + PICK_ATTACK_LOCATION, + TAKE_OFF, + FLY_TO_POSITION, + FIRE_AT_TARGET, + FIRE_AT_TARGET2, + RETURN_TO_BASE + }; + switch (Status) { + + /* + ** Double check target and validate the attack zone. + */ + case VALIDATE_AZ: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Status = PICK_ATTACK_LOCATION; + } + break; + + /* + ** Pick a good location to attack from. + */ + case PICK_ATTACK_LOCATION: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + Assign_Destination(Good_Fire_Location(TarCom)); + if (Target_Legal(NavCom)) { + Status = TAKE_OFF; + } else { + Status = RETURN_TO_BASE; + } + } + break; + + /* + ** Take off (if necessary). + */ + case TAKE_OFF: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + } else { + if (Process_Take_Off()) { + Status = FLY_TO_POSITION; + + /* + ** Break off radio contact with the helipad it is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Start flying toward the destination by skewing at first. + ** As the flight progresses, the body will rotate to face + ** the direction of travel. + */ + int diff = SecondaryFacing.Difference(Direction(NavCom)); + diff = Bound(diff, -128, 128); + PrimaryFacing = DirType((int)SecondaryFacing.Current()+diff); + } + return(1); + } + break; + + /* + ** Fly to attack location. + */ + case FLY_TO_POSITION: + if (Target_Legal(TarCom)) { + + /* + ** If the navcom was cleared mysteriously, then try to pick + ** a new attack location. This is a likely event if the player + ** clicks on a new target while in flight to an existing target. + */ + if (!Target_Legal(NavCom)) { + Status = PICK_ATTACK_LOCATION; + return(1); + } + + int distance = Process_Fly_To(true, NavCom); + + if (distance < 0x0200) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + + if (distance < 0x0010) { + Status = FIRE_AT_TARGET; + Assign_Destination(TARGET_NONE); + } + } else { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + return(1); + } + } else { + Status = RETURN_TO_BASE; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + Status = FIRE_AT_TARGET2; + break; + + case FIRE_REARM: + case FIRE_FACING: + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + Status = FIRE_AT_TARGET2; + } + break; + } + return(1); + + /* + ** Fire at the target. + */ + case FIRE_AT_TARGET2: + if (!Target_Legal(TarCom)) { + Status = RETURN_TO_BASE; + return(1); + } + + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(Direction(TarCom)); + switch (Can_Fire(TarCom, 0)) { + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_REARM: + break; + + case FIRE_OK: + Fire_At(TarCom, 0); + Map[::As_Cell(TarCom)].Incoming(Coord, true); + + if (Ammo) { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } else { + Status = RETURN_TO_BASE; + } + break; + + default: + if (!Ammo) { + Status = RETURN_TO_BASE; + } else { + if (!In_Range(TarCom)) { + Status = PICK_ATTACK_LOCATION; + } else { + Status = Rule.IsCurleyShuffle ? PICK_ATTACK_LOCATION : FIRE_AT_TARGET; + } + } + break; + } + break; + + /* + ** Fly back to landing spot. + */ + case RETURN_TO_BASE: + /* + ** Break off of firing at the target if there is no more + ** point in attacking it this mission. The player will + ** reassign a target for the next mission. + */ + if (!Ammo && (IsALoaner || House->IsHuman)) { + Assign_Target(TARGET_NONE); + } + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + break; + + default: + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * AircraftClass::New_LZ -- Find a good landing zone. * + * * + * Use this routine to locate a good landing zone that is nearby the location specified. * + * By using this routine it is possible to assign the same landing zone to several * + * aircraft and they will land nearby without conflict. * + * * + * INPUT: oldlz -- Target value of desired landing zone (usually a cell target value). * + * * + * OUTPUT: Returns with the new good landing zone. It might be the same value passed in. * + * * + * WARNINGS: The landing zone might be a goodly distance away from the ideal if there is * + * extensive blocking terrain in the vicinity. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::New_LZ(TARGET oldlz) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) { + COORDINATE coord = As_Coord(oldlz); + + /* + ** Scan outward in a series of concentric rings up to certain distance + ** in cells. + */ + for (int radius = 0; radius < Rule.LZScanRadius / CELL_LEPTON_W; radius++) { + FacingType modifier = Random_Pick(FACING_N, FACING_NW); + CELL lastcell = -1; + + /* + ** Perform a radius scan out from the original center location. Try to + ** find a cell that is allowed to be a legal LZ. + */ + for (FacingType facing = FACING_N; facing < FACING_COUNT; facing++) { + CELL newcell = Coord_Cell(Coord_Move(coord, Facing_Dir(facing+modifier), radius * ICON_LEPTON_W)); + if (Map.In_Radar(newcell)) { + TARGET newtarget = ::As_Target(newcell); + + if (newcell != lastcell && Is_LZ_Clear(newtarget) && Cell_Seems_Ok(newcell)) { + return(newtarget); + } + lastcell = newcell; + } + } + } + } + return(oldlz); +} + + +/*********************************************************************************************** + * AircraftClass::Receive_Message -- Handles receipt of radio messages. * + * * + * This routine receives all radio messages directed at this aircraft. It is used to handle * + * all inter-object coordination. Typically, this would be for transport helicopters and * + * other complex landing operations required of helicopters. * + * * + * INPUT: from -- The source of this radio message. * + * * + * message -- The message itself. * + * * + * param -- An optional parameter that may be used to transfer additional * + * data. * + * * + * OUTPUT: Returns with the radio response from the aircraft. * + * * + * WARNINGS: Some radio messages are handled by the base classes. * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType AircraftClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + switch (message) { + + case RADIO_PREPARED: + if (Target_Legal(TarCom)) return(RADIO_NEGATIVE); + if ((Height == 0 && Ammo == Class->MaxAmmo) || (Height > 0 && Ammo > 0)) return(RADIO_ROGER); + return(RADIO_NEGATIVE); + + /* + ** Something disastrous has happened to the object in contact with. Fall back + ** and regroup. This means that any landing process is immediately aborted. + */ + case RADIO_RUN_AWAY: + if (IsLanding) { + IsLanding = false; + IsTakingOff = true; + } + if (Class->IsFixedWing) { + Assign_Destination(Good_LZ()); + if (!Target_Legal(NavCom)) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_MOVE); + } + } else { + Scatter(0, true); + } + break; + + /* + ** The ground control requests that this specified landing spot be used. + */ + case RADIO_MOVE_HERE: + FootClass::Receive_Message(from, message, param); + if (Is_Target_Building(param)) { + if (Transmit_Message(RADIO_CAN_LOAD, As_Techno(param)) != RADIO_ROGER) { + return(RADIO_NEGATIVE); + } + Assign_Mission(MISSION_ENTER); + Assign_Destination((TARGET)param); + } else { + Assign_Mission(MISSION_MOVE); + Assign_Destination((TARGET)param); + } + Commence(); + return(RADIO_ROGER); + + /* + ** Ground control is requesting if the aircraft requires navigation direction. + */ + case RADIO_NEED_TO_MOVE: + FootClass::Receive_Message(from, message, param); + if (!Target_Legal(NavCom) && !IsTakingOff && !IsLanding) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + Close_Door(5, 4); + } + + /* + ** If a civilian has entered the transport, then the transport will immediately + ** fly off the map. + */ + if (_Counts_As_Civ_Evac(from)) { + Assign_Mission(MISSION_RETREAT); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + FootClass::Receive_Message(from, message, param); + + if (!IsTethered && !IsLanding && !IsTakingOff && Height == 0) { + + Open_Door(5, 4); + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + CELL cell; + /*DirType dir =*/ Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner()) || Height > 0) return(RADIO_STATIC); + if (/*!In_Radio_Contact() &&*/ How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + default: + break; + } + + /* + ** Let the base class take over processing this message. + */ + return(FootClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * AircraftClass::Desired_Load_Dir -- Determines where passengers should line up. * + * * + * This routine is used by the transport helicopter to determine the location where the * + * infantry passengers should line up before loading. * + * * + * INPUT: object -- The object that is trying to load up on this transport. * + * * + * -- Reference to the cell that the passengers should move to before the * + * actual load process may begin. * + * * + * OUTPUT: Returns with the direction that the helicopter should face for the load operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/30/1995 JLB : Revamped to scan all adjacent cells. * + *=============================================================================================*/ +DirType AircraftClass::Desired_Load_Dir(ObjectClass * object, CELL & moveto) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + CELL center = Coord_Cell(Center_Coord()); + for (int sweep = FACING_N; sweep < FACING_S; sweep++) { + moveto = Adjacent_Cell(center, FacingType(FACING_S+sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + + moveto = Adjacent_Cell(center, FacingType(FACING_S-sweep)); + if (Map.In_Radar(moveto) && (Coord_Cell(object->Center_Coord()) == moveto || Map[moveto].Is_Clear_To_Move(SPEED_FOOT, false, false))) return(DIR_N); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Take_Off -- State machine support for taking off. * + * * + * This routine is used by the main game state machine processor. This utility routine * + * handles a helicopter as it transitions from landed to flying state. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter reached flight level now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Process_Take_Off(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsLanding = false; + IsTakingOff = true; + + if (Class->IsFixedWing) { + Set_Speed(0xFF); + if (Height == FLIGHT_LEVEL) { + return(true); + } + + } else { + + switch (Height) { + case 0: + Close_Door(5, 4); + PrimaryFacing = SecondaryFacing; + break; + + case FLIGHT_LEVEL/2: + PrimaryFacing.Set_Desired(Direction(NavCom)); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/3): + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + Set_Speed(0x20); + break; + + case FLIGHT_LEVEL-(FLIGHT_LEVEL/5): + Set_Speed(0x40); + break; + + case FLIGHT_LEVEL: + Set_Speed(0xFF); + IsTakingOff = false; + return(true); + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Process_Landing -- Landing process state machine handler. * + * * + * This is a support routine that is called by the main state machine routines. This * + * routine is responsible for handling the helicopter as it transitions from flight to * + * landing. * + * * + * INPUT: none * + * * + * OUTPUT: Has the helicopter completely landed now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 03/04/1996 JLB : Handles fixed wing aircraft. * + *=============================================================================================*/ +bool AircraftClass::Process_Landing(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + IsTakingOff = false; + IsLanding = true; + + if (Class->IsFixedWing) { + int distance = Distance(NavCom); + + if (distance > 0x0100) { + SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + + switch (Height) { + case 0: + Set_Speed(0); + IsLanding = false; + return(true); + + default: +// if (distance*2 > Class->LandingSpeed) { +// Set_Speed(Class->LandingSpeed); +// } else { +// Set_Speed(distance/2); +// } + + Set_Speed(Class->LandingSpeed / House->AirspeedBias); + break; + } + + } else { + switch (Height) { + case 0: + IsLanding = false; + return(true); + + case FLIGHT_LEVEL/2: + Set_Speed(0); + break; + + case FLIGHT_LEVEL: + break; + + default: + break; + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Can_Enter_Cell -- Determines if the aircraft can land at this location. * + * * + * This routine is used when the passability of a cell needs to be determined. This is * + * necessary when scanning for a location that the aircraft can land. * + * * + * INPUT: cell -- The cell location to check for landing. * + * * + * OUTPUT: Returns a value indicating if the cell is a legal landing spot or not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +MoveType AircraftClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (!Map.In_Radar(cell)) return(MOVE_NO); + + CellClass * cellptr = &Map[cell]; + + ObjectClass const * occupier = cellptr->Cell_Occupier(); + + if (occupier == NULL || + !occupier->Is_Techno() || + ((TechnoClass *)occupier)->House->Is_Ally(House) || + (((TechnoClass *)occupier)->Cloak != CLOAKED && + (ScenarioInit == 0 && (occupier->What_Am_I() != RTTI_BUILDING || !((BuildingClass*)occupier)->Class->IsInvisible)) ) + ) { + + if (!cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false)) return(MOVE_NO); + } + + if (Session.Type == GAME_NORMAL && IsOwnedByPlayer && !cellptr->IsMapped) { + return(MOVE_NO); + } + + return(MOVE_OK); +} + + +/*********************************************************************************************** + * AircraftClass::Good_Fire_Location -- Searches for and finds a good spot to fire from. * + * * + * Given the specified target, this routine will locate a good spot for the aircraft to * + * fire at the target. * + * * + * INPUT: target -- The target that is desired to be attacked. * + * * + * OUTPUT: Returns with the target location of the place that firing should be made from. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 06/14/1995 JLB : Finer resolution on ring scan. * + * 11/02/1996 JLB : Bias fire position to get closer to moving objects. * + *=============================================================================================*/ +TARGET AircraftClass::Good_Fire_Location(TARGET target) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Target_Legal(target)) { + int range = Weapon_Range(0); + COORDINATE tcoord = As_Coord(target); + CELL bestcell = 0; + CELL best2cell = 0; + int bestval = -1; + int best2val = -1; + + /* + ** Try to get closer to a target that is moving. + */ + COORDINATE altcoord = 0; + if (Is_Target_Object(target) && As_Object(target)->Is_Foot()) { + TARGET alttarg = ((FootClass *)As_Object(target))->NavCom; + if (Target_Legal(alttarg)) { + altcoord = As_Coord(alttarg); + } + } + + for (int r = range-0x0100; r > 0x0100; r -= 0x0100) { + for (int face = 0; face < 255; face += 16) { + COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r); + CELL newcell = Coord_Cell(newcoord); + + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (Map.In_Radar(newcell) && (Session.Type != GAME_NORMAL || Map[newcell].Is_Visible(PlayerPtr)) && Cell_Seems_Ok(newcell, true)) { + int dist; + if (altcoord != 0) { + dist = ::Distance(newcoord, altcoord); + } else { + dist = Distance(newcoord); + } + if (bestval == -1 || dist < bestval) { + best2val = bestval; + best2cell = bestcell; + bestval = dist; + bestcell = newcell; + } + } + } + if (bestval != -1) break; + } + + if (best2val == -1) { + best2cell = bestcell; + } + + /* + ** If it found a good firing location, then return this location as + ** a target value. + */ + if (bestval != -1) { + if (Percent_Chance(50)) { + return(::As_Target(bestcell)); + } else { + return(::As_Target(best2cell)); + } + } + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * AircraftClass::Cell_Seems_Ok -- Checks to see if a cell is good to enter. * + * * + * This routine examines the navigation computers of other aircraft in order to see if the * + * specified cell is safe to fly to. The intent of this routine is to avoid unnecessary * + * mid-air collisions. * + * * + * INPUT: cell -- The cell to examine for clear airspace. * + * * + * strict -- Should the scan consider the aircraft, that is making this check, a * + * blocking aircraft. Typically, the aircraft itself is not considered * + * a blockage -- an aircraft can always exist where it is currently * + * located. A strict check is useful for helicopters that need to move * + * around at the slightest provocation. * + * * + * OUTPUT: Is the specified cell free from airspace conflicts? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Cell_Seems_Ok(CELL cell, bool strict) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that no other aircraft are heading to the selected location. If they + ** are, then don't consider the location as valid. + */ + TARGET astarget = ::As_Target(cell); + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * air = Aircraft.Ptr(index); + if (air && (strict || air != this) && !air->IsInLimbo) { + if (Coord_Cell(air->Coord) == cell || air->NavCom == astarget) { + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * AircraftClass::Pip_Count -- Returns the number of "objects" in aircraft. * + * * + * This routine is used by the render logic to draw the little container "pips". This * + * corresponds to the number of passengers for a transport helicopter or the number of * + * shots remaining for an attack helicopter. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of "pips" to render on the aircraft. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Pip_Count(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + int retval = 0; + + bool carrying_passengers = (Class->Max_Passengers() > 0) && ((*this != AIRCRAFT_BADGER) || (Mission != MISSION_HUNT)); + if (carrying_passengers) { + retval = How_Many(); + } else { + if (Ammo) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + } + return(retval); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Enter -- Control aircraft to fly to the helipad or repair center. * + * * + * This routine is used when the aircraft needs to fly for either rearming or repairing. * + * It tries to establish contact with the support building. Once contact is established * + * the ground controller takes care of commanding the aircraft. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + * 07/04/1995 JLB : Ground controller gives orders. * + *=============================================================================================*/ +int AircraftClass::Mission_Enter(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + TAKEOFF, + ALTITUDE, + STACK, + DOWNWIND, + CROSSWIND, + TRAVEL, + LANDING + }; + + /* + ** Verify that it has a valid NavCom. If it doesn't then request one from the + ** building this building is trying to land upon. If that fails, then enter + ** idle mode. + */ + if (!Target_Legal(NavCom) && In_Which_Layer() != LAYER_GROUND) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Enter_Idle_Mode(); + return(1); + } + } + + switch (Status) { + case INITIAL: + if (Height < FLIGHT_LEVEL || IsLanding) { + Status = TAKEOFF; + } else { + Status = ALTITUDE; + } + break; + + case TAKEOFF: + if (Process_Take_Off()) { + /* + ** After takeoff is complete, break radio contact with any helipad that this + ** helicopter is taking off from. + */ + if (In_Radio_Contact() && Map[Coord].Cell_Building() == Contact_With_Whom()) { + Transmit_Message(RADIO_OVER_OUT); + } + Status = ALTITUDE; + } + break; + + case ALTITUDE: + /* + ** Establish radio contact with the building this helicopter is trying + ** to land at. + */ + if (In_Radio_Contact()) { + if (!Target_Legal(NavCom)) { + Transmit_Message(RADIO_DOCKING); + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + return(1); + } + } + Status = STACK; + } else { + TechnoClass * tech = As_Techno(NavCom); + if (tech && Transmit_Message(RADIO_CAN_LOAD, tech) == RADIO_ROGER) { + Transmit_Message(RADIO_HELLO, tech); + Transmit_Message(RADIO_DOCKING); + Status = STACK; + } else { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (tech->What_Am_I() != RTTI_VESSEL) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#else + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); +#endif + } + } + break; + + case STACK: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_STACK)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = DOWNWIND; + } + } else { + Status = DOWNWIND; + } + break; + + case DOWNWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(200); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_DOWNWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = CROSSWIND; + } + } else { + Status = CROSSWIND; + } + break; + + case CROSSWIND: + if (Class->IsFixedWing) { + int distance; + TARGET togo; + + Set_Speed(140); + BuildingClass const * building = As_Building(NavCom); + if (building) { + togo = ::As_Target(building->Check_Point(CHECK_CROSSWIND)); + } else { + togo = NavCom; + } + + distance = Process_Fly_To(true, togo); + if (distance < 0x0080) { + Status = TRAVEL; + } + } else { + Status = TRAVEL; + } + break; + + case TRAVEL: + Transmit_Message(RADIO_DOCKING); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!In_Radio_Contact() && !Is_Target_Vessel(NavCom)) { +#else + if (!In_Radio_Contact()) { +#endif + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } else { + int distance = Process_Fly_To(true, NavCom); + + if (Class->IsFixedWing) { + + if (distance < 0x0400) { + Status = LANDING; + } + return(1); + + } else { + if (distance < 0x0080) { + if (Target_Legal(TarCom)) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + SecondaryFacing.Set_Desired(Pose_Dir()); + } + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Is_Target_Vessel(NavCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + break; + } +#endif + if (distance < 0x0010) { + Status = LANDING; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Is_Target_Vessel(NavCom) && As_Vessel(NavCom)->NavCom) { + Status = TRAVEL; + } +#endif + + } + break; + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); +// SecondaryFacing.Set_Desired(::Direction(Fire_Coord(0), As_Coord(NavCom))); + } + } + return(3); + } + break; + + case LANDING: + if (IsTakingOff && !Class->IsFixedWing) { + Assign_Destination(TARGET_NONE); + Enter_Idle_Mode(); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// If we were trying to land on a carrier and it moved, take off again + if ( As_Vessel(NavCom) && !In_Radio_Contact()) { + Status = INITIAL; + break; + } +#endif + if (Process_Landing()) { + switch (Transmit_Message(RADIO_IM_IN)) { + case RADIO_ROGER: + Assign_Mission(MISSION_GUARD); + break; + + case RADIO_ATTACH: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(Contact_With_Whom()->What_Am_I() != RTTI_VESSEL) Limbo(); +#else + Limbo(); +#endif + Contact_With_Whom()->Attach(this); + break; + + default: + Enter_Idle_Mode(); + } + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * AircraftClass::Good_LZ -- Locates a good spot to land. * + * * + * This routine is used when helicopters need a place to land, but there are no obvious * + * spots (i.e., helipad) available. It will try to land near a friendly helipad or friendly * + * building if there are no helipads anywhere. In the event that there are no friendly * + * buildings anywhere on the map, then just land right where it is flying. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target location where this aircraft should land. This value may * + * not be a clear cell, but the normal landing logic will resolve that problem. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET AircraftClass::Good_LZ(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Scan through all of the buildings and try to land near + ** the helipad (if there is one) or the nearest friendly building. + */ + CELL bestcell = 0; + int bestdist = -1; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building && !building->IsInLimbo && building->House == House) { + int dist = Distance(building); + if (*building == Class->Building) { + dist /= 4; + } + if (bestdist == -1 || dist < bestdist) { + bestdist = dist; + bestcell = Coord_Cell(building->Center_Coord()); + } + } + } + + /* + ** Return with the suitable location if one was found. + */ + if (bestdist != -1) { + return(::As_Target(bestcell)); + } + + /* + ** No good location was found. Just try to land here. + */ + return(::As_Target(Coord_Cell(Coord))); +} + + +/*********************************************************************************************** + * AircraftClass::Set_Speed -- Sets the speed for the aircraft. * + * * + * This routine will set the speed for the aircraft. The speed is specified as a fraction * + * of full speed. * + * * + * INPUT: speed -- The fixed point fractional speed setting. 0x00 is stopped, 0xFF is full * + * speed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Set_Speed(int speed) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + FootClass::Set_Speed(speed); + + MPHType sp = MPHType(min(Class->MaxSpeed * SpeedBias * House->AirspeedBias, MPH_LIGHT_SPEED)); + Fly_Speed(speed, sp); +} + + +/*********************************************************************************************** + * AircraftClass::Fire_Direction -- Determines the direction of fire. * + * * + * This routine will determine what direction a projectile would take if it were fired * + * from the aircraft. This is the direction that the aircraft's body is facing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction of projectile fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + *=============================================================================================*/ +DirType AircraftClass::Fire_Direction(void) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + return(SecondaryFacing.Current()); +} + + +/*********************************************************************************************** + * AircraftClass::~AircraftClass -- Destructor for aircraft object. * + * * + * This is the destructor for aircraft. It will limbo the aircraft if it isn't already * + * and also removes the aircraft from any team it may be attached to. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass::~AircraftClass(void) +{ + if (GameActive && Class) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + AircraftClass::Limbo(); + Class = 0; + } + ID = -1; +} + + +/*********************************************************************************************** + * AircraftClass::Scatter -- Causes the aircraft to move away a bit. * + * * + * This routine will cause the aircraft to move away from its current location and then * + * enter some idle mode. Typically this is called when the aircraft is attacked while on * + * the ground. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Scatter(COORDINATE , bool, bool ) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter) return; + + /* + ** Fixed wing aircraft never scatter. + */ + if (Class->IsFixedWing) return; + + if (IsLanding || Height == 0) { + IsLanding = false; + IsTakingOff = true; + } + Enter_Idle_Mode(); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard -- Handles aircraft in guard mode. * + * * + * Aircraft don't like to be in guard mode if in flight. If this situation is detected, * + * then figure out what the aircraft should be doing and go do it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This routine typically calls the normal guard logic for ground units. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 10/10/1995 JLB : Hunts for harvesters that are unescorted. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + + /* + ** If part of a team, then do nothing, since the team + ** handler will take care of giving this aircraft a + ** mission. + */ + if (Team) { + if (Target_Legal(NavCom)) { + Assign_Mission(MISSION_MOVE); + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (Class->PrimaryWeapon == NULL) { + Assign_Destination(::As_Target(Coord_Cell(Coord))); + Assign_Mission(MISSION_MOVE); + } else { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + } + return(1); + } + if (House->IsHuman) return(MissionControl[Mission].Normal_Delay()); + + /* + ** If the aircraft is very badly damaged, then it will search for a + ** repair bay first. + */ + if (House->Available_Money() >= 100 && Health_Ratio() <= Rule.ConditionYellow) { + if (!In_Radio_Contact() || + (Height == 0 && + (Contact_With_Whom()->What_Am_I() != RTTI_BUILDING || *((BuildingClass *)Contact_With_Whom()) != STRUCT_REPAIR))) { + + + BuildingClass * building = Find_Docking_Bay(STRUCT_REPAIR, true); + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft cannot attack anything because of lack of ammo, + ** abort any normal guard logic in order to look for a helipad + ** to rearm. + */ + if (Ammo == 0 && Is_Weapon_Equipped()) { + if (!In_Radio_Contact()) { + BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (!Class->IsFixedWing) { + int dist = 0x7FFFFFFF; + if (building) dist=Distance(building); + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *ship = Vessels.Ptr(index); + if (ship != NULL && *ship == VESSEL_CARRIER && !ship->IsInLimbo && ship->IsActive && ship->House == House && ship->How_Many() < ship->Class->Max_Passengers()) { + if (Distance(ship) < dist || !building) { + building = (BuildingClass *)ship; + dist = Distance(ship); + } +// break; + } + } + } +#endif + if (building != NULL) { + Assign_Destination(building->As_Target()); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_ENTER); + return(1); + } + } + } + + /* + ** If the aircraft already has a target, then attack it if possible. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + + /* + ** Transport helicopters don't really do anything but just sit there. + */ + if (!Is_Weapon_Equipped()) { + return(TICKS_PER_SECOND*3); + } + + /* + ** Computer controlled helicopters will defend themselves by bouncing around + ** and looking for a free helipad. + */ + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + /* + ** Perform a special check to hunt for harvesters that are outside of the protective + ** shield of their base. + */ + if (House->State != STATE_ATTACKED) { + TARGET target = House->Find_Juicy_Target(Coord); + + if (Target_Legal(target)) { + Assign_Target(target); + Assign_Mission(MISSION_ATTACK); + } + } + + return(FootClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * AircraftClass::Mission_Guard_Area -- Handles the aircraft guard area logic. * + * * + * This routine handles area guard logic for aircraft. Aircraft require special handling * + * for this mode since they are to guard area only if they are in a position to do so. * + * Otherwise they just defend themselves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +int AircraftClass::Mission_Guard_Area(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Height == FLIGHT_LEVEL) { + if (!Team.Is_Valid()) Enter_Idle_Mode(); + return(1); + } + if (House->IsHuman) return(TICKS_PER_SECOND); + + if (Height == 0 && !In_Radio_Contact()) { + Scatter(0, true); + return(TICKS_PER_SECOND*3); + } + + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + return(1); + } + return(FootClass::Mission_Guard_Area()); +} + + +/*********************************************************************************************** + * AircraftClass::Response_Attack -- Gives audio response to attack order. * + * * + * This routine is used to give an audio response to an attack order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Attack(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Move -- Gives audio response to move request. * + * * + * This routine is used to give an audio response to movement orders. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Move(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Response_Select -- Gives audio response when selected. * + * * + * This routine is called when an audio response for selection is desired. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Response_Select(void) +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * AircraftClass::Can_Fire -- Checks to see if the aircraft can fire. * + * * + * This routine is used to determine if the aircraft can fire its weapon at the target * + * specified. If it cannot, then the reason why is returned. * + * * + * INPUT: target -- The target that the aircraft might fire upon. * + * * + * which -- The weapon that will be used to fire. * + * * + * OUTPUT: Returns with the reason why it can't fire or with FIRE_OK. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + * 07/11/1996 JLB : Fixed for camera carrying aircraft. * + *=============================================================================================*/ +FireErrorType AircraftClass::Can_Fire(TARGET target, int which) const +{ + assert(Aircraft.ID(this) == ID); + assert(IsActive); + + if (Passenger && !Is_Something_Attached()) { + return(FIRE_AMMO); + } + + bool camera = (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsCamera); + bool fudge = (Passenger || (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet != NULL && Class->PrimaryWeapon->Bullet->IsParachuted)); + + if (fudge && !camera && !Ammo && !Passenger) { + return(FIRE_AMMO); + } + + /* + ** Passenger aircraft that wish to 'fire' actually are requesting to + ** paradrop or 'throw out' the cargo. This is always allowed if the terrain under the + ** aircraft is generally clear. + */ + if (camera || (fudge && Passenger && Is_Something_Attached())) { + if (Arm != 0) return(FIRE_REARM); + + if (Distance(target) < (camera ? 0x0380 : 0x0200) && Map.In_Radar(Coord_Cell(Center_Coord()))) { +// if (Distance(target) < (camera ? 0x0380 : 0x0280) && Map.In_Radar(Coord_Cell(Center_Coord()))) { + return(FIRE_OK); + } + return(FIRE_RANGE); + } + + FireErrorType canfire = FootClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsFixedWing) { + + int diff = PrimaryFacing.Difference(Direction(TarCom)); + if (ABS(diff) > (fudge ? 16 : 8)) { + return(FIRE_FACING); + } + } + } + return(canfire); +} + + +/*********************************************************************************************** + * AircraftClass::Landing_Takeoff_AI -- Handle aircraft take off and landing processing. * + * * + * This routine handles the tricky maneuver of taking off and landing. The process of * + * landing is not entirely safe and thus the aircraft may be destroyed as a consequence. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft destroyed by this process? * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. Be sure to * + * examine the return value and if true, abort all further processing of this * + * aircraft since it is now dead. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Landing_Takeoff_AI(void) +{ + /* + ** Handle landing and taking off logic. Helicopters are prime users of this technique. The + ** aircraft will either gain or lose altitude as appropriate. As the aircraft transitions + ** between flying level and ground level, it will be moved into the appropriate render + ** layer. + */ + if (Is_Door_Closed() && (IsLanding || IsTakingOff)) { + LayerType layer = In_Which_Layer(); + + if (IsLanding) { + Mark(MARK_UP); + if (Height) Height -= Pixel_To_Lepton(1); + if (Height <= 0) { + Height = 0; + IsLanding = false; + Set_Speed(0); + + /* + ** If the NavCom now equals the destination, then clear out the NavCom. + */ + if (Coord_Cell(Center_Coord()) == As_Cell(NavCom)) { + Assign_Destination(TARGET_NONE); + } + + /* + ** If a fixed-wing aircraft just landed on the ground, blow him up + */ + if (Class->IsFixedWing && Mission != MISSION_ENTER) { + Strength = 1; + + int damage = Strength; + Map.Remove(this, layer); + Take_Damage(damage, 0, WARHEAD_AP, 0, true); + return(true); + } + + if (Target_Legal(NavCom) && As_Techno(NavCom) == Contact_With_Whom()) { + if (In_Radio_Contact() && Transmit_Message(RADIO_IM_IN) != RADIO_ROGER) { + Scatter(0, true); + } + } + } + Mark(MARK_DOWN); + } + if (IsTakingOff) { + Mark(MARK_UP); +// Map.Remove(this, layer); + Height += Pixel_To_Lepton(1); + if (Height >= FLIGHT_LEVEL) { + Height = FLIGHT_LEVEL; + IsTakingOff = false; + } +// Map.Submit(this, In_Which_Layer()); + Mark(MARK_DOWN); + } + + /* + ** Make adjustments for altitude by moving from one layer to another as + ** necessary. + */ + if (layer != In_Which_Layer()) { + + /* + ** When the aircraft is about to enter the ground layer, perform on last + ** check to see if it is legal to enter that location. If not, then + ** start the take off process. Let the normal logic handle this + ** change of plans. + */ + bool ok = true; + if (In_Which_Layer() == LAYER_GROUND && !IsTakingOff && !Class->IsFixedWing) { + if (!Is_LZ_Clear(::As_Target(Coord_Cell(Coord)))) { + IsTakingOff = true; + Mark(MARK_UP); + Height += Pixel_To_Lepton(1); + Mark(MARK_DOWN); + ok = false; + } + } + + if (ok) { + + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + /* + ** When the aircraft is close to the ground, it should exist as a ground object. + ** This aspect is controlled by the Place_Down and Pick_Up functions. + */ + if (In_Which_Layer() == LAYER_GROUND) { + Assign_Destination(TARGET_NONE); // Clear the navcom. + Transmit_Message(RADIO_TETHER); + Look(); +// Map.Sight_From(Coord_Cell(Coord), 1, House, false); + } else { + Transmit_Message(RADIO_UNTETHER); + + /* + ** If the navigation computer is not attached to the object this + ** aircraft is in radio contact with, then assume that radio + ** contact is now superfluous. Break radio contact. + */ + if (In_Radio_Contact() && Target_Legal(NavCom) && NavCom != Contact_With_Whom()->As_Target()) { + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Edge_Of_World_AI -- Detect if aircraft has exited the map. * + * * + * Certain aircraft will be eliminated when they leave the edge of the world presumably * + * after completing their mission. An exception is for aircraft that have been newly * + * created as reinforcements and have not yet completed their mission. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the aircraft deleted by this routine? * + * * + * WARNINGS: Be sure to call this routine only once per aircraft per game logic loop. If * + * the return value is true, then abort any further processing of this aircraft * + * since it has been eliminated. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool AircraftClass::Edge_Of_World_AI(void) +{ + if (!Map.In_Radar(Coord_Cell(Coord))) { + if (Mission == MISSION_RETREAT /*|| (*this == AIRCRAFT_CARGO && !Is_Something_Attached())*/) { + + /* + ** Check to see if there are any civilians aboard. If so, then flag the house + ** that the civilian evacuation trigger event has been fulfilled. + */ + while (Is_Something_Attached()) { + FootClass * obj = Detach_Object(); + + /* + ** Flag the owning house that civ evacuation has occurred. + */ + if (_Counts_As_Civ_Evac(obj)) { + obj->House->IsCivEvacuated = true; + } + + if (obj->Team.Is_Valid()) obj->Team->IsLeaveMap = true; + +#ifdef OLD + /* + ** Transport planes that leave can only be because they carry purchased + ** equipment and must be have their cost refunded. + */ + if (*this == AIRCRAFT_CARGO) { + House->Refund_Money(obj->Class_Of().Cost_Of()); + } +#endif + delete obj; + } + if (Team.Is_Valid()) { + Team->IsLeaveMap = true; + } + Stun(); + delete this; + return(true); + } + } else { + IsLocked = true; + } + return(false); +} + + +/*********************************************************************************************** + * AircraftClass::Movement_AI -- Handles aircraft physical movement logic. * + * * + * This routine manages the aircraft movement across the map. If any movement occurred, the * + * aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Movement_AI(void) +{ + /* + ** If for some strange reason, there is a valid NavCom, but this aircraft is not + ** in a movement order, then give it a movement order. + */ + if (Target_Legal(NavCom) && Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + + if (Speed != 0) { + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_UP); + Physics(Coord, PrimaryFacing); + Mark(MARK_DOWN); + } else { + Mark(MARK_CHANGE_REDRAW); + if (Physics(Coord, PrimaryFacing) != RESULT_NONE) { + Mark(MARK_CHANGE_REDRAW); + } + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Rotation_AI -- Handle aircraft body and flight rotation. * + * * + * This will process the aircraft visible body and flight model rotation operations. If * + * any rotation occurred, the aircraft will be flagged to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per aircraft per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Rotation_AI(void) +{ + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } + if (Class->IsFixedWing) { + SecondaryFacing = PrimaryFacing; + } + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE_REDRAW); + } + } +} + + +/*********************************************************************************************** + * AircraftClass::Per_Cell_Process -- Handle the aircraft per cell process. * + * * + * This is a seldom used function since its only purpose is to be called when an aircraft * + * lands on the ground. * + * * + * INPUT: why -- Why was this per cell process function called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/15/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Per_Cell_Process(PCPType why) +{ + BStart(BENCH_PCP); + FootClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * AircraftClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. Aircraft have their own version of this routine because a fixed-wing plane * + * trying to land will behave poorly if given a new destinatio while it's landing. * * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Assign_Destination(TARGET dest) +{ + assert(IsActive); + if (dest == NavCom) return; + + if (Target_Legal(dest) && Class->IsFixedWing && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { +// if (Target_Legal(dest) /*&& Class->IsFixedWing*/ && (IsLanding || (Target_Legal(NavCom) && dest != NavCom))) { + +// if (Class->IsFixedWing || As_Cell(dest) != Coord_Cell(Center_Coord())) { + Process_Take_Off(); + Status = 0; +// } + } + FootClass::Assign_Destination(dest); +} + + +/*********************************************************************************************** + * AircraftClass::In_Which_Layer -- Calculates the display layer of the aircraft. * + * * + * This examines the aircraft to determine what display layer it should be located * + * in. Fixed wing aircraft must always be in the top layer if they are flying even though * + * they may be low to the ground. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this aircraft resides in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +LayerType AircraftClass::In_Which_Layer(void) const +{ + if (Class->IsFixedWing && Height > 0) { + return(LAYER_TOP); + } + return(FootClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * AircraftClass::Look -- Aircraft will look if they are on the ground always. * + * * + * Aircraft perform a look operation according to their sight range. If the aircraft is * + * on the ground, then it will look a distance of one cell regardless of what its * + * specified sight range is. * + * * + * INPUT: incremental -- Is this an incremental look? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/23/1996 JLB : Created. * + *=============================================================================================*/ +void AircraftClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + if (Height == 0) { + sight_range = 1; + } + + if (sight_range) { + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + } +} + +unsigned AircraftClass::Spied_By() const +{ + unsigned spiedby = FootClass::Spied_By(); + + /* + ** Aircraft inherit the spied by flags of the helipad they've landed on + */ + BuildingClass* building = Map[Coord].Cell_Building(); + if ((Height == 0) && In_Radio_Contact() && (building == Contact_With_Whom())) { + spiedby |= building->Spied_By(); + } + + return spiedby; +} \ No newline at end of file diff --git a/code/redalert/aircraft.h b/code/redalert/aircraft.h new file mode 100644 index 0000000..719419c --- /dev/null +++ b/code/redalert/aircraft.h @@ -0,0 +1,252 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AIRCRAFT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AIRCRAFT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 22, 1994 * + * * + * Last Update : November 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AIRCRAFT_H +#define AIRCRAFT_H + +#include "radio.h" +#include "fly.h" +#include "target.h" + + +/* +** This aircraft class is used for all flying sentient objects. This includes fixed wing +** aircraft as well as helicopters. It excludes bullets even though some bullets might +** be considered to be "flying" in a loose interpretatin of the word. +*/ +class AircraftClass : public FootClass, public FlyClass +{ + public: + /* + ** This is a pointer to the class control structure for the aircraft. + */ + CCPtr Class; + + //----------------------------------------------------------------------------- + static void * operator new(size_t); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *); + operator AircraftType(void) const {return Class->Type;}; + AircraftClass(AircraftType classid, HousesType house); + AircraftClass(NoInitClass const & x) : FootClass(x), FlyClass(x), Class(x), SecondaryFacing(x), SightTimer(x) {}; + virtual ~AircraftClass(void); + + static void Init(void); + + virtual int Mission_Attack(void); + virtual int Mission_Unload(void); + virtual int Mission_Hunt(void); + virtual int Mission_Retreat(void); + virtual int Mission_Move(void); + virtual int Mission_Enter(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + + virtual void Assign_Destination(TARGET target); + /* + ** State machine support routines. + */ + bool Process_Take_Off(void); + bool Process_Landing(void); + int Process_Fly_To(bool slowdown, TARGET dest); + + /* + ** Query functions. + */ + virtual LayerType In_Which_Layer(void) const; + virtual DirType Turret_Facing(void) const {return(SecondaryFacing.Current());} + int Shape_Number(void) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual int Pip_Count(void) const; + TARGET Good_Fire_Location(TARGET target) const; + bool Cell_Seems_Ok(CELL cell, bool landing=false) const; + DirType Pose_Dir(void) const; + TARGET Good_LZ(void) const; + virtual DirType Fire_Direction(void) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + /* + ** Landing zone support functionality. + */ + virtual void Per_Cell_Process(PCPType why); + bool Is_LZ_Clear(TARGET target) const; + TARGET New_LZ(TARGET oldlz) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Look(bool incremental=false); + void Draw_Rotors(int x, int y, WindowNumberType window) const; + virtual int Exit_Object(TechnoClass *); + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Set_Speed(int speed); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Player_Assign_Mission(MissionType mission, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which); + + /* + ** AI. + */ + bool Landing_Takeoff_AI(void); + bool Edge_Of_World_AI(void); + void Movement_AI(void); + void Rotation_AI(void); + int Paradrop_Cargo(void); + virtual void AI(void); + virtual void Enter_Idle_Mode(bool initial = false); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static char * INI_Name(void) {return "AIRCRAFT";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual unsigned Spied_By() const; + + public: + + /* + ** This is the facing used for the body of the aircraft. Typically, this is the same + ** as the PrimaryFacing, but in the case of helicopters, it can be different. + */ + FacingClass SecondaryFacing; + + /* + ** If this is a passenger carrying aircraft then this flag will be set. This is + ** necessary because once the passengers are unloaded, the fact that it was a + ** passenger carrier must still be known. + */ + bool Passenger; + + private: + + /* + ** Aircraft can be in either state of landing, taking off, or in steady altitude. + ** These flags are used to control transition between flying and landing. It is + ** necessary to handle the transition in this manner so that it occurs smoothly + ** during the graphic processing section. + */ + unsigned IsLanding:1; + unsigned IsTakingOff:1; + + /* + ** It is very common for aircraft to be homing in on a target. When this flag is + ** true, the aircraft will constantly adjust its facing toward the TarCom. When the + ** target is very close (one cell away or less), then this flag is automatically cleared. + ** This is because the homing algorithm is designed to get the aircraft to the destination + ** but no more. Checking when this flag is cleared is a way of flagging transition into + ** a new mode. Example: Transport helicopters go into a hovering into correct position + ** mode when the target is reached. + */ + unsigned IsHoming:1; + + /* + ** Helicopters that are about to land must hover into a position exactly above the landing + ** zone. When this flag is true, the aircraft will be adjusted so that it is exactly over + ** the TarCom. The facing of the aircraft is not altered by this movement. The affect + ** like the helicopter is hovering and shifting sideways to position over the landing + ** zone. When the position is over the landing zone, then this flag is set to false. + */ + unsigned IsHovering:1; + + /* + ** This is the jitter tracker to be used when the aircraft is a helicopter and + ** is flying. It is most noticeable when the helicopter is hovering. + */ + unsigned char Jitter; + + private: + + /* + ** This timer controls when the aircraft will reveal the terrain around itself. + ** When this timer expires and this aircraft has a sight range, then the + ** look around process will occur. + */ + CDTimerClass SightTimer; + + /* + ** Most attack aircraft can make several attack runs. This value contains the + ** number of attack runs the aircraft has left. When this value reaches + ** zero then the aircraft is technically out of ammo. + */ + char AttacksRemaining; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/anim.cpp b/code/redalert/anim.cpp new file mode 100644 index 0000000..65d3a16 --- /dev/null +++ b/code/redalert/anim.cpp @@ -0,0 +1,1221 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ANIM.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : ANIM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * AnimClass::AI -- This is the low level anim processor. * + * AnimClass::AnimClass -- The constructor for animation objects. * + * AnimClass::Attach_To -- Attaches animation to object specified. * + * AnimClass::Sort_Above -- Sorts the animation above the target specified. * + * AnimClass::Center_Coord -- Determine center of animation. * + * AnimClass::Detach -- Remove animation if attached to target. * + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * AnimClass::Init -- Performs pre-scenario initialization. * + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * AnimClass::Middle -- Processes any middle events. * + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * AnimClass::Render -- Draws an animation object. * + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * AnimClass::Start -- Processes initial animation side effects. * + * AnimClass::delete -- Returns an anim object back to the free pool. * + * AnimClass::new -- Allocates an anim object from the pool. * + * AnimClass::~AnimClass -- Destructor for anim objects. * + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#define VIC 1 + +/*********************************************************************************************** + * Anim_From_Name -- Given a name, this finds the corresponding anim type. * + * * + * This routine will convert the supplied ASCII name into the animation type that it * + * represents. * + * * + * INPUT: name -- Pointer to the ASCII name to convert. * + * * + * OUTPUT: Returns with the animation type that matches the name specified. If no match could * + * be found, then ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Anim_From_Name(char const * name) +{ + #ifdef VIC + if (name == NULL) return(ANIM_NONE); + + for (int anim = ANIM_FIRST; anim < ANIM_COUNT; anim++) { + if (stricmp(AnimTypeClass::As_Reference((AnimType)anim).IniName, name) == 0) { + return((AnimType)anim); + } + } +#endif + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Shorten_Attached_Anims -- Reduces attached animation durations. * + * * + * This routine is used to reduce the amount of time any attached animations will process. * + * Typical use of this is when an object is on fire and the object should now be destroyed * + * but the attached animations are to run until completion before destruction can follow. * + * This routine will make the animation appear to run its course, but in as short of time * + * as possible. The shortening effect is achieved by reducing the number of times the * + * animation will loop. * + * * + * INPUT: obj -- Pointer to the object that all attached animations will be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +void Shorten_Attached_Anims(ObjectClass * obj) +{ + if (obj != NULL) { + for (int index = 0; index < Anims.Count(); index++) { + AnimClass & anim = * Anims.Ptr(index); + + if (As_Object(anim.xObject) == obj) { + anim.Loops = 0; + } + } + } +} + + +/*********************************************************************************************** + * AnimClass::Sort_Y -- Returns with the sorting coordinate for the animation. * + * * + * This routine is used by the sorting system. Animations that are located in the ground * + * layer will be sorted by this the value returned from this function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the sort coordinate to use for this animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/15/1994 JLB : Handles flat anims (infantry decay anims). * + *=============================================================================================*/ +COORDINATE AnimClass::Sort_Y(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(As_Object(xObject)->Sort_Y(), 0x00010000L)); + } + if (Target_Legal(SortTarget)) { + ObjectClass * obj = As_Object(SortTarget); + if (obj && obj->IsActive) { + return(Coord_Add(obj->Sort_Y(), 0x00010000L)); + } + } + if (*this == ANIM_MOVE_FLASH) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, -24))); + } + if (Class->IsGroundLayer || *this == ANIM_LZ_SMOKE) { + return(Coord_Add(Center_Coord(), XYP_COORD(0, 14))); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Center_Coord -- Determine center of animation. * + * * + * This support function will return the "center" of the animation. The actual coordinate * + * of the animation may be dependant on if the the animation is attached to an object. * + * In such a case, it must factor in the object's location. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate of the center of the animation. The coordinate is in real * + * game coordinates -- taking into consideration if the animation is attached. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/02/1996 JLB : Coordinate based on visual center of object. * + *=============================================================================================*/ +COORDINATE AnimClass::Center_Coord(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (xObject != TARGET_NONE) { + return(Coord_Add(Coord, As_Object(xObject)->Target_Coord())); + } +#endif + return(Coord); +} + + +/*********************************************************************************************** + * AnimClass::Render -- Draws an animation object. * + * * + * This is the working routine that renders the animation shape. It gets called once * + * per animation per frame. It needs to be fast. * + * * + * INPUT: bool; Should the animation be rendered in spite of render flag? * + * * + * OUTPUT: bool; Was the animation rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Render(bool forced) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (Delay) return(false); + if (Map[Center_Coord()].IsVisible) { + const_cast(this)->IsToDisplay = true; // const_cast. ST - 5/8/2019 + } +#endif + return(ObjectClass::Render(forced)); +} + + +/*********************************************************************************************** + * AnimClass::Draw_It -- Draws the animation at the location specified. * + * * + * This routine is used to render the animation object at the location specified. This is * + * how the map imagery gets updated. * + * * + * INPUT: x,y -- The pixel coordinates to draw the animation at. * + * * + * window -- The to base the draw coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 05/19/1995 JLB : Added white translucent effect. * + *=============================================================================================*/ +void AnimClass::Draw_It(int x, int y, WindowNumberType window) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + bool render_legacy = !IsInvisible && (Class->VirtualAnim == ANIM_NONE || window != WINDOW_VIRTUAL); + bool render_virtual = Target_Legal(VirtualAnimTarget) && window == WINDOW_VIRTUAL; + + if (render_legacy) { + BStart(BENCH_ANIMS); + + IsTheaterShape = Class->IsTheater; + + void const * shapefile = Get_Image_Data(); + if (shapefile != NULL) { + void const * transtable = NULL; + int shapenum = Class->Start + Fetch_Stage(); + void const * remap = NULL; + + /* + ** If the translucent table hasn't been determined yet, then check to see if it + ** should use the white or normal translucent tables. + */ + if (transtable == NULL && Class->IsWhiteTrans) transtable = DisplayClass::WhiteTranslucentTable; + if (transtable == NULL && Class->IsTranslucent) transtable = DisplayClass::TranslucentTable; + if (Class->Type == ANIM_ATOM_BLAST) transtable = Map.UnitShadow; + + /* + ** Set the shape flags to properly take into account any fading or ghosting + ** table necessary. + */ + ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL; + if (transtable != NULL) flags = flags | SHAPE_GHOST; + + /* + ** Draw the animation shape, but ignore legacy if beyond normal stage count. + */ + if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, DIR_N, Class->VirtualScale); + } + } + IsTheaterShape = false; + BEnd(BENCH_ANIMS); + } + if (render_virtual) { + AnimClass* virtual_anim = As_Animation(VirtualAnimTarget); + virtual_anim->Make_Visible(); + virtual_anim->Draw_It(x, y, window); + virtual_anim->Make_Invisible(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Mark -- Signals to map that redrawing is necessary. * + * * + * This routine is used by the animation logic system to inform the map that the cells * + * under the animation must be rerendered. * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +bool AnimClass::Mark(MarkType mark) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +// ObjectClass::Mark(mark); + return(true); + } +#endif + return(false); +} + + +/*********************************************************************************************** + * AnimClass::Overlap_List -- Determines the overlap list for the animation. * + * * + * Use this routine to fetch the overlap list for the animation. This overlap list is the * + * cells that this animation spills over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the overlap list for this particular instance of the * + * animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Overlap_List(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + static short const OverlapAtom[] = { + (-MAP_CELL_W * 2) - 1, (-MAP_CELL_W * 2), (-MAP_CELL_W * 2) + 1, + (-MAP_CELL_W * 1) - 1, (-MAP_CELL_W * 1), (-MAP_CELL_W * 1) + 1, + (-MAP_CELL_W * 0) - 1, (-MAP_CELL_W * 0), (-MAP_CELL_W * 0) + 1, + ( MAP_CELL_W * 1) - 1, ( MAP_CELL_W * 1), ( MAP_CELL_W * 1) + 1, + ( MAP_CELL_W * 2) - 1, ( MAP_CELL_W * 2), ( MAP_CELL_W * 2) + 1, + REFRESH_EOL + }; + + if (IsToDelete) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + if (Class->Type == ANIM_ATOM_BLAST) { + return(OverlapAtom); + } + +#ifdef PARTIAL +IsTheaterShape = Class->IsTheater; + if (Class->Get_Image_Data() != NULL) { + int shapenum = Class->Start + Fetch_Stage(); + int count = Get_Build_Frame_Count(Class->Get_Image_Data()); + shapenum = min(shapenum, count-1); + + if (Class->DimensionData == NULL) { + Class->DimensionData = new Rect [count]; + } + if (Class->DimensionData != NULL && !Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Class->Get_Image_Data(), shapenum); +IsTheaterShape = false; + return(Coord_Spillage_List(Center_Coord(), Class->DimensionData[shapenum])); + } + } +IsTheaterShape = false; +#endif +#endif + return(Coord_Spillage_List(Center_Coord(), Class->Size)); +} + + +/*********************************************************************************************** + * AnimClass::Occupy_List -- Determines the occupy list for the animation. * + * * + * Animations always occupy only the cell that their center is located over. As such, this * + * routine always returns a simple (center cell) occupation list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the occupation list for the animation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +short const * AnimClass::Occupy_List(bool) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + static short _simple[] = {REFRESH_EOL}; + +#endif + return(_simple); +} + + +/*********************************************************************************************** + * AnimClass::Init -- Performs pre-scenario initialization. * + * * + * This routine is used to initialize the animation system prior to a scenario being loaded * + * or reloaded. It effectively removes all animations from the system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Init(void) +{ + Anims.Free_All(); +} + + +/*********************************************************************************************** + * AnimClass::new -- Allocates an anim object from the pool. * + * * + * This routine is used to allocate a free anim class object from the preallocated pool * + * in the near heap. If there are no free animation objects, then null is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a free anim object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void * AnimClass::operator new(size_t) +{ + void * ptr = Anims.Allocate(); + if (ptr != NULL) { + ((AnimClass *)ptr)->Set_Active(); + } else { + GlyphX_Debug_Print("AnimClass::operator new FAILED"); + } + return(ptr); +} + + +/*********************************************************************************************** + * AnimClass::delete -- Returns an anim object back to the free pool. * + * * + * This routine is used to return an anim object back to the pool of free anim objects. * + * Anim objects so returned are available to be reallocated for the next animation. * + * * + * INPUT: ptr -- Pointer to the anim object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((AnimClass *)ptr)->IsActive = false; + } + Anims.Free((AnimClass *)ptr); +} + + +/*********************************************************************************************** + * AnimClass::AnimClass -- The constructor for animation objects. * + * * + * This routine is used as the constructor of animation objects. It initializes and adds * + * the animation object to the display and logic systems. * + * * + * INPUT: animnum -- The animation number to start. * + * * + * coord -- The location of the animation. * + * * + * timedelay-- The delay before the animation starts. * + * * + * loop -- The number of times to loop this animation. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 08/03/1994 JLB : Added a delayed affect parameter. * + *=============================================================================================*/ +AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, unsigned char loop) : + ObjectClass(RTTI_ANIM, Anims.ID(this)), + Class(AnimTypes.Ptr((int)animnum)), + xObject(TARGET_NONE), + SortTarget(TARGET_NONE), + OwnerHouse(HOUSE_NONE), + Loops(1), + IsToDelete(false), + IsBrandNew(true), + IsInvisible(false), + Delay(timedelay), + Accum(0), + AttachLayer(LAYER_NONE) +{ +#ifdef VIC + if (Class->Stages == -1) { +IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); +IsTheaterShape = false; + + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(0); + + if (Class->IsGroundLayer) { + Height = FLIGHT_LEVEL; + } + + AnimClass::Unlimbo(coord); + + VisibleFlags = 0xffff; + + /* + ** Drop zone smoke always reveals the map around itself. + */ + if (*this == ANIM_LZ_SMOKE) { + // Added PlayerPtr here as Sight_From now needs to know who to perform the action for. This should be OK as long as it's not used in MP. ST - 8/2/2019 2:34PM + Map.Sight_From(Coord_Cell(coord), Rule.DropZoneRadius / CELL_LEPTON_W, PlayerPtr, false); + } + + Loops = (unsigned char)(max(loop, 1) * Class->Loops); + Loops = (unsigned char)max(Loops, 1); + + /* + ** If the animation starts immediately, then play the associated sound effect now. + */ + if (!Delay) { + Start(); + } + + /* + ** Check for a virtual animation + */ + if (Class->VirtualAnim != ANIM_NONE) { + AnimClass* virtual_anim = new AnimClass(Class->VirtualAnim, 0, timedelay, loop); + if (virtual_anim != NULL) { + virtual_anim->Make_Invisible(); + VirtualAnimTarget = virtual_anim->As_Target(); + } else { + VirtualAnimTarget = TARGET_NONE; + } + } else { + VirtualAnimTarget = TARGET_NONE; + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::~AnimClass -- Destructor for anim objects. * + * * + * This destructor handles removing the animation object from the system. It might require * + * informing any object this animation is attached to that it is no longer attached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + *=============================================================================================*/ +AnimClass::~AnimClass(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + if (GameActive) { + + /* + ** If this anim is attached to another object + ** then check to see if this is the last anim attached to it. If this + ** is the case, then inform the object that it is no longer attached to + ** an animation. + */ + if (Target_Legal(xObject) && As_Object(xObject) != NULL) { + ObjectClass * to = As_Object(xObject); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Scan for any other animations that are attached to the object that + ** this animation is attached to. If there are no others, then inform the + ** attached object of this fact. + */ + int index; + for (index = 0; index < Anims.Count(); index++) { + if (Anims.Ptr(index) != this && Anims.Ptr(index)->xObject == xObject) break; + } + + /* + ** Tell the object that it is no longer being damaged. + */ + if (index == Anims.Count()) { + to->Fire_Out(); + if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_UP); + to->IsAnimAttached = false; + if (to->In_Which_Layer() == LAYER_GROUND) to->Mark(MARK_OVERLAP_DOWN); + } + Coord = Coord_Add(to->Center_Coord(), Coord); + xObject = TARGET_NONE; + } + + Limbo(); + } + + xObject = TARGET_NONE; + Class = 0; + ID = -1; + +#endif +} + + +/*********************************************************************************************** + * AnimClass::AI -- This is the low level anim processor. * + * * + * This routine is called once per frame per animation. It handles transition between * + * animation frames and marks the map for redraw as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Speed is of upmost importance. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::AI(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + /* + ** For ground level based animations (ones that can run slowly as well as + ** occur behind other ground objects) always cause the cell to be redrawn. + */ +#ifdef PARTIAL + if (!Delay && Class->IsGroundLayer) { + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); + } +#else + Map.Refresh_Cells(Coord_Cell(Center_Coord()), Overlap_List()); +#endif + + /* + ** Special case check to make sure that building on top of a smoke marker + ** causes the smoke marker to vanish. + */ + if (Class->Type == ANIM_LZ_SMOKE && Map[Center_Coord()].Cell_Building()) { + IsToDelete = true; + } + + /* + ** Don't allow corpses on rocks, rivers, and water (looks really weird). + */ + if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { + LandType land = Map[Center_Coord()].Land_Type(); + if (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER) { + IsToDelete = true; + } + } + + /* + ** Delete this animation and bail early if the animation is flagged to be deleted + ** immediately. + */ + if (IsToDelete) { + delete this; + return; + } + + /* + ** If this is a brand new animation, then don't process it the first logic pass + ** since it might end up skipping the first animation frame before it has had a + ** chance to draw it. + */ + if (IsBrandNew) { + IsBrandNew = false; + return; + } + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + if (Delay) { + Delay--; + if (!Delay) { + Start(); + } + } else { + +#ifdef FIXIT_MULTI_SAVE + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } +#endif + + /* + ** This is necessary because there is no recording of animations on the map + ** and thus the animation cannot be intelligently flagged for redraw. Most + ** animations move fast enough that they would need to be redrawn every + ** game frame anyway so this isn't TOO bad. + */ + Mark(MARK_CHANGE); + + if (StageClass::Graphic_Logic()) { + int stage = Fetch_Stage(); + + /* + ** If this animation is attached to another object and it is a + ** damaging kind of animation, then do the damage to the other + ** object. + */ + if (xObject != TARGET_NONE && Class->Damage > 0 && stage < Class->Stages) { + Accum += Class->Damage; + + if (Accum >= 1) { + + /* + ** Administer the damage. If the object was destroyed by this anim, + ** then the attached damaging anim is also destroyed. + */ + int damage = Accum; + Accum -= damage; + if (As_Object(xObject)->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) { + delete this; + if (Target_Legal(VirtualAnimTarget)) { + delete As_Animation(VirtualAnimTarget); + } + return; + } + } + } + + /* + ** During the biggest stage (covers the most ground), perform any ground altering + ** action required. This masks craters and scorch marks, so that they appear + ** naturally rather than "popping" into existence while in plain sight. + */ + if (Class->Biggest && Class->Start+stage == Class->Biggest) { + Middle(); + } + + /* + ** Check to see if the last frame has been displayed. If so, then the + ** animation either ends or loops. + */ + if ((Loops <= 1 && stage >= Class->Stages) || (Loops > 1 && stage >= Class->LoopEnd-Class->Start)) { + + /* + ** Determine if this animation should loop another time. If so, then start the loop + ** but if not, then proceed into the animation termination handler. + */ + if (Loops) Loops--; + if (Loops) { + Set_Stage(Class->LoopStart); + } else { + + if (Class->VirtualAnim != ANIM_NONE) { + Make_Invisible(); + if (!Target_Legal(VirtualAnimTarget)) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + delete this; + } + } + } + else { + if ((Class->VirtualStages < 0) || (stage >= Class->VirtualStages)) { + if (Class->ChainTo != ANIM_NONE) { + Chain(); + } + else { + delete this; + } + } + } + } + } + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Attach_To -- Attaches animation to object specified. * + * * + * An animation can be "attached" to an object. In such cases, the animation is rendered * + * as an offset from the center of the object it is attached to. This allows affects such * + * as fire or smoke to be consistently placed on the vehicle it is associated with. * + * * + * INPUT: obj -- Pointer to the object to attach the animation to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Attach_To(ObjectClass * obj) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (obj == NULL) return; + assert(obj->IsActive); + + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_UP); + obj->IsAnimAttached = true; + if (obj->In_Which_Layer() == LAYER_GROUND) obj->Mark(MARK_OVERLAP_DOWN); + Limbo(); + xObject = obj->As_Target(); + Unlimbo(Coord); + AttachLayer = In_Which_Layer(); + Height = (AttachLayer == LAYER_GROUND) ? FLIGHT_LEVEL : 0; + Coord = Coord_Sub(Coord, obj->Target_Coord()); +#endif +} + + +/*********************************************************************************************** + * AnimClass::Sort_Above -- Sorts the animation right above the specified target. * + * * + * Allows an animation to always be sorted above a particular target. Typically used * + * for explosions and other effects that look weird beneath those objects. * + * * + * INPUT: target -- Target to sort above. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/2019 SKY : Created. * + *=============================================================================================*/ +void AnimClass::Sort_Above(TARGET target) +{ +#ifdef VIC + SortTarget = target; +#endif +} + + +/*********************************************************************************************** + * AnimClass::In_Which_Layer -- Determines what render layer the anim should be in. * + * * + * Use this routine to find out which display layer (ground or air) that the animation * + * should be in. This information is used to place the animation into the correct display * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that the animation should exist in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/25/1994 JLB : Created. * + *=============================================================================================*/ +LayerType AnimClass::In_Which_Layer(void) const +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (AttachLayer != LAYER_NONE) { + return AttachLayer; + } + + if (Class->Type >= ANIM_CORPSE1 && Class->Type <= ANIM_CORPSE3) { + return(LAYER_SURFACE); + } + + if (Target_Legal(xObject)) { + return As_Object(xObject)->In_Which_Layer(); + } + + if (Class->IsGroundLayer) { + return(LAYER_GROUND); + } +#endif + return(LAYER_AIR); +} + + +/*********************************************************************************************** + * AnimClass::Start -- Processes initial animation side effects. * + * * + * This routine is called when the animation first starts. Sometimes there are side effects * + * associated with this animation that must occur immediately. Typically, this is the * + * sound effect assigned to this animation. If this animation is supposed to attach itself * + * to any object at its location, then do so at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Start(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + Mark(); + + /* + ** Play the sound effect for this animation. + */ + Sound_Effect(Class->Sound, Coord); + + /* + ** If the stage where collateral effects occur is the first stage of the animation, then + ** perform this action now. Subsequent checks against this stage value starts with the + ** second frame of the animation. + */ + if (!Class->Biggest) { + Middle(); + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Middle -- Processes any middle events. * + * * + * This routine is called when the animation as reached its largest stage. Typically, this * + * routine is used to cause scorches or craters to appear at a cosmetically pleasing * + * moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/17/1995 JLB : Ion camera added. * + *=============================================================================================*/ +void AnimClass::Middle(void) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + + if (Class->Type == ANIM_ATOM_BLAST) { + Do_Atom_Damage(OwnerHouse, cell); + } + + /* + ** If this animation leaves scorch marks (e.g., napalm), then do so at this time. + */ + if (Class->IsScorcher) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Center_Coord()); + } + + /* + ** Some animations leave a crater when they occur. Artillery is a good example. + ** Craters always remove the Tiberium where they occur. + */ + if (Class->IsCraterForming) { + + /* + ** Craters reduce the level of Tiberium in the cell. + */ + cellptr->Reduce_Tiberium(6); + + /* + ** If there already is a crater in the cell, then just expand the + ** crater. + */ + new SmudgeClass(SMUDGE_CRATER1, Center_Coord()); + } + + AnimClass * newanim; + + /* + ** If this animation spawns side effects during its lifetime, then + ** do so now. Usually, these side effects are in the form of other + ** animations. + */ + switch (Class->Type) { + case ANIM_NAPALM1: + case ANIM_NAPALM2: + case ANIM_NAPALM3: + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0040), true), 0, Random_Pick(1, 2)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x00A0), true), 0, Random_Pick(1, 2)); + } + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Map.Closest_Free_Spot(Coord_Scatter(Center_Coord(), 0x0070), true), 0, Random_Pick(1, 2)); + } + break; + + case ANIM_FIRE_MED: + case ANIM_FIRE_MED2: + newanim = new AnimClass(ANIM_FIRE_SMALL, Center_Coord(), 0, Random_Pick(1, 2)); + if (newanim != NULL && xObject != TARGET_NONE) { + newanim->Attach_To(As_Object(xObject)); + } + break; + + default: + break; + } +#endif +} + + +void AnimClass::Chain(void) +{ + /* + ** The animation should end now, but first check to see if + ** it needs to chain into another animation. If so, then the + ** animation isn't technically over. It metamorphoses into the + ** new form. + */ + if (Class->ChainTo != ANIM_NONE) { + + Class = (AnimTypeClass *)&AnimTypeClass::As_Reference(Class->ChainTo); + + if (Class->Stages == -1) { + IsTheaterShape = Class->IsTheater; + ((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data()); + IsTheaterShape = false; + } + if (Class->LoopEnd == -1) { + ((int&)Class->LoopEnd) = Class->Stages; + } + + IsToDelete = false; + Loops = Class->Loops; + Accum = 0; + if (Class->IsNormalized) { + Set_Rate(Options.Normalize_Delay(Class->Delay)); + } else { + Set_Rate(Class->Delay); + } + Set_Stage(Class->Start); + Start(); + } +} + + +/*********************************************************************************************** + * AnimClass::Detach -- Remove animation if attached to target. * + * * + * This routine is called when the specified target is being removed from the game. If this * + * animation happens to be attached to this object, then the animation must be remove as * + * well. * + * * + * INPUT: target -- The target that is about to be destroyed. * + * * + * all -- Is the target being destroyed RIGHT NOW? If not, then it will be * + * destroyed soon. In that case, the animation should continue to remain * + * attached for cosmetic reasons. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/02/1995 JLB : Detach is a precursor to animation destruction. * + *=============================================================================================*/ +void AnimClass::Detach(TARGET target, bool all) +{ +#ifdef VIC + assert(Anims.ID(this) == ID); + assert(IsActive); + + if (all) { + if (VirtualAnimTarget && VirtualAnimTarget == target) { + VirtualAnimTarget = TARGET_NONE; + } + if (xObject == target) { + Map.Remove(this, In_Which_Layer()); + xObject = TARGET_NONE; + AttachLayer = LAYER_NONE; + IsToDelete = true; + Mark(MARK_UP); + } + } +#endif +} + + +/*********************************************************************************************** + * AnimClass::Do_Atom_Damage -- Do atom bomb damage centered around the cell specified. * + * * + * This routine will apply damage around the ground-zero cell specified. * + * * + * INPUT: ownerhouse -- The owner of this atom bomb. * + * * + * cell -- The ground zero location to apply the atom bomb damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell) +{ +#ifdef VIC + /* + ** Find someone to blame the explosion on. This is necessary in + ** order to properly enact retribution and record the kill for + ** score purposes. + */ + BuildingClass * building = NULL; + TechnoClass * backup = NULL; + if (ownerhouse != HOUSE_NONE) { + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj != NULL && obj->Is_Techno() && obj->Owner() == ownerhouse) { + backup = (TechnoClass *)obj; + if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_MSLO) { + building = (BuildingClass *)obj; + break; + } + } + } + + if (building == NULL) building = (BuildingClass *)backup; + } + + int radius; + int rawdamage; + if (Session.Type == GAME_NORMAL) { + radius = 4; + rawdamage = Rule.AtomDamage; + //WhitePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 + } else { + radius = 3; + rawdamage = Rule.AtomDamage/5; + } + + Wide_Area_Damage(Cell_Coord(cell), radius * CELL_LEPTON_W, rawdamage, building, WARHEAD_FIRE); + Shake_The_Screen(3); + if (Session.Type == GAME_NORMAL) { + //GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019 + } +#endif +} \ No newline at end of file diff --git a/code/redalert/anim.h b/code/redalert/anim.h new file mode 100644 index 0000000..ff69a8a --- /dev/null +++ b/code/redalert/anim.h @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ANIM.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ANIM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1994 * + * * + * Last Update : May 30, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ANIM_H +#define ANIM_H + +#include "type.h" + + +/********************************************************************************************** +** This is the class that controls the shape animation objects. Shape animation objects are +** displayed over the top of the game map. Typically, they are used for explosion and fire +** effects. +*/ +class AnimClass : public ObjectClass, public StageClass { + /* + ** This points to the type of animation object this is. + */ + CCPtr Class; + + public: + + AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1); + AnimClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~AnimClass(void); + + operator AnimType(void) const {return Class->Type;}; + + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + void Attach_To(ObjectClass *obj); + void Sort_Above(TARGET target); + void Make_Invisible(void) {IsInvisible = true;}; + void Make_Visible(void) {IsInvisible = false;}; + static void Do_Atom_Damage(HousesType ownerhouse, CELL cell); + + /* + ** 2019/09/19 JAS + ** Added functions for accessing which players can see this anim + */ + void Set_Visible_Flags(unsigned flags) { VisibleFlags = flags; } + unsigned Get_Visible_Flags() const { return (Delay == 0) ? VisibleFlags : 0; } + + virtual bool Can_Place_Here(COORDINATE ) const {return true;} + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual bool Render(bool forced) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void AI(void); + virtual void Detach(TARGET target, bool all); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(FileClass & file); + + /* + ** If this animation is attached to an object, then this points to that object. An + ** animation that is attached will follow that object as it moves. This is important + ** for animations such as flames and smoke. + */ + TARGET xObject; + + /* + ** If specified, this animation uses the sort target for Y sorting + */ + TARGET SortTarget; + + /* + ** If this animation has an owner, then it will be recorded here. An owner + ** is used when damage is caused by this animation during the middle of its + ** animation. + */ + HousesType OwnerHouse; + + /* + ** This counter tells how many more times the animation should loop before it + ** terminates. + */ + unsigned char Loops; + + protected: + void Middle(void); + void Start(void); + void Chain(void); + + private: + /* + ** Delete this animation at the next opportunity. This is flagged when the + ** animation is to be prematurely ended as a result of some outside event. + */ + unsigned IsToDelete:1; + + /* + ** If the animation has just been created, then don't do any animation + ** processing until it has been through the render loop at least once. + */ + unsigned IsBrandNew:1; + + /* + ** If this animation is invisible, then this flag will be true. An invisible + ** animation is one that is created for the sole purpose of keeping all + ** machines synchronized. It will not be displayed. + */ + unsigned IsInvisible:1; + + /* + ** 2019/09/19 JAS + ** Flags storing which players can see this anim. + */ + unsigned VisibleFlags; + + /* + ** Is this animation in a temporary suspended state? If so, then it won't + ** be rendered until this value is zero. The flag will be set to false + ** after the first countdown timer reaches 0. + */ + int Delay; + + /* + ** If this is an animation that damages whatever it is attached to, then this + ** value holds the accumulation of fractional damage points. When the accumulated + ** fractions reach 256, then one damage point is applied to the attached object. + */ + fixed Accum; + + /* + ** The map layer this animation is in when attached to an object. + */ + LayerType AttachLayer; + + /* + ** This references the virtual animation. + */ + TARGET VirtualAnimTarget; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + + + +#endif \ No newline at end of file diff --git a/code/redalert/audio.cpp b/code/redalert/audio.cpp new file mode 100644 index 0000000..c6f4801 --- /dev/null +++ b/code/redalert/audio.cpp @@ -0,0 +1,840 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/AUDIO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * Sound_Effect -- General purpose sound player. * + * Sound_Effect -- Plays a sound effect in the tactical map. * + * Speak -- Computer speaks to the player. * + * Speak_AI -- Handles starting the EVA voices. * + * Speech_Name -- Fetches the name for the voice specified. * + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * Voc_Name -- Fetches the name for the sound effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + /* + ** + ** + ** Win32lib stubs + ** + ** + ** + */ + + +SFX_Type SoundType; +Sample_Type SampleType; + +int File_Stream_Sample(char const *filename, BOOL real_time_start) { return 1; }; +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start) { return 1; }; +void __cdecl Sound_Callback(void) {}; +void __cdecl far maintenance_callback(void) {}; +void *Load_Sample(char const *filename) { return NULL; }; +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size) { return 0; } +long Sample_Read(int fh, void *buffer, long size) { return 0; }; +void Free_Sample(void const *sample) {}; +BOOL Audio_Init(HWND window, int bits_per_sample, BOOL stereo, int rate, int reverse_channels) { return 0; }; +void Sound_End(void) {}; +void Stop_Sample(int handle) {}; +BOOL Sample_Status(int handle) { return 0; }; +BOOL Is_Sample_Playing(void const * sample) { return 0; }; +void Stop_Sample_Playing(void const * sample) {}; +int Play_Sample(void const *sample, int priority, int volume, signed short panloc) { return 1; }; +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id) { return 1; }; +int Set_Sound_Vol(int volume) { return 0; }; +int Set_Score_Vol(int volume) { return 0; }; +void Fade_Sample(int handle, int ticks) {}; +int Get_Free_Sample_Handle(int priority) { return 1; }; +int Get_Digi_Handle(void) { return 1; } +long Sample_Length(void const *sample) { return 0; }; +void Restore_Sound_Buffers(void) {}; +BOOL Set_Primary_Buffer_Format(void) { return 0; }; +BOOL Start_Primary_Sound_Buffer(BOOL forced) { return 0; }; +void Stop_Primary_Sound_Buffer(void) {}; + + +/*************************************************************************** +** Controls what special effects may occur on the sound effect. +*/ +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +} ContextType; + +// static struct { // MBL 02.21.2019 +// Had to name the struct for VS 2017 distributed build. ST - 4/10/2019 3:59PM +struct SoundEffectNameStruct { + char const * Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT] = { + + /* + ** Civilian voices (technicians too). + */ + {"GIRLOKAY", 20, IN_NOVAR}, // VOC_GIRL_OKAY + {"GIRLYEAH", 20, IN_NOVAR}, // VOC_GIRL_YEAH + {"GUYOKAY1", 20, IN_NOVAR}, // VOC_GUY_OKAY + {"GUYYEAH1", 20, IN_NOVAR}, // VOC_GUY_YEAH + + {"MINELAY1", 5, IN_VAR}, // VOC_MINELAY1 + + /* + ** Infantry and vehicle responses. + */ + {"ACKNO", 20, IN_VAR}, // VOC_ACKNOWL "acknowledged" + {"AFFIRM1", 20, IN_VAR}, // VOC_AFFIRM "affirmative" + {"AWAIT1", 20, IN_VAR}, // VOC_AWAIT1 "awaiting orders" + {"EAFFIRM1", 20, IN_NOVAR}, // VOC_ENG_AFFIRM Engineer: "affirmative" + {"EENGIN1", 20, IN_NOVAR}, // VOC_ENG_ENG Engineer: "engineering" + {"NOPROB", 20, IN_VAR}, // VOC_NO_PROB "not a problem" + {"READY", 20, IN_VAR}, // VOC_READY "ready and waiting" + {"REPORT1", 20, IN_VAR}, // VOC_REPORT "reporting" + {"RITAWAY", 20, IN_VAR}, // VOC_RIGHT_AWAY "right away sir" + {"ROGER", 20, IN_VAR}, // VOC_ROGER "roger" + {"UGOTIT", 20, IN_VAR}, // VOC_UGOTIT "you got it" + {"VEHIC1", 20, IN_VAR}, // VOC_VEHIC1 "vehicle reporting" + {"YESSIR1", 20, IN_VAR}, // VOC_YESSIR "yes sir" + + {"DEDMAN1", 10, IN_NOVAR}, // VOC_SCREAM1 short infantry scream + {"DEDMAN2", 10, IN_NOVAR}, // VOC_SCREAM3 short infantry scream + {"DEDMAN3", 10, IN_NOVAR}, // VOC_SCREAM4 short infantry scream + {"DEDMAN4", 10, IN_NOVAR}, // VOC_SCREAM5 short infantry scream + {"DEDMAN5", 10, IN_NOVAR}, // VOC_SCREAM6 short infantry scream + {"DEDMAN6", 10, IN_NOVAR}, // VOC_SCREAM7 short infantry scream + {"DEDMAN7", 10, IN_NOVAR}, // VOC_SCREAM10 short infantry scream + {"DEDMAN8", 10, IN_NOVAR}, // VOC_SCREAM11 short infantry scream + {"DEDMAN10", 10, IN_NOVAR}, // VOC_YELL1 long infantry scream + + {"CHRONO2", 5, IN_NOVAR}, // VOC_CHRONO Chronosphere sound + {"CANNON1", 1, IN_NOVAR}, // VOC_CANNON1 Cannon sound (medium). + {"CANNON2", 1, IN_NOVAR}, // VOC_CANNON2 Cannon sound (short). + {"IRONCUR9", 10, IN_NOVAR}, // VOC_IRON1 + {"EMOVOUT1", 20, IN_NOVAR}, // VOC_ENG_MOVEOUT Engineer: "movin' out" + {"SONPULSE", 10, IN_NOVAR}, // VOC_SONAR + {"SANDBAG2", 5, IN_NOVAR}, // VOC_SANDBAG sand bag crunch + {"MINEBLO1", 5, IN_NOVAR}, // VOC_MINEBLOW weird mine explosion + {"CHUTE1", 1, IN_NOVAR}, // VOC_CHUTE1 Wind swoosh sound. + {"DOGY1", 5, IN_NOVAR}, // VOC_DOG_BARK Dog bark. + {"DOGW5", 10, IN_NOVAR}, // VOC_DOG_WHINE Dog whine. + {"DOGG5P", 10, IN_NOVAR}, // VOC_DOG_GROWL2 Strong dog growl. + {"FIREBL3", 1, IN_NOVAR}, // VOC_FIRE_LAUNCH Fireball launch sound. + {"FIRETRT1", 1, IN_NOVAR}, // VOC_FIRE_EXPLODE Fireball explode sound. + {"GRENADE1", 1, IN_NOVAR}, // VOC_GRENADE_TOSS Grenade toss. + {"GUN11", 1, IN_NOVAR}, // VOC_GUN_5 5 round gun burst (slow). + {"GUN13", 1, IN_NOVAR}, // VOC_GUN_7 7 round gun burst (fast). + {"EYESSIR1", 20, IN_NOVAR}, // VOC_ENG_YES, Engineer: "yes sir" + {"GUN27", 1, IN_NOVAR}, // VOC_GUN_RIFLE Rifle shot. + {"HEAL2", 1, IN_NOVAR}, // VOC_HEAL Healing effect. + {"HYDROD1", 1, IN_NOVAR}, // VOC_DOOR Hyrdrolic door. + {"INVUL2", 1, IN_NOVAR}, // VOC_INVULNERABLE Invulnerability effect. + {"KABOOM1", 1, IN_NOVAR}, // VOC_KABOOM1 Long explosion (muffled). + {"KABOOM12", 1, IN_NOVAR}, // VOC_KABOOM12 Very long explosion (muffled). + {"KABOOM15", 1, IN_NOVAR}, // VOC_KABOOM15 Very long explosion (muffled). + {"SPLASH9", 5, IN_NOVAR}, // VOC_SPLASH water splash + {"KABOOM22", 1, IN_NOVAR}, // VOC_KABOOM22 Long explosion (sharp). + {"AACANON3", 1, IN_NOVAR}, + {"TANDETH1", 10, IN_NOVAR}, + {"MGUNINF1", 1, IN_NOVAR}, // VOC_GUN_5F 5 round gun burst (fast). + {"MISSILE1", 1, IN_NOVAR}, // VOC_MISSILE_1 Missile with high tech effect. + {"MISSILE6", 1, IN_NOVAR}, // VOC_MISSILE_2 Long missile launch. + {"MISSILE7", 1, IN_NOVAR}, // VOC_MISSILE_3 Short missile launch. + {"x", 1, IN_NOVAR}, + {"PILLBOX1", 1, IN_NOVAR}, // VOC_GUN_5R 5 round gun burst (rattles). + {"RABEEP1", 1, IN_NOVAR}, // VOC_BEEP Generic beep sound. + {"RAMENU1", 1, IN_NOVAR}, // VOC_CLICK Generic click sound. + {"SILENCER", 1, IN_NOVAR}, // VOC_SILENCER Silencer. + {"TANK5", 1, IN_NOVAR}, // VOC_CANNON6 Long muffled cannon shot. + {"TANK6", 1, IN_NOVAR}, // VOC_CANNON7 Sharp mechanical cannon fire. + {"TORPEDO1", 1, IN_NOVAR}, // VOC_TORPEDO Torpedo launch. + {"TURRET1", 1, IN_NOVAR}, // VOC_CANNON8 Sharp cannon fire. + {"TSLACHG2", 10, IN_NOVAR}, // VOC_TESLA_POWER_UP Hum charge up. + {"TESLA1", 10, IN_NOVAR}, // VOC_TESLA_ZAP Tesla zap effect. + {"SQUISHY2", 10, IN_NOVAR}, // VOC_SQUISH Squish effect. + {"SCOLDY1", 10, IN_NOVAR}, // VOC_SCOLD Scold bleep. + {"RADARON2", 20, IN_NOVAR}, // VOC_RADAR_ON Powering up electronics. + {"RADARDN1", 10, IN_NOVAR}, // VOC_RADAR_OFF B movie power down effect. + {"PLACBLDG", 10, IN_NOVAR}, // VOC_PLACE_BUILDING_DOWN Building slam down sound. + {"KABOOM30", 1, IN_NOVAR}, // VOC_KABOOM30 Short explosion (HE). + {"KABOOM25", 10, IN_NOVAR}, // VOC_KABOOM25 Short growling explosion. + {"x", 10, IN_NOVAR}, + {"DOGW7", 10, IN_NOVAR}, // VOC_DOG_HURT Dog whine (loud). + {"DOGW3PX", 10, IN_NOVAR}, // VOC_DOG_YES Dog 'yes sir'. + {"CRMBLE2", 10, IN_NOVAR}, // VOC_CRUMBLE Building crumble. + {"CASHUP1", 10, IN_NOVAR}, // VOC_MONEY_UP Rising money tick. + {"CASHDN1", 10, IN_NOVAR}, // VOC_MONEY_DOWN Falling money tick. + {"BUILD5", 10, IN_NOVAR}, // VOC_CONSTRUCTION Building construction sound. + {"BLEEP9", 10, IN_NOVAR}, // VOC_GAME_CLOSED Long bleep. + {"BLEEP6", 10, IN_NOVAR}, // VOC_INCOMING_MESSAGE Soft happy warble. + {"BLEEP5", 10, IN_NOVAR}, // VOC_SYS_ERROR Sharp soft warble. + {"BLEEP17", 10, IN_NOVAR}, // VOC_OPTIONS_CHANGED Mid range soft warble. + {"BLEEP13", 10, IN_NOVAR}, // VOC_GAME_FORMING Long warble. + {"BLEEP12", 10, IN_NOVAR}, // VOC_PLAYER_LEFT Chirp sequence. + {"BLEEP11", 10, IN_NOVAR}, // VOC_PLAYER_JOINED Reverse chirp sequence. + {"H2OBOMB2", 10, IN_NOVAR}, // VOC_DEPTH_CHARGE Distant explosion sound. + {"CASHTURN", 10, IN_NOVAR}, // VOC_CASHTURN Airbrake. + {"TUFFGUY1", 20, IN_NOVAR}, // VOC_TANYA_CHEW Tanya: "Chew on this" + {"ROKROLL1", 20, IN_NOVAR}, // VOC_TANYA_ROCK Tanya: "Let's rock" + {"LAUGH1", 20, IN_NOVAR}, // VOC_TANYA_LAUGH Tanya: "ha ha ha" + {"CMON1", 20, IN_NOVAR}, // VOC_TANYA_SHAKE Tanya: "Shake it baby" + {"BOMBIT1", 20, IN_NOVAR}, // VOC_TANYA_CHING Tanya: "Cha Ching" + {"GOTIT1", 20, IN_NOVAR}, // VOC_TANYA_GOT Tanya: "That's all you got" + {"KEEPEM1", 20, IN_NOVAR}, // VOC_TANYA_KISS Tanya: "Kiss it bye bye" + {"ONIT1", 20, IN_NOVAR}, // VOC_TANYA_THERE Tanya: "I'm there" + {"LEFTY1", 20, IN_NOVAR}, // VOC_TANYA_GIVE Tanya: "Give it to me" + {"YEAH1", 20, IN_NOVAR}, // VOC_TANYA_YEA Tanya: "Yea?" + {"YES1", 20, IN_NOVAR}, // VOC_TANYA_YES Tanya: "Yes sir?" + {"YO1", 20, IN_NOVAR}, // VOC_TANYA_WHATS Tanya: "What's up." + {"WALLKIL2", 5, IN_NOVAR}, // VOC_WALLKILL2 Crushing wall sound. + {"x", 10, IN_NOVAR}, + {"GUN5", 5, IN_NOVAR}, // VOC_TRIPLE_SHOT Three quick shots in succession. + {"SUBSHOW1", 5, IN_NOVAR}, // VOC_SUBSHOW Submarine surface sound. + {"EINAH1", 20, IN_NOVAR}, // VOC_E_AH, Einstien "ah" + {"EINOK1", 20, IN_NOVAR}, // VOC_E_OK, Einstien "ok" + {"EINYES1", 20, IN_NOVAR}, // VOC_E_YES, Einstien "yes" + {"MINE1", 10, IN_NOVAR}, // VOC_TRIP_MINE mine explosion sound + + {"SCOMND1", 20, IN_NOVAR}, // VOC_SPY_COMMANDER Spy: "commander?" + {"SYESSIR1", 20, IN_NOVAR}, // VOC_SPY_YESSIR Spy: "yes sir" + {"SINDEED1", 20, IN_NOVAR}, // VOC_SPY_INDEED Spy: "indeed" + {"SONWAY1", 20, IN_NOVAR}, // VOC_SPY_ONWAY Spy: "on my way" + {"SKING1", 20, IN_NOVAR}, // VOC_SPY_KING Spy: "for king and country" + {"MRESPON1", 20, IN_NOVAR}, // VOC_MED_REPORTING Medic: "reporting" + {"MYESSIR1", 20, IN_NOVAR}, // VOC_MED_YESSIR Medic: "yes sir" + {"MAFFIRM1", 20, IN_NOVAR}, // VOC_MED_AFFIRM Medic: "affirmative" + {"MMOVOUT1", 20, IN_NOVAR}, // VOC_MED_MOVEOUT Medic: "movin' out" + {"BEEPSLCT", 10, IN_NOVAR}, // VOC_BEEP_SELECT map selection beep + + {"SYEAH1", 20, IN_NOVAR}, // VOC_THIEF_YEA Thief: "yea?" + {"ANTDIE", 20, IN_NOVAR}, // VOC_ANTDIE + {"ANTBITE", 20, IN_NOVAR}, // VOC_ANTBITE + {"SMOUT1", 20, IN_NOVAR}, // VOC_THIEF_MOVEOUT Thief: "movin' out" + {"SOKAY1", 20, IN_NOVAR}, // VOC_THIEF_OKAY Thief: "ok" + {"x", 20, IN_NOVAR}, + {"SWHAT1", 20, IN_NOVAR}, // VOC_THIEF_WHAT Thief: "what" + {"SAFFIRM1", 20, IN_NOVAR}, // VOC_THIEF_AFFIRM Thief: "affirmative" +//ADDED VG 2/24/97 + {"STAVCMDR", 20, IN_NOVAR}, + {"STAVCRSE", 20, IN_NOVAR}, + {"STAVYES", 20, IN_NOVAR}, + {"STAVMOV", 20, IN_NOVAR}, + {"BUZZY1", 20, IN_NOVAR}, + {"RAMBO1", 20, IN_NOVAR}, + {"RAMBO2", 20, IN_NOVAR}, + {"RAMBO3", 20, IN_NOVAR}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"MYES1", 20, IN_NOVAR}, // VOC_MECHYES1 Mechanic: "Yes sir!" + {"MHOWDY1", 20, IN_NOVAR}, // VOC_MECHHOWDY1 Mechanic: "Howdy!" + {"MRISE1", 20, IN_NOVAR}, // VOC_MECHRISE1 Mechanic: "Rise 'n shine!" + {"MHUH1", 20, IN_NOVAR}, // VOC_MECHHUH1 Mechanic: "Huh?" + {"MHEAR1", 20, IN_NOVAR}, // VOC_MECHHEAR1 Mechanic: "I Hear Ya!" + {"MLAFF1", 20, IN_NOVAR}, // VOC_MECHLAFF1 Mechanic: guffaw + {"MBOSS1", 20, IN_NOVAR}, // VOC_MECHBOSS1 Mechanic: "Sure Thing, Boss!" + {"MYEEHAW1", 20, IN_NOVAR}, // VOC_MECHYEEHAW1 Mechanic: "Yee Haw!" + {"MHOTDIG1", 20, IN_NOVAR}, // VOC_MECHHOTDIG1 Mechanic: "Hot Diggity Dog!" + {"MWRENCH1", 20, IN_NOVAR}, // VOC_MECHWRENCH1 Mechanic: "I'll get my wrench." + + {"JBURN1", 20, IN_NOVAR}, // VOC_STBURN1 Shock Trooper: "Burn baby burn!" + {"JCHRGE1", 20, IN_NOVAR}, // VOC_STCHRGE1 Shock Trooper: "Fully charged!" + {"JCRISP1", 20, IN_NOVAR}, // VOC_STCRISP1 Shock Trooper: "Extra Crispy!" + {"JDANCE1", 20, IN_NOVAR}, // VOC_STDANCE1 Shock Trooper: "Let's Dance!" + {"JJUICE1", 20, IN_NOVAR}, // VOC_STJUICE1 Shock Trooper: "Got juice?" + {"JJUMP1", 20, IN_NOVAR}, // VOC_STJUMP1 Shock Trooper: "Need a jump?" + {"JLIGHT1", 20, IN_NOVAR}, // VOC_STLIGHT1 Shock Trooper: "Lights out!" + {"JPOWER1", 20, IN_NOVAR}, // VOC_STPOWER1 Shock Trooper: "Power on!" + {"JSHOCK1", 20, IN_NOVAR}, // VOC_STSHOCK1 Shock Trooper: "Shocking!" + {"JYES1", 20, IN_NOVAR}, // VOC_STYES1 Shock Trooper: "Yesssss!" + + {"CHROTNK1", 20, IN_NOVAR}, // VOC_CHRONOTANK1 Chrono tank teleport + {"FIXIT1", 20, IN_NOVAR}, // VOC_MECH_FIXIT1 Mechanic fixes something + {"MADCHRG2", 20, IN_NOVAR}, // VOC_MAD_CHARGE MAD tank charges up + {"MADEXPLO", 20, IN_NOVAR}, // VOC_MAD_EXPLODE MAD tank explodes + {"SHKTROP1", 20, IN_NOVAR}, // VOC_SHOCK_TROOP1 Shock Trooper fires + +#endif +}; + + +const char* GetEffectFileName(int index) { + return SoundEffectName[index].Name; +} + +// +// External handlers. MBL 06.17.2019 +// +extern void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house); +// extern void On_Speech(int speech_index); MBL 02.06.2020 +extern void On_Speech(int speech_index, HouseClass *house); +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + + + +/*********************************************************************************************** + * Voc_From_Name -- Fetch VocType from ASCII name specified. * + * * + * This will find the corresponding VocType from the ASCII string specified. It does this * + * by finding a root filename that matches the string. * + * * + * INPUT: name -- Pointer to the ASCII string that will be converted into a VocType. * + * * + * OUTPUT: Returns with the VocType that matches the string specified. If no match could be * + * found, then VOC_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +VocType Voc_From_Name(char const * name) +{ + if (name == NULL) return(VOC_NONE); + + for (VocType voc = VOC_FIRST; voc < VOC_COUNT; voc++) { + if (stricmp(name, SoundEffectName[voc].Name) == 0) { + return(voc); + } + } + + return(VOC_NONE); +} + + +/*********************************************************************************************** + * Voc_Name -- Fetches the name for the sound effect. * + * * + * This routine returns the descriptive name of the sound effect. Currently, this is just * + * the root of the file name. * + * * + * INPUT: voc -- The VocType that the corresponding name is requested. * + * * + * OUTPUT: Returns with a pointer to the text string the represents the sound effect. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * Voc_Name(VocType voc) +{ + if (voc == VOC_NONE) return("none"); + return(SoundEffectName[voc].Name); +} + + +/*********************************************************************************************** + * Sound_Effect -- Plays a sound effect in the tactical map. * + * * + * This routine is used when a sound effect occurs in the game world. It handles fading * + * the sound according to distance. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * coord -- The world location that the sound originates from. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 01/05/1995 JLB : Reduces sound more dramatically when off screen. * + * 09/15/1996 JLB : Revamped volume logic. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +void Sound_Effect(VocType voc, COORDINATE coord, int variation, HousesType house) +{ + // + // Intercept sound effect calls. MBL 06.17.2019 + // + On_Sound_Effect((int)voc, variation, coord, (int)house); + +#if 0 + CELL cell_pos = 0; + int pan_value; + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return; + } + if (coord) { + cell_pos = Coord_Cell(coord); + } + + fixed volume = 1; + pan_value = 0; + if (coord && !Map.In_View(cell_pos)) { + int distance = Distance(coord, Map.TacticalCoord) / CELL_LEPTON_W; + fixed dfixed = fixed(distance, 128+64); + dfixed.Sub_Saturate(1); + volume = fixed(1) - dfixed; + + pan_value = Cell_X(cell_pos); + pan_value -= Coord_XCell(Map.TacticalCoord) + (Lepton_To_Cell(Map.TacLeptonWidth) / 2); + if (ABS(pan_value) > Lepton_To_Cell(Map.TacLeptonWidth / 2)) { + pan_value *= 0x8000; + pan_value /= (MAP_CELL_W >> 2); + pan_value = Bound(pan_value, -0x7FFF, 0x7FFF); + } else { + pan_value = 0; + } + } + + Sound_Effect(voc, volume, variation, pan_value, house); +#endif +} + + +/*********************************************************************************************** + * Sound_Effect -- General purpose sound player. * + * * + * This is used for general purpose sound effects. These are sounds that occur outside * + * of the game world. They do not have a corresponding game world location as their source. * + * * + * INPUT: voc -- The sound effect number to play. * + * * + * volume -- The volume to assign to this sound effect. * + * * + * variation -- This is the optional variation number to use when playing special * + * sound effects that have variations. For normal sound effects, this * + * parameter is ignored. * + * * + * house -- This specifies the optional house override value to use when playing * + * sound effects that have a variation. If not specified, then the current * + * player is examined for the house variation to use. * + * * + * OUTPUT: Returns with the sound handle (-1 if no sound was played). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + * 11/12/1994 JLB : Handles cache logic. * + * 05/04/1995 JLB : Variation adjustments. * + * 11/01/1996 JLB : House override control. * + *=============================================================================================*/ +int Sound_Effect(VocType voc, fixed volume, int variation, signed short pan_value, HousesType house) +{ + // + // Intercept sound effect calls. MBL 06.17.2019 + // + pan_value; + COORDINATE coord = 0; + On_Sound_Effect((int)voc, variation, coord, (int)house); + +#if 0 + char name[_MAX_FNAME+_MAX_EXT]; // Working filename of sound effect. + + if (Debug_Quiet || Options.Volume == 0 || voc == VOC_NONE || !SoundOn || SampleType == SAMPLE_NONE) { + return(-1); + } + + /* + ** Alter the volume according to the game volume setting. + */ + volume = volume * Options.Volume; + + /* + ** Fetch a pointer to the sound effect data. Modify the sound as appropriate and desired. + */ + char const * ext = ".AUD"; + if (SoundEffectName[voc].Where == IN_VAR) { + + /* + ** If there is no forced house, then use the current player + ** act like house. + */ + if (house == HOUSE_NONE) { + house = PlayerPtr->ActLike; + } + + /* + ** Change the extension based on the variation and house accent requested. + */ + if (((1 << house) & HOUSEF_ALLIES) != 0) { + + /* + ** For infantry, use a variation on the response. For vehicles, always + ** use the vehicle response table. + */ + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".V00"; + } else { + ext = ".V02"; + } + } else { + if (variation % 2) { + ext = ".V01"; + } else { + ext = ".V03"; + } + } + } else { + if (variation < 0) { + if (ABS(variation) % 2) { + ext = ".R00"; + } else { + ext = ".R02"; + } + } else { + if (variation % 2) { + ext = ".R01"; + } else { + ext = ".R03"; + } + } + } + } + _makepath(name, NULL, NULL, SoundEffectName[voc].Name, ext); + void const * ptr = MFCD::Retrieve(name); + + /* + ** If the sound data pointer is not null, then presume that it is valid. + */ + if (ptr != NULL) { + volume.Sub_Saturate(1); + return(Play_Sample(ptr, SoundEffectName[voc].Priority * volume, volume*256, pan_value)); + } +#endif + + return(-1); +} + + +/* +** This elaborates all the EVA speech voices. +*/ +/*static PG*/ char const * Speech[VOX_COUNT] = { + "MISNWON1", // VOX_ACCOMPLISHED mission accomplished + "MISNLST1", // VOX_FAIL your mission has failed + "PROGRES1", // VOX_NO_FACTORY unable to comply, building in progress + "CONSCMP1", // VOX_CONSTRUCTION construction complete + "UNITRDY1", // VOX_UNIT_READY unit ready + "NEWOPT1", // VOX_NEW_CONSTRUCT new construction options + "NODEPLY1", // VOX_DEPLOY cannot deploy here + "STRCKIL1", // VOX_STRUCTURE_DESTROYED, structure destroyed + "NOPOWR1", // VOX_INSUFFICIENT_POWER, insufficient power + "NOFUNDS1", // VOX_NO_CASH insufficient funds + "BCT1", // VOX_CONTROL_EXIT battle control terminated + "REINFOR1", // VOX_REINFORCEMENTS reinforcements have arrived + "CANCLD1", // VOX_CANCELED canceled + "ABLDGIN1", // VOX_BUILDING building + "LOPOWER1", // VOX_LOW_POWER low power + "NOFUNDS1", // VOX_NEED_MO_MONEY insufficent funds + "BASEATK1", // VOX_BASE_UNDER_ATTACK our base is under attack + "NOBUILD1", // VOX_UNABLE_TO_BUILD unable to build more + "PRIBLDG1", // VOX_PRIMARY_SELECTED primary building selected +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef ENGLISH + "TANK01", // VOX_MADTANK_DEPLOYED M.A.D. Tank Deployed +#else + "none", +#endif +#else + "none", +#endif + "none", // VOX_SOVIET_CAPTURED Allied building captured + "UNITLST1", // VOX_UNIT_LOST unit lost + "SLCTTGT1", // VOX_SELECT_TARGET select target + "ENMYAPP1", // VOX_PREPARE enemy approaching + "SILOND1", // VOX_NEED_MO_CAPACITY silos needed + "ONHOLD1", // VOX_SUSPENDED on hold + "REPAIR1", // VOX_REPAIRING repairing + "none", + "none", + "AUNITL1", // VOX_AIRCRAFT_LOST airborne unit lost + "none", + "AAPPRO1", // VOX_ALLIED_FORCES_APPROACHING allied forces approaching + "AARRIVE1", // VOX_ALLIED_APPROACHING allied reinforcements have arrived + "none", + "none", + "BLDGINF1", // VOX_BUILDING_INFILTRATED building infiltrated + "CHROCHR1", // VOX_CHRONO_CHARGING chronosphere charging + "CHRORDY1", // VOX_CHRONO_READY chronosphere ready + "CHROYES1", // VOX_CHRONO_TEST chronosphere test successful + "CMDCNTR1", // VOX_HQ_UNDER_ATTACK command center under attack + "CNTLDED1", // VOX_CENTER_DEACTIVATED control center deactivated + "CONVYAP1", // VOX_CONVOY_APPROACHING convoy approaching + "CONVLST1", // VOX_CONVOY_UNIT_LOST convoy unit lost + "XPLOPLC1", // VOX_EXPLOSIVE_PLACED explosive charge placed + "CREDIT1", // VOX_MONEY_STOLEN credits stolen + "NAVYLST1", // VOX_SHIP_LOST naval unit lost + "SATLNCH1", // VOX_SATALITE_LAUNCHED satalite launched + "PULSE1", // VOX_SONAR_AVAILABLE sonar pulse available + "none", + "SOVFAPP1", // VOX_SOVIET_FORCES_APPROACHING soviet forces approaching + "SOVREIN1", // VOX_SOVIET_REINFROCEMENTS soviet reinforcements have arrived + "TRAIN1", // VOX_TRAINING training + "AREADY1", // VOX_ABOMB_READY + "ALAUNCH1", // VOX_ABOMB_LAUNCH + "AARRIVN1", // VOX_ALLIES_N + "AARRIVS1", // VOX_ALLIES_S + "AARIVE1", // VOX_ALLIES_E + "AARRIVW1", // VOX_ALLIES_W + "1OBJMET1", // VOX_OBJECTIVE1 + "2OBJMET1", // VOX_OBJECTIVE2 + "3OBJMET1", // VOX_OBJECTIVE3 + "IRONCHG1", // VOX_IRON_CHARGING + "IRONRDY1", // VOX_IRON_READY + "KOSYRES1", // VOX_RESCUED + "OBJNMET1", // VOX_OBJECTIVE_NOT + "FLAREN1", // VOX_SIGNAL_N + "FLARES1", // VOX_SIGNAL_S + "FLAREE1", // VOX_SIGNAL_E + "FLAREW1", // VOX_SIGNAL_W + "SPYPLN1", // VOX_SPY_PLANE + "TANYAF1", // VOX_FREED + "ARMORUP1", // VOX_UPGRADE_ARMOR + "FIREPO1", // VOX_UPGRADE_FIREPOWER + "UNITSPD1", // VOX_UPGRADE_SPEED + "MTIMEIN1", // VOX_MISSION_TIMER + "UNITFUL1", // VOX_UNIT_FULL + "UNITREP1", // VOX_UNIT_REPAIRED + "40MINR", // VOX_TIME_40 + "30MINR", // VOX_TIME_30 + "20MINR", // VOX_TIME_20 + "10MINR", // VOX_TIME_10 + "5MINR", // VOX_TIME_5 + "4MINR", // VOX_TIME_4 + "3MINR", // VOX_TIME_3 + "2MINR", // VOX_TIME_2 + "1MINR", // VOX_TIME_1 + "TIMERNO1", // VOX_TIME_STOP + "UNITSLD1", // VOX_UNIT_SOLD + "TIMERGO1", // VOX_TIMER_STARTED + "TARGRES1", // VOX_TARGET_RESCUED + "TARGFRE1", // VOX_TARGET_FREED + "TANYAR1", // VOX_TANYA_RESCUED + "STRUSLD1", // VOX_STRUCTURE_SOLD + "SOVFORC1", // VOX_SOVIET_FORCES_FALLEN + "SOVEMP1", // VOX_SOVIET_SELECTED + "SOVEFAL1", // VOX_SOVIET_EMPIRE_FALLEN + "OPTERM1", // VOX_OPERATION_TERMINATED + "OBJRCH1", // VOX_OBJECTIVE_REACHED + "OBJNRCH1", // VOX_OBJECTIVE_NOT_REACHED + "OBJMET1", // VOX_OBJECTIVE_MET + "MERCR1", // VOX_MERCENARY_RESCUED + "MERCF1", // VOX_MERCENARY_FREED + "KOSYFRE1", // VOX_KOSOYGEN_FREED + "FLARE1", // VOX_FLARE_DETECTED + "COMNDOR1", // VOX_COMMANDO_RESCUED + "COMNDOF1", // VOX_COMMANDO_FREED + "BLDGPRG1", // VOX_BUILDING_IN_PROGRESS + "ATPREP1", // VOX_ATOM_PREPPING + "ASELECT1", // VOX_ALLIED_SELECTED + "APREP1", // VOX_ABOMB_PREPPING + "ATLNCH1", // VOX_ATOM_LAUNCHED + "AFALLEN1", // VOX_ALLIED_FORCES_FALLEN + "AAVAIL1", // VOX_ABOMB_AVAILABLE + "AARRIVE1", // VOX_ALLIED_REINFORCEMENTS + "SAVE1", // VOX_MISSION_SAVED + "LOAD1" // VOX_MISSION_LOADED +}; + +const char* GetSpeechFileName(int index) { + return Speech[index]; +} + + +static VoxType CurrentVoice = VOX_NONE; + + +/*********************************************************************************************** + * Speech_Name -- Fetches the name for the voice specified. * + * * + * Use this routine to fetch the ASCII name of the speech id specified. Typical use of this * + * would be to build a displayable list of the speech types. The trigger system uses this * + * so that a speech type can be selected. * + * * + * INPUT: speech -- The speech type id to convert to ASCII string. * + * * + * OUTPUT: Returns with a pointer to the speech ASCII representation of the speech id type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 JLB : Created. * + *=============================================================================================*/ +char const * Speech_Name(VoxType speech) +{ + if (speech == VOX_NONE) return("none"); + return(Speech[speech]); +} + + +/*********************************************************************************************** + * Speak -- Computer speaks to the player. * + * * + * This routine is used to have the game computer (EVA) speak to the player. * + * * + * INPUT: voice -- The voice number to speak (see defines.h). * + * * + * OUTPUT: Returns with the handle of the playing speech (-1 if no voice started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/1994 JLB : Created. * + *=============================================================================================*/ +// void Speak(VoxType voice) // MBL 02.06.2020 +void Speak(VoxType voice, HouseClass *house, COORDINATE coord) +{ + // MBL 06.17.2019 + if (voice == VOX_NONE) + { + return; + } + + // + // Intercept speech calls. MBL 06.17.2019 + // + + // On_Speech((int)voice); // MBL 02.06.2020 + On_Speech((int)voice, house); + if (coord) { + On_Ping(house, coord); + } + +#if 0 + if (!Debug_Quiet && Options.Volume != 0 && SampleType != 0 && voice != VOX_NONE && voice != SpeakQueue && voice != CurrentVoice && SpeakQueue == VOX_NONE) { + SpeakQueue = voice; + Speak_AI(); + } +#endif +} + + +/*********************************************************************************************** + * Speak_AI -- Handles starting the EVA voices. * + * * + * This starts the EVA voice talking as well. If there is any speech request in the queue, * + * it will be started when the current voice is finished. Call this routine as often as * + * possible (once per game tick is sufficient). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 10/11/1996 JLB : Handles multiple speech buffers. * + *=============================================================================================*/ +void Speak_AI(void) +{ +// MBL 06.17.2019 KO +#if 0 + static int _index = 0; + if (Debug_Quiet || SampleType == 0) return; + + if (!Is_Sample_Playing(SpeechBuffer[_index])) { + CurrentVoice = VOX_NONE; + if (SpeakQueue != VOX_NONE) { + + /* + ** Try to find a previously loaded copy of the EVA speech in one of the + ** speech buffers. + */ + void const * speech = NULL; + for (int index = 0; index < ARRAY_SIZE(SpeechRecord); index++) { + if (SpeechRecord[index] == SpeakQueue) break; + } + + /* + ** If a previous copy could not be located, then load the requested + ** voice into the oldest buffer available. + */ + if (speech == NULL) { + _index = (_index + 1) % ARRAY_SIZE(SpeechRecord); + + char name[_MAX_FNAME+_MAX_EXT]; + + _makepath(name, NULL, NULL, Speech[SpeakQueue], ".AUD"); + CCFileClass file(name); + if (file.Is_Available() && file.Read(SpeechBuffer[_index], SPEECH_BUFFER_SIZE)) { + speech = SpeechBuffer[_index]; + SpeechRecord[_index] = SpeakQueue; + } + } + + /* + ** Since the speech file was loaded, play it. + */ + if (speech != NULL) { + Play_Sample(speech, 254, Options.Volume * 256); + CurrentVoice = SpeakQueue; + } + + SpeakQueue = VOX_NONE; + } + } +#endif + + // MBL 06.18.2019 + if (SpeakQueue != VOX_NONE) + { + // On_Speech((int)SpeakQueue); // MBL 02.06.2020 + On_Speech((int)SpeakQueue, NULL); + SpeakQueue = VOX_NONE; + } +} + + +/*********************************************************************************************** + * Stop_Speaking -- Forces the EVA voice to stop talking. * + * * + * Use this routine to immediately stop the EVA voice from speaking. It also clears out * + * the pending voice queue. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void Stop_Speaking(void) +{ + SpeakQueue = VOX_NONE; + Stop_Sample_Playing(SpeechBuffer); +} diff --git a/code/redalert/audio.h b/code/redalert/audio.h new file mode 100644 index 0000000..3793f53 --- /dev/null +++ b/code/redalert/audio.h @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\audio.h_v 4.43 05 Jul 1996 17:58:10 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : AUDIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 21, 1994 * + * * + * Last Update : June 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef AUDIO_H +#define AUDIO_H + +#include "memory.h" + +class AudioClass { + char const * Name; // Name of audio asset. + void const * Data; // Loaded audio data. + int Handle; // Handle of asset (as it is playing). + MemoryClass *Mem; // Pointer to memory handler class. + unsigned IsMIDI:1; // Is this a midi file? + + public: + AudioClass(void); + AudioClass(char const *name, MemoryClass &mem); + virtual ~AudioClass(void); + + bool Load(char const *name = 0); + bool Free(void); + bool Play(int volume = 0xFF); + bool Stop(void); + bool Pause(void); + bool Resume(void); + bool Set_Name(char const *name); + bool Is_Playing(void) const; + bool Is_Loaded(void) const; + bool Is_MIDI(void) const; +}; + +inline AudioClass::AudioClass(void) +{ + Name = 0; + Data = 0; + Mem = 0; + Handle = -1; +}; + +inline AudioClass::AudioClass(char const *name, MemoryClass &mem) +{ + if (mem) { + Mem = &mem; + } else { + Mem = &::Mem; // Uses global default memory handler. + } + Name = strdup(name); + Data = 0; + Handle = -1; +}; + +inline AudioClass::~AudioClass(void) +{ + if (GameActive) { + if (Name) free(Name); + if (Data) Mem->Free(Data); + Name = 0; + Data = 0; + Handle = -1; + } +}; + + +#endif diff --git a/code/redalert/audiomix.h b/code/redalert/audiomix.h new file mode 100644 index 0000000..2a36b08 --- /dev/null +++ b/code/redalert/audiomix.h @@ -0,0 +1,14 @@ +// AUDIOMIX.H +// + + +#pragma once + +void AudMix_Init(void); +const char* GetEffectFileName(int index); +const char* GetThemeMusicFileName(int index); +void AudMix_PlayMusic(int musicId); +const char* GetSpeechFileName(int index); +void AudMix_QueueVideoAudio(const char* audio_buffer, int length, int rate); +void AudMix_TickMovieAudio(void); +void AudMix_SetMusicState(bool musicState); \ No newline at end of file diff --git a/code/redalert/audmix.cpp b/code/redalert/audmix.cpp new file mode 100644 index 0000000..ce2fdf5 --- /dev/null +++ b/code/redalert/audmix.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ + +#include "function.h" +#include "AUDIOMIX.H" + +#include +#include + +ALCdevice* device = nullptr; +ALCcontext* context = nullptr; + +struct AudioBuffer_t { + ALuint buffer; + ALsizei size; + ALsizei frequency; + ALenum format; +}; + +enum AudioBufferType_t { + AUDIO_BUFFER_HOUSE_NONE = 0, + AUDIO_BUFFER_HOUSE_ALIED, + AUDIO_BUFFER_HOUSE_SOVIET, + AUDIO_BUFFER_NUMTYPES +}; + +#define MAX_PRECACHE_AUDIO 512 +#define MAX_STREAM_BUFFERS 20 + +AudioBuffer_t precacheAudioTable[AUDIO_BUFFER_NUMTYPES][MAX_PRECACHE_AUDIO]; +AudioBuffer_t speechPrecacheAudioTable[MAX_PRECACHE_AUDIO]; +AudioBuffer_t musicPrecacheAudioTable[MAX_PRECACHE_AUDIO]; +ALuint streaming_buffers[MAX_STREAM_BUFFERS]; + +int currentMusicPlaying = -1; +int currentAudioTSource = 0; + +struct RIFF_Header { + char chunkID[4]; + long chunkSize; + char format[4]; +}; + +struct WAVE_Format { + char subChunkID[4]; + long subChunkSize; + short audioFormat; + short numChannels; + long sampleRate; + long byteRate; + short blockAlign; + short bitsPerSample; +}; + +struct WAVE_Data { + char subChunkID[4]; + long subChunk2Size; +}; + +#define MAX_AUDIO_SOURCES 32 +#define SPEECH_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 3) +#define MOVIE_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 2) +#define MUSIC_AUDIO_SOURCE (MAX_AUDIO_SOURCES - 1) +ALuint audio_sources[MAX_AUDIO_SOURCES]; +int current_streaming_audiosource = 0; + +bool loadWavFile(const char* filename, AudioBuffer_t &audioBuffer) { + FILE* soundFile = NULL; + WAVE_Format wave_format; + RIFF_Header riff_header; + WAVE_Data wave_data; + unsigned char* data; + + ALuint* buffer = &audioBuffer.buffer; + ALsizei* size = &audioBuffer.size; + ALsizei* frequency = &audioBuffer.frequency; + ALenum* format = &audioBuffer.format; + + try { + soundFile = fopen(filename, "rb"); + if (!soundFile) { + return false; + } + + fread(&riff_header, sizeof(RIFF_Header), 1, soundFile); + + if ((riff_header.chunkID[0] != 'R' || + riff_header.chunkID[1] != 'I' || + riff_header.chunkID[2] != 'F' || + riff_header.chunkID[3] != 'F') && + (riff_header.format[0] != 'W' || + riff_header.format[1] != 'A' || + riff_header.format[2] != 'V' || + riff_header.format[3] != 'E')) + throw ("Invalid RIFF or WAVE Header"); + + fread(&wave_format, sizeof(WAVE_Format), 1, soundFile); + + if (wave_format.subChunkID[0] != 'f' || + wave_format.subChunkID[1] != 'm' || + wave_format.subChunkID[2] != 't' || + wave_format.subChunkID[3] != ' ') + throw ("Invalid Wave Format"); + + if (wave_format.subChunkSize > 16) + fseek(soundFile, sizeof(short), SEEK_CUR); + + fread(&wave_data, sizeof(WAVE_Data), 1, soundFile); + + if (wave_data.subChunkID[0] != 'd' || + wave_data.subChunkID[1] != 'a' || + wave_data.subChunkID[2] != 't' || + wave_data.subChunkID[3] != 'a') + throw ("Invalid data header"); + + data = new unsigned char[wave_data.subChunk2Size]; + + if (!fread(data, wave_data.subChunk2Size, 1, soundFile)) + throw ("error loading WAVE data into struct!"); + + *size = wave_data.subChunk2Size; + *frequency = wave_format.sampleRate; + + if (wave_format.numChannels == 1) { + if (wave_format.bitsPerSample == 8) + *format = AL_FORMAT_MONO8; + else if (wave_format.bitsPerSample == 16) + *format = AL_FORMAT_MONO16; + } + else if (wave_format.numChannels == 2) { + if (wave_format.bitsPerSample == 8) + *format = AL_FORMAT_STEREO8; + else if (wave_format.bitsPerSample == 16) + *format = AL_FORMAT_STEREO16; + } + + alGenBuffers(1, buffer); + alBufferData(*buffer, *format, (void*)data, + *size, *frequency); + fclose(soundFile); + + return true; + } + catch (char* error) { + if (soundFile != NULL) + fclose(soundFile); + return false; + } +} + + +/* +============= +AudMix_IsEnabled +============= +*/ +bool AudMix_IsEnabled(void) { + return device != NULL && context != NULL; +} + +/* +============= +AudMix_PlayMusic +============= +*/ +void AudMix_PlayMusic(int musicId) { + if (currentMusicPlaying != -1) { + //alSourcei(musicPrecacheAudioTable[currentMusicPlaying].sourceId, AL_LOOPING, 0); + alSourceStop(audio_sources[MAX_AUDIO_SOURCES - 1]); + } + + alSourcei(audio_sources[MAX_AUDIO_SOURCES - 1], AL_BUFFER, musicPrecacheAudioTable[musicId].buffer); + alSourcei(audio_sources[MAX_AUDIO_SOURCES - 1], AL_LOOPING, 1); + alSourcePlay(audio_sources[MAX_AUDIO_SOURCES - 1]); + currentMusicPlaying = musicId; +} + +bool AudMix_IsSourcePlaying(ALuint source) +{ + ALenum state; + alGetSourcei(source, AL_SOURCE_STATE, &state); + return (state == AL_PLAYING); +} + +void AudMix_TickMovieAudio(void) { + int numBuffers = 0; + + alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_PROCESSED, &numBuffers); + while (numBuffers--) + { + ALuint buffer; + alSourceUnqueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &buffer); + } +} + +/* +============= +AudMix_QueueVideoAudio +============= +*/ +void AudMix_QueueVideoAudio(const char* audio_buffer, int length, int rate) { + int numBuffers = 0; + + alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_PROCESSED, &numBuffers); + while (numBuffers--) + { + ALuint buffer; + alSourceUnqueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &buffer); + } + + numBuffers = 0; + alGetSourcei(audio_sources[MOVIE_AUDIO_SOURCE], AL_BUFFERS_QUEUED, &numBuffers); + if (numBuffers == MAX_STREAM_BUFFERS) + { + // assert(!"Steam dropping raw samples, reached MAX_STREAM_BUFFERS"); + return; + } + + if (current_streaming_audiosource >= MAX_STREAM_BUFFERS) + current_streaming_audiosource = 0; + + alBufferData(streaming_buffers[current_streaming_audiosource], AL_FORMAT_MONO16, (ALvoid*)audio_buffer, length, 22050); + alSourceQueueBuffers(audio_sources[MOVIE_AUDIO_SOURCE], 1, &streaming_buffers[current_streaming_audiosource]); + if(!AudMix_IsSourcePlaying(audio_sources[MOVIE_AUDIO_SOURCE])) + alSourcePlay(audio_sources[MOVIE_AUDIO_SOURCE]); + + //MOVIE_AUDIO_SOURCE + current_streaming_audiosource++; +} + +/* +============= +AudMix_Init +============= +*/ +void AudMix_Init(void) { + // Open up the audio device. + device = alcOpenDevice(NULL); + if (!device) { + return; + } + + context = alcCreateContext(device, NULL); + if (!alcMakeContextCurrent(context)) { + return; + } + + // Precache all of the audio. + for (int i = 0; i < VOC_COUNT; i++) { + char expandedFilePath[512]; + + sprintf(expandedFilePath, "sound/%s.wav", GetEffectFileName(i)); + + if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][i])) + { + sprintf(expandedFilePath, "sound/alied/%s.wav", GetEffectFileName(i)); + if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_ALIED][i])) { + // assert(!"failed to load audio!"); + } + + sprintf(expandedFilePath, "sound/russian/%s.wav", GetEffectFileName(i)); + if (!loadWavFile(expandedFilePath, precacheAudioTable[AUDIO_BUFFER_HOUSE_SOVIET][i])) { + //assert(!"failed to load audio!"); + } + } + } + + // Precache all the Speech + for (int i = 0; i < VOX_COUNT; i++) { + char expandedFilePath[512]; + + sprintf(expandedFilePath, "sound/speech/%s.wav", GetSpeechFileName(i)); + if (!loadWavFile(expandedFilePath, speechPrecacheAudioTable[i])) { + //assert(!"failed to load music!"); + } + } + + // Precache the music. + for (int i = 0; i < THEME_COUNT; i++) { + char expandedFilePath[512]; + + sprintf(expandedFilePath, "sound/music/%s.wav", GetThemeMusicFileName(i)); + if (!loadWavFile(expandedFilePath, musicPrecacheAudioTable[i])) { + //assert(!"failed to load music!"); + } + } + + // Allocate the audio sources. + alGenSources(MAX_AUDIO_SOURCES, &audio_sources[0]); + for(int i = 0; i < MAX_AUDIO_SOURCES; i++) + alSource3f(audio_sources[i], AL_POSITION, 0.0f, 0.0f, 0.0f); + + alListener3f(AL_POSITION, 0.0f, 0.0f, 0.0f); + + alGenBuffers(MAX_STREAM_BUFFERS, streaming_buffers); +} + +void PlayAudio(AudioBuffer_t& buffer, bool speech) { + // Only one speech sound can play at once. + if (speech) { + //if (AudMix_IsSourcePlaying(SPEECH_AUDIO_SOURCE)) + // return; + + alSourceStop(audio_sources[SPEECH_AUDIO_SOURCE]); + alSourcei(audio_sources[SPEECH_AUDIO_SOURCE], AL_BUFFER, buffer.buffer); + alSourcePlay(audio_sources[SPEECH_AUDIO_SOURCE]); + return; + } + + if (currentAudioTSource >= MAX_AUDIO_SOURCES - 3) + currentAudioTSource = 0; + + alSourcei(audio_sources[currentAudioTSource], AL_BUFFER, buffer.buffer); + alSourcePlay(audio_sources[currentAudioTSource]); + + currentAudioTSource++; +} + +void AudMix_SetMusicState(bool musicState) { + if (!musicState) { + alSourcePause(audio_sources[MUSIC_AUDIO_SOURCE]); + } + else { + alSourcePlay(audio_sources[MUSIC_AUDIO_SOURCE]); + } +} + +void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house) +{ + // MBL 06.17.2019 + int voc = sound_index; + if (voc == VOC_NONE) + { + return; + } + + if (precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][sound_index].buffer != 0) { + PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_NONE][sound_index], false); + } + else if(PlayerPtr != NULL) { + // We have to play a faction audio. + if (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE || PlayerPtr->Class->House == HOUSE_TURKEY) { + PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_SOVIET][sound_index], false); + } + else { + PlayAudio(precacheAudioTable[AUDIO_BUFFER_HOUSE_ALIED][sound_index], false); + } + } +} + + +// void On_Speech(int speech_index) // MBL 02.06.2020 +void On_Speech(int speech_index, HouseClass* house) +{ + if (house == NULL) { + PlayAudio(speechPrecacheAudioTable[speech_index], true); + } + + // DLLExportClass::On_Speech(PlayerPtr, speech_index); // MBL 02.06.2020 + //if (house == NULL) { + // DLLExportClass::On_Speech(PlayerPtr, speech_index); + //} + //else + //{ + // DLLExportClass::On_Speech(house, speech_index); + //} +} + + +/*********************************************************************************************** + * Is_Speaking -- Checks to see if the eva voice is still playing. * + * * + * Call this routine when the EVA voice being played needs to be checked. A typical use * + * of this would be when some action needs to be delayed until the voice has finished -- * + * say the end of the game. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the EVA voice still playing? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1995 JLB : Created. * + *=============================================================================================*/ +bool Is_Speaking(void) +{ + Speak_AI(); + return AudMix_IsSourcePlaying(SPEECH_AUDIO_SOURCE); +} diff --git a/code/redalert/b64pipe.cpp b/code/redalert/b64pipe.cpp new file mode 100644 index 0000000..cefe2a4 --- /dev/null +++ b/code/redalert/b64pipe.cpp @@ -0,0 +1,162 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64PIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64pipe.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Pipe::Put -- Processes a block of data through the pipe. * + * * + * This will take the data submitted and either Base64 encode or decode it (as specified * + * in the pipe's constructor). The nature of Base64 encoding means that the data will * + * grow 30% in size when encoding and decrease by a like amount when decoding. * + * * + * INPUT: source -- Pointer to the data to be translated. * + * * + * length -- The number of bytes to translate. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final end of * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + if (Counter > 0) { + int len = (slen < (fromsize-Counter)) ? slen : (fromsize-Counter); + memmove(&from[Counter], source, len); + Counter += len; + slen -= len; + source = ((char *)source) + len; + + if (Counter == fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(from, fromsize, to, tosize); + } else { + outcount = Base64_Decode(from, fromsize, to, tosize); + } + total += Pipe::Put(to, outcount); + Counter = 0; + } + } + + while (slen >= fromsize) { + int outcount; + if (Control == ENCODE) { + outcount = Base64_Encode(source, fromsize, to, tosize); + } else { + outcount = Base64_Decode(source, fromsize, to, tosize); + } + source = ((char *)source) + fromsize; + total += Pipe::Put(to, outcount); + slen -= fromsize; + } + + if (slen > 0) { + memmove(from, source, slen); + Counter = slen; + } + + return(total); +} + + +/*********************************************************************************************** + * Base64Pipe::Flush -- Flushes the final pending data through the pipe. * + * * + * If there is any non-processed data accumulated in the holding buffer (quite likely when * + * encoding), then it will be processed and flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Pipe::Flush(void) +{ + int len = 0; + + if (Counter) { + if (Control == ENCODE) { + int chars = Base64_Encode(PBuffer, Counter, CBuffer, sizeof(CBuffer)); + len += Pipe::Put(CBuffer, chars); + } else { + int chars = Base64_Decode(CBuffer, Counter, PBuffer, sizeof(PBuffer)); + len += Pipe::Put(PBuffer, chars); + } + Counter = 0; + } + len += Pipe::Flush(); + return(len); +} + + + diff --git a/code/redalert/b64pipe.h b/code/redalert/b64pipe.h new file mode 100644 index 0000000..9b6af7e --- /dev/null +++ b/code/redalert/b64pipe.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64PIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64PIPE_H +#define B64PIPE_H + +#include "pipe.h" + +/* +** This class performs Base64 encoding/decoding to the data that is piped through. Note that +** encoded data will grow in size by about 30%. The reverse occurs when decoding. +*/ +class Base64Pipe : public Pipe +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Pipe(CodeControl control) : Control(control), Counter(0) {} + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Pipe(Base64Pipe & rvalue); + Base64Pipe & operator = (Base64Pipe const & pipe); +}; + +#endif diff --git a/code/redalert/b64straw.cpp b/code/redalert/b64straw.cpp new file mode 100644 index 0000000..d9283c5 --- /dev/null +++ b/code/redalert/b64straw.cpp @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64STRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "b64straw.h" +#include "base64.h" +#include + + +/*********************************************************************************************** + * Base64Straw::Get -- Fetch data and convert it to/from base 64 encoding. * + * * + * This routine will fetch the number of bytes requested and perform any conversion as * + * necessary upon the data. The nature of Base 64 encoding means that the data will * + * increase in size by 30% when encoding and decrease in like manner when decoding. * + * * + * INPUT: source -- The buffer to hold the processed data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number is less * + * than requested, then this indicates that the data stream has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Base64Straw::Get(void * source, int slen) +{ + int total = 0; + + char * from; + int fromsize; + char * to; + int tosize; + + if (Control == ENCODE) { + from = PBuffer; + fromsize = sizeof(PBuffer); + to = CBuffer; + tosize = sizeof(CBuffer); + } else { + from = CBuffer; + fromsize = sizeof(CBuffer); + to = PBuffer; + tosize = sizeof(PBuffer); + } + + /* + ** Process the byte request in code blocks until there are either + ** no more source bytes available or the request has been fulfilled. + */ + while (slen > 0) { + + /* + ** Transfer any processed bytes available to the request buffer. + */ + if (Counter > 0) { + int len = (slen < Counter) ? slen : Counter; + memmove(source, &to[tosize-Counter], len); + Counter -= len; + slen -= len; + source = ((char *)source) + len; + total += len; + } + if (slen == 0) break; + + /* + ** More bytes are needed, so fetch and process another base 64 block. + */ + int incount = Straw::Get(from, fromsize); + if (Control == ENCODE) { + Counter = Base64_Encode(from, incount, to, tosize); + } else { + Counter = Base64_Decode(from, incount, to, tosize); + } + if (Counter == 0) break; + } + + return(total); +} diff --git a/code/redalert/b64straw.h b/code/redalert/b64straw.h new file mode 100644 index 0000000..022ccef --- /dev/null +++ b/code/redalert/b64straw.h @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/B64STRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : B64STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef B64STRAW_H +#define B64STRAW_H + +#include "straw.h" + +/* +** Performs Base 64 encoding/decoding on the data that is drawn through the straw. Note that +** encoding increases the data size by about 30%. The reverse occurs when decoding. +*/ +class Base64Straw : public Straw +{ + public: + typedef enum CodeControl { + ENCODE, + DECODE + } CodeControl; + + Base64Straw(CodeControl control) : Control(control), Counter(0) {} + virtual int Get(void * source, int slen); + + private: + + /* + ** Indicates if this is for encoding or decoding of Base64 data. + */ + CodeControl Control; + + /* + ** The counter of the number of accumulated bytes pending for processing. + */ + int Counter; + + /* + ** Buffer that holds the Base64 coded bytes. This will be the staging buffer if + ** this is for a decoding process. Otherwise, it will be used as a scratch buffer. + */ + char CBuffer[4]; + + /* + ** Buffer that holds the plain bytes. This will be the staging buffer if this + ** is for an encoding process. Otherwise, it will be used as a scratch buffer. + */ + char PBuffer[3]; + + /* + ** Explicitly disable the copy constructor and the assignment operator. + */ + Base64Straw(Base64Straw & rvalue); + Base64Straw & operator = (Base64Straw const & pipe); +}; + + +#endif diff --git a/code/redalert/bar.cpp b/code/redalert/bar.cpp new file mode 100644 index 0000000..3e432c5 --- /dev/null +++ b/code/redalert/bar.cpp @@ -0,0 +1,236 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BAR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * ProgressBarClass::Set_Limit -- Set the logic tracking value. * + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "bar.h" +#include "fixed.h" + + +/*********************************************************************************************** + * ProgressBarClass::ProgressBarClass -- Constructor for the bargraph object. * + * * + * This is the constructor for the bargraph object. It establishes the dimensions and * + * coordinate of the bargraph as well as the colors it will use when drawn. * + * * + * INPUT: w,y -- Pixel coordinate of the upper left corner of the bargraph. * + * * + * width,height -- Dimensions of the bargraph. * + * * + * forecolor -- The color to use for the filled portion of the bargraph. * + * * + * backcolor -- The color to use for the non-filled portion of the bargraph. * + * * + * bordercolor -- Optional border color. If not zero, then the bargraph will be * + * outlined with this color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +ProgressBarClass::ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor) : + X(x), + Y(y), + Width(width), + Height(height), + BarColor(forecolor), + BackColor(backcolor), + BorderColor(bordercolor), + CurrentValue(0), + LastDisplayCurrent(0), + IsDrawn(false) +{ +} + + +/*********************************************************************************************** + * ProgressBarClass::Is_Horizontal -- Determines if the bargraph is horizontal or not. * + * * + * If the bargraph is oriented horizontally, then this function will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this bargraph horizontal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Is_Horizontal(void) const +{ + if (Width > Height) return(true); + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Update -- Update the value and redraw as necessary. * + * * + * This will update the value of the bargraph to the fill ratio specified and then * + * redraw it if required. Very small changes to the bargraph value might not result in a * + * visual change. * + * * + * INPUT: value -- The new value to assign to this bargraph. * + * * + * OUTPUT: none * + * * + * WARNINGS: bool; Did this update result in a redraw? * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +bool ProgressBarClass::Update(fixed value) +{ + CurrentValue = value; + + if (!IsDrawn || value - LastDisplayCurrent >= fixed(1, 10)) { + Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ProgressBarClass::Outline -- Draw an outline around the bargraph if supposed to. * + * * + * This routine will draw a border around the bargraph if this bargraph has a color * + * specified for the border. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Outline(void) const +{ + if (Is_Outlined()) { + LogicPage->Draw_Line(X, Y, X+Width, Y, BorderColor); + LogicPage->Draw_Line(X, Y, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X, Y+Height, X, Y+Height, BorderColor); + LogicPage->Draw_Line(X+Width, Y, X+Width, Y+Height, BorderColor); + } +} + + +/*********************************************************************************************** + * ProgressBarClass::Redraw -- Redraw the bargraph. * + * * + * This will redraw the entire bargraph. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/16/1996 JLB : Created. * + *=============================================================================================*/ +void ProgressBarClass::Redraw(void) const +{ + Hide_Mouse(); + + Outline(); + + /* + ** Determine the inner dimensions of the bargraph. This will be + ** somewhat smaller than indicated if it has a border. + */ + int x = X; + int y = Y; + int w = Width; + int h = Height; + if (Is_Outlined()) { + x += 1; + y += 1; + w -= 2; + h -= 2; + } + + /* + ** The working "length" of the bargraph is dependant on whether the + ** bargraph is horizontal or vertical. + */ + int size = Is_Horizontal() ? w : h; + + /* + ** Determine the number of pixels to fill in the bargraph depending on the + ** size of the internal value. The larger the internal value the more + ** filled the bargraph becomes. + */ + int fill = CurrentValue * size; + + /* + ** Draw the filled portion of the bargraph if there is any pixels to draw. + */ + if (fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x, y, x+fill, y+h, BarColor); + } else { + LogicPage->Fill_Rect(x, y+fill, x+w, y+h, BarColor); + } + } + + /* + ** Draw the unfilled portion of the bargraph if there are any pixels to + ** draw of it. + */ + if (w-fill > 0) { + if (Is_Horizontal()) { + LogicPage->Fill_Rect(x+fill, y, x+w, y+h, BackColor); + } else { + LogicPage->Fill_Rect(x, y, x+w, y+fill-1, BackColor); + } + } + + Show_Mouse(); + + ProgressBarClass * me = (ProgressBarClass *)this; + me->LastDisplayCurrent = CurrentValue; + me->IsDrawn = true; +} diff --git a/code/redalert/bar.h b/code/redalert/bar.h new file mode 100644 index 0000000..3e0ea79 --- /dev/null +++ b/code/redalert/bar.h @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BAR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/16/96 * + * * + * Last Update : August 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BAR_H +#define BAR_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include "fixed.h" + + +/* +** This is a manager for a progress (or other) bargraph. Such a graph consists of a fill +** and a background region. The fill percentage of the bargraph is controlled by an +** update value. The bargraph can be optionally outlined. +*/ +class ProgressBarClass +{ + public: + ProgressBarClass(int x, int y, int width, int height, int forecolor, int backcolor, int bordercolor=0); + + bool Update(fixed value); + void Redraw(void) const; + + private: + + void Outline(void) const; + bool Is_Horizontal(void) const; + bool Is_Outlined(void) const {return(BorderColor != 0);} + + /* + ** This is the upper left coordinates of the bargraph. + */ + int X,Y; + + /* + ** This is the dimensions of the bargraph. + */ + int Width, Height; + + /* + ** These are the colors to use when drawing the progress bar. + */ + int BarColor; + int BackColor; + int BorderColor; + + /* + ** This is the current value of the bargraph. + */ + fixed CurrentValue; + + /* + ** This is the current value as of the last time the bargraph was rendered. + */ + fixed LastDisplayCurrent; + + /* + ** If the bargraph has been drawn at least once, then this flag will + ** be true. + */ + unsigned IsDrawn:1; +}; + + +#endif diff --git a/code/redalert/base.cpp b/code/redalert/base.cpp new file mode 100644 index 0000000..14107df --- /dev/null +++ b/code/redalert/base.cpp @@ -0,0 +1,545 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * BaseClass::Load -- loads from a saved game file * + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * BaseClass::Read_INI -- INI reading routine * + * BaseClass::Save -- saves to a saved game file * + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * BaseNodeClass::operator != -- inequality operator * + * BaseNodeClass::operator == -- equality operator * + * BaseNodeClass::operator > -- greater-than operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BaseNodeClass::operator == -- equality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * true = equal, false = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator == (BaseNodeClass const & node) +{ + return(Type == node.Type && Cell == node.Cell); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator != -- inequality operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator !=(BaseNodeClass const & node) +{ + return(!(*this == node)); +} + + +/*********************************************************************************************** + * BaseNodeClass::operator > -- greater-than operator * + * * + * INPUT: * + * node node to test against * + * * + * OUTPUT: * + * comparison result * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int BaseNodeClass::operator > (BaseNodeClass const & ) +{ + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Load -- loads from a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to demand driven data source. * + *=============================================================================================*/ +bool BaseClass::Load(Straw & file) +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Read in & check the size of this class + */ + if (file.Get(&i, sizeof(i)) != sizeof(i)) { + return(false); + } + + if (i != sizeof(*this)) { + return(false); + } + + /* + ** Read in the House & the number of structures in the base + */ + if (file.Get(&House, sizeof(House)) != sizeof(House)) { + return(false); + } + + if (file.Get(&num_struct, sizeof(num_struct)) != sizeof(num_struct)) { + return(false); + } + + /* + ** Read each node entry & add it to the list + */ + for (i = 0; i < num_struct; i++) { + if (file.Get(&node, sizeof(node)) != sizeof(node)) { + return(false); + } + Nodes.Add(node); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Save -- saves to a saved game file * + * * + * INPUT: * + * file open file * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/04/1996 JLB : Converted to supply driven data output. * + *=============================================================================================*/ +bool BaseClass::Save(Pipe & file) const +{ + int num_struct; + int i; + BaseNodeClass node; + + /* + ** Write the size of this class + */ + i = sizeof(*this); + file.Put(&i, sizeof(i)); + + /* + ** Write the House & the number of structures in the base + */ + file.Put(&House, sizeof(House)); + + num_struct = Nodes.Count(); + file.Put(&num_struct, sizeof(num_struct)); + + /* + ** Write each node entry + */ + for (i = 0; i < num_struct; i++) { + node = Nodes[i]; + file.Put(&node, sizeof(node)); + } + + return(true); +} + + +/*********************************************************************************************** + * BaseClass::Is_Built -- Tells if given item in the list has been built yet * + * * + * INPUT: * + * index index into base list * + * * + * OUTPUT: * + * true = yes, false = no * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Built(int index) const +{ + if (Get_Building(index) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Building -- Returns ptr to the built building for the given node * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to already-built building, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 07/30/1996 JLB : Handle arbitrary overlapper list length. * + *=============================================================================================*/ +BuildingClass * BaseClass::Get_Building(int index) const +{ + ObjectClass * obj[1 + ARRAY_SIZE(Map[(CELL)0].Overlapper)]; + + /* + ** Check the location on the map where this building should be; if it's + ** there, return a pointer to it. + */ + CELL cell = Nodes[index].Cell; + + obj[0] = Map[cell].Cell_Building(); + int count = 1; + for (int xindex = 0; xindex < ARRAY_SIZE(Map[cell].Overlapper); xindex++) { + if (Map[cell].Overlapper[xindex] != NULL) { + obj[count++] = Map[cell].Overlapper[xindex]; + } + } + + BuildingClass * bldg = NULL; + for (int i = 0; i < count; i++) { + if (obj[i] && + Coord_Cell(obj[i]->Coord) == Nodes[index].Cell && + obj[i]->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)obj[i])->Class->Type == Nodes[index].Type) { + + bldg = (BuildingClass *)obj[i]; + break; + } + } + + return(bldg); +} + + +/*********************************************************************************************** + * BaseClass::Is_Node -- Tells if the given building is part of our base list * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * true = building is a node in the list, false = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +bool BaseClass::Is_Node(BuildingClass const * obj) +{ + if (Get_Node(obj) != NULL) { + return(true); + } else { + return(false); + } +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Returns ptr to the node corresponding to given object * + * * + * INPUT: * + * obj pointer to building to test * + * * + * OUTPUT: * + * ptr to node * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(BuildingClass const * obj) +{ + for (int i = 0; i < Nodes.Count(); i++) { + if (obj->Class->Type == Nodes[i].Type && Coord_Cell(obj->Coord) == Nodes[i].Cell) { + return(&Nodes[i]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Get_Node -- Finds the node that matches the cell specified. * + * * + * This routine is used to find a matching node the corresponds to the cell specified. * + * * + * INPUT: cell -- The cell to use in finding a match. * + * * + * OUTPUT: Returns a pointer to the matching node if found. If not found, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/12/1996 JLB : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Get_Node(CELL cell) +{ + for (int index = 0; index < Nodes.Count(); index++) { + if (cell == Nodes[index].Cell) { + return(&Nodes[index]); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Next_Buildable -- returns ptr to the next node that needs to be built * + * * + * If 'type' is not NONE, returns ptr to the next "hole" in the list of the given type. * + * Otherwise, returns ptr to the next hole in the list of any type. * + * * + * INPUT: * + * type type of building to check for * + * * + * OUTPUT: * + * ptr to a BaseNodeClass, NULL if none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +BaseNodeClass * BaseClass::Next_Buildable(StructType type) +{ + /* + ** Loop through all node entries, returning a pointer to the first + ** un-built one that matches the requested type. + */ + for (int i = 0; i < Nodes.Count(); i++) { + + /* + ** For STRUCT_NONE, return the first hole found + */ + if (type == STRUCT_NONE) { + if (!Is_Built(i)) { + return(&Nodes[i]); + } + + } else { + + /* + ** For a "real" building type, return the first hold for that type + */ + if (Nodes[i].Type==type && !Is_Built(i)) { + return(&Nodes[i]); + } + } + } + + +// If no entry could be found, then create a fake one that will allow +// placement of the building. Make it static and reuse the next time this +// routine is called. + + return(NULL); +} + + +/*********************************************************************************************** + * BaseClass::Read_INI -- INI reading routine * + * * + * INI entry format: * + * BLDG=COORDINATE * + * BLDG=COORDINATE * + * ... * + * * + * INPUT: * + * buffer pointer to loaded INI file * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routines assumes there is only one base defined for the scenario. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + * 02/20/1996 JLB : Fixed to know what house to build base from. * + *=============================================================================================*/ +void BaseClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; + char uname[10]; + BaseNodeClass node; // node to add to list + + Mono_Clear_Screen(); + /* + ** First, determine the house of the human player, and set the Base's house + ** accordingly. + */ + House = ini.Get_HousesType(INI_Name(), "Player", PlayerPtr->Class->House); + + /* + ** Read the number of buildings that will go into the base node list + */ + int count = ini.Get_Int(INI_Name(), "Count", 0); + + /* + ** Read each entry in turn, in the same order they were written out. + */ + for (int i = 0; i < count; i++) { + + /* + ** Get an INI entry + */ + sprintf(uname,"%03d",i); + ini.Get_String(INI_Name(), uname, NULL, buf, sizeof(buf)); + + /* + ** Set the node's building type + */ + node.Type = BuildingTypeClass::From_Name(strtok(buf,",")); + + /* + ** Read & set the node's coordinate + */ + node.Cell = atoi(strtok(NULL,",")); + + /* + ** Add this node to the Base's list + */ + Nodes.Add(node); + } +} + + +/*********************************************************************************************** + * BaseClass::Write_INI -- Writes all the base information to the INI database. * + * * + * Use this routine to write all prebuild base information to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: If there was any preexisting prebuild base data in the database, it will be * + * be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void BaseClass::Write_INI(CCINIClass & ini) +{ + /* + ** Clear out all existing base data from the ini file. + */ + ini.Clear(INI_Name()); + + if (House != HOUSE_NONE) { + + /* + ** Write out the owner of this buildable list. + */ + ini.Put_HousesType(INI_Name(), "Player", House); + + /* + ** Save the # of buildings in the Nodes list. This is essential because + ** they must be read in the same order they were created, so "000" must be + ** read first, etc. + */ + ini.Put_Int(INI_Name(), "Count", Nodes.Count()); + + /* + ** Write each entry into the INI + */ + for (int i = 0; i < Nodes.Count(); i++) { + char buf[128]; + char uname[10]; + + sprintf(uname,"%03d",i); + sprintf(buf,"%s,%d", + BuildingTypeClass::As_Reference(Nodes[i].Type).IniName, + Nodes[i].Cell); + + ini.Put_String(INI_Name(), uname, buf); + } + } +} diff --git a/code/redalert/base.h b/code/redalert/base.h new file mode 100644 index 0000000..1e3157c --- /dev/null +++ b/code/redalert/base.h @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 03/27/95 * + * * + * Last Update : March 27, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BASE_H +#define BASE_H + + +/**************************************************************************** +** This class defines one "node" in the pre-built base list. Each node +** contains a type of building to build, and the COORDINATE to build it at. +*/ +class BaseNodeClass +{ + public: + BaseNodeClass(void) {}; + BaseNodeClass(StructType building, CELL cell) : Type(building), Cell(cell) {}; + int operator == (BaseNodeClass const & node); + int operator != (BaseNodeClass const & node); + int operator > (BaseNodeClass const & node); + + StructType Type; + CELL Cell; +}; + + +/**************************************************************************** +** This is the class that defines a pre-built base for the computer AI. +** (Despite its name, this is NOT the "base" class for C&C's class hierarchy!) +*/ +class BaseClass +{ + public: + + /* + ** Constructor/Destructor + */ + BaseClass(void) {}; + virtual ~BaseClass() {Nodes.Clear();} + + /* + ** Initialization + */ + void Init(void) {House = HOUSE_NONE; Nodes.Clear();} + + /* + ** The standard suite of load/save support routines + */ + void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "Base";} + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void) {}; + virtual void Decode_Pointers(void) {}; + + /* + ** Tells if the given node has been built or not + */ + bool Is_Built(int index) const; + + /* + ** Returns a pointer to the object for the given node + */ + BuildingClass * Get_Building(int index) const; + + /* + ** Tells if the given building ptr is a node in this base's list. + */ + bool Is_Node(BuildingClass const * obj); + + /* + ** Returns a pointer to the requested node. + */ + BaseNodeClass * Get_Node(BuildingClass const * obj); + BaseNodeClass * Get_Node(int index) { return (&Nodes[index]); } + BaseNodeClass * Get_Node(CELL cell); + + /* + ** Returns a pointer to the next "hole" in the Nodes list. + */ + BaseNodeClass * Next_Buildable(StructType type = STRUCT_NONE); + + /* + ** This is the list of "nodes" that define the base. Portions of this + ** list can be pre-built by simply saving those buildings in the INI + ** along with non-base buildings, so Is_Built will return true for them. + */ + DynamicVectorClass Nodes; + + /* + ** This is the house this base belongs to. + */ + HousesType House; +}; + + +#endif + diff --git a/code/redalert/base64.cpp b/code/redalert/base64.cpp new file mode 100644 index 0000000..a8b1a22 --- /dev/null +++ b/code/redalert/base64.cpp @@ -0,0 +1,434 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE64.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE64.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * Base64_Encode -- Encode data into Base 64 format. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "base64.h" +#include + +/* +** This is the magic padding character used to fill out the encoded data to a multiple of +** 4 characters even though the source data is less than necessary to accomplish this. +** The pad character lets the decoder know of this condition and it will compensate +** accordingly. +*/ +static char const * const _pad = "="; + +/* +** This encoder translation table will convert a 6 bit number into an ASCII character. +*/ +static char const * const _encoder = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** The decoder translation table takes an ASCII character and converts it into a +** 6 bit number. +*/ +#define BAD 0xFE // Ignore this character in source data. +#define END 0xFF // Signifies premature end of input data. +static unsigned char const _decoder[256] = { + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,62,BAD,BAD,BAD,63, + 52,53,54,55,56,57,58,59,60,61,BAD,BAD,BAD,END,BAD,BAD, + BAD,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14, + 15,16,17,18,19,20,21,22,23,24,25,BAD,BAD,BAD,BAD,BAD, + BAD,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, + 41,42,43,44,45,46,47,48,49,50,51,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD, + BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD,BAD +}; + +int const PacketChars = 4; + + +/* +** The packet type is used to construct and disect the Base64 data blocks. The data +** consists of three source data bytes mapped onto four 6 bit Base64 code elements. +*/ +typedef union { + struct { +#ifdef BIG_ENDIAN + unsigned char C1; + unsigned char C2; + unsigned char C3; +#else + unsigned char C3; + unsigned char C2; + unsigned char C1; +#endif + unsigned char pad; + } Char; + struct { +#ifdef BIG_ENDIAN + unsigned O1:6; + unsigned O2:6; + unsigned O3:6; + unsigned O4:6; +#else + unsigned O4:6; + unsigned O3:6; + unsigned O2:6; + unsigned O1:6; +#endif + unsigned pad:8; + } SubCode; + unsigned int Raw; +} PacketType; + + +/*********************************************************************************************** + * Base64_Encode -- Encode data into Base 64 format. * + * * + * This will take an arbitrary length of source data and transform it into base 64 format * + * data. Base 64 format has the property of being very portable across text editors and * + * country character encoding schemes. As such it is ideal for e-mail. Note that the output * + * data will be about 33% larger than the source. * + * * + * INPUT: source -- Pointer to the source data to convert. * + * * + * slen -- The number of bytes to encode. * + * * + * dest -- Pointer to the destination buffer that will hold the encoded data. * + * * + * dlen -- The size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the encoded output. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Encode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + /* + ** Process the source data in blocks of three bytes. Fewer than three bytes + ** results in special padding output characters (automatically discarded + ** during the decode process). + */ + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen >= PacketChars) { + + /* + ** Fetch 24 bits of source data. + */ + PacketType packet; + + int pad = 0; + packet.Raw = 0; + packet.Char.C1 = *sptr++; + slen--; + if (slen) { + packet.Char.C2 = *sptr++; + slen--; + } else { + pad++; + } + if (slen) { + packet.Char.C3 = *sptr++; + slen--; + } else { + pad++; + } + + /* + ** Translate and write 4 characters of Base64 data. Pad with pad + ** characters if there is insufficient source data for a full packet. + */ + *dptr++ = _encoder[packet.SubCode.O1]; + *dptr++ = _encoder[packet.SubCode.O2]; + if (pad < 2) { + *dptr++ = _encoder[packet.SubCode.O3]; + } else { + *dptr++ = _pad[0]; + } + if (pad < 1) { + *dptr++ = _encoder[packet.SubCode.O4]; + } else { + *dptr++ = _pad[0]; + } + + dlen -= PacketChars; + total += PacketChars; + } + + /* + ** Add a trailing null as a courtesy measure. + */ + if (dlen > 0) { + *dptr = '\0'; + } + + /* + ** Return with the total number of characters in the output buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * Base64_Decode -- Decodes Base 64 data into its original data form. * + * * + * Use this routine to decode base 64 data back into the original data. A property of this * + * decode process is that unrecognized input characters are ignored. This allows mangled * + * source (filled with line breaks or spaces) to be correctly decoded. The decode process * + * terminates when the end of the source data has been reached or the special end of data * + * marker is encountered. * + * * + * INPUT: source -- Pointer to the source data to decode. * + * * + * slen -- The number of bytes in the source data buffer. * + * * + * dest -- Pointer to the destination buffer to be filled with the decoded data. * + * * + * dlen -- The maximum size of the destination buffer. * + * * + * OUTPUT: Returns with the number of bytes stored into the destination buffer. This will * + * always be less than the number of source bytes (usually by about 33%). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int Base64_Decode(void const * source, int slen, void * dest, int dlen) +{ + /* + ** Check the parameters for legality. + */ + if (source == NULL || slen == 0 || dest == NULL || dlen == 0) { + return(0); + } + + int total = 0; + unsigned char const * sptr = (unsigned char const *)source; + unsigned char * dptr = (unsigned char *)dest; + while (slen > 0 && dlen > 0) { + + PacketType packet; + packet.Raw = 0; + + /* + ** Process input until a full packet has been accumulated or the + ** source is exhausted. + */ + int pcount = 0; + while (pcount < PacketChars && slen > 0) { + unsigned char c = *sptr++; + slen--; + + unsigned char code = _decoder[c]; + + /* + ** An unrecognized character is skipped. + */ + if (code == BAD) continue; + + /* + ** The "=" character signifies the end of data regardless of what + ** the source buffer length value may be. + */ + if (code == END) { + slen = 0; + break; + } + + /* + ** A valid Base64 character was found so add it to the packet + ** data. + */ + switch (pcount) { + case 0: + packet.SubCode.O1 = code; + break; + case 1: + packet.SubCode.O2 = code; + break; + case 2: + packet.SubCode.O3 = code; + break; + case 3: + packet.SubCode.O4 = code; + break; + } + pcount++; + } + + /* + ** A packet block is ready for output into the destination buffer. + */ + *dptr++ = packet.Char.C1; + dlen--; + total++; + if (dlen > 0 && pcount > 2) { + *dptr++ = packet.Char.C2; + dlen--; + total++; + } + if (dlen > 0 && pcount > 3) { + *dptr++ = packet.Char.C3; + dlen--; + total++; + } + } + + /* + ** Return with the total number of characters decoded into the + ** output buffer. + */ + return(total); +} + + +/* +Base64 Content-Transfer-Encoding + +The Base64 Content-Transfer-Encoding is designed to represent arbitrary +sequences of octets in a form that need not be humanly readable. The encoding +and decoding algorithms are simple, but the encoded data are consistently +only about 33 percent larger than the unencoded data. This encoding is +virtually identical to the one used in Privacy Enhanced Mail (PEM) +applications, as defined in RFC 1421. The base64 encoding is adapted from +RFC 1421, with one change: base64 eliminates the "*" mechanism for embedded +clear text. + +A 65-character subset of US-ASCII is used, enabling 6 bits to be represented +per printable character. (The extra 65th character, "=", is used to signify a +special processing function.) + +NOTE: + This subset has the important property that it is represented identically + in all versions of ISO 646, including US ASCII, and all characters in the + subset are also represented identically in all versions of EBCDIC. Other + popular encodings, such as the encoding used by the uuencode utility and + the base85 encoding specified as part of Level 2 PostScript, do not share + these properties, and thus do not fulfill the portability requirements a + binary transport encoding for mail must meet. + +The encoding process represents 24-bit groups of input bits as output strings +of 4 encoded characters. Proceeding from left to right, a 24-bit input group is +formed by concatenating 3 8-bit input groups. These 24 bits are then treated as +4 concatenated 6-bit groups, each of which is translated into a single digit in +the base64 alphabet. When encoding a bit stream via the base64 encoding, the +bit stream must be presumed to be ordered with the most-significant-bit first. +That is, the first bit in the stream will be the high-order bit in the first +byte, and the eighth bit will be the low-order bit in the first byte, and so on. + +Each 6-bit group is used as an index into an array of 64 printable characters. +The character referenced by the index is placed in the output string. These +characters, identified in Table 1, below, are selected so as to be universally +representable, and the set excludes characters with particular significance to +SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries defined in this +document (e.g., "-"). + +Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + +The output stream (encoded bytes) must be represented in lines of no more than +76 characters each. All line breaks or other characters not found in Table 1 +must be ignored by decoding software. In base64 data, characters other than +those in Table 1, line breaks, and other white space probably indicate a +transmission error, about which a warning message or even a message rejection +might be appropriate under some circumstances. + +Special processing is performed if fewer than 24 bits are available at the end +of the data being encoded. A full encoding quantum is always completed at the +end of a body. When fewer than 24 input bits are available in an input group, +zero bits are added (on the right) to form an integral number of 6-bit groups. +Padding at the end of the data is performed using the '=' character. Since all +base64 input is an integral number of octets, only the following cases can +arise: (1) the final quantum of encoding input is an integral multiple of 24 +bits; here, the final unit of encoded output will be an integral multiple of 4 +characters with no "=" padding, (2) the final quantum of encoding input is +exactly 8 bits; here, the final unit of encoded output will be two characters +followed by two "=" padding characters, or (3) the final quantum of encoding +input is exactly 16 bits; here, the final unit of encoded output will be three +characters followed by one "=" padding character. + +Because it is used only for padding at the end of the data, the occurrence of +any '=' characters may be taken as evidence that the end of the data has been +reached (without truncation in transit). No such assurance is possible, +however, when the number of octets transmitted was a multiple of three. + +Any characters outside of the base64 alphabet are to be ignored in +base64-encoded data. The same applies to any illegal sequence of characters in +the base64 encoding, such as "=====" + +Care must be taken to use the proper octets for line breaks if base64 encoding +is applied directly to text material that has not been converted to canonical +form. In particular, text line breaks must be converted into CRLF sequences +prior to base64 encoding. The important thing to note is that this may be done +directly by the encoder rather than in a prior canonicalization step in some +implementations. + +NOTE: + There is no need to worry about quoting apparent encapsulation boundaries + within base64-encoded parts of multipart entities because no hyphen + characters are used in the base64 encoding. + +*/ \ No newline at end of file diff --git a/code/redalert/base64.h b/code/redalert/base64.h new file mode 100644 index 0000000..82cf1cb --- /dev/null +++ b/code/redalert/base64.h @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BASE64.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE64.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +int Base64_Encode(void const * source, int slen, void * dest, int dlen); +int Base64_Decode(void const * source, int slen, void * dest, int dlen); diff --git a/code/redalert/bbdata.cpp b/code/redalert/bbdata.cpp new file mode 100644 index 0000000..a3db318 --- /dev/null +++ b/code/redalert/bbdata.cpp @@ -0,0 +1,297 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BBDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BBDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specif* + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * BulletTypeClass::Load_Shapes -- Load shape data for bullet types. * + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + + +/*********************************************************************************************** + * BulletTypeClass::BulletTypeClass -- Constructor for bullet type objects. * + * * + * This is basically a constructor for static type objects used by bullets. All bullets * + * are of a type constructed by this routine at game initialization time. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 07/17/1996 JLB : Uses correct default values. * + *=============================================================================================*/ +BulletTypeClass::BulletTypeClass(char const * name) : + ObjectTypeClass( RTTI_BULLETTYPE, + BulletTypes.ID(this), + true, + true, + false, + false, + true, + true, + false, + TXT_NONE, + name + ), + IsHigh(false), + IsShadow(true), + IsArcing(false), + IsDropping(false), + IsInvisible(false), + IsProximityArmed(false), + IsFlameEquipped(false), + IsFueled(false), + IsFaceless(true), + IsInaccurate(false), + IsTranslucent(false), + IsAntiAircraft(false), + IsAntiGround(true), + IsAntiSub(false), + IsDegenerate(false), + IsSubSurface(false), + IsParachuted(false), + IsGigundo(false), + Type(BulletType(ID)), + ROT(0), + Arming(0), + Tumble(0) +{ +} + + +/*********************************************************************************************** + * BulletTypeClass::operator new -- Allocates a bullet type object from the special heap. * + * * + * This allocates a bullet type object from a special heap that is used just for * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to an allocated block or NULL if the allocation could not * + * occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BulletTypeClass::operator new(size_t) +{ + return(BulletTypes.Alloc()); +} + + +/*********************************************************************************************** + * BulletTypeClass::operator delete -- Deletes a bullet type object from the special heap. * + * * + * This is the counterpart to the operator new function for bullet type objects. It will * + * return the bullet type object back to the special heap used for bullet type object * + * allocation. * + * * + * INPUT: ptr -- Pointer to the bullet type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::operator delete(void * ptr) +{ + BulletTypes.Free((BulletTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BulletTypeClass::Init_Heap -- Initialize the heap objects for the bullet type. * + * * + * This performs any necessary initialization for the bullet types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::Init_Heap(void) +{ + /* + ** These bullet type class objects must be allocated in the exact order that they + ** are specified in the BulletType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BulletTypeClass("Invisible"); // BULLET_INVISIBLE + new BulletTypeClass("Cannon"); // BULLET_CANNON + new BulletTypeClass("Ack"); // BULLET_ACK + new BulletTypeClass("Torpedo"); // BULLET_TORPEDO + new BulletTypeClass("FROG"); // BULLET_FROG + new BulletTypeClass("HeatSeeker"); // BULLET_HEAT_SEEKER + new BulletTypeClass("LaserGuided"); // BULLET_LASER_GUIDED + new BulletTypeClass("Lobbed"); // BULLET_LOBBED + new BulletTypeClass("Bomblet"); // BULLET_BOMBLET + new BulletTypeClass("Ballistic"); // BULLET_BALLISTIC + new BulletTypeClass("Parachute"); // BULLET_PARACHUTE + new BulletTypeClass("Fireball"); // BULLET_FIREBALL + new BulletTypeClass("LeapDog"); // BULLET_DOG + new BulletTypeClass("Catapult"); // BULLET_CATAPULT + new BulletTypeClass("AAMissile"); // BULLET_AAMISSILE + new BulletTypeClass("GPSSatellite");// BULLET_GPS_SATELLITE + new BulletTypeClass("NukeUp"); // BULLET_NUKE_UP + new BulletTypeClass("NukeDown"); // BULLET_NUKE_DOWN +} + + +/*********************************************************************************************** + * BulletTypeClass::One_Time -- Performs the one time processing for bullets. * + * * + * This routine is used to perform any one time processing for the bullet type class. It * + * handles loading of the shape files. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called before any rendering of bullets occurs and should * + * only be called once. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BulletTypeClass::One_Time(void) +{ + /* + ** Load the bullet shapes. + */ + for (int index = BULLET_FIRST; index < BULLET_COUNT; index++) { + BulletTypeClass const & bullet = As_Reference((BulletType)index); + char fullname[_MAX_FNAME+_MAX_EXT]; + + if (!bullet.IsInvisible) { + _makepath(fullname, NULL, NULL, bullet.GraphicName, ".SHP"); + + #ifdef NDEBUG + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + #else + RawFileClass file(fullname); + + if (file.Is_Available()) { + ((void const *&)bullet.ImageData) = Load_Alloc_Data(file); + } else { + ((void const *&)bullet.ImageData) = MFCD::Retrieve(fullname); + } + #endif + } + } +} + + +/*********************************************************************************************** + * BulletTypeClass::As_Reference -- Returns with a reference to the bullet type object specifi * + * * + * Given a bullet type identifier, this routine will return a reference to the bullet type * + * object it refers to. * + * * + * INPUT: type -- The bullet type identifier to convert to a reference. * + * * + * OUTPUT: Returns with a reference to the bullet type object. * + * * + * WARNINGS: Make sure that the type parameter specified is a valid bullet type. If not, * + * then the results are undefined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletTypeClass & BulletTypeClass::As_Reference(BulletType type) +{ + return(*BulletTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BulletTypeClass::Read_INI -- Fetch the bullet type data from the INI database. * + * * + * Use this routine to fetch override information about this bullet type class object * + * from the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to examine. * + * * + * OUTPUT: bool; Was the section for this bullet found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + Arming = ini.Get_Int(Name(), "Arm", Arming); + ROT = ini.Get_Int(Name(), "ROT", ROT); + Tumble = ini.Get_Int(Name(), "Frames", Tumble); + IsHigh = ini.Get_Bool(Name(), "High", IsHigh); + IsShadow = ini.Get_Bool(Name(), "Shadow", IsShadow); + IsArcing = ini.Get_Bool(Name(), "Arcing", IsArcing); + IsDropping = ini.Get_Bool(Name(), "Dropping", IsDropping); + IsInvisible = ini.Get_Bool(Name(), "Inviso", IsInvisible); + IsProximityArmed = ini.Get_Bool(Name(), "Proximity", IsProximityArmed); + IsFlameEquipped = ini.Get_Bool(Name(), "Animates", IsFlameEquipped); + IsFueled = ini.Get_Bool(Name(), "Ranged", IsFueled); + IsInaccurate = ini.Get_Bool(Name(), "Inaccuate", IsInaccurate); + IsAntiAircraft = ini.Get_Bool(Name(), "AA", IsAntiAircraft); + IsAntiGround = ini.Get_Bool(Name(), "AG", IsAntiGround); + IsAntiSub = ini.Get_Bool(Name(), "ASW", IsAntiSub); + IsDegenerate = ini.Get_Bool(Name(), "Degenerates", IsDegenerate); + IsSubSurface = ini.Get_Bool(Name(), "UnderWater", IsSubSurface); + IsParachuted = ini.Get_Bool(Name(), "Parachuted", IsParachuted); + IsFaceless = !ini.Get_Bool(Name(), "Rotates", !IsFaceless); + IsTranslucent = ini.Get_Bool(Name(), "Translucent", IsTranslucent); + IsGigundo = ini.Get_Bool(Name(), "Gigundo", IsGigundo); + ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); + return(true); + } + return(false); +} diff --git a/code/redalert/bdata.cpp b/code/redalert/bdata.cpp new file mode 100644 index 0000000..0c21468 --- /dev/null +++ b/code/redalert/bdata.cpp @@ -0,0 +1,3941 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BDATA.CPP 2 3/03/97 10:37p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * BuildingTypeClass::Display -- Renders a generic view of building. * + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building * + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obj* + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding an object. * + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * BuildingTypeClass::Width -- Determines width of building in icons. * + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap* + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "Image.h" + +#define FATSHIP + +#define MCW MAP_CELL_W + +#define XYCELL(x,y) (y*MAP_CELL_W+x) +static short const ExitPyle[] = { + XYCELL(1,2), + XYCELL(2,2), + XYCELL(0,2), + XYCELL(-1,2), + XYCELL(-1,-1), + XYCELL(0,-1), + XYCELL(1,-1), + XYCELL(2,-1), + XYCELL(2,-1), + XYCELL(-1,0), + XYCELL(2,0), + XYCELL(2,1), + XYCELL(-1,1), + REFRESH_EOL +}; + +static short const ExitSub[] = { + XYCELL( 0, 2), + XYCELL( 2, 2), + XYCELL(-1, 2), + XYCELL( 1, 2), + XYCELL( 3, 2) +}; + +static short const ExitWeap[] = { + XYCELL(1,2), + XYCELL(-1,3), + XYCELL(0,3), + XYCELL(1,3), + XYCELL(-2,3), + XYCELL(2,3), + REFRESH_EOL +}; + +static short const ComList[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List000111111[] = {(MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List0010[] = {MCW, REFRESH_EOL}; +static short const List0011[] = {(MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List010111100[] = {1, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), REFRESH_EOL}; +static short const List0111[] = {1, (MCW*1), (MCW*1)+1, REFRESH_EOL}; +static short const List1000[] = {0, REFRESH_EOL}; +static short const List101000011[] = {0, 2, (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; +static short const List1100[] = {0, 1, REFRESH_EOL}; +static short const List1101[] = {0, 1, (MCW*1)+1, REFRESH_EOL}; +static short const List11[] = {0, 1, REFRESH_EOL}; +static short const List12[] = {MCW, REFRESH_EOL}; +static short const List1[] = {0, REFRESH_EOL}; +static short const List21[] = {0, 1, REFRESH_EOL}; +static short const List22[] = {0, 1, MCW, MCW+1, REFRESH_EOL}; +static short const List22_0011[] = {MCW, MCW+1, REFRESH_EOL}; +static short const List22_1100[] = {0, 1, REFRESH_EOL}; +static short const List2[] = {0, 1, MCW+1, MCW, REFRESH_EOL}; +static short const List32[] = {0, 1, 2, MCW, MCW+1, MCW+2, REFRESH_EOL}; +//static short const List42[] = {0, 1, 2, 3, MCW, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const ListFix[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const ListWeap[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, REFRESH_EOL}; +static short const ListWestwood[] = {1, 2, 3, MCW+1, MCW+2, MCW+3, REFRESH_EOL}; +static short const OListSAM[] = {-MCW, -(MCW-1), REFRESH_EOL}; +#ifdef FATSHIP +static short const ListSPen[] = {0, 1, 2, MCW, MCW+1, MCW+2, MCW+MCW, MCW+MCW+1, MCW+MCW+2, REFRESH_EOL}; +static short const OListSPen[] = {REFRESH_EOL}; +#else +static short const ListSPen[] = {1, MCW, MCW+1, MCW+2, MCW+MCW+1, REFRESH_EOL}; +static short const OListSPen[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +#endif +static short const OListWestwood[] = {0, MCW, REFRESH_EOL}; +static short const StoreList[] = {0, REFRESH_EOL}; + +static short const ListFactory[] = {0, 1, 2, (MCW*1), (MCW*1)+1, (MCW*1)+2, (MCW*2), (MCW*2)+1, (MCW*2)+2, REFRESH_EOL}; + +static short const OListFix[] = {0, 2, MCW+MCW, MCW+MCW+2, REFRESH_EOL}; +static short const OListWeap[] = {REFRESH_EOL}; +static short const OComList[] = {1, REFRESH_EOL}; +static short const OList12[] = {0, REFRESH_EOL}; +static short const OListTmpl[] = {0, 1, 2, REFRESH_EOL}; + +#ifdef RA_AF +#include "BDATA_AF.H" +#endif + +/*************************************************************************** +*/ +static BuildingTypeClass const ClassBarrel( + STRUCT_BARREL, + TXT_BARREL, // NAME: Short name of the structure. + "BARL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarrel3( + STRUCT_BARREL3, + TXT_BARREL, // NAME: Short name of the structure. + "BRL3", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAVMine( + STRUCT_AVMINE, + TXT_AVMINE, // NAME: Short name of the structure. + "MINV", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAPMine( + STRUCT_APMINE, + TXT_APMINE, // NAME: Short name of the structure. + "MINP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NORMAL, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + false, // Can the player select this? + false, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassIronCurtain( + STRUCT_IRON_CURTAIN, + TXT_IRON_CURTAIN, // NAME: Short name of the structure. + "IRON", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassForwardCom( + STRUCT_FORWARD_COM, + TXT_FORWARD_COM, // NAME: Short name of the structure. + "FCOM", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22_0011,// OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedTech( + STRUCT_ADVANCED_TECH, + TXT_ADVANCED_TECH, // NAME: Short name of the structure. + "ATEK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassChronosphere( + STRUCT_CHRONOSPHERE, + TXT_CHRONOSPHERE, // NAME: Short name of the structure. + "PDOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassWeapon( + STRUCT_WEAP, + TXT_WEAPON_FACTORY, // NAME: Short name of the structure. + "WEAP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XY_Coord(CELL_LEPTON_W+(CELL_LEPTON_W/2), CELL_LEPTON_H), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_UNITTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassShipYard( + STRUCT_SHIP_YARD, + TXT_SHIP_YARD, // NAME: Short name of the structure. + "SYRD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSubPen( + STRUCT_SUB_PEN, + TXT_SUB_PEN, // NAME: Short name of the structure. + "SPEN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_VESSELTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPillbox( + STRUCT_PILLBOX, + TXT_PILLBOX, // NAME: Short name of the structure. + "PBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCamoPillbox( + STRUCT_CAMOPILLBOX, + TXT_CAMOPILLBOX, // NAME: Short name of the structure. + "HBOX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0010, // Vertical offset. + 0x0040, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTesla( + STRUCT_TESLA, + TXT_TESLA, // NAME: Short name of the structure. + "TSLA", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x00C8, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTurret( + STRUCT_TURRET, + TXT_TURRET, // NAME: Short name of the structure. + "GUN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + (DirType)208, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAAGun( + STRUCT_AAGUN, + TXT_AAGUN, // NAME: Short name of the structure. + "AGUN", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_NE, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List12, // OCCUPYLIST: List of active foundation squares. + (short const *)OList12 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFlameTurret( + STRUCT_FLAME_TURRET, + TXT_FLAME_TURRET, // NAME: Short name of the structure. + "FTUR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassConst( + STRUCT_CONST, + TXT_CONST_YARD, // NAME: Short name of the structure. + "FACT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_BUILDINGTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeConst( + STRUCT_FAKECONST, + TXT_FAKE_CONST, // NAME: Short name of the structure. + "FACF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFactory,// OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeWeapon( + STRUCT_FAKEWEAP, + TXT_FAKE_WEAP, // NAME: Short name of the structure. + "WEAF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(10+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*3)-(CELL_PIXEL_H/2))-21), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListWeap, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWeap // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRefinery( + STRUCT_REFINERY, + TXT_REFINERY, // NAME: Short name of the structure. + "PROC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List010111100, // OCCUPYLIST: List of active foundation squares. + (short const *)List101000011 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassStorage( + STRUCT_STORAGE, + TXT_STORAGE, // NAME: Short name of the structure. + "SILO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)StoreList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHelipad( + STRUCT_HELIPAD, + TXT_HELIPAD, // NAME: Short name of the structure. + "HPAD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassCommand( + STRUCT_RADAR, + TXT_COMMAND, // NAME: Short name of the structure. + "DOME", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassGapGenerator( + STRUCT_GAP, + TXT_GAP_GENERATOR, // NAME: Short name of the structure. + "GAP", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_12, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0010, // OCCUPYLIST: List of active foundation squares. + (short const *)List1 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSAM( + STRUCT_SAM, + TXT_SAM, // NAME: Short name of the structure. + "SAM", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0030, // Vertical offset. + 0x0080, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + true, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassMissileSilo( + STRUCT_MSLO, + TXT_MSLO, // NAME: Short name of the structure. + "MSLO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List21, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSAM // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAirStrip( + STRUCT_AIRSTRIP, + TXT_AIRSTRIP, // NAME: Short name of the structure. + "AFLD", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_AIRCRAFTTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassPower( + STRUCT_POWER, + TXT_POWER, // NAME: Short name of the structure. + "POWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + (short const *)List22_1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassAdvancedPower( + STRUCT_ADVANCED_POWER, + TXT_ADVANCED_POWER, // NAME: Short name of the structure. + "APWR", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassSovietTech( + STRUCT_SOVIET_TECH, + TXT_SOVIET_TECH, // NAME: Short name of the structure. + "STEK", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List000111111, // OCCUPYLIST: List of active foundation squares. + (short const *)OListTmpl // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassHospital( + STRUCT_HOSPITAL, + TXT_HOSPITAL, // NAME: Short name of the structure. + "HOSP", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBioLab( + STRUCT_BIO_LAB, + TXT_BIO_LAB, // NAME: Short name of the structure. + "BIO", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List2, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassBarracks( + STRUCT_BARRACKS, + TXT_BARRACKS, // NAME: Short name of the structure. + "BARR", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. +// XYP_COORD(24,47), // Exit point for produced units. + XYP_COORD(18,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassTent( + STRUCT_TENT, + TXT_BARRACKS, // NAME: Short name of the structure. + "TENT", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List22, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassKennel( + STRUCT_KENNEL, + TXT_KENNEL, // NAME: Short name of the structure. + "KENN", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(8,16), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_INFANTRYTYPE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. +// (short const *)ExitPyle, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeShipYard( + STRUCT_FAKE_YARD, + TXT_FAKE_YARD, // NAME: Short name of the structure. + "SYRF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitWeap, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeSubPen( + STRUCT_FAKE_PEN, + TXT_FAKE_PEN, // NAME: Short name of the structure. + "SPEF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(22+(CELL_PIXEL_W/2), ((CELL_PIXEL_H*2)-(CELL_PIXEL_H/2))), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + (short const *)ExitSub, // Preferred exit cell list. + (short const *)ListSPen, // OCCUPYLIST: List of active foundation squares. + (short const *)OListSPen // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassFakeCommand( + STRUCT_FAKE_RADAR, + TXT_FAKE_RADAR, // NAME: Short name of the structure. + "DOMF", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + true, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ComList, // OCCUPYLIST: List of active foundation squares. + (short const *)OComList // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassRepair( + STRUCT_REPAIR, + TXT_FIX_IT, // NAME: Short name of the structure. + "FIX", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + false, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_33, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListFix, // OCCUPYLIST: List of active foundation squares. + (short const *)OListFix // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV01( + STRUCT_V01, + TXT_CIV1, // NAME: Short name of the structure. + "V01", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV02( + STRUCT_V02, + TXT_CIV2, // NAME: Short name of the structure. + "V02", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV03( + STRUCT_V03, + TXT_CIV3, // NAME: Short name of the structure. + "V03", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV04( + STRUCT_V04, + TXT_CIV4, // NAME: Short name of the structure. + "V04", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV05( + STRUCT_V05, + TXT_CIV5, // NAME: Short name of the structure. + "V05", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV06( + STRUCT_V06, + TXT_CIV6, // NAME: Short name of the structure. + "V06", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV07( + STRUCT_V07, + TXT_CIV7, // NAME: Short name of the structure. + "V07", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV08( + STRUCT_V08, + TXT_CIV8, // NAME: Short name of the structure. + "V08", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV09( + STRUCT_V09, + TXT_CIV9, // NAME: Short name of the structure. + "V09", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV10( + STRUCT_V10, + TXT_CIV10, // NAME: Short name of the structure. + "V10", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV11( + STRUCT_V11, + TXT_CIV11, // NAME: Short name of the structure. + "V11", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV12( + STRUCT_V12, + TXT_CIV12, // NAME: Short name of the structure. + "V12", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV13( + STRUCT_V13, + TXT_CIV13, // NAME: Short name of the structure. + "V13", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV14( + STRUCT_V14, + TXT_CIV14, // NAME: Short name of the structure. + "V14", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV15( + STRUCT_V15, + TXT_CIV15, // NAME: Short name of the structure. + "V15", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV16( + STRUCT_V16, + TXT_CIV16, // NAME: Short name of the structure. + "V16", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV17( + STRUCT_V17, + TXT_CIV17, // NAME: Short name of the structure. + "V17", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV18( + STRUCT_V18, + TXT_CIV18, // NAME: Short name of the structure. + "V18", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV19( + STRUCT_PUMP, + TXT_PUMP, // NAME: Short name of the structure. + "V19", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV20( + STRUCT_V20, + TXT_CIV20, // NAME: Short name of the structure. + "V20", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV21( + STRUCT_V21, + TXT_CIV21, // NAME: Short name of the structure. + "V21", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1101, // OCCUPYLIST: List of active foundation squares. + (short const *)List0010 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV22( + STRUCT_V22, + TXT_CIV22, // NAME: Short name of the structure. + "V22", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV23( + STRUCT_V23, + TXT_CIV23, // NAME: Short name of the structure. + "V23", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV24( + STRUCT_V24, + TXT_CIV24, // NAME: Short name of the structure. + "V24", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0011, // OCCUPYLIST: List of active foundation squares. + (short const *)List1100 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV25( + STRUCT_V25, + TXT_CIV25, // NAME: Short name of the structure. + "V25", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List0111, // OCCUPYLIST: List of active foundation squares. + (short const *)List1000 // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV26( + STRUCT_V26, + TXT_CIV26, // NAME: Short name of the structure. + "V26", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV27( + STRUCT_V27, + TXT_CIV27, // NAME: Short name of the structure. + "V27", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV28( + STRUCT_V28, + TXT_CIV28, // NAME: Short name of the structure. + "V28", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV29( + STRUCT_V29, + TXT_CIV29, // NAME: Short name of the structure. + "V29", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV30( + STRUCT_V30, + TXT_CIV30, // NAME: Short name of the structure. + "V30", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV31( + STRUCT_V31, + TXT_CIV31, // NAME: Short name of the structure. + "V31", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV32( + STRUCT_V32, + TXT_CIV32, // NAME: Short name of the structure. + "V32", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV33( + STRUCT_V33, + TXT_CIV33, // NAME: Short name of the structure. + "V33", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV34( + STRUCT_V34, + TXT_CIV34, // NAME: Short name of the structure. + "V34", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV35( + STRUCT_V35, + TXT_CIV35, // NAME: Short name of the structure. + "V35", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +static BuildingTypeClass const ClassV36( + STRUCT_V36, + TXT_CIV36, // NAME: Short name of the structure. + "V36", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassV37( + STRUCT_V37, + TXT_CIV37, // NAME: Short name of the structure. + "V37", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_42, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)ListWestwood, // OCCUPYLIST: List of active foundation squares. + (short const *)OListWestwood // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassMission( + STRUCT_MISSION, + TXT_CIVMISS, // NAME: Short name of the structure. + "MISS", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_32, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List32, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + +// Sandbag wall +static BuildingTypeClass const Sandbag( + STRUCT_SANDBAG_WALL, + TXT_SANDBAG_WALL, // NAME: Short name of the structure. + "SBAG", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Cyclone fence +static BuildingTypeClass const Cyclone( + STRUCT_CYCLONE_WALL, + TXT_CYCLONE_WALL, // NAME: Short name of the structure. + "CYCL", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Brick wall +static BuildingTypeClass const Brick( + STRUCT_BRICK_WALL, + TXT_BRICK_WALL, // NAME: Short name of the structure. + "BRIK", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Barbwire wall +static BuildingTypeClass const Barbwire( + STRUCT_BARBWIRE_WALL, + TXT_BARBWIRE_WALL, // NAME: Short name of the structure. + "BARB", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +// Wood wall +static BuildingTypeClass const Wood( + STRUCT_WOOD_WALL, + TXT_WOOD_WALL, // NAME: Short name of the structure. + "WOOD", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const Fence( + STRUCT_FENCE, + TXT_FENCE, // NAME: Short name of the structure. + "FENC", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_NONE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + true, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + false, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); + + +#ifdef FIXIT_ANTS +static BuildingTypeClass const ClassQueen( + STRUCT_QUEEN, + TXT_NONE, // NAME: Short name of the structure. + "QUEE", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(24,47), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + false, // Simple (one frame) damage imagery? + false, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + false, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + true, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_21, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List11, // OCCUPYLIST: List of active foundation squares. + NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva1( + STRUCT_LARVA1, + TXT_NONE, // NAME: Short name of the structure. + "LAR1", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +static BuildingTypeClass const ClassLarva2( + STRUCT_LARVA2, + TXT_NONE, // NAME: Short name of the structure. + "LAR2", // NAME: Short name of the structure. + FACING_NONE, // Foundation direction from center of building. + XYP_COORD(0,0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + false, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + false, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_11, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const *)List1, // OCCUPYLIST: List of active foundation squares. + (short const *)NULL // OVERLAPLIST:List of overlap cell offset. +); +#endif +void const * BuildingTypeClass::WarFactoryOverlay; +void const * LightningShapes; + + +/*********************************************************************************************** + * BuildingTypeClass::BuildingTypeClass -- This is the constructor for the building types. * + * * + * This is the constructor used to create the building types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass::BuildingTypeClass( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap) : + TechnoTypeClass(RTTI_BUILDINGTYPE, + int(type), + name, + ininame, + remap, + verticaloffset, + primaryoffset, + primarylateral, + primaryoffset, + primarylateral, + is_nominal, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + false, + is_theater, + is_turret_equipped, + is_remappable, + true, + (is_turret_equipped ? 32 : 1), + SPEED_NONE), + IsBase(true), + IsFake(is_fake), + IsBibbed(false), + IsWall(is_wall), + IsSimpleDamage(is_simpledamage), + IsCaptureable(false), + IsRegulated(is_regulated), + IsPowered(false), + IsUnsellable(false), + FoundationFace(foundation), + Adjacent(1), + ToBuild(tobuild), + ExitCoordinate(exitpoint), + ExitList(exitlist), + Type(type), + StartFace(sframe), + Capacity(0), + Power(0), + Drain(0), + Size(size), + OccupyList(sizelist), + OverlapList(overlap), + BuildupData(0) +{ + + Anims[BSTATE_CONSTRUCTION].Start = 0; + Anims[BSTATE_CONSTRUCTION].Count = 1; + Anims[BSTATE_CONSTRUCTION].Rate = 0; + + Anims[BSTATE_IDLE].Start = 0; + Anims[BSTATE_IDLE].Count = 1; + Anims[BSTATE_IDLE].Rate = 0; + + Anims[BSTATE_ACTIVE].Start = 0; + Anims[BSTATE_ACTIVE].Count = 1; + Anims[BSTATE_ACTIVE].Rate = 0; + + Anims[BSTATE_AUX1].Start = 0; + Anims[BSTATE_AUX1].Count = 1; + Anims[BSTATE_AUX1].Rate = 0; + + Anims[BSTATE_AUX2].Start = 0; + Anims[BSTATE_AUX2].Count = 1; + Anims[BSTATE_AUX2].Rate = 0; +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator new -- Allocates a building type object from the special heap. * + * * + * This routine will allocate a building type object from the special heap used just for * + * allocation of object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated object. If the allocation could not * + * succeed, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * BuildingTypeClass::operator new(size_t) +{ + return(BuildingTypes.Alloc()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::operator delete -- Deletes a building type object from the special heap. * + * * + * This will delete a previously allocated building type object. The memory is returned * + * to the special heap that is used for that purpose. * + * * + * INPUT: ptr -- Pointer to the building type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::operator delete(void * ptr) +{ + BuildingTypes.Free((BuildingTypeClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Heap -- Initialize the heap as necessary for the building type obje * + * * + * This routine performs the necessary heap initializations. Since we know exactly what * + * building type objects will be needed, they are pre-allocated at this time. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Heap(void) +{ + /* + ** These building type class objects must be allocated in the exact order that they + ** are specified in the StructType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new BuildingTypeClass(ClassAdvancedTech); // STRUCT_ADVANCED_TECH + new BuildingTypeClass(ClassIronCurtain); // STRUCT_IRON_CURTAIN + new BuildingTypeClass(ClassWeapon); // STRUCT_WEAP + new BuildingTypeClass(ClassChronosphere); // STRUCT_CHRONOSPHERE + new BuildingTypeClass(ClassPillbox); // STRUCT_PILLBOX + new BuildingTypeClass(ClassCamoPillbox); // STRUCT_CAMOPILLBOX + new BuildingTypeClass(ClassCommand); // STRUCT_RADAR + new BuildingTypeClass(ClassGapGenerator); // STRUCT_GAP + new BuildingTypeClass(ClassTurret); // STRUCT_TURRET + new BuildingTypeClass(ClassAAGun); // STRUCT_AAGUN + new BuildingTypeClass(ClassFlameTurret); // STRUCT_FLAME_TURRET + new BuildingTypeClass(ClassConst); // STRUCT_CONST + new BuildingTypeClass(ClassRefinery); // STRUCT_REFINERY + new BuildingTypeClass(ClassStorage); // STRUCT_STORAGE + new BuildingTypeClass(ClassHelipad); // STRUCT_HELIPAD + new BuildingTypeClass(ClassSAM); // STRUCT_SAM + new BuildingTypeClass(ClassAirStrip); // STRUCT_AIRSTRIP + new BuildingTypeClass(ClassPower); // STRUCT_POWER + new BuildingTypeClass(ClassAdvancedPower);// STRUCT_ADVANCED_POWER + new BuildingTypeClass(ClassSovietTech); // STRUCT_SOVIET_TECH + new BuildingTypeClass(ClassHospital); // STRUCT_HOSPITAL + new BuildingTypeClass(ClassBarracks); // STRUCT_BARRACKS + new BuildingTypeClass(ClassTent); // STRUCT_TENT + new BuildingTypeClass(ClassKennel); // STRUCT_KENNEL + new BuildingTypeClass(ClassRepair); // STRUCT_REPAIR + new BuildingTypeClass(ClassBioLab); // STRUCT_BIO_LAB + new BuildingTypeClass(ClassMission); // STRUCT_MISSION + new BuildingTypeClass(ClassShipYard); // STRUCT_SHIP_YARD + new BuildingTypeClass(ClassSubPen); // STRUCT_SUB_PEN + new BuildingTypeClass(ClassMissileSilo); // STRUCT_MSLO + new BuildingTypeClass(ClassForwardCom); // STRUCT_FORWARD_COM + new BuildingTypeClass(ClassTesla); // STRUCT_TESLA + new BuildingTypeClass(ClassFakeWeapon); // STRUCT_FAKEWEAP + new BuildingTypeClass(ClassFakeConst); // STRUCT_FAKECONST + new BuildingTypeClass(ClassFakeShipYard); // STRUCT_FAKE_YARD + new BuildingTypeClass(ClassFakeSubPen); // STRUCT_FAKE_PEN + new BuildingTypeClass(ClassFakeCommand); // STRUCT_FAKE_RADAR + new BuildingTypeClass(Sandbag); // STRUCT_SANDBAG_WALL + new BuildingTypeClass(Cyclone); // STRUCT_CYCLONE_WALL + new BuildingTypeClass(Brick); // STRUCT_BRICK_WALL + new BuildingTypeClass(Barbwire); // STRUCT_BARBWIRE_WALL + new BuildingTypeClass(Wood); // STRUCT_WOOD_WALL + new BuildingTypeClass(Fence); // STRUCT_FENCE + new BuildingTypeClass(ClassAVMine); // STRUCT_AVMINE + new BuildingTypeClass(ClassAPMine); // STRUCT_APMINE + new BuildingTypeClass(ClassV01); // STRUCT_V1 + new BuildingTypeClass(ClassV02); // STRUCT_V2 + new BuildingTypeClass(ClassV03); // STRUCT_V3 + new BuildingTypeClass(ClassV04); // STRUCT_V4 + new BuildingTypeClass(ClassV05); // STRUCT_V5 + new BuildingTypeClass(ClassV06); // STRUCT_V6 + new BuildingTypeClass(ClassV07); // STRUCT_V7 + new BuildingTypeClass(ClassV08); // STRUCT_V8 + new BuildingTypeClass(ClassV09); // STRUCT_V9 + new BuildingTypeClass(ClassV10); // STRUCT_V10 + new BuildingTypeClass(ClassV11); // STRUCT_V11 + new BuildingTypeClass(ClassV12); // STRUCT_V12 + new BuildingTypeClass(ClassV13); // STRUCT_V13 + new BuildingTypeClass(ClassV14); // STRUCT_V14 + new BuildingTypeClass(ClassV15); // STRUCT_V15 + new BuildingTypeClass(ClassV16); // STRUCT_V16 + new BuildingTypeClass(ClassV17); // STRUCT_V17 + new BuildingTypeClass(ClassV18); // STRUCT_V18 + new BuildingTypeClass(ClassV19); // STRUCT_PUMP + new BuildingTypeClass(ClassV20); // STRUCT_V20 + new BuildingTypeClass(ClassV21); // STRUCT_V21 + new BuildingTypeClass(ClassV22); // STRUCT_V22 + new BuildingTypeClass(ClassV23); // STRUCT_V23 + new BuildingTypeClass(ClassV24); // STRUCT_V24 + new BuildingTypeClass(ClassV25); // STRUCT_V25 + new BuildingTypeClass(ClassV26); // STRUCT_V26 + new BuildingTypeClass(ClassV27); // STRUCT_V27 + new BuildingTypeClass(ClassV28); // STRUCT_V28 + new BuildingTypeClass(ClassV29); // STRUCT_V29 + new BuildingTypeClass(ClassV30); // STRUCT_V30 + new BuildingTypeClass(ClassV31); // STRUCT_V31 + new BuildingTypeClass(ClassV32); // STRUCT_V32 + new BuildingTypeClass(ClassV33); // STRUCT_V33 + new BuildingTypeClass(ClassV34); // STRUCT_V34 + new BuildingTypeClass(ClassV35); // STRUCT_V35 + new BuildingTypeClass(ClassV36); // STRUCT_V36 + new BuildingTypeClass(ClassV37); // STRUCT_V37 + new BuildingTypeClass(ClassBarrel); // STRUCT_BARREL + new BuildingTypeClass(ClassBarrel3); // STRUCT_BARREL3 + +#ifdef FIXIT_ANTS + new BuildingTypeClass(ClassQueen); // STRUCT_QUEEN + new BuildingTypeClass(ClassLarva1); // STRUCT_LARVA1 + new BuildingTypeClass(ClassLarva2); // STRUCT_LARVA2 +#endif + +#ifdef RA_AF + new BuildingTypeClass(ClassAFV01); // STRUCT_AFV01 +#endif +} + + +/*********************************************************************************************** + * BuildingTypeClass::One_Time -- Performs special one time action for buildings. * + * * + * This routine is used to do the one time action necessary to handle building type class * + * objects. This entails loading of the building shapes and the brain file used by * + * buildings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should only be called ONCE. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/11/1994 JLB : Updated construction time and frame count logic. * + *=============================================================================================*/ +void BuildingTypeClass::One_Time(void) +{ + static const struct { + StructType Class; // Building class number. + BStateType Stage; // Animation sequence to assign animation range to. + int Start; // Starting frame number. + int Length; // Number of frames (-1 means use all frames). + int Rate; // Rate of animation. + } _anims[] = { + {STRUCT_CHRONOSPHERE, BSTATE_IDLE, 0, 4, 3}, // idling + {STRUCT_CHRONOSPHERE, BSTATE_ACTIVE, 4, 16,3}, // charging up and activating + {STRUCT_MSLO, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_MSLO, BSTATE_ACTIVE, 0, 5, 2}, // door opening + {STRUCT_MSLO, BSTATE_AUX1, 4, 1, 0}, // door held open + {STRUCT_MSLO, BSTATE_AUX2, 5, 3, 2}, // door closing + {STRUCT_CAMOPILLBOX, BSTATE_ACTIVE, 0, 2, 1}, + {STRUCT_GAP, BSTATE_IDLE, 0, 32,3}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_AIRSTRIP, BSTATE_AUX1, 0, 8, 3}, + {STRUCT_BARRACKS, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_BARRACKS, BSTATE_IDLE, 0, 10,3}, + {STRUCT_TENT, BSTATE_ACTIVE, 0, 10,3}, + {STRUCT_TENT, BSTATE_IDLE, 0, 10,3}, +#ifdef FIXIT_ANTS + {STRUCT_QUEEN, BSTATE_IDLE, 0, 10,3}, +#endif + {STRUCT_CONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_FAKECONST, BSTATE_ACTIVE, 0, 26,3}, + {STRUCT_HELIPAD, BSTATE_ACTIVE, 0, 7, 4}, + {STRUCT_HELIPAD, BSTATE_IDLE, 0, 0, 0}, + {STRUCT_HOSPITAL, BSTATE_IDLE, 0, 4, 3}, + {STRUCT_PUMP, BSTATE_IDLE, 0, 14,4}, + {STRUCT_REPAIR, BSTATE_ACTIVE, 0, 7, 2}, + {STRUCT_REPAIR, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_V20, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V21, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V22, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_V23, BSTATE_IDLE, 0, 3, 3}, + {STRUCT_WEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_WEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_ACTIVE, 0, 1, 0}, + {STRUCT_FAKEWEAP, BSTATE_IDLE, 0, 1, 0}, + {STRUCT_IRON_CURTAIN, BSTATE_ACTIVE, 0, 11,3}, + {STRUCT_TESLA, BSTATE_ACTIVE, 0, 10,2}, + {STRUCT_AIRSTRIP, BSTATE_IDLE, 0, 8, 3} + }; + + for (int sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + BuildingTypeClass const & building = As_Reference((StructType)sindex); + /* + ** Fetch the sidebar cameo image for this building. + */ + if (building.Level != -1) { +// if (building.IsBuildable) { + sprintf(buffer, "%sICON", building.Graphic_Name()); + + if (building.IsFake) { + buffer[3] = 'F'; + } + + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)building.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch the construction animation for this building. + */ + sprintf(buffer, "%sMAKE", building.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + void const * dataptr; + dataptr = MFCD::Retrieve(fullname); + ((void const *&)building.BuildupData) = dataptr; + if (dataptr != NULL) { + int timedelay = 1; + int count = Get_Build_Frame_Count(dataptr); + if (count > 0) { + timedelay = (Rule.BuildupTime * TICKS_PER_MINUTE) / count; + } + building.Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + + /* + ** Fetch the normal game shape for this building. + */ + _makepath(fullname, NULL, NULL, building.Graphic_Name(), ".SHP"); + ((void const *&)building.ImageData) = MFCD::Retrieve(fullname); + { + const char* imageFileName = Buildings_FindHDTexture(fullname, 0, 0); + + if (imageFileName) { + Image_t *image = Image_LoadImage(imageFileName, true, true); + int numBuildingAnims = Buildings_GetNumFramesForTile(fullname, 0); + int shapeId = 1; + + // Load the old school animation pipeline. + { + while (true) { + imageFileName = Buildings_FindHDTexture(fullname, shapeId, 0); + if (imageFileName == NULL) + break; + + for (int d = 0; d < MAX_MPLAYER_COLORS; d++) { + Image_Add32BitImage(imageFileName, image, d, shapeId, 0); + } + shapeId++; + } + } + + if (numBuildingAnims > 0) + { + for (int i = 1; i < numBuildingAnims; i++) { + imageFileName = Buildings_FindHDTexture(fullname, 0, i); + for (int d = 0; d < MAX_MPLAYER_COLORS; d++) { + Image_Add32BitImage(imageFileName, image, d, 0, i); + } + } + } + image->numFrames = numBuildingAnims; + ((void const*&)building.HDImageData) = image; + + if (building.ImageData != NULL && building.HDImageData != NULL) { + int width = Get_Build_Frame_Width(building.ImageData); + int height = Get_Build_Frame_Height(building.ImageData); + + for (int d = 0; d < shapeId; d++) + { + building.HDImageData->renderwidth[d] = width; + building.HDImageData->renderheight[d] = height; + } + } + } + } + } + + // Try to load weap2.shp and tesla coil's lightning shapes + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, (char const *)"WEAP2",".SHP"); + WarFactoryOverlay = MFCD::Retrieve(fullname); + _makepath(fullname, NULL, NULL, (char const *)"LITNING",".SHP"); + LightningShapes = MFCD::Retrieve(fullname); + + /* + ** Install all the special animation sequences for the different building types. + */ + for (unsigned index = 0; index < (sizeof(_anims) / sizeof(_anims[0])); index++) { + As_Reference(_anims[index].Class).Init_Anim(_anims[index].Stage, _anims[index].Start, _anims[index].Length, _anims[index].Rate); + } + +} + + +/*********************************************************************************************** + * Struct_From_Name -- Find BData structure from its name. * + * * + * This routine will convert an ASCII name for a building class into * + * the actual building class it represents. * + * * + * INPUT: name -- ASCII representation of a building class. * + * * + * OUTPUT: Returns with the actual building class number that the string * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +StructType BuildingTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int classid = STRUCT_FIRST; classid < STRUCT_COUNT; classid++) { + if (stricmp(As_Reference((StructType)classid).IniName, name) == 0) { + return((StructType)classid); + } + } + } + return(STRUCT_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * BuildingTypeClass::Display -- Renders a generic view of building. * + * * + * This routine is used to display a generic representation of the * + * building. Typical use of this occurs with the scenario editor. * + * * + * INPUT: x,y -- Coordinate to display the building (centered). * + * * + * window -- The window the building should be rendered * + * relative to. * + * * + * house -- The house color to use for the building. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ +// jmarshall - hd buildings. + Image_t* hdimage = Get_HDImage_Data(); + if (hdimage != NULL) { + //CC_Draw_Shape((Image_t *)Get_HDImage_Data(), 0, x, y, window, SHAPE_CENTER | SHAPE_WIN_REL, NULL, NULL, DIR_N); + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + GL_RenderImage(hdimage, x, y, hdimage->renderwidth[0], hdimage->renderheight[0]); + } + else { + void const* ptr = Get_Cameo_Data(); + if (ptr == NULL) { + IsTheaterShape = IsTheater; + ptr = Get_Image_Data(); + } + CC_Draw_Shape(ptr, 0, x, y, window, SHAPE_CENTER | SHAPE_WIN_REL); + IsTheaterShape = false; + } +// jmarshall end +} + + +/*********************************************************************************************** + * BuildingTypeClass::Prep_For_Add -- Prepares scenario editor for adding a * + * * + * This routine is used to prepare the scenario editor for the addition * + * of a building object to the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/04/1994 JLB : Uses map editing interface routines. * + *=============================================================================================*/ +void BuildingTypeClass::Prep_For_Add(void) +{ + for (StructType index = STRUCT_FIRST; index < STRUCT_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() || As_Reference(index).Get_HDImage_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingTypeClass::Create_And_Place -- Creates and places a building object onto the map. * + * * + * This routine is used by the scenario editor to create and place buildings on the map. * + * * + * INPUT: cell -- The cell that the building is to be placed upon. * + * * + * house -- The owner of the building. * + * * + * OUTPUT: bool; Was the building successfully created and placed on the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + BuildingClass * ptr; + + ptr = new BuildingClass(Type, house); + if (ptr != NULL) { + return(ptr->Unlimbo(Cell_Coord(cell), DIR_N)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Create_One_Of -- Creates a building of this type. * + * * + * This routine will create a building object of this type. The building object is in a * + * limbo state. It is presumed that the building object will be unlimboed at the correct * + * place and time. Typical use is when the building is created in a factory situation * + * and will be placed on the map when construction completes. * + * * + * INPUT: house -- Pointer to the house that is to be the owner of the building. * + * * + * OUTPUT: Returns with a pointer to the building. If the building could not be created * + * then a NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * BuildingTypeClass::Create_One_Of(HouseClass * house) const +{ + HousesType htype = HOUSE_NEUTRAL; + if (house != NULL) { + htype = house->Class->House; + } + return(new BuildingClass(Type, htype)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init_Anim -- Initialize an animation control for a building. * + * * + * This routine will initialize one animation control element for a * + * specified building. This modifies a "const" class and thus must * + * perform some strategic casting to get away with this. * + * * + * INPUT: state -- The animation state to apply these data values to. * + * * + * start -- Starting frame for the building's animation. * + * * + * count -- The number of frames in this animation. * + * * + * rate -- The countdown timer between animation frames. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/18/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init_Anim(BStateType state, int start, int count, int rate) const +{ + ((int &)Anims[state].Start) = start; + ((int &)Anims[state].Count) = count; + ((int &)Anims[state].Rate) = rate; +} + + +/*********************************************************************************************** + * BuildingTypeClass::Init -- Performs theater specific initialization. * + * * + * This routine is used to perform any initialization that is custom per theater. * + * Typically, this is fetching the building shape data for those building types that have * + * theater specific art. * + * * + * INPUT: theater -- The theater to base this initialization on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + char fullname[_MAX_FNAME+_MAX_EXT]; + + for (int sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const * classptr = &As_Reference((StructType)sindex); + + if (classptr->IsTheater) { + _makepath(fullname, NULL, NULL, classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->ImageData) = MFCD::Retrieve(fullname); +// jmarshall + { + const char* imageFileName = Buildings_FindHDTexture(fullname, 0, 0); + + // Try load the theatre specific asset first + if (imageFileName) { + Image_t *image = ((BuildingTypeClass*)classptr)->HDImageData = Image_LoadImage(imageFileName, true, true); + + // Load the old school animation pipeline. + int shapeId = 1; + { + while (true) { + imageFileName = Buildings_FindHDTexture(fullname, shapeId, 0); + if (imageFileName == NULL) + break; + + for (int d = 0; d < MAX_MPLAYER_COLORS; d++) { + Image_Add32BitImage(imageFileName, image, d, shapeId, 0); + } + shapeId++; + } + } + + + int numBuildingAnims = Buildings_GetNumFramesForTile(fullname, 0); + if (numBuildingAnims > 0) + { + for (int i = 1; i < numBuildingAnims; i++) { + imageFileName = Buildings_FindHDTexture(fullname, 0, i); + for (int d = 0; d < MAX_MPLAYER_COLORS; d++) { + Image_Add32BitImage(imageFileName, ((BuildingTypeClass*)classptr)->HDImageData, d, 0, i); + } + } + } + + ((BuildingTypeClass*)classptr)->HDImageData->numFrames = numBuildingAnims; + } + + if (classptr->ImageData != NULL && classptr->HDImageData != NULL) { + int width = Get_Build_Frame_Width(classptr->ImageData); + int height = Get_Build_Frame_Height(classptr->ImageData); + + classptr->HDImageData->renderwidth[0] = width; + classptr->HDImageData->renderheight[0] = height; + } + } +// jmarshall end + /* + ** Buildup data is probably theater specific as well. Fetch a pointer to the + ** data at this time as well. + */ + sprintf(fullname, "%sMAKE.%s", classptr->Graphic_Name(), Theaters[theater].Suffix); + ((void const *&)classptr->BuildupData) = MFCD::Retrieve(fullname); + if (classptr->BuildupData) { + int timedelay = 1; + int count = Get_Build_Frame_Count(classptr->BuildupData); + if (count != 0) { + timedelay = (5 * TICKS_PER_SECOND) / count; + } + classptr->Init_Anim(BSTATE_CONSTRUCTION, 0, count, timedelay); + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingTypeClass::Dimensions -- Fetches the pixel dimensions of the building. * + * * + * This routine will fetch the dimensions of the building (in pixels). These dimensions are * + * used to render the selection rectangle and the health bar. * + * * + * INPUT: width -- Reference to the pixel width (to be filled in). * + * * + * height -- Reference to the pixel height (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingTypeClass::Dimensions(int &width, int &height) const +{ + width = Width() * ICON_PIXEL_W; + width -= (width/5); + height = Height() * ICON_PIXEL_H; + height -= (height/5); +} + + +/*********************************************************************************************** + * BuildingTypeClass::As_Reference -- Fetches reference to the building type specified. * + * * + * This routine will fetch a reference to the BuildingTypeClass as indicated by the * + * building type number specified. * + * * + * INPUT: type -- The building type number to convert into a BuildingTypeClass reference. * + * * + * OUTPUT: Returns with a reference to the building type class as indicated by the * + * parameter. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass & BuildingTypeClass::As_Reference(StructType type) +{ + return(*BuildingTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Occupy_List -- Fetches the occupy list for the building. * + * * + * Use this routine to fetch the occupy list pointer for the building. The occupy list is * + * used to determine what cells the building occupies and thus precludes other buildings * + * or objects from using. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to a cell offset list to be used to determine what cells * + * this building occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Occupy_List(bool placement) const +{ + SmudgeType bib = SMUDGE_NONE; + CELL cell=0; + + if (placement && Bib_And_Offset(bib, cell)) { + + SmudgeTypeClass const & smudge = SmudgeTypeClass::As_Reference(bib); + static short _list[25]; + short * dest = &_list[0]; + + /* + ** Copy the bib overlap list into the working buffer. + */ + short const * src = smudge.Occupy_List(); + while (*src != REFRESH_EOL) { + *dest++ = (*src++) + cell; + } + + /* + ** Append the building occupy list to this working buffer. + */ + src = OccupyList; + while (src && *src != REFRESH_EOL) { + *dest++ = *src++; + } + *dest = REFRESH_EOL; + + return(&_list[0]); + } + + if (OccupyList != NULL) { + return(OccupyList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Overlap_List -- Fetches the overlap list for the building. * + * * + * This routine will fetch the overlap list for the building. The overlap list is used * + * to determine what cells the building's graphics cover, but is not considered to occupy * + * for movement purposes. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that is used to determine the * + * cells that this building overlaps. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +short const * BuildingTypeClass::Overlap_List(void) const +{ + if (OverlapList != NULL) { + return(OverlapList); + } + + static short const _templap[] = {REFRESH_EOL}; + return(&_templap[0]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Width -- Determines width of building in icons. * + * * + * Use this routine to determine the width of the building type in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building width in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Width(void) const +{ + static int width[BSIZE_COUNT] = { + 1, + 2, + 1, + 2, + 2, + 3, + 3, + 4, + 5 + }; + return(width[Size]); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Height -- Determines the height of the building in icons. * + * * + * Use this routine to find the height of the building in icons. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the building height in icons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/23/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Height(bool bib) const +{ + static int height[BSIZE_COUNT] = { + 1, + 1, + 2, + 2, + 3, + 2, + 3, + 2, + 5 + }; + return(height[Size] + ((bib && IsBibbed) ? 1 : 0)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Bib_And_Offset -- Determines the bib and appropriate cell offset. * + * * + * This routine is used to determine what (if any) bib should be used for this building * + * and also the cell offset for the upper left corner of the bib smudge type. * + * * + * INPUT: bib -- Reference to the bib that should be used for this building. * + * * + * cell -- The cell offset for the upper left corner of the bib. This offset is * + * relative to the upper left corner of the building. * + * * + * OUTPUT: Is a bib required for this building? If the result is true, then the correct * + * bib and cell offset will be filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const +{ + bib = SMUDGE_NONE; + + if (IsBibbed) { + switch (Width()) { + case 2: + bib = SMUDGE_BIB3; + break; + + case 3: + bib = SMUDGE_BIB2; + break; + + case 4: + bib = SMUDGE_BIB1; + break; + + default: + bib = SMUDGE_NONE; + break; + } + + /* + ** Adjust the bib position for special buildings that have the bib as part + ** of the building art itself. + */ + if (bib != SMUDGE_NONE) { + cell += ((Height()-1)*MAP_CELL_W); + } + } + return(bib != SMUDGE_NONE); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * + * * + * Use this routine to determine the maximum number of pips to display on this building * + * when it is rendered. Typically, this is the tiberium capacity divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this building when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Max_Pips(void) const +{ + int maxpips = (Width() * ICON_PIXEL_W) / 4; + return(Bound((int)(Capacity/100), 0, maxpips)); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Raw_Cost -- Fetches the raw (base) cost of this building type. * + * * + * This routine is used to fetch the real raw base cost of the building. The raw cost * + * is the cost of the building less any free unit that would come with the building * + * if it were built in the normal fashion. Specifically, the helicopter cost is subtracted * + * from the helipad and the harvester cost is subtracted from the refinery. This cost * + * is used for refunding. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the raw (base) cost to build the building of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Raw_Cost(void) const +{ + int cost = TechnoTypeClass::Raw_Cost(); + + if (Type == STRUCT_HELIPAD && !Rule.IsSeparate) { + cost -= (AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Cost)/2; + } + if (Type == STRUCT_REFINERY) { + cost -= UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost; + } + return(cost); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Cost_Of -- Fetches the cost of this building. * + * * + * This routine will fetch the cost to build the building of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Cost_Of(void) const +{ + if (Rule.IsSeparate && Type == STRUCT_HELIPAD) { + return(Raw_Cost()); + } + return(TechnoTypeClass::Cost_Of()); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Flush_For_Placement -- Tries to clear placement area for this building t * + * * + * This routine is called when a clear space for placement is desired at the cell location * + * specified. Typical use of this routine is by the computer when it wants to build up * + * its base. * + * * + * INPUT: cell -- The cell that the building of this type would like to be placed down at. * + * * + * house -- Pointer to the house that want to clear the foundation zone. * + * * + * OUTPUT: Placement is temporarily blocked, please try again later? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Flush_For_Placement(CELL cell, HouseClass * house) const +{ + bool again = false; + if (cell > 0) { + short const * list = Occupy_List(true); + + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + + if (Map.In_Radar(newcell)) { + TechnoClass * occupier = Map[newcell].Cell_Techno(); + if (occupier != NULL) { + again = true; + if (occupier->House->Is_Ally(house) && occupier->Is_Foot() && !Target_Legal(((FootClass *)occupier)->NavCom)) { + Map[newcell].Incoming(0, true); + } else { +// Base_Is_Attacked(occupier); + } + } + } + } + } + return(again); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Read_INI -- Fetch building type data from the INI database. * + * * + * This routine will fetch the building type class data from the INI database file. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the building entry found and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool BuildingTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + Speed = ini.Get_Bool(Name(), "WaterBound", (Speed == SPEED_FLOAT)) ? SPEED_FLOAT : SPEED_NONE; + Capacity = ini.Get_Int(Name(), "Storage", Capacity); + Adjacent = ini.Get_Int(Name(), "Adjacent", Adjacent); + IsCaptureable = ini.Get_Bool(Name(), "Capturable", IsCaptureable); + IsPowered = ini.Get_Bool(Name(), "Powered", IsPowered); + IsBibbed = ini.Get_Bool(Name(), "Bib", IsBibbed); + IsUnsellable = ini.Get_Bool(Name(), "Unsellable", IsUnsellable); + IsBase = ini.Get_Bool(Name(), "BaseNormal", IsBase); + Power = ini.Get_Int(Name(), "Power", (Power > 0) ? Power : -Drain); + if (Power < 0) { + Drain = -Power; + Power = 0; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Coord_Fixup -- Adjusts coordinate to be legal for assignment. * + * * + * This routine will adjust the specified coordinate so that it will be legal for assignment* + * to this building. All buildings are given a coordinate that is in the upper left corner * + * of a cell. This routine will drop the fractional component of the coordinate. * + * * + * INPUT: coord -- The coordinate to fixup into a legal to assign value. * + * * + * OUTPUT: Returns with a coordinate that can be assigned to the building. * + * * + * WARNINGS: The coordinate is not examined to see if the cell is legal for placing the * + * building. It merely adjusts the coordinate so that is legal at first glance. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} + + +/*********************************************************************************************** + * BuildingTypeClass::Full_Name -- Fetches the name to give this building. * + * * + * This routine will return the displayable given name for this building type. Normally, * + * this is the official name as well, however in the case of civilian buildings, the * + * name will just be "Civilian Building" unless special options are in place. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the text number of the building type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingTypeClass::Full_Name(void) const +{ + if (Debug_Map || Rule.IsNamed || *this < STRUCT_V01 || *this > STRUCT_V37) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN_BUILDING); +} diff --git a/code/redalert/bdata_af.h b/code/redalert/bdata_af.h new file mode 100644 index 0000000..e99510e --- /dev/null +++ b/code/redalert/bdata_af.h @@ -0,0 +1,34 @@ +// BDAD_AF.H +// + +// Building Data for Red Alert Allied Force. + +static BuildingTypeClass const ClassAFV01( + STRUCT_AFV01, + TXT_CIV1, // NAME: Short name of the structure. + "afV01", // NAME: Short name of the structure. + FACING_S, // Foundation direction from center of building. + XYP_COORD(0, 0), // Exit point for produced units. + REMAP_ALTERNATE, // Sidebar remap logic. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + false, // Is this building a fake (decoy?) + true, // Animation rate is regulated for constant speed? + true, // Always use the given name for the building? + false, // Is this a wall type structure? + true, // Simple (one frame) damage imagery? + true, // Is it invisible to radar? + true, // Can the player select this? + true, // Is this a legal target for attack or move? + true, // Is this an insignificant building? + true, // Theater specific graphic image? + false, // Does it have a rotating turret? + false, // Can the building be color remapped to indicate owner? + RTTI_NONE, // The object type produced at this factory. + DIR_N, // Starting idle frame to match construction. + BSIZE_22, // SIZE: Building size. + NULL, // Preferred exit cell list. + (short const*)List0011, // OCCUPYLIST: List of active foundation squares. + (short const*)List1100 // OVERLAPLIST:List of overlap cell offset. +); \ No newline at end of file diff --git a/code/redalert/bdataxml.cpp b/code/redalert/bdataxml.cpp new file mode 100644 index 0000000..d027f61 --- /dev/null +++ b/code/redalert/bdataxml.cpp @@ -0,0 +1,98 @@ +// BDATAXML.CPP +// + +#include "FUNCTION.H" +#include "tinyxml2.h" + +#include +#include + +int64_t generateHashValue(const char* fname, const int size); + +struct BuildingsTileRule_t { + std::string name; + int64_t hash; + int shape; + std::vector frames; +}; + +struct BuildingsXMLInfo_t { + std::vector tiles; +}; + +BuildingsXMLInfo_t buildingsXmlRules; + +const char* Buildings_FindHDTexture(const char* shapeFileName, int shapeNum, int frameNum) { + char tmpFileName[2048]; + strcpy(tmpFileName, shapeFileName); + COM_SetExtension(tmpFileName, strlen(tmpFileName), ""); + int64_t hash = generateHashValue(tmpFileName, strlen(tmpFileName)); + for (int i = 0; i < buildingsXmlRules.tiles.size(); i++) { + if (buildingsXmlRules.tiles[i].hash == hash && buildingsXmlRules.tiles[i].shape == shapeNum) { + static char hdTexturePath[2048]; + if (buildingsXmlRules.tiles[i].frames.size() == 0) + return NULL; + + sprintf(hdTexturePath, "DATA/ART/TEXTURES/SRGB/RED_ALERT/STRUCTURES/%s", buildingsXmlRules.tiles[i].frames[frameNum].c_str()); + return &hdTexturePath[0]; + } + } + return NULL; +} + +int Buildings_GetNumFramesForTile(const char* shapeFileName, int shapeNum) { + char tmpFileName[2048]; + strcpy(tmpFileName, shapeFileName); + COM_SetExtension(tmpFileName, strlen(tmpFileName), ""); + int64_t hash = generateHashValue(tmpFileName, strlen(tmpFileName)); + for (int i = 0; i < buildingsXmlRules.tiles.size(); i++) { + if (buildingsXmlRules.tiles[i].hash == hash && buildingsXmlRules.tiles[i].shape == shapeNum) { + return buildingsXmlRules.tiles[i].frames.size(); + } + } + return 0; +} + +void Buildings_ParseTile(tinyxml2::XMLNode* tile, BuildingsTileRule_t& tileRule) { + tinyxml2::XMLNode* KeyNode = tile->FirstChildElement("Key"); + tinyxml2::XMLNode* NameNode = KeyNode->FirstChildElement("Name"); + tinyxml2::XMLNode* ShapeNode = KeyNode->FirstChildElement("Shape"); + tinyxml2::XMLNode* ValueNode = tile->FirstChildElement("Value"); + tinyxml2::XMLNode* FramesNode = ValueNode->FirstChildElement("Frames"); + tinyxml2::XMLNode* FrameNode = FramesNode->FirstChildElement("Frame"); + + tileRule.name = NameNode->FirstChild()->ToText()->Value(); + tileRule.hash = generateHashValue(tileRule.name.c_str(), tileRule.name.size()); + tileRule.shape = atoi(ShapeNode->FirstChild()->ToText()->Value()); + + while (FrameNode != NULL) { + if (FrameNode->FirstChild() != NULL) { + tileRule.frames.push_back(FrameNode->FirstChild()->ToText()->Value()); + } + FrameNode = FrameNode->NextSiblingElement("Frame"); + } +} + +void Buildings_LoadRuleXML(const char* path, BuildingsXMLInfo_t& info) { + tinyxml2::XMLDocument doc; + doc.LoadFile(path); + + tinyxml2::XMLElement* root = doc.FirstChildElement(); + if (root == NULL) + return; + + tinyxml2::XMLNode* tilesetTypeClassNode = root->FirstChild(); + + tinyxml2::XMLNode* tilesParent = tilesetTypeClassNode->FirstChildElement("Tiles"); + tinyxml2::XMLNode* tile = tilesParent->FirstChildElement("Tile"); + while (tile != NULL) { + BuildingsTileRule_t tileRule; + Buildings_ParseTile(tile, tileRule); + info.tiles.push_back(tileRule); + tile = tile->NextSiblingElement("Tile"); + } +} + +void Buildings_LoadRules(void) { + Buildings_LoadRuleXML("data/xml/tilesets/RA_STRUCTURES.XML", buildingsXmlRules); +} \ No newline at end of file diff --git a/code/redalert/bench.cpp b/code/redalert/bench.cpp new file mode 100644 index 0000000..0eccdf3 --- /dev/null +++ b/code/redalert/bench.cpp @@ -0,0 +1,165 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BENCH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BENCH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Benchmark::Begin -- Start the benchmark operation. * + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * Benchmark::End -- Mark the end of a benchmarked operation * + * Benchmark::Reset -- Clear out the benchmark statistics. * + * Benchmark::Value -- Fetch the current average benchmark time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0) +#include "bench.h" +#include "mpu.h" + + +/*********************************************************************************************** + * Benchmark::Benchmark -- Constructor for the benchmark object. * + * * + * This will construct the benchmark object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +Benchmark::Benchmark(void) : + Average(0), + Counter(0), + TotalCount(0) +{ +} + + +/*********************************************************************************************** + * Benchmark::Reset -- Clear out the benchmark statistics. * + * * + * Use this routine to clear out all the accumulated statistics within this benchmark * + * object. The object is set just as if it was freshly constructed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Reset(void) +{ + Average = 0; + Counter = 0; + TotalCount = 0; +} + + +/*********************************************************************************************** + * Benchmark::Begin -- Start the benchmark operation. * + * * + * Call this routine before the operation to be benchmarked is begun. The corresponding * + * End() function must be called after the operation has completed. * + * * + * INPUT: reset -- Should the entire benchmark object be reset at this time as well? * + * * + * OUTPUT: none * + * * + * WARNINGS: The Begin() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::Begin(bool reset) +{ + if (reset) Reset(); + Clock = 0; +} + + +/*********************************************************************************************** + * Benchmark::End -- Mark the end of a benchmarked operation * + * * + * This routine is called at the end of the operation that is being benchmarked. It is * + * important to call this routine as soon as possible after the event being benchmarked * + * has completed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The Being() and End() functions are NOT nestable. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void Benchmark::End(void) +{ + unsigned long value = Clock; + + if (Counter == MAXIMUM_EVENT_COUNT) { + Average -= Average / MAXIMUM_EVENT_COUNT; + Average += value; + } else { + Average += value; + Counter++; + } + TotalCount++; +} + + +/*********************************************************************************************** + * Benchmark::Value -- Fetch the current average benchmark time. * + * * + * This routine will take the statistics already accumulated and determine the average * + * time recorded. This value will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the average time that all events tracked by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +unsigned long Benchmark::Value(void) const +{ + if (Counter) { + return(Average / Counter); + } + return(0); +} +#endif \ No newline at end of file diff --git a/code/redalert/bench.h b/code/redalert/bench.h new file mode 100644 index 0000000..b18734f --- /dev/null +++ b/code/redalert/bench.h @@ -0,0 +1,121 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BENCH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BENCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BENCH_H +#define BENCH_H +#if (0) + +#include "mpu.h" +#include "ftimer.h" + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a timer access object that will fetch the internal Pentium +** clock value. +*/ +class PentiumTimerClass +{ + public: + unsigned long operator () (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} + operator unsigned long (void) const {unsigned long h;unsigned long l = Get_CPU_Clock(h);return((l >> 4) | (h << 28));} +}; + + +/* +** A performance tracking tool object. It is used to track elapsed time. Unlike a simple clock, this +** class will keep a running average of the duration. Typical use of this would be to benchmark some +** process that occurs multiple times. By benchmarking an average time, inconsistencies in a particular +** run can be overcome. +*/ +class Benchmark +{ + public: + Benchmark(void); + + void Begin(bool reset=false); + void End(void); + + void Reset(void); + unsigned long Value(void) const; + unsigned long Count(void) const {return(TotalCount);} + + private: + /* + ** The maximum number of events to keep running average of. If + ** events exceed this number, then older events drop off the + ** accumulated time. This number needs to be as small as + ** is reasonable. The larger this number gets, the less magnitude + ** that the benchmark timer can handle. Example; At a value of + ** 256, the magnitude of the timer can only be 24 bits. + */ + enum {MAXIMUM_EVENT_COUNT=256}; + + /* + ** This is the timer the is used to clock the events. + */ + BasicTimerClass Clock; + + /* + ** The total time off all events tracked so far. + */ + unsigned long Average; + + /* + ** The total number of events tracked so far. + */ + unsigned long Counter; + + /* + ** Absolute total number of events (possibly greater than the + ** number of events tracked in the average). + */ + unsigned long TotalCount; +}; + + +#endif +#endif \ No newline at end of file diff --git a/code/redalert/bfiofile.cpp b/code/redalert/bfiofile.cpp new file mode 100644 index 0000000..cf690d8 --- /dev/null +++ b/code/redalert/bfiofile.cpp @@ -0,0 +1,984 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BFIOFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * BufferIOFileClass::Close -- Perform a closure of the file. * + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * BufferIOFileClass::Read -- Reads data from the file cache. * + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * BufferIOFileClass::Write -- Writes data to the file cache. * + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "bfiofile.h" +#include + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- Filename based constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(char const * filename) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ + BufferIOFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * BufferIOFileClass::BufferIOFileClass -- default constructor for a file object. * + * * + * This is the default constructor for a file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::BufferIOFileClass(void) : + IsAllocated(false), + IsOpen(false), + IsDiskOpen(false), + IsCached(false), + IsChanged(false), + UseBuffer(false), + BufferRights(0), + Buffer(0), + BufferSize(0), + BufferPos(0), + BufferFilePos(0), + BufferChangeBeg(-1), + BufferChangeEnd(-1), + FileSize(0), + FilePos(0), + TrueFileStart(0) +{ +} + + +/*********************************************************************************************** + * BufferIOFileClass::~BufferIOFileClass -- Destructor for the file object. * + * * + * This destructor will free all memory allocated thru using Cache routines. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +BufferIOFileClass::~BufferIOFileClass(void) +{ + Free(); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Cache -- Load part or all of a file data into RAM. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Cache( long size, void * ptr ) +{ + if (Buffer) { + // + // if trying to cache again with size or ptr fail + // + if (size || ptr) { + return( false ); + } else { + return( true ); + } + } + + if ( Is_Available() ) { + FileSize = Size(); + } else { + FileSize = 0; + } + + if (size) { + // + // minimum buffer size for performance + // + if (size < MINIMUM_BUFFER_SIZE) { + size = MINIMUM_BUFFER_SIZE; + + /* + ** Specifying a size smaller than the minimum is an error + ** IF a buffer pointer was also specified. In such a case the + ** system cannot use the buffer. + */ + if (ptr) { + Error(EINVAL); + } + } + + BufferSize = size; + } else { + BufferSize = FileSize; + } + + // + // if size == 0 and a ptr to a buffer is specified then that is invalid. + // if the BufferSize is 0 then this must be a new file and no size was + // specified so exit. + // + if ( (size == 0 && ptr) || !BufferSize) { + return( false ); + } + + if (ptr) { + Buffer = ptr; + } else { + Buffer = new char [BufferSize]; + } + + if (Buffer) { + IsAllocated = true; + IsDiskOpen = false; + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + TrueFileStart = 0; + + // + // the file was checked for availability then set the FileSize + // + if (FileSize) { + long readsize; + int opened = false; + long prevpos = 0; + + + if (FileSize <= BufferSize) { + readsize = FileSize; + } else { + readsize = BufferSize; + } + + if ( Is_Open() ) { + // + // get previous file position + // + prevpos = Seek(0); + + // + // get true file position + // + if ( RawFileClass::Is_Open() ) { + TrueFileStart = RawFileClass::Seek(0); + } else { + TrueFileStart = prevpos; + } + + if (FileSize <= BufferSize) { + // + // if previous position is non-zero seek to the beginning + // + if (prevpos) { + Seek(0, SEEK_SET); + } + + // + // set the buffer position for future reads/writes + // + BufferPos = prevpos; + } else { + BufferFilePos = prevpos; + } + + FilePos = prevpos; + } else { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + long actual = Read(Buffer, readsize); + + if (actual != readsize) { + Error(EIO); + } + + if (opened) { + Close(); + } else { + // + // seek to the previous position in the file + // + Seek(prevpos, SEEK_SET); + } + + IsCached = true; + } + + UseBuffer = true; + return(true); + } + + Error(ENOMEM); + + return(false); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Free -- Frees the allocated buffer. * + * * + * This routine will free the buffer. By using this in conjunction with the * + * Cache() function, one can maintain tight control of memory usage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Free(void) +{ + if (Buffer) { + if (IsAllocated) { + delete [] Buffer; + IsAllocated = false; + } + + Buffer = 0; + } + + BufferSize = 0; + IsOpen = false; + IsCached = false; + IsChanged = false; + UseBuffer = false; +} + + +/*********************************************************************************************** + * BufferIOFileClass::Commit -- Writes the cache to the file if it has changed. * + * * + * * + * INPUT: none * + * * + * OUTPUT: false, did not need to write the buffer. * + * true, wrote the buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +bool BufferIOFileClass::Commit( void ) +{ + long size; + + + if (UseBuffer) { + if (IsChanged) { + size = BufferChangeEnd - BufferChangeBeg; + + if (IsDiskOpen) { + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Seek( TrueFileStart + FilePos, SEEK_SET ); + } else { + RawFileClass::Open(); + RawFileClass::Seek( TrueFileStart + BufferFilePos + + BufferChangeBeg, SEEK_SET ); + RawFileClass::Write( Buffer, size ); + RawFileClass::Close(); + } + + IsChanged = false; + return( true ); + } else { + return( false ); + } + } else { + return( false ); + } +} + + +/*********************************************************************************************** + * BufferIOFileClass::Set_Name -- Checks for name changed for a cached file. * + * * + * Checks for a previous filename and that it is cached. If so, then check the * + * new filename against the old. If they are the same then return that filename. * + * Otherwise, the file object's name is set with just the raw filename as passed * + * to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +char const * BufferIOFileClass::Set_Name(char const * filename) +{ + if ( File_Name() && UseBuffer) { + if ( strcmp(filename, File_Name() ) == 0) { + return( File_Name() ); + } else { + Commit(); + IsCached = false; + } + } + + RawFileClass::Set_Name(filename); + return( File_Name() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Available -- Checks for existence of file cached or on disk. * + * * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/16/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Available(int ) +{ + if (UseBuffer) { + return(true); + } + + return( RawFileClass::Is_Available() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Is_Open -- Determines if the file is open. * + * * + * If part or all of the file is cached, then return that it is opened. A closed file * + * doesn't have a valid pointer. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Is_Open(void) const +{ + if (IsOpen && UseBuffer) { + return( true ); + } + + return( RawFileClass::Is_Open() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return( BufferIOFileClass::Open( rights ) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +int BufferIOFileClass::Open(int rights) +{ + BufferIOFileClass::Close(); + + if (UseBuffer) { + + BufferRights = rights; // save rights requested for checks later + + if (rights != READ || + (rights == READ && FileSize > BufferSize) ) { + + if (rights == WRITE) { + RawFileClass::Open( rights ); + RawFileClass::Close(); + rights = READ | WRITE; + TrueFileStart = 0; // now writing to single file + } + + if (TrueFileStart) { + UseBuffer = false; + Open( rights ); + UseBuffer = true; + } else { + RawFileClass::Open( rights ); + } + + IsDiskOpen = true; + + if (BufferRights == WRITE) { + FileSize = 0; + } + + } else { + IsDiskOpen = false; + } + + BufferPos = 0; + BufferFilePos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + FilePos = 0; + IsOpen = true; + } else { + RawFileClass::Open( rights ); + } + + return( true ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Write -- Writes data to the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Write(void const * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if (!Open(WRITE)) { + return(0); + } + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + + if (UseBuffer) { + long sizewritten = 0; + + if (BufferRights != READ) { + while (size) { + long sizetowrite; + + if (size >= (BufferSize - BufferPos) ) { + sizetowrite = (BufferSize - BufferPos); + } else { + sizetowrite = size; + } + + if (sizetowrite != BufferSize) { + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + } + + memmove((char *)Buffer + BufferPos, (char *)buffer + sizewritten, sizetowrite); + + IsChanged = true; + sizewritten += sizetowrite; + size -= sizetowrite; + + if (BufferChangeBeg == -1) { + BufferChangeBeg = BufferPos; + BufferChangeEnd = BufferPos; + } else { + if (BufferChangeBeg > BufferPos) { + BufferChangeBeg = BufferPos; + } + } + + BufferPos += sizetowrite; + + if (BufferChangeEnd < BufferPos) { + BufferChangeEnd = BufferPos; + } + + FilePos = BufferFilePos + BufferPos; + + if (FileSize < FilePos) { + FileSize = FilePos; + } + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizewritten; + } else { + size = RawFileClass::Write(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Read -- Reads data from the file cache. * + * * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Read(void * buffer, long size) +{ + int opened = false; + + if ( !Is_Open() ) { + if ( Open() ) { + TrueFileStart = RawFileClass::Seek(0); + opened = true; + } + } + + if (UseBuffer) { + long sizeread = 0; + + if (BufferRights != WRITE) { + while (size) { + long sizetoread; + + if (size >= (BufferSize - BufferPos) ) { + sizetoread = (BufferSize - BufferPos); + } else { + sizetoread = size; + } + + if ( !IsCached ) { + long readsize; + + if (FileSize < BufferSize) { + readsize = FileSize; + BufferFilePos = 0; + } else { + readsize = BufferSize; + BufferFilePos = FilePos; + } + + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( BufferFilePos, SEEK_SET ); + RawFileClass::Read( Buffer, readsize ); + } + + BufferPos = 0; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + IsCached = true; + } + + memmove((char *)buffer + sizeread, (char *)Buffer + BufferPos, sizetoread); + + sizeread += sizetoread; + size -= sizetoread; + BufferPos += sizetoread; + FilePos = BufferFilePos + BufferPos; + + // + // end of buffer reached? + // + if (BufferPos == BufferSize) { + Commit(); + + BufferPos = 0; + BufferFilePos = FilePos; + BufferChangeBeg = -1; + BufferChangeEnd = -1; + + if (size && FileSize > FilePos) { + if (TrueFileStart) { + UseBuffer = false; + Seek( FilePos, SEEK_SET ); + Read( Buffer, BufferSize ); + Seek( FilePos, SEEK_SET ); + UseBuffer = true; + } else { + RawFileClass::Seek( FilePos, SEEK_SET ); + RawFileClass::Read( Buffer, BufferSize ); + } + } else { + IsCached = false; + } + } + } + } else { + Error(EACCES); + } + + size = sizeread; + } else { + size = RawFileClass::Read(buffer, size); + } + + if (opened) { + Close(); + } + + return( size ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/15/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Seek(long pos, int dir) +{ + if (UseBuffer) { + bool adjusted = false; + + switch (dir) { + case SEEK_END: + FilePos = FileSize; + break; + + case SEEK_SET: + FilePos = 0; + break; + + case SEEK_CUR: + default: + break; + } + + if (TrueFileStart) { + if (pos >= TrueFileStart) { + pos -= TrueFileStart; + adjusted = true; + } + } + + FilePos += pos; + + if (FilePos < 0) { + FilePos = 0; + } + + if (FilePos > FileSize ) { + FilePos = FileSize; + } + + if (FileSize <= BufferSize) { + BufferPos = FilePos; + } else { + if (FilePos >= BufferFilePos && + FilePos < (BufferFilePos + BufferSize) ) { + BufferPos = FilePos - BufferFilePos; + } else { + Commit(); +// check!! + if (TrueFileStart) { + UseBuffer = false; + Seek(FilePos, SEEK_SET); + UseBuffer = true; + } else { + RawFileClass::Seek(FilePos, SEEK_SET); + } + + IsCached = false; + } + } + + if (TrueFileStart && adjusted) { + return( FilePos + TrueFileStart ); + } + + return( FilePos ); + } + + return( RawFileClass::Seek(pos, dir) ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Size -- Determines size of file (in bytes). * + * * + * If part or all of the file is cached, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +long BufferIOFileClass::Size(void) +{ + if (IsOpen && UseBuffer) { + return( FileSize ); + } + + return( RawFileClass::Size() ); +} + + +/*********************************************************************************************** + * BufferIOFileClass::Close -- Perform a closure of the file. * + * * + * Call Commit() to write the buffer if the file is cached and the buffer has changed, * + * then call lower level Close(). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +void BufferIOFileClass::Close(void) +{ + if (UseBuffer) { + Commit(); + + if (IsDiskOpen) { + + if (TrueFileStart) { + UseBuffer = false; + Close(); + UseBuffer = true; + } else { + RawFileClass::Close(); + } + + IsDiskOpen = false; + } + + IsOpen = false; + } else { + RawFileClass::Close(); + } +} + diff --git a/code/redalert/bfiofile.h b/code/redalert/bfiofile.h new file mode 100644 index 0000000..e034baa --- /dev/null +++ b/code/redalert/bfiofile.h @@ -0,0 +1,92 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BFIOFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : BFIOFILE.H * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : November 10, 1995 * + * * + * Last Update : November 10, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BFIOFILE_H +#define BFIOFILE_H + +#include "rawfile.h" + +/* +** This derivation of the raw file class handles buffering the input/output in order to +** achieve greater speed. The buffering is not active by default. It must be activated +** by setting the appropriate buffer through the Cache() function. +*/ +class BufferIOFileClass : public RawFileClass +{ + public: + + BufferIOFileClass(char const *filename); + BufferIOFileClass(void); + virtual ~BufferIOFileClass(void); + + bool Cache( long size=0, void *ptr=NULL ); + void Free( void ); + bool Commit( void ); + virtual char const * Set_Name(char const *filename); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + + enum {MINIMUM_BUFFER_SIZE=1024}; + + private: + + unsigned IsAllocated:1; + unsigned IsOpen:1; + unsigned IsDiskOpen:1; + unsigned IsCached:1; + unsigned IsChanged:1; + unsigned UseBuffer:1; + + int BufferRights; + + void *Buffer; + + long BufferSize; + long BufferPos; + long BufferFilePos; + long BufferChangeBeg; + long BufferChangeEnd; + long FileSize; + long FilePos; + long TrueFileStart; +}; + +#endif diff --git a/code/redalert/bigcheck.cpp b/code/redalert/bigcheck.cpp new file mode 100644 index 0000000..1a8f97a --- /dev/null +++ b/code/redalert/bigcheck.cpp @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// BigCheck.cpp +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#include "function.h" +#include "bigcheck.h" + +//*********************************************************************************************** +int BigCheckBoxClass::Draw_Me( int forced ) +{ + if (ToggleClass::Draw_Me(forced)) + { + Hide_Mouse(); + + if( !IsOn ) + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 0, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 2, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + else + { + if( !IsDisabled ) + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 1, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + else + CC_Draw_Shape( MFCD::Retrieve( "bigcheck.shp" ), 3, X, Y, WINDOW_MAIN, SHAPE_NORMAL ); + } + + TextPrintType flags = TextFlags; + + RemapControlType* pScheme; + +// if( !IsDisabled ) + pScheme = GadgetClass::Get_Color_Scheme(); +// else +// { +// pScheme = &GreyScheme; +// flags = flags | TPF_MEDIUM_COLOR; +// } + + Conquer_Clip_Text_Print( szCaption, X + BIGCHECK_OFFSETX, Y + BIGCHECK_OFFSETY, pScheme, TBLACK, flags, Width, 0 ); + + Show_Mouse(); + return true; + } + return false; +} + +//*********************************************************************************************** +int BigCheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ +/* if( flags & LEFTPRESS ) + { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } +*/ + return(ToggleClass::Action(flags, key)); +} + +#endif diff --git a/code/redalert/bigcheck.h b/code/redalert/bigcheck.h new file mode 100644 index 0000000..d53c807 --- /dev/null +++ b/code/redalert/bigcheck.h @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// Bigcheck.h +// ajw 9/14/98 + +#ifdef WOLAPI_INTEGRATION + +#ifndef BIGCHECKBOX_H +#define BIGCHECKBOX_H + +#include "toggle.h" + +#define BIGCHECK_OFFSETX 20 +#define BIGCHECK_OFFSETY 0 + +//*********************************************************************************************** +class BigCheckBoxClass : public ToggleClass +{ +public: + BigCheckBoxClass( unsigned id, int x, int y, int w, int h, const char* szCaptionIn, TextPrintType TextFlags, + bool bInitiallyChecked = false ) : + ToggleClass( id, x, y, w, h ), + TextFlags( TextFlags ) + { + szCaption = new char[ strlen( szCaptionIn ) + 1 ]; + strcpy( szCaption, szCaptionIn ); + if( bInitiallyChecked ) + Turn_On(); + IsToggleType = 1; + } + virtual ~BigCheckBoxClass() + { + delete [] szCaption; + } + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + bool Toggle() + { + if( IsOn ) + { + Turn_Off(); + return false; + } + Turn_On(); + return true; + } + +protected: + TextPrintType TextFlags; + char* szCaption; + +}; + +#endif + +#endif diff --git a/code/redalert/blowfish.cpp b/code/redalert/blowfish.cpp new file mode 100644 index 0000000..53225bf --- /dev/null +++ b/code/redalert/blowfish.cpp @@ -0,0 +1,595 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWFISH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWFISH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowfishEngine::Decrypt -- Decrypts data using blowfish algorithm. * + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowfish.h" +#include +#include + + +/* +** Byte order controlled long integer. This integer is constructed +** so that character 0 (C0) is the most significant byte of the +** integer. This is biased toward big endian architecture, but that +** just happens to be how the Blowfish algorithm was designed. +*/ +typedef union { + unsigned long Long; + struct { + unsigned char C3; + unsigned char C2; + unsigned char C1; + unsigned char C0; + } Char; +} Int; + + +/*********************************************************************************************** + * BlowfishEngine::~BlowfishEngine -- Destructor for the Blowfish engine. * + * * + * This destructor will clear out the s-box tables so that even if the memory for the * + * class remains, it will contain no compromising data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +BlowfishEngine::~BlowfishEngine(void) +{ + if (IsKeyed) { + Submit_Key(NULL, 0); + } +} + + +/*********************************************************************************************** + * BlowfishEngine::Submit_Key -- Submit a key that will allow data processing. * + * * + * This routine must be called before any data can be encrypted or decrypted. This routine * + * need only be called when the key is to be changed or set for the first time. Once the * + * key has been set, the engine may be used to encrypt, decrypt, or both operations * + * indefinitely. The key must be 56 bytes or less in length. This is necessary because * + * any keys longer than that will not correctly affect the encryption process. * + * * + * If the key pointer is NULL, then the S-Box tables are reset to identity. This will * + * mask the previous key setting. Use this method to clear the engine after processing in * + * order to gain a measure of security. * + * * + * INPUT: key -- Pointer to the key data block. * + * * + * length -- The length of the submitted key. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming process. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Submit_Key(void const * key, int length) +{ + assert(length <= MAX_KEY_LENGTH); + + /* + ** Initialize the permutation and S-Box tables to a known + ** constant value. + */ + memcpy(P_Encrypt, P_Init, sizeof(P_Init)); + memcpy(P_Decrypt, P_Init, sizeof(P_Init)); + memcpy(bf_S, S_Init, sizeof(S_Init)); + + /* + ** Validate parameters. + */ + if (key == 0 || length == 0) { + IsKeyed = false; + return; + } + + /* + ** Combine the key with the permutation table. Wrap the key + ** as many times as necessary to ensure that the entire + ** permutation table has been modified. The key is lifted + ** into a long by using endian independent means. + */ + int j = 0; + unsigned char const * key_ptr = (unsigned char const *)key; + unsigned long * p_ptr = &P_Encrypt[0]; + for (int index = 0; index < ROUNDS+2; index++) { + unsigned long data = 0; + + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + data = (data << CHAR_BIT) | key_ptr[j++ % length]; + + *p_ptr++ ^= data; + } + + /* + ** The permutation table must be scrambled by means of the key. This + ** is how the key is factored into the encryption -- by merely altering + ** the permutation (and S-Box) tables. Because this transformation alters + ** the table data WHILE it is using the table data, the tables are + ** thoroughly obfuscated by this process. + */ + unsigned long left = 0x00000000L; + unsigned long right = 0x00000000L; + unsigned long * p_en = &P_Encrypt[0]; // Encryption table. + unsigned long * p_de = &P_Decrypt[ROUNDS+1]; // Decryption table. + for (int p_index = 0; p_index < ROUNDS+2; p_index += 2) { + Sub_Key_Encrypt(left, right); + + *p_en++ = left; + *p_en++ = right; + + *p_de-- = left; + *p_de-- = right; + } + + /* + ** Perform a similar transmutation to the S-Box tables. Also notice that the + ** working 64 bit number is carried into this process from the previous + ** operation. + */ + for (int sbox_index = 0; sbox_index < 4; sbox_index++) { + for (int ss_index = 0; ss_index < UCHAR_MAX+1; ss_index += 2) { + Sub_Key_Encrypt(left, right); + bf_S[sbox_index][ss_index] = left; + bf_S[sbox_index][ss_index + 1] = right; + } + } + + IsKeyed = true; +} + + +/*********************************************************************************************** + * BlowfishEngine::Encrypt -- Encrypt an arbitrary block of data. * + * * + * Use this routine to encrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be encrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: plaintext-- Pointer to the data block to be encrypted. * + * * + * length -- The length of the data block. * + * * + * cyphertext- Pointer to the output buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes encrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only encrypt * + * the plaintext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Encrypt(void const * plaintext, int length, void * cyphertext) +{ + if (plaintext == 0 || length == 0) { + return(0); + } + if (cyphertext == 0) cyphertext = (void *)plaintext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(plaintext, cyphertext, P_Encrypt); + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(cyphertext, plaintext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(cyphertext, plaintext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Decrypt -- Decrypt an arbitrary block of data. * + * * + * Use this routine to decrypt an arbitrary block of data. The block must be an even * + * multiple of 8 bytes. Any bytes left over will not be decrypted. The 8 byte requirement * + * is necessary because the underlying algorithm processes blocks in 8 byte chunks. * + * Partial blocks are unrecoverable and useless. * + * * + * INPUT: cyphertext- Pointer to the data block to be decrypted. * + * * + * length -- The length of the data block. * + * * + * plaintext-- Pointer to the output buffer that will hold the decrypted data. * + * * + * OUTPUT: Returns with the actual number of bytes decrypted. * + * * + * WARNINGS: You must submit the key before calling this routine. This will only decrypt * + * the cyphertext in 8 byte increments. Modulo bytes left over are not processed. * + * * + * HISTORY: * + * 04/14/1996 JLB : Created. * + *=============================================================================================*/ +int BlowfishEngine::Decrypt(void const * cyphertext, int length, void * plaintext) +{ + if (cyphertext == 0 || length == 0) { + return(0); + } + if (plaintext == 0) plaintext = (void *)cyphertext; + + if (IsKeyed) { + + /* + ** Validate parameters. + */ + int blocks = length / BYTES_PER_BLOCK; + + /* + ** Process the buffer in 64 bit chunks. + */ + for (int index = 0; index < blocks; index++) { + Process_Block(cyphertext, plaintext, P_Decrypt); + cyphertext = ((char *)cyphertext) + BYTES_PER_BLOCK; + plaintext = ((char *)plaintext) + BYTES_PER_BLOCK; + } + int encrypted = blocks * BYTES_PER_BLOCK; + + /* + ** Copy over any trailing left over appendix bytes. + */ + if (encrypted < length) { + memmove(plaintext, cyphertext, length - encrypted); + } + + return(encrypted); + } + + /* + ** Non-keyed processing merely copies the data. + */ + if (plaintext != cyphertext) { + memmove(plaintext, cyphertext, length); + } + return(length); +} + + +/*********************************************************************************************** + * BlowfishEngine::Process_Block -- Process a block of data using Blowfish algorithm. * + * * + * This is the main processing routine for encryption and decryption. The algorithm * + * consists of a 16 round Feistal network and uses mathematics from different algebraic * + * groups (strengthens against differential cryptanalysis). The large S-Boxes and the * + * rounds strengthen it against linear cryptanalysis. * + * * + * INPUT: plaintext -- Pointer to the source text (it actually might be a pointer to * + * the cyphertext if this is called as a decryption process). * + * * + * cyphertext -- Pointer to the output buffer that will hold the processed block. * + * * + * ptable -- Pointer to the permutation table. This algorithm will encrypt * + * and decrypt using the same S-Box tables. The encryption control * + * is handled by the permutation table. * + * * + * OUTPUT: none * + * * + * WARNINGS: The source and destination buffers must be 8 bytes long. * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable) +{ + /* + ** Input the left and right halves of the source block such that + ** the byte order is constant regardless of the endian + ** persuasion of the current processor. The blowfish algorithm is + ** biased toward "big endian" architecture and some optimizations + ** could be done for big endian processors in that case. + */ + unsigned char const * source = (unsigned char const *)plaintext; + Int left; + left.Char.C0 = *source++; + left.Char.C1 = *source++; + left.Char.C2 = *source++; + left.Char.C3 = *source++; + + Int right; + right.Char.C0 = *source++; + right.Char.C1 = *source++; + right.Char.C2 = *source++; + right.Char.C3 = *source; + + /* + ** Perform all Feistal rounds on the block. This is the encryption/decryption + ** process. Since there is an exchange that occurs after each round, two + ** rounds are combined in this loop to avoid unnecessary exchanging. + */ + for (int index = 0; index < ROUNDS/2; index++) { + left.Long ^= *ptable++; + right.Long ^= ((( bf_S[0][left.Char.C0] + bf_S[1][left.Char.C1]) ^ bf_S[2][left.Char.C2]) + bf_S[3][left.Char.C3]); + right.Long ^= *ptable++; + left.Long ^= ((( bf_S[0][right.Char.C0] + bf_S[1][right.Char.C1]) ^ bf_S[2][right.Char.C2]) + bf_S[3][right.Char.C3]); + } + + /* + ** The final two longs in the permutation table are processed into the block. + ** The left and right halves are still reversed as a side effect of the last + ** round. + */ + left.Long ^= *ptable++; + right.Long ^= *ptable; + + /* + ** The final block data is output in endian architecture + ** independent format. Notice that the blocks are output as + ** right first and left second. This is to counteract the final + ** superfluous exchange that occurs as a side effect of the + ** encryption rounds. + */ + unsigned char * out = (unsigned char *)cyphertext; + *out++ = right.Char.C0; + *out++ = right.Char.C1; + *out++ = right.Char.C2; + *out++ = right.Char.C3; + + *out++ = left.Char.C0; + *out++ = left.Char.C1; + *out++ = left.Char.C2; + *out = left.Char.C3; +} + + +/*********************************************************************************************** + * BlowfishEngine::Sub_Key_Encrypt -- Encrypts a block for use in S-Box processing. * + * * + * This is the same as the normal process block function but it doesn't have the endian * + * fixup logic. Since this routine is only called for S-Box table generation and it is * + * known that the S-Box initial data is already in local machine endian format, the * + * byte order fixups are not needed. This also has a tendency to speed up S-Box generation * + * as well. * + * * + * INPUT: left -- The left half of the data block. * + * * + * right -- The right half of the data block. * + * * + * OUTPUT: none, but the processed block is stored back into the left and right half * + * integers. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/19/1996 JLB : Created. * + *=============================================================================================*/ +void BlowfishEngine::Sub_Key_Encrypt(unsigned long & left, unsigned long & right) +{ + Int l; + l.Long = left; + + Int r; + r.Long = right; + + for (int index = 0; index < ROUNDS; index += 2) { + l.Long ^= P_Encrypt[index]; + r.Long ^= ((( bf_S[0][l.Char.C0] + bf_S[1][l.Char.C1]) ^ bf_S[2][l.Char.C2]) + bf_S[3][l.Char.C3]); + r.Long ^= P_Encrypt[index+1]; + l.Long ^= ((( bf_S[0][r.Char.C0] + bf_S[1][r.Char.C1]) ^ bf_S[2][r.Char.C2]) + bf_S[3][r.Char.C3]); + } + left = r.Long ^ P_Encrypt[ROUNDS+1]; + right = l.Long ^ P_Encrypt[ROUNDS]; +} + + +/* +** These tables have the bytes stored in machine endian format. Because of this, +** a special block cypher routine is needed when the sub-keys are generated. +** This is kludgier than it otherwise should be. However, storing these +** integers in machine independent format would be even more painful. +*/ + +unsigned long const BlowfishEngine::P_Init[BlowfishEngine::ROUNDS+2] = { + 0x243F6A88U,0x85A308D3U,0x13198A2EU,0x03707344U,0xA4093822U,0x299F31D0U,0x082EFA98U,0xEC4E6C89U, + 0x452821E6U,0x38D01377U,0xBE5466CFU,0x34E90C6CU,0xC0AC29B7U,0xC97C50DDU,0x3F84D5B5U,0xB5470917U, + 0x9216D5D9U,0x8979FB1BU +} ; + +unsigned long const BlowfishEngine::S_Init[4][UCHAR_MAX+1] = { + { + 0xD1310BA6U,0x98DFB5ACU,0x2FFD72DBU,0xD01ADFB7U,0xB8E1AFEDU,0x6A267E96U,0xBA7C9045U,0xF12C7F99U, + 0x24A19947U,0xB3916CF7U,0x0801F2E2U,0x858EFC16U,0x636920D8U,0x71574E69U,0xA458FEA3U,0xF4933D7EU, + 0x0D95748FU,0x728EB658U,0x718BCD58U,0x82154AEEU,0x7B54A41DU,0xC25A59B5U,0x9C30D539U,0x2AF26013U, + 0xC5D1B023U,0x286085F0U,0xCA417918U,0xB8DB38EFU,0x8E79DCB0U,0x603A180EU,0x6C9E0E8BU,0xB01E8A3EU, + 0xD71577C1U,0xBD314B27U,0x78AF2FDAU,0x55605C60U,0xE65525F3U,0xAA55AB94U,0x57489862U,0x63E81440U, + 0x55CA396AU,0x2AAB10B6U,0xB4CC5C34U,0x1141E8CEU,0xA15486AFU,0x7C72E993U,0xB3EE1411U,0x636FBC2AU, + 0x2BA9C55DU,0x741831F6U,0xCE5C3E16U,0x9B87931EU,0xAFD6BA33U,0x6C24CF5CU,0x7A325381U,0x28958677U, + 0x3B8F4898U,0x6B4BB9AFU,0xC4BFE81BU,0x66282193U,0x61D809CCU,0xFB21A991U,0x487CAC60U,0x5DEC8032U, + 0xEF845D5DU,0xE98575B1U,0xDC262302U,0xEB651B88U,0x23893E81U,0xD396ACC5U,0x0F6D6FF3U,0x83F44239U, + 0x2E0B4482U,0xA4842004U,0x69C8F04AU,0x9E1F9B5EU,0x21C66842U,0xF6E96C9AU,0x670C9C61U,0xABD388F0U, + 0x6A51A0D2U,0xD8542F68U,0x960FA728U,0xAB5133A3U,0x6EEF0B6CU,0x137A3BE4U,0xBA3BF050U,0x7EFB2A98U, + 0xA1F1651DU,0x39AF0176U,0x66CA593EU,0x82430E88U,0x8CEE8619U,0x456F9FB4U,0x7D84A5C3U,0x3B8B5EBEU, + 0xE06F75D8U,0x85C12073U,0x401A449FU,0x56C16AA6U,0x4ED3AA62U,0x363F7706U,0x1BFEDF72U,0x429B023DU, + 0x37D0D724U,0xD00A1248U,0xDB0FEAD3U,0x49F1C09BU,0x075372C9U,0x80991B7BU,0x25D479D8U,0xF6E8DEF7U, + 0xE3FE501AU,0xB6794C3BU,0x976CE0BDU,0x04C006BAU,0xC1A94FB6U,0x409F60C4U,0x5E5C9EC2U,0x196A2463U, + 0x68FB6FAFU,0x3E6C53B5U,0x1339B2EBU,0x3B52EC6FU,0x6DFC511FU,0x9B30952CU,0xCC814544U,0xAF5EBD09U, + 0xBEE3D004U,0xDE334AFDU,0x660F2807U,0x192E4BB3U,0xC0CBA857U,0x45C8740FU,0xD20B5F39U,0xB9D3FBDBU, + 0x5579C0BDU,0x1A60320AU,0xD6A100C6U,0x402C7279U,0x679F25FEU,0xFB1FA3CCU,0x8EA5E9F8U,0xDB3222F8U, + 0x3C7516DFU,0xFD616B15U,0x2F501EC8U,0xAD0552ABU,0x323DB5FAU,0xFD238760U,0x53317B48U,0x3E00DF82U, + 0x9E5C57BBU,0xCA6F8CA0U,0x1A87562EU,0xDF1769DBU,0xD542A8F6U,0x287EFFC3U,0xAC6732C6U,0x8C4F5573U, + 0x695B27B0U,0xBBCA58C8U,0xE1FFA35DU,0xB8F011A0U,0x10FA3D98U,0xFD2183B8U,0x4AFCB56CU,0x2DD1D35BU, + 0x9A53E479U,0xB6F84565U,0xD28E49BCU,0x4BFB9790U,0xE1DDF2DAU,0xA4CB7E33U,0x62FB1341U,0xCEE4C6E8U, + 0xEF20CADAU,0x36774C01U,0xD07E9EFEU,0x2BF11FB4U,0x95DBDA4DU,0xAE909198U,0xEAAD8E71U,0x6B93D5A0U, + 0xD08ED1D0U,0xAFC725E0U,0x8E3C5B2FU,0x8E7594B7U,0x8FF6E2FBU,0xF2122B64U,0x8888B812U,0x900DF01CU, + 0x4FAD5EA0U,0x688FC31CU,0xD1CFF191U,0xB3A8C1ADU,0x2F2F2218U,0xBE0E1777U,0xEA752DFEU,0x8B021FA1U, + 0xE5A0CC0FU,0xB56F74E8U,0x18ACF3D6U,0xCE89E299U,0xB4A84FE0U,0xFD13E0B7U,0x7CC43B81U,0xD2ADA8D9U, + 0x165FA266U,0x80957705U,0x93CC7314U,0x211A1477U,0xE6AD2065U,0x77B5FA86U,0xC75442F5U,0xFB9D35CFU, + 0xEBCDAF0CU,0x7B3E89A0U,0xD6411BD3U,0xAE1E7E49U,0x00250E2DU,0x2071B35EU,0x226800BBU,0x57B8E0AFU, + 0x2464369BU,0xF009B91EU,0x5563911DU,0x59DFA6AAU,0x78C14389U,0xD95A537FU,0x207D5BA2U,0x02E5B9C5U, + 0x83260376U,0x6295CFA9U,0x11C81968U,0x4E734A41U,0xB3472DCAU,0x7B14A94AU,0x1B510052U,0x9A532915U, + 0xD60F573FU,0xBC9BC6E4U,0x2B60A476U,0x81E67400U,0x08BA6FB5U,0x571BE91FU,0xF296EC6BU,0x2A0DD915U, + 0xB6636521U,0xE7B9F9B6U,0xFF34052EU,0xC5855664U,0x53B02D5DU,0xA99F8FA1U,0x08BA4799U,0x6E85076AU, + },{ + 0x4B7A70E9U,0xB5B32944U,0xDB75092EU,0xC4192623U,0xAD6EA6B0U,0x49A7DF7DU,0x9CEE60B8U,0x8FEDB266U, + 0xECAA8C71U,0x699A17FFU,0x5664526CU,0xC2B19EE1U,0x193602A5U,0x75094C29U,0xA0591340U,0xE4183A3EU, + 0x3F54989AU,0x5B429D65U,0x6B8FE4D6U,0x99F73FD6U,0xA1D29C07U,0xEFE830F5U,0x4D2D38E6U,0xF0255DC1U, + 0x4CDD2086U,0x8470EB26U,0x6382E9C6U,0x021ECC5EU,0x09686B3FU,0x3EBAEFC9U,0x3C971814U,0x6B6A70A1U, + 0x687F3584U,0x52A0E286U,0xB79C5305U,0xAA500737U,0x3E07841CU,0x7FDEAE5CU,0x8E7D44ECU,0x5716F2B8U, + 0xB03ADA37U,0xF0500C0DU,0xF01C1F04U,0x0200B3FFU,0xAE0CF51AU,0x3CB574B2U,0x25837A58U,0xDC0921BDU, + 0xD19113F9U,0x7CA92FF6U,0x94324773U,0x22F54701U,0x3AE5E581U,0x37C2DADCU,0xC8B57634U,0x9AF3DDA7U, + 0xA9446146U,0x0FD0030EU,0xECC8C73EU,0xA4751E41U,0xE238CD99U,0x3BEA0E2FU,0x3280BBA1U,0x183EB331U, + 0x4E548B38U,0x4F6DB908U,0x6F420D03U,0xF60A04BFU,0x2CB81290U,0x24977C79U,0x5679B072U,0xBCAF89AFU, + 0xDE9A771FU,0xD9930810U,0xB38BAE12U,0xDCCF3F2EU,0x5512721FU,0x2E6B7124U,0x501ADDE6U,0x9F84CD87U, + 0x7A584718U,0x7408DA17U,0xBC9F9ABCU,0xE94B7D8CU,0xEC7AEC3AU,0xDB851DFAU,0x63094366U,0xC464C3D2U, + 0xEF1C1847U,0x3215D908U,0xDD433B37U,0x24C2BA16U,0x12A14D43U,0x2A65C451U,0x50940002U,0x133AE4DDU, + 0x71DFF89EU,0x10314E55U,0x81AC77D6U,0x5F11199BU,0x043556F1U,0xD7A3C76BU,0x3C11183BU,0x5924A509U, + 0xF28FE6EDU,0x97F1FBFAU,0x9EBABF2CU,0x1E153C6EU,0x86E34570U,0xEAE96FB1U,0x860E5E0AU,0x5A3E2AB3U, + 0x771FE71CU,0x4E3D06FAU,0x2965DCB9U,0x99E71D0FU,0x803E89D6U,0x5266C825U,0x2E4CC978U,0x9C10B36AU, + 0xC6150EBAU,0x94E2EA78U,0xA5FC3C53U,0x1E0A2DF4U,0xF2F74EA7U,0x361D2B3DU,0x1939260FU,0x19C27960U, + 0x5223A708U,0xF71312B6U,0xEBADFE6EU,0xEAC31F66U,0xE3BC4595U,0xA67BC883U,0xB17F37D1U,0x018CFF28U, + 0xC332DDEFU,0xBE6C5AA5U,0x65582185U,0x68AB9802U,0xEECEA50FU,0xDB2F953BU,0x2AEF7DADU,0x5B6E2F84U, + 0x1521B628U,0x29076170U,0xECDD4775U,0x619F1510U,0x13CCA830U,0xEB61BD96U,0x0334FE1EU,0xAA0363CFU, + 0xB5735C90U,0x4C70A239U,0xD59E9E0BU,0xCBAADE14U,0xEECC86BCU,0x60622CA7U,0x9CAB5CABU,0xB2F3846EU, + 0x648B1EAFU,0x19BDF0CAU,0xA02369B9U,0x655ABB50U,0x40685A32U,0x3C2AB4B3U,0x319EE9D5U,0xC021B8F7U, + 0x9B540B19U,0x875FA099U,0x95F7997EU,0x623D7DA8U,0xF837889AU,0x97E32D77U,0x11ED935FU,0x16681281U, + 0x0E358829U,0xC7E61FD6U,0x96DEDFA1U,0x7858BA99U,0x57F584A5U,0x1B227263U,0x9B83C3FFU,0x1AC24696U, + 0xCDB30AEBU,0x532E3054U,0x8FD948E4U,0x6DBC3128U,0x58EBF2EFU,0x34C6FFEAU,0xFE28ED61U,0xEE7C3C73U, + 0x5D4A14D9U,0xE864B7E3U,0x42105D14U,0x203E13E0U,0x45EEE2B6U,0xA3AAABEAU,0xDB6C4F15U,0xFACB4FD0U, + 0xC742F442U,0xEF6ABBB5U,0x654F3B1DU,0x41CD2105U,0xD81E799EU,0x86854DC7U,0xE44B476AU,0x3D816250U, + 0xCF62A1F2U,0x5B8D2646U,0xFC8883A0U,0xC1C7B6A3U,0x7F1524C3U,0x69CB7492U,0x47848A0BU,0x5692B285U, + 0x095BBF00U,0xAD19489DU,0x1462B174U,0x23820E00U,0x58428D2AU,0x0C55F5EAU,0x1DADF43EU,0x233F7061U, + 0x3372F092U,0x8D937E41U,0xD65FECF1U,0x6C223BDBU,0x7CDE3759U,0xCBEE7460U,0x4085F2A7U,0xCE77326EU, + 0xA6078084U,0x19F8509EU,0xE8EFD855U,0x61D99735U,0xA969A7AAU,0xC50C06C2U,0x5A04ABFCU,0x800BCADCU, + 0x9E447A2EU,0xC3453484U,0xFDD56705U,0x0E1E9EC9U,0xDB73DBD3U,0x105588CDU,0x675FDA79U,0xE3674340U, + 0xC5C43465U,0x713E38D8U,0x3D28F89EU,0xF16DFF20U,0x153E21E7U,0x8FB03D4AU,0xE6E39F2BU,0xDB83ADF7U, + },{ + 0xE93D5A68U,0x948140F7U,0xF64C261CU,0x94692934U,0x411520F7U,0x7602D4F7U,0xBCF46B2EU,0xD4A20068U, + 0xD4082471U,0x3320F46AU,0x43B7D4B7U,0x500061AFU,0x1E39F62EU,0x97244546U,0x14214F74U,0xBF8B8840U, + 0x4D95FC1DU,0x96B591AFU,0x70F4DDD3U,0x66A02F45U,0xBFBC09ECU,0x03BD9785U,0x7FAC6DD0U,0x31CB8504U, + 0x96EB27B3U,0x55FD3941U,0xDA2547E6U,0xABCA0A9AU,0x28507825U,0x530429F4U,0x0A2C86DAU,0xE9B66DFBU, + 0x68DC1462U,0xD7486900U,0x680EC0A4U,0x27A18DEEU,0x4F3FFEA2U,0xE887AD8CU,0xB58CE006U,0x7AF4D6B6U, + 0xAACE1E7CU,0xD3375FECU,0xCE78A399U,0x406B2A42U,0x20FE9E35U,0xD9F385B9U,0xEE39D7ABU,0x3B124E8BU, + 0x1DC9FAF7U,0x4B6D1856U,0x26A36631U,0xEAE397B2U,0x3A6EFA74U,0xDD5B4332U,0x6841E7F7U,0xCA7820FBU, + 0xFB0AF54EU,0xD8FEB397U,0x454056ACU,0xBA489527U,0x55533A3AU,0x20838D87U,0xFE6BA9B7U,0xD096954BU, + 0x55A867BCU,0xA1159A58U,0xCCA92963U,0x99E1DB33U,0xA62A4A56U,0x3F3125F9U,0x5EF47E1CU,0x9029317CU, + 0xFDF8E802U,0x04272F70U,0x80BB155CU,0x05282CE3U,0x95C11548U,0xE4C66D22U,0x48C1133FU,0xC70F86DCU, + 0x07F9C9EEU,0x41041F0FU,0x404779A4U,0x5D886E17U,0x325F51EBU,0xD59BC0D1U,0xF2BCC18FU,0x41113564U, + 0x257B7834U,0x602A9C60U,0xDFF8E8A3U,0x1F636C1BU,0x0E12B4C2U,0x02E1329EU,0xAF664FD1U,0xCAD18115U, + 0x6B2395E0U,0x333E92E1U,0x3B240B62U,0xEEBEB922U,0x85B2A20EU,0xE6BA0D99U,0xDE720C8CU,0x2DA2F728U, + 0xD0127845U,0x95B794FDU,0x647D0862U,0xE7CCF5F0U,0x5449A36FU,0x877D48FAU,0xC39DFD27U,0xF33E8D1EU, + 0x0A476341U,0x992EFF74U,0x3A6F6EABU,0xF4F8FD37U,0xA812DC60U,0xA1EBDDF8U,0x991BE14CU,0xDB6E6B0DU, + 0xC67B5510U,0x6D672C37U,0x2765D43BU,0xDCD0E804U,0xF1290DC7U,0xCC00FFA3U,0xB5390F92U,0x690FED0BU, + 0x667B9FFBU,0xCEDB7D9CU,0xA091CF0BU,0xD9155EA3U,0xBB132F88U,0x515BAD24U,0x7B9479BFU,0x763BD6EBU, + 0x37392EB3U,0xCC115979U,0x8026E297U,0xF42E312DU,0x6842ADA7U,0xC66A2B3BU,0x12754CCCU,0x782EF11CU, + 0x6A124237U,0xB79251E7U,0x06A1BBE6U,0x4BFB6350U,0x1A6B1018U,0x11CAEDFAU,0x3D25BDD8U,0xE2E1C3C9U, + 0x44421659U,0x0A121386U,0xD90CEC6EU,0xD5ABEA2AU,0x64AF674EU,0xDA86A85FU,0xBEBFE988U,0x64E4C3FEU, + 0x9DBC8057U,0xF0F7C086U,0x60787BF8U,0x6003604DU,0xD1FD8346U,0xF6381FB0U,0x7745AE04U,0xD736FCCCU, + 0x83426B33U,0xF01EAB71U,0xB0804187U,0x3C005E5FU,0x77A057BEU,0xBDE8AE24U,0x55464299U,0xBF582E61U, + 0x4E58F48FU,0xF2DDFDA2U,0xF474EF38U,0x8789BDC2U,0x5366F9C3U,0xC8B38E74U,0xB475F255U,0x46FCD9B9U, + 0x7AEB2661U,0x8B1DDF84U,0x846A0E79U,0x915F95E2U,0x466E598EU,0x20B45770U,0x8CD55591U,0xC902DE4CU, + 0xB90BACE1U,0xBB8205D0U,0x11A86248U,0x7574A99EU,0xB77F19B6U,0xE0A9DC09U,0x662D09A1U,0xC4324633U, + 0xE85A1F02U,0x09F0BE8CU,0x4A99A025U,0x1D6EFE10U,0x1AB93D1DU,0x0BA5A4DFU,0xA186F20FU,0x2868F169U, + 0xDCB7DA83U,0x573906FEU,0xA1E2CE9BU,0x4FCD7F52U,0x50115E01U,0xA70683FAU,0xA002B5C4U,0x0DE6D027U, + 0x9AF88C27U,0x773F8641U,0xC3604C06U,0x61A806B5U,0xF0177A28U,0xC0F586E0U,0x006058AAU,0x30DC7D62U, + 0x11E69ED7U,0x2338EA63U,0x53C2DD94U,0xC2C21634U,0xBBCBEE56U,0x90BCB6DEU,0xEBFC7DA1U,0xCE591D76U, + 0x6F05E409U,0x4B7C0188U,0x39720A3DU,0x7C927C24U,0x86E3725FU,0x724D9DB9U,0x1AC15BB4U,0xD39EB8FCU, + 0xED545578U,0x08FCA5B5U,0xD83D7CD3U,0x4DAD0FC4U,0x1E50EF5EU,0xB161E6F8U,0xA28514D9U,0x6C51133CU, + 0x6FD5C7E7U,0x56E14EC4U,0x362ABFCEU,0xDDC6C837U,0xD79A3234U,0x92638212U,0x670EFA8EU,0x406000E0U, + },{ + 0x3A39CE37U,0xD3FAF5CFU,0xABC27737U,0x5AC52D1BU,0x5CB0679EU,0x4FA33742U,0xD3822740U,0x99BC9BBEU, + 0xD5118E9DU,0xBF0F7315U,0xD62D1C7EU,0xC700C47BU,0xB78C1B6BU,0x21A19045U,0xB26EB1BEU,0x6A366EB4U, + 0x5748AB2FU,0xBC946E79U,0xC6A376D2U,0x6549C2C8U,0x530FF8EEU,0x468DDE7DU,0xD5730A1DU,0x4CD04DC6U, + 0x2939BBDBU,0xA9BA4650U,0xAC9526E8U,0xBE5EE304U,0xA1FAD5F0U,0x6A2D519AU,0x63EF8CE2U,0x9A86EE22U, + 0xC089C2B8U,0x43242EF6U,0xA51E03AAU,0x9CF2D0A4U,0x83C061BAU,0x9BE96A4DU,0x8FE51550U,0xBA645BD6U, + 0x2826A2F9U,0xA73A3AE1U,0x4BA99586U,0xEF5562E9U,0xC72FEFD3U,0xF752F7DAU,0x3F046F69U,0x77FA0A59U, + 0x80E4A915U,0x87B08601U,0x9B09E6ADU,0x3B3EE593U,0xE990FD5AU,0x9E34D797U,0x2CF0B7D9U,0x022B8B51U, + 0x96D5AC3AU,0x017DA67DU,0xD1CF3ED6U,0x7C7D2D28U,0x1F9F25CFU,0xADF2B89BU,0x5AD6B472U,0x5A88F54CU, + 0xE029AC71U,0xE019A5E6U,0x47B0ACFDU,0xED93FA9BU,0xE8D3C48DU,0x283B57CCU,0xF8D56629U,0x79132E28U, + 0x785F0191U,0xED756055U,0xF7960E44U,0xE3D35E8CU,0x15056DD4U,0x88F46DBAU,0x03A16125U,0x0564F0BDU, + 0xC3EB9E15U,0x3C9057A2U,0x97271AECU,0xA93A072AU,0x1B3F6D9BU,0x1E6321F5U,0xF59C66FBU,0x26DCF319U, + 0x7533D928U,0xB155FDF5U,0x03563482U,0x8ABA3CBBU,0x28517711U,0xC20AD9F8U,0xABCC5167U,0xCCAD925FU, + 0x4DE81751U,0x3830DC8EU,0x379D5862U,0x9320F991U,0xEA7A90C2U,0xFB3E7BCEU,0x5121CE64U,0x774FBE32U, + 0xA8B6E37EU,0xC3293D46U,0x48DE5369U,0x6413E680U,0xA2AE0810U,0xDD6DB224U,0x69852DFDU,0x09072166U, + 0xB39A460AU,0x6445C0DDU,0x586CDECFU,0x1C20C8AEU,0x5BBEF7DDU,0x1B588D40U,0xCCD2017FU,0x6BB4E3BBU, + 0xDDA26A7EU,0x3A59FF45U,0x3E350A44U,0xBCB4CDD5U,0x72EACEA8U,0xFA6484BBU,0x8D6612AEU,0xBF3C6F47U, + 0xD29BE463U,0x542F5D9EU,0xAEC2771BU,0xF64E6370U,0x740E0D8DU,0xE75B1357U,0xF8721671U,0xAF537D5DU, + 0x4040CB08U,0x4EB4E2CCU,0x34D2466AU,0x0115AF84U,0xE1B00428U,0x95983A1DU,0x06B89FB4U,0xCE6EA048U, + 0x6F3F3B82U,0x3520AB82U,0x011A1D4BU,0x277227F8U,0x611560B1U,0xE7933FDCU,0xBB3A792BU,0x344525BDU, + 0xA08839E1U,0x51CE794BU,0x2F32C9B7U,0xA01FBAC9U,0xE01CC87EU,0xBCC7D1F6U,0xCF0111C3U,0xA1E8AAC7U, + 0x1A908749U,0xD44FBD9AU,0xD0DADECBU,0xD50ADA38U,0x0339C32AU,0xC6913667U,0x8DF9317CU,0xE0B12B4FU, + 0xF79E59B7U,0x43F5BB3AU,0xF2D519FFU,0x27D9459CU,0xBF97222CU,0x15E6FC2AU,0x0F91FC71U,0x9B941525U, + 0xFAE59361U,0xCEB69CEBU,0xC2A86459U,0x12BAA8D1U,0xB6C1075EU,0xE3056A0CU,0x10D25065U,0xCB03A442U, + 0xE0EC6E0EU,0x1698DB3BU,0x4C98A0BEU,0x3278E964U,0x9F1F9532U,0xE0D392DFU,0xD3A0342BU,0x8971F21EU, + 0x1B0A7441U,0x4BA3348CU,0xC5BE7120U,0xC37632D8U,0xDF359F8DU,0x9B992F2EU,0xE60B6F47U,0x0FE3F11DU, + 0xE54CDA54U,0x1EDAD891U,0xCE6279CFU,0xCD3E7E6FU,0x1618B166U,0xFD2C1D05U,0x848FD2C5U,0xF6FB2299U, + 0xF523F357U,0xA6327623U,0x93A83531U,0x56CCCD02U,0xACF08162U,0x5A75EBB5U,0x6E163697U,0x88D273CCU, + 0xDE966292U,0x81B949D0U,0x4C50901BU,0x71C65614U,0xE6C6C7BDU,0x327A140AU,0x45E1D006U,0xC3F27B9AU, + 0xC9AA53FDU,0x62A80F00U,0xBB25BFE2U,0x35BDD2F6U,0x71126905U,0xB2040222U,0xB6CBCF7CU,0xCD769C2BU, + 0x53113EC0U,0x1640E3D3U,0x38ABBD60U,0x2547ADF0U,0xBA38209CU,0xF746CE76U,0x77AFA1C5U,0x20756060U, + 0x85CBFE4EU,0x8AE88DD8U,0x7AAAF9B0U,0x4CF9AA7EU,0x1948C25CU,0x02FB8A8CU,0x01C36AE4U,0xD6EBE1F9U, + 0x90D4F869U,0xA65CDEA0U,0x3F09252DU,0xC208E69FU,0xB74E6132U,0xCE77E25BU,0x578FDFE3U,0x3AC372E6U + } +}; + diff --git a/code/redalert/blowfish.h b/code/redalert/blowfish.h new file mode 100644 index 0000000..24cfc2f --- /dev/null +++ b/code/redalert/blowfish.h @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWFISH.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWFISH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/14/96 * + * * + * Last Update : April 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BLOWFISH_H +#define BLOWFISH_H + +#include + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This engine will process data blocks by encryption and decryption. +** The "Blowfish" algorithm is in the public domain. It uses +** a Feistal network (similar to IDEA). It has no known +** weaknesses, but is still relatively new. Blowfish is particularly strong +** against brute force attacks. It is also quite strong against linear and +** differential cryptanalysis. Its weakness is that it takes a relatively +** long time to set up with a new key (1/100th of a second on a P6-200). +** The time to set up a key is equivalent to encrypting 4240 bytes. +*/ +class BlowfishEngine { + public: + BlowfishEngine(void) : IsKeyed(false) {} + ~BlowfishEngine(void); + + void Submit_Key(void const * key, int length); + + int Encrypt(void const * plaintext, int length, void * cyphertext); + int Decrypt(void const * cyphertext, int length, void * plaintext); + + /* + ** This is the maximum key length supported. + */ + enum {MAX_KEY_LENGTH=56}; + + private: + bool IsKeyed; + + void Sub_Key_Encrypt(unsigned long & left, unsigned long & right); + + void Process_Block(void const * plaintext, void * cyphertext, unsigned long const * ptable); + void Initialize_Tables(void); + + enum { + ROUNDS = 16, // Feistal round count (16 is standard). + BYTES_PER_BLOCK=8 // The number of bytes in each cypher block (don't change). + }; + + /* + ** Initialization data for sub keys. The initial values are constant and + ** filled with a number generated from pi. Thus they are not random but + ** they don't hold a weak pattern either. + */ + static unsigned long const P_Init[(int)ROUNDS+2]; + static unsigned long const S_Init[4][UCHAR_MAX+1]; + + /* + ** Permutation tables for encryption and decryption. + */ + unsigned long P_Encrypt[(int)ROUNDS+2]; + unsigned long P_Decrypt[(int)ROUNDS+2]; + + /* + ** S-Box tables (four). + */ + unsigned long bf_S[4][UCHAR_MAX+1]; +}; + +#endif + diff --git a/code/redalert/blowpipe.cpp b/code/redalert/blowpipe.cpp new file mode 100644 index 0000000..172d228 --- /dev/null +++ b/code/redalert/blowpipe.cpp @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blowpipe.h" +#include +#include + + +/*********************************************************************************************** + * BlowPipe::Flush -- Flushes any pending data out the pipe. * + * * + * If there is any pending data in the holding buffer, then this routine will force it to * + * be flushed out the end of the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of bytes output at the end final distant pipe * + * segment in the chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Flush(void) +{ + int total = 0; + if (Counter > 0 && BF != NULL) { + total += Pipe::Put(Buffer, Counter); + } + Counter = 0; + total += Pipe::Flush(); + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Put -- Submit a block of data for encrypt/decrypt. * + * * + * This will take the data block specified and process it before passing it on to the next * + * link in the pipe chain. A key must be submitted before this routine will actually perform* + * any processing. Prior to key submission, the data is passed through unchanged. * + * * + * INPUT: source -- Pointer to the buffer that contains the data to pass through. * + * * + * length -- The length of the data in the buffer. * + * * + * OUTPUT: Returns with then actual number of bytes output at the final distant end link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged in any way. + */ + if (BF == NULL) { + return(Pipe::Put(source, slen)); + } + + int total = 0; + + /* + ** If there is a partial block accumulated, then tag on the new data to + ** this block and process it if the block is full. Proceed with the bulk + ** processing if there are any left over bytes from this step. This step + ** can be skipped if there are no pending bytes in the buffer. + */ + if (Counter) { + int sublen = ((int)sizeof(Buffer)-Counter < slen) ? (sizeof(Buffer)-Counter) : slen; + memmove(&Buffer[Counter], source, sublen); + Counter += sublen; + source = ((char *)source) + sublen; + slen -= sublen; + + if (Counter == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(Buffer, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + Counter = 0; + } + } + + /* + ** Process the input data in blocks until there is not enough + ** source data to fill a full block of data. + */ + while (slen >= sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(source, sizeof(Buffer), Buffer); + } else { + BF->Encrypt(source, sizeof(Buffer), Buffer); + } + total += Pipe::Put(Buffer, sizeof(Buffer)); + source = ((char *)source) + sizeof(Buffer); + slen -= sizeof(Buffer); + } + + /* + ** If there are any left over bytes, then they must be less than the size of + ** the staging buffer. Store the bytes in the staging buffer for later + ** processing. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + + /* + ** Return with the total number of bytes flushed out to the final end of the + ** pipe chain. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowPipe::Key -- Submit a key to the blowfish pipe handler. * + * * + * This routine will take the key provided and use it to process the data that passes * + * through this pipe. Prior to a key being submitted, the data passes through the pipe * + * unchanged. * + * * + * INPUT: key -- Pointer to the key data to use. * + * * + * length-- The length of the key. The key length must not be greater than 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowPipe::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} + + + diff --git a/code/redalert/blowpipe.h b/code/redalert/blowpipe.h new file mode 100644 index 0000000..03bd0ef --- /dev/null +++ b/code/redalert/blowpipe.h @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLOWPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLOWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLOWPIPE_H +#define BLOWPIPE_H + +#include "pipe.h" +#include "blowfish.h" + +/* +** Performs Blowfish encryption/decryption on the data stream that is piped +** through this class. +*/ +class BlowPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowPipe(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowPipe(void) {delete BF;BF = NULL;} + virtual int Flush(void); + + virtual int Put(void const * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this pipe unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowPipe(BlowPipe & rvalue); + BlowPipe & operator = (BlowPipe const & pipe); +}; + + +#endif diff --git a/code/redalert/blwstraw.cpp b/code/redalert/blwstraw.cpp new file mode 100644 index 0000000..430349d --- /dev/null +++ b/code/redalert/blwstraw.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLWSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BlowStraw::Get -- Fetch a block of data from the straw. * + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "blwstraw.h" +#include +#include + + +/*********************************************************************************************** + * BlowStraw::Get -- Fetch a block of data from the straw. * + * * + * This routine will take a block of data from the straw and process it according to the * + * encrypt/decrypt flag and the key supplied. Prior to a key be supplied, the data passes * + * through this straw unchanged. * + * * + * INPUT: source -- Pointer to the buffer to hold the data being requested. * + * * + * length -- The length of the data being requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If the number * + * returned is less than the number requested, then this indicates that the data * + * source has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BlowStraw::Get(void * source, int slen) +{ + /* + ** Verify the parameter for legality. + */ + if (source == NULL || slen <= 0) { + return(0); + } + + /* + ** If there is no blowfish engine present, then merely pass the data through + ** unchanged. + */ + if (BF == NULL) { + return(Straw::Get(source, slen)); + } + + int total = 0; + + while (slen > 0) { + + /* + ** If there are any left over bytes in the buffer, pass them + ** through first. + */ + if (Counter > 0) { + int sublen = (slen < Counter) ? slen : Counter; + memmove(source, &Buffer[sizeof(Buffer)-Counter], sublen); + Counter -= sublen; + source = ((char *)source) + sublen; + slen -= sublen; + total += sublen; + } + if (slen == 0) break; + + /* + ** Fetch and encrypt/decrypt the next block. + */ + int incount = Straw::Get(Buffer, sizeof(Buffer)); + if (incount == 0) break; + + /* + ** Only full blocks are processed. Partial blocks are + ** merely passed through unchanged. + */ + if (incount == sizeof(Buffer)) { + if (Control == DECRYPT) { + BF->Decrypt(Buffer, incount, Buffer); + } else { + BF->Encrypt(Buffer, incount, Buffer); + } + } else { + memmove(&Buffer[sizeof(Buffer)-incount], Buffer, incount); + } + Counter = incount; + } + + /* + ** Return with the total number of bytes placed into the buffer. + */ + return(total); +} + + +/*********************************************************************************************** + * BlowStraw::Key -- Submit a key to the Blowfish straw. * + * * + * This will take the key specified and use it to process the data that flows through this * + * straw segment. Prior to a key being submitted, the data will flow through unchanged. * + * * + * INPUT: key -- Pointer to the key to submit. * + * * + * length-- The length of the key. The length must not exceed 56 bytes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void BlowStraw::Key(void const * key, int length) +{ + /* + ** Create the blowfish engine if one isn't already present. + */ + if (BF == NULL) { + BF = new BlowfishEngine; + } + + assert(BF != NULL); + + if (BF != NULL) { + BF->Submit_Key(key, length); + } +} diff --git a/code/redalert/blwstraw.h b/code/redalert/blwstraw.h new file mode 100644 index 0000000..a1779f0 --- /dev/null +++ b/code/redalert/blwstraw.h @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BLWSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BLWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BLWSTRAW_H +#define BLWSTRAW_H + +#include "straw.h" +#include "blowfish.h" + + +/* +** Performs Blowfish encryption/decryption to the data that is drawn through this straw. The +** process is controlled by the key which must be submitted to the class before any data +** manipulation will occur. The Blowfish algorithm is symmetric, thus the same key is used +** for encryption as is for decryption. +*/ +class BlowStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + BlowStraw(CryptControl control) : BF(NULL), Counter(0), Control(control) {} + virtual ~BlowStraw(void) {delete BF;BF = NULL;} + + virtual int Get(void * source, int slen); + + // Submit key for blowfish engine. + void Key(void const * key, int length); + + protected: + /* + ** The Blowfish engine used for encryption/decryption. If this pointer is + ** NULL, then this indicates that the blowfish engine is not active and no + ** key has been submitted. All data would pass through this straw unchanged + ** in that case. + */ + BlowfishEngine * BF; + + private: + char Buffer[8]; + int Counter; + CryptControl Control; + + BlowStraw(BlowStraw & rvalue); + BlowStraw & operator = (BlowStraw const & straw); +}; + + +#endif diff --git a/code/redalert/bmp8.cpp b/code/redalert/bmp8.cpp new file mode 100644 index 0000000..813326d --- /dev/null +++ b/code/redalert/bmp8.cpp @@ -0,0 +1,186 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "bmp8.h" + +//*********************************************************************************************** +BMP8::~BMP8() +{ + // free resources + if( hBitmap ) + ::DeleteObject( hBitmap ); + if( hPal ) + ::DeleteObject( hPal ); +} + +//*********************************************************************************************** +bool BMP8::Init( const char* szFile, HWND hWnd ) +{ + int i; + char string[128]; + DWORD dwRead; + BITMAPFILEHEADER bitmapHeader; + BITMAPINFOHEADER bitmapInfoHeader; + LPLOGPALETTE lpLogPalette; + char *palData; + HGLOBAL hmem2; + LPVOID lpvBits; + PAINTSTRUCT ps; + HDC hdc; + HPALETTE select; + UINT realize; + RECT rect; + + + // Remember window handle for use later. + this->hWnd = hWnd; + + // Retrieve a handle identifying the file. + HANDLE hFile = ::CreateFile( + szFile, + GENERIC_READ, + FILE_SHARE_READ, + (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_READONLY, + (HANDLE)NULL ); + + if( !hFile ) + return false; + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapHeader, sizeof(BITMAPFILEHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Retrieve the BITMAPFILEHEADER structure. + ::ReadFile( hFile, &bitmapInfoHeader, sizeof(BITMAPINFOHEADER), &dwRead, (LPOVERLAPPED)NULL ); + + // Allocate memory for the BITMAPINFO structure. + HGLOBAL infoHeaderMem = ::GlobalAlloc( GHND, sizeof(BITMAPINFOHEADER) + ((1<bmiHeader.biSize = bitmapInfoHeader.biSize; + lpHeaderMem->bmiHeader.biWidth = bitmapInfoHeader.biWidth; + lpHeaderMem->bmiHeader.biHeight = bitmapInfoHeader.biHeight; + lpHeaderMem->bmiHeader.biPlanes = bitmapInfoHeader.biPlanes; + lpHeaderMem->bmiHeader.biBitCount = bitmapInfoHeader.biBitCount; + lpHeaderMem->bmiHeader.biCompression = bitmapInfoHeader.biCompression; + lpHeaderMem->bmiHeader.biSizeImage = bitmapInfoHeader.biSizeImage; + lpHeaderMem->bmiHeader.biXPelsPerMeter = bitmapInfoHeader.biXPelsPerMeter; + lpHeaderMem->bmiHeader.biYPelsPerMeter = bitmapInfoHeader.biYPelsPerMeter; + lpHeaderMem->bmiHeader.biClrUsed = bitmapInfoHeader.biClrUsed; + lpHeaderMem->bmiHeader.biClrImportant = bitmapInfoHeader.biClrImportant; + + // Retrieve the color table. + // 1 << bitmapInfoHeader.biBitCount == 2 ^ bitmapInfoHeader.biBitCount + ::ReadFile( hFile, lpHeaderMem->bmiColors, ((1<palVersion=0x300; + lpLogPalette->palNumEntries=256; + + palData = (char*)lpHeaderMem->bmiColors; + + for( i = 0; i < 256; i++ ) + { + lpLogPalette->palPalEntry[i].peRed = *palData++; + lpLogPalette->palPalEntry[i].peGreen = *palData++; + lpLogPalette->palPalEntry[i].peBlue = *palData++; + lpLogPalette->palPalEntry[i].peFlags = *palData++; + } + hPal = ::CreatePalette( lpLogPalette ); + delete [] lpLogPalette; + + // Allocate memory for the required number of bytes. + hmem2 = ::GlobalAlloc( GHND, (bitmapHeader.bfSize - bitmapHeader.bfOffBits) ); + + lpvBits = ::GlobalLock( hmem2 ); + + // Retrieve the bitmap data. + ::ReadFile( hFile, lpvBits, (bitmapHeader.bfSize - bitmapHeader.bfOffBits), &dwRead, (LPOVERLAPPED)NULL ); + + // Create a bitmap from the data stored in the .BMP file. + hdc = ::GetDC( hWnd ); + select = ::SelectPalette( hdc, hPal, 0 ); + if( !select ) + return false; + realize = ::RealizePalette( hdc ); + if( realize == GDI_ERROR ) + return false; + + hBMP = ::CreateDIBitmap( hdc, &bitmapInfoHeader, CBM_INIT, lpvBits, lpHeaderMem, DIB_RGB_COLORS ); + ::ReleaseDC( hWnd, hdc ); + + // Unlock the global memory objects and close the .BMP file. + ::GlobalUnlock( infoHeaderMem ); + ::GlobalUnlock( hmem2 ); + ::CloseHandle( hFile ); + + if( !hBMP ) + return false; + + return true; +} + + +bit8 BMP8::drawBmp(void) +{ + // Paint the window (and draw the bitmap). + + PAINTSTRUCT ps; + HDC hdc; + char string[128]; + + InvalidateRect(WindowHandle_,NULL,FALSE); // keep windows from screwing up the + // redrawing (as much). + hdc=BeginPaint(WindowHandle_,&ps); + + //Do palette stuff + HPALETTE select=SelectPalette(ps.hdc,PalHandle_,0); + if (select==NULL) + { + sprintf(string,"Select Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + UINT realize=RealizePalette(ps.hdc); + if (realize==GDI_ERROR) + { + sprintf(string,"Realize Pal Fail: %d",GetLastError()); + MessageBox(NULL,string,"OK",MB_OK); + } + + HDC hdcMem = CreateCompatibleDC(ps.hdc); + SelectObject(hdcMem, BitmapHandle_); + BITMAP bm; + GetObject(BitmapHandle_, sizeof(BITMAP), (LPSTR) &bm); + + /// for non-stretching version + ///////BitBlt(ps.hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + RECT clientRect; + GetClientRect(WindowHandle_,&clientRect); + SetStretchBltMode(ps.hdc,COLORONCOLOR); + StretchBlt(ps.hdc,0,0,clientRect.right,clientRect.bottom,hdcMem,0,0,bm.bmWidth, + bm.bmHeight,SRCCOPY); + + + DeleteDC(hdcMem); + EndPaint(WindowHandle_,&ps); + return(TRUE); +} diff --git a/code/redalert/bmp8.h b/code/redalert/bmp8.h new file mode 100644 index 0000000..cee488c --- /dev/null +++ b/code/redalert/bmp8.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef BMP8_H +#define BMP8_H + +//#include +//#include +//#include "wstypes.h" +//#include "winblows.h" + +class BMP8 +{ +public: + BMP8() : hBMP( NULL ), hPal( NULL ), hWnd( NULL ) {} + ~BMP8(); + + bool Init( const char* szFile, HWND hWnd ); + bool Draw(void); // call this from your WM_PAINT message + +private: + HBITMAP hBMP; + HPALETTE hPal; + HWND hWnd; +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/buff.cpp b/code/redalert/buff.cpp new file mode 100644 index 0000000..d0f5654 --- /dev/null +++ b/code/redalert/buff.cpp @@ -0,0 +1,217 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : September 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Buffer::Buffer -- Constructor for buffer object. * + * Buffer::Buffer -- Copy constructor for buffer object. * + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * Buffer::Reset -- Clears the buffer object to null state. * + * Buffer::operator = -- Assignment operator for the buffer object. * + * Buffer::~Buffer -- Destructor for buffer object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include + + +/*********************************************************************************************** + * Buffer::Buffer -- Constructor for buffer object. * + * * + * This is the normal constructor for a buffer object. The buffer pointer and size are * + * specified as parameters. * + * * + * INPUT: buffer -- Pointer to the buffer. * + * * + * size -- The size of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to construct a Buffer object that has a pointer but a size * + * value of zero. The Buffer object can still be used for its pointer, but it * + * any function that requires a size will fail. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(void * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for char * pointer. +Buffer::Buffer(char * buffer, long size) : + BufferPtr(buffer), + Size(size), + IsAllocated(false) +{ +} + + +// Alternate constructor for void const * pointer. +Buffer::Buffer(void const * buffer, long size) : + BufferPtr((void*)buffer), + Size(size), + IsAllocated(false) +{ +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Self-allocating constructor for buffer object. * + * * + * This construtor for a buffer object will automatically allocate the bytes necessary * + * to fulfill the size requested. This object is also responsible for deleting the buffer * + * it allocated. * + * * + * INPUT: size -- The size of the buffer to allocated. * + * * + * OUTPUT: none * + * * + * WARNINGS: There is no way to tell if the allocation failed. To verify, call Get_Buffer * + * and compare with NULL. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(long size) : + BufferPtr(NULL), + Size(size), + IsAllocated(false) +{ + if (size > 0) { + BufferPtr = new char[size]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * Buffer::Buffer -- Copy constructor for buffer object. * + * * + * This will make a duplicate of the specified buffer object. The ownership of the pointer * + * remains with the original object. This prevents multiple deletion of the same pointer. * + * * + * INPUT: buffer -- Reference to the buffer object to be dupilcated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::Buffer(Buffer const & buffer) : + IsAllocated(false) +{ + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; +} + + +/*********************************************************************************************** + * Buffer::operator = -- Assignment operator for the buffer object. * + * * + * This will make a duplicate of the buffer object specified. Any buffer pointed to by the * + * left hand buffer will be lost (possibley freed as a result). * + * * + * INPUT: buffer -- Reference to the right hand buffer object. * + * * + * OUTPUT: Returns with a reference to the copied buffer object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/02/1996 JLB : Created. * + *=============================================================================================*/ +Buffer & Buffer::operator = (Buffer const & buffer) +{ + if (buffer != this) { + if (IsAllocated) { + delete [] BufferPtr; + } + IsAllocated = false; + BufferPtr = buffer.BufferPtr; + Size = buffer.Size; + } + return(*this); +} + + +/*********************************************************************************************** + * Buffer::~Buffer -- Destructor for buffer object. * + * * + * This destructor will free any buffer it is responsible for. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +Buffer::~Buffer(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * Buffer::Reset -- Clears the buffer object to null state. * + * * + * This routine will bring the buffer object into a null (newly constructed) state. If * + * there was any buffer allocated or referred to by this object, it will be freed or * + * dereferenced as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will free the buffer if it is responsible for doing so when * + * it is no longer referenced. * + * * + * HISTORY: * + * 09/07/1996 JLB : Created. * + *=============================================================================================*/ +void Buffer::Reset(void) +{ + if (IsAllocated) { + delete [] BufferPtr; + } + BufferPtr = NULL; + Size = 0; + IsAllocated = false; +} diff --git a/code/redalert/buff.h b/code/redalert/buff.h new file mode 100644 index 0000000..c0dfe7b --- /dev/null +++ b/code/redalert/buff.h @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFF.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/29/96 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCBUFF_H +#define CCBUFF_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** A general purpose buffer pointer handler object. It holds not only the pointer to the +** buffer, but its size as well. By using this class instead of separate pointer and size +** values, function interfaces and algorithms become simpler to manage and understand. +*/ +class Buffer { + public: + Buffer(char * ptr, long size=0); + Buffer(void * ptr=0, long size=0); + Buffer(void const * ptr, long size=0); + Buffer(long size); + Buffer(Buffer const & buffer); + ~Buffer(void); + + Buffer & operator = (Buffer const & buffer); + operator void * (void) const {return(BufferPtr);} + operator char * (void) const {return((char *)BufferPtr);} + + void Reset(void); + void * Get_Buffer(void) const {return(BufferPtr);} + long Get_Size(void) const {return(Size);} + bool Is_Valid(void) const {return(BufferPtr != 0);} + + protected: + + /* + ** Pointer to the buffer memory. + */ + void * BufferPtr; + + /* + ** The size of the buffer memory. + */ + long Size; + + /* + ** Was the buffer allocated by this class? If so, then this class + ** will be responsible for freeing the buffer. + */ + bool IsAllocated; +}; + + +#endif diff --git a/code/redalert/bufferx.h b/code/redalert/bufferx.h new file mode 100644 index 0000000..62ded06 --- /dev/null +++ b/code/redalert/bufferx.h @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUFFERX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUFFER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/04/96 * + * * + * Last Update : May 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef BUFFERx_H +#define BUFFERx_H + +#include "wwfile.h" + +/* +** This is a transmuter interface designed to aid implementation of compression, encryption, or +** data analysis classes. The Transmuter should be derived into a class that performs the necessary +** processing. +*/ +class Transmuter { + public: + Transmuter(void) : Output(0) {} + virtual ~Transmuter(void) {} + + /* + ** These are the interface function that are used to pass data to the transmuter. The + ** default implementation of these functions do nothing other than pass the data onto + ** the subsequent transmuter. For practical use, these functions should be overloaded to + ** do something more useful. + */ + virtual void Attach(Transmuter * transmuter) {Output = transmuter;} + virtual void Flush(void) {if (Output) Output->Flush();} + virtual void Put(const void * input, unsigned length) {if (Output) Output->Put(input, length);} + + protected: + + /* + ** Pointer to the output transmuter. + */ + Transmuter * Output; +}; + + +class FileTransmuter { + public: + FileTransmuter(FileClass * file = NULL) : OutputFile(file) {} + + virtual void Attach(FileClass * file) {OutputFile = file;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (OutputFile) OutputFile->Write(input, length);} + + protected: + FileClass * OutputFile; +}; + + +class BufferTransmuter { + public: + BufferTransmuter(void * buffer = NULL) : BufferPtr(buffer) {} + + virtual void Attach(void * buffer) {BufferPtr = buffer;} + virtual void Flush(void) {} + virtual void Put(const void * input, unsigned length) {if (BufferPtr) {memcpy(BufferPtr, input, length);((char *&)BufferPtr) += length;}} + + protected: + void * BufferPtr; +}; + + +#endif + + diff --git a/code/redalert/building.cpp b/code/redalert/building.cpp new file mode 100644 index 0000000..c78dd1d --- /dev/null +++ b/code/redalert/building.cpp @@ -0,0 +1,6069 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUILDING.CPP 5 3/13/97 5:18p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * BuildingClass::BuildingClass -- Constructor for buildings. * + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * BuildingClass::Captured -- Captures the building. * + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern.* + * BuildingClass::Click_With -- Handles clicking on the map while the building is selected. * + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * BuildingClass::Detach -- Handles target removal from the game system. * + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this buildin* + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * BuildingClass::Mark -- Building interface to map rendering system. * + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * BuildingClass::Mission_Construction -- Handles mission construction. * + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells * + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * BuildingClass::Value -- Determine the value of this building. * + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * BuildingClass::What_Action -- Determines what action will occur. * + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * BuildingClass::delete -- Deallocates building object. * + * BuildingClass::new -- Allocates a building object from building pool. * + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:35PM +*/ +#include "SidebarGlyphx.h" + +enum SAMState { + SAM_READY, // Launcher can be facing any direction tracking targets. + SAM_FIRING // Stationary while missile is being fired. +}; + + +/*************************************************************************** +** Center of building offset table. +*/ +COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { + 0x00800080L, + 0x008000FFL, + 0x00FF0080L, + 0x00FF00FFL, + 0x018000FFL, + 0x00FF0180L, + 0x01800180L, + + 0x00FF0200L, + + 0x02800280L, +}; + + +/*********************************************************************************************** + * BuildingClass::Receive_Message -- Handle an incoming message to the building. * + * * + * This routine handles an incoming message to the building. Messages regulate the * + * various cooperative ventures between buildings and units. This might include such * + * actions as coordinating the construction yard animation with the actual building's * + * construction animation. * + * * + * INPUT: from -- The originator of the message received. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter that might be used to return * + * extra information to the message originator. * + * * + * OUTPUT: Returns with the response to the message (typically, this is just RADIO_OK). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/09/1994 JLB : Created. * + * 06/26/1995 JLB : Forces refinery load anim to start immediately. * + * 08/13/1995 JLB : Uses ScenarioInit for special loose "CAN_LOAD" check. * + *=============================================================================================*/ +RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** This message is received as a request to attach/load/dock with this building. + ** Verify that this is allowed and return the appropriate response. + */ + case RADIO_CAN_LOAD: + TechnoClass::Receive_Message(from, message, param); + if (!House->Is_Ally(from)) return(RADIO_STATIC); + if (Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION || BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact() && Contact_With_Whom() != from)) return(RADIO_NEGATIVE); + switch (Class->Type) { + case STRUCT_AIRSTRIP: + if (from->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_HELIPAD: + if (from->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass const *)from)->Class->IsFixedWing) { + return(RADIO_ROGER); + } + break; + + case STRUCT_REPAIR: + if (from->What_Am_I() == RTTI_UNIT || (from->What_Am_I() == RTTI_AIRCRAFT)) { + if (Transmit_Message(RADIO_ON_DEPOT, from) != RADIO_ROGER) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + case STRUCT_REFINERY: + if (from->What_Am_I() == RTTI_UNIT && + *((UnitClass *)from) == UNIT_HARVESTER && + (ScenarioInit || !Is_Something_Attached())) { + + return(RADIO_ROGER); + } + break; + + default: + break; + } + return(RADIO_STATIC); + + /* + ** This message is received when the object has attached itself to this + ** building. + */ + case RADIO_IM_IN: + if (Mission == MISSION_DECONSTRUCTION) { + return(RADIO_NEGATIVE); + } + switch (Class->Type) { + case STRUCT_REPAIR: + IsReadyToCommence = true; + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_AIRSTRIP: + case STRUCT_HELIPAD: + Assign_Mission(MISSION_REPAIR); + from->Assign_Mission(MISSION_SLEEP); + return(RADIO_ROGER); + + case STRUCT_REFINERY: + Mark(MARK_CHANGE); + from->Assign_Mission(MISSION_UNLOAD); + return(RADIO_ROGER); + + default: + break; + } + break; + + /* + ** Docking maneuver maintenance message. See if new order should be given to the + ** unit trying to dock. + */ + case RADIO_DOCKING: + TechnoClass::Receive_Message(from, message, param); + + /* + ** When in radio contact for loading, the refinery starts + ** flashing the lights. + */ + if (*this == STRUCT_REFINERY && BState != BSTATE_FULL) { + Begin_Mode(BSTATE_FULL); + } + + /* + ** If this building is already in radio contact, then it might + ** be able to satisfy the request to load by bumping off any + ** preoccupying task. + */ + if (*this == STRUCT_REPAIR) { + if (Contact_With_Whom() != from) { + if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) { + if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + return(RADIO_ROGER); + } + } + } + } + + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + switch (Class->Type) { + case STRUCT_AIRSTRIP: + param = As_Target(); + break; + + case STRUCT_HELIPAD: + param = As_Target(); + break; + + case STRUCT_REPAIR: + Transmit_Message(RADIO_TETHER); + param = ::As_Target(Coord_Cell(Center_Coord())); + break; + + case STRUCT_REFINERY: + param = ::As_Target(Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S))); + break; + } + + /* + ** Tell the harvester to move to the docking pad of the building. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + + /* + ** Since the harvester is already there, tell it to begin the backup + ** procedure now. If it can't, then tell it to get outta here. + */ + Transmit_Message(RADIO_TETHER); + if (*this == STRUCT_REFINERY && Transmit_Message(RADIO_BACKUP_NOW, from) != RADIO_ROGER) { + from->Scatter(NULL, true, true); + } + } + } + return(RADIO_ROGER); + + /* + ** If a transport or harvester is requesting permission to head toward, dock + ** and load/unload, check to make sure that this is allowed given the current + ** state of the building. + */ + case RADIO_ARE_REFINERY: + if (Is_Something_Attached() || In_Radio_Contact() || IsInLimbo || House->Class->House != from->Owner() || (*this != STRUCT_REFINERY/* && *this != STRUCT_REPAIR*/)) { + return(RADIO_NEGATIVE); + } + return(RADIO_ROGER); + + /* + ** Someone is telling us that it is starting construction. This should only + ** occur if this is a construction yard and a building was just placed on + ** the map. + */ + case RADIO_BUILDING: + Assign_Mission(MISSION_REPAIR); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Someone is telling us that they have finished construction. This should + ** only occur if this is a construction yard and the building that was being + ** constructed has finished. In this case, stop the construction yard + ** animation. + */ + case RADIO_COMPLETE: + if (Mission != MISSION_DECONSTRUCTION) { + Assign_Mission(MISSION_GUARD); + } + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message may occur unexpectedly if the unit in contact with this + ** building is suddenly destroyed. Handle any cleanup necessary. For example, + ** a construction yard should stop its construction animation in this case. + */ + case RADIO_OVER_OUT: + Begin_Mode(BSTATE_IDLE); + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** This message is received when an object has completely left + ** building. Sometimes special cleanup action is required when + ** this event occurs. + */ + case RADIO_UNLOADED: + if (*this == STRUCT_REPAIR) { + if (Distance(from) < 0x0180) { + return(RADIO_ROGER); + } + } + TechnoClass::Receive_Message(from, message, param); + if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + return(RADIO_ROGER); + + default: + break; + } + + /* + ** Pass along the message to the default message handler in the radio itself. + */ + return(TechnoClass::Receive_Message(from, message, param)); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * BuildingClass::Debug_Dump -- Displays building status to the monochrome screen. * + * * + * This utility function will output the current status of the building class to the * + * monochrome screen. It is through this data that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Debug_Dump(MonoClass * mono) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_BUILDING)); + mono->Fill_Attrib(66, 13, 12, 1, IsRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToRebuild ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsAllowedToSell ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 16, 12, 1, IsCharging ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 17, 12, 1, IsCharged ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 18, 12, 1, IsJamming ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 19, 12, 1, IsJammed ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(1, 11); + if (Factory) { + mono->Printf("%s %d%%", Factory->Get_Object()->Class_Of().IniName, (100*Factory->Completion())/FactoryClass::STEP_COUNT); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Draw_It -- Displays the building at the location specified. * + * * + * This is the low level graphic routine that displays the building at the location * + * specified. * + * * + * INPUT: x,y -- The coordinate to draw the building at. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a clipping window parameter. * + * 07/06/1995 JLB : Handles damaged silos correctly. * + *=============================================================================================*/ +void BuildingClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); +// jmarshall + Image_t* hdimage = Get_HDImage_Data(); + if (hdimage != NULL) { + Techno_Draw_Object_HD(hdimage, Shape_Number(), x, y, window); + TechnoClass::Draw_It(x, y, window); + return; + } +// jmarshall end + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** Actually draw the building shape. + */ + IsTheaterShape = Class->IsTheater; //Let Build_Frame know if this is a theater specific shape + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window); + IsTheaterShape = false; + + /* + ** Patch for adding overlay onto weapon factory. Only add the overlay if + ** the building has more than 1 hp. Also, if the building's in radio + ** contact, he must be unloading a constructed vehicle, so draw that + ** vehicle before drawing the overlay. + */ + if (BState != BSTATE_CONSTRUCTION) { + + /* + ** A Tethered object is always rendered AFTER the building. + */ + if (*this == STRUCT_WEAP && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo && Contact_With_Whom()->What_Am_I() != RTTI_BUILDING) { + TechnoClass * contact = Contact_With_Whom(); + + assert(contact->IsActive); + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } + + /* + ** Draw the weapon factory custom overlay graphic. + */ + if ( (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP)) { + int shapenum = Door_Stage(); + if (Health_Ratio() <= Rule.ConditionYellow) shapenum += 4; + // Added override shape file name. ST - 8/1/2019 5:24PM + //Techno_Draw_Object(Class->WarFactoryOverlay, shapenum, x, y, window); + Techno_Draw_Object_Virtual(Class->WarFactoryOverlay, shapenum, x, y, window, DIR_N, 0x0100, "WEAP2"); + } + + /* + ** Draw any repair feedback graphic required. + */ + if (IsRepairing && IsWrenchVisible) { + CC_Draw_Shape(ObjectTypeClass::SelectShapes, SELECT_WRENCH, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + TechnoClass::Draw_It(x, y, window); + + /* + ** If this is a factory that we're spying on, show what it's producing + */ + if ((Spied_By() & (1<<(PlayerPtr->Class->House)) && Is_Selected_By_Player()) || ((window == WINDOW_VIRTUAL) && (Session.Type != GAME_NORMAL))) { + + /* + ** Fetch the factory that is associate with this building. For computer controlled buildings, the + ** factory pointer is integral to the building itself. For human controlled buildings, the factory + ** pointer is part of the house structure and must be retrieved from there. + */ + FactoryClass * factory = NULL; + if (House->IsHuman) { + factory = House->Fetch_Factory(Class->ToBuild); + } else { + factory = Factory; + } + + /* + ** If there is a factory associated with this building, then fetch any attached + ** object under production and display its cameo image over the top of this building. + */ + if (factory != NULL) { + TechnoClass * obj = factory->Get_Object(); + if (obj != NULL) { +#ifdef FIXIT_CSII + CC_Draw_Shape(obj, obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_NORMAL, NULL); +#else + void const * remapper = obj->House->Remap_Table(false, obj->Techno_Type_Class()->Remap); + CC_Draw_Shape(obj->Techno_Type_Class()->Get_Cameo_Data(), 0, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL | ((remapper != NULL) ? SHAPE_FADING : SHAPE_NORMAL), remapper); +#endif + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Shape_Number -- Fetch the shape number for this building. * + * * + * This routine will examine the current state of the building and return with the shape * + * number to use. The shape number is subordinate to the building graphic image data. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when rendering this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Shape_Number(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + int shapenum = Fetch_Stage(); + + /* + ** The shape file to use for rendering depends on whether the building + ** is undergoing construction or not. + */ + if (BState == BSTATE_CONSTRUCTION) { + + /* + ** If the building is deconstructing, then the display frame progresses + ** from the end to the beginning. Reverse the shape number accordingly. + */ + if (Mission == MISSION_DECONSTRUCTION) { + shapenum = (Class->Anims[BState].Start+Class->Anims[BState].Count-1)-shapenum; + } + + } else { + + /* + ** If this is a camouflaged pill box and it is not owned by the player, then + ** it is displayed with the MEGA-camouflaged imagery. + */ + if ((!IsOwnedByPlayer) && (*this == STRUCT_CAMOPILLBOX)) { + shapenum += 1; + } + + /* + ** The Tesla Coil has a stage value that can be overridden by + ** its current state. + */ + if (*this == STRUCT_TESLA) { + if (IsCharged) { + shapenum = 3; + } else { + if (IsCharging) { + shapenum = Fetch_Stage(); + } else { + shapenum = 0; + } + } + } + + /* + ** Buildings that contain a turret handle their shape determination + ** differently than normal buildings. They need to take into consideration + ** the direction the turret is facing. + */ + if (Class->IsTurretEquipped) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing.Current())]; + + if (*this == STRUCT_SAM) { + + /* + ** SAM sites that are free to rotate fetch their animation frame + ** from the building's turret facing. All other animation stages + ** fetch their frame from the embedded animation sequencer. + */ +// if (Status == SAM_READY || Status == SAM_FIRING || Status == SAM_LOCKING) { +// shapenum = Fetch_Stage(); +// } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 35; + } + } else { + if (IsInRecoilState) { + shapenum += 32; + } + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 64; + } + } + } else { + + /* + ** If it is a significantly damaged weapons factory, it is shown in + ** the worst state possible. + */ + if (*this == STRUCT_WEAP || *this == STRUCT_FAKEWEAP) { + shapenum = 0; + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum = 1; + } + + } else { + + /* + ** Special render stage for silos. The stage is dependent on the current + ** Tiberium collected as it relates to Tiberium capacity. + */ + if (*this == STRUCT_STORAGE) { + + int level = 0; + if (House->Capacity) { + level = (House->Tiberium * 5) / House->Capacity; + } + + shapenum += Bound(level, 0, 4); + if (Health_Ratio() <= Rule.ConditionYellow) { + shapenum += 5; + } + + } else { + + /* + ** If below half strenth, then show the damage frames of the + ** building. + */ + if (Health_Ratio() <= Rule.ConditionYellow) { + if (*this == STRUCT_CHRONOSPHERE) { + shapenum += 29; + } else { + int last1 = Class->Anims[BSTATE_IDLE].Start + Class->Anims[BSTATE_IDLE].Count; + int last2 = Class->Anims[BSTATE_ACTIVE].Start + Class->Anims[BSTATE_ACTIVE].Count; + int largest = max(last1, last2); + last2 = Class->Anims[BSTATE_AUX1].Start + Class->Anims[BSTATE_AUX1].Count; + largest = max(largest, last2); + last2 = Class->Anims[BSTATE_AUX2].Start + Class->Anims[BSTATE_AUX2].Count; + largest = max(largest, last2); + shapenum += largest; + } + } + } + } + } + } + return(shapenum); +} + + +/*********************************************************************************************** + * BuildingClass::Mark -- Building interface to map rendering system. * + * * + * This routine is used to mark the map cells so that when it renders * + * the underlying icons will also be updated as necessary. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Building is removed. * + * MARK_CHANGE -- Building changes shape. * + * MARK_DOWN -- Building is added. * + * * + * OUTPUT: bool; Did the mark operation succeed? Failure could be the result of marking down * + * when the building is already marked down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/31/1994 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added health bar tracking. * + * 12/23/1994 JLB : Calls low level check before proceeding. * + * 01/27/1995 JLB : Special road spacer template added. * + *=============================================================================================*/ +bool BuildingClass::Mark(MarkType mark) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { + short const * offset = Overlap_List(); + short const * occupy = Occupy_List(); + CELL cell = Coord_Cell(Coord); + SmudgeType bib; + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge != NULL) { + smudge->Disown(cell); + delete smudge; + } + } + break; + + case MARK_DOWN: + + /* + ** Special wall logic is handled here. A building that is really a wall + ** gets converted into an overlay wall type when it is placed down. The + ** actual building object itself is destroyed. + */ + if (Class->IsWall) { + switch (Class->Type) { + case STRUCT_BRICK_WALL: + new OverlayClass(OVERLAY_BRICK_WALL, cell, House->Class->House); + break; + + case STRUCT_BARBWIRE_WALL: + new OverlayClass(OVERLAY_BARBWIRE_WALL, cell, House->Class->House); + break; + + case STRUCT_SANDBAG_WALL: + new OverlayClass(OVERLAY_SANDBAG_WALL, cell, House->Class->House); + break; + + case STRUCT_WOOD_WALL: + new OverlayClass(OVERLAY_WOOD_WALL, cell, House->Class->House); + break; + + case STRUCT_CYCLONE_WALL: + new OverlayClass(OVERLAY_CYCLONE_WALL, cell, House->Class->House); + break; + + case STRUCT_FENCE: + new OverlayClass(OVERLAY_FENCE, cell, House->Class->House); + break; + + default: + break; + } + Transmit_Message(RADIO_OVER_OUT); + delete this; + + } else { + if (Can_Enter_Cell(cell) == MOVE_OK) { + /* + ** Determine if a bib is required for this building. If one is, then + ** create and place it. + */ + CELL newcell = cell; + if (Class->Bib_And_Offset(bib, newcell)) { + new SmudgeClass(bib, Cell_Coord(newcell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } + + Map.Place_Down(cell, this); + } else { + return(false); + } + } + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(false)); + Map.Refresh_Cells(cell, occupy); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * + * * + * This function is to handle the AI logic for the building. The graphic logic (facing, * + * firing, and animation) is handled elsewhere. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/26/1994 JLB : Handles production. * + * 06/11/1995 JLB : Revamped. * + *=============================================================================================*/ +void BuildingClass::AI(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Process building animation state changes. Transition to a following state + ** if there is one specified and the current animation sequence has expired. + ** This process must occur before mission AI since the mission AI relies on + ** the bstate change to occur immediately before the MissionClass::AI. + */ + Animation_AI(); + + /* + ** If now is a good time to act on a new mission, then do so. This process occurs + ** here because some outside event may have requested a mission change for the building. + ** Such outside requests (player input) must be initiated BEFORE the normal AI process. + */ + if (IsReadyToCommence && BState != BSTATE_CONSTRUCTION) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** Proceed with normal logic processing. This is where the mission processing + ** occurs. This call must be located after the animation sequence makes the + ** transition to the next frame (see above) in order for the mission logic to + ** act at the exact moment of graphic transition BEFORE it has a chance to + ** be displayed. + */ + TechnoClass::AI(); + + /* + ** Bail if the object died in the AI routine. + */ + if (!IsActive) { + return; + } + + /* + ** Building ammo is instantly reloaded. + */ + if (!Ammo) { + Ammo = Class->MaxAmmo; + } + + /* + ** If now is a good time to act on a new mission, then do so. This occurs here because + ** some AI event may have requested a mission change (usually from another mission + ** state machine). This must occur here before it has a chance to render. + */ + if (IsReadyToCommence) { + + /* + ** Clear the commencement flag ONLY if something actually occurred. By acting + ** this way, a building can set the IsReadyToCommence flag before it goes + ** to "sleep" knowing that it will wake up as soon as a new mission comes + ** along. + */ + if (Commence()) { + IsReadyToCommence = false; + } + } + + /* + ** If a change of animation was requested, then make the change + ** now. The building animation system acts independently but subordinate + ** to the mission state machine system. By performing the animation change-up + ** here, the mission AI system is ensured of immediate visual affect when it + ** decides to change the animation state of the building. + */ + if (QueueBState != BSTATE_NONE) { + if (BState != QueueBState) { + BState = QueueBState; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + } + QueueBState = BSTATE_NONE; + } + + /* + ** If the building's strength has changed, then update the power + ** accordingly. + */ + if (Strength != LastStrength) { + int oldpower = Power_Output(); + LastStrength = Strength; + int newpower = Power_Output(); + House->Adjust_Power(newpower - oldpower); + } + + /* + ** Check to see if the destruction countdown timer is active. If so, then decrement it. + ** When this timer reaches zero, the building is removed from the map. All the explosions + ** are presumed to be in progress at this time. + */ + if (Strength == 0) { + if (CountDown == 0) { + Limbo(); + Drop_Debris(WhomToRepay); + delete this; + } + return; + } + + /* + ** Charging logic. + */ + Charging_AI(); + + /* + ** Handle any repair process that may be going on. + */ + Repair_AI(); + + /* + ** For computer controlled buildings, determine what should be produced and start + ** production accordingly. + */ + Factory_AI(); + + /* + ** Check for demolition timeout. When timeout has expired, the building explodes. + */ + if (IsGoingToBlow && CountDown == 0) { + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + TechnoClass *saboteur = As_Techno(WhomToRepay); + if (saboteur && saboteur->IsActive && saboteur->House && saboteur->House->IsHuman) { + On_Achievement_Event(saboteur->House, "BUILDING_DESTROYED_C4", object_type->IniName); + } + } + + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_FIRE, As_Techno(WhomToRepay), true); + if (!IsActive) { + return; + } + Mark(MARK_CHANGE); + } + + /* + ** Turret equiped buildings must handle turret rotation logic here. This entails + ** rotating the turret to the desired facing as well as figuring out what that + ** desired facing should be. + */ + Rotation_AI(); + + /* + ** Gap Generators need to scan if they've just become activated, or if + ** the power has just come on enough so they can scan. Also, they need + ** to un-jam if the power has just dropped off. + */ + if (*this == STRUCT_GAP) { + if (Arm == 0) { + IsJamming = false; + Arm = TICKS_PER_MINUTE * Rule.GapRegenInterval + Random_Pick(1, TICKS_PER_SECOND); + } + + if (!IsJamming) { + if (House->Power_Fraction() >= 1) { + Map.Jam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + IsJamming = true; + } + } else { + if (House->Power_Fraction() < 1) { + IsJamming = false; + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + } + } + } + + /* + ** Radar facilities and SAMs need to check for the proximity of a mobile + ** radar jammer. + */ + if ((*this == STRUCT_RADAR || *this == STRUCT_SAM) && (Frame % TICKS_PER_SECOND) == 0) { + IsJammed = false; + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj != NULL && + !obj->IsInLimbo && + !obj->House->Is_Ally(House) && + obj->Class->IsJammer && + Distance(obj) <= Rule.RadarJamRadius) { + + IsJammed = true; + break; + } + } + } + + /* + ** Chronosphere active animation control. + */ + if (*this == STRUCT_CHRONOSPHERE && BState == BSTATE_ACTIVE && QueueBState == BSTATE_NONE && Scen.FadeTimer == 0) { + Begin_Mode(BSTATE_IDLE); + } +} + + +/*********************************************************************************************** + * BuildingClass::Unlimbo -- Removes a building from limbo state. * + * * + * Use this routine to transform a building that has been held in limbo * + * state, into one that really exists on the map. Once a building as * + * been unlimboed, then it becomes a normal object in the game world. * + * * + * INPUT: pos -- The position to place the building on the map. * + * * + * dir (optional) -- not used for this class * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: The unlimbo operation might not be successful if the * + * building could not be placed at the location specified. * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 06/07/1994 JLB : Matches virtual function format for base class. * + * 05/09/1995 JLB : Handles wall placement. * + * 06/18/1995 JLB : Checks for wall legality before placing down. * + *=============================================================================================*/ +bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this is a wall type building, then it never gets unlimboed. Instead, it gets + ** converted to an overlay type. + */ + if (Class->IsWall) { + if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + OverlayType otype = OVERLAY_NONE; + switch (Class->Type) { + case STRUCT_SANDBAG_WALL: + otype = OVERLAY_SANDBAG_WALL; + break; + + case STRUCT_CYCLONE_WALL: + otype = OVERLAY_CYCLONE_WALL; + break; + + case STRUCT_BRICK_WALL: + otype = OVERLAY_BRICK_WALL; + break; + + case STRUCT_BARBWIRE_WALL: + otype = OVERLAY_BARBWIRE_WALL; + break; + + case STRUCT_WOOD_WALL: + otype = OVERLAY_WOOD_WALL; + break; + + case STRUCT_FENCE: + otype = OVERLAY_FENCE; + break; + + default: + otype = OVERLAY_NONE; + break; + + } + if (otype != OVERLAY_NONE) { + ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); + if (o && o->Unlimbo(coord)) { + Map[coord].Owner = House->Class->House; + Transmit_Message(RADIO_OVER_OUT); + Map.Sight_From(Coord_Cell(coord), Class->SightRange, House); + delete this; + return(true); + } + } + } + return(false); + } + + /* + ** Normal building unlimbo process. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->BScan |= (1L << Class->Type); + House->ActiveBScan |= (1L << Class->Type); + + /* + ** Recalculate the center point of the house's base. + */ + House->Recalc_Center(); + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Add(this); + + /* + ** Possibly the sidebar will be affected by this addition. + */ + House->IsRecalcNeeded = true; + LastStrength = 0; + + // Changes to support client/server multiplayer. ST - 8/2/2019 2:36PM + //if ((!IsDiscoveredByPlayer && Map[coord].IsVisible) || Session.Type != GAME_NORMAL) { + if ((!Is_Discovered_By_Player(House) && Map[coord].Is_Visible(House)) || Session.Type != GAME_NORMAL) { + if (House->IsHuman) { + //Revealed(PlayerPtr); + Revealed(House); + } + } + if (!House->IsHuman) { + Revealed(House); + } + + // Changes to support client/server multiplayer. ST - 8/2/2019 2:36PM + //if (IsOwnedByPlayer) { + if (Is_Owned_By_Player()) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if ((Class->Ownable & (HOUSEF_GOOD | HOUSEF_BAD)) != (HOUSEF_GOOD | HOUSEF_BAD)) { + if (Class->Ownable & HOUSEF_GOOD) { + ActLike = HOUSE_GREECE; + } else { + ActLike = HOUSE_USSR; + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Take_Damage -- Inflicts damage points upon a building. * + * * + * This routine will inflict damage points upon the specified building. * + * It will handle the damage animation and building destruction. Use * + * this routine whenever a building is attacked. * + * * + * INPUT: damage -- Amount of damage to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The kind of damage to inflict. * + * * + * source -- The source of the damage. This is used to change targeting. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: true/false; Was the building destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1991 : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Added warhead modifier to damage. * + * 06/03/1994 JLB : Added source of damage as target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 07/15/1995 JLB : Power ratio gets adjusted. * + *=============================================================================================*/ +ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + int shakes; + + if (this != source /*&& !Class->IsInsignificant*/) { + + if (source) { + House->LATime = Frame; + House->LAType = source->What_Am_I(); + House->LAZone = House->Which_Zone(this); + House->LAEnemy = source->Owner(); + + if (!House->Is_Ally(source)) { + House->Enemy = source->Owner(); + } + + Base_Is_Attacked(source); + } + + short const * offset = Occupy_List(); + + /* + ** Memorize who they used to be in radio contact with. + */ + TechnoClass *tech = Contact_With_Whom(); + /* + ** Perform the low level damage assessment. + */ + res = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + switch (res) { + case RESULT_DESTROYED: + + /* + ** Add the building to the base prebuild list if allowed. This will force + ** the computer to rebuild this structure if it can. + */ + if (IsToRebuild && Class->Level != -1 && Base.House == House->Class->House && Base.Get_Node(this) == 0) { +// if (IsToRebuild && Class->IsBuildable && Base.House == House->Class->House && Base.Get_Node(this) == 0) { + Base.Nodes.Add(BaseNodeClass(Class->Type, Coord_Cell(Coord))); + } + + /* + ** Destroy all attached objects. + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + /* + ** If we were in contact with a landed plane, blow the plane up too. + */ + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + int damage = 500; + tech->Take_Damage(damage, 0, WARHEAD_AP, source, forced); + } + + Sound_Effect(VOC_KABOOM22, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + + /* + ** If the building is destroyed, then lots of + ** explosions occur. + */ + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Cell_Coord(cell)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0080), Random_Pick(0, 7), Random_Pick(1, 3)); + if (Percent_Chance(50)) { + new AnimClass(ANIM_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + new AnimClass(ANIM_FBALL1, Coord_Scatter(Cell_Coord(cell), 0x0040), Random_Pick(0, 3)); + } + + shakes = Class->Cost_Of() / 400; + if (shakes) { + Shake_The_Screen(shakes, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(shakes, source->Owner()); + } + } + Sound_Effect(VOC_CRUMBLE, Coord); + if (Mission == MISSION_DECONSTRUCTION) { + CountDown = 0; + Set_Rate(0); + } else { + CountDown = 8; + } + + /* + ** If it is in radio contact and the object seems to be attached, then tell + ** it to run away. + */ + if (In_Radio_Contact() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + Transmit_Message(RADIO_RUN_AWAY); + } + + /* + ** A force destruction will not generate survivors. + */ + if (forced || *this == STRUCT_KENNEL) { + IsSurvivorless = true; + } + + /* + ** Destruction of a radar facility or advanced communications + ** center will cause the spiedby field to change... + */ + if (SpiedBy) { + SpiedBy = 0; + StructType struc = *this; + if (struc == STRUCT_RADAR /* || struc == STRUCT_EYE */) { + Update_Radar_Spied(); + } + } + + /* + ** Destruction of a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** Destruction of a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass *obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + /* + ** Destruction of a barrel will cause the surrounding squares to + ** be hit with damage. + */ + if (*this == STRUCT_BARREL || *this == STRUCT_BARREL3) { + COORDINATE center = Center_Coord(); + CELL cellcenter = Coord_Cell(center); + + BulletClass * bullet; + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_N)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_N); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_E)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_E); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_S)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_S); + } + + bullet = new BulletClass(BULLET_INVISIBLE, ::As_Target(Adjacent_Cell(cellcenter, FACING_W)), 0, 200, WARHEAD_FIRE, MPH_MEDIUM_FAST); + if (bullet) { + bullet->Unlimbo(center, DIR_W); + } + } + + if (House) { + House->Check_Pertinent_Structures(); + } + + break; + + case RESULT_HALF: + if (*this == STRUCT_PUMP) { + AnimClass * anim = new AnimClass(ANIM_OILFIELD_BURN, Coord_Add(Coord, 0x00400130L), 1); + if (anim) { + anim->Attach_To(this); + } + } + // Fall into next case. + + case RESULT_MAJOR: + Sound_Effect(VOC_KABOOM1, Coord); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + AnimClass * anim = NULL; + + /* + ** Show pieces of fire to indicate that a significant change in + ** damage level has occurred. + */ + if (warhead == WARHEAD_FIRE) { + switch (Random_Pick(0, 5+Class->Width()+Class->Height())) { + case 0: + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + anim = new AnimClass(ANIM_ON_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 6: + case 7: + case 8: + anim = new AnimClass(ANIM_ON_FIRE_MED, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, Random_Pick(1, 3)); + break; + + case 9: + anim = new AnimClass(ANIM_ON_FIRE_BIG, Coord_Scatter(Cell_Coord(cell), 0x0060), 0, 1); + break; + + default: + break; + } + } else { + if (Percent_Chance(50)) { + /* + ** Building may catch on fire, but only if it wasn't a + ** renovator that caused the damage. + */ + if (source == NULL || source->What_Am_I() != RTTI_INFANTRY || *(InfantryClass *)source != INFANTRY_RENOVATOR) { + anim = new AnimClass(ANIM_FIRE_SMALL, Coord_Scatter(Cell_Coord(cell), 0x0060), Random_Pick(0, 7), Random_Pick(1, 3)); + } + } + } + /* + ** If the animation was created, then attach it to the building. + */ + if (anim) { + anim->Attach_To(this); + } + } + break; + + case RESULT_NONE: + break; + + case RESULT_LIGHT: + break; + } + + if (source && res != RESULT_NONE) { + + /* + ** If any damage occurred, then inform the house of this fact. If it is the player's + ** house, it might announce this fact. + */ + if (!Class->IsInsignificant) { + House->Attacked(this); + } + + /* + ** Save the type of the house that's doing the damage, so if the building burns + ** to death credit can still be given for the kill + */ + WhoLastHurtMe = source->Owner(); + + /* + ** When certain buildings are hit, they "snap out of it" and + ** return fire if they are able and allowed. + */ + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && + !House->Is_Ally(source) && + Class->PrimaryWeapon != NULL && + (!Target_Legal(TarCom) || !In_Range(TarCom))) { + + if (source->What_Am_I() != RTTI_AIRCRAFT && (!House->IsHuman || Rule.IsSmartDefense)) { + Assign_Target(source->As_Target()); + } else { + + /* + ** Generate a random rotation effect since there is nothing else that this + ** building can do. + */ + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Random_Pick(DIR_N, DIR_MAX)); + } + } + } + } + } + + return(res); +} + + +/*********************************************************************************************** + * BuildingClass::new -- Allocates a building object from building pool. * + * * + * This routine will allocate a building slot from the building alloc * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated building. If NULL is * + * returned, then this indicates a failure to allocate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + * 05/17/1994 JLB : Revamped allocation scheme * + * 07/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void * BuildingClass::operator new(size_t ) +{ + void * ptr = Buildings.Allocate(); + if (ptr) { + ((BuildingClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BuildingClass::delete -- Deallocates building object. * + * * + * This is the memory deallocation operation for a building object. * + * Since buildings are allocated out of a fixed memory block, all that * + * is needed is to flag the unit as inactive. * + * * + * INPUT: ptr -- Pointer to building to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::operator delete(void *ptr) +{ + if (ptr) { + ((BuildingClass *)ptr)->IsActive = false; + } + Buildings.Free((BuildingClass *)ptr); +} + + +/*********************************************************************************************** + * BuildingClass::BuildingClass -- Constructor for buildings. * + * * + * This routine inserts a building into the object tracking system. * + * It is placed into a limbo state unless a location is provided for * + * it to unlimbo at. * + * * + * INPUT: type -- The structure type to make this object. * + * * + * house -- The owner of this building. * + * * + * pos -- The position to unlimbo the building. If -1 is * + * specified, then the building remains in a limbo * + * state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + * 08/07/1995 JLB : Fixed act like value to match expected value. * + *=============================================================================================*/ +BuildingClass::BuildingClass(StructType type, HousesType house) : + TechnoClass(RTTI_BUILDING, Buildings.ID(this), house), + Class(BuildingTypes.Ptr((int)type)), + Factory(0), + ActLike(House->ActLike), + IsToRebuild(false), + IsToRepair(false), + IsAllowedToSell(true), + IsReadyToCommence(false), + IsRepairing(false), + IsWrenchVisible(false), + IsGoingToBlow(false), + IsSurvivorless(false), + IsCharging(false), + IsCharged(false), + IsCaptured(false), + IsJamming(false), + IsJammed(false), + HasFired(false), + HasOpened(false), + CountDown(0), + BState(BSTATE_NONE), + QueueBState(BSTATE_NONE), + WhoLastHurtMe(house), + WhomToRepay(TARGET_NONE), + AnimToTrack(TARGET_NONE), + LastStrength(0), + PlacementDelay(0) +{ + House->Tracking_Add(this); + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + Ammo = Class->MaxAmmo; + + /* + ** If the building could never be built, then it can never be sold either. This + ** is due to the lack of buildup animation. + */ + if (Class->Get_Buildup_Data() != NULL) { +// if (!Class->IsBuildable) { + IsAllowedToSell = false; + } + +// if (Session.Type == GAME_INTERNET) { +// House->BuildingTotals->Increment_Unit_Total( (int) type); +// } + +} + + +/*********************************************************************************************** + * BuildingClass::~BuildingClass -- Destructor for building type objects. * + * * + * This destructor for building objects will put the building in limbo if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +BuildingClass::~BuildingClass(void) +{ + if (GameActive && Class) { + if (House) { + House->Tracking_Remove(this); + } + BuildingClass::Limbo(); + } + Class = 0; + + delete (FactoryClass *)Factory; + Factory = 0; + ID = -1; +} + + +/*********************************************************************************************** + * BuildingClass::Drop_Debris -- Drops rubble when building is destroyed. * + * * + * This routine is called when a building is destroyed. It handles * + * placing the rubble down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 06/13/1995 JLB : Added smoke and normal infantry survivor possibility. * + * 07/16/1995 JLB : Survival rate depends on if captured or sabotaged. * + *=============================================================================================*/ +void BuildingClass::Drop_Debris(TARGET source) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * offset; + CELL cell; + + /* + ** Generate random survivors from the destroyed building. + */ + cell = Coord_Cell(Coord); + offset = Occupy_List(); + int odds = 2; + if (Target_Legal(WhomToRepay)) odds -= 1; + if (IsCaptured) odds += 6; + int count = How_Many_Survivors(); + while (*offset != REFRESH_EOL) { + CELL newcell; + + newcell = cell + *offset++; + CellClass const * cellptr = &Map[newcell]; + + /* + ** Infantry could run out of a destroyed building. + */ + if (!House->IsToDie && count > 0) { + InfantryClass * i = NULL; + + if (Random_Pick(0, odds) == 1) { + i = NULL; + InfantryType typ = Crew_Type(); + if (typ != INFANTRY_NONE) i = new InfantryClass(typ, House->Class->House); + if (i != NULL) { + if (Class->Get_Buildup_Data() != NULL && i->Class->IsNominal) i->IsTechnician = true; + ScenarioInit++; + if (i->Unlimbo(Cell_Coord(newcell), DIR_N)) { + count--; + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength); + i->Scatter(0, true); + if (source != TARGET_NONE && !House->Is_Ally(As_Object(source))) { + i->Assign_Mission(MISSION_ATTACK); + i->Assign_Target(source); + } else { + if (House->IsHuman) { + i->Assign_Mission(MISSION_GUARD); + } else { + i->Assign_Mission(MISSION_HUNT); + } + } + } else { + delete i; + } + ScenarioInit--; + } + } + } + + /* + ** Smoke and fire only appear on terrestrail cells. They should not appear on + ** rivers, clifs, or water cells. + */ + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + + /* + ** Possibly add some smoke rising from the ashes of the building. + */ + switch (Random_Pick(0, 5)) { + case 0: + case 1: + case 2: + new AnimClass(ANIM_SMOKE_M, Coord_Scatter(Cell_Coord(newcell), 0x0050, false), Random_Pick(0, 5), Random_Pick(1, 2)); + break; + + default: + break; + } + + /* + ** The building always scars the ground in some fashion. + */ + if (Percent_Chance(25)) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(newcell)); + } else { + new SmudgeClass(Random_Pick(SMUDGE_CRATER1, SMUDGE_CRATER6), Coord_Scatter(Cell_Coord(newcell), 0x0080, false)); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles clicking on the map while the building is selected.* + * * + * This interface routine handles when the player clicks on the map while this building * + * is currently selected. This is used to assign an override target to a turret or * + * guard tower. * + * * + * INPUT: target -- The target that was clicked upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK && object != NULL) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + + if (action == ACTION_TOGGLE_PRIMARY && Class->Is_Factory()) { + OutList.Add(EventClass(EventClass::PRIMARY, TargetClass(this))); + } +} + + +/*********************************************************************************************** + * BuildingClass::Active_Click_With -- Handles cell selection for buildings. * + * * + * This routine really only serves one purpose -- to allow targeting of the ground for * + * buildings that are equipped with weapons. * + * * + * INPUT: action -- The requested action to perform. * + * * + * cell -- The cell location to perform the action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 10/04/1995 JLB : Handles construction yard undeploy to move logic. * + *=============================================================================================*/ +void BuildingClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (action == ACTION_ATTACK) { + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + } + + if (action == ACTION_MOVE && *this == STRUCT_CONST) { + OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(::As_Target(cell)))); + OutList.Add(EventClass(EventClass::SELL, TargetClass(this))); + + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } +} + + +/*********************************************************************************************** + * BuildingClass::Assign_Target -- Assigns a target to the building. * + * * + * Assigning of a target to a building makes sense if the building is one that can attack. * + * This routine would be used to assign the attack target to a turret or guard tower. * + * * + * INPUT: target -- The target that was clicked on while this building was selected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 11/02/1994 JLB : Checks for range before assigning target. * + *=============================================================================================*/ +void BuildingClass::Assign_Target(TARGET target) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this != STRUCT_SAM && *this != STRUCT_AAGUN && !In_Range(target, 0)) { + target = TARGET_NONE; + } + + TechnoClass::Assign_Target(target); +} + + +/*********************************************************************************************** + * BuildingClass::Init -- Initialize the building system to an empty null state. * + * * + * This routine initializes the building system in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Init(void) +{ + Buildings.Free_All(); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Object -- Initiates an object to leave the building. * + * * + * This function is used to cause an object to exit the building. It is called when a * + * factory produces a vehicle or other mobile object and that object needs to exit the * + * building to join the ranks of a regular unit. Typically, the object is placed down on * + * the map such that it overlaps the building and then it is given a movement order so that * + * it will move to an adjacent free cell. * + * * + * INPUT: base -- Pointer to the object that is to exit the building. * + * * + * OUTPUT: Returns the success rating for the exit attempt; * + * 0 = complete failure (refund money please) * + * 1 = temporarily prevented (try again later please) * + * 2 = successful * + * * + * WARNINGS: The building is placed in radio contact with the object. The object is in a * + * tethered condition. This condition will be automatically broken when the * + * object reaches the adjacent square. * + * * + * HISTORY: * + * 11/28/1994 JLB : Created. * + * 04/10/1995 JLB : Handles building production by computer. * + * 06/17/1995 JLB : Handles refinery exit. * + *=============================================================================================*/ +int BuildingClass::Exit_Object(TechnoClass * base) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!base) return(0); + + TechnoTypeClass const * ttype = (TechnoTypeClass const *)&base->Class_Of(); + + /* + ** A unit exiting a building is always considered to be "locked". That means, it + ** will be considered as to have legally entered the visible map domain. + */ + base->IsLocked = true; + + /* + ** Find a good cell to unload the object to. The object, probably a vehicle + ** will drive/walk to the adjacent free cell. + */ + CELL cell = 0; + + switch (base->What_Am_I()) { + + case RTTI_AIRCRAFT: + if (!In_Radio_Contact()) { + AircraftClass * air = (AircraftClass *)base; + + air->Height = 0; + ScenarioInit++; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + Transmit_Message(RADIO_HELLO, air); + Transmit_Message(RADIO_TETHER); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } else { + AircraftClass * air = (AircraftClass *)base; + + if (Cell_X(Coord_Cell(Center_Coord())) - Map.MapCellX < Map.MapCellWidth/2) { + cell = XY_Cell(Map.MapCellX-1, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } else { + cell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Random_Pick(0, Map.MapCellHeight-1)+Map.MapCellY); + } + ScenarioInit++; + if (air->Unlimbo(Cell_Coord(cell), DIR_N)) { +//BG air->Assign_Destination(::As_Target(Nearby_Location(air))); +/*BG*/ air->Assign_Destination(::As_Target(air->Nearby_Location(this))); + air->Assign_Mission(MISSION_MOVE); + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + case RTTI_VESSEL: + switch (Class->Type) { + case STRUCT_SUB_PEN: + case STRUCT_SHIP_YARD: + ScenarioInit++; + cell = Find_Exit_Cell(base); + if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) { + base->Assign_Mission(MISSION_GUARD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + default: + break; + } + break; + + case RTTI_INFANTRY: + case RTTI_UNIT: + switch (Class->Type) { + case STRUCT_REFINERY: + if (base->What_Am_I() == RTTI_UNIT) { + cell = Coord_Cell(Center_Coord()); + UnitClass * unit = (UnitClass *)base; + + cell = Adjacent_Cell(cell, FACING_SW); + ScenarioInit++; + if (unit->Unlimbo(Cell_Coord(Adjacent_Cell(cell, DIR_S)), DIR_SW_X2)) { + unit->PrimaryFacing = DIR_S; + unit->Assign_Mission(MISSION_HARVEST); + } + ScenarioInit--; + } else { + base->Scatter(0, true); + } + break; + + case STRUCT_WEAP: + if (Mission == MISSION_UNLOAD) { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg->Owner() == Owner() && *bldg == STRUCT_WEAP && bldg != this && bldg->Mission == MISSION_GUARD && !bldg->Factory) { + FactoryClass * temp = Factory; + bldg->Factory = Factory; + Factory = 0; + int retval = (bldg->Exit_Object(base)); + bldg->Factory = 0; + Factory = temp; + return(retval); + } + } + return(1); // fail while we're still unloading previous + } + ScenarioInit++; + if (base->Unlimbo(Exit_Coord(), DIR_S)) { + base->Mark(MARK_UP); + base->Coord = Exit_Coord(); + base->Mark(MARK_DOWN); + Transmit_Message(RADIO_HELLO, base); + Transmit_Message(RADIO_TETHER); + Assign_Mission(MISSION_UNLOAD); + ScenarioInit--; + return(2); + } + ScenarioInit--; + break; + + case STRUCT_BARRACKS: + case STRUCT_TENT: + case STRUCT_KENNEL: + + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + + /* + ** Establish radio contact so unload coordination can occur. This + ** radio contact should always succeed. + */ + if (Transmit_Message(RADIO_HELLO, base) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOAD); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + + default: + cell = Find_Exit_Cell(base); + if (cell != 0) { + DirType dir = Direction(cell); + COORDINATE start = Exit_Coord(); + + ScenarioInit++; + if (base->Unlimbo(start, dir)) { + + base->Assign_Mission(MISSION_MOVE); + + /* + ** When disembarking from a transport then guard an area around the + ** center of the base. + */ + base->Assign_Destination(::As_Target(cell)); + if (House->IQ >= Rule.IQGuardArea) { + base->Assign_Mission(MISSION_GUARD_AREA); + base->ArchiveTarget = ::As_Target(House->Where_To_Go((FootClass *)base)); + } + ScenarioInit--; + return(2); + } + ScenarioInit--; + } + break; + } + break; + + case RTTI_BUILDING: + + if (!House->IsHuman) { + + /* + ** Find the next available spot to place this newly created building. If the + ** building could be placed at the desired location, fine. If not, then this + ** routine will return failure. The calling routine will probably abandon this + ** building in preference to building another. + */ + BaseNodeClass * node = Base.Next_Buildable(((BuildingClass *)base)->Class->Type); + COORDINATE coord = 0; + if (node) { + coord = Cell_Coord(node->Cell); + } else { + + /* + ** Find a suitable new spot to place. + */ + coord = House->Find_Build_Location((BuildingClass *)base); + } + + if (coord) { + if (Flush_For_Placement(base, Coord_Cell(coord))) { + return(1); + } + if (base->Unlimbo(coord)) { + if (node && ((BuildingClass *)base)->Class->Type == House->BuildStructure) { + House->BuildStructure = STRUCT_NONE; + } + return(2); + } + } + } + break; + + default: + break; + } + + /* + ** Failure to exit the object results in a false return value. + */ + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Only do this for real human players. ST - 3/22/2019 1:38PM + */ + if (PlayerPtr != House) { + if (Session.Type != GAME_GLYPHX_MULTIPLAYER || House->IsHuman == false) { + return; + } + } + + bool buildable_via_capture = (IsCaptured && ActLike != House->ActLike) ? true : false; + + if (!IsInLimbo && Is_Discovered_By_Player()) { + switch (Class->ToBuild) { + int i; + int u; + int f; + int a; + int v; + + case RTTI_VESSELTYPE: + for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) { + if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference((VesselType)v), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_VESSELTYPE, v, House, buildable_via_capture); + } else { + Map.Add(RTTI_VESSELTYPE, v, buildable_via_capture); + } + } + } + break; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference((StructType)i), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_BUILDINGTYPE, i, House, buildable_via_capture); + } else { + Map.Add(RTTI_BUILDINGTYPE, i, buildable_via_capture); + } + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference((UnitType)u), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_UNITTYPE, u, House, buildable_via_capture); + } else { + Map.Add(RTTI_UNITTYPE, u, buildable_via_capture); + } + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference((InfantryType)f), ActLike)) { + if (InfantryTypeClass::As_Reference((InfantryType)f).IsDog) { + if (*this == STRUCT_KENNEL) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); + } else { + Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); + } + } + } else { + if (*this != STRUCT_KENNEL) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_INFANTRYTYPE, f, House, buildable_via_capture); + } else { + Map.Add(RTTI_INFANTRYTYPE, f, buildable_via_capture); + } + } + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference((AircraftType)a), ActLike)) { + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Sidebar_Glyphx_Add(RTTI_AIRCRAFTTYPE, a, House, buildable_via_capture); + } else { + Map.Add(RTTI_AIRCRAFTTYPE, a, buildable_via_capture); + } + } + } + break; + + default: + break; + } + } +} + + + +#if (0) //Old code for reference. ST - 8/2/2019 2:41PM +/*********************************************************************************************** + * BuildingClass::Update_Buildables -- Informs sidebar of additional construction options. * + * * + * This routine will tell the sidebar of objects that can be built. The function is called * + * whenever a building matures. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/11/1994 JLB : Created. * + * 12/23/1994 JLB : Only updates for PLAYER buildings. * + *=============================================================================================*/ +void BuildingClass::Update_Buildables(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (House == PlayerPtr && !IsInLimbo && IsDiscoveredByPlayer) { + switch (Class->ToBuild) { + int i; + int u; + int f; + int a; + int v; + + case RTTI_VESSELTYPE: + for (v = VESSEL_FIRST; v < VESSEL_COUNT; v++) { + if (PlayerPtr->Can_Build(&VesselTypeClass::As_Reference((VesselType)v), ActLike)) { + Map.Add(RTTI_VESSELTYPE, v); + } + } + break; + + case RTTI_BUILDINGTYPE: + for (i = STRUCT_FIRST; i < STRUCT_COUNT; i++) { + if (PlayerPtr->Can_Build(&BuildingTypeClass::As_Reference((StructType)i), ActLike)) { + Map.Add(RTTI_BUILDINGTYPE, i); + } + } + break; + + case RTTI_UNITTYPE: + for (u = UNIT_FIRST; u < UNIT_COUNT; u++) { + if (PlayerPtr->Can_Build(&UnitTypeClass::As_Reference((UnitType)u), ActLike)) { + Map.Add(RTTI_UNITTYPE, u); + } + } + break; + + case RTTI_INFANTRYTYPE: + for (f = INFANTRY_FIRST; f < INFANTRY_COUNT; f++) { + if (PlayerPtr->Can_Build(&InfantryTypeClass::As_Reference((InfantryType)f), ActLike)) { + if (InfantryTypeClass::As_Reference((InfantryType)f).IsDog) { + if (*this == STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } else { + if (*this != STRUCT_KENNEL) { + Map.Add(RTTI_INFANTRYTYPE, f); + } + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + for (a = AIRCRAFT_FIRST; a < AIRCRAFT_COUNT; a++) { + if (PlayerPtr->Can_Build(&AircraftTypeClass::As_Reference((AircraftType)a), ActLike)) { + Map.Add(RTTI_AIRCRAFTTYPE, a); + } + } + break; + + default: + break; + } + } +} +#endif + + +/*********************************************************************************************** + * BuildingClass::Fire_Out -- Handles when attached animation expires. * + * * + * This routine is used to perform any fixups necessary when the attached animation has * + * terminated. This occurs when the fire & smoke animation that a SAM site produces stops. * + * At that point, normal reload procedures can commence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Fire_Out(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); +} + + +/*********************************************************************************************** + * BuildingClass::Limbo -- Handles power adjustment as building goes into limbo. * + * * + * This routine will handle the power adjustments for the associated house when the * + * building goes into limbo. This means that its power drain or production is subtracted * + * from the house accumulated totals. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the building limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Limbo(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + + /* + ** Update the total factory type, assuming this building has a factory. + */ + House->Active_Remove(this); + House->IsRecalcNeeded = true; + House->Recalc_Center(); + + /* + ** Update the power status of the owner's house. + */ + House->Adjust_Power(-Power_Output()); + House->Adjust_Drain(-Class->Drain); + House->Adjust_Capacity(-Class->Capacity, true); + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + /* + ** This could be a building that builds. If so, then the sidebar may need adjustment. + ** Set IsInLimbo to true to "fool" the sidebar into knowing that this building + ** isn't available. Set it back to false so the rest of the Limbo code works. + ** Otherwise, the sidebar won't properly remove non-available buildables. + */ +// if (IsOwnedByPlayer && !ScenarioInit) { +// IsInLimbo = true; +// Map.Recalc(); +// IsInLimbo = false; +// } + } + return(TechnoClass::Limbo()); +} + + +/*********************************************************************************************** + * BuildingClass::Turret_Facing -- Fetches the turret facing for this building. * + * * + * This will return the turret facing for this building. Some buildings don't have a * + * visual turret (e.g., pillbox) so they return a turret facing that always faces their * + * current target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current facing of the turret. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Turret_Facing(void) const +{ + if (!Class->IsTurretEquipped && Target_Legal(TarCom)) { + return(::Direction(Center_Coord(), As_Coord(TarCom))); + } + return(PrimaryFacing.Current()); +} + + +/*********************************************************************************************** + * BuildingClass::Greatest_Threat -- Searches for target that building can fire upon. * + * * + * This routine intercepts the Greatest_Threat function so that it can add the ability * + * to search for ground targets, if this isn't a SAM site. * + * * + * INPUT: threat -- The base threat control value. Typically, it might be THREAT_RANGE * + * or THREAT_NORMAL. * + * * + * OUTPUT: Returns with a suitable target. If none could be found, then TARGET_NONE is * + * returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +TARGET BuildingClass::Greatest_Threat(ThreatType threat) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + threat = threat | THREAT_RANGE; + +// if (Class->PrimaryWeapon != NULL) { +// if (Class->PrimaryWeapon->Bullet->IsAntiAircraft) { +// threat = threat | THREAT_AIR; +// } +// if (Class->PrimaryWeapon->Bullet->IsAntiGround) { +// threat = threat | THREAT_BUILDINGS|THREAT_INFANTRY|THREAT_BOATS|THREAT_VEHICLES; +// } +// threat = threat | THREAT_RANGE; +// } + return(TechnoClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * BuildingClass::Grand_Opening -- Handles construction completed special operations. * + * * + * This routine is called when construction has finished. Typically, this enables * + * new production options for factories. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + * 06/13/1995 JLB : Added helipad. * + *=============================================================================================*/ +void BuildingClass::Grand_Opening(bool captured) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (!HasOpened || captured) { + HasOpened = true; + + /* + ** Adjust the owning house according to the power, drain, and Tiberium capacity that + ** this building has. + */ + House->Adjust_Drain(Class->Drain); + House->Adjust_Capacity(Class->Capacity); + House->IsRecalcNeeded = true; + + /* SPECIAL CASE: + ** Tiberium Refineries get a free harvester. Add a harvester to the + ** reinforcement list at this time. + */ + if (*this == STRUCT_REFINERY && !ScenarioInit && !captured && !Debug_Map && (!House->IsHuman || PurchasePrice == 0 || PurchasePrice > Class->Raw_Cost())) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), DIR_S)); + + UnitClass * unit = new UnitClass(UNIT_HARVESTER, House->Class->House); + if (unit != NULL) { + + /* + ** Try to place down the harvesters. If it could not be placed, then try + ** to place it in a nearby location. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_W)) { + cell = unit->Nearby_Location(this); + + /* + ** If the harvester could still not be placed, then refund the money + ** to the owner and then bail. + */ + if (!unit->Unlimbo(Cell_Coord(cell), DIR_SW)) { + House->Refund_Money(unit->Class->Cost_Of()); + delete unit; + } + } + } else { + + /* + ** If the harvester could not be created in the first place, then give + ** the full refund price to the owning player. + */ + House->Refund_Money(UnitTypeClass::As_Reference(UNIT_HARVESTER).Cost_Of()); + } + } + + /* + ** Helicopter pads get a free attack helicopter. + */ + if (!Rule.IsSeparate && *this == STRUCT_HELIPAD && !captured) { + ScenarioInit++; + AircraftClass * air = 0; + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_BAD || House->ActLike == HOUSE_UKRAINE) { + air = new AircraftClass(AIRCRAFT_HIND, House->Class->House); + } else { + air = new AircraftClass(AIRCRAFT_LONGBOW, House->Class->House); + } + if (air) { + air->Height = 0; + if (air->Unlimbo(Docking_Coord(), air->Pose_Dir())) { + air->Assign_Mission(MISSION_GUARD); + air->Transmit_Message(RADIO_HELLO, this); + Transmit_Message(RADIO_TETHER); + } + } + ScenarioInit--; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair -- Initiates or terminates the repair process. * + * * + * This routine will start, stop, or toggle the repair process. When a building repairs, it * + * occurs incrementally over time. * + * * + * INPUT: control -- Determines how to control the repair process. * + * 0: Turns repair process off (if it was on). * + * 1: Turns repair process on (if it was off). * + * -1:Toggles repair process to other state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/08/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (control) { + case -1: + IsRepairing = (IsRepairing == false); + break; + + case 1: + if (IsRepairing) return; + IsRepairing = true; + break; + + case 0: + if (!IsRepairing) return; + IsRepairing = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + VocType soundid = VOC_NONE; + if (IsRepairing) { + if (Strength == Class->MaxStrength) { + soundid = VOC_SCOLD; + } else { + soundid = VOC_CLICK; + if (House->IsPlayerControl) { + Clicked_As_Target(PlayerPtr->Class->House); // 2019/09/20 JAS - Added record of who clicked on the object + } + IsWrenchVisible = true; + } + } else { + soundid = VOC_CLICK; + } + + if (House->IsPlayerControl) { + Sound_Effect(soundid, Coord); + } +} + + +/*********************************************************************************************** + * BuildingClass::Sell_Back -- Controls the sell back (demolish) operation. * + * * + * This routine will initiate or stop the sell back process for a building. It is called * + * when the player clicks on a building when the sell mode is active. * + * * + * INPUT: control -- The action to perform. 0 = turn deconstruction off, 1 = deconstruct, * + * -1 = toggle deconstruction state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Sell_Back(int control) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Get_Buildup_Data()) { + bool decon = false; + switch (control) { + case -1: + decon = (Mission != MISSION_DECONSTRUCTION); + break; + + case 1: + if (Mission == MISSION_DECONSTRUCTION) return; + if (IsGoingToBlow) return; + decon = true; + break; + + case 0: + if (Mission != MISSION_DECONSTRUCTION) return; + decon = false; + break; + + default: + break; + } + + /* + ** At this point, we know that the repair state has changed. Perform + ** appropriate action. + */ + if (decon) { + Assign_Mission(MISSION_DECONSTRUCTION); + Commence(); + if (House->IsPlayerControl) { + Clicked_As_Target(PlayerPtr->Class->House); + } + } + if (House->IsPlayerControl) { + Sound_Effect(VOC_CLICK); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines action to perform if click on specified object. * + * * + * This routine will determine what action to perform if the mouse was clicked on the * + * object specified. This determination is used to control the mouse imagery and the * + * function process when the mouse button is pressed. * + * * + * INPUT: object -- Pointer to the object that, if clicked on, will control what action * + * is to be performed. * + * * + * OUTPUT: Returns with the ActionType that will occur if the mouse is clicked over the * + * object specified while the building is currently selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(ObjectClass const * object) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(object); + + if (action == ACTION_SELF) { + int index; + if (Class->Is_Factory() && PlayerPtr == House && *House->Factory_Counter(Class->ToBuild) > 1) { + switch (Class->ToBuild) { + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + action = ACTION_NONE; + if (*this == STRUCT_KENNEL) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_KENNEL) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } else { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_INFANTRYTYPE && *bldg != STRUCT_KENNEL) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + break; + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + action = ACTION_NONE; + if (*this == STRUCT_AIRSTRIP) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && *bldg == STRUCT_AIRSTRIP) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + else { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (bldg != this && bldg->Owner() == Owner() && bldg->Class->ToBuild == RTTI_AIRCRAFTTYPE && *bldg != STRUCT_AIRSTRIP) { + action = ACTION_TOGGLE_PRIMARY; + break; + } + } + } + break; + + case RTTI_UNITTYPE: + case RTTI_UNIT: + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + action = ACTION_TOGGLE_PRIMARY; + break; + + case RTTI_NONE: + action = ACTION_NONE; + break; + + default: + break; + } + + } else { + action = ACTION_NONE; + } + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. Also don't allow targeting if the object is too + ** far away. + */ + if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN || !In_Range(object, 0))) { + action = ACTION_NONE; + } + + if (action == ACTION_MOVE) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::What_Action -- Determines what action will occur. * + * * + * This routine examines the cell specified and returns with the action that will be * + * performed if that cell were clicked upon while the building is selected. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the ActionType that indicates what should occur if the mouse is clicked * + * on this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/18/1995 JLB : Created. * + *=============================================================================================*/ +ActionType BuildingClass::What_Action(CELL cell) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + ActionType action = TechnoClass::What_Action(cell); + + if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Is_MCV_Deploy())) { + action = ACTION_NONE; + } + + /* + ** Don't allow targeting of SAM sites, even if the CTRL key + ** is held down. + */ + if (action == ACTION_ATTACK && Class->PrimaryWeapon != NULL && !Class->PrimaryWeapon->Bullet->IsAntiGround) { +// if (action == ACTION_ATTACK && (*this == STRUCT_SAM || *this == STRUCT_AAGUN)) { + action = ACTION_NONE; + } + + return(action); +} + + +/*********************************************************************************************** + * BuildingClass::Begin_Mode -- Begins an animation mode for the building. * + * * + * This routine will start the building animating. This animation will loop indefinitely * + * until explicitly stopped. * + * * + * INPUT: bstate -- The animation state to initiate. * + * * + * OUTPUT: none * + * * + * WARNINGS: The building graphic state will reflect the first stage of this animation the * + * very next time it is rendered. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/02/1995 JLB : Uses normalize animation rate where applicable. * + *=============================================================================================*/ +void BuildingClass::Begin_Mode(BStateType bstate) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + QueueBState = bstate; + if (BState == BSTATE_NONE || bstate == BSTATE_CONSTRUCTION || ScenarioInit) { + BState = bstate; + QueueBState = BSTATE_NONE; + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + int rate = ctrl->Rate; + if (Class->IsRegulated && bstate != BSTATE_CONSTRUCTION) { + rate = Options.Normalize_Delay(rate); + } + Set_Rate(rate); + Set_Stage(ctrl->Start); + } +} + + +/*********************************************************************************************** + * BuildingClass::Center_Coord -- Fetches the center coordinate for the building. * + * * + * This routine is used to set the center coordinate for this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate for the center location for the building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Center_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, CenterOffset[Class->Size])); +} + + +/*********************************************************************************************** + * BuildingClass::Docking_Coord -- Fetches the coordinate to use for docking. * + * * + * This routine will return the coordinate to use when an object wishes to dock with this * + * building. Normally the docking coordinate would be the center of the building. * + * Exceptions to this would be the airfield and helipad. Their docking coordinates are * + * offset to match the building artwork. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to head to when trying to dock with this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Docking_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_HELIPAD) { + return(Coord_Add(Coord, XYP_COORD(24, 18))); + } + if (*this == STRUCT_AIRSTRIP) { + return(Coord_Add(Coord, XYP_COORD(ICON_PIXEL_W + ICON_PIXEL_W/2, 28))); + } + return(TechnoClass::Docking_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Fire -- Determines if this building can fire. * + * * + * Use this routine to see if the building can fire its weapon. * + * * + * * + * INPUT: target -- The target that firing upon is desired. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the fire possibility code. If firing is allowed, then FIRE_OK is * + * returned. Other cases will result in appropriate fire code value that indicates * + * why firing is not allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +FireErrorType BuildingClass::Can_Fire(TARGET target, int which) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + FireErrorType canfire = TechnoClass::Can_Fire(target, which); + + if (canfire == FIRE_OK) { + + /* + ** Double check to make sure that the facing is roughly toward + ** the target. If the difference is too great, then firing is + ** temporarily postponed. + */ + if (Class->IsTurretEquipped) { + int diff = PrimaryFacing.Difference(Direction(TarCom)); + diff = abs(diff); + if (ABS(diff) > (*this == STRUCT_SAM ? 64 : 8)) { +// if (ABS(diff) > 8) { + return(FIRE_FACING); + } + + /* + ** If the turret is rotating then firing must be delayed. + */ +// if (PrimaryFacing.Is_Rotating()) { +// return(FIRE_ROTATING); +// } + } + + /* + ** Certain buildings cannot fire if there is insufficient power. + */ + if (Class->IsPowered && House->Power_Fraction() < 1) { + return(FIRE_BUSY); + } + + /* + ** If an obelisk can fire, check the state of charge. + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && !IsCharged) { + return(FIRE_BUSY); + } + } + return(canfire); +} + + +/*********************************************************************************************** + * BuildingClass::Toggle_Primary -- Toggles the primary factory state. * + * * + * This routine will change the primary factory state of this building. The primary * + * factory is the one that units will be produced from (by default). * + * * + * INPUT: none * + * * + * OUTPUT: Is this building NOW the primary factory? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Toggle_Primary(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (IsLeader) { + IsLeader = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (!building->IsInLimbo && building->Owner() == Owner() && building->Class->ToBuild == Class->ToBuild) { + if (Class->ToBuild == RTTI_INFANTRYTYPE) { + if (*building == STRUCT_KENNEL && *this == STRUCT_KENNEL) { + building->IsLeader = false; + } else { + if (*building != STRUCT_KENNEL && *this != STRUCT_KENNEL) { + building->IsLeader = false; + } + } + } else if (Class->ToBuild == RTTI_AIRCRAFTTYPE) { + if (*building == STRUCT_AIRSTRIP && *this == STRUCT_AIRSTRIP) { + building->IsLeader = false; + } else { + if (*building != STRUCT_AIRSTRIP && *this != STRUCT_AIRSTRIP) { + building->IsLeader = false; + } + } + } else { + building->IsLeader = false; + } + } + } + IsLeader = true; + // + // MBL 04.20.2020 - Update so that each player in multiplayer will properly hear this when it applies to them + // + // if ((HouseClass *)House == PlayerPtr) { + // Speak(VOX_PRIMARY_SELECTED); + // } + if ((HouseClass *)House->IsHuman) { + Speak(VOX_PRIMARY_SELECTED, House); + } + } + Mark(MARK_CHANGE); + return(IsLeader); +} + + +/*********************************************************************************************** + * BuildingClass::Captured -- Captures the building. * + * * + * This routine will change the owner of the building. It handles updating any related * + * game systems as a result. Factories are the most prone to have great game related * + * consequences when captured. This could also affect the sidebar and building ownership. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the capture attempt successful? * + * * + * WARNINGS: Capturing could fail if the house is already owned by the one specified or * + * the building isn't allowed to be captured. * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + * 07/05/1995 JLB : Fixed production problem with capturing enemy buildings. * + *=============================================================================================*/ +bool BuildingClass::Captured(HouseClass * newowner) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsCaptureable && newowner != House) { +#ifdef TOFIX + switch (Owner()) { + case HOUSE_GOOD: + Speak(VOX_GDI_CAPTURED); + break; + + case HOUSE_BAD: + Speak(VOX_NOD_CAPTURED); + break; + } +#endif + + /* + ** Maybe trigger an achivement. ST - 11/14/2019 1:53PM + */ + if (newowner->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + if (newowner->ActLike != House->ActLike) { + On_Achievement_Event(newowner, "OPPOSING_BUILDING_CAPTURED", object_type->IniName); + } else { + On_Achievement_Event(newowner, "BUILDING_CAPTURED", object_type->IniName); + } + } + } + + /* + ** Make sure the capturer isn't spying on his own building, and if + ** it was a radar facility, update the target house's RadarSpied field. + */ + if (SpiedBy & (1<<(newowner->Class->House)) ) { + SpiedBy -= (1<<(newowner->Class->House)); + if (*this == STRUCT_RADAR) { + Update_Radar_Spied(); + } + } + + if (House == PlayerPtr) { + Map.PowerClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + IsJamming = false; + Arm = 0; + } + + /* + ** Add this building to the list of buildings captured this game. For internet stats purposes. + */ + if (Session.Type == GAME_INTERNET) { + newowner->CapturedBuildings->Increment_Unit_Total (Class->Type); + } + + House->Adjust_Power(-Power_Output()); + LastStrength = 0; + House->Adjust_Drain(-Class->Drain); + int booty = House->Adjust_Capacity(-Class->Capacity, true); + + /* + ** If there is something loaded, then it gets captured as well. + */ + TechnoClass * tech = Attached_Object(); + if (tech) tech->Captured(newowner); + + /* + ** If something isn't technically attached, but is sitting on this + ** building for another reason (e.g., helicopter on helipad), then it + ** gets captured as well. + */ + tech = Contact_With_Whom(); + if (tech) { + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && (::Distance(tech->Center_Coord(), Docking_Coord()) < 0x0040 || + (tech->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)tech)->Class->IsFixedWing && ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND)) ) { + tech->Captured(newowner); + } else { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } + } + + /* + ** Abort any computer production in progress. + */ + if (Factory) { + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** Decrement the factory counter for the original owner. + */ + House->Active_Remove(this); + + /* + ** Flag that both owners now need to update their buildable lists. + */ + House->IsRecalcNeeded = true; + newowner->IsRecalcNeeded = true; + HouseClass * oldowner = House; + TARGET tocap = As_Target(); + + IsCaptured = true; + TechnoClass::Captured(newowner); + + oldowner->ToCapture = tocap; + oldowner->Recalc_Center(); + House->Recalc_Center(); + if (House->ToCapture == As_Target()) { + House->ToCapture = TARGET_NONE; + } + + SmudgeType bib; + CELL cell = Coord_Cell(Coord); + if (Class->Bib_And_Offset(bib, cell)) { + SmudgeClass * smudge = new SmudgeClass(bib); + if (smudge) { + smudge->Disown(cell); + delete smudge; + } +#ifdef FIXIT_CAPTURE_BIB + if (Session.Type == GAME_NORMAL) { + new SmudgeClass(bib, Cell_Coord(cell), Class->IsBase ? House->Class->House : HOUSE_NONE); + } else { + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); + } +#else + new SmudgeClass(bib, Cell_Coord(cell), House->Class->House); +#endif + } + + House->Stole(Refund_Amount()); + + /* + ** Increment the factory count for the new owner. + */ + House->Active_Add(this); + + IsRepairing = false; + Grand_Opening(true); + House->Harvested(booty); + + Mark(MARK_CHANGE); + + /* + ** Perform a look operation when captured if it was the player + ** that performed the capture. + */ + if (Session.Type == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) { + Look(false); + } else { + if (House == PlayerPtr) { + Look(false); + } + } + /* + ** If it was spied upon by the player who just captured it, clear the + ** spiedby flag for that house. + */ + if (SpiedBy & (1 << (newowner->Class->House))) { + SpiedBy &= ~(1 << (newowner->Class->House)); + } + + /* + ** Update the new building's colors on the radar map. + */ + short const * offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + CELL cell = Coord_Cell(Coord) + *offset++; + Map.Radar_Pixel(cell); + } + + if (oldowner) { + oldowner->Check_Pertinent_Structures(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Sort_Y -- Returns the building coordinate used for sorting. * + * * + * The coordinate value returned from this function should be used for sorting purposes. * + * It has special offset adjustment applied so that vehicles don't overlap (as much). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value suitable to be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 06/19/1995 JLB : Handles buildings that come with bibs built-in. * + *=============================================================================================*/ +COORDINATE BuildingClass::Sort_Y(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_REPAIR) { + return(Coord); + } + if (*this == STRUCT_HELIPAD) { + return(Center_Coord()); + } + if (*this == STRUCT_AIRSTRIP) { + return(Center_Coord()); + } + if (*this == STRUCT_BARRACKS /*|| *this == STRUCT_POWER*/) { + return(Center_Coord()); + } + if (*this == STRUCT_REFINERY) { + return(Center_Coord()); + } + + /* + ** Mines need to bias their sort location such that they are typically drawn + ** before any objects that might overlap them. + */ + if (*this == STRUCT_AVMINE || *this == STRUCT_APMINE) { + return(Coord_Move(Center_Coord(), DIR_N, CELL_LEPTON_H)); + } + + return(Coord_Add(Center_Coord(), XY_Coord(0, (Class->Height()*256)/3))); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Enter_Cell -- Determines if building can be placed down. * + * * + * This routine will determine if the building can be placed down at the location * + * specified. * + * * + * INPUT: cell -- The cell to examine. This is usually the cell of the upper left corner * + * of the building if it were to be placed down. * + * * + * OUTPUT: Returns with the move legality value for placement at the location specified. This * + * will either be MOVE_OK or MOVE_NO. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST && IsDown) { + return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); + } + + if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) { + return(MOVE_NO); + } + + return(Class->Legal_Placement(cell) ? MOVE_OK : MOVE_NO); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Demolish -- Can the player demolish (sell back) the building? * + * * + * Determines if the player can sell this building. Selling is possible if the building * + * is not currently in construction or deconstruction animation. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building be demolished at this time? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 07/01/1995 JLB : If there is no buildup data, then the building can't be sold. * + * 07/17/1995 JLB : Cannot sell a refinery that has a harvester attached. * + *=============================================================================================*/ +bool BuildingClass::Can_Demolish(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsUnsellable) return(false); + + if (Class->Get_Buildup_Data() && BState != BSTATE_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION && Mission != MISSION_CONSTRUCTION) { + if (*this == STRUCT_REFINERY && Is_Something_Attached()) return(false); + return(true); + } + return(false); +} + + +bool BuildingClass::Can_Demolish_Unit(void) const +{ + return((*this == STRUCT_REPAIR || *this == STRUCT_AIRSTRIP) && In_Radio_Contact() && Distance(Contact_With_Whom()) < 0x0080); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. * + * * + * Buildings that can attack are given this mission. They will wait until a suitable target * + * comes within range and then launch into the attack mission. Buildings that have no * + * weaponry will just sit in this routine forever. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine will be called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Guard(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If this building has a weapon, then search for a target to attack. When + ** a target is found, switch into attack mode to deal with the threat. + */ + if (Is_Weapon_Equipped()) { + + /* + ** Weapon equipped buildings are ALWAYS ready to launch into another mission if + ** they are sitting around in guard mode. + */ + IsReadyToCommence = true; + + /* + ** If there is no target available, then search for one. + */ + if (!Target_Legal(TarCom)) { + ThreatType threat = THREAT_NORMAL; + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** There is a valid target. Switch into attack mode right away. + */ + if (Target_Legal(TarCom)) { + Assign_Mission(MISSION_ATTACK); + Commence(); + return(1); + } + } else { + + /* + ** This is the very simple state machine that basically does + ** nothing. This is the mode that non weapon equipped buildings + ** are normally in. + */ + enum { + INITIAL_ENTRY, + IDLE + }; + switch (Status) { + case INITIAL_ENTRY: + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + case IDLE: + /* + ** Special case to break out of guard mode if this is a repair + ** facility and there is a customer waiting at the grease pit. + */ + if (*this == STRUCT_REPAIR && + In_Radio_Contact() && + Contact_With_Whom()->Is_Techno() && + ((TechnoClass *)Contact_With_Whom())->Mission == MISSION_ENTER && + Distance(Contact_With_Whom()) < 0x0040 && + Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + + Assign_Mission(MISSION_REPAIR); + return(1); + } + break; + + default: + break; + } + + if (*this == STRUCT_REPAIR) { + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } else { + return(MissionControl[Mission].Normal_Delay() * 3 + Random_Pick(0, 2)); + } + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Construction -- Handles mission construction. * + * * + * This routine will handle mission construction. When this mission is complete, the * + * building will begin normal operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Construction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_CONSTRUCTION); + Transmit_Message(RADIO_BUILDING); + if (House->IsPlayerControl) { + Sound_Effect(VOC_CONSTRUCTION, Coord); + } + Status = DURING; + break; + + case DURING: + if (IsReadyToCommence) { + + /* + ** When construction is complete, then transmit this + ** to the construction yard so that it can stop its + ** construction animation. + */ + Transmit_Message(RADIO_COMPLETE); // "I'm finished." + Transmit_Message(RADIO_OVER_OUT); // "You're free." + Begin_Mode(BSTATE_IDLE); + Grand_Opening(); + Assign_Mission(MISSION_GUARD); + PrimaryFacing = Class->StartFace; + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Deconstruction -- Handles building deconstruction. * + * * + * This state machine is only used when the building is deconstructing as a result of * + * selling. When this mission is finished, the building will no longer exist. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 08/13/1995 JLB : Enable selling of units on a repair bay. * + * 08/20/1995 JLB : Scatters infantry from scattered starting points. * + *=============================================================================================*/ +int BuildingClass::Mission_Deconstruction(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Always force repair off. + */ + Repair(0); + + enum { + INITIAL, + HOLDING, + DURING + }; + switch (Status) { + case INITIAL: + + /* + ** Special check for the repair bay which has the ability to sell + ** whatever is on it. If there is something on the repair bay, then + ** it will be sold. If there is nothing on the repair bay, then + ** the repair bay itself will be sold. + */ + if (Can_Demolish_Unit() && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * tech = Contact_With_Whom(); + Transmit_Message(RADIO_OVER_OUT); + if (IsOwnedByPlayer) Speak(VOX_UNIT_SOLD); + tech->Sell_Back(1); + Assign_Mission(MISSION_GUARD); + return(1); + } + + /* + ** Selling off a shipyard or sub pen may cause attached ships + ** who are repairing themselves to discontinue repairs. + */ + if (*this == STRUCT_SHIP_YARD || *this == STRUCT_SUB_PEN) { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (obj->IsSelfRepairing) { + if (::Distance(Center_Coord(), obj->Center_Coord()) < 0x0200) { + obj->IsSelfRepairing = false; + obj->IsToSelfRepair = false; + } + } + } + } + } + + IsReadyToCommence = false; + Transmit_Message(RADIO_RUN_AWAY); + Status = HOLDING; + break; + + case HOLDING: + if (!IsTethered) { + + /* + ** The crew will evacuate from the building. The number of crew + ** members leaving is equal to the unrecovered cost of the building + ** divided by 100 (the typical cost of a minigunner infantryman). + */ + if (!Target_Legal(ArchiveTarget) || !Is_MCV_Deploy() || *this != STRUCT_CONST) { + int count = How_Many_Survivors(); + bool engine = false; + + while (count) { + + /* + ** Ensure that the player only gets ONE engineer and not from a captured + ** construction yard. + */ + InfantryType typ = Crew_Type(); + while (typ == INFANTRY_RENOVATOR && engine) { + typ = Crew_Type(); + } + if (typ == INFANTRY_RENOVATOR) engine = true; + + InfantryClass * infantry = 0; + if (typ != INFANTRY_NONE) infantry = new InfantryClass(typ, House->Class->House); + if (infantry != NULL) { + ScenarioInit++; + COORDINATE coord = Coord_Add(Center_Coord(), XYP_COORD(0, -12)); + coord = Map[coord].Closest_Free_Spot(coord, false); + + if (infantry->Unlimbo(coord, DIR_N)) { + infantry->IsZoneCheat = infantry->Can_Enter_Cell(Coord_Cell(infantry->Center_Coord())) != MOVE_OK; + if (infantry->Class->IsNominal) infantry->IsTechnician = true; + ScenarioInit--; + infantry->Scatter(0, true); + ScenarioInit++; + infantry->Assign_Mission(MISSION_GUARD_AREA); + } else { + delete infantry; + } + ScenarioInit--; + } + count--; + } + } + + if (House->IsPlayerControl) { + Sound_Effect(VOC_CASHTURN, Coord); + } + + /* + ** Destroy all attached objects. ST - 4/24/2020 9:38PM + */ + while (Attached_Object()) { + FootClass * obj = Detach_Object(); + + Detach_All(true); + delete obj; + } + + Transmit_Message(RADIO_OVER_OUT); + Status = DURING; + Begin_Mode(BSTATE_CONSTRUCTION); + IsReadyToCommence = false; + break; + } + Transmit_Message(RADIO_RUN_AWAY); + break; + + case DURING: + if (IsReadyToCommence) { + House->IsRecalcNeeded = true; + + // MBL 05.06.2020 - "Structure Sold" is being heard when selecting/moving a redepolyable Con Yard to turn back into an MCV (RA only), so moving below + #if 0 + // MBL 04.06.2020: Fix being heard by wrong player + // if (IsOwnedByPlayer) Speak(VOX_STRUCTURE_SOLD); + if (IsOwnedByPlayer) { + if ((HouseClass *)House == PlayerPtr) { + Speak(VOX_STRUCTURE_SOLD); + } + } + #endif + bool mcv_redeployed = false; + + /* + ** Construction yards that deconstruct, really just revert back + ** to an MCV. + */ + if (Target_Legal(ArchiveTarget) && *this == STRUCT_CONST && House->IsHuman && Strength > 0) { + ScenarioInit++; + UnitClass * unit = new UnitClass(UNIT_MCV, House->Class->House); + ScenarioInit--; + if (unit != NULL) { + + /* + ** Unlimbo the MCV onto the map. The MCV should start in the same + ** health condition that the construction yard was in. + */ + fixed ratio = Health_Ratio(); + int money = Refund_Amount(); + TARGET arch = ArchiveTarget; + COORDINATE place = Coord_Snap(Adjacent_Cell(Coord, DIR_SE)); + + delete this; + + if (unit->Unlimbo(place, DIR_SW)) { + unit->Strength = (int)unit->Class_Of().MaxStrength * ratio; // Cast to (int). ST - 5/8/2019 + + /* + ** Lift the move destination from the building and assign + ** it to the unit. + */ + if (Target_Legal(arch)) { + unit->Assign_Destination(arch); + unit->Assign_Mission(MISSION_MOVE); + } + + mcv_redeployed = true; + + } else { + + /* + ** If, for some strange reason, the MCV could not be placed on the + ** map, then give the player some money to compensate. + */ + House->Refund_Money(money); + } + } else { + House->Refund_Money(Refund_Amount()); + delete this; + } + + } else { + + /* + ** Selling off a gap generator will cause the cells it affects + ** to stop being jammed. + */ + if (*this == STRUCT_GAP) { + Remove_Gap_Effect(); + } + + /* + ** A sold building still counts as a kill, but it just isn't directly + ** attributed to the enemy. + */ + WhoLastHurtMe = HOUSE_NONE; + Record_The_Kill(NULL); + + /* + ** The player gets part of the money back for the sell. + */ + House->Refund_Money(Refund_Amount()); + House->Stole(-Refund_Amount()); + Limbo(); + + if (House) { + House->Check_Pertinent_Structures(); + } + + /* + ** Finally, delete the building from the game. + */ + delete this; + } + + // MBL 05.06.2020 - "Structure Sold" was being heard when selecting/moving a redepolyable Con Yard to turn back into an MCV (RA only) above, so moved here + #if 1 + if (!mcv_redeployed) + { + if (IsOwnedByPlayer) { + if ((HouseClass *)House == PlayerPtr) { + Speak(VOX_STRUCTURE_SOLD); + } + } + } + #endif + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Attack -- Handles attack mission for building. * + * * + * Buildings that can attack are processed by this attack mission state machine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 02/22/1996 JLB : SAM doesn't lower back into ground. * + *=============================================================================================*/ +int BuildingClass::Mission_Attack(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_SAM) { + switch (Status) { + + /* + ** This is the target tracking state of the launcher. It will rotate + ** to face the current TarCom of the launcher. + */ + case SAM_READY: + if ((Class->IsPowered && House->Power_Fraction() < 1) || IsJammed) { + return(1); + } + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } else { + if (!PrimaryFacing.Is_Rotating()) { + DirType facing = Direction(TarCom); + if (PrimaryFacing.Difference(facing)) { + PrimaryFacing.Set_Desired(facing); + } else { + Status = SAM_FIRING; + } + } + } + return(1); + + /* + ** The launcher is in the process of firing. + */ + case SAM_FIRING: + if (!Target_Legal(TarCom) || !Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->Height == 0) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + FireErrorType error = Can_Fire(TarCom, 0); + if (error == FIRE_ILLEGAL || error == FIRE_CANT || error == FIRE_RANGE) { + Assign_Target(TARGET_NONE); + Status = SAM_READY; + } else { + if (error == FIRE_FACING) { + Status = SAM_READY; + } else { + if (error == FIRE_OK) { + Fire_At(TarCom, 0); + Fire_At(TarCom, 1); + Status = SAM_READY; + } + } + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].AA_Delay() + Random_Pick(0, 2)); + + } + + if (!Target_Legal(TarCom)) { + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + return(1); + } + + int primary = What_Weapon_Should_I_Use(TarCom); + IsReadyToCommence = true; + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + case FIRE_CANT: + case FIRE_RANGE: + case FIRE_AMMO: + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_GUARD); + Commence(); + break; + + case FIRE_FACING: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(2); + + case FIRE_REARM: + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(Arm); + + case FIRE_BUSY: + return(1); + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + Fire_At(TarCom, primary); + return(1); + + default: + break; + } + PrimaryFacing.Set_Desired(Direction(TarCom)); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Harvest -- Handles refinery unloading harvesters. * + * * + * This state machine handles the refinery when it unloads the harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Harvest(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL, // Dock the Tiberium cannister. + WAIT_FOR_DOCK, // Waiting for docking to complete. + MIDDLE, // Offload "bails" of tiberium. + WAIT_FOR_UNDOCK // Waiting for undocking to complete. + }; + switch (Status) { + case INITIAL: + Status = WAIT_FOR_DOCK; + break; + + case WAIT_FOR_DOCK: + if (IsReadyToCommence) { + IsReadyToCommence = false; + Status = MIDDLE; + } + break; + + case MIDDLE: + if (IsReadyToCommence) { + IsReadyToCommence = false; + + /* + ** Force any bib squatters to scatter. + */ + Map[Adjacent_Cell(Coord_Cell(Center_Coord()), DIR_S)].Incoming(0, true, true); + + FootClass * techno = Attached_Object(); + if (techno) { + int bail = techno->Offload_Tiberium_Bail(); + + if (bail) { + House->Harvested(bail); + if (techno->Tiberium_Load() > 0) { + return(1); + } + } + } + Status = WAIT_FOR_UNDOCK; + } + break; + + case WAIT_FOR_UNDOCK: + if (IsReadyToCommence) { + + /* + ** Detach harvester and go back into idle state. + */ + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Repair -- Handles the repair (active) state for building. * + * * + * This state machine is used when the building is active in some sort of repair or * + * construction mode. The construction yard will animate. The repair facility will repair * + * anything that it docked on it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 06/25/1995 JLB : Handles repair facility * + * 07/29/1995 JLB : Repair rate is controlled by power rating. * + *=============================================================================================*/ +int BuildingClass::Mission_Repair(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_CONST) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + Begin_Mode(BSTATE_ACTIVE); + Status = DURING; + break; + + case DURING: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + } + break; + + default: + break; + } + return(1); + } + + if (*this == STRUCT_REPAIR) { + enum { + INITIAL, + IDLE, + DURING + }; + switch (Status) { + case INITIAL: + { + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Assign_Mission(MISSION_GUARD); + return(1); + } + IsReadyToCommence = false; + int distance = 0x10; + TechnoClass *tech = Contact_With_Whom(); + + /* + ** BG: If the unit to repair is an aircraft, and the aircraft is + ** fixed-wing, and it's landed, be much more liberal with the + ** distance check. Fixed-wing aircraft are very inaccurate with + ** their landings. + */ + if (tech->What_Am_I() == RTTI_AIRCRAFT) { + if ( ((AircraftClass *)tech)->Class->IsFixedWing && + ((AircraftClass *)tech)->In_Which_Layer() == LAYER_GROUND) { + distance = 0x80; + } + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Distance(Contact_With_Whom()) < distance) { + Status = IDLE; + return(TICKS_PER_SECOND/4); + } + break; + } + + case IDLE: + if (!In_Radio_Contact()) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + TechnoClass * radio = Contact_With_Whom(); + + if ( ((radio->Health_Ratio() < Rule.ConditionGreen) || + (radio->What_Am_I() == RTTI_UNIT && *(UnitClass *)radio == UNIT_MINELAYER)) + && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) { + + /* + ** If the object over the repair bay is marked as useless, then + ** sell it back to get some money. + */ + if (radio->IsUseless) { + if (!radio->House->IsHuman) { + radio->Sell_Back(1); + } + Status = INITIAL; + IsReadyToCommence = true; + } else { + + // + // MBL 04.27.2020: Legacy VOX_REPAIRING seems to be never called in TD, but only in RA. + // It is currently supported as a client GUI event when standard repairing begins, with "REPAIR1" on both TD and RA + // + // This repairing is in reference to the repair bay + // There is a newer bug (https://jaas.ea.com/browse/TDRA-6224) reporting that it is heard in multiplayer by + // other players, from this call, so modifiying the original call here: + // + // if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House); + + Status = DURING; + Begin_Mode(BSTATE_ACTIVE); + IsReadyToCommence = false; + } + } else { +// Transmit_Message(RADIO_RUN_AWAY); +///*BG*/ if(radio->Health_Ratio() >= Rule.ConditionGreen) { +// Transmit_Message(RADIO_RUN_AWAY); +// } + } + } + break; + + case DURING: + if (!In_Radio_Contact()) { + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + return(1); + } + + /* + ** Check to see if the repair light blink has completed and the attached + ** unit is not doing something else. If these conditions are favorable, + ** the repair can proceed another step. + */ + if (IsReadyToCommence && Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + IsReadyToCommence = false; + + /* + ** Tell the attached unit to repair one step. It will respond with how + ** it fared. + */ + switch (Transmit_Message(RADIO_REPAIR)) { + + /* + ** The repair step proceeded smoothly. Proceed normally with the + ** repair process. + */ + case RADIO_ROGER: + break; + + /* + ** The repair operation was aborted because of some reason. Presume + ** that the reason is because of low cash. + */ + case RADIO_CANT: + if (IsOwnedByPlayer) Speak(VOX_NO_CASH); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step resulted in a completely repaired unit. + */ + case RADIO_ALL_DONE: + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House); + +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + /* + ** The repair step could not be completed because this unit is already + ** at full strength. + */ + case RADIO_NEGATIVE: + default: +// Transmit_Message(RADIO_RUN_AWAY); + Begin_Mode(BSTATE_IDLE); + Status = IDLE; + break; + + } + } + return(1); + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); + } + + if (*this == STRUCT_HELIPAD || *this == STRUCT_AIRSTRIP) { + enum { + INITIAL, + DURING + }; + switch (Status) { + case INITIAL: + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER && Transmit_Message(RADIO_PREPARED) == RADIO_NEGATIVE) { + Begin_Mode(BSTATE_ACTIVE); + Contact_With_Whom()->Assign_Mission(MISSION_SLEEP); + Status = DURING; + return(1); + } + Assign_Mission(MISSION_GUARD); + break; + + case DURING: + if (IsReadyToCommence) { + if (!In_Radio_Contact() || Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_NEGATIVE) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_PREPARED) == RADIO_ROGER) { + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + Assign_Mission(MISSION_GUARD); + return(1); + } + + if (Transmit_Message(RADIO_RELOAD) != RADIO_ROGER) { + Assign_Mission(MISSION_GUARD); + Contact_With_Whom()->Assign_Mission(MISSION_GUARD); + return(1); + } else { + fixed pfrac = Saturate(House->Power_Fraction(), 1); + if (pfrac < fixed::_1_2) pfrac = fixed::_1_2; + int time = Inverse(pfrac) * Rule.ReloadRate * TICKS_PER_MINUTE; +// int time = Bound((int)(TICKS_PER_SECOND * Saturate(House->Power_Fraction(), 1)), 0, TICKS_PER_SECOND); +// time = (TICKS_PER_SECOND*3) - time; + IsReadyToCommence = false; + return(time); + } + } + break; + + default: + break; + } + return(3); + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Missile -- State machine for nuclear missile launch. * + * * + * This handles the Temple of Nod launching its nuclear missile. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int BuildingClass::Mission_Missile(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_ADVANCED_TECH) { + enum { + DOOR_OPENING, + LAUNCH_UP, + SATELLITE_DEPLOY, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building, the missile rising, and smoke broiling. + */ + case DOOR_OPENING: + { +#ifdef FIXIT_VERSION_3 + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + if (sput) { + IsReadyToCommence = false; + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + } +#else + IsReadyToCommence = false; + COORDINATE door = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + AnimClass * sput = new AnimClass(ANIM_SPUTDOOR, door); + Status = LAUNCH_UP; + AnimToTrack = sput->As_Target(); + return(1); +#endif + } + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + AnimClass * sput = As_Animation(AnimToTrack); + if (sput) { + if (sput->Fetch_Stage() >= 19) { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + + BulletClass * bullet = new BulletClass(BULLET_GPS_SATELLITE, targ, this, 200, WARHEAD_FIRE, MPH_ROCKET); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)0xC0, 0x30); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Assign_Mission(MISSION_GUARD); + } + } + } + } + return(1); + } + } + + if (*this == STRUCT_MSLO) { + enum { + INITIAL, + DOOR_OPENING, + LAUNCH_UP, + LAUNCH_DOWN, + DONE_LAUNCH + }; + + switch (Status) { + + /* + ** The initial case is responsible for starting the door + ** opening on the building. + */ + case INITIAL: + IsReadyToCommence = false; + Begin_Mode(BSTATE_ACTIVE); // open the door + Status = DOOR_OPENING; + return(1); + + /* + ** This polls for the case when the door is actually open and + ** then kicks off the missile smoke. + */ + case DOOR_OPENING: + if (IsReadyToCommence) { + Begin_Mode(BSTATE_AUX1); // hold the door open + Status = LAUNCH_UP; + return(14); + } + return(1); + + /* + ** Once the smoke has been going for a little while this + ** actually handles launching the missile into the air. + */ + case LAUNCH_UP: + { + CELL center = Coord_Cell(Center_Coord()); + CELL cell = XY_Cell( Cell_X(center), 1); + TARGET targ = ::As_Target(cell); + BulletClass * bullet = new BulletClass(BULLET_NUKE_UP, targ, this, 200, WARHEAD_HE, MPH_VERY_FAST); + if (bullet) { + COORDINATE launch = Coord_Move(Center_Coord(), (DirType)28, 0xA0); + if (!bullet->Unlimbo(launch, DIR_N)) { + delete bullet; + bullet = NULL; + } + } + + if (bullet) { + Speak(VOX_ABOMB_LAUNCH); + Status = LAUNCH_DOWN; + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + return(6); + } + bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 64; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + return(8 * TICKS_PER_SECOND); + } + } + return(1); + + /* + ** Once the missile is in the air, this handles waiting for + ** the missile to be off the screen and then launching one down + ** over the target. + */ + case LAUNCH_DOWN: + { + Begin_Mode(BSTATE_AUX2); // start the door closing + +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (House->Control.TechLevel <= 10) { + Status = DONE_LAUNCH; + return(6); + } + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(House->NukeDest), this, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + if (bullet) { +#endif + Status = DONE_LAUNCH; + return(6); + } +#ifdef OBSOLETE + } + return(1); +#endif + + /* + ** Once the missile is done launching this handles allowing + ** the building to sit there with its door closed. + */ + case DONE_LAUNCH: + Begin_Mode(BSTATE_IDLE); // keep the door closed. + Assign_Mission(MISSION_GUARD); + return(60); + } + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * BuildingClass::Revealed -- Reveals the building to the specified house. * + * * + * This routine will reveal the building to the specified house. It will handle updating * + * the sidebar for player owned buildings. A player owned building that hasn't been * + * revealed, is in a state of pseudo-limbo. It cannot be used for any of its special * + * abilities even though it exists on the map for all other purposes. * + * * + * INPUT: house -- The house that this building is being revealed to. * + * * + * OUTPUT: Was this building revealed by this procedure? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Revealed(HouseClass * house) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (TechnoClass::Revealed(house)) { + + if (!ScenarioInit) { + House->JustBuiltStructure = Class->Type; + House->IsBuiltSomething = true; + } + House->IsRecalcNeeded = true; + + /* + ** Perform any grand opening here so that in the scenarios where a player + ** owned house is not yet revealed, it won't be reflected in the sidebar + ** selection icons. + */ + if (!In_Radio_Contact() && House->IsHuman && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } else { + if (!In_Radio_Contact() && !House->IsHuman && house == House && Mission != MISSION_CONSTRUCTION) { + Grand_Opening(); + } + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Enter_Idle_Mode -- The building will enter its idle mode. * + * * + * This routine is called when the exact mode of the building isn't known. By examining * + * the building's condition, this routine will assign an appropriate mission. * + * * + * INPUT: initial -- This this being called during scenario init? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Enter_Idle_Mode(bool initial) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** Assign an appropriate mission for the building. If the ScenarioInit flag is true, then + ** this must be an initial building. Start such buildings in idle state. For other buildings + ** it indicates that it is being placed during game play and thus it must start in + ** the "construction" mission. + */ + MissionType mission = MISSION_GUARD; + + + if (!initial || ScenarioInit || Debug_Map) { + Begin_Mode(BSTATE_IDLE); + mission = MISSION_GUARD; + } else { + Begin_Mode(BSTATE_CONSTRUCTION); + mission = MISSION_CONSTRUCTION; + } + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * BuildingClass::Pip_Count -- Determines "full" pips to display for building. * + * * + * This routine will determine the number of pips that should be filled in when rendering * + * the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of pips to display as filled in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Pip_Count(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(Class->Max_Pips() * House->Tiberium_Fraction()); +} + + +/*********************************************************************************************** + * BuildingClass::Death_Announcement -- Announce the death of this building. * + * * + * This routine is called when the building is destroyed by "unnatural" means. Typically * + * as a result of combat. If the building is known to the player, then it should be * + * announced. * + * * + * INPUT: source -- The object most directly responsible for the building's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Death_Announcement(TechnoClass const * source) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (source != NULL && House->IsPlayerControl) { + Speak(VOX_STRUCTURE_DESTROYED); + } +} + + +/*********************************************************************************************** + * BuildingClass::Fire_Direction -- Fetches the direction of firing. * + * * + * This routine will return with the default direction to use when firing from this * + * building. This is the facing of the turret except for the case of non-turret equipped * + * buildings that have a weapon (e.g., guard tower). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +DirType BuildingClass::Fire_Direction(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + return(PrimaryFacing.Current()); + } + return(Direction(TarCom)); +} + + +/*********************************************************************************************** + * BuildingClass::Remap_Table -- Fetches the remap table to use for this building. * + * * + * Use this routine to fetch the remap table to use. This override function is needed * + * because the default remap table for techno objects presumes the object is a unit. * + * Buildings aren't units. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the proper remap table to use for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Remap_Table(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(House->Remap_Table(IsBlushing, Class->Remap)); +} + + +/*********************************************************************************************** + * BuildingClass::Mission_Unload -- Handles the unload mission for a building. * + * * + * This is the unload mission for a building. This really only applies to the weapon's * + * factory, since it needs the sophistication of an unload mission due to the door * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Mission_Unload(void) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (*this == STRUCT_WEAP) { + CELL cell = Coord_Cell(Coord) + Class->ExitList[0]; + COORDINATE coord = Cell_Coord(cell); + CellClass * cellptr = &Map[cell]; + enum { + INITIAL, + CLEAR_BIB, + OPEN, + LEAVE, + CLOSE + }; + enum { + DOOR_STAGES = 5, + DOOR_RATE = 8 + }; + UnitClass * unit; + switch (Status) { + /* + ** Start the door opening. + */ + case INITIAL: +// if (cellptr->Cell_Techno()) { +// cellptr->Incoming(0, true); +// } + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_GUARD); + unit->Commence(); + } + Open_Door(DOOR_RATE, DOOR_STAGES); + Status = CLEAR_BIB; + break; + + /* + ** Now that the occupants can peek out the door, they will tell + ** everyone that could be blocking the way, that they should + ** scatter away. + */ + case CLEAR_BIB: + if (cellptr->Cell_Techno()) { + cellptr->Incoming(0, true, true); + + /* + ** Scatter everything around the weapon's factory door. + */ + for (FacingType f = FACING_FIRST; f < FACING_COUNT; f++) { + CellClass * cptr = &cellptr->Adjacent_Cell(f); + if (cptr->Cell_Building() == NULL) { + cptr->Incoming(coord, true, true); + } + } + } else { + Status = OPEN; + } + break; + + /* + ** When the door is finally open and the way is clear, tell the + ** unit to drive out. + */ + case OPEN: + if (Is_Door_Open()) { + unit = (UnitClass *)Contact_With_Whom(); + if (unit) { + unit->Assign_Mission(MISSION_MOVE); + + if (House->IQ >= Rule.IQGuardArea) { + unit->Assign_Mission(MISSION_GUARD_AREA); + unit->ArchiveTarget = ::As_Target(House->Where_To_Go(unit)); + } + unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, coord); +// unit->Force_Track(DriveClass::OUT_OF_WEAPON_FACTORY, Adjacent_Cell(Adjacent_Cell(Center_Coord(), FACING_S), FACING_S)); + unit->Set_Speed(128); + Status = LEAVE; + } else { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } + } + break; + + /* + ** Wait until the unit has completely left the building. + */ + case LEAVE: + if (!IsTethered) { + Close_Door(DOOR_RATE, DOOR_STAGES); + Status = CLOSE; + } else { + +// if (In_Radio_Contact() && !((FootClass *)Contact_With_Whom())->IsDriving) { +// Transmit_Message(RADIO_OVER_OUT); +// } + + } + break; + + /* + ** Wait while the door closes. + */ + case CLOSE: + if (Is_Door_Closed()) { + Enter_Idle_Mode(); + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + Assign_Mission(MISSION_GUARD); + return(1); +} + + +/*********************************************************************************************** + * BuildingClass::Power_Output -- Fetches the current power output from this building. * + * * + * This routine will return the current power output for this building. The power output * + * is adjusted according to the damage level of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current power output for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Power_Output(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->Power) { + return(Class->Power * fixed(LastStrength, Class->MaxStrength)); + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Detach -- Handles target removal from the game system. * + * * + * This routine is called when the specified target is about to be removed from the game * + * system. * + * * + * INPUT: target -- The target to be removed from this building's targeting computer. * + * * + * all -- Is the target about to be completely eliminated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach(TARGET target, bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + TechnoClass::Detach(target, all); + if (target == WhomToRepay) { + WhomToRepay = TARGET_NONE; + } + if (target == AnimToTrack) { + AnimToTrack = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BuildingClass::Crew_Type -- This determines the crew that this object generates. * + * * + * When selling very cheap buildings (such as the silo), a technician will pop out since * + * generating minigunners would be overkill -- the player could use this loophole to * + * gain an advantage. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type that this building will generate as a survivor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType BuildingClass::Crew_Type(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + switch (Class->Type) { + case STRUCT_STORAGE: + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + + case STRUCT_CONST: + if (!IsCaptured && House->IsHuman && Percent_Chance(25)) { + return(INFANTRY_RENOVATOR); + } + break; + + case STRUCT_KENNEL: + if (Percent_Chance(50)) { + return(INFANTRY_DOG); + } else { + return(INFANTRY_NONE); + } + + case STRUCT_TENT: + case STRUCT_BARRACKS: + return(INFANTRY_E1); + + default: + break; + } + return(TechnoClass::Crew_Type()); +} + + +/*********************************************************************************************** + * BuildingClass::Detach_All -- Possibly abandons production according to factory type. * + * * + * When this routine is called, it indicates that the building is about to be destroyed * + * or captured. In such a case any production it may be doing, must be abandoned. * + * * + * INPUT: all -- Is the object about the be completely destroyed? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Detach_All(bool all) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + /* + ** If it is producing something, then it must be abandoned. + */ + if (Factory) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + /* + ** If the owner HouseClass is building something, and this building can + ** build that thing, we may be the last building for that house that can + ** build that thing; if so, abandon production of it. + */ + if (House) { + FactoryClass * factory = House->Fetch_Factory(Class->ToBuild); + + /* + ** If a factory was found, then temporarily disable this building and then + ** determine if any object that is being produced can still be produced. If + ** not, then the object being produced must be abandoned. + */ + if (factory) { + TechnoClass * object = factory->Get_Object(); + IsInLimbo = true; + if (object && !object->Techno_Type_Class()->Who_Can_Build_Me(true, false, House->Class->House)) { + House->Abandon_Production(Class->ToBuild); + } + IsInLimbo = false; + } + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * BuildingClass::Flush_For_Placement -- Handles clearing a zone for object placement. * + * * + * This routine is used to clear the way for placement of the specified object (usually * + * a building). If there are friendly units blocking the placement area, they are told * + * to scatter. Enemy blocking units are attacked. * + * * + * INPUT: techno -- Pointer to the object that is desired to be placed. * + * * + * cell -- The cell that placement wants to occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1995 JLB : Created. * + * 09/27/1995 JLB : Revised to use type class function. * + *=============================================================================================*/ +bool BuildingClass::Flush_For_Placement(TechnoClass * techno, CELL cell) +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (techno) { + return (((BuildingTypeClass const &)techno->Class_Of()).Flush_For_Placement(cell, House)); + } + return(false); +} + + +/*********************************************************************************************** + * BuildingClass::Find_Exit_Cell -- Find a clear location to exit an object from this building * + * * + * This routine is called when the building needs to discharge a unit. It will find a * + * nearby (adjacent) cell that is clear enough for the specified object to enter. Typical * + * use of this routine is when the airfield disgorges its cargo. * + * * + * INPUT: techno -- Pointer to the object that wishes to exit this building. * + * * + * OUTPUT: Returns with the cell number to use for object placement. If no free location * + * could be found, then zero (0) is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 02/20/1996 JLB : Added default case for exit cell calculation. * + *=============================================================================================*/ +CELL BuildingClass::Find_Exit_Cell(TechnoClass const * techno) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + CELL const * ptr; + CELL origin = Coord_Cell(Coord); + + ptr = Class->ExitList; + if (ptr != NULL) { + while (*ptr != REFRESH_EOL) { + CELL cell = origin + *ptr++; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } else { + int x1, x2; + int y1, y2; + CELL cell; + + y1 = -1; + y2 = Class->Height(); + for (x1 = -1; x1 <= Class->Width(); x1++) { + cell = origin + x1 + (y1 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + x1 + (y2 * MAP_CELL_W); + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + + x1 = -1; + x2 = Class->Width(); + for (y1 = -1; y1 <= Class->Height(); y1++) { + cell = origin + (y1 * MAP_CELL_W) + x1; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + cell = origin + (y1 * MAP_CELL_W) + x2; + if (Map.In_Radar(cell) && techno->Can_Enter_Cell(cell) == MOVE_OK) { + return(cell); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * BuildingClass::Can_Player_Move -- Can this building be moved? * + * * + * This routine answers the question 'can this building be moved?' Typically, only the * + * construction yard can be moved and it does this by undeploying back into a MCV. * + * * + * INPUT: none * + * * + * OUTPUT: Can the building move to a new location under player control? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/04/1995 JLB : Created. * + *=============================================================================================*/ +bool BuildingClass::Can_Player_Move(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + return(*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); +} + + +/*********************************************************************************************** + * BuildingClass::Exit_Coord -- Determines location where object will leave it. * + * * + * This routine will return the coordinate where an object that wishes to leave the * + * building will exit at. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that the object should be created at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Exit_Coord(void) const +{ + assert(Buildings.ID(this) == ID); + assert(IsActive); + + if (Class->ExitCoordinate) { + return(Coord_Add(Coord, Class->ExitCoordinate)); + } + return(TechnoClass::Exit_Coord()); +} + + +/*********************************************************************************************** + * BuildingClass::Check_Point -- Fetches the landing checkpoint for the given flight pattern. * + * * + * Use this routine to coordinate a landing operation. The specified checkpoint is * + * converted into a cell number. The landing aircraft should fly over that cell and then * + * request the next check point. * + * * + * INPUT: cp -- The check point to convert to a cell number. * + * * + * OUTPUT: Returns with the cell that the aircraft should fly over in order to complete * + * that portion of the landing pattern. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CELL BuildingClass::Check_Point(CheckPointType cp) const +{ + CELL xoffset = 6; // Downwind offset. + CELL yoffset = 5; // Crosswind offset. + CELL cell = Coord_Cell(Center_Coord()); + + switch (cp) { + case CHECK_STACK: + xoffset = 0; + break; + + case CHECK_CROSSWIND: + yoffset = 0; + break; + + case CHECK_DOWNWIND: + default: + break; + } + + if ((Cell_X(cell) - Map.MapCellX) > Map.MapCellWidth/2) { + xoffset = -xoffset; + } + + if ((Cell_Y(cell) - Map.MapCellY) > Map.MapCellHeight/2) { + yoffset = -yoffset; + } + + return(XY_Cell(Cell_X(cell)+xoffset, Cell_Y(cell)+yoffset)); +} + + +/*********************************************************************************************** + * BuildingClass::Update_Radar_Spied - set house's RadarSpied field appropriately. * + * * + * This routine is called when a radar facility is captured or destroyed. It fills in the * + * RadarSpied field of the house based on whether there's a spied-upon radar facility or not* + * * + * INPUT: none * + * * + * OUTPUT: House->RadarSpied field gets set appropriately. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/22/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Update_Radar_Spied(void) +{ + House->RadarSpied = 0; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House) { + if (*obj == STRUCT_RADAR /* || *obj == STRUCT_EYE */) { + House->RadarSpied |= obj->Spied_By(); + } + } + } + Map.RadarClass::Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * BuildingClass::Read_INI -- Reads buildings from INI file. * + * * + * This is the basic scenario initialization of building function. It * + * is called when reading the scenario startup INI file and it handles * + * creation of all specified buildings. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cell, Facing, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Read_INI(CCINIClass & ini) +{ + BuildingClass * b; // Working unit pointer. + HousesType bhouse; // Building house. + StructType classid; // Building type. + CELL cell; // Cell of building. + char buf[128]; + char * trigname; // building's trigger's name + + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get a building entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + bhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + + /* + ** 2nd token: building name. + */ + classid = BuildingTypeClass::From_Name(strtok(NULL, ",")); + + if (bhouse != HOUSE_NONE && classid != STRUCT_NONE) { + int strength; + DirType facing; + + /* + ** 3rd token: strength. + */ + strength = atoi(strtok(NULL, ",")); + + /* + ** 4th token: cell #. + */ + cell = atoi(strtok(NULL, ",")); + + /* + ** 5th token: facing. + */ + facing = (DirType)atoi(strtok(NULL, ",")); + + /* + ** 6th token: triggername (can be NULL). + */ + trigname = strtok(NULL, ","); + + bool sellable = false; + char * token_pointer = strtok(NULL, ","); + if (token_pointer) { + sellable = atoi(token_pointer); + } + + bool rebuild = false; + token_pointer = strtok(NULL, ","); + if (token_pointer) { + rebuild = atoi(token_pointer); + } + + if (HouseClass::As_Pointer(bhouse) != NULL) { + b = new BuildingClass(classid, bhouse); + if (b) { + + TriggerTypeClass * tp = TriggerTypeClass::From_Name(trigname); + if (tp) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + b->Trigger = tt; + } + } + b->IsAllowedToSell = sellable; + b->IsToRebuild = rebuild; + b->IsToRepair = rebuild || *b == STRUCT_CONST; + + if (b->Unlimbo(Cell_Coord(cell), facing)) { + strength = min(strength, 0x100); + strength = (int)b->Class->MaxStrength * fixed(strength, 256); // Cast to (int). ST - 5/8/2019 + b->Strength = strength; + if (b->Strength > b->Class->MaxStrength-3) b->Strength = b->Class->MaxStrength; + b->IsALemon = false; + } else { + + /* + ** If the building could not be unlimboed on the map, then this indicates + ** a serious error. Delete the building. + */ + delete b; + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Write_INI -- Write out the building data to the INI file specified. * + * * + * This will store the building data (as it relates to scenario initialization) to the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that the building data will be stored to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing building data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the data out. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (!building->IsInLimbo) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%d", + building->House->Class->IniName, + building->Class->IniName, + building->Health_Ratio()*256, + Coord_Cell(building->Coord), + building->PrimaryFacing.Current(), + building->Trigger.Is_Valid() ? building->Trigger->Class->IniName : "None", + building->IsAllowedToSell, + building->IsToRebuild + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Target_Coord -- Return the coordinate to use when firing on this building. * + * * + * This routine will determine the "center" location of this building for purposes of * + * targeting. Usually, this location is somewhere near the foundation of the building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing upon this building (or trying to * + * walk onto it). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BuildingClass::Target_Coord(void) const +{ + COORDINATE coord = Center_Coord(); + + if (Class->FoundationFace != FACING_NONE) { + return(Adjacent_Cell(coord, Class->FoundationFace)); + } + return(coord); +} + + +/*********************************************************************************************** + * BuildingClass::Factory_AI -- Handle factory production and initiation. * + * * + * Some building (notably the computer controlled ones) can have a factory object attached. * + * This routine handles processing of that factory and also detecting when production * + * should begin in order to initiate production. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Factory_AI(void) +{ + /* + ** Handle any production tied to this building. Only computer controlled buildings have + ** production attached to the building itself. The player uses the sidebar interface for + ** all production control. + */ + if (Factory.Is_Valid() && Factory->Has_Completed() && PlacementDelay == 0) { + TechnoClass * product = Factory->Get_Object(); +// FactoryClass * fact = Factory; + + switch (Exit_Object(product)) { + + /* + ** If the object could not leave the factory, then either request + ** a transport, place the (what must be a) building using another method, or + ** abort the production and refund money. + */ + case 0: + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + break; + + /* + ** Exiting this building is prevented by some temporary blockage. Wait + ** a bit before trying again. + */ + case 1: + PlacementDelay = TICKS_PER_SECOND*3; + break; + + /* + ** The object was successfully sent from this factory. Inform the house + ** tracking logic that the requested object has been produced. + */ + case 2: + switch (product->What_Am_I()) { + case RTTI_VESSEL: + House->JustBuiltVessel = ((VesselClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_UNIT: + House->JustBuiltUnit = ((UnitClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + House->JustBuiltInfantry = ((InfantryClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + House->JustBuiltStructure = ((BuildingClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + House->JustBuiltAircraft = ((AircraftClass*)product)->Class->Type; + House->IsBuiltSomething = true; + break; + + default: + break; + } +// fact->Completed(); + Factory->Completed(); +// delete fact; + delete (FactoryClass *)Factory; + Factory = 0; + break; + + default: + break; + } + } + + /* + ** Pick something to create for this factory. + */ + if (House->IsStarted && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + + /* + ** Buildings that produce other objects have special factory logic handled here. + */ + if (Class->ToBuild != RTTI_NONE) { + if (Factory.Is_Valid()) { + + /* + ** If production has halted, then just abort production and make the + ** funds available for something else. + */ + if (PlacementDelay == 0 && !Factory->Is_Building()) { + Factory->Abandon(); + delete (FactoryClass *)Factory; + Factory = 0; + } + + } else { + + /* + ** Only look to start production if there is at least a small amount of + ** money available. In cases where there is no practical money left, then + ** production can never complete -- don't bother starting it. + */ + if (House->IsStarted && House->Available_Money() > 10) { + TechnoTypeClass const * techno = House->Suggest_New_Object(Class->ToBuild, *this == STRUCT_KENNEL); + + /* + ** If a suitable object type was selected for production, then start + ** producing it now. + */ + if (techno != NULL) { + Factory = new FactoryClass; + if (Factory.Is_Valid()) { + if (!Factory->Set(*techno, *House)) { + delete (FactoryClass *)Factory; + Factory = 0; + } else { + House->Production_Begun(Factory->Get_Object()); + Factory->Start(); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Rotation_AI -- Process any turret rotation required of this building. * + * * + * Some buildings have a turret and this routine handles processing the turret rotation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 10/27/1996 JLB : Rotation does not occur if power and no power avail. * + *=============================================================================================*/ +void BuildingClass::Rotation_AI(void) +{ + if (Class->IsTurretEquipped && + Mission != MISSION_CONSTRUCTION && + Mission != MISSION_DECONSTRUCTION && + (!Class->IsPowered || House->Power_Fraction() >= 1)) { + + /* + ** Rotate turret to match desired facing. + */ + if (PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Charging_AI -- Handles the special charging logic for Tesla coils. * + * * + * This handles the special logic required of the charging tesla coil. It requires special * + * processing since its charge up is dependant upon the target and power surplus of the * + * owning house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Charging_AI(void) +{ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->IsElectric && BState != BSTATE_CONSTRUCTION) { + if (Target_Legal(TarCom) && House->Power_Fraction() >= 1) { + if (!IsCharged) { + if (IsCharging) { +// if (stagechange) { + Mark(MARK_CHANGE); + if (Fetch_Stage() >= 9) { + IsCharged = true; + IsCharging = false; + Set_Rate(0); + } +// } + } else if (!Arm) { + IsCharged = false; + IsCharging = true; + Set_Stage(0); + Set_Rate(3); + Sound_Effect(VOC_TESLA_POWER_UP, Coord); + } + } + } else { + if (IsCharging || IsCharged) { + Mark(MARK_CHANGE); + IsCharging = false; + IsCharged = false; + Set_Stage(0); + Set_Rate(0); + } + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Repair_AI -- Handle the repair (and sell) logic for the building. * + * * + * This routine handle the repair animation and healing logic. It also detects when the * + * (computer controlled) building should begin repair or sell itself. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Repair_AI(void) +{ + if (House->IQ >= Rule.IQRepairSell && Mission != MISSION_CONSTRUCTION && Mission != MISSION_DECONSTRUCTION) { + /* + ** Possibly start repair process if the building is below half strength. + */ +// unsigned ratio = MIN(House->Smartness, 0x00F0); + if (Can_Repair()) { + if (House->Available_Money() >= Rule.RepairThreshhold) { + if (!House->DidRepair) { + if (!IsRepairing && (IsCaptured || IsToRepair || House->IsHuman || Session.Type != GAME_NORMAL)) { + House->DidRepair = true; // flag that this house did its repair allocation for this frame + Repair(1); + + if (!House->IsHuman) { + House->RepairTimer = Random_Pick((int)(House->RepairDelay * (TICKS_PER_MINUTE/4)), (int)(House->RepairDelay * TICKS_PER_MINUTE * 2)); + } + } + } + } else { + if ((Session.Type != GAME_NORMAL || IsAllowedToSell) && IsTickedOff && House->Control.TechLevel >= Rule.IQSellBack && Random_Pick(0, 50) < House->Control.TechLevel && !Trigger.Is_Valid() && *this != STRUCT_CONST && Health_Ratio() < Rule.ConditionRed) { + Sell_Back(1); + } + } + } + } + + /* + ** If it is repairing, then apply any repair effects as necessary. + */ + if (IsRepairing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0) { + IsWrenchVisible = (IsWrenchVisible == false); + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + /* + ** Check for and expend any necessary monies to continue the repair. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsRepairing = false; + } + } else { + IsRepairing = false; + } + } +} + + +/*********************************************************************************************** + * BuildingClass::Animation_AI -- Handles normal building animation processing. * + * * + * This will process the general building animation mechanism. It detects when the * + * building animation sequence has completed and flags the building to perform mission * + * changes as a result. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per building per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void BuildingClass::Animation_AI(void) +{ + bool stagechange = Graphic_Logic(); + bool toloop = false; + + /* + ** Always refresh the SAM site if it has an animation change. + */ + if (*this == STRUCT_SAM && stagechange) Mark(MARK_CHANGE); + + if ((!Class->IsTurretEquipped && *this != STRUCT_TESLA) || Mission == MISSION_CONSTRUCTION || Mission == MISSION_DECONSTRUCTION) { + if (stagechange) { + + /* + ** Check for animation end or if special case of MCV deconstructing when it is allowed + ** to convert back into an MCV. + */ + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + + /* + ** When the last frame of the current animation sequence is reached, flag that + ** a new mission may be started. This must occur before the animation actually + ** loops so that if a mission change does occur, it will have a chance to change + ** the building graphic before the last frame is replaced by the first frame of + ** the loop. + */ + if (Fetch_Stage() == ctrl->Start+ctrl->Count-1 || (!Target_Legal(ArchiveTarget) /*Is_MCV_Deploy()*/ && *this == STRUCT_CONST && Mission == MISSION_DECONSTRUCTION && Fetch_Stage() == (42-19))) { + IsReadyToCommence = true; + } + + /* + ** If the animation advances beyond the last frame, then start the animation + ** sequence over from the beginning. + */ + if (Fetch_Stage() >= ctrl->Start+ctrl->Count) { + toloop = true; + } + Mark(MARK_CHANGE); + } else { + if (BState == BSTATE_NONE || Fetch_Rate() == 0) { + IsReadyToCommence = true; + } + } + } + + /* + ** If there is a door that is animating, then it might cause this building + ** to be redrawn. Check for and flag to redraw as necessary. + */ + if (Time_To_Redraw()) { + Clear_Redraw_Flag(); + Mark(MARK_CHANGE); + } + + /* + ** The animation sequence has looped. Restart it and flag this loop condition. + ** This is used to tell the mission system that the animation has completed. It + ** also signals that now is a good time to act on any pending mission. + */ + if (toloop) { + BuildingTypeClass::AnimControlType const * ctrl = Fetch_Anim_Control(); + if (BState == BSTATE_CONSTRUCTION || BState == BSTATE_IDLE) { + Set_Rate(Options.Normalize_Delay(ctrl->Rate)); + } else { + Set_Rate(ctrl->Rate); + } + Set_Stage(ctrl->Start); + Mark(MARK_CHANGE); + } +} + + +/*********************************************************************************************** + * BuildingClass::How_Many_Survivors -- This determine the maximum number of survivors. * + * * + * This routine is called to determine how many survivors should run from this building * + * when it is either sold or destroyed. Buildings that are captured have fewer survivors. * + * The number of survivors is a portion of the cost of the building divided by the cost * + * of a minigunner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of soldiers that should run from this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::How_Many_Survivors(void) const +{ + if (IsSurvivorless || !Class->IsCrew) return(0); + + int divisor = InfantryTypeClass::As_Reference(INFANTRY_E1).Raw_Cost(); + if (divisor == 0) return(0); + if (IsCaptured) divisor *= 2; + int count = (Class->Raw_Cost() * Rule.SurvivorFraction) / divisor; + return(Bound(count, 1, 5)); +} + + +/*********************************************************************************************** + * BuildingClass::Get_Image_Data -- Fetch the image pointer for the building. * + * * + * This routine will return with a pointer to the shape data for the building. The shape * + * data is different than normal when the building is undergoing construction and * + * disassembly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this building. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * BuildingClass::Get_Image_Data(void) const +{ + if (BState == BSTATE_CONSTRUCTION) { + return(Class->Get_Buildup_Data()); + } + return(TechnoClass::Get_Image_Data()); +} + +struct Image_t *BuildingClass::Get_HDImage_Data(void) const +{ + if (BState == BSTATE_CONSTRUCTION) { + return NULL; + } + return(TechnoClass::Get_HDImage_Data()); +} + +/*********************************************************************************************** + * BuildingClass::Value -- Determine the value of this building. * + * * + * The value of the building is normally just its ordinary assigned value. However, in the * + * case of fakes, the value is artificially enhanced to match the structure that is * + * being faked. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the point value of the building type. * + * * + * WARNINGS: The point value returned should not be used for scoring, only for target * + * scanning. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int BuildingClass::Value(void) const +{ + if (Class->IsFake) { + switch (Class->Type) { + case STRUCT_FAKEWEAP: + return(BuildingTypeClass::As_Reference(STRUCT_WEAP).Reward + BuildingTypeClass::As_Reference(STRUCT_WEAP).Risk); + + case STRUCT_FAKECONST: + return(BuildingTypeClass::As_Reference(STRUCT_CONST).Reward + BuildingTypeClass::As_Reference(STRUCT_CONST).Risk); + + case STRUCT_FAKE_YARD: + return(BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Reward + BuildingTypeClass::As_Reference(STRUCT_SHIP_YARD).Risk); + + case STRUCT_FAKE_PEN: + return(BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Reward + BuildingTypeClass::As_Reference(STRUCT_SUB_PEN).Risk); + + case STRUCT_FAKE_RADAR: + return(BuildingTypeClass::As_Reference(STRUCT_RADAR).Reward + BuildingTypeClass::As_Reference(STRUCT_RADAR).Risk); + + default: + break; + } + } + return(TechnoClass::Value()); +} + + +/*********************************************************************************************** + * BuildingClass::Remove_Gap_Effect -- Stop a gap generator from jamming cells. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 09/20/1996 BWG : Created. * + *=============================================================================================*/ +void BuildingClass::Remove_Gap_Effect(void) +{ + // unjam this one's field... + Map.UnJam_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, House); + + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:14AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (!House->IsPlayerControl && PlayerPtr->IsGPSActive) { + Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsGPSActive && player_house != House) { + Map.Sight_From(Coord_Cell(Center_Coord()), Rule.GapShroudRadius, player_house); + } + } + } + + + // and rejam any overlapping buildings' fields + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *obj = Buildings.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == House && *obj == STRUCT_GAP && obj!=this) { + obj->IsJamming = false; + obj->Arm = 0; +// Map.Jam_From(Coord_Cell(obj->Center_Coord()), Rule.GapShroudRadius, PlayerPtr); + } + } +} + + +short const * BuildingClass::Overlap_List(bool redraw) const +{ + if ((Spied_By() & (1 << PlayerPtr->Class->House)) != 0 && Is_Selected_By_Player()) { + if (*this == STRUCT_BARRACKS || *this == STRUCT_TENT) { + static short const _list[] = { + -1, 2, (MAP_CELL_W*1)-1, (MAP_CELL_W*1)+2, REFRESH_EOL + }; + return(_list); + } else if (*this == STRUCT_REFINERY) { + static short const _list[] = { + 0, 2, (MAP_CELL_W*2)+0, (MAP_CELL_W*2)+1, (MAP_CELL_W*2)+2, REFRESH_EOL + }; + return(_list); + } + } + return(TechnoClass::Overlap_List(redraw)); +} + + +unsigned BuildingClass::Spied_By() const +{ + unsigned spiedby = TechnoClass::Spied_By(); + + /* + ** If it's an ore refinery or other such storage-capable building, + ** loop thru all of their buildings to see if ANY of them are spied + ** upon, 'cause once you spy any money, you've spied all of it. + */ + if (Class->Capacity) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building->House == House && building->Class->Capacity) { + spiedby |= building->SpiedBy; + } + } + } + + return spiedby; +} \ No newline at end of file diff --git a/code/redalert/building.h b/code/redalert/building.h new file mode 100644 index 0000000..2da8a8e --- /dev/null +++ b/code/redalert/building.h @@ -0,0 +1,364 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BUILDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BUILDING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUILDING_H +#define BUILDING_H + +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "bullet.h" +#include "target.h" +#include "factory.h" +#include "techno.h" + +#define MAX_DOOR_STAGE 18 // # of frames of door opening on weapons factory +#define DOOR_OPEN_STAGE 9 // frame on which the door is entirely open +#define MAX_REPAIR_ANIM_STAGE 5 // # of stages of anim for repair center cycling + +/**************************************************************************** +** For each instance of a building in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular building. +*/ +class BuildingClass : public TechnoClass +{ + public: + + /* + ** This points to the control data that gives this building its characteristics. + */ + CCPtr Class; + + /* + ** If this building is in the process of producing something, then this + ** will point to the factory manager. + */ + CCPtr Factory; + + /* + ** This is the house that originally owned this factory. Objects buildable + ** by this house type will be produced from this factory regardless of who + ** the current owner is. + */ + HousesType ActLike; + + /* + ** This building should be rebuilt if it is destroyed. This is in spite + ** of the condition of the prebuilt base list. + */ + unsigned IsToRebuild:1; + + /* + ** Is the building allowed to repair itself? + */ + unsigned IsToRepair:1; + + /* + ** If the computer owns this building, then it is allowed to sell it if + ** the situation warrants it. In the other case, it cannot sell the + ** building regardless of conditions. + */ + unsigned IsAllowedToSell:1; + + /* + ** If the building is at a good point to change orders, then this + ** flag will be set to true. + */ + unsigned IsReadyToCommence:1; + + /* + ** If this building is currently spending money to repair itself, then + ** this flag is true. It will automatically be set to false when the building + ** has reached full strength, when money is exhausted, or if the player + ** specifically stops the repair process. + */ + unsigned IsRepairing:1; + + /* + ** If repair is currently in progress and this flag is true, then a wrench graphic + ** will be overlaid on the building to give visual feedback for the repair process. + */ + unsigned IsWrenchVisible:1; + + /* + ** This flag is set when a commando has raided the building and planted + ** plastic explosives. When the CommandoCountDown timer expires, the + ** building takes massive damage. + */ + unsigned IsGoingToBlow:1; + + /* + ** If this building was destroyed by some method that would prevent + ** survivors, then this flag will be true. + */ + unsigned IsSurvivorless:1; + + /* + ** These state control variables are used by the obelisk for the charging + ** animation. + */ + unsigned IsCharging:1; + unsigned IsCharged:1; + + /* + ** A building that has been captured will not contain the full compliment + ** of crew. This is true even if it subsequently gets captured back. + */ + unsigned IsCaptured:1; + + /* + ** Used by the gap generator to decide if it should jam or unjam + */ + unsigned IsJamming:1; + + /* + ** Used by radar facilities to know if they're being jammed by a mobile + ** radar jammer + */ + unsigned IsJammed:1; + + /* + ** Used only by advanced tech center, this keeps track of whether the + ** GPS satellite has been fired or not. + */ + unsigned HasFired:1; + + /* + ** If Grand_Opening was already called for this building, then this + ** flag will be true. By utilizing this flag, multiple inadvertant + ** calls to Grand_Opening won't cause problems. + */ + unsigned HasOpened:1; + + /* + ** Special countdown to destruction value. If the building is destroyed, + ** it won't actually be removed from the map until this value reaches + ** zero. This delay is for cosmetic reasons. + */ + CDTimerClass CountDown; + + /* + ** This is the current animation processing state that the building is + ** in. + */ + BStateType BState; + BStateType QueueBState; + + /* + ** For multiplayer games, this keeps track of the last house to damage + ** this building, so if it burns to death or otherwise gradually dies, + ** proper credit can be given for the kill. + */ + HousesType WhoLastHurtMe; + + /* + ** This is the saboteur responsible for this building's destruction. + */ + TARGET WhomToRepay; + + /* + ** This is a record of the last strength of the building. Every so often, + ** it will compare this strength to the current strength. If there is a + ** discrepancy, then the owner power is adjusted accordingly. + */ + int LastStrength; + + /* + ** This is a target id of an animation we're keeping track of. Examples + ** of this usage are the advanced tech center, which needs to know + ** when the sputdoor animation has reached a certain stage. + */ + TARGET AnimToTrack; + + /* + ** This is the countdown timer that regulates placement retry logic + ** for factory type buildings. + */ + CDTimerClass PlacementDelay; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BuildingClass(StructType type, HousesType house); +#ifdef FIXIT_MULTI_SAVE + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), Factory(x), CountDown(x), PlacementDelay(x) {}; +#else + BuildingClass(NoInitClass const & x) : TechnoClass(x), Class(x), CountDown(x), PlacementDelay(x) {}; +#endif + virtual ~BuildingClass(void); + operator StructType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + TARGET Target_Scan(void); + BuildingTypeClass::AnimControlType const * Fetch_Anim_Control(void) {return (&Class->Anims[BState]);}; + + /* + ** Query functions. + */ + virtual int Value(void) const; + virtual void const * Get_Image_Data(void) const; +// jmarshall + struct Image_t* BuildingClass::Get_HDImage_Data(void) const; +// jmarshall end + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual InfantryType Crew_Type(void) const; + virtual int Pip_Count(void) const; + virtual bool Can_Player_Move(void) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual ActionType What_Action(CELL cell) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual DirType Fire_Direction(void) const; + virtual short const * Overlap_List(bool redraw=false) const; + int Shape_Number(void) const; + int Power_Output(void) const; + CELL Check_Point(CheckPointType cp) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual void Grand_Opening(bool captured = false); + virtual void Update_Buildables(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType = FACING_NONE) const; + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Limbo(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void); + virtual int Exit_Object(TechnoClass * base); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void Fire_Out(void); + void Begin_Mode(BStateType bstate); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Death_Announcement(TechnoClass const * source=0) const; + virtual FireErrorType Can_Fire(TARGET, int which) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual bool Captured(HouseClass * newowner); + void Update_Radar_Spied(void); + + /* + ** AI. + */ + void Charging_AI(void); + void Rotation_AI(void); + void Factory_AI(void); + void Repair_AI(void); + void Animation_AI(void); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int control); + virtual void Sell_Back(int control); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Assign_Target(TARGET target); + virtual bool Toggle_Primary(void); + bool Flush_For_Placement(TechnoClass * techno, CELL cell); + + virtual int Mission_Unload(void); + virtual int Mission_Repair(void); + virtual int Mission_Attack(void); + virtual int Mission_Harvest(void); + virtual int Mission_Guard(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Missile(void); + virtual void Enter_Idle_Mode(bool initial=false); + void Remove_Gap_Effect(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "STRUCTURES";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual unsigned Spied_By() const; + + private: + void Drop_Debris(TARGET source = TARGET_NONE); + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + static COORDINATE const CenterOffset[BSIZE_COUNT]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/bullet.cpp b/code/redalert/bullet.cpp new file mode 100644 index 0000000..06e2c19 --- /dev/null +++ b/code/redalert/bullet.cpp @@ -0,0 +1,1110 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BULLET.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : October 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::AI -- Logic processing for bullet. * + * BulletClass::BulletClass -- Bullet constructor. * + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * BulletClass::delete -- Bullet memory delete. * + * BulletClass::new -- Allocates memory for bullet object. * + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * BulletClass::BulletClass -- Bullet constructor. * + * * + * This is the constructor for the bullet class. It handles all * + * initialization of the bullet and starting it in motion toward its * + * target. * + * * + * INPUT: id -- The type of bullet this is (could be missile). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 06/20/1994 JLB : Firer is a base class pointer. * + * 12/10/1994 JLB : Auto calculate range optional. * + * 12/12/1994 JLB : Handles small arms as an instantaneous effect. * + * 12/23/1994 JLB : Fixed scatter algorithm for non-homing projectiles. * + * 12/31/1994 JLB : Removed range parameter (not needed). * + *=============================================================================================*/ +BulletClass::BulletClass(BulletType id, TARGET target, TechnoClass * payback, int strength, WarheadType warhead, int speed) : + ObjectClass(RTTI_BULLET, Bullets.ID(this)), + Class(BulletTypes.Ptr((int)id)), + Payback(payback), + PrimaryFacing(DIR_N), + IsInaccurate(false), + IsToAnimate(false), + IsLocked(true), + TarCom(target), + MaxSpeed(speed), + Warhead(warhead) +{ + Strength = strength; + Height = FLIGHT_LEVEL; +} + + +/*********************************************************************************************** + * BulletClass::~BulletClass -- Destructor for bullet objects. * + * * + * The bullet destructor must detect if a dog has been attached to this bullet. If so, * + * then the attached dog must be unlimboed back onto the map. This operation is necessary * + * because, unlike other objects, the dog flies with the bullet it fires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +BulletClass::~BulletClass(void) +{ + if (GameActive) { + + /* + ** SPECIAL CASE: + ** The dog is attached to the dog bullet in a limbo state. When the bullet is + ** destroyed, the dog must come back out of limbo at the closest location possible to + ** the bullet. + */ + if (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) { + + InfantryClass * dog = (InfantryClass *)Payback; + if (dog) { + bool unlimbo = false; + DirType dogface = dog->PrimaryFacing; + COORDINATE newcoord = Coord; + + /* + ** Ensure that the coordinate, that the dog is to appear at, is legal. If not, + ** then find a nearby legal location. + */ + if (Can_Enter_Cell(newcoord) != MOVE_OK) { + newcoord = Map.Nearby_Location(Coord_Cell(newcoord), dog->Class->Speed); + } + + /* + ** Try to put the dog down where the target impacted. If we can't + ** put it in that cell, then scan through the adjacent cells, + ** starting with our current heading, until we find a place where + ** we can put him down. If all 8 adjacent cell checks fail, then + ** just delete the dog. + */ + for (int i = -1; i < 8; i++) { + if (i != -1) { + newcoord = Adjacent_Cell(Coord, FacingType(i)); + } + ScenarioInit++; + if (dog->Unlimbo(newcoord, dog->PrimaryFacing)) { + dog->Mark(MARK_DOWN); + dog->Do_Action(DO_DOG_MAUL, true); + if (dog->WasSelected) { + dog->Select(); + } + ScenarioInit--; + + unlimbo = true; + break; + } + ScenarioInit--; + } + + Payback = 0; + + if (!unlimbo) { + delete dog; + } + } + } + BulletClass::Limbo(); + } + + Class=0; + Payback=0; +} + + +/*********************************************************************************************** + * BulletClass::new -- Allocates memory for bullet object. * + * * + * This function will "allocate" a block of memory for a bullet object. * + * This memory block is merely lifted from a fixed pool of blocks. * + * * + * INPUT: size -- The size of the memory block needed. * + * * + * OUTPUT: Returns with a pointer to an available bullet object block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void * BulletClass::operator new(size_t ) +{ + void * ptr = Bullets.Allocate(); + if (ptr) { + ((BulletClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * BulletClass::delete -- Bullet memory delete. * + * * + * Since bullets memory is merely "allocated" out of a pool, it never * + * actually gets deleted. * + * * + * INPUT: ptr -- Generic pointer to bullet object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::operator delete(void * ptr) +{ + if (ptr) { + ((BulletClass *)ptr)->IsActive = false; + } + Bullets.Free((BulletClass *)ptr); +} + + +/*********************************************************************************************** + * BulletClass::Occupy_List -- Determines the bullet occupation list. * + * * + * This function will determine the cell occupation list and return a pointer to it. Most * + * bullets are small and the list is usually short, but on occasion, it can be a list that * + * rivals the size of regular vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cell offset list that covers all the cells a bullet * + * is over. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 01/05/1995 JLB : Handles projectiles with altitude. * + *=============================================================================================*/ +short const * BulletClass::Occupy_List(bool) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Super-gigundo units use the >= 64 coord spillage list logic. + */ + if (Class->IsGigundo) { + static short _list[] = { + -1, 0, 1, + MAP_CELL_W*1-1, MAP_CELL_W*1, MAP_CELL_W*1+1, + -MAP_CELL_W*1-1, -MAP_CELL_W*1, -MAP_CELL_W*1+1, + MAP_CELL_W*2-1, MAP_CELL_W*2, MAP_CELL_W*2+1, + -MAP_CELL_W*2-1, -MAP_CELL_W*2, -MAP_CELL_W*2+1, + -MAP_CELL_W*3-1, -MAP_CELL_W*3, -MAP_CELL_W*3+1, + REFRESH_EOL + }; + return(_list); +// return(Coord_Spillage_List(Coord, 64)); + } + + /* + ** Flying units need a special adjustment to the spillage list to take into account + ** that the bullet imagery and the shadow are widely separated. + */ + if (Height > 0) { + static short _list[25]; + const short * ptr = Coord_Spillage_List(Coord, 5); + int index = 0; + CELL cell1 = Coord_Cell(Coord); + + while (ptr[index] != REFRESH_EOL) { + _list[index] = ptr[index]; + index++; + } + + COORDINATE coord = Coord_Move(Coord, DIR_N, Height); + CELL cell2 = Coord_Cell(coord); + ptr = Coord_Spillage_List(coord, 5); + while (*ptr != REFRESH_EOL) { + _list[index++] = *ptr + (cell2 - cell1); + ptr++; + } + _list[index] = REFRESH_EOL; + return(_list); + } + + return(Coord_Spillage_List(Coord, 10)); +} + + +/*********************************************************************************************** + * BulletClass::Mark -- Performs related map refreshing under bullet. * + * * + * This routine marks the objects under the bullet so that they will * + * be redrawn. This is necessary as the bullet moves -- objects under * + * its path need to be restored. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Mark(MarkType mark) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (!Class->IsInvisible) { + Map.Refresh_Cells(Coord_Cell(Coord), Occupy_List()); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::AI -- Logic processing for bullet. * + * * + * This routine will perform all logic (flight) logic on the bullet. * + * Primarily this is motion, fuse tracking, and detonation logic. Call * + * this routine no more than once per bullet per game tick. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::AI(void) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + COORDINATE coord; + + ObjectClass::AI(); + + if (!IsActive) return; + + /* + ** Ballistic objects are handled here. + */ + bool forced = false; // Forced explosion. + if ((Class->IsArcing || Class->IsDropping) && !IsFalling) { + forced = true; + } + + /* + ** Homing projectiles constantly change facing to face toward the target but + ** they only do so every other game frame (improves game speed and makes + ** missiles not so deadly). + */ + if ((Frame & 0x01) && Class->ROT != 0 && Target_Legal(TarCom)) { + PrimaryFacing.Set_Desired(Direction256(Coord, ::As_Coord(TarCom))); + } + + /* + ** Move the projectile forward according to its speed + ** and direction. + */ + coord = Coord; + if (Class->IsFlameEquipped) { + if (IsToAnimate) { + if (stricmp(Class->GraphicName, "FB1") == 0) { + new AnimClass(ANIM_FBALL_FADE, coord, 1); + } else { + new AnimClass(ANIM_SMOKE_PUFF, coord, 1); + } + } + IsToAnimate = !IsToAnimate; + } + + /* + ** Handle any body rotation at this time. This process must + ** occur every game fame in order to achieve smooth rotation. + */ + if (PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Rotation_Adjust(Class->ROT); + } + switch (Physics(coord, PrimaryFacing)) { + /* + ** When a projectile reaches the edge of the world, it + ** vanishes from existence -- presumed to explode off + ** map. + */ + case IMPACT_EDGE: + Mark(); + if (Payback != NULL && Class->Type == BULLET_GPS_SATELLITE) { + + bool reveal = false; + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (Payback->House == PlayerPtr) { + reveal = true; + } + } else { + if (Payback->House->IsHuman) { + reveal = true; + } + } + if (reveal) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, Payback->House); + } + Map.RadarClass::Flag_To_Redraw(true); + } + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +#ifdef OBSOLETE + /* + ** Hack: If it's the artificial nukes, don't let the bullets come down (as + ** they're the only ones that blow up). We know it's artificial if you're + ** at tech level 10 or below, because you can't build the nuclear silo until + ** tech level 15 or so. + */ + if (Payback != NULL && Class->Type == BULLET_NUKE_UP && Payback->House->Control.TechLevel <= 10) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(Payback->House->NukeDest), Payback, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(Payback->House->NukeDest); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(Payback->House->NukeDest), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + } + } +#endif + delete this; + break; + + default: + case IMPACT_NONE: + + /* + ** The projectile has moved. Check its fuse. If detonation + ** is signaled, then do so. Otherwise, just move. + */ + case IMPACT_NORMAL: + Mark(); +// if(Class->Type == BULLET_NUKE_DOWN) { +// Render(true); +// } + if (Class->Type == BULLET_NUKE_UP) { + if (Payback != NULL) { + if (Distance(Payback->As_Target()) > 0x0C00) { + delete this; + return; + } + } + } + Coord = coord; + + /* + ** See if the bullet should be forced to explode now in spite of what + ** the fuse would otherwise indicate. Maybe the bullet hit a wall? + */ + if (!forced) { + forced = Is_Forced_To_Explode(Coord); + } + + /* + ** If the bullet is not to explode, then perform normal flight + ** maintenance (usually nothing). Otherwise, explode and then + ** delete the bullet. + */ + if (!forced && (Class->IsDropping || !Fuse_Checkup(Coord))) { + /* + ** Certain projectiles lose strength when they travel. + */ + if (Class->IsDegenerate && Strength > 5) { + Strength--; + } + + } else { + Bullet_Explodes(forced); + delete this; + } + break; + } + +} + + +/*********************************************************************************************** + * BulletClass::Shape_Number -- Fetches the shape number for the bullet object. * + * * + * Use this routine to fetch a shape number to use for this bullet object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use when drawing this bullet. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int BulletClass::Shape_Number(void) const +{ + int shapenum = 0; + + if (!Class->IsFaceless) { + shapenum = UnitClass::BodyShape[Dir_To_32(PrimaryFacing)]; + } + + /* + ** For tumbling projectiles, fetch offset stage. + */ + if (Class->Tumble > 0) { + shapenum += (long)Frame % Class->Tumble; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * BulletClass::Draw_It -- Displays the bullet at location specified. * + * * + * This routine displays the bullet visual at the location specified. * + * * + * INPUT: x,y -- The center coordinate to render the bullet at. * + * * + * window -- The window to clip to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window clipping parameter. * + * 01/08/1995 JLB : Handles translucent colors if necessary. * + *=============================================================================================*/ +void BulletClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Certain projectiles aren't visible. This includes small bullets (which are actually + ** invisible) and flame thrower flames (which are rendered as an animation instead of a projectile). + */ + if (Class->IsInvisible) return; + + /* + ** If there is no shape loaded for this object, then + ** it obviously can't be rendered -- just bail. + */ + void const * shapeptr = Get_Image_Data(); + if (shapeptr == NULL) return; + + /* + ** Get the basic shape number for this projectile. + */ + int shapenum = Shape_Number(); + + /* + ** For flying projectiles, draw the shadow and adjust the actual projectile body + ** render position. + */ + if (Height > 0 && Class->IsShadow) { + + if (Class->IsParachuted) { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, AnimTypeClass::As_Reference(ANIM_PARA_BOMB).Get_Image_Data(), 1, x+Lepton_To_Pixel(Height/2), y+10, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } else { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::UnitShadow); + } + y -= Lepton_To_Pixel(Height); + } + + /* + ** Draw the main body of the projectile. + */ + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Class->IsTranslucent) { + flags = SHAPE_GHOST; + } + if (Class->IsSubSurface) + { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, NULL, DisplayClass::FadingShade); + } else { + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapeptr, shapenum, x, y, window, flags|SHAPE_CENTER|SHAPE_WIN_REL, NULL, DisplayClass::UnitShadow); + } +} + + +/*********************************************************************************************** + * BulletClass::Init -- Clears the bullets array for scenario preparation. * + * * + * This routine will zero out the bullet tracking list and object array in preparation for * + * the start of a new scenario. All bullets cease to exists after this function is * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Init(void) +{ + Bullets.Free_All(); +} + + +/*********************************************************************************************** + * BulletClass::Detach -- Removes specified target from this bullet's targeting system. * + * * + * When an object is removed from the game system, it must be removed all targeting and * + * tracking systems as well. This routine is used to remove the specified object from the * + * bullet. If the object isn't part of this bullet's tracking system, then no action is * + * performed. * + * * + * INPUT: target -- The target to remove from this tracking system. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Detach(TARGET target, bool all) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + ObjectClass * obj = As_Object(target); + if (Payback != NULL && obj == Payback) { + + /* + ** If we're being called as a result of the dog that fired us being put + ** in limbo, then don't detach. If for any other reason, detach. + */ + if (Payback->What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)Payback)->Class->IsDog) { + Payback = 0; + } + } + + if (all && target == TarCom) { + TarCom = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * BulletClass::Unlimbo -- Transitions a bullet object into the game render/logic system. * + * * + * This routine is used to take a bullet object that is in limbo and transition it to the * + * game system. A bullet object so transitioned, will be drawn and logic processing * + * performed. In effect, it comes into existence. * + * * + * INPUT: coord -- The location where the bullet object is to appear. * + * * + * dir -- The initial facing for the bullet object. * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + /* + ** Try to unlimbo the bullet as far as the base class is concerned. Use the already + ** set direction and strength if the "punt" values were passed in. This allows a bullet + ** to be setup prior to being launched. + */ + if (!Class->IsHigh) { + Height = 0; + } + if (ObjectClass::Unlimbo(coord)) { + Map.Remove(this, In_Which_Layer()); + + COORDINATE tcoord = As_Coord(TarCom); + + /* + ** Homing projectiles (missiles) do NOT override facing. They just fire in the + ** direction specified and let the chips fall where they may. + */ + if (Class->ROT == 0 && !Class->IsDropping) { + dir = Direction(tcoord); + } + + /* + ** Possibly adjust the target if this projectile is inaccurate. This occurs whenever + ** certain weapons are trained upon targets they were never designed to attack. Example: when + ** turrets or anti-tank missiles are fired at infantry. Indirect + ** fire is inherently inaccurate. + */ + if (IsInaccurate || Class->IsInaccurate || + ((Is_Target_Cell(TarCom) || Is_Target_Infantry(TarCom)) && (Warhead == WARHEAD_AP || Class->IsFueled))) { + + /* + ** Inaccuracy for low velocity or homing projectiles manifests itself as a standard + ** Circular Error of Probability (CEP) algorithm. High speed projectiles usually + ** just overshoot the target by extending the straight line flight. + */ + if (/*Class->ROT != 0 ||*/ Class->IsArcing) { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.HomingScatter); + scatterdist = max(scatterdist, 0); + + dir = (DirType)((dir + (Random_Pick(0, 10)-5)) & 0x00FF); + tcoord = Coord_Scatter(tcoord, Random_Pick(0, scatterdist)); + } else { + int scatterdist = (::Distance(coord, tcoord)/16)-0x0040; + scatterdist = min(scatterdist, Rule.BallisticScatter); + scatterdist = max(scatterdist, 0); + tcoord = Coord_Move(tcoord, dir, Random_Pick(0, scatterdist)); + } + } + + /* + ** For very fast and invisible projectiles, just make the projectile exist at the target + ** location and dispense with the actual flight. + */ + if (MaxSpeed == MPH_LIGHT_SPEED && Class->IsInvisible) { + Coord = tcoord; + } + + /* + ** Set the range equal to either the class defined range or the calculated + ** number of game frames it would take for the projectile to reach the target. + */ + int range = 0xFF; + if (!Class->IsDropping) { + range = (::Distance(tcoord, Coord) / MaxSpeed) + 4; + } + + /* + ** Projectile speed is usually the default value for that projectile, but + ** certain projectiles alter speed according to the distance to the + ** target. + */ + int speed = MaxSpeed; + if (speed == MPH_LIGHT_SPEED) speed = MPH_IMMOBILE; + if (Class->IsArcing) { + speed = MaxSpeed + (Distance(tcoord) / 32); + + /* + ** Set minimum speed (i.e., distance) for arcing projectiles. + */ + speed = max(speed, 25); + } + if (!Class->IsDropping) { + Fly_Speed(255, (MPHType)speed); + } + + /* + ** Arm the fuse. + */ + Arm_Fuse(Coord, tcoord, range, ((As_Aircraft(TarCom)!=0) ? 0 : Class->Arming)); + + /* + ** Projectiles that make a ballistic flight to impact point must determine a + ** vertical component for the projectile launch. This is crudely simulated + ** by biasing ground speed according to target distance and then giving + ** enough vertical velocity to keep the projectile airborne for the + ** desired amount of time. The mathematically correct solution would be to + ** calculate launch angle (given fixed projectile velocity) and then derive + ** the vertical and horizontal components. That solution would require a + ** square root and an arcsine lookup table. OUCH! + */ + Riser = 0; + if (Class->IsArcing) { + IsFalling = true; + Height = 1; + Riser = ((Distance(tcoord)/2) / (speed+1)) * Rule.Gravity; + Riser = max(Riser, 10); + } + if (Class->IsDropping) { + IsFalling = true; + Height = FLIGHT_LEVEL; +// Height = Pixel_To_Lepton(24); + Riser = 0; + if (Class->IsParachuted) { + AnimClass * anim = new AnimClass(ANIM_PARA_BOMB, Target_Coord()); +// AnimClass * anim = new AnimClass(ANIM_PARACHUTE, Target_Coord()); + if (anim) { + anim->Attach_To(this); + } + } + } + Map.Submit(this, In_Which_Layer()); + + PrimaryFacing = dir; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Target_Coord -- Fetches coordinate to use when firing on this object. * + * * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that should be used when firing at the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Target_Coord(void) const +{ + assert(Bullets.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Coord)); +} + + +/*********************************************************************************************** + * BulletClass::Sort_Y -- Sort coordinate for bullet rendering. * + * * + * This will return the coordinate to use when sorting this bullet in the display list. * + * Typically, this only occurs for bullets in the ground layer. Since bullets are to be * + * seen a bit more than the normal sorting order would otherwise imply, bias the sort * + * value such that bullets will tend to be drawn on top of the objects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when sorting this bullet in the display list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE BulletClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Move(Coord, DIR_S, CELL_LEPTON_H/2)); +} + + +/*********************************************************************************************** + * BulletClass::In_Which_Layer -- Fetches the layer that the bullet resides in. * + * * + * This examines the bullet to determine what rendering layer it should be in. The * + * normal logic applies unless this is a torpedo. A torpedo is always in the surface * + * layer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the render layer that this bullet should reside in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +LayerType BulletClass::In_Which_Layer(void) const +{ + if (Class->IsSubSurface) { + return(LAYER_SURFACE); + } + return(ObjectClass::In_Which_Layer()); +} + + +/*********************************************************************************************** + * BulletClass::Is_Forced_To_Explode -- Checks if bullet should explode NOW. * + * * + * This routine will examine the bullet and where it is travelling in order to determine * + * if it should prematurely explode. Typical of this would be when a bullet hits a wall * + * or a torpedo hits a ship -- regardless of where the projectile was originally aimed. * + * * + * INPUT: coord -- The new coordinate to place the bullet at presuming it is forced to * + * explode and a modification of the bullet's coordinate is needed. * + * Otherwise, the coordinate is not modified. * + * * + * OUTPUT: bool; Should the bullet explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +bool BulletClass::Is_Forced_To_Explode(COORDINATE & coord) const +{ + coord = Coord; + CellClass const * cellptr = &Map[coord]; + + /* + ** Check for impact on a wall or other high obstacle. + */ + if (!Class->IsHigh && cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsHigh) { + coord = Cell_Coord(Coord_Cell(coord)); + return(true); + } + + /* + ** Check to make sure that underwater projectiles (torpedoes) will not + ** travel in anything but water. + */ + if (Class->IsSubSurface) { + int d = ::Distance(Coord_Fraction(coord), XY_Coord(CELL_LEPTON_W/2, CELL_LEPTON_W/2)); + if (cellptr->Land_Type() != LAND_WATER || (d < CELL_LEPTON_W/3 && cellptr->Cell_Techno() != NULL && cellptr->Cell_Techno() != Payback)) { + + /* + ** Force explosion to be at center of techno object if one is present. + */ + if (cellptr->Cell_Techno() != NULL) { + coord = cellptr->Cell_Techno()->Target_Coord(); + } + + /* + ** However, if the torpedo was blocked by a bridge, then force the + ** torpedo to explode on top of that bridge cell. + */ + if (cellptr->Is_Bridge_Here()) { + coord = Coord_Snap(coord); + } + + return(true); + } + } + + /* + ** Bullets are generally more effective when they are fired at aircraft. + */ + if (Class->IsAntiAircraft && As_Aircraft(TarCom) && Distance(TarCom) < 0x0080) { + return(true); + } + + /* + ** No reason for forced explosion was detected, so return 'false' to + ** indicate that no forced explosion is required. + */ + return(false); +} + + +/*********************************************************************************************** + * BulletClass::Bullet_Explodes -- Performs bullet explosion logic. * + * * + * This handles the exploding bullet action. It will generate the animation and the * + * damage as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The bullet should be deleted after this routine is called. * + * * + * HISTORY: * + * 10/10/1996 JLB : Created. * + *=============================================================================================*/ +void BulletClass::Bullet_Explodes(bool forced) +{ + /* + ** When the target is reached, explode and do the damage + ** required of it. For homing objects, don't force the explosion to + ** match the target position. Non-homing projectiles adjust position so + ** that they hit the target. This compensates for the error in line of + ** flight logic. + */ + if ( (Payback != NULL && Payback->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)Payback)->Class->IsDog) || + (!forced && !Class->IsArcing && Class->ROT == 0 && Fuse_Target())) { + Coord = Fuse_Target(); + } + + /* + ** Non-aircraft targets apply damage to the ground. + */ + if (!Is_Target_Aircraft(TarCom) || As_Aircraft(TarCom)->In_Which_Layer() == LAYER_GROUND) { + Explosion_Damage(Coord, Strength, Payback, Warhead); + if (!IsActive) return; + + } else { + + /* + ** Special damage apply for SAM missiles. This is the only way that missile + ** damage affects the aircraft target. + */ + if (Distance(TarCom) < 0x0080) { + AircraftClass * object = As_Aircraft(TarCom); + + int str = Strength; + if (object) object->Take_Damage(str, 0, Warhead, Payback); + } + } + + /* + ** For projectiles that are invisible while travelling toward the target, + ** allow scatter effect for the impact animation. + */ + if (Class->IsInvisible) { + Coord = Coord_Scatter(Coord, 0x0020); + } + + /* + ** Fetch the land type that the explosion will be upon. Special case for + ** flying aircraft targets, their land type will be LAND_NONE. + */ + CellClass const * cellptr = &Map[Coord]; + LandType land = cellptr->Land_Type(); + if (Is_Target_Aircraft(TarCom) && As_Aircraft(TarCom)->In_Which_Layer() == LAYER_TOP) { + land = LAND_NONE; + } + + AnimType anim = Combat_Anim(Strength, Warhead, land); + + /* + ** If it's a water explosion that's going to play, don't play it + ** if its cell is the same as the center cell of the target ship. + */ + if (anim >= ANIM_WATER_EXP1 && anim <= ANIM_WATER_EXP3 && Is_Target_Vessel(TarCom)) { + if (Coord_Cell(Coord) == Coord_Cell(As_Vessel(TarCom)->Center_Coord())) { + anim = (AnimType) (ANIM_VEH_HIT1 + (anim - ANIM_WATER_EXP1)); + } + } + + if (anim != ANIM_NONE) { + AnimClass * aptr = new AnimClass(anim, Coord); + if (aptr) { + aptr->Sort_Above(TarCom); + } + /* + ** Special case trap: if they're making the nuclear explosion, + ** and no anim is available, force the nuclear damage anyway + ** because nuke damage is done in the middle of the animation + ** and if there's no animation, there won't be any damage. + */ + if (!aptr && anim == ANIM_ATOM_BLAST) { + GlyphX_Debug_Print("FAILED to create ANIM_ATOM_BLAST"); + HousesType house = HOUSE_NONE; + if (Payback) { + house = Payback->House->Class->House; + } + AnimClass::Do_Atom_Damage(house, Coord_Cell(Coord)); + } + + // MBL 05.20.2020 + // Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked + // Per https://jaas.ea.com/browse/TDRA-6610 + // + else if (aptr && anim == ANIM_ATOM_BLAST && aptr->OwnerHouse == HOUSE_NONE) { + if (Payback && Payback->House && Payback->House->Class) { + aptr->OwnerHouse = Payback->House->Class->House; + } + } + } + +// if (Payback && Payback->House == PlayerPtr && stricmp(Class->Name(), "GPSSATELLITE") == 0) { + if (Payback && Class->Type == BULLET_GPS_SATELLITE) { + + bool reveal = false; + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (Payback->House == PlayerPtr) { + reveal = true; + } + } else { + if (Payback->House->IsHuman) { + reveal = true; + } + } + + if (reveal) { + if (!Map.Is_Radar_Active()) { + Map.Radar_Activate(1); + } + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, Payback->House); + } + Map.RadarClass::Flag_To_Redraw(true); + } +// Sound_Effect(VOC_SATTACT2); + Payback->House->IsGPSActive = true; + Payback->House->IsVisionary = true; + } +} \ No newline at end of file diff --git a/code/redalert/bullet.h b/code/redalert/bullet.h new file mode 100644 index 0000000..9853e05 --- /dev/null +++ b/code/redalert/bullet.h @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/BULLET.H 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BULLET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BULLET_H +#define BULLET_H + +#include "object.h" +#include "fly.h" +#include "fuse.h" + + +class BulletClass : public ObjectClass, + public FlyClass, + public FuseClass +{ + public: + + /* + ** This specifies exactly what kind of bullet this is. All of the static attributes + ** for this bullet is located in the BulletTypeClass pointed to by this variable. + */ + CCPtr Class; + + private: + /* + ** Records who sent this "present" so that an appropriate "thank you" can + ** be returned. + */ + TechnoClass * Payback; + + /* + ** This is the facing that the projectile is travelling. + */ + FacingClass PrimaryFacing; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + BulletClass(BulletType id, TARGET target, TechnoClass * Payback, int strength, WarheadType warhead, int speed); +#ifdef FIXIT_MULTI_SAVE + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x), PrimaryFacing(x) {}; +#else + BulletClass(NoInitClass const & x) : ObjectClass(x), Class(x), FlyClass(x), FuseClass(x) {}; +#endif + virtual ~BulletClass(void); + operator BulletType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + bool Is_Forced_To_Explode(COORDINATE & coord) const; + void Bullet_Explodes(bool forced); + int Shape_Number(void) const; + virtual LayerType In_Which_Layer(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual void Assign_Target(TARGET target) {TarCom = target;}; + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Detach(TARGET target, bool all); + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual void AI(void); + virtual short const * Occupy_List(bool = false) const; + virtual short const * Overlap_List(void) const {return Occupy_List(false);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** If this bullet is forced to be inaccurate because of some outside means. A tank + ** firing while moving is a good example. + */ + unsigned IsInaccurate:1; + + private: + // Crude animation flag. + unsigned IsToAnimate:1; + + /* + ** Is this missile allowed to come in from out of bounds? + */ + unsigned IsLocked:1; + + /* + ** This is the target of the projectile. It is especially significant for those projectiles + ** that home in on a target. + */ + TARGET TarCom; + + /* + ** The speed of this projectile. + */ + int MaxSpeed; + + /* + ** The warhead of this projectile. + */ + WarheadType Warhead; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif diff --git a/code/redalert/cargo.cpp b/code/redalert/cargo.cpp new file mode 100644 index 0000000..7e85a6c --- /dev/null +++ b/code/redalert/cargo.cpp @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARGO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : 10/31/94 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CargoClass::Attach -- Add unit to cargo hold. * + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * CargoClass::Debug_Dump -- Displays the cargo value to the monochrome screen. * + * * + * This routine is used to dump the current cargo value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void CargoClass::Debug_Dump(MonoClass * mono) const +{ + if (How_Many()) { + mono->Set_Cursor(63, 3); + mono->Printf("(%d)%04X", How_Many(), Attached_Object()); + } +} +#endif + + +/*********************************************************************************************** + * CargoClass::Attach -- Add unit to cargo hold. * + * * + * This routine will add the specified unit to the cargo hold. The * + * unit will chain to any existing units in the hold. The chaining is * + * in a LIFO order. * + * * + * INPUT: object-- Pointer to the object to attach to the cargo hold. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 10/31/94 JLB : Handles chained objects. * + *=============================================================================================*/ +void CargoClass::Attach(FootClass * object) +{ + /* + ** If there is no object, then no action is necessary. + */ + if (object == NULL) return; + + object->Limbo(); + + /* + ** Attach any existing cargo hold object to the end of the list as indicated by the + ** object pointer passed into this routine. This is necessary because several objects may + ** be attached at one time or several objects may be attached as a result of several calls + ** to this routine. Either case must be handled properly. + */ + ObjectClass * o = object->Next; + while (o != NULL) { + if (o->Next == (void*)NULL) break; + o = o->Next; + } + if (o != NULL) { + o->Next = CargoHold; + } else { + object->Next = CargoHold; + } + + /* + ** Finally, assign the object pointer as the first object attached to this cargo hold. + */ + CargoHold = object; + Quantity = 0; + object = CargoHold; + while (object != NULL) { + Quantity++; + object = (FootClass *)(ObjectClass *)object->Next; + } +} + + +/*********************************************************************************************** + * CargoClass::Detach_Object -- Removes a unit from the cargo hold. * + * * + * This routine will take a unit from the cargo hold and extract it. * + * The unit extracted is the last unit added to the hold. If there * + * is no unit in the hold or the occupant is not a unit, then NULL is * + * returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the unit that has been extracted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Detach_Object(void) +{ + TechnoClass * unit = Attached_Object(); + + if (unit != NULL) { + CargoHold = (FootClass *)(ObjectClass *)unit->Next; + unit->Next = 0; + Quantity--; + } + return((FootClass *)unit); +} + + +/*********************************************************************************************** + * CargoClass::Attached_Object -- Determine attached unit pointer. * + * * + * This routine will return with a pointer to the attached unit if one * + * is present. One would need to know this if this is a transport * + * unit and it needs to unload. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the attached unit. If there is no * + * attached unit, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 06/07/1994 JLB : Handles generic object types. * + *=============================================================================================*/ +FootClass * CargoClass::Attached_Object(void) const +{ + if (Is_Something_Attached()) { + return(CargoHold); + } + return(NULL); +} + + diff --git a/code/redalert/cargo.h b/code/redalert/cargo.h new file mode 100644 index 0000000..524415a --- /dev/null +++ b/code/redalert/cargo.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARGO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARGO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CARGO_H +#define CARGO_H + +class FootClass; + +/**************************************************************************** +** This class handles the basic cargo logic. +*/ +class CargoClass { + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CargoClass(void) : Quantity(0), CargoHold(0) {}; + CargoClass(NoInitClass const & ) {}; + ~CargoClass(void) {CargoHold=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void) {}; + + int How_Many(void) const {return Quantity;}; + bool Is_Something_Attached(void) const {return (CargoHold != 0);}; + FootClass * Attached_Object(void) const; + FootClass * Detach_Object(void); + void Attach(FootClass * object); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + + /* + ** This is the number of objects attached to this cargo hold. For transporter + ** objects, they might contain more than one object. + */ + unsigned char Quantity; + + /* + ** This is the target value of any attached object. A value of zero indicates + ** that no object is attached. + */ + FootClass * CargoHold; +}; + +#endif + diff --git a/code/redalert/carry.cpp b/code/redalert/carry.cpp new file mode 100644 index 0000000..c0fefb5 --- /dev/null +++ b/code/redalert/carry.cpp @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARRY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : May 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * CarryoverClass::Create -- Creates a carried over object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CarryoverClass::CarryoverClass -- Constructor for carry over objects. * + * * + * This is the constructor for a carry over object. Such an object is used to record the * + * object that will be "carried over" into a new scenario at some future time. * + * * + * INPUT: techno -- Pointer to the object that will be carried over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +CarryoverClass::CarryoverClass(TechnoClass * techno) : + RTTI(RTTI_NONE), + Cell(0), + Strength(0), + House(HOUSE_NONE) +{ + if (techno) { + RTTI = techno->What_Am_I(); + + switch (RTTI) { + case RTTI_UNIT: + Type.Unit = ((UnitClass *)techno)->Class->Type; + break; + + case RTTI_BUILDING: + Type.Building = ((BuildingClass *)techno)->Class->Type; + break; + + case RTTI_INFANTRY: + Type.Infantry = ((InfantryClass *)techno)->Class->Type; + break; + + case RTTI_VESSEL: + Type.Vessel = ((VesselClass *)techno)->Class->Type; + break; + + default: + break; + } + + House = techno->Owner(); + Strength = techno->Strength; + Cell = Coord_Cell(techno->Coord); + } +} + + +/*********************************************************************************************** + * CarryoverClass::Create -- Creates a carried over object. * + * * + * Use this routine to convert a carried over object into an actual object that will be * + * placed on the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully created and placed on the map? * + * * + * WARNINGS: This routine might not place the object if the old map location was invalid * + * or there are other barriers to the object's creation and placement. * + * * + * HISTORY: * + * 05/10/1996 JLB : Created. * + *=============================================================================================*/ +bool CarryoverClass::Create(void) const +{ + TechnoClass * techno = 0; + TechnoTypeClass const * ttype = 0; + + switch (RTTI) { + case RTTI_UNIT: + ttype = &UnitTypeClass::As_Reference(Type.Unit); + techno = new UnitClass(Type.Unit, House); + break; + + case RTTI_INFANTRY: + ttype = &InfantryTypeClass::As_Reference(Type.Infantry); + techno = new InfantryClass(Type.Infantry, House); + break; + + case RTTI_BUILDING: + ttype = &BuildingTypeClass::As_Reference(Type.Building); + techno = new BuildingClass(Type.Building, House); + break; + + case RTTI_VESSEL: + ttype = &VesselTypeClass::As_Reference(Type.Vessel); + techno = new VesselClass(Type.Vessel, House); + break; + } + + if (techno) { + bool oldscen = ScenarioInit; + techno->Strength = Strength; + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = 0; + } + techno->Unlimbo(Cell_Coord(Cell)); + if (RTTI == RTTI_INFANTRY) { + ScenarioInit = oldscen; + } + } + + return(false); +} + diff --git a/code/redalert/carry.h b/code/redalert/carry.h new file mode 100644 index 0000000..971c6a2 --- /dev/null +++ b/code/redalert/carry.h @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CARRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CARRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CARRY_H +#define CARRY_H + +class CarryoverClass : public LinkClass { + public: + CarryoverClass(TechnoClass * techno = 0); + CarryoverClass(NoInitClass const & x) : LinkClass(x) {} + + bool Create(void) const; + + //protected: + /* + ** What type of object this is. + */ + RTTIType RTTI; + + /* + ** This is the object type that is to be carried over. The exact nature of + ** this type depends on the RTTI value. Only certain object types are + ** recorded. + */ + union { + StructType Building; + UnitType Unit; + InfantryType Infantry; + VesselType Vessel; + } Type; + + /* + ** The location of the object. + */ + CELL Cell; + + /* + ** The strength of the object at the time is was recorded. + */ + int Strength; + + /* + ** This is the owner of the object. + */ + HousesType House; +}; + + +#endif diff --git a/code/redalert/cbn_.h b/code/redalert/cbn_.h new file mode 100644 index 0000000..81960ee --- /dev/null +++ b/code/redalert/cbn_.h @@ -0,0 +1,153 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* Error Constants, in case of values above 10 hexadecimal please call +* Marx International, Inc. +****************************************************************************/ + +#define PARAM_ERR 0x01 +#define BOX_NOT_FOUND 0x02 + +#define BOX_CRYPT_ERR 0x03 +#define UNI_CRYPT_ERR 0x04 +#define READ_RAM_ERR 0x05 +#define WRITE_RAM_ERR 0x06 +#define RAM_COUNT_ERR 0x07 +#define BOX_READY_ERR 0x08 + + +/**************************************************************************** +* Constants +****************************************************************************/ + +#define CRYPTLENG 32 +#define RAM1LENG 50 +#define RAM2LENG 433 +#define IDEALENG 32 + +/**************************************************************************** +* User Functions +****************************************************************************/ +extern "C" { + +extern short CbN_BoxReady(unsigned short iPortNr, unsigned char *pcBoxName); + +extern short CbN_ReadID1(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID2(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID3(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID4(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID5(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID6(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID7(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadID8(unsigned short iPortNr, unsigned char *pcSCodeId, + unsigned long *plIdReturn); + +extern short CbN_ReadSER(unsigned short iPortNr, unsigned char *pcSCodeSer, + unsigned long *plSerNum); + +extern short CbN_ReadRAM1(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam1,unsigned short + iStartAdr,unsigned short iLength, unsigned char *pcOutData); + +extern short CbN_ReadRAM2(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam2,unsigned short + iStartAdr,unsigned short iLength, unsigned char *pcOutData); + +extern short CbN_WriteRAM1(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam1,unsigned short + iStartAdr,unsigned short iLength,unsigned char *pcOutData); + +extern short CbN_WriteRAM2(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned char *pcPasswRam2,unsigned short + iStartAdr,unsigned short iLength,unsigned char *pcOutData); + +extern short CbN_IncRAM1(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam1, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_DecRAM1(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam1, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_IncRAM2(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam2, unsigned short + iCounterAdr, unsigned short *piNewCount); + +extern short CbN_DecRAM2(unsigned short iPortNr, unsigned short iIdNr, unsigned char + *pcSCodeId, unsigned char *pcPasswRam2, unsigned short + iCounterAdr, unsigned short *piNewCount); + + +extern short CbN_Decrypt(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned short iSeed, unsigned short iLength, + unsigned char *pcOutData); + +extern short CbN_Encrypt(unsigned short iPortNr,unsigned short iIdNr,unsigned char + *pcSCodeId,unsigned short iSeed,unsigned short iLength, + unsigned char *pcOutData); + +extern short CbN_IDEA_Encrypt(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength); + +extern short CbN_IDEA_Decrypt(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength); + +extern short CbN_IDEA_EncryptP(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength, unsigned long lIdeaKey); + +extern short CbN_IDEA_DecryptP(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char *pcBuff, + unsigned long lLength, unsigned long lIdeaKey); + +extern short CbN_SetCounterRAM1(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + iNewCount); + +extern short CbN_ReadCounterRAM1(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + *piCurrentCount); + +extern short CbN_SetCounterRAM2(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + iNewCount); + +extern short CbN_ReadCounterRAM2(unsigned short iPortNr, unsigned short iIdNr, + unsigned char *pcSCodeId, unsigned char + *pcPassRam, unsigned short iAdrCount, unsigned short + *piCurrentCount); + +} + \ No newline at end of file diff --git a/code/redalert/ccdde.cpp b/code/redalert/ccdde.cpp new file mode 100644 index 0000000..30e26b6 --- /dev/null +++ b/code/redalert/ccdde.cpp @@ -0,0 +1,428 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * DDE_Callback -- DDE server callback function * + * DDEServerClass::DDEServerClass -- class constructor * + * DDEServerClass::Enable -- Enables the DDE callback * + * DDEServerClass::Disable -- Disables the DDE callback * + * DDEServerClass::~DDEServerClass -- class destructor * + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include +#include "ccdde.h" +#include +#include + +DDEServerClass DDEServer; //Instance of the DDE Server class + +Instance_Class *DDE_Class = NULL; // pointer for client callback + // this *must* be called DDE_Class + +BOOL RA95AlreadyRunning = FALSE; //Was there an instance of Red Alert 95 already running when we started? + +/* +** Misc externs so we dont have to include FUNCTION.H +*/ +extern HWND MainWindow; +extern TimerClass GameTimer; +extern bool GameTimerInUse; +extern void WWDebugString (char *string); + + +/*********************************************************************************************** + * DDE_Callback -- DDE server callback function * + * * + * Just acts as a wrapper for the DDEServerClass callback function * + * * + * INPUT: ptr to data from client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:19PM ST : Created * + *=============================================================================================*/ +BOOL CALLBACK DDE_Callback (unsigned char *data, long length) +{ + return (DDEServer.Callback(data, length)); +} + + + + +/*********************************************************************************************** + * DDEServerClass::DDEServerClass -- class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::DDEServerClass(void) +{ + MPlayerGameInfo = NULL; //Flag that we havnt received a start game info packet yet + + //DDE_Class = new Instance_Class ("CONQUER", "WCHAT"); + DDE_Class = new Instance_Class ("REDALERT", "WCHAT"); + + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + + if (DDE_Class->Test_Server_Running(DDE_Class->local_name)){ + RA95AlreadyRunning = TRUE; + }else{ + //DDE_Class->Register_Server( DDE_Callback ); // ST - 5/8/2019 + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Enable -- Enables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Enable(void) +{ + if (!IsEnabled){ + DDE_Class->Enable_Callback( TRUE ); + IsEnabled = TRUE; + } +} + + + +/*********************************************************************************************** + * DDEServerClass::Disable -- Disables the DDE callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/96 9:44PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Disable(void) +{ + if (IsEnabled){ + DDE_Class->Enable_Callback( FALSE ); + IsEnabled = FALSE; + } +} + + + + + + +/*********************************************************************************************** + * DDEServerClass::~DDEServerClass -- class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:20PM ST : Created * + *=============================================================================================*/ +DDEServerClass::~DDEServerClass(void) +{ + Delete_MPlayer_Game_Info(); + delete( DDE_Class ); +} + + + +/*********************************************************************************************** + * DDESC::Callback -- callback function. Called from the DDE_Callback wrapper function * + * * + * * + * * + * INPUT: data from DDE client * + * length of data * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: Data has length and type as first 2 ints * + * * + * HISTORY: * + * 6/8/96 3:21PM ST : Created * + *=============================================================================================*/ +BOOL DDEServerClass::Callback(unsigned char *data, long length) +{ + + /* + ** If the packet length < 0 then this is a special advisory packet + */ + if ( length<0 ) { + + switch( length ) { + + case DDE_ADVISE_CONNECT: + WWDebugString("RA95 - DDE advisory: client connect detected."); + return TRUE; + + case DDE_ADVISE_DISCONNECT: + WWDebugString("RA95 - DDE advisory: client disconnect detected."); + return TRUE; + + default: + WWDebugString("RA95 - DDE advisory: Unknown DDE advise type."); + return FALSE; + } + + }else{ + + /* + ** Packet must be at least the length of the packet type & size fields to be valid + */ + if (length < 2*sizeof(int)) { + WWDebugString ("RA95 - Received invalid packet."); + return (FALSE); + } + + /* + ** Find out what kind of packet this is and its length. + */ + int *packet_pointer = (int *)data; + int actual_length = ntohl(*packet_pointer++); + int packet_type = ntohl(*packet_pointer++); + + /* + ** Strip the ID int from the start of the packet + */ + data += 2*sizeof (int); + length -= 2*sizeof (int); + actual_length -= 2*sizeof (int); + + /* + ** Take the appropriate action for the packet type + */ + switch ( packet_type ){ + + /* + ** This is a packet with the info required for starting a new internet game. This is really + * just C&CSPAWN.INI sent from WChat instead of read from disk. + */ + case DDE_PACKET_START_MPLAYER_GAME: + WWDebugString("RA95 - Received start game packet."); + Delete_MPlayer_Game_Info(); + MPlayerGameInfo = new char [actual_length + 1]; + memcpy (MPlayerGameInfo, data, actual_length); + *(MPlayerGameInfo + actual_length) = 0; //Terminator in case we treat it as a string + MPlayerGameInfoLength = actual_length; + LastHeartbeat = 0; + break; + + case DDE_TICKLE: + WWDebugString("RA95 - Received 'tickle' packet."); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + break; + + case DDE_PACKET_HEART_BEAT: + WWDebugString("RA95 - Received heart beat packet."); + if (GameTimerInUse){ + LastHeartbeat = GameTimer.Time(); + }else{ + LastHeartbeat = 0; + } + break; + + default: + WWDebugString("RA95 - Received unrecognised packet."); + break; + + } + } + + return (TRUE); + +} + + + +/*********************************************************************************************** + * DDESC::Get_MPlayer_Game_Info -- returns a pointer to the multiplayer setup info from wchat * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ptr to data in .INI file format * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:23PM ST : Created * + *=============================================================================================*/ +char *DDEServerClass::Get_MPlayer_Game_Info (void) +{ + return (MPlayerGameInfo); +} + + + +/*********************************************************************************************** + * DDESC::Delete_MPlayer_Game_Info -- clears out multi player game setup info * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 3:24PM ST : Created * + *=============================================================================================*/ +void DDEServerClass::Delete_MPlayer_Game_Info(void) +{ + if (MPlayerGameInfo){ + delete [] MPlayerGameInfo; + MPlayerGameInfo = NULL; + } +} + + + +/*********************************************************************************************** + * DDESC::Time_Since_Heartbeat -- returns the time in ticks since the last heartbeat from wchat* + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: time since heartbeat * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:05PM ST : Created * + *=============================================================================================*/ +int DDEServerClass::Time_Since_Heartbeat(void) +{ + return (GameTimer.Time() - LastHeartbeat); +} + + + + +/*********************************************************************************************** + * Send_Data_To_DDE_Server -- sends a packet to WChat * + * * + * * + * * + * INPUT: ptr to the data to send * + * length of data * + * packet type identifier * + * * + * OUTPUT: true if packet successfully sent * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 11:07PM ST : Created * + *=============================================================================================*/ +BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type) +{ + if( DDE_Class->Open_Poke_Connection(DDE_Class->remote_name) == FALSE) { + WWDebugString("RA95 - Failed to connect for POKE!"); + return (FALSE); + } + + char *poke_data = new char [length + 2*sizeof(int)]; + + int *poke_data_int = (int*)poke_data; + + *poke_data_int = htonl (length + 2*sizeof(int)); + *(poke_data_int+1)= htonl (packet_type); + + memcpy (poke_data + 8, data, length); + + + if(DDE_Class->Poke_Server( (LPBYTE) poke_data, ntohl(*poke_data_int) ) == FALSE) { + WWDebugString("RA95 - POKE failed!\n"); + DDE_Class->Close_Poke_Connection(); // close down the link + delete poke_data; + return (FALSE); + } + + DDE_Class->Close_Poke_Connection(); // close down the link + + delete poke_data; + + return (TRUE); +} + + +#endif //WIN32 diff --git a/code/redalert/ccdde.h b/code/redalert/ccdde.h new file mode 100644 index 0000000..fc8dd5f --- /dev/null +++ b/code/redalert/ccdde.h @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : CCDDE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * C&C's interface to the DDE class * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "dde.h" + +class DDEServerClass { + + public: + + DDEServerClass (void); + ~DDEServerClass (void); + + + char *Get_MPlayer_Game_Info (void); //Returns pointer to game info + int Get_MPlayer_Game_Info_Length(){return(MPlayerGameInfoLength);}; //Len of game info + BOOL Callback(unsigned char *data, long length); //DDE callback function + void Delete_MPlayer_Game_Info(void); //release the game info memory + void Enable(void); //Enable the DDE callback + void Disable(void); //Disable the DDE callback + int Time_Since_Heartbeat(void); //Returns the time since the last hearbeat from WChat + + /* + ** Enumeration for DDE packet types from WChat + */ + enum { + DDE_PACKET_START_MPLAYER_GAME, //Start game packet. This includes game options + DDE_PACKET_GAME_RESULTS, //Game results packet. The game statistics. + DDE_PACKET_HEART_BEAT, //Heart beat packet so we know WChat is still there. + DDE_TICKLE, //Message to prompt other app to take focus. + DDE_CONNECTION_FAILED + }; + + + private: + + char *MPlayerGameInfo; //Pointer to game start packet + int MPlayerGameInfoLength; //Length of game start packet. + BOOL IsEnabled; //Flag for DDE callback enable + int LastHeartbeat; // Time since last heartbeat packet was received from WChat + +}; + + +extern DDEServerClass DDEServer; +extern BOOL Send_Data_To_DDE_Server (char *data, int length, int packet_type); + + +#endif //WIN32 diff --git a/code/redalert/ccfile.cpp b/code/redalert/ccfile.cpp new file mode 100644 index 0000000..3743b4a --- /dev/null +++ b/code/redalert/ccfile.cpp @@ -0,0 +1,692 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCFILE.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCFileClass::CCFileClass -- Default constructor for file object. * + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * CCFileClass::Close -- Closes the file. * + * CCFileClass::Error -- Handles displaying a file error message. * + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * CCFileClass::Is_Open -- Determines if the file is open. * + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * CCFileClass::Read -- Reads data from the file. * + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * CCFileClass::Size -- Determines the size of the file. * + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +#include "ccfile.h" + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Filename based constructor for C&C file. * + * * + * Use this constructor for a file when the filename is known at construction time. * + * * + * INPUT: filename -- Pointer to the filename to use for this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: The filename pointer is presumed to be inviolate throughout the duration of * + * the file object. If this is not guaranteed, then use the default constructor * + * and then set the name manually. * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(char const * filename) : + Position(0) +{ + CCFileClass::Set_Name(filename); +} + + +/*********************************************************************************************** + * CCFileClass::CCFileClass -- Default constructor for file object. * + * * + * This is the default constructor for a C&C file object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +CCFileClass::CCFileClass(void) : + Position(0) +{ +} + + +/*********************************************************************************************** + * CCFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Error(int , int , char const * ) +{ + if (!Force_CD_Available(RequiredCD)) { + Prog_End("CCFileClass::Error CD not found", true); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } +} + + +/*********************************************************************************************** + * CCFileClass::Write -- Writes data to the file (non mixfile files only). * + * * + * This routine will write data to the file, but NOT to a file that is part of a mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the data to be written. * + * * + * size -- The number of bytes to write. * + * * + * OUTPUT: Returns the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Write(void const * buffer, long size) +{ + /* + ** If this is part of a mixfile, then writing is not allowed. Error out with a fatal + ** message. + */ + if (Is_Resident()) { + Error(EACCES, false, File_Name()); + } + + return(CDFileClass::Write(buffer, size)); +} + + +/*********************************************************************************************** + * CCFileClass::Read -- Reads data from the file. * + * * + * This routine determines if the file is part of the mixfile system. If it is, then * + * the file is copied from RAM if it is located there. Otherwise it is read from disk * + * according to the correct position of the file within the parent mixfile. * + * * + * INPUT: buffer -- Pointer to the buffer to place the read data. * + * * + * size -- The number of bytes to read. * + * * + * OUTPUT: Returns the actual number of bytes read (this could be less than requested). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Read(void * buffer, long size) +{ + bool opened = false; + + /* + ** If the file isn't currently open, then open it. + */ + if (!Is_Open()) { + if (Open()) { + opened = true; + } + } + + /* + ** If the file is part of a loaded mixfile, then a mere copy is + ** all that is required for the read. + */ + if (Is_Resident()) { + long maximum = Data.Get_Size() - Position; + + size = maximum < size ? maximum : size; +// size = MIN(maximum, size); + if (size) { + memmove(buffer, (char *)Data + Position, size); +// Mem_Copy((char *)Pointer + Position, buffer, size); + Position += size; + } + if (opened) Close(); + return(size); + } + + long s = CDFileClass::Read(buffer, size); + + /* + ** If the file was opened by this routine, then close it at this time. + */ + if (opened) Close(); + + /* + ** Return with the number of bytes read. + */ + return(s); +} + + +/*********************************************************************************************** + * CCFileClass::Seek -- Moves the current file pointer in the file. * + * * + * This routine will change the current file pointer to the position specified. It follows * + * the same rules the a normal Seek() does, but if the file is part of the mixfile system, * + * then only the position value needs to be updated. * + * * + * INPUT: pos -- The position to move the file to relative to the position indicated * + * by the "dir" parameter. * + * * + * dir -- The direction to affect the position change against. This can be * + * either SEEK_CUR, SEEK_END, or SEEK_SET. * + * * + * OUTPUT: Returns with the position of the new location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +long CCFileClass::Seek(long pos, int dir) +{ + /* + ** When the file is resident, a mere adjustment of the virtual file position is + ** all that is required of a seek. + */ + if (Is_Resident()) { + switch (dir) { + case SEEK_END: + Position = Data.Get_Size(); + break; + + case SEEK_SET: + Position = 0; + break; + + case SEEK_CUR: + default: + break; + } + Position += pos; + Position = Position < 0 ? 0 : Position; + Position = Position > Data.Get_Size() ? Data.Get_Size() : Position; +// Position = Bound(Position+pos, 0L, Length); + return(Position); + } + return(CDFileClass::Seek(pos, dir)); +} + + +/*********************************************************************************************** + * CCFileClass::Size -- Determines the size of the file. * + * * + * If the file is part of the mixfile system, then the size of the file is already * + * determined and available. Otherwise, go to the low level system to find the file * + * size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the file in bytes. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 08/05/1996 JLB : Handles returning size of embedded file. * + *=============================================================================================*/ +long CCFileClass::Size(void) +{ + /* + ** If the file is resident, the the size is already known. Just return the size in this + ** case. + */ + if (Is_Resident()) return(Data.Get_Size()); + + /* + ** If the file is not available as a stand alone file, then search for it in the + ** mixfiles in order to get its size. + */ + if (!CDFileClass::Is_Available()) { + long length = 0; + MFCD::Offset(File_Name(), NULL, NULL, NULL, &length); + return(length); + } + + return(CDFileClass::Size()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Available -- Checks for existence of file on disk or in mixfile. * + * * + * This routine will examine the mixfile system looking for the file. If the file could * + * not be found there, then the disk is examined directly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file available for opening? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Available(int ) +{ + /* + ** A file that is open is presumed available. + */ + if (Is_Open()) return(true); + + /* + ** A file that is part of a mixfile is also presumed available. + */ + if (MFCD::Offset(File_Name())) { + return(true); + } + + /* + ** Otherwise a manual check of the file system is required to + ** determine if the file is actually available. + */ + return(CDFileClass::Is_Available()); +} + + +/*********************************************************************************************** + * CCFileClass::Is_Open -- Determines if the file is open. * + * * + * A mixfile is open if there is a pointer to the mixfile data. In absence of this, * + * the the file is open if the file handle is valid. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Is_Open(void) const +{ + /* + ** If the file is part of a cached file, then return that it is opened. A closed file + ** doesn't have a valid pointer. + */ + if (Is_Resident()) return(true); + + /* + ** Otherwise, go to a lower level to determine if the file is open. + */ + return(CDFileClass::Is_Open()); +} + + +/*********************************************************************************************** + * CCFileClass::Close -- Closes the file. * + * * + * If this is a mixfile file, then only the pointers need to be adjusted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +void CCFileClass::Close(void) +{ + new(&Data) ::Buffer; + Position = 0; // Starts at beginning offset. + CDFileClass::Close(); +} + + +/*********************************************************************************************** + * CCFileClass::Open -- Opens a file from either the mixfile system or the rawfile system. * + * * + * This routine will open the specified file. It examines the mixfile system to find a * + * match. If one is found then the file is "opened" in a special cached way. Otherwise * + * it is opened as a standard DOS file. * + * * + * INPUT: rights -- The access rights desired. * + * * + * OUTPUT: bool; Was the file opened successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +int CCFileClass::Open(int rights) +{ + /* + ** Always close the file if it was open. + */ + Close(); + + /* + ** Perform a preliminary check to see if the specified file + ** exists on the disk. If it does, then open this file regardless + ** of whether it also exists in RAM. This is slower, but allows + ** upgrade files to work. + */ + if ((rights & WRITE) || CDFileClass::Is_Available()) { + return(CDFileClass::Open(rights)); + } + + /* + ** Check to see if file is part of a mixfile and that mixfile is currently loaded + ** into RAM. + */ + MFCD * mixfile = NULL; + void * pointer = NULL; + long length = 0; + long start = 0; + if (MFCD::Offset(File_Name(), &pointer, &mixfile, &start, &length)) { + + assert(mixfile != NULL); + + /* + ** If the mixfile is located on disk, then fake out the file system to read from + ** the mixfile, but think it is reading from a solitary file. + */ + if (pointer == NULL && mixfile != NULL) { + + /* + ** This is a legitimate open to the file. All access to the file through this + ** file object will be appropriately adjusted for mixfile support however. Also + ** note that the filename attached to this object is NOT the same as the file + ** attached to the file handle. + */ + char * dupfile = strdup(File_Name()); + Open(mixfile->Filename, READ); + Searching(false); // Disable multi-drive search. + Set_Name(dupfile); + Searching(true); + free(dupfile); + Bias(0); + Bias(start, length); + Seek(0, SEEK_SET); + } else { + new (&Data) ::Buffer(pointer, length); + Position = 0; + } + + } else { + + /* + ** The file cannot be found in any mixfile, so it must reside as + ** an individual file on the disk. Or else it is just plain missing. + */ + return(CDFileClass::Open(rights)); + } + return(true); +} + + +/*********************************************************************************************** + * CCFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +unsigned long CCFileClass::Get_Date_Time(void) +{ + unsigned long datetime; + MFCD * mixfile; + + datetime = CDFileClass::Get_Date_Time(); + + if ( !datetime ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Get_Date_Time() ); + } + // else return 0 indicating no file + } + + return( datetime ); +} + + +/*********************************************************************************************** + * CCFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + *=============================================================================================*/ +bool CCFileClass::Set_Date_Time( unsigned long datetime ) +{ + bool status; + MFCD * mixfile; + + status = CDFileClass::Set_Date_Time( datetime ); + + if ( !status ) { + if (MFCD::Offset(File_Name(), NULL, &mixfile, NULL, NULL)) { + // + // check for nested MIX files + // + return( CCFileClass(mixfile->Filename).Set_Date_Time( datetime ) ); + } + // else return 0 indicating no file + } + + return( status ); +} + + +/*********************************************************************************** +** Backward compatibility section. +*/ +//extern "C" { + + +static CCFileClass Handles[10]; + +int __cdecl Open_File(char const * file_name, int mode) +{ + for (int index = 0; index < ARRAY_SIZE(Handles); index++) { + if (!Handles[index].Is_Open()) { + if (Handles[index].Open(file_name, mode)) { + return(index); + } + break; + } + } + return(WWERROR); +} + +void __cdecl Close_File(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + Handles[handle].Close(); + } +} + +long __cdecl Read_File(int handle, void * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Read(buf, bytes)); + } + return(0); +} + +long __cdecl Write_File(int handle, void const * buf, unsigned long bytes) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Write(buf, bytes)); + } + return(0); +} + +int __cdecl Find_File(char const * file_name) +{ + CCFileClass file(file_name); + return(file.Is_Available()); +} + +#ifdef NEVER +int __cdecl Delete_File(char const * file_name) +{ + return(CCFileClass(file_name).Delete()); +} + +int __cdecl Create_File(char const * file_name) +{ + return(CCFileClass(file_name).Create()); +} + +unsigned long __cdecl Load_Data(char const * name, void * ptr, unsigned long size) +{ + return(CCFileClass(name).Read(ptr, size)); +} +#endif + +void * __cdecl Load_Alloc_Data(char const * name, int ) +{ + CCFileClass file(name); + + return(Load_Alloc_Data(file)); +} + +unsigned long __cdecl File_Size(int handle) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Size()); + } + return(0); +} + +#ifdef NEVER +unsigned long __cdecl Write_Data(char const * name, void const * ptr, unsigned long size) +{ + return(CCFileClass(name).Write(ptr, size)); +} +#endif + +unsigned long __cdecl Seek_File(int handle, long offset, int starting) +{ + if (handle != WWERROR && Handles[handle].Is_Open()) { + return(Handles[handle].Seek(offset, starting)); + } + return(0); +} + +#ifdef NEVER +bool __cdecl Multi_Drive_Search(bool on) +{ +// return(CCFileClass::Multi_Drive_Search(on)); + return(on); +} + +void __cdecl WWDOS_Init(void) +{ +} + +void __cdecl WWDOS_Shutdown(void) +{ +} + +int __cdecl Find_Disk_Number(char const *) +{ + return(0); +} +#endif + +//unsigned long __cdecl Load_Uncompress(char const * file, BuffType uncomp_buff, BuffType dest_buff, void * reserved_data) +//{ +// return(Load_Uncompress(CCFileClass(file), uncomp_buff, dest_buff, reserved_data)); +// return(CCFileClass(file).Load_Uncompress(uncomp_buff, dest_buff, reserved_data)); +//} + +#ifdef WIN32 +extern "C" { +int MaxDevice; +int DefaultDrive; +char CallingDOSInt; + +} +#endif + + +void Unfragment_File_Cache(void) +{ +} + + diff --git a/code/redalert/ccfile.h b/code/redalert/ccfile.h new file mode 100644 index 0000000..4e94787 --- /dev/null +++ b/code/redalert/ccfile.h @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 17, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCFILE_H +#define CCFILE_H + +//#include +#include +#include "mixfile.h" +#include "cdfile.h" +#include "buff.h" + + +/* +** This derived class for file access knows about mixfiles (packed files). It can handle opening +** a file that is embedded within a mixfile. This is true if the mixfile is cached or resides on +** disk. It is functionally similar to pakfiles, except much faster and less RAM intensive. +*/ +class CCFileClass : public CDFileClass +{ + public: + CCFileClass(char const * filename); + CCFileClass(void); + virtual ~CCFileClass(void) {Position = 0;}; + + // Delete should be overloaded here as well. Don't allow deletes of mixfiles. + + bool Is_Resident(void) const {return(Data.Get_Buffer() != NULL);} + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int rights=READ) {Set_Name(filename);return Open(rights);}; + virtual int Open(int rights=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + private: + + /* + ** This indicates the file is actually part of a resident image of the mixfile + ** itself. In this case, the embedded file handle is invalid. All file access actually + ** gets routed through the cached version of the file. This is a pointer to the start + ** of the RAM image of the file. + */ + ::Buffer Data; +// void * Pointer; + + /* + ** This is the size of the file if it was embedded in a mixfile. The size must be manually + ** kept track of because the DOS file size is invalid. + */ +// long Length; + + /* + ** This is the current seek position of the file. It is duplicated here if the file is + ** part of a mixfile since the DOS seek position is not accurate. This value will + ** range from zero to the size of the file in bytes. + */ + long Position; + + // Force these to never be invoked. + CCFileClass const & operator = (CCFileClass const & c); + CCFileClass (CCFileClass const & ); +}; + +template <> class MixFileClass; + +#endif diff --git a/code/redalert/ccini.cpp b/code/redalert/ccini.cpp new file mode 100644 index 0000000..142b72b --- /dev/null +++ b/code/redalert/ccini.cpp @@ -0,0 +1,1484 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCINI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCINI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current databas* + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * CCINIClass::Load -- Load the INI database from the file specified. * + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * CCINIClass::Save -- Save the INI data to the file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the file specified. * + * * + * This routine will load the database from the file specified in much the same manner * + * that the INIClass load function works. However, this class will examine the message * + * digest (if present) and compare it to the actual digest. If they differ, a special * + * return value is used. This will allow verification of the integrity of the ini data. * + * * + * INPUT: file -- Reference to the file that will be read from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: If the file was not read, returns 0. If the file was read ok, returns 1. If the * + * file was read ok, but the digest doesn't verify, returns 2. * + * * + * WARNINGS: If no message digest was present in the INI file, then no verification can * + * be performed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles digest control. * + *=============================================================================================*/ +bool CCINIClass::Load(FileClass & file, bool withdigest) +{ + return(Load(FileStraw(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Load -- Load the INI database from the data stream specified. * + * * + * This will load the INI database and in the process, it will fetch and verify any * + * message digest present. * + * * + * INPUT: straw -- The data stream to fetch the INI data from. * + * * + * withdigest -- Should a message digest be examined when loaded. If there is a * + * mismatch detected, then an error will be returned. * + * * + * OUTPUT: bool; Was the database loaded ok? (hack: returns "2" if digest doesn't match). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Load(Straw & file, bool withdigest) +{ + bool ok = INIClass::Load(file); + + Invalidate_Message_Digest(); + if (ok && withdigest) { + + /* + ** If a digest is present, fetch it. + */ + unsigned char digest[20]; + int len = Get_UUBlock("Digest", digest, sizeof(digest)); + if (len > 0) { + Clear("Digest"); + + /* + ** Calculate the message digest for the INI data that was read. + */ + Calculate_Message_Digest(); + + /* + ** If the message digests don't match, then return with the special error code. + */ + if (memcmp(digest, Digest, sizeof(digest)) != 0) { + return(2); + } + } + } + return(ok); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Save the INI data to the file specified. * + * * + * This routine will save the INI data to the file. It will add a message digest so that * + * validity check can be performed when the INI data is subsequently read. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: bool; Was the INI data saved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(FileClass & file, bool withdigest) const +{ + return(Save(FilePipe(file), withdigest)); +} + + +/*********************************************************************************************** + * CCINIClass::Save -- Pipes the INI database to the pipe specified. * + * * + * This routine will pipe the INI data to the pipe segment specified. It is functionally * + * the same as the save operation. A message digest is added to the output data so that * + * validity check can occur during a subsequent read. * + * * + * INPUT: straw -- Reference to the pipe that will receive the output ini data stream. * + * * + * withdigest -- Should a message digest be generated and saved with the INI * + * data file? * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 08/21/1996 JLB : Handles message digest control. * + *=============================================================================================*/ +int CCINIClass::Save(Pipe & pipe, bool withdigest) const +{ + if (!withdigest) { + return(INIClass::Save(pipe)); + } + + /* + ** Just in case these entries are present, clear them out. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Calculate what the new digest should be. + */ + ((CCINIClass *)this)->Calculate_Message_Digest(); + + /* + ** Store the actual digest into the INI database. + */ + ((CCINIClass *)this)->Put_UUBlock("Digest", Digest, sizeof(Digest)); + + /* + ** Output the database to the pipe specified. + */ + int length = INIClass::Save(pipe); + + /* + ** Remove the digest from the database. It shouldn't stick around as if it were real data + ** since it isn't really part of the INI database proper. + */ + ((CCINIClass *)this)->Clear("Digest"); + + /* + ** Finally, return with the total number of bytes send out the pipe. + */ + return(length); +} + + +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Lepton -- Fetches a lepton value from the INI database. * + * * + * This routine will fetch the lepton value as if it were expressed as cells. Example; * + * a value of 1 would mean 256 in leptons. * + * * + * INPUT: section -- The section identifier to look under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default value to use if the specified section and entry could * + * not be located. * + * * + * OUTPUT: Returns with the lepton value of the section & entry specified. If not found, then * + * the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +LEPTON CCINIClass::Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const +{ + fixed result = Get_Fixed(section, entry, fixed(defvalue, CELL_LEPTON_W)); + return(result * CELL_LEPTON_W); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Lepton -- Stores a lepton value to the INI database. * + * * + * This routine will store the lepton value as if it were expressed in cells. Example; * + * A lepton of 128 will be stored as ".5". * + * * + * INPUT: section -- The section identifier to store the value under. * + * * + * entry -- The entry to store the lepton value at. * + * * + * value -- The lepton value to store. * + * * + * OUTPUT: bool; Was the lepton value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Lepton(char const * section, char const * entry, LEPTON value) +{ + return(Put_Fixed(section, entry, fixed(value, CELL_LEPTON_W))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_MPHType -- Fetches the speed value as a number from 0 to 100. * + * * + * This routine will fetch the speed value as if it were expressed as a number from 0 * + * to 100. The value of 100 would translate into a speed of 256 leptons per game frame. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to find. * + * * + * defvalue -- The default speed value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the speed value. If no entry could be found, then the default value * + * will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +MPHType CCINIClass::Get_MPHType(char const * section, char const * entry, MPHType defvalue) const +{ + int val = Get_Int(section, entry, ((int)defvalue * 100) / 256); + return (MPHType(_Scale_To_256(val))); +} + + +/*********************************************************************************************** + * CCINIClass::Put_MPHType -- Stores the speed value to the section & entry specified. * + * * + * Use this routine to store a speed value into the INI database. The number stored will * + * be in a 0..100 format. A speed of 256 leptons per tick would be stored as 100. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the speed value to. * + * * + * value -- The speed value to store. * + * * + * OUTPUT: bool; Was the speed value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_MPHType(char const * section, char const * entry, MPHType value) +{ + return(Put_Int(section, entry, ((int)value * 100) / 256)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Owners -- Fetch the owners (list of house bits). * + * * + * Use this to fetch a house bit array value from the INI database. This value will be * + * various bit positions set (1 << house#) for each house specified in the database. * + * Houses can be specified in series by the house name separated by commas or by the * + * special group names of "soviet", and "allies" to cover the houses that are members of * + * these groups. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default house bitfield to use if the entry could not be found. * + * * + * OUTPUT: Returns with the house bitfield value. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Owners(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long ownable = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + ownable = 0; + char * name = strtok(buffer, ","); + + while (name) { + ownable |= Owner_From_Name(name); + name = strtok(NULL, ","); + } + } + return(ownable); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Owners -- Store the house bitfield to the INI database. * + * * + * Use this routine to store the house bitfield data into the database. The bitfield format * + * matches the format used by the Get_Owners function. Example; if both England and * + * Spain were specified in the bitfield, the entry would be stored as "England,Spain". * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier that is assigned the value. * + * * + * value -- The value to assign to the entry. * + * * + * OUTPUT: bool; Was the entry stored in the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Owners(char const * section, char const * entry, long value) +{ + char buffer[128]; + + buffer[0] = '\0'; + + if ((value & HOUSEF_ALLIES) == HOUSEF_ALLIES) { + strcat(buffer, "allies"); + value &= ~HOUSEF_ALLIES; + } + if ((value & HOUSEF_SOVIET) == HOUSEF_SOVIET) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, "soviet"); + value &= ~HOUSEF_SOVIET; + } + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if ((value & (1 << house)) != 0) { + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, HouseTypeClass::As_Reference(house).Name()); + } + } + + if (buffer[0] != '\0') { + return(Put_String(section, entry, buffer)); + } + return(true); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ArmorType -- Fetches the armor type from the INI database. * + * * + * This routine will fetch the armor type from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Th identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the armor type specified in the INI database. If it could not be * + * found, then the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType CCINIClass::Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, ArmorName[defvalue], buffer, sizeof(buffer)); + return(Armor_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ArmorType -- Store the armor type to the INI database. * + * * + * Use this routine to store the specified armor type to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the value at. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the entry stored in the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ArmorType(char const * section, char const * entry, ArmorType value) +{ + return(Put_String(section, entry, ArmorName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VocType -- Fetch a voc (sound effect) from the INI database. * + * * + * This routine will fetch a voc number from the database. The voc number will either * + * be a valid sound effect or VOC_NONE if no match could be found. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the sound effect (VocType) from the INI database. If the entry could * + * not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VocType CCINIClass::Get_VocType(char const * section, char const * entry, VocType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Voc_Name(defvalue), buffer, sizeof(buffer)); + return(Voc_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VocType -- Store a sound effect identifier into the INI database. * + * * + * Use this routine to store a voc identifier (stored a the text name of the sound) into * + * the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- The entry to assign the value to. * + * * + * value -- The sound effect to store to the entry. * + * * + * OUTPUT: bool; Was the sound effect entry stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VocType(char const * section, char const * entry, VocType value) +{ + if (value == VOC_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Voc_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_AnimType -- Fetch an animation type number from the INI database. * + * * + * This will fetch an AnimType number from the INI database. The anim is stored as a text * + * name of the art file used for that anim. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default AnimType to use if the entry could not be located. * + * * + * OUTPUT: Returns with the anim type specified in the database. If it could not be found, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +AnimType CCINIClass::Get_AnimType(char const * section, char const * entry, AnimType defvalue) const +{ + char buffer[128]; + + Get_String(section, entry, Anim_Name(defvalue), buffer, sizeof(buffer)); + return(Anim_From_Name(buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Put_AnimType -- Stores the animation identifier to the INI database. * + * * + * This routine will store the animation identifier (stored as the text name of the art * + * file it uses) to the INI database. * + * * + * INPUT: section -- The section identifier to place the entry under. * + * * + * entry -- The entry identifier to assign the animation number to. * + * * + * value -- The animation identifier to store with the entry. * + * * + * OUTPUT: bool; Was the animation identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_AnimType(char const * section, char const * entry, AnimType value) +{ + if (value == ANIM_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, Anim_Name(value))); +} + + +UnitType CCINIClass::Get_UnitType(char const * section, char const * entry, UnitType defvalue) const +{ + char buffer[128]; + + char const * def = ""; + if (defvalue != UNIT_NONE) { + def = UnitTypeClass::As_Reference(defvalue).Name(); + } + Get_String(section, entry, def, buffer, sizeof(buffer)); + return(UnitTypeClass::From_Name(buffer)); +} + + +bool CCINIClass::Put_UnitType(char const * section, char const * entry, UnitType value) +{ + if (value == UNIT_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, UnitTypeClass::As_Reference(value).Name())); +} + + + +/*********************************************************************************************** + * CCINIClass::Get_WeaponType -- Fetches the weapon type from the INI database. * + * * + * This routine will fetch the weapon type from the INI database. The weapon type is * + * stored as a custom identifier string. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default weapon value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the weapon type specified by the entry. If the entry could not be * + * found then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType CCINIClass::Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Weapon_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WeaponType -- Store the weapon identifier to the INI database. * + * * + * Store the weapon identifier (as custom string name) to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier to store the weapon identifier with. * + * * + * value -- The weapon identifier to store. * + * * + * OUTPUT: bool; Was the weapon identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WeaponType(char const * section, char const * entry, WeaponType value) +{ + if (value == WEAPON_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WeaponTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_WarheadType -- Fetch the warhead type from the INI database. * + * * + * Will fetch the warhead identifier from the INI database. * + * * + * INPUT: section -- The identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default return value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the found warhead type. If the entry could not be found then the * + * default warhead value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +WarheadType CCINIClass::Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (WarheadType wh = WARHEAD_FIRST; wh < WARHEAD_COUNT; wh++) { + if (stricmp(WarheadTypeClass::As_Pointer(wh)->Name(), buffer) == 0) { + return(wh); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_WarheadType -- Stores the warhead identifier to the INI database. * + * * + * This will store the weapon identifier specified to the INI database. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry to store the warhead identifier. * + * * + * value -- The warhead identifier to store. * + * * + * OUTPUT: bool; Was the warhead identifier stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_WarheadType(char const * section, char const * entry, WarheadType value) +{ + if (value == WARHEAD_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, WarheadTypeClass::As_Pointer(value)->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_OverlayType -- Fetch the overlay identifier from the INI database. * + * * + * This routine will fetch the overlay identifier from the database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the overlay identifier found. If it could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +OverlayType CCINIClass::Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(OverlayTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_OverlayType -- Store the overlay identifier into the INI database. * + * * + * Use this routine to store the overlay identifier into the INI database. * + * * + * INPUT: section -- Identifier for to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * value -- The overlay type value to store with the entry. * + * * + * OUTPUT: bool; Was the overlay value stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_OverlayType(char const * section, char const * entry, OverlayType value) +{ + assert(value != OVERLAY_NONE); + return(Put_String(section, entry, OverlayTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_BulletType -- Fetch the bullet identifier from the INI database. * + * * + * Use this routine to fetch the bullet type identifier from the INI database. * + * * + * INPUT: section -- The section identifier to search for the entry under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default bullet type value to return if the entry could not be * + * located. * + * * + * OUTPUT: Returns with the bullet type identifier found. If the entry could not be found * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +BulletType CCINIClass::Get_BulletType(char const * section, char const * entry, BulletType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (BulletType proj = BULLET_FIRST; proj < BULLET_COUNT; proj++) { + if (stricmp(BulletTypeClass::As_Reference(proj).Name(), buffer) == 0) { +// if (stricmp(ProjectileNames[proj], buffer) == 0) { + return(proj); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_BulletType -- Store the projectile identifier into the INI database. * + * * + * This routine will store the projectile name (as the identifier) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry identifier to store the projectile value with. * + * * + * value -- The projectile identifier to store. * + * * + * OUTPUT: bool; Was the projectile identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_BulletType(char const * section, char const * entry, BulletType value) +{ + if (value == BULLET_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, BulletTypeClass::As_Reference(value).Name())); +// return(Put_String(section, entry, ProjectileNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_HousesType -- Fetch a house identifier from the INI database. * + * * + * Use this routine to fetch an individual house identifier from the INI database. This is * + * somewhat similar to the Get_Owners function but is limited to a single house. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the house identifier if it was found. If not found, then the default * + * value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +HousesType CCINIClass::Get_HousesType(char const * section, char const * entry, HousesType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(HouseTypeClass::From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_HousesType -- Store a house identifier to the INI database. * + * * + * Use this routine to store the specified house identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * value -- The house identifier to store in the database. * + * * + * OUTPUT: bool; Was the house identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_HousesType(char const * section, char const * entry, HousesType value) +{ + return(Put_String(section, entry, HouseTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_VQType -- Fetch the VQ movie identifier from the INI database. * + * * + * Fetches the VQ movie name (identifier) from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the VQ movie identifier found. If the entry could not be located, * + * then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +VQType CCINIClass::Get_VQType(char const * section, char const * entry, VQType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + for (VQType vq = VQ_FIRST; vq < VQ_COUNT; vq++) { + if (stricmp(buffer, VQName[vq]) == 0) { + return(vq); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_VQType -- Store the VQ movie identifier into the INI database. * + * * + * Use this routine to store the VQ movie identifier into the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The VQ movie identifier to store to the INI database. * + * * + * OUTPUT: bool; Was the VQ identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_VQType(char const * section, char const * entry, VQType value) +{ + if (value == VQ_NONE) { + return(Put_String(section, entry, "")); + } + return(Put_String(section, entry, VQName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TheaterType -- Fetch the theater type from the INI database. * + * * + * This will fetch the theater identifier from the INI database. * + * * + * INPUT: section -- Identifier for the section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the theater type found. If the entry could not be found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TheaterType CCINIClass::Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theater_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TheaterType -- Store the theater identifier to the INI database. * + * * + * Use this routine to store the theater name to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier for the entry to store. * + * * + * value -- The theater identifier to store. * + * * + * OUTPUT: bool; Was the theater identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TheaterType(char const * section, char const * entry, TheaterType value) +{ + return(Put_String(section, entry, Theaters[value].Name)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TriggerType -- Fetch the trigger type identifier from the INI database. * + * * + * This routine will fetch the trigger type identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * OUTPUT: Returns with the trigger type pointer if a match was found. No match found will * + * return a NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * CCINIClass::Get_TriggerType(char const * section, char const * entry) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TriggerTypeClass::From_Name(buffer)); + } + return(NULL); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TriggerType -- Store the trigger identifier to the INI database. * + * * + * This routine will store the trigger (as its name) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- The entry name to store the trigger identifier to. * + * * + * value -- The trigger type to store. The trigger name will be stored. * + * * + * OUTPUT: bool; Was the trigger name stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value) +{ + return(Put_String(section, entry, value->Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_ThemeType -- Fetch the theme identifier. * + * * + * This routine will fetch the theme identifier from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier of the entry to search for. * + * * + * defvalue -- The default theme identifier to return if the entry could not be found.* + * * + * OUTPUT: Returns with the theme identifier if it was found. If not found, then the default * + * value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ThemeType CCINIClass::Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Theme.From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_ThemeType -- Store the theme identifier to the INI database. * + * * + * This routine will store the specified theme identifier to the INI database. * + * * + * INPUT: section -- Identifier for the section to store the entry under. * + * * + * entry -- Identifier for the entry to store the value to. * + * * + * value -- The theme identifier to store. * + * * + * OUTPUT: bool; Was the theme identifier stored. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_ThemeType(char const * section, char const * entry, ThemeType value) +{ + return(Put_String(section, entry, Theme.Base_Name(value))); +} + + +/*********************************************************************************************** + * CCINIClass::Get_SourceType -- Fetch the source (edge) type from the INI database. * + * * + * This routine will fetch the source (reinforcement edge) identifier from the INI * + * database. * + * * + * INPUT: section -- Identifier for the section that the entry will be searched under. * + * * + * entry -- Identifier for the entry that will be searched for. * + * * + * defvalue -- The default value to return if the entry could not be located. * + * * + * OUTPUT: Returns with the source type of the entry if found. If not found, then the * + * default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +SourceType CCINIClass::Get_SourceType(char const * section, char const * entry, SourceType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Source_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_SourceType -- Store the source (edge) identifier to the INI database. * + * * + * This will store the source type (reinforcement edge) to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier of the entry to store the source identifier to. * + * * + * value -- The source (edge) value to store. * + * * + * OUTPUT: bool; Was the source identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_SourceType(char const * section, char const * entry, SourceType value) +{ + return(Put_String(section, entry, SourceName[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_CrateType -- Fetches a crate type value from the INI database. * + * * + * This will return with the crate type specified in the INI database. * + * * + * INPUT: section -- Identifier for the section to search under. * + * * + * entry -- The entry to find the matching crate value for. * + * * + * defvalue -- The default crate value to return if the entry could not be found. * + * * + * OUTPUT: Returns with the crate type identified with the specified entry. If the entry * + * could not be located, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +CrateType CCINIClass::Get_CrateType(char const * section, char const * entry, CrateType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(Crate_From_Name(buffer)); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_CrateType -- Stores the crate value in the section and entry specified. * + * * + * This will store the specified crate value to the section and entry specified. * + * * + * INPUT: section -- The section identifier to store the entry under. * + * * + * entry -- The entry identifier to store the crate value with. * + * * + * value -- The crate value to store. * + * * + * OUTPUT: bool; Was the crate value stored to the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_CrateType(char const * section, char const * entry, CrateType value) +{ + return(Put_String(section, entry, CrateNames[value])); +} + + +/*********************************************************************************************** + * CCINIClass::Get_TerrainType -- Fetch the terrain type identifier from the INI database. * + * * + * Fetches the terrain type number from the INI database. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- Identifier for the entry to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the terrain type if found. If the entry wasn't found, then the * + * default value will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TerrainType CCINIClass::Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const +{ + char buffer[128]; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + return(TerrainTypeClass::From_Name(strtok(buffer, ","))); + } + return(defvalue); +} + + +/*********************************************************************************************** + * CCINIClass::Put_TerrainType -- Store the terrain type number to the INI database. * + * * + * This will store the terrain type identifier to the INI database. * + * * + * INPUT: section -- The section to store the entry under. * + * * + * entry -- Identifier that the terrain number will be stored to. * + * * + * value -- The terrain type identifier to store. * + * * + * OUTPUT: bool; Was the terrain identifier stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_TerrainType(char const * section, char const * entry, TerrainType value) +{ + return(Put_String(section, entry, TerrainTypeClass::As_Reference(value).Name())); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Buildings -- Fetch a building bitfield from the INI database. * + * * + * This routing will fetch the a list of buildings from the INI database. The buildings * + * are expressed as a comma separated list of building identifiers. The return value is * + * a composite of bits that represent these buildings -- one bit per building type. * + * * + * INPUT: section -- The section to search for the entry under. * + * * + * entry -- The entry to fetch the building list from. * + * * + * defvalue -- The default value to return if the section and entry could not be * + * located. * + * * + * OUTPUT: Returns with the building list (as a bitfield). If the entry could not be * + * found, the the default value is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +long CCINIClass::Get_Buildings(char const * section, char const * entry, long defvalue) const +{ + char buffer[128]; + long pre; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + + pre = 0; + char * token = strtok(buffer, ","); + while (token != NULL && *token != '\0') { + StructType building = BuildingTypeClass::From_Name(token); + if (building != STRUCT_NONE) { + pre |= (1L << building); + } + token = strtok(NULL, ","); + } + } else { + pre = defvalue; + } + + return(pre); +} + + +/*********************************************************************************************** + * CCINIClass::Put_Buildings -- Store a building list to the INI database. * + * * + * This will store a list of buildings to the INI database. The buildings are listed by * + * their identifier names separated by commas. * + * * + * INPUT: section -- The identifier for the section to store the entry under. * + * * + * entry -- The entry to store the building list to. * + * * + * value -- A list of buildings (in the form of a bit field -- one bit per * + * building type). * + * * + * OUTPUT: Was the building list stored to the INI file? * + * * + * WARNINGS: This is limited to the buildings that can be expressed in a bitfield long. * + * Which means, there can be only a maximum of 32 building types listed and * + * even then, the total line length generated must not exceed 128 bytes. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +bool CCINIClass::Put_Buildings(char const * section, char const * entry, long value) +{ + char buffer[128] = ""; + int maxi = (32 < STRUCT_COUNT) ? 32 : STRUCT_COUNT; + + for (StructType index = STRUCT_FIRST; index < maxi; index++) { + if ((value & (1L << index)) != 0) { + + if (buffer[0] != '\0') { + strcat(buffer, ","); + } + strcat(buffer, BuildingTypeClass::As_Reference(index).IniName); + } + } + + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * CCINIClass::Get_Unique_ID -- Fetch a unique identifier number for the INI file. * + * * + * This is a shorthand version of the message digest. It calculates the ID number from the * + * message digest itself. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a 32 bit unique identifier number for the INI database. * + * * + * WARNINGS: Since the return value is only 32 bits, it is much less secure than the * + * complete message digest. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +int CCINIClass::Get_Unique_ID(void) const +{ + if (!IsDigestPresent) { + ((CCINIClass *)this)->Calculate_Message_Digest(); + } + + return(CRCEngine()(&Digest[0], sizeof(Digest))); +} + + +/*********************************************************************************************** + * CCINIClass::Calculate_Message_Digest -- Calculate a message digest for the current database * + * * + * This will calculate a new message digest according to the current state of the INI * + * database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: If the database is changed in any fashion, this message digest will be rendered * + * obsolete. * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Calculate_Message_Digest(void) +{ + /* + ** Calculate the message digest for the INI data that was read. + */ + SHAPipe sha; + INIClass::Save(sha); + sha.Result(Digest); + IsDigestPresent = true; +} + + +/*********************************************************************************************** + * CCINIClass::Invalidate_Message_Digest -- Flag message digest as being invalid. * + * * + * This flags the message digest as being invalid so that it will be recalculated when * + * needed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + *=============================================================================================*/ +void CCINIClass::Invalidate_Message_Digest(void) +{ + IsDigestPresent = false; +} diff --git a/code/redalert/ccini.h b/code/redalert/ccini.h new file mode 100644 index 0000000..bfec980 --- /dev/null +++ b/code/redalert/ccini.h @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCINI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCINI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/24/96 * + * * + * Last Update : May 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CCINI_H +#define CCINI_H + +#include "ini.h" +#include "fixed.h" +#include "pk.h" + +class TriggerTypeClass; + +/* +** The advanced version of the INI database manager. It handles the C&C expansion types and +** identifiers. In addition, it automatically stores a message digest with the INI data +** so that verification can occur. +*/ +class CCINIClass : public INIClass +{ + public: + CCINIClass(void) : IsDigestPresent(false) {} + + bool Load(FileClass & file, bool withdigest); + int Load(Straw & file, bool withdigest); + int Save(FileClass & file, bool withdigest) const; + int Save(Pipe & pipe, bool withdigest) const; + + long Get_Buildings(char const * section, char const * entry, long defvalue) const; + UnitType Get_UnitType(char const * section, char const * entry, UnitType defvalue) const; + AnimType Get_AnimType(char const * section, char const * entry, AnimType defvalue) const; + ArmorType Get_ArmorType(char const * section, char const * entry, ArmorType defvalue) const; + BulletType Get_BulletType(char const * section, char const * entry, BulletType defvalue) const; + HousesType Get_HousesType(char const * section, char const * entry, HousesType defvalue) const; + LEPTON Get_Lepton(char const * section, char const * entry, LEPTON defvalue) const; + MPHType Get_MPHType(char const * section, char const * entry, MPHType defvalue) const; + OverlayType Get_OverlayType(char const * section, char const * entry, OverlayType defvalue) const; + SourceType Get_SourceType(char const * section, char const * entry, SourceType defvalue) const; + TerrainType Get_TerrainType(char const * section, char const * entry, TerrainType defvalue) const; + TheaterType Get_TheaterType(char const * section, char const * entry, TheaterType defvalue) const; + ThemeType Get_ThemeType(char const * section, char const * entry, ThemeType defvalue) const; + TriggerTypeClass * Get_TriggerType(char const * section, char const * entry) const; + VQType Get_VQType(char const * section, char const * entry, VQType defvalue) const; + VocType Get_VocType(char const * section, char const * entry, VocType defvalue) const; + WarheadType Get_WarheadType(char const * section, char const * entry, WarheadType defvalue) const; + WeaponType Get_WeaponType(char const * section, char const * entry, WeaponType defvalue) const; + long Get_Owners(char const * section, char const * entry, long defvalue) const; + CrateType Get_CrateType(char const * section, char const * entry, CrateType defvalue) const; + + + bool Put_Buildings(char const * section, char const * entry, long value); + bool Put_AnimType(char const * section, char const * entry, AnimType value); + bool Put_UnitType(char const * section, char const * entry, UnitType value); + bool Put_ArmorType(char const * section, char const * entry, ArmorType value); + bool Put_BulletType(char const * section, char const * entry, BulletType value); + bool Put_HousesType(char const * section, char const * entry, HousesType value); + bool Put_Lepton(char const * section, char const * entry, LEPTON value); + bool Put_MPHType(char const * section, char const * entry, MPHType value); + bool Put_VQType(char const * section, char const * entry, VQType value); + bool Put_OverlayType(char const * section, char const * entry, OverlayType value); + bool Put_Owners(char const * section, char const * entry, long value); + bool Put_SourceType(char const * section, char const * entry, SourceType value); + bool Put_TerrainType(char const * section, char const * entry, TerrainType value); + bool Put_TheaterType(char const * section, char const * entry, TheaterType value); + bool Put_ThemeType(char const * section, char const * entry, ThemeType value); + bool Put_TriggerType(char const * section, char const * entry, TriggerTypeClass * value); + bool Put_VocType(char const * section, char const * entry, VocType value); + bool Put_WarheadType(char const * section, char const * entry, WarheadType value); + bool Put_WeaponType(char const * section, char const * entry, WeaponType value); + bool Put_CrateType(char const * section, char const * entry, CrateType value); + + int Get_Unique_ID(void) const; + + private: + void Calculate_Message_Digest(void); + void Invalidate_Message_Digest(void); + + bool IsDigestPresent:1; + + /* + ** This is the message digest (SHA) of the INI database that was embedded as part of + ** the INI file. + */ + unsigned char Digest[20]; +}; + +#endif diff --git a/code/redalert/ccmpath.cpp b/code/redalert/ccmpath.cpp new file mode 100644 index 0000000..4392fc5 --- /dev/null +++ b/code/redalert/ccmpath.cpp @@ -0,0 +1,418 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCMPATH.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : January 11, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_MPATH -- Performs MPATH-specific initialization * + * Shutdown_MPATH -- Shuts down MPATH connections * + * Connect_MPATH -- Waits for connections to other players * + * Destroy_MPATH_Connection -- Destroys the given connection * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + + +/*************************************************************************** + * Init_MPATH -- Performs MPATH-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_MPATH(void) +{ +#if(MPATH) + //------------------------------------------------------------------------ + // Allocate a packet buffer for MPATH's use + //------------------------------------------------------------------------ + Session.MPathPacket = new char[Session.MPathSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_MPATH_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + MPath->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_MPATH(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_MPATH + + +/*************************************************************************** + * Shutdown_MPATH -- Shuts down MPATH connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_MPATH(void) +{ +#if(MPATH) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.MPathPacket) { + delete [] Session.MPathPacket; + Session.MPathPacket = NULL; + } + + if (MPath) { + delete MPath; + MPath = NULL; + } + + return; + +#endif +} // end of Shutdown_MPATH + + +/*************************************************************************** + * Connect_MPATH -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_MPATH(void) +{ +#if(MPATH) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = MPath->Find_Num_Connections(); + num_found = 0; + Session.NumPlayers = num_players + 1; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + MPath->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (MPath->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->MPathAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->MPathAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + MPath->Send_Global_Message(&send_packet, sizeof(send_packet), 0, 0); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_MPATH_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_MPATH_Connection(int id, int error) +{ +#if(MPATH) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),MPath->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),MPath->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the MPATH connection + ------------------------------------------------------------------------*/ + MPath->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_MPATH_Connection + + +/***************************** end of ccmpath.cpp **************************/ diff --git a/code/redalert/ccptr.cpp b/code/redalert/ccptr.cpp new file mode 100644 index 0000000..fa3021d --- /dev/null +++ b/code/redalert/ccptr.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCPTR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCPTR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CCPtr::operator > -- Greater than comparison operator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + + +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; +template class CCPtr; + + +template FixedIHeapClass* CCPtr::Heap = NULL; + + +/* +** These member functions for the CCPtr class cannot be declared inside the +** class definition since they could refer to other objects that themselves +** contain CCPtr objects. The recursive nature of this type of declaration +** is not handled by Watcom, hence the body declaration is dislocated here. +*/ +template +CCPtr::CCPtr(T * ptr) : ID(-1) +{ + if (ptr != NULL) { + ID = ptr->ID; + } +} + + +/*********************************************************************************************** + * CCPtr::operator > -- Greater than comparison operator. * + * * + * This will compare two pointer value to see if the left hand value is greater than the * + * right hand. The values are compared by comparing based on their Name() functions. * + * * + * INPUT: rvalue -- The right handle CCPtr value. * + * * + * OUTPUT: Is the left hand value greater than the right hand value? * + * * + * WARNINGS: The values pointed to by CCPtr must have a Name() function defined. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +bool CCPtr::operator > (CCPtr const & rvalue) const +{ + return (stricmp((*this)->Name(), rvalue->Name()) > 0); +} \ No newline at end of file diff --git a/code/redalert/ccptr.h b/code/redalert/ccptr.h new file mode 100644 index 0000000..2b5141d --- /dev/null +++ b/code/redalert/ccptr.h @@ -0,0 +1,114 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CCPTR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCPTR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/07/96 * + * * + * Last Update : June 7, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CCPTR_H +#define CCPTR_H + +/* +** The CCPtr class is designed for a specific purpose. It functions like a pointer except that +** it requires no fixups for saving and loading. If pointer fixups are not an issue, than using +** regular pointers would be more efficient. +*/ +template +class CCPtr +{ + public: + CCPtr(void) : ID(-1) {}; + CCPtr(NoInitClass const & ) {}; + CCPtr(T * ptr); + + operator T * (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + T & operator * (void) const { + assert(Heap != NULL && ID < Heap->Length()); + return(*(T*)(*Heap)[ID]); + } + T * operator -> (void) const { + if (ID == -1) return(NULL); + assert(Heap != NULL && ID < Heap->Length()); + return((T*) (*Heap)[ID]); + } + + bool Is_Valid(void) const {return(ID != -1);} + + bool operator == (CCPtr const & rvalue) const {return(ID == rvalue.ID);} + bool operator != (CCPtr const & rvalue) const {return(ID != rvalue.ID);} + bool operator > (CCPtr const & rvalue) const; + bool operator <= (CCPtr const & rvalue) const {return (rvalue > *this);} + bool operator < (CCPtr const & rvalue) const {return (*this != rvalue && rvalue > *this);} + bool operator >= (CCPtr const & rvalue) const {return (*this == rvalue || rvalue > *this);} + + long Raw(void) const {return(ID);} + void Set_Raw(long value) {ID = value;} + + static void Set_Heap(FixedIHeapClass *heap) {Heap = heap;} + + private: + + static FixedIHeapClass * Heap; + + /* + ** This is the ID number of the object it refers to. By using an ID number, this class can + ** be saved and loaded without pointer fixups. + */ + int ID; +}; + +/* +** These template helper functions tell the compiler what to do in the +** ambiguous case of a CCPtr on one side and a regular type pointer on the +** other side. In such a case the compiler could create a temp CCPtr object +** OR call the conversion operator on the existing CCPtr object. Either way +** is technically valid, but the compiler doesn't know which is better so it +** generates an error. These routines force the conversion operator rather than +** creating a temporary object. This presumes that the conversion operator is +** cheaper than constructing a temporary and that cheaper solutions are desirable. +*/ +template +int operator == (CCPtr & lvalue, T * rvalue) +{ + return((T*)lvalue == rvalue); +} + +template +int operator == (T * lvalue, CCPtr & rvalue) +{ + return(lvalue == (T*)rvalue); +} + +#endif \ No newline at end of file diff --git a/code/redalert/ccten.cpp b/code/redalert/ccten.cpp new file mode 100644 index 0000000..a919246 --- /dev/null +++ b/code/redalert/ccten.cpp @@ -0,0 +1,600 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CCTEN.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 01/09/96 * + * * + * Last Update : November 27, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_TEN -- Performs TEN-specific initialization * + * Shutdown_TEN -- Shuts down TEN connections * + * Connect_TEN -- Waits for connections to other players * + * Destroy_TEN_Connection -- Destroys the given connection * + * Debug_Mono -- Custom mono prints * + * Send_TEN_Win_Packet -- Sends a win packet to server * + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#if(TEN) +#ifdef WIN32 +#define WINDOWS +#endif +#include "ten.h" +#endif + +void Connect_TEN(void); +void Debug_Mono(void); + +/*************************************************************************** + * Init_TEN -- Performs TEN-specific initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +int Init_TEN(void) +{ +#if(TEN) + //------------------------------------------------------------------------ + // Allocate a packet buffer for TEN's use + //------------------------------------------------------------------------ + Session.TenPacket = new char[Session.TenSize]; + + //------------------------------------------------------------------------ + // Read the multiplayer settings from the CONQUER.INI file, and the game + // options from the options file. + //------------------------------------------------------------------------ + Session.Read_MultiPlayer_Settings(); + + if (!Read_TEN_Game_Options()) { + WWMessageBox().Process("Unable to load game settings!"); + //Prog_End(); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Flush all incoming packets + //------------------------------------------------------------------------ + Ten->Flush_All(); + + //------------------------------------------------------------------------ + // Form connections to all other players + //------------------------------------------------------------------------ + Connect_TEN(); + + //------------------------------------------------------------------------ + // Set multiplayer values for the local system, and timing values. + //------------------------------------------------------------------------ + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + + return (1); +#else + return (1); +#endif + +} // end of Init_TEN + + +/*************************************************************************** + * Shutdown_TEN -- Shuts down TEN connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/09/1996 BRR : Created. * + *=========================================================================*/ +void Shutdown_TEN(void) +{ +#if(TEN) + CDTimerClass timer; + + //------------------------------------------------------------------------ + // Wait a full second before exiting, to ensure all packets get sent. + //------------------------------------------------------------------------ + timer = 60; + while (timer) ; + + //------------------------------------------------------------------------ + // Free memory + //------------------------------------------------------------------------ + if (Session.TenPacket) { + delete [] Session.TenPacket; + Session.TenPacket = NULL; + } + + if (Ten) { + delete Ten; + Ten = NULL; + } + + return; + +#endif +} // end of Shutdown_TEN + + +/*************************************************************************** + * Connect_TEN -- Waits for connections to other players * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * MPlayerCount must have been initialized at this point. * + * * + * HISTORY: * + * 01/10/1996 BRR : Created. * + *=========================================================================*/ +void Connect_TEN(void) +{ +#if(TEN) + typedef struct ConnectPacketTag { + NetCommandType Dummy; // packet type; set to PING + char Name[MPLAYER_NAME_MAX]; // player's name + HousesType House; // player's ActLike + unsigned char Color; // player's Color + } ConnectPacketType; + int num_players; + int num_found; + ConnectPacketType send_packet; + ConnectPacketType receive_packet; + int address; + int found; + int size; + int i; + CDTimerClass send_timer; + NodeNameType *who; + + enum { + D_TXT6_H = 7, + D_MARGIN = 5, + }; + static int x,y,w,h; + char const *buf1; + char const *buf2; + + int display = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // + // Clear the Players list + // + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + // + // Add myself to the list first thing + // + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + // + // Find out how many players to wait for + // + num_players = Session.NumPlayers - 1; + num_found = 0; + + // + // Send out a packet announcing my presence + // + send_packet.Dummy = NET_PING; + strcpy(send_packet.Name, Session.Handle); + send_packet.House = Session.House; + send_packet.Color = Session.ColorIdx; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + + // + // Start our packet-sending timer + // + send_timer = 240; + + // + // Wait for all players to enter the game + // + display = 1; + while (num_found < num_players) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = 1; + } + #endif + + if (display) { + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + buf1 = Text_String(TXT_WAITING_FOR_CONNECTIONS); + buf2 = Text_String(TXT_PRESS_ESC); + w = MAX(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + w += (D_MARGIN * 2); + h = (D_TXT6_H * 2) + (D_MARGIN * 7); + x = 160 - (w / 2); + y = 100 - (h / 2); + Hide_Mouse(); + //Set_Logic_Page(SeenBuff); + Dialog_Box(x * RESFACTOR, y * RESFACTOR, w * RESFACTOR, h * RESFACTOR); + + Fancy_Text_Print(buf1, + 160 * RESFACTOR, + (y + (D_MARGIN * 2)) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(buf2, + 160 * RESFACTOR, + (y + (D_MARGIN * 2) + D_TXT6_H + D_MARGIN) * RESFACTOR, + scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Show_Mouse(); + display = 0; + } + + Ten->Service(); + + // + // Check for an incoming packet; if a PING comes in, see if we already + // have this player in our Players list. If not, add him. + // + if (Ten->Get_Global_Message (&receive_packet, &size, &address) && + receive_packet.Dummy == NET_PING) { + found = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (Session.Players[i]->TenAddress == address) { + found = 1; + break; + } + } + + // + // Create a new connection and a new node in the list. + // + if (!found) { + + who = new NodeNameType; + strcpy(who->Name, receive_packet.Name); + who->TenAddress = address; + who->Player.House = receive_packet.House; + who->Player.Color = (PlayerColorType)receive_packet.Color; + Session.Players.Add (who); + + num_found++; + + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 1, + address); + } + } + + // + // If the user hits ESC, bail out. + // + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + //Prog_End(); + Emergency_Exit(0); + } + } + + // + // When our timer expires, re-send the packet. + // + if (!send_timer) { + send_packet.Dummy = NET_PING; + Ten->Send_Global_Message(&send_packet, sizeof(send_packet), 0, -1); + send_timer = 240; + } + } + +#else + return; +#endif +} + + +/*************************************************************************** + * Destroy_TEN_Connection -- Destroys the given connection * + * * + * INPUT: * + * id connection ID to destroy (must be a HousesType) * + * error 0 = user signed off; 1 = connection error; otherwise, * + * no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +void Destroy_TEN_Connection(int id, int error) +{ +#if(TEN) + int i; + HouseClass *housep; + char txt[80]; + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /*------------------------------------------------------------------------ + Create a message to display to the user + ------------------------------------------------------------------------*/ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST),Ten->Connection_Name(id)); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME),Ten->Connection_Name(id)); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /*------------------------------------------------------------------------ + Delete the TEN connection + ------------------------------------------------------------------------*/ + Ten->Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /*------------------------------------------------------------------------ + If we're the last player left, tell the user. + ------------------------------------------------------------------------*/ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +#else + id = id; + error = error; + +#endif +} // end of Destroy_TEN_Connection + + +/*************************************************************************** + * Debug_Mono -- Custom mono prints * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Debug_Mono(void) +{ +#if(TEN) + int i; + int id; + + Mono_Printf("STATE: # Connections:%d\n",Ten->Num_Connections()); + for (i=0;iNum_Connections();i++) { + id = Ten->Connection_ID(i); + Mono_Printf("Connection %d: Name:%s, ID:%d, Address:%d\n", + i, + Ten->Connection_Name(id), + Ten->Connection_ID(i), + Ten->Connection_Address(id)); + } + +#endif +} + + +/*************************************************************************** + * Send_TEN_Win_Packet -- Sends a win packet to server * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Win_Packet(void) +{ +#if(TEN) + char winbuf[80]; + char idbuf[20]; + int first = 1; + HouseClass *hptr; + int i; + + // + // Build a special text buffer to send to the TEN server. Format: + // "winner 'id id'", where 'id' is the Ten Player ID of each player + // on the winning team. (For TEN, the color index is the player ID.) + // + sprintf(winbuf,"winner '"); + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + if (!first) { + strcat(winbuf," "); + } else { + first = 0; + } + sprintf(idbuf,"%d", Session.Players[i]->Player.Color); + strcat (winbuf, idbuf); + } + } + strcat (winbuf,"' "); + tenArSetPlayerState(winbuf); +#endif // TEN + +} // end of Send_TEN_Win_Packet + + +/*************************************************************************** + * Send_TEN_Alliance -- Sends an ally/enemy packet to server * + * * + * INPUT: * + * whom name of player we're allying / enemying with * + * ally 1 = we're allying; 0 = we're breaking the alliance * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Alliance(char *whom, int ally) +{ +#if(TEN) + char buf[80]; + + if (ally) { + sprintf(buf,"ally '%s' ",whom); + } else { + sprintf(buf,"enemy '%s' ",whom); + } + + tenArSetPlayerState(buf); + +#else + whom = whom; + ally = ally; +#endif // TEN + +} // end of Send_TEN_Alliance + + +/*************************************************************************** + * Send_TEN_Out_Of_Sync -- Announces this game out of sync * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Out_Of_Sync(void) +{ +#if(TEN) + tenArSetPlayerState("sync '1' "); +#endif // TEN + +} // end of Send_TEN_Out_Of_Sync + + +/*************************************************************************** + * Send_TEN_Packet_Too_Late -- Announces packet-received-too-late * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/27/1996 BRR : Created. * + *=========================================================================*/ +void Send_TEN_Packet_Too_Late(void) +{ +#if(TEN) + tenArSetPlayerState("toolate '1' "); +#endif // TEN + +} // end of Send_TEN_Packet_Too_Late + + +/***************************** end of ccten.cpp *****************************/ diff --git a/code/redalert/cdata.cpp b/code/redalert/cdata.cpp new file mode 100644 index 0000000..cad7202 --- /dev/null +++ b/code/redalert/cdata.cpp @@ -0,0 +1,3340 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "image.h" + +static TemplateTypeClass const Empty( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Clear( + TEMPLATE_CLEAR1, + THEATERF_TEMPERATE|THEATERF_SNOW|THEATERF_INTERIOR, + "CLEAR1", + TXT_CLEAR +); +static TemplateTypeClass const Road01( + TEMPLATE_ROAD01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D01", + TXT_ROAD +); +static TemplateTypeClass const Road02( + TEMPLATE_ROAD02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D02", + TXT_ROAD +); +static TemplateTypeClass const Road03( + TEMPLATE_ROAD03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D03", + TXT_ROAD +); +static TemplateTypeClass const Road04( + TEMPLATE_ROAD04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D04", + TXT_ROAD +); +static TemplateTypeClass const Road05( + TEMPLATE_ROAD05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D05", + TXT_ROAD +); +static TemplateTypeClass const Road06( + TEMPLATE_ROAD06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D06", + TXT_ROAD +); +static TemplateTypeClass const Road07( + TEMPLATE_ROAD07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D07", + TXT_ROAD +); +static TemplateTypeClass const Road08( + TEMPLATE_ROAD08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D08", + TXT_ROAD +); +static TemplateTypeClass const Road09( + TEMPLATE_ROAD09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D09", + TXT_ROAD +); +static TemplateTypeClass const Road10( + TEMPLATE_ROAD10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D10", + TXT_ROAD +); +static TemplateTypeClass const Road11( + TEMPLATE_ROAD11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D11", + TXT_ROAD +); +static TemplateTypeClass const Road12( + TEMPLATE_ROAD12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D12", + TXT_ROAD +); +static TemplateTypeClass const Road13( + TEMPLATE_ROAD13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D13", + TXT_ROAD +); +static TemplateTypeClass const Road14( + TEMPLATE_ROAD14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D14", + TXT_ROAD +); +static TemplateTypeClass const Road15( + TEMPLATE_ROAD15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D15", + TXT_ROAD +); +static TemplateTypeClass const Road16( + TEMPLATE_ROAD16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D16", + TXT_ROAD +); +static TemplateTypeClass const Road17( + TEMPLATE_ROAD17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D17", + TXT_ROAD +); +static TemplateTypeClass const Road18( + TEMPLATE_ROAD18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D18", + TXT_ROAD +); +static TemplateTypeClass const Road19( + TEMPLATE_ROAD19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D19", + TXT_ROAD +); +static TemplateTypeClass const Road20( + TEMPLATE_ROAD20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D20", + TXT_ROAD +); +static TemplateTypeClass const Road21( + TEMPLATE_ROAD21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D21", + TXT_ROAD +); +static TemplateTypeClass const Road22( + TEMPLATE_ROAD22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D22", + TXT_ROAD +); +static TemplateTypeClass const Road23( + TEMPLATE_ROAD23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D23", + TXT_ROAD +); +static TemplateTypeClass const Road24( + TEMPLATE_ROAD24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D24", + TXT_ROAD +); +static TemplateTypeClass const Road25( + TEMPLATE_ROAD25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D25", + TXT_ROAD +); +static TemplateTypeClass const Road26( + TEMPLATE_ROAD26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D26", + TXT_ROAD +); +static TemplateTypeClass const Road27( + TEMPLATE_ROAD27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D27", + TXT_ROAD +); +static TemplateTypeClass const Road28( + TEMPLATE_ROAD28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D28", + TXT_ROAD +); +static TemplateTypeClass const Road29( + TEMPLATE_ROAD29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D29", + TXT_ROAD +); +static TemplateTypeClass const Road30( + TEMPLATE_ROAD30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D30", + TXT_ROAD +); +static TemplateTypeClass const Road31( + TEMPLATE_ROAD31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D31", + TXT_ROAD +); +static TemplateTypeClass const Road32( + TEMPLATE_ROAD32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D32", + TXT_ROAD +); +static TemplateTypeClass const Road33( + TEMPLATE_ROAD33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D33", + TXT_ROAD +); +static TemplateTypeClass const Road34( + TEMPLATE_ROAD34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D34", + TXT_ROAD +); +static TemplateTypeClass const Road35( + TEMPLATE_ROAD35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D35", + TXT_ROAD +); +static TemplateTypeClass const Road36( + TEMPLATE_ROAD36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D36", + TXT_ROAD +); +static TemplateTypeClass const Road37( + TEMPLATE_ROAD37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D37", + TXT_ROAD +); +static TemplateTypeClass const Road38( + TEMPLATE_ROAD38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D38", + TXT_ROAD +); +static TemplateTypeClass const Road39( + TEMPLATE_ROAD39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D39", + TXT_ROAD +); +static TemplateTypeClass const Road40( + TEMPLATE_ROAD40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D40", + TXT_ROAD +); +static TemplateTypeClass const Road41( + TEMPLATE_ROAD41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D41", + TXT_ROAD +); +static TemplateTypeClass const Road42( + TEMPLATE_ROAD42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D42", + TXT_ROAD +); +static TemplateTypeClass const Road43( + TEMPLATE_ROAD43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D43", + TXT_ROAD +); +static TemplateTypeClass const Road44( + TEMPLATE_ROAD44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D44", + TXT_ROAD +); +static TemplateTypeClass const Road45( + TEMPLATE_ROAD45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "D45", + TXT_ROAD +); +static TemplateTypeClass const Water( + TEMPLATE_WATER, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W1", + TXT_WATER +); +static TemplateTypeClass const Water2( + TEMPLATE_WATER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "W2", + TXT_WATER +); +static TemplateTypeClass const Shore01( + TEMPLATE_SHORE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH01", + TXT_SHORE +); +static TemplateTypeClass const Shore02( + TEMPLATE_SHORE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH02", + TXT_SHORE +); +static TemplateTypeClass const Shore03( + TEMPLATE_SHORE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH03", + TXT_SHORE +); +static TemplateTypeClass const Shore04( + TEMPLATE_SHORE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH04", + TXT_SHORE +); +static TemplateTypeClass const Shore05( + TEMPLATE_SHORE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH05", + TXT_SHORE +); +static TemplateTypeClass const Shore06( + TEMPLATE_SHORE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH06", + TXT_SHORE +); +static TemplateTypeClass const Shore07( + TEMPLATE_SHORE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH07", + TXT_SHORE +); +static TemplateTypeClass const Shore08( + TEMPLATE_SHORE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH08", + TXT_SHORE +); +static TemplateTypeClass const Shore09( + TEMPLATE_SHORE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH09", + TXT_SHORE +); +static TemplateTypeClass const Shore10( + TEMPLATE_SHORE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH10", + TXT_SHORE +); +static TemplateTypeClass const Shore11( + TEMPLATE_SHORE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH11", + TXT_SHORE +); +static TemplateTypeClass const Shore12( + TEMPLATE_SHORE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH12", + TXT_SHORE +); +static TemplateTypeClass const Shore13( + TEMPLATE_SHORE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH13", + TXT_SHORE +); +static TemplateTypeClass const Shore14( + TEMPLATE_SHORE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH14", + TXT_SHORE +); +static TemplateTypeClass const Shore15( + TEMPLATE_SHORE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH15", + TXT_SHORE +); +static TemplateTypeClass const Shore16( + TEMPLATE_SHORE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH16", + TXT_SHORE +); +static TemplateTypeClass const Shore17( + TEMPLATE_SHORE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH17", + TXT_SHORE +); +static TemplateTypeClass const Shore18( + TEMPLATE_SHORE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH18", + TXT_SHORE +); +static TemplateTypeClass const Shore19( + TEMPLATE_SHORE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH19", + TXT_SHORE +); +static TemplateTypeClass const Shore20( + TEMPLATE_SHORE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH20", + TXT_SHORE +); +static TemplateTypeClass const Shore21( + TEMPLATE_SHORE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH21", + TXT_SHORE +); +static TemplateTypeClass const Shore22( + TEMPLATE_SHORE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH22", + TXT_SHORE +); +static TemplateTypeClass const Shore23( + TEMPLATE_SHORE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH23", + TXT_SHORE +); +static TemplateTypeClass const Shore24( + TEMPLATE_SHORE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH24", + TXT_SHORE +); +static TemplateTypeClass const Shore25( + TEMPLATE_SHORE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH25", + TXT_SHORE +); +static TemplateTypeClass const Shore26( + TEMPLATE_SHORE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH26", + TXT_SHORE +); +static TemplateTypeClass const Shore27( + TEMPLATE_SHORE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH27", + TXT_SHORE +); +static TemplateTypeClass const Shore28( + TEMPLATE_SHORE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH28", + TXT_SHORE +); +static TemplateTypeClass const Shore29( + TEMPLATE_SHORE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH29", + TXT_SHORE +); +static TemplateTypeClass const Shore30( + TEMPLATE_SHORE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH30", + TXT_SHORE +); +static TemplateTypeClass const Shore31( + TEMPLATE_SHORE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH31", + TXT_SHORE +); +static TemplateTypeClass const Shore32( + TEMPLATE_SHORE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH32", + TXT_SHORE +); +static TemplateTypeClass const Shore33( + TEMPLATE_SHORE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH33", + TXT_SHORE +); +static TemplateTypeClass const Shore34( + TEMPLATE_SHORE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH34", + TXT_SHORE +); +static TemplateTypeClass const Shore35( + TEMPLATE_SHORE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH35", + TXT_SHORE +); +static TemplateTypeClass const Shore36( + TEMPLATE_SHORE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH36", + TXT_SHORE +); +static TemplateTypeClass const Shore37( + TEMPLATE_SHORE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH37", + TXT_SHORE +); +static TemplateTypeClass const Shore38( + TEMPLATE_SHORE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH38", + TXT_SHORE +); +static TemplateTypeClass const Shore39( + TEMPLATE_SHORE39, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH39", + TXT_SHORE +); +static TemplateTypeClass const Shore40( + TEMPLATE_SHORE40, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH40", + TXT_SHORE +); +static TemplateTypeClass const Shore41( + TEMPLATE_SHORE41, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH41", + TXT_SHORE +); +static TemplateTypeClass const Shore42( + TEMPLATE_SHORE42, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH42", + TXT_SHORE +); +static TemplateTypeClass const Shore43( + TEMPLATE_SHORE43, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH43", + TXT_SHORE +); +static TemplateTypeClass const Shore44( + TEMPLATE_SHORE44, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH44", + TXT_SHORE +); +static TemplateTypeClass const Shore45( + TEMPLATE_SHORE45, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH45", + TXT_SHORE +); +static TemplateTypeClass const Shore46( + TEMPLATE_SHORE46, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH46", + TXT_SHORE +); +static TemplateTypeClass const Shore47( + TEMPLATE_SHORE47, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH47", + TXT_SHORE +); +static TemplateTypeClass const Shore48( + TEMPLATE_SHORE48, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH48", + TXT_SHORE +); +static TemplateTypeClass const Shore49( + TEMPLATE_SHORE49, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH49", + TXT_SHORE +); +static TemplateTypeClass const Shore50( + TEMPLATE_SHORE50, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH50", + TXT_SHORE +); +static TemplateTypeClass const Shore51( + TEMPLATE_SHORE51, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH51", + TXT_SHORE +); +static TemplateTypeClass const Shore52( + TEMPLATE_SHORE52, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH52", + TXT_SHORE +); +static TemplateTypeClass const Shore53( + TEMPLATE_SHORE53, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH53", + TXT_SHORE +); +static TemplateTypeClass const Shore54( + TEMPLATE_SHORE54, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH54", + TXT_SHORE +); +static TemplateTypeClass const Shore55( + TEMPLATE_SHORE55, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH55", + TXT_SHORE +); +static TemplateTypeClass const Shore56( + TEMPLATE_SHORE56, + THEATERF_TEMPERATE|THEATERF_SNOW, + "SH56", + TXT_SHORE +); +static TemplateTypeClass const Boulder1( + TEMPLATE_BOULDER1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B1", + TXT_SLOPE +); +static TemplateTypeClass const Boulder2( + TEMPLATE_BOULDER2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B2", + TXT_SLOPE +); +static TemplateTypeClass const Boulder3( + TEMPLATE_BOULDER3, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B3", + TXT_SLOPE +); +static TemplateTypeClass const Boulder4( + TEMPLATE_BOULDER4, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B4", + TXT_SLOPE +); +static TemplateTypeClass const Boulder5( + TEMPLATE_BOULDER5, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B5", + TXT_SLOPE +); +static TemplateTypeClass const Boulder6( + TEMPLATE_BOULDER6, + THEATERF_TEMPERATE|THEATERF_SNOW, + "B6", + TXT_SLOPE +); +static TemplateTypeClass const Slope01( + TEMPLATE_SLOPE01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S01", + TXT_SLOPE +); +static TemplateTypeClass const Slope02( + TEMPLATE_SLOPE02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S02", + TXT_SLOPE +); +static TemplateTypeClass const Slope03( + TEMPLATE_SLOPE03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S03", + TXT_SLOPE +); +static TemplateTypeClass const Slope04( + TEMPLATE_SLOPE04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S04", + TXT_SLOPE +); +static TemplateTypeClass const Slope05( + TEMPLATE_SLOPE05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S05", + TXT_SLOPE +); +static TemplateTypeClass const Slope06( + TEMPLATE_SLOPE06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S06", + TXT_SLOPE +); +static TemplateTypeClass const Slope07( + TEMPLATE_SLOPE07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S07", + TXT_SLOPE +); +static TemplateTypeClass const Slope08( + TEMPLATE_SLOPE08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S08", + TXT_SLOPE +); +static TemplateTypeClass const Slope09( + TEMPLATE_SLOPE09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S09", + TXT_SLOPE +); +static TemplateTypeClass const Slope10( + TEMPLATE_SLOPE10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S10", + TXT_SLOPE +); +static TemplateTypeClass const Slope11( + TEMPLATE_SLOPE11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S11", + TXT_SLOPE +); +static TemplateTypeClass const Slope12( + TEMPLATE_SLOPE12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S12", + TXT_SLOPE +); +static TemplateTypeClass const Slope13( + TEMPLATE_SLOPE13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S13", + TXT_SLOPE +); +static TemplateTypeClass const Slope14( + TEMPLATE_SLOPE14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S14", + TXT_SLOPE +); +static TemplateTypeClass const Slope15( + TEMPLATE_SLOPE15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S15", + TXT_SLOPE +); +static TemplateTypeClass const Slope16( + TEMPLATE_SLOPE16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S16", + TXT_SLOPE +); +static TemplateTypeClass const Slope17( + TEMPLATE_SLOPE17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S17", + TXT_SLOPE +); +static TemplateTypeClass const Slope18( + TEMPLATE_SLOPE18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S18", + TXT_SLOPE +); +static TemplateTypeClass const Slope19( + TEMPLATE_SLOPE19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S19", + TXT_SLOPE +); +static TemplateTypeClass const Slope20( + TEMPLATE_SLOPE20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S20", + TXT_SLOPE +); +static TemplateTypeClass const Slope21( + TEMPLATE_SLOPE21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S21", + TXT_SLOPE +); +static TemplateTypeClass const Slope22( + TEMPLATE_SLOPE22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S22", + TXT_SLOPE +); +static TemplateTypeClass const Slope23( + TEMPLATE_SLOPE23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S23", + TXT_SLOPE +); +static TemplateTypeClass const Slope24( + TEMPLATE_SLOPE24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S24", + TXT_SLOPE +); +static TemplateTypeClass const Slope25( + TEMPLATE_SLOPE25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S25", + TXT_SLOPE +); +static TemplateTypeClass const Slope26( + TEMPLATE_SLOPE26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S26", + TXT_SLOPE +); +static TemplateTypeClass const Slope27( + TEMPLATE_SLOPE27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S27", + TXT_SLOPE +); +static TemplateTypeClass const Slope28( + TEMPLATE_SLOPE28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S28", + TXT_SLOPE +); +static TemplateTypeClass const Slope29( + TEMPLATE_SLOPE29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S29", + TXT_SLOPE +); +static TemplateTypeClass const Slope30( + TEMPLATE_SLOPE30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S30", + TXT_SLOPE +); +static TemplateTypeClass const Slope31( + TEMPLATE_SLOPE31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S31", + TXT_SLOPE +); +static TemplateTypeClass const Slope32( + TEMPLATE_SLOPE32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S32", + TXT_SLOPE +); +static TemplateTypeClass const Slope33( + TEMPLATE_SLOPE33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S33", + TXT_SLOPE +); +static TemplateTypeClass const Slope34( + TEMPLATE_SLOPE34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S34", + TXT_SLOPE +); +static TemplateTypeClass const Slope35( + TEMPLATE_SLOPE35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S35", + TXT_SLOPE +); +static TemplateTypeClass const Slope36( + TEMPLATE_SLOPE36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S36", + TXT_SLOPE +); +static TemplateTypeClass const Slope37( + TEMPLATE_SLOPE37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S37", + TXT_SLOPE +); +static TemplateTypeClass const Slope38( + TEMPLATE_SLOPE38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "S38", + TXT_SLOPE +); +static TemplateTypeClass const Patch01( + TEMPLATE_PATCH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P01", + TXT_PATCH +); +static TemplateTypeClass const Patch02( + TEMPLATE_PATCH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P02", + TXT_PATCH +); +static TemplateTypeClass const Patch03( + TEMPLATE_PATCH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P03", + TXT_PATCH +); +static TemplateTypeClass const Patch04( + TEMPLATE_PATCH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P04", + TXT_PATCH +); +static TemplateTypeClass const Patch07( + TEMPLATE_PATCH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P07", + TXT_PATCH +); +static TemplateTypeClass const Patch08( + TEMPLATE_PATCH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P08", + TXT_PATCH +); +static TemplateTypeClass const Patch13( + TEMPLATE_PATCH13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P13", + TXT_PATCH +); +static TemplateTypeClass const Patch14( + TEMPLATE_PATCH14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P14", + TXT_PATCH +); +static TemplateTypeClass const Patch15( + TEMPLATE_PATCH15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "P15", + TXT_PATCH +); +static TemplateTypeClass const River01( + TEMPLATE_RIVER01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV01", + TXT_RIVER +); +static TemplateTypeClass const River02( + TEMPLATE_RIVER02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV02", + TXT_RIVER +); +static TemplateTypeClass const River03( + TEMPLATE_RIVER03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV03", + TXT_RIVER +); +static TemplateTypeClass const River04( + TEMPLATE_RIVER04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV04", + TXT_RIVER +); +static TemplateTypeClass const River05( + TEMPLATE_RIVER05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV05", + TXT_RIVER +); +static TemplateTypeClass const River06( + TEMPLATE_RIVER06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV06", + TXT_RIVER +); +static TemplateTypeClass const River07( + TEMPLATE_RIVER07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV07", + TXT_RIVER +); +static TemplateTypeClass const River08( + TEMPLATE_RIVER08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV08", + TXT_RIVER +); +static TemplateTypeClass const River09( + TEMPLATE_RIVER09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV09", + TXT_RIVER +); +static TemplateTypeClass const River10( + TEMPLATE_RIVER10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV10", + TXT_RIVER +); +static TemplateTypeClass const River11( + TEMPLATE_RIVER11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV11", + TXT_RIVER +); +static TemplateTypeClass const River12( + TEMPLATE_RIVER12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV12", + TXT_RIVER +); +static TemplateTypeClass const River13( + TEMPLATE_RIVER13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV13", + TXT_RIVER +); +static TemplateTypeClass const River14( + TEMPLATE_RIVER14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV14", + TXT_RIVER +); +static TemplateTypeClass const River15( + TEMPLATE_RIVER15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RV15", + TXT_RIVER +); +static TemplateTypeClass const Ford1( + TEMPLATE_FORD1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD1", + TXT_RIVER +); +static TemplateTypeClass const Ford2( + TEMPLATE_FORD2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FORD2", + TXT_RIVER +); +static TemplateTypeClass const Falls1( + TEMPLATE_FALLS1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1", + TXT_RIVER +); +static TemplateTypeClass const Falls1a( + TEMPLATE_FALLS1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS1A", + TXT_RIVER +); +static TemplateTypeClass const Falls2( + TEMPLATE_FALLS2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2", + TXT_RIVER +); +static TemplateTypeClass const Falls2a( + TEMPLATE_FALLS2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "FALLS2A", + TXT_RIVER +); +static TemplateTypeClass const Bridge1x( + TEMPLATE_BRIDGE1X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1( + TEMPLATE_BRIDGE1, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1h( + TEMPLATE_BRIDGE1H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1d( + TEMPLATE_BRIDGE1D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE1D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2x( + TEMPLATE_BRIDGE2X, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2( + TEMPLATE_BRIDGE2, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2h( + TEMPLATE_BRIDGE2H, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2H", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2d( + TEMPLATE_BRIDGE2D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BRIDGE2D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1ax( + TEMPLATE_BRIDGE_1AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1a( + TEMPLATE_BRIDGE_1A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1b( + TEMPLATE_BRIDGE_1B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge1c( + TEMPLATE_BRIDGE_1C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR1C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2ax( + TEMPLATE_BRIDGE_2AX, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2X", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2a( + TEMPLATE_BRIDGE_2A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2b( + TEMPLATE_BRIDGE_2B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge2c( + TEMPLATE_BRIDGE_2C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR2C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3a( + TEMPLATE_BRIDGE_3A, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3A", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3b( + TEMPLATE_BRIDGE_3B, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3B", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3c( + TEMPLATE_BRIDGE_3C, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3C", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3d( + TEMPLATE_BRIDGE_3D, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3D", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3e( + TEMPLATE_BRIDGE_3E, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3E", + TXT_BRIDGE +); +static TemplateTypeClass const Bridge3f( + TEMPLATE_BRIDGE_3F, + THEATERF_TEMPERATE|THEATERF_SNOW, + "BR3F", + TXT_BRIDGE +); +static TemplateTypeClass const ShoreCliff01( + TEMPLATE_SHORECLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC01", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff02( + TEMPLATE_SHORECLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC02", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff03( + TEMPLATE_SHORECLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC03", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff04( + TEMPLATE_SHORECLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC04", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff05( + TEMPLATE_SHORECLIFF05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC05", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff06( + TEMPLATE_SHORECLIFF06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC06", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff07( + TEMPLATE_SHORECLIFF07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC07", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff08( + TEMPLATE_SHORECLIFF08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC08", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff09( + TEMPLATE_SHORECLIFF09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC09", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff10( + TEMPLATE_SHORECLIFF10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC10", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff11( + TEMPLATE_SHORECLIFF11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC11", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff12( + TEMPLATE_SHORECLIFF12, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC12", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff13( + TEMPLATE_SHORECLIFF13, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC13", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff14( + TEMPLATE_SHORECLIFF14, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC14", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff15( + TEMPLATE_SHORECLIFF15, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC15", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff16( + TEMPLATE_SHORECLIFF16, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC16", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff17( + TEMPLATE_SHORECLIFF17, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC17", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff18( + TEMPLATE_SHORECLIFF18, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC18", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff19( + TEMPLATE_SHORECLIFF19, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC19", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff20( + TEMPLATE_SHORECLIFF20, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC20", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff21( + TEMPLATE_SHORECLIFF21, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC21", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff22( + TEMPLATE_SHORECLIFF22, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC22", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff23( + TEMPLATE_SHORECLIFF23, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC23", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff24( + TEMPLATE_SHORECLIFF24, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC24", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff25( + TEMPLATE_SHORECLIFF25, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC25", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff26( + TEMPLATE_SHORECLIFF26, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC26", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff27( + TEMPLATE_SHORECLIFF27, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC27", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff28( + TEMPLATE_SHORECLIFF28, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC28", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff29( + TEMPLATE_SHORECLIFF29, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC29", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff30( + TEMPLATE_SHORECLIFF30, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC30", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff31( + TEMPLATE_SHORECLIFF31, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC31", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff32( + TEMPLATE_SHORECLIFF32, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC32", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff33( + TEMPLATE_SHORECLIFF33, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC33", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff34( + TEMPLATE_SHORECLIFF34, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC34", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff35( + TEMPLATE_SHORECLIFF35, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC35", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff36( + TEMPLATE_SHORECLIFF36, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC36", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff37( + TEMPLATE_SHORECLIFF37, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC37", + TXT_SHORE +); +static TemplateTypeClass const ShoreCliff38( + TEMPLATE_SHORECLIFF38, + THEATERF_TEMPERATE|THEATERF_SNOW, + "WC38", + TXT_SHORE +); +static TemplateTypeClass const Rough01( + TEMPLATE_ROUGH01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF01", + TXT_ROCK +); +static TemplateTypeClass const Rough02( + TEMPLATE_ROUGH02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF02", + TXT_ROCK +); +static TemplateTypeClass const Rough03( + TEMPLATE_ROUGH03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF03", + TXT_ROCK +); +static TemplateTypeClass const Rough04( + TEMPLATE_ROUGH04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF04", + TXT_ROCK +); +static TemplateTypeClass const Rough05( + TEMPLATE_ROUGH05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF05", + TXT_ROCK +); +static TemplateTypeClass const Rough06( + TEMPLATE_ROUGH06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF06", + TXT_ROCK +); +static TemplateTypeClass const Rough07( + TEMPLATE_ROUGH07, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF07", + TXT_ROCK +); +static TemplateTypeClass const Rough08( + TEMPLATE_ROUGH08, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF08", + TXT_ROCK +); +static TemplateTypeClass const Rough09( + TEMPLATE_ROUGH09, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF09", + TXT_ROCK +); +static TemplateTypeClass const Rough10( + TEMPLATE_ROUGH10, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF10", + TXT_ROCK +); +static TemplateTypeClass const Rough11( + TEMPLATE_ROUGH11, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RF11", + TXT_ROCK +); +static TemplateTypeClass const RiverCliff01( + TEMPLATE_RIVERCLIFF01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC01", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff02( + TEMPLATE_RIVERCLIFF02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC02", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff03( + TEMPLATE_RIVERCLIFF03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC03", + TXT_RIVER +); +static TemplateTypeClass const RiverCliff04( + TEMPLATE_RIVERCLIFF04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "RC04", + TXT_RIVER +); + +static TemplateTypeClass const F01( + TEMPLATE_F01, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F01", + TXT_RIVER +); +static TemplateTypeClass const F02( + TEMPLATE_F02, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F02", + TXT_RIVER +); +static TemplateTypeClass const F03( + TEMPLATE_F03, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F03", + TXT_RIVER +); +static TemplateTypeClass const F04( + TEMPLATE_F04, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F04", + TXT_RIVER +); +static TemplateTypeClass const F05( + TEMPLATE_F05, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F05", + TXT_RIVER +); +static TemplateTypeClass const F06( + TEMPLATE_F06, + THEATERF_TEMPERATE|THEATERF_SNOW, + "F06", + TXT_RIVER +); + +static TemplateTypeClass const ARRO0001( + TEMPLATE_ARRO0001, + THEATERF_INTERIOR, + "ARRO0001", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0002( + TEMPLATE_ARRO0002, + THEATERF_INTERIOR, + "ARRO0002", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0003( + TEMPLATE_ARRO0003, + THEATERF_INTERIOR, + "ARRO0003", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0004( + TEMPLATE_ARRO0004, + THEATERF_INTERIOR, + "ARRO0004", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0005( + TEMPLATE_ARRO0005, + THEATERF_INTERIOR, + "ARRO0005", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0006( + TEMPLATE_ARRO0006, + THEATERF_INTERIOR, + "ARRO0006", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0007( + TEMPLATE_ARRO0007, + THEATERF_INTERIOR, + "ARRO0007", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0008( + TEMPLATE_ARRO0008, + THEATERF_INTERIOR, + "ARRO0008", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0009( + TEMPLATE_ARRO0009, + THEATERF_INTERIOR, + "ARRO0009", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0010( + TEMPLATE_ARRO0010, + THEATERF_INTERIOR, + "ARRO0010", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0011( + TEMPLATE_ARRO0011, + THEATERF_INTERIOR, + "ARRO0011", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0012( + TEMPLATE_ARRO0012, + THEATERF_INTERIOR, + "ARRO0012", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0013( + TEMPLATE_ARRO0013, + THEATERF_INTERIOR, + "ARRO0013", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0014( + TEMPLATE_ARRO0014, + THEATERF_INTERIOR, + "ARRO0014", + TXT_INTERIOR +); +static TemplateTypeClass const ARRO0015( + TEMPLATE_ARRO0015, + THEATERF_INTERIOR, + "ARRO0015", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0001( + TEMPLATE_FLOR0001, + THEATERF_INTERIOR, + "FLOR0001", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0002( + TEMPLATE_FLOR0002, + THEATERF_INTERIOR, + "FLOR0002", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0003( + TEMPLATE_FLOR0003, + THEATERF_INTERIOR, + "FLOR0003", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0004( + TEMPLATE_FLOR0004, + THEATERF_INTERIOR, + "FLOR0004", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0005( + TEMPLATE_FLOR0005, + THEATERF_INTERIOR, + "FLOR0005", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0006( + TEMPLATE_FLOR0006, + THEATERF_INTERIOR, + "FLOR0006", + TXT_INTERIOR +); +static TemplateTypeClass const FLOR0007( + TEMPLATE_FLOR0007, + THEATERF_INTERIOR, + "FLOR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0001( + TEMPLATE_GFLR0001, + THEATERF_INTERIOR, + "GFLR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0002( + TEMPLATE_GFLR0002, + THEATERF_INTERIOR, + "GFLR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0003( + TEMPLATE_GFLR0003, + THEATERF_INTERIOR, + "GFLR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0004( + TEMPLATE_GFLR0004, + THEATERF_INTERIOR, + "GFLR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GFLR0005( + TEMPLATE_GFLR0005, + THEATERF_INTERIOR, + "GFLR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0001( + TEMPLATE_GSTR0001, + THEATERF_INTERIOR, + "GSTR0001", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0002( + TEMPLATE_GSTR0002, + THEATERF_INTERIOR, + "GSTR0002", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0003( + TEMPLATE_GSTR0003, + THEATERF_INTERIOR, + "GSTR0003", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0004( + TEMPLATE_GSTR0004, + THEATERF_INTERIOR, + "GSTR0004", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0005( + TEMPLATE_GSTR0005, + THEATERF_INTERIOR, + "GSTR0005", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0006( + TEMPLATE_GSTR0006, + THEATERF_INTERIOR, + "GSTR0006", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0007( + TEMPLATE_GSTR0007, + THEATERF_INTERIOR, + "GSTR0007", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0008( + TEMPLATE_GSTR0008, + THEATERF_INTERIOR, + "GSTR0008", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0009( + TEMPLATE_GSTR0009, + THEATERF_INTERIOR, + "GSTR0009", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0010( + TEMPLATE_GSTR0010, + THEATERF_INTERIOR, + "GSTR0010", + TXT_INTERIOR +); +static TemplateTypeClass const GSTR0011( + TEMPLATE_GSTR0011, + THEATERF_INTERIOR, + "GSTR0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0001( + TEMPLATE_LWAL0001, + THEATERF_INTERIOR, + "LWAL0001", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0002( + TEMPLATE_LWAL0002, + THEATERF_INTERIOR, + "LWAL0002", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0003( + TEMPLATE_LWAL0003, + THEATERF_INTERIOR, + "LWAL0003", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0004( + TEMPLATE_LWAL0004, + THEATERF_INTERIOR, + "LWAL0004", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0005( + TEMPLATE_LWAL0005, + THEATERF_INTERIOR, + "LWAL0005", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0006( + TEMPLATE_LWAL0006, + THEATERF_INTERIOR, + "LWAL0006", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0007( + TEMPLATE_LWAL0007, + THEATERF_INTERIOR, + "LWAL0007", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0008( + TEMPLATE_LWAL0008, + THEATERF_INTERIOR, + "LWAL0008", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0009( + TEMPLATE_LWAL0009, + THEATERF_INTERIOR, + "LWAL0009", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0010( + TEMPLATE_LWAL0010, + THEATERF_INTERIOR, + "LWAL0010", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0011( + TEMPLATE_LWAL0011, + THEATERF_INTERIOR, + "LWAL0011", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0012( + TEMPLATE_LWAL0012, + THEATERF_INTERIOR, + "LWAL0012", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0013( + TEMPLATE_LWAL0013, + THEATERF_INTERIOR, + "LWAL0013", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0014( + TEMPLATE_LWAL0014, + THEATERF_INTERIOR, + "LWAL0014", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0015( + TEMPLATE_LWAL0015, + THEATERF_INTERIOR, + "LWAL0015", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0016( + TEMPLATE_LWAL0016, + THEATERF_INTERIOR, + "LWAL0016", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0017( + TEMPLATE_LWAL0017, + THEATERF_INTERIOR, + "LWAL0017", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0018( + TEMPLATE_LWAL0018, + THEATERF_INTERIOR, + "LWAL0018", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0019( + TEMPLATE_LWAL0019, + THEATERF_INTERIOR, + "LWAL0019", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0020( + TEMPLATE_LWAL0020, + THEATERF_INTERIOR, + "LWAL0020", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0021( + TEMPLATE_LWAL0021, + THEATERF_INTERIOR, + "LWAL0021", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0022( + TEMPLATE_LWAL0022, + THEATERF_INTERIOR, + "LWAL0022", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0023( + TEMPLATE_LWAL0023, + THEATERF_INTERIOR, + "LWAL0023", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0024( + TEMPLATE_LWAL0024, + THEATERF_INTERIOR, + "LWAL0024", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0025( + TEMPLATE_LWAL0025, + THEATERF_INTERIOR, + "LWAL0025", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0026( + TEMPLATE_LWAL0026, + THEATERF_INTERIOR, + "LWAL0026", + TXT_INTERIOR +); +static TemplateTypeClass const LWAL0027( + TEMPLATE_LWAL0027, + THEATERF_INTERIOR, + "LWAL0027", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0001( + TEMPLATE_STRP0001, + THEATERF_INTERIOR, + "STRP0001", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0002( + TEMPLATE_STRP0002, + THEATERF_INTERIOR, + "STRP0002", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0003( + TEMPLATE_STRP0003, + THEATERF_INTERIOR, + "STRP0003", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0004( + TEMPLATE_STRP0004, + THEATERF_INTERIOR, + "STRP0004", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0005( + TEMPLATE_STRP0005, + THEATERF_INTERIOR, + "STRP0005", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0006( + TEMPLATE_STRP0006, + THEATERF_INTERIOR, + "STRP0006", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0007( + TEMPLATE_STRP0007, + THEATERF_INTERIOR, + "STRP0007", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0008( + TEMPLATE_STRP0008, + THEATERF_INTERIOR, + "STRP0008", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0009( + TEMPLATE_STRP0009, + THEATERF_INTERIOR, + "STRP0009", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0010( + TEMPLATE_STRP0010, + THEATERF_INTERIOR, + "STRP0010", + TXT_INTERIOR +); +static TemplateTypeClass const STRP0011( + TEMPLATE_STRP0011, + THEATERF_INTERIOR, + "STRP0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0001( + TEMPLATE_WALL0001, + THEATERF_INTERIOR, + "WALL0001", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0002( + TEMPLATE_WALL0002, + THEATERF_INTERIOR, + "WALL0002", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0003( + TEMPLATE_WALL0003, + THEATERF_INTERIOR, + "WALL0003", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0004( + TEMPLATE_WALL0004, + THEATERF_INTERIOR, + "WALL0004", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0005( + TEMPLATE_WALL0005, + THEATERF_INTERIOR, + "WALL0005", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0006( + TEMPLATE_WALL0006, + THEATERF_INTERIOR, + "WALL0006", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0007( + TEMPLATE_WALL0007, + THEATERF_INTERIOR, + "WALL0007", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0008( + TEMPLATE_WALL0008, + THEATERF_INTERIOR, + "WALL0008", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0009( + TEMPLATE_WALL0009, + THEATERF_INTERIOR, + "WALL0009", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0010( + TEMPLATE_WALL0010, + THEATERF_INTERIOR, + "WALL0010", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0011( + TEMPLATE_WALL0011, + THEATERF_INTERIOR, + "WALL0011", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0012( + TEMPLATE_WALL0012, + THEATERF_INTERIOR, + "WALL0012", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0013( + TEMPLATE_WALL0013, + THEATERF_INTERIOR, + "WALL0013", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0014( + TEMPLATE_WALL0014, + THEATERF_INTERIOR, + "WALL0014", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0015( + TEMPLATE_WALL0015, + THEATERF_INTERIOR, + "WALL0015", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0016( + TEMPLATE_WALL0016, + THEATERF_INTERIOR, + "WALL0016", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0017( + TEMPLATE_WALL0017, + THEATERF_INTERIOR, + "WALL0017", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0018( + TEMPLATE_WALL0018, + THEATERF_INTERIOR, + "WALL0018", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0019( + TEMPLATE_WALL0019, + THEATERF_INTERIOR, + "WALL0019", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0020( + TEMPLATE_WALL0020, + THEATERF_INTERIOR, + "WALL0020", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0021( + TEMPLATE_WALL0021, + THEATERF_INTERIOR, + "WALL0021", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0022( + TEMPLATE_WALL0022, + THEATERF_INTERIOR, + "WALL0022", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0023( + TEMPLATE_WALL0023, + THEATERF_INTERIOR, + "WALL0023", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0024( + TEMPLATE_WALL0024, + THEATERF_INTERIOR, + "WALL0024", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0025( + TEMPLATE_WALL0025, + THEATERF_INTERIOR, + "WALL0025", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0026( + TEMPLATE_WALL0026, + THEATERF_INTERIOR, + "WALL0026", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0027( + TEMPLATE_WALL0027, + THEATERF_INTERIOR, + "WALL0027", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0028( + TEMPLATE_WALL0028, + THEATERF_INTERIOR, + "WALL0028", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0029( + TEMPLATE_WALL0029, + THEATERF_INTERIOR, + "WALL0029", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0030( + TEMPLATE_WALL0030, + THEATERF_INTERIOR, + "WALL0030", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0031( + TEMPLATE_WALL0031, + THEATERF_INTERIOR, + "WALL0031", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0032( + TEMPLATE_WALL0032, + THEATERF_INTERIOR, + "WALL0032", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0033( + TEMPLATE_WALL0033, + THEATERF_INTERIOR, + "WALL0033", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0034( + TEMPLATE_WALL0034, + THEATERF_INTERIOR, + "WALL0034", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0035( + TEMPLATE_WALL0035, + THEATERF_INTERIOR, + "WALL0035", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0036( + TEMPLATE_WALL0036, + THEATERF_INTERIOR, + "WALL0036", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0037( + TEMPLATE_WALL0037, + THEATERF_INTERIOR, + "WALL0037", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0038( + TEMPLATE_WALL0038, + THEATERF_INTERIOR, + "WALL0038", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0039( + TEMPLATE_WALL0039, + THEATERF_INTERIOR, + "WALL0039", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0040( + TEMPLATE_WALL0040, + THEATERF_INTERIOR, + "WALL0040", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0041( + TEMPLATE_WALL0041, + THEATERF_INTERIOR, + "WALL0041", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0042( + TEMPLATE_WALL0042, + THEATERF_INTERIOR, + "WALL0042", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0043( + TEMPLATE_WALL0043, + THEATERF_INTERIOR, + "WALL0043", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0044( + TEMPLATE_WALL0044, + THEATERF_INTERIOR, + "WALL0044", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0045( + TEMPLATE_WALL0045, + THEATERF_INTERIOR, + "WALL0045", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0046( + TEMPLATE_WALL0046, + THEATERF_INTERIOR, + "WALL0046", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0047( + TEMPLATE_WALL0047, + THEATERF_INTERIOR, + "WALL0047", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0048( + TEMPLATE_WALL0048, + THEATERF_INTERIOR, + "WALL0048", + TXT_INTERIOR +); +static TemplateTypeClass const WALL0049( + TEMPLATE_WALL0049, + THEATERF_INTERIOR, + "WALL0049", + TXT_INTERIOR +); + +static TemplateTypeClass const Xtra0001( + TEMPLATE_XTRA0001, + THEATERF_INTERIOR, + "XTRA0001", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0002( + TEMPLATE_XTRA0002, + THEATERF_INTERIOR, + "XTRA0002", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0003( + TEMPLATE_XTRA0003, + THEATERF_INTERIOR, + "XTRA0003", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0004( + TEMPLATE_XTRA0004, + THEATERF_INTERIOR, + "XTRA0004", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0005( + TEMPLATE_XTRA0005, + THEATERF_INTERIOR, + "XTRA0005", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0006( + TEMPLATE_XTRA0006, + THEATERF_INTERIOR, + "XTRA0006", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0007( + TEMPLATE_XTRA0007, + THEATERF_INTERIOR, + "XTRA0007", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0008( + TEMPLATE_XTRA0008, + THEATERF_INTERIOR, + "XTRA0008", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0009( + TEMPLATE_XTRA0009, + THEATERF_INTERIOR, + "XTRA0009", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0010( + TEMPLATE_XTRA0010, + THEATERF_INTERIOR, + "XTRA0010", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0011( + TEMPLATE_XTRA0011, + THEATERF_INTERIOR, + "XTRA0011", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0012( + TEMPLATE_XTRA0012, + THEATERF_INTERIOR, + "XTRA0012", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0013( + TEMPLATE_XTRA0013, + THEATERF_INTERIOR, + "XTRA0013", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0014( + TEMPLATE_XTRA0014, + THEATERF_INTERIOR, + "XTRA0014", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0015( + TEMPLATE_XTRA0015, + THEATERF_INTERIOR, + "XTRA0015", + TXT_INTERIOR +); +static TemplateTypeClass const Xtra0016( + TEMPLATE_XTRA0016, + THEATERF_INTERIOR, + "XTRA0016", + TXT_INTERIOR +); + +#ifdef FIXIT_ANTS +static TemplateTypeClass const AntHill( + TEMPLATE_HILL01, + THEATERF_TEMPERATE, + "HILL01", + TXT_ROCK +); +#endif + +/*********************************************************************************************** + * TemplateTypeClass::TemplateTypeClass -- Constructor for template type objects. * + * * + * This is the constructor for the template types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass::TemplateTypeClass( + TemplateType iconset, + int theater, + char const * ininame, + int fullname) : + ObjectTypeClass( + RTTI_TEMPLATETYPE, + int(iconset), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(iconset), + Theater(theater), + Width(0), + Height(0) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator new -- Allocates a template type from special heap. * + * * + * This allocates a template type object from the special heap used for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated template type object. If no object * + * could be allocated, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void * TemplateTypeClass::operator new(size_t) +{ + return(TemplateTypes.Alloc()); +} + + +/*********************************************************************************************** + * TemplateTypeClass::operator delete -- Deletes a template type object. * + * * + * This routine will return a template type object back to the special heap it was * + * allocated from. * + * * + * INPUT: ptr -- Pointer to the template type object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::operator delete(void * ptr) +{ + TemplateTypes.Free((TemplateTypeClass *)ptr); +} + + +static void _Watcom_Ugh_Hack(void) +{ + (void)new TemplateTypeClass(Road37); // TEMPLATE_ROAD37 + (void)new TemplateTypeClass(Road38); // TEMPLATE_ROAD38 + (void)new TemplateTypeClass(Road39); // TEMPLATE_ROAD39 + (void)new TemplateTypeClass(Road40); // TEMPLATE_ROAD40 + (void)new TemplateTypeClass(Road41); // TEMPLATE_ROAD41 + (void)new TemplateTypeClass(Road42); // TEMPLATE_ROAD42 + (void)new TemplateTypeClass(Road43); // TEMPLATE_ROAD43 + (void)new TemplateTypeClass(Rough01); // TEMPLATE_ROUGH01 + (void)new TemplateTypeClass(Rough02); // TEMPLATE_ROUGH02 + (void)new TemplateTypeClass(Rough03); // TEMPLATE_ROUGH03 + (void)new TemplateTypeClass(Rough04); // TEMPLATE_ROUGH04 + (void)new TemplateTypeClass(Rough05); // TEMPLATE_ROUGH05 + (void)new TemplateTypeClass(Rough06); // TEMPLATE_ROUGH06 + (void)new TemplateTypeClass(Rough07); // TEMPLATE_ROUGH07 + (void)new TemplateTypeClass(Rough08); // TEMPLATE_ROUGH08 + (void)new TemplateTypeClass(Rough09); // TEMPLATE_ROUGH09 + (void)new TemplateTypeClass(Rough10); // TEMPLATE_ROUGH10 + (void)new TemplateTypeClass(Rough11); // TEMPLATE_ROUGH11 + (void)new TemplateTypeClass(Road44); // TEMPLATE_ROAD44 + (void)new TemplateTypeClass(Road45); // TEMPLATE_ROAD45 + (void)new TemplateTypeClass(River14); // TEMPLATE_RIVER14 + (void)new TemplateTypeClass(River15); // TEMPLATE_RIVER15 + (void)new TemplateTypeClass(RiverCliff01); // TEMPLATE_RIVERCLIFF01 + (void)new TemplateTypeClass(RiverCliff02); // TEMPLATE_RIVERCLIFF02 + (void)new TemplateTypeClass(RiverCliff03); // TEMPLATE_RIVERCLIFF03 + (void)new TemplateTypeClass(RiverCliff04); // TEMPLATE_RIVERCLIFF04 + (void)new TemplateTypeClass(Bridge1a); // TEMPLATE_BRIDGE_1A + (void)new TemplateTypeClass(Bridge1b); // TEMPLATE_BRIDGE_1B + (void)new TemplateTypeClass(Bridge1c); // TEMPLATE_BRIDGE_1C + (void)new TemplateTypeClass(Bridge2a); // TEMPLATE_BRIDGE_2A + (void)new TemplateTypeClass(Bridge2b); // TEMPLATE_BRIDGE_2B + (void)new TemplateTypeClass(Bridge2c); // TEMPLATE_BRIDGE_2C + (void)new TemplateTypeClass(Bridge3a); // TEMPLATE_BRIDGE_3A + (void)new TemplateTypeClass(Bridge3b); // TEMPLATE_BRIDGE_3B + (void)new TemplateTypeClass(Bridge3c); // TEMPLATE_BRIDGE_3C + (void)new TemplateTypeClass(Bridge3d); // TEMPLATE_BRIDGE_3D + (void)new TemplateTypeClass(Bridge3e); // TEMPLATE_BRIDGE_3E + (void)new TemplateTypeClass(Bridge3f); // TEMPLATE_BRIDGE_3F + (void)new TemplateTypeClass(F01); // TEMPLATE_F01 + (void)new TemplateTypeClass(F02); // TEMPLATE_F02 + (void)new TemplateTypeClass(F03); // TEMPLATE_F03 + (void)new TemplateTypeClass(F04); // TEMPLATE_F04 + (void)new TemplateTypeClass(F05); // TEMPLATE_F05 + (void)new TemplateTypeClass(F06); // TEMPLATE_F06 + (void)new TemplateTypeClass(ARRO0001); // TEMPLATE_ARRO0001 + (void)new TemplateTypeClass(ARRO0002); // TEMPLATE_ARRO0002 + (void)new TemplateTypeClass(ARRO0003); // TEMPLATE_ARRO0003 + (void)new TemplateTypeClass(ARRO0004); // TEMPLATE_ARRO0004 + (void)new TemplateTypeClass(ARRO0005); // TEMPLATE_ARRO0005 + (void)new TemplateTypeClass(ARRO0006); // TEMPLATE_ARRO0006 + (void)new TemplateTypeClass(ARRO0007); // TEMPLATE_ARRO0007 + (void)new TemplateTypeClass(ARRO0008); // TEMPLATE_ARRO0008 + (void)new TemplateTypeClass(ARRO0009); // TEMPLATE_ARRO0009 + (void)new TemplateTypeClass(ARRO0010); // TEMPLATE_ARRO0010 + (void)new TemplateTypeClass(ARRO0011); // TEMPLATE_ARRO0011 + (void)new TemplateTypeClass(ARRO0012); // TEMPLATE_ARRO0012 + (void)new TemplateTypeClass(ARRO0013); // TEMPLATE_ARRO0013 + (void)new TemplateTypeClass(ARRO0014); // TEMPLATE_ARRO0014 + (void)new TemplateTypeClass(ARRO0015); // TEMPLATE_ARRO0015 + (void)new TemplateTypeClass(FLOR0001); // TEMPLATE_FLOR0001 + (void)new TemplateTypeClass(FLOR0002); // TEMPLATE_FLOR0002 + (void)new TemplateTypeClass(FLOR0003); // TEMPLATE_FLOR0003 + (void)new TemplateTypeClass(FLOR0004); // TEMPLATE_FLOR0004 + (void)new TemplateTypeClass(FLOR0005); // TEMPLATE_FLOR0005 + (void)new TemplateTypeClass(FLOR0006); // TEMPLATE_FLOR0006 + (void)new TemplateTypeClass(FLOR0007); // TEMPLATE_FLOR0007 + (void)new TemplateTypeClass(GFLR0001); // TEMPLATE_GFLR0001 + (void)new TemplateTypeClass(GFLR0002); // TEMPLATE_GFLR0002 + (void)new TemplateTypeClass(GFLR0003); // TEMPLATE_GFLR0003 + (void)new TemplateTypeClass(GFLR0004); // TEMPLATE_GFLR0004 + (void)new TemplateTypeClass(GFLR0005); // TEMPLATE_GFLR0005 + (void)new TemplateTypeClass(GSTR0001); // TEMPLATE_GSTR0001 + (void)new TemplateTypeClass(GSTR0002); // TEMPLATE_GSTR0002 + (void)new TemplateTypeClass(GSTR0003); // TEMPLATE_GSTR0003 + (void)new TemplateTypeClass(GSTR0004); // TEMPLATE_GSTR0004 + (void)new TemplateTypeClass(GSTR0005); // TEMPLATE_GSTR0005 + (void)new TemplateTypeClass(GSTR0006); // TEMPLATE_GSTR0006 + (void)new TemplateTypeClass(GSTR0007); // TEMPLATE_GSTR0007 + (void)new TemplateTypeClass(GSTR0008); // TEMPLATE_GSTR0008 + (void)new TemplateTypeClass(GSTR0009); // TEMPLATE_GSTR0009 + (void)new TemplateTypeClass(GSTR0010); // TEMPLATE_GSTR0010 + (void)new TemplateTypeClass(GSTR0011); // TEMPLATE_GSTR0011 + (void)new TemplateTypeClass(LWAL0001); // TEMPLATE_LWAL0001 + (void)new TemplateTypeClass(LWAL0002); // TEMPLATE_LWAL0002 + (void)new TemplateTypeClass(LWAL0003); // TEMPLATE_LWAL0003 + (void)new TemplateTypeClass(LWAL0004); // TEMPLATE_LWAL0004 + (void)new TemplateTypeClass(LWAL0005); // TEMPLATE_LWAL0005 + (void)new TemplateTypeClass(LWAL0006); // TEMPLATE_LWAL0006 + (void)new TemplateTypeClass(LWAL0007); // TEMPLATE_LWAL0007 + (void)new TemplateTypeClass(LWAL0008); // TEMPLATE_LWAL0008 + (void)new TemplateTypeClass(LWAL0009); // TEMPLATE_LWAL0009 + (void)new TemplateTypeClass(LWAL0010); // TEMPLATE_LWAL0010 + (void)new TemplateTypeClass(LWAL0011); // TEMPLATE_LWAL0011 + (void)new TemplateTypeClass(LWAL0012); // TEMPLATE_LWAL0012 + (void)new TemplateTypeClass(LWAL0013); // TEMPLATE_LWAL0013 + (void)new TemplateTypeClass(LWAL0014); // TEMPLATE_LWAL0014 + (void)new TemplateTypeClass(LWAL0015); // TEMPLATE_LWAL0015 + (void)new TemplateTypeClass(LWAL0016); // TEMPLATE_LWAL0016 + (void)new TemplateTypeClass(LWAL0017); // TEMPLATE_LWAL0017 + (void)new TemplateTypeClass(LWAL0018); // TEMPLATE_LWAL0018 + (void)new TemplateTypeClass(LWAL0019); // TEMPLATE_LWAL0019 + (void)new TemplateTypeClass(LWAL0020); // TEMPLATE_LWAL0020 + (void)new TemplateTypeClass(LWAL0021); // TEMPLATE_LWAL0021 + (void)new TemplateTypeClass(LWAL0022); // TEMPLATE_LWAL0022 + (void)new TemplateTypeClass(LWAL0023); // TEMPLATE_LWAL0023 + (void)new TemplateTypeClass(LWAL0024); // TEMPLATE_LWAL0024 + (void)new TemplateTypeClass(LWAL0025); // TEMPLATE_LWAL0025 + (void)new TemplateTypeClass(LWAL0026); // TEMPLATE_LWAL0026 + (void)new TemplateTypeClass(LWAL0027); // TEMPLATE_LWAL0027 + (void)new TemplateTypeClass(STRP0001); // TEMPLATE_STRP0001 + (void)new TemplateTypeClass(STRP0002); // TEMPLATE_STRP0002 + (void)new TemplateTypeClass(STRP0003); // TEMPLATE_STRP0003 + (void)new TemplateTypeClass(STRP0004); // TEMPLATE_STRP0004 + (void)new TemplateTypeClass(STRP0005); // TEMPLATE_STRP0005 + (void)new TemplateTypeClass(STRP0006); // TEMPLATE_STRP0006 + (void)new TemplateTypeClass(STRP0007); // TEMPLATE_STRP0007 + (void)new TemplateTypeClass(STRP0008); // TEMPLATE_STRP0008 + (void)new TemplateTypeClass(STRP0009); // TEMPLATE_STRP0009 + (void)new TemplateTypeClass(STRP0010); // TEMPLATE_STRP0010 + (void)new TemplateTypeClass(STRP0011); // TEMPLATE_STRP0011 + (void)new TemplateTypeClass(WALL0001); // TEMPLATE_WALL0001 + (void)new TemplateTypeClass(WALL0002); // TEMPLATE_WALL0002 + (void)new TemplateTypeClass(WALL0003); // TEMPLATE_WALL0003 + (void)new TemplateTypeClass(WALL0004); // TEMPLATE_WALL0004 + (void)new TemplateTypeClass(WALL0005); // TEMPLATE_WALL0005 + (void)new TemplateTypeClass(WALL0006); // TEMPLATE_WALL0006 + (void)new TemplateTypeClass(WALL0007); // TEMPLATE_WALL0007 + (void)new TemplateTypeClass(WALL0008); // TEMPLATE_WALL0008 + (void)new TemplateTypeClass(WALL0009); // TEMPLATE_WALL0009 + (void)new TemplateTypeClass(WALL0010); // TEMPLATE_WALL0010 + (void)new TemplateTypeClass(WALL0011); // TEMPLATE_WALL0011 + (void)new TemplateTypeClass(WALL0012); // TEMPLATE_WALL0012 + (void)new TemplateTypeClass(WALL0013); // TEMPLATE_WALL0013 + (void)new TemplateTypeClass(WALL0014); // TEMPLATE_WALL0014 + (void)new TemplateTypeClass(WALL0015); // TEMPLATE_WALL0015 + (void)new TemplateTypeClass(WALL0016); // TEMPLATE_WALL0016 + (void)new TemplateTypeClass(WALL0017); // TEMPLATE_WALL0017 + (void)new TemplateTypeClass(WALL0018); // TEMPLATE_WALL0018 + (void)new TemplateTypeClass(WALL0019); // TEMPLATE_WALL0019 + (void)new TemplateTypeClass(WALL0020); // TEMPLATE_WALL0020 + (void)new TemplateTypeClass(WALL0021); // TEMPLATE_WALL0021 + (void)new TemplateTypeClass(WALL0022); // TEMPLATE_WALL0022 + (void)new TemplateTypeClass(WALL0023); // TEMPLATE_WALL0023 + (void)new TemplateTypeClass(WALL0024); // TEMPLATE_WALL0024 + (void)new TemplateTypeClass(WALL0025); // TEMPLATE_WALL0025 + (void)new TemplateTypeClass(WALL0026); // TEMPLATE_WALL0026 + (void)new TemplateTypeClass(WALL0027); // TEMPLATE_WALL0027 + (void)new TemplateTypeClass(WALL0028); // TEMPLATE_WALL0028 + (void)new TemplateTypeClass(WALL0029); // TEMPLATE_WALL0029 + (void)new TemplateTypeClass(WALL0030); // TEMPLATE_WALL0030 + (void)new TemplateTypeClass(WALL0031); // TEMPLATE_WALL0031 + (void)new TemplateTypeClass(WALL0032); // TEMPLATE_WALL0032 + (void)new TemplateTypeClass(WALL0033); // TEMPLATE_WALL0033 + (void)new TemplateTypeClass(WALL0034); // TEMPLATE_WALL0034 + (void)new TemplateTypeClass(WALL0035); // TEMPLATE_WALL0035 + (void)new TemplateTypeClass(WALL0036); // TEMPLATE_WALL0036 + (void)new TemplateTypeClass(WALL0037); // TEMPLATE_WALL0037 + (void)new TemplateTypeClass(WALL0038); // TEMPLATE_WALL0038 + (void)new TemplateTypeClass(WALL0039); // TEMPLATE_WALL0039 + (void)new TemplateTypeClass(WALL0040); // TEMPLATE_WALL0040 + (void)new TemplateTypeClass(WALL0041); // TEMPLATE_WALL0041 + (void)new TemplateTypeClass(WALL0042); // TEMPLATE_WALL0042 + (void)new TemplateTypeClass(WALL0043); // TEMPLATE_WALL0043 + (void)new TemplateTypeClass(WALL0044); // TEMPLATE_WALL0044 + (void)new TemplateTypeClass(WALL0045); // TEMPLATE_WALL0045 + (void)new TemplateTypeClass(WALL0046); // TEMPLATE_WALL0046 + (void)new TemplateTypeClass(WALL0047); // TEMPLATE_WALL0047 + (void)new TemplateTypeClass(WALL0048); // TEMPLATE_WALL0048 + (void)new TemplateTypeClass(WALL0049); // TEMPLATE_WALL0049 + (void)new TemplateTypeClass(Bridge1h); // TEMPLATE_BRIDGE1H + (void)new TemplateTypeClass(Bridge2h); // TEMPLATE_BRIDGE2H + + (void)new TemplateTypeClass(Bridge1ax); // TEMPLATE_BRIDGE_1AX + (void)new TemplateTypeClass(Bridge2ax); // TEMPLATE_BRIDGE_2AX + (void)new TemplateTypeClass(Bridge1x); // TEMPLATE_BRIDGE1X + (void)new TemplateTypeClass(Bridge2x); // TEMPLATE_BRIDGE2X + + (void)new TemplateTypeClass(Xtra0001); // TEMPLATE_XTRA0001 + (void)new TemplateTypeClass(Xtra0002); // TEMPLATE_XTRA0002 + (void)new TemplateTypeClass(Xtra0003); // TEMPLATE_XTRA0003 + (void)new TemplateTypeClass(Xtra0004); // TEMPLATE_XTRA0004 + (void)new TemplateTypeClass(Xtra0005); // TEMPLATE_XTRA0005 + (void)new TemplateTypeClass(Xtra0006); // TEMPLATE_XTRA0006 + (void)new TemplateTypeClass(Xtra0007); // TEMPLATE_XTRA0007 + (void)new TemplateTypeClass(Xtra0008); // TEMPLATE_XTRA0008 + (void)new TemplateTypeClass(Xtra0009); // TEMPLATE_XTRA0009 + (void)new TemplateTypeClass(Xtra0010); // TEMPLATE_XTRA0010 + (void)new TemplateTypeClass(Xtra0011); // TEMPLATE_XTRA0011 + (void)new TemplateTypeClass(Xtra0012); // TEMPLATE_XTRA0012 + (void)new TemplateTypeClass(Xtra0013); // TEMPLATE_XTRA0013 + (void)new TemplateTypeClass(Xtra0014); // TEMPLATE_XTRA0014 + (void)new TemplateTypeClass(Xtra0015); // TEMPLATE_XTRA0015 + (void)new TemplateTypeClass(Xtra0016); // TEMPLATE_XTRA0016 + +#ifdef FIXIT_ANTS + (void)new TemplateTypeClass(AntHill); // TEMPLATE_ROAD36 +#endif +} + + + +void TemplateTypeClass::Init_Heap(void) +{ + /* + ** These template type class objects must be allocated in the exact order that they + ** are specified in the TemplateType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + (void)new TemplateTypeClass(Clear); // TEMPLATE_CLEAR1 + (void)new TemplateTypeClass(Water); // TEMPLATE_WATER + (void)new TemplateTypeClass(Water2); // TEMPLATE_WATER2 + (void)new TemplateTypeClass(Shore01); // TEMPLATE_SHORE1 + (void)new TemplateTypeClass(Shore02); // TEMPLATE_SHORE2 + (void)new TemplateTypeClass(Shore03); // TEMPLATE_SHORE3 + (void)new TemplateTypeClass(Shore04); // TEMPLATE_SHORE4 + (void)new TemplateTypeClass(Shore05); // TEMPLATE_SHORE5 + (void)new TemplateTypeClass(Shore06); // TEMPLATE_SHORE6 + (void)new TemplateTypeClass(Shore07); // TEMPLATE_SHORE7 + (void)new TemplateTypeClass(Shore08); // TEMPLATE_SHORE8 + (void)new TemplateTypeClass(Shore09); // TEMPLATE_SHORE9 + (void)new TemplateTypeClass(Shore10); // TEMPLATE_SHORE10 + (void)new TemplateTypeClass(Shore11); // TEMPLATE_SHORE11 + (void)new TemplateTypeClass(Shore12); // TEMPLATE_SHORE12 + (void)new TemplateTypeClass(Shore13); // TEMPLATE_SHORE13 + (void)new TemplateTypeClass(Shore14); // TEMPLATE_SHORE14 + (void)new TemplateTypeClass(Shore15); // TEMPLATE_SHORE15 + (void)new TemplateTypeClass(Shore16); // TEMPLATE_SHORE16 + (void)new TemplateTypeClass(Shore17); // TEMPLATE_SHORE17 + (void)new TemplateTypeClass(Shore18); // TEMPLATE_SHORE18 + (void)new TemplateTypeClass(Shore19); // TEMPLATE_SHORE19 + (void)new TemplateTypeClass(Shore20); // TEMPLATE_SHORE20 + (void)new TemplateTypeClass(Shore21); // TEMPLATE_SHORE21 + (void)new TemplateTypeClass(Shore22); // TEMPLATE_SHORE22 + (void)new TemplateTypeClass(Shore23); // TEMPLATE_SHORE23 + (void)new TemplateTypeClass(Shore24); // TEMPLATE_SHORE24 + (void)new TemplateTypeClass(Shore25); // TEMPLATE_SHORE25 + (void)new TemplateTypeClass(Shore26); // TEMPLATE_SHORE26 + (void)new TemplateTypeClass(Shore27); // TEMPLATE_SHORE27 + (void)new TemplateTypeClass(Shore28); // TEMPLATE_SHORE28 + (void)new TemplateTypeClass(Shore29); // TEMPLATE_SHORE29 + (void)new TemplateTypeClass(Shore30); // TEMPLATE_SHORE30 + (void)new TemplateTypeClass(Shore31); // TEMPLATE_SHORE31 + (void)new TemplateTypeClass(Shore32); // TEMPLATE_SHORE32 + (void)new TemplateTypeClass(Shore33); // TEMPLATE_SHORE33 + (void)new TemplateTypeClass(Shore34); // TEMPLATE_SHORE34 + (void)new TemplateTypeClass(Shore35); // TEMPLATE_SHORE35 + (void)new TemplateTypeClass(Shore36); // TEMPLATE_SHORE36 + (void)new TemplateTypeClass(Shore37); // TEMPLATE_SHORE37 + (void)new TemplateTypeClass(Shore38); // TEMPLATE_SHORE38 + (void)new TemplateTypeClass(Shore39); // TEMPLATE_SHORE39 + (void)new TemplateTypeClass(Shore40); // TEMPLATE_SHORE40 + (void)new TemplateTypeClass(Shore41); // TEMPLATE_SHORE41 + (void)new TemplateTypeClass(Shore42); // TEMPLATE_SHORE42 + (void)new TemplateTypeClass(Shore43); // TEMPLATE_SHORE43 + (void)new TemplateTypeClass(Shore44); // TEMPLATE_SHORE44 + (void)new TemplateTypeClass(Shore45); // TEMPLATE_SHORE45 + (void)new TemplateTypeClass(Shore46); // TEMPLATE_SHORE46 + (void)new TemplateTypeClass(Shore47); // TEMPLATE_SHORE47 + (void)new TemplateTypeClass(Shore48); // TEMPLATE_SHORE48 + (void)new TemplateTypeClass(Shore49); // TEMPLATE_SHORE49 + (void)new TemplateTypeClass(Shore50); // TEMPLATE_SHORE50 + (void)new TemplateTypeClass(Shore51); // TEMPLATE_SHORE51 + (void)new TemplateTypeClass(Shore52); // TEMPLATE_SHORE52 + (void)new TemplateTypeClass(Shore53); // TEMPLATE_SHORE53 + (void)new TemplateTypeClass(Shore54); // TEMPLATE_SHORE54 + (void)new TemplateTypeClass(Shore55); // TEMPLATE_SHORE55 + (void)new TemplateTypeClass(Shore56); // TEMPLATE_SHORE56 + (void)new TemplateTypeClass(ShoreCliff01); // TEMPLATE_SHORECLIFF01 + (void)new TemplateTypeClass(ShoreCliff02); // TEMPLATE_SHORECLIFF02 + (void)new TemplateTypeClass(ShoreCliff03); // TEMPLATE_SHORECLIFF03 + (void)new TemplateTypeClass(ShoreCliff04); // TEMPLATE_SHORECLIFF04 + (void)new TemplateTypeClass(ShoreCliff05); // TEMPLATE_SHORECLIFF05 + (void)new TemplateTypeClass(ShoreCliff06); // TEMPLATE_SHORECLIFF06 + (void)new TemplateTypeClass(ShoreCliff07); // TEMPLATE_SHORECLIFF07 + (void)new TemplateTypeClass(ShoreCliff08); // TEMPLATE_SHORECLIFF08 + (void)new TemplateTypeClass(ShoreCliff09); // TEMPLATE_SHORECLIFF09 + (void)new TemplateTypeClass(ShoreCliff10); // TEMPLATE_SHORECLIFF10 + (void)new TemplateTypeClass(ShoreCliff11); // TEMPLATE_SHORECLIFF11 + (void)new TemplateTypeClass(ShoreCliff12); // TEMPLATE_SHORECLIFF12 + (void)new TemplateTypeClass(ShoreCliff13); // TEMPLATE_SHORECLIFF13 + (void)new TemplateTypeClass(ShoreCliff14); // TEMPLATE_SHORECLIFF14 + (void)new TemplateTypeClass(ShoreCliff15); // TEMPLATE_SHORECLIFF15 + (void)new TemplateTypeClass(ShoreCliff16); // TEMPLATE_SHORECLIFF16 + (void)new TemplateTypeClass(ShoreCliff17); // TEMPLATE_SHORECLIFF17 + (void)new TemplateTypeClass(ShoreCliff18); // TEMPLATE_SHORECLIFF18 + (void)new TemplateTypeClass(ShoreCliff19); // TEMPLATE_SHORECLIFF19 + (void)new TemplateTypeClass(ShoreCliff20); // TEMPLATE_SHORECLIFF20 + (void)new TemplateTypeClass(ShoreCliff21); // TEMPLATE_SHORECLIFF21 + (void)new TemplateTypeClass(ShoreCliff22); // TEMPLATE_SHORECLIFF22 + (void)new TemplateTypeClass(ShoreCliff23); // TEMPLATE_SHORECLIFF23 + (void)new TemplateTypeClass(ShoreCliff24); // TEMPLATE_SHORECLIFF24 + (void)new TemplateTypeClass(ShoreCliff25); // TEMPLATE_SHORECLIFF25 + (void)new TemplateTypeClass(ShoreCliff26); // TEMPLATE_SHORECLIFF26 + (void)new TemplateTypeClass(ShoreCliff27); // TEMPLATE_SHORECLIFF27 + (void)new TemplateTypeClass(ShoreCliff28); // TEMPLATE_SHORECLIFF28 + (void)new TemplateTypeClass(ShoreCliff29); // TEMPLATE_SHORECLIFF29 + (void)new TemplateTypeClass(ShoreCliff30); // TEMPLATE_SHORECLIFF30 + (void)new TemplateTypeClass(ShoreCliff31); // TEMPLATE_SHORECLIFF31 + (void)new TemplateTypeClass(ShoreCliff32); // TEMPLATE_SHORECLIFF32 + (void)new TemplateTypeClass(ShoreCliff33); // TEMPLATE_SHORECLIFF33 + (void)new TemplateTypeClass(ShoreCliff34); // TEMPLATE_SHORECLIFF34 + (void)new TemplateTypeClass(ShoreCliff35); // TEMPLATE_SHORECLIFF35 + (void)new TemplateTypeClass(ShoreCliff36); // TEMPLATE_SHORECLIFF36 + (void)new TemplateTypeClass(ShoreCliff37); // TEMPLATE_SHORECLIFF37 + (void)new TemplateTypeClass(ShoreCliff38); // TEMPLATE_SHORECLIFF38 + (void)new TemplateTypeClass(Boulder1); // TEMPLATE_BOULDER1 + (void)new TemplateTypeClass(Boulder2); // TEMPLATE_BOULDER2 + (void)new TemplateTypeClass(Boulder3); // TEMPLATE_BOULDER3 + (void)new TemplateTypeClass(Boulder4); // TEMPLATE_BOULDER4 + (void)new TemplateTypeClass(Boulder5); // TEMPLATE_BOULDER5 + (void)new TemplateTypeClass(Boulder6); // TEMPLATE_BOULDER6 + (void)new TemplateTypeClass(Patch01); // TEMPLATE_PATCH1 + (void)new TemplateTypeClass(Patch02); // TEMPLATE_PATCH2 + (void)new TemplateTypeClass(Patch03); // TEMPLATE_PATCH3 + (void)new TemplateTypeClass(Patch04); // TEMPLATE_PATCH4 + (void)new TemplateTypeClass(Patch07); // TEMPLATE_PATCH7 + (void)new TemplateTypeClass(Patch08); // TEMPLATE_PATCH8 + (void)new TemplateTypeClass(Patch13); // TEMPLATE_PATCH13 + (void)new TemplateTypeClass(Patch14); // TEMPLATE_PATCH14 + (void)new TemplateTypeClass(Patch15); // TEMPLATE_PATCH15 + (void)new TemplateTypeClass(River01); // TEMPLATE_RIVER1 + (void)new TemplateTypeClass(River02); // TEMPLATE_RIVER2 + (void)new TemplateTypeClass(River03); // TEMPLATE_RIVER3 + (void)new TemplateTypeClass(River04); // TEMPLATE_RIVER4 + (void)new TemplateTypeClass(River05); // TEMPLATE_RIVER5 + (void)new TemplateTypeClass(River06); // TEMPLATE_RIVER6 + (void)new TemplateTypeClass(River07); // TEMPLATE_RIVER7 + (void)new TemplateTypeClass(River08); // TEMPLATE_RIVER8 + (void)new TemplateTypeClass(River09); // TEMPLATE_RIVER9 + (void)new TemplateTypeClass(River10); // TEMPLATE_RIVER10 + (void)new TemplateTypeClass(River11); // TEMPLATE_RIVER11 + (void)new TemplateTypeClass(River12); // TEMPLATE_RIVER12 + (void)new TemplateTypeClass(River13); // TEMPLATE_RIVER13 + (void)new TemplateTypeClass(Falls1); // TEMPLATE_FALLS1 + (void)new TemplateTypeClass(Falls1a); // TEMPLATE_FALLS1A + (void)new TemplateTypeClass(Falls2); // TEMPLATE_FALLS2 + (void)new TemplateTypeClass(Falls2a); // TEMPLATE_FALLS2A + (void)new TemplateTypeClass(Ford1); // TEMPLATE_FORD1 + (void)new TemplateTypeClass(Ford2); // TEMPLATE_FORD2 + (void)new TemplateTypeClass(Bridge1); // TEMPLATE_BRIDGE1 + (void)new TemplateTypeClass(Bridge1d); // TEMPLATE_BRIDGE1D + (void)new TemplateTypeClass(Bridge2); // TEMPLATE_BRIDGE2 + (void)new TemplateTypeClass(Bridge2d); // TEMPLATE_BRIDGE2D + (void)new TemplateTypeClass(Slope01); // TEMPLATE_SLOPE1 + (void)new TemplateTypeClass(Slope02); // TEMPLATE_SLOPE2 + (void)new TemplateTypeClass(Slope03); // TEMPLATE_SLOPE3 + (void)new TemplateTypeClass(Slope04); // TEMPLATE_SLOPE4 + (void)new TemplateTypeClass(Slope05); // TEMPLATE_SLOPE5 + (void)new TemplateTypeClass(Slope06); // TEMPLATE_SLOPE6 + (void)new TemplateTypeClass(Slope07); // TEMPLATE_SLOPE7 + (void)new TemplateTypeClass(Slope08); // TEMPLATE_SLOPE8 + (void)new TemplateTypeClass(Slope09); // TEMPLATE_SLOPE9 + (void)new TemplateTypeClass(Slope10); // TEMPLATE_SLOPE10 + (void)new TemplateTypeClass(Slope11); // TEMPLATE_SLOPE11 + (void)new TemplateTypeClass(Slope12); // TEMPLATE_SLOPE12 + (void)new TemplateTypeClass(Slope13); // TEMPLATE_SLOPE13 + (void)new TemplateTypeClass(Slope14); // TEMPLATE_SLOPE14 + (void)new TemplateTypeClass(Slope15); // TEMPLATE_SLOPE15 + (void)new TemplateTypeClass(Slope16); // TEMPLATE_SLOPE16 + (void)new TemplateTypeClass(Slope17); // TEMPLATE_SLOPE17 + (void)new TemplateTypeClass(Slope18); // TEMPLATE_SLOPE18 + (void)new TemplateTypeClass(Slope19); // TEMPLATE_SLOPE19 + (void)new TemplateTypeClass(Slope20); // TEMPLATE_SLOPE20 + (void)new TemplateTypeClass(Slope21); // TEMPLATE_SLOPE21 + (void)new TemplateTypeClass(Slope22); // TEMPLATE_SLOPE22 + (void)new TemplateTypeClass(Slope23); // TEMPLATE_SLOPE23 + (void)new TemplateTypeClass(Slope24); // TEMPLATE_SLOPE24 + (void)new TemplateTypeClass(Slope25); // TEMPLATE_SLOPE25 + (void)new TemplateTypeClass(Slope26); // TEMPLATE_SLOPE26 + (void)new TemplateTypeClass(Slope27); // TEMPLATE_SLOPE27 + (void)new TemplateTypeClass(Slope28); // TEMPLATE_SLOPE28 + (void)new TemplateTypeClass(Slope29); // TEMPLATE_SLOPE29 + (void)new TemplateTypeClass(Slope30); // TEMPLATE_SLOPE30 + (void)new TemplateTypeClass(Slope31); // TEMPLATE_SLOPE31 + (void)new TemplateTypeClass(Slope32); // TEMPLATE_SLOPE32 + (void)new TemplateTypeClass(Slope33); // TEMPLATE_SLOPE33 + (void)new TemplateTypeClass(Slope34); // TEMPLATE_SLOPE34 + (void)new TemplateTypeClass(Slope35); // TEMPLATE_SLOPE35 + (void)new TemplateTypeClass(Slope36); // TEMPLATE_SLOPE36 + (void)new TemplateTypeClass(Slope37); // TEMPLATE_SLOPE37 + (void)new TemplateTypeClass(Slope38); // TEMPLATE_SLOPE38 + (void)new TemplateTypeClass(Road01); // TEMPLATE_ROAD1 + (void)new TemplateTypeClass(Road02); // TEMPLATE_ROAD2 + (void)new TemplateTypeClass(Road03); // TEMPLATE_ROAD3 + (void)new TemplateTypeClass(Road04); // TEMPLATE_ROAD4 + (void)new TemplateTypeClass(Road05); // TEMPLATE_ROAD5 + (void)new TemplateTypeClass(Road06); // TEMPLATE_ROAD6 + (void)new TemplateTypeClass(Road07); // TEMPLATE_ROAD7 + (void)new TemplateTypeClass(Road08); // TEMPLATE_ROAD8 + (void)new TemplateTypeClass(Road09); // TEMPLATE_ROAD9 + (void)new TemplateTypeClass(Road10); // TEMPLATE_ROAD10 + (void)new TemplateTypeClass(Road11); // TEMPLATE_ROAD11 + (void)new TemplateTypeClass(Road12); // TEMPLATE_ROAD12 + (void)new TemplateTypeClass(Road13); // TEMPLATE_ROAD13 + (void)new TemplateTypeClass(Road14); // TEMPLATE_ROAD14 + (void)new TemplateTypeClass(Road15); // TEMPLATE_ROAD15 + (void)new TemplateTypeClass(Road16); // TEMPLATE_ROAD16 + (void)new TemplateTypeClass(Road17); // TEMPLATE_ROAD17 + (void)new TemplateTypeClass(Road18); // TEMPLATE_ROAD18 + (void)new TemplateTypeClass(Road19); // TEMPLATE_ROAD19 + (void)new TemplateTypeClass(Road20); // TEMPLATE_ROAD20 + (void)new TemplateTypeClass(Road21); // TEMPLATE_ROAD21 + (void)new TemplateTypeClass(Road22); // TEMPLATE_ROAD22 + (void)new TemplateTypeClass(Road23); // TEMPLATE_ROAD23 + (void)new TemplateTypeClass(Road24); // TEMPLATE_ROAD24 + (void)new TemplateTypeClass(Road25); // TEMPLATE_ROAD25 + (void)new TemplateTypeClass(Road26); // TEMPLATE_ROAD26 + (void)new TemplateTypeClass(Road27); // TEMPLATE_ROAD27 + (void)new TemplateTypeClass(Road28); // TEMPLATE_ROAD28 + (void)new TemplateTypeClass(Road29); // TEMPLATE_ROAD29 + (void)new TemplateTypeClass(Road30); // TEMPLATE_ROAD30 + (void)new TemplateTypeClass(Road31); // TEMPLATE_ROAD31 + (void)new TemplateTypeClass(Road32); // TEMPLATE_ROAD32 + (void)new TemplateTypeClass(Road33); // TEMPLATE_ROAD33 + (void)new TemplateTypeClass(Road34); // TEMPLATE_ROAD34 + (void)new TemplateTypeClass(Road35); // TEMPLATE_ROAD35 + (void)new TemplateTypeClass(Road36); // TEMPLATE_ROAD36 + + /* + ** Separate out the list of new operator calls. Watcom bombs + ** if they are kept together. + */ + _Watcom_Ugh_Hack(); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Land_Type -- Determines land type from template and icon number. * + * * + * This routine will convert the specified icon number into the appropriate land type. The * + * land type can be determined from the embedded colors in the "control template" section * + * of the original art file. This control information is encoded into the icon data file * + * to be retrieved and interpreted as the program sees fit. The engine only recognizes * + * the first 16 colors as control colors, so the control map color value serves as an * + * index into a simple lookup table. * + * * + * INPUT: icon -- The icon number within this template that is to be examined and used * + * to determine the land type. * + * * + * OUTPUT: Returns with the land type that corresponds to the icon number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/12/1995 JLB : Created. * + *=============================================================================================*/ +LandType TemplateTypeClass::Land_Type(int icon) const +{ + IconsetClass const * icontrol = (IconsetClass const *)Get_Image_Data(); + + if (icontrol != NULL) { + unsigned char const * map = icontrol->Control_Map(); + if (map != NULL) { + static LandType _land[16] = { + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, + LAND_CLEAR, // Clear + LAND_CLEAR, + LAND_CLEAR, + LAND_BEACH, // Beach + LAND_CLEAR, + LAND_ROCK, // Rock + LAND_ROAD, // Road + LAND_WATER, // Water + LAND_RIVER, // River + LAND_CLEAR, + LAND_CLEAR, + LAND_ROUGH, // Rough + LAND_CLEAR, + }; + + return(_land[map[icon % (icontrol->Map_Width() * icontrol->Map_Height())]]); + } + } + return(LAND_CLEAR); +} + + +/*********************************************************************************************** + * TemplateTypeClass::From_Name -- Determine template from ASCII name. * + * * + * This routine is used to determine the template number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the template. * + * * + * OUTPUT: Returns with the template number. If the name had no match, * + * then returns with TEMPLATE_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +TemplateType TemplateTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(TEMPLATE_NONE); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the template map and build an * + * occupation list. This list is used to render a template cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the template occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 12/12/1995 JLB : Optimized for direct access to iconset data. * + *=============================================================================================*/ +short const * TemplateTypeClass::Occupy_List(bool) const +{ + static short _occupy[13*8+5]; + short * ptr; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + ptr = &_occupy[0]; + for (int index = 0; index < Width * Height; index++) { + if (*map++ != 0xFF) { + *ptr++ = (index % Width) + ((index / Width)*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + + return((short const *)&_occupy[0]); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Init -- Loads graphic data for templates. * + * * + * This routine loads the template graphic data for all the template * + * type supported for the specified theater. This routine is called * + * whenever the theater for the scenario is first determined. * + * * + * INPUT: theater -- The theater that the template data is to be * + * loaded for. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk! * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 06/02/1994 JLB : Only handles iconset loading now (as it should). * + *=============================================================================================*/ +void TemplateTypeClass::Init(TheaterType theater) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + void const * ptr; // Working loaded iconset pointer. + + for (TemplateType index = TEMPLATE_FIRST; index < TEMPLATE_COUNT; index++) { + TemplateTypeClass const & tplate = As_Reference(index); + + ((void const *&)tplate.ImageData) = NULL; + if (tplate.Theater & (1<renderwidth, hdImage->renderheight); + ((void const*&)tplate.HDImageData) = hdImage; + } + else + { + // If no HD assets, load the legacy assets into a texture. + char tmp[512]; + sprintf(tmp, "icon_%s", fullname); + ((void const*&)tplate.HDImageData) = Load_Stamp(fullname, tplate.ImageData); + } + } + } +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TemplateTypeClass::Display -- Displays a generic representation of template. * + * * + * This routine is used to display a generic view of the template * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int w,h; + int index; + bool scale; // Should the template be half sized? + + w = Bound(Width, 1, 13); + h = Bound(Height, 1, 8); + scale = (w > 3 || h > 3); + if (scale) { + x -= (w * ICON_PIXEL_W) / 4; + y -= (h * ICON_PIXEL_H) / 4; + } else { + x -= (w * ICON_PIXEL_W) / 2; + y -= (h * ICON_PIXEL_H) / 2; + } + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IconsetClass const * iconset = (IconsetClass const *)Get_Image_Data(); + unsigned char const * map = iconset->Map_Data(); + + for (index = 0; index < w*h; index++) { + if (map[index] != 0xFF) { +// jmarshall - even if using legacy data, always use hdimage. + HidPage.Draw_Stamp(Get_HDImage_Data(), index, 0, 0, NULL, WINDOW_MAIN); + // Disabled legacy blit code. + //Buffer_Enable_HD_Texture(true); // At this point, Draw_Stamp creates a 32bit image. + //if (scale) { + // HidPage.Scale((*LogicPage), 0, 0, + // x + ((index % w)*(ICON_PIXEL_W/2)), + // y + ((index / w)*(ICON_PIXEL_H/2)), + // ICON_PIXEL_W, ICON_PIXEL_H, + // ICON_PIXEL_W/2, ICON_PIXEL_H/2, (char *)NULL); + // + //} else { + // HidPage.Blit((*LogicPage), 0, 0, x + ((index % w)*(ICON_PIXEL_W)), + // y + ((index / w)*(ICON_PIXEL_H)), ICON_PIXEL_W, ICON_PIXEL_H); + //} + //Buffer_Enable_HD_Texture(false); +// jmarshall end + } + } +} + + +/*********************************************************************************************** + * TemplateTypeClass::Prep_For_Add -- Prepares to add template to scenario. * + * * + * This routine prepares a list of template objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a template object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + * 05/28/1994 JLB : Only handles real templates now. * + * 06/04/1994 JLB : Uses map editing interface functions. * + *=============================================================================================*/ +void TemplateTypeClass::Prep_For_Add(void) +{ + for (TemplateType index = TEMPLATE_CLEAR1; index < TEMPLATE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TemplateTypeClass::Create_And_Place -- Creates and places a template object on the map. * + * * + * This support routine is used by the scenario editor to add a template object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the template object. * + * * + * OUTPUT: bool; Was the template object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TemplateTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TemplateClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateTypeClass::Create_One_Of -- Creates an object of this template type. * + * * + * This routine will create an object of this type. For certain template objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a template at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this template type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TemplateTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TemplateClass(Type, -1)); +} + + +/*********************************************************************************************** + * TemplateTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * TemplateTypeClass::As_Reference -- Fetches a reference to the template specified. * + * * + * This will return a reference to the TemplateTypeClass requested. * + * * + * INPUT: type -- The template type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the template type class specified. * + * * + * WARNINGS: Be sure to pass a valid type parameter. This routine doesn't check it for * + * legality. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +TemplateTypeClass & TemplateTypeClass::As_Reference(TemplateType type) +{ + return(*TemplateTypes.Ptr(type)); +} + + +COORDINATE TemplateTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/code/redalert/cdfile.cpp b/code/redalert/cdfile.cpp new file mode 100644 index 0000000..a34776e --- /dev/null +++ b/code/redalert/cdfile.cpp @@ -0,0 +1,746 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDFILE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : CDFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * CDFileClass::Open -- Opens the file object -- with path search. * + * CDFileClass::Open -- Opens the file wherever it can be found. * + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * harderr_handler -- Handles hard DOS errors. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "cdfile.h" +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +/* +** Pointer to the first search path record. +*/ +CDFileClass::SearchDriveType * CDFileClass::First = 0; + +int CDFileClass::CurrentCDDrive = 0; +int CDFileClass::LastCDDrive = 0; +char CDFileClass::RawPath[512] = {0}; + +CDFileClass::CDFileClass(char const *filename) : + IsDisabled(false) +{ + CDFileClass::Set_Name(filename); +// memset (RawPath, 0, sizeof(RawPath)); +} + + +CDFileClass::CDFileClass(void) : + IsDisabled(false) +{ +} +extern int Get_CD_Index (int cd_drive, int timeout); + +/*********************************************************************************************** + * harderr_handler -- Handles hard DOS errors. * + * * + * This routine will handle the low level DOS error trapper. Instead of displaying the * + * typical "Abort, Retry, Ignore" message, it simply returns with the failure code. The * + * cause of the error will fail. The likely case would be with disk I/O. * + * * + * INPUT: * + * * + * OUTPUT: Return the failure code. * + * * + * WARNINGS: Do no processing in this routine that could possibly generate another * + * hard error condition. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +int harderr_handler(unsigned int , unsigned int , unsigned int *) +#else +int harderr_handler(unsigned int , unsigned int , unsigned int __far *) +#endif +{ + return(0); // _HARDERR_FAIL); +} + + +/*********************************************************************************************** + * Is_Disk_Inserted -- Checks to see if a disk is inserted in specified drive. * + * * + * This routine will examine the drive specified to see if there is a disk inserted. It * + * can be used for floppy drives as well as for the CD-ROM. * + * * + * INPUT: disk -- The drive number to examine. 0=A, 1=B, etc. * + * * + * OUTPUT: bool; Is a disk inserted into the specified drive? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +int cdecl Is_Disk_Inserted(int disk) +{ +#ifndef OBSOLETE + disk; + return true; +#if (0) + struct find_t fb; + char scan[] = "?:\\*.*"; + + #ifndef WIN32 + _harderr(harderr_handler); // BG: Install hard error handler + #endif + + scan[0] = (char)('A' + disk); + return(_dos_findfirst(scan, _A_SUBDIR, &fb) == 0); +#endif +#else + struct { + struct { + char Length; + char Unit; + char Function; + char Status; + char Reserved[8]; + } ReqHdr; + char MediaDescriptor; // Media descriptor byte from BPB. + void *Transfer; // Pointer to transfer address block. + short Length; // Number of bytes to transfer. + short Sector; // Starting sector number. + void *Volume; // Pointer to requested volume. + } IOCTLI; + char status[5]; + + memset(IOCTLI, 0, sizeof(IOCTLI)); + IOCTLI.ReqHdr.Length = 26; + IOCTLI.ReqHdr.Unit = 0; // First CD-ROM controlled by this driver. + //IOCTLI.ReqHdr.Unit = 11; // Hard coded for K: + IOCTLI.ReqHdr.Function = 3; // IOCTL read + IOCTLI.Transfer = &status[0]; + IOCTLI.Length = sizeof(status); + status[0] = 6; // Fetch device status code. + _AX = 0x440D; + _CX = 0x0003; + geninterrupt(0x21); + return(!(_AX & (1<<11))); +#endif +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file object -- with path search. * + * * + * This will open the file object, but since the file object could have been constructed * + * with a pathname, this routine will try to find the file first. For files opened for * + * writing, then use the existing filename without performing a path search. * + * * + * INPUT: rights -- The access rights to use when opening the file * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(int rights) +{ + return(BufferIOFileClass::Open(rights)); +} +/*********************************************************************************************** + * CDFC::Refresh_Search_Drives -- Updates the search path when a CD changes or is added * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:01AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Refresh_Search_Drives (void) +{ + Clear_Search_Drives(); + Set_Search_Drives(RawPath); +} + +#if 0 +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = false; + int empty = false; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + char const * ptr = strtok(pathlist, ";"); + while (ptr) { + char path[PATH_MAX]; // Working path buffer. + SearchDriveType *srch; // Working pointer to path object. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + */ + if (strncmp(path, "?:", 2) == 0) { + #ifndef WIN32 + GetCDClass temp; + int cd = temp.GetCDDrive(); + #else + int cd = 10; + #endif + found = cd; + empty = !Is_Disk_Inserted(cd); + if (!found || empty) goto nextpath; + path[0] = (char)('A' + cd); + } + + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + if (srch) { + found = true; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } + } + + /* + ** Find the next path string and resubmit. + */ +nextpath: + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} +#endif + + +/*********************************************************************************************** + * CDFileClass::Set_Search_Drives -- Sets a list of search paths for file access. * + * * + * This routine sets up a list of search paths to use when accessing files. The path list * + * is scanned if the file could not be found in the current directory. This is the primary * + * method of supporting CD-ROM drives, but is also useful for scanning network and other * + * directories. The pathlist as passed to this routine is of the same format as the path * + * list used by DOS -- paths are separated by semicolons and need not end in an antivirgule.* + * * + * If a path entry begins with "?:" then the question mark will be replaced with the first * + * CD-ROM drive letter available. If there is no CD-ROM driver detected, then this path * + * entry will be ignored. By using this feature, you can always pass the CD-ROM path * + * specification to this routine and it will not break if the CD-ROM is not loaded (as in * + * the case during development). * + * * + * Here is an example path specification: * + * * + * Set_Search_Drives("DATA;?:\DATA;F:\PROJECT\DATA"); * + * * + * In this example, the current directory will be searched first, followed by a the * + * subdirectory "DATA" located off of the current directory. If not found, then the CD-ROM * + * will be searched in a directory called "\DATA". If not found or the CD-ROM is not * + * present, then it will look to the hard coded path of "F:\PROJECTS\DATA" (maybe a * + * network?). If all of these searches fail, the file system will default to the current * + * directory and let the normal file error system take over. * + * * + * INPUT: pathlist -- Pointer to string of path specifications (separated by semicolons) * + * that will be used to search for files. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + * 05/21/1996 ST : Modified to recognise multiple CD drives * + *=============================================================================================*/ +int CDFileClass::Set_Search_Drives(char * pathlist) +{ + int found = FALSE; + int empty = FALSE; + + /* + ** If there is no pathlist to add, then just return. + */ + if (!pathlist) return(0); + + /* + ** Save the path as it was passed in so we can parse it again later. + ** Check for the case where RawPath was passed in. + */ + if (pathlist != RawPath) { + strcat (RawPath, ";"); + strcat (RawPath, pathlist); + } + + char const * ptr = strtok(pathlist, ";"); + while (ptr != NULL) { + if (strlen(ptr) > 0) { + + char path[MAX_PATH]; // Working path buffer. + + /* + ** Fixup the path to be legal. Legal is defined as all that is necessary to + ** create a pathname is to append the actual filename submitted to the + ** file system. This means that it must have either a trailing ':' or '\' + ** character. + */ + strcpy(path, ptr); + switch (path[strlen(path)-1]) { + case ':': + case '\\': + break; + + default: + strcat(path, "\\"); + break; + } + + /* + ** If there is a drive letter specified, and this drive letter is '?', then it should + ** be substituted with the CD-ROM drive letter. In the case of no CD-ROM attached, then + ** merely ignore this path entry. + ** Adds an extra entry for each CD drive in the system that has a C&C disc inserted. + ** ST - 5/21/96 4:40PM + */ + if (strncmp(path, "?:", 2) == 0) { + if (CurrentCDDrive) { + found = true; + + /* + ** If the drive has a C&C CD in it then add it to the path + */ + if (Get_CD_Index(CurrentCDDrive, 2*60) >= 0) { + path[0] = (char)(CurrentCDDrive + 'A'); + Add_Search_Drive(path); + } + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + continue; + } + + found = true; + Add_Search_Drive(path); + } + + /* + ** Find the next path string and resubmit. + */ + ptr = strtok(NULL, ";"); + } + if (!found) return(1); + if (empty) return(2); + return(0); +} + + +/*********************************************************************************************** + * CDFC::Add_Search_Drive -- Add a new path to the search path list * + * * + * * + * * + * INPUT: path * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 10:12AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Add_Search_Drive(char *path) +{ + SearchDriveType *srch; // Working pointer to path object. + /* + ** Allocate a record structure. + */ + srch = new SearchDriveType; + + /* + ** Attach the path to this structure. + */ + srch->Path = strdup(path); + srch->Next = NULL; + + /* + ** Attach this path record to the end of the path chain. + */ + if (!First) { + First = srch; + } else { + SearchDriveType * chain = First; + + while (chain->Next) { + chain = (SearchDriveType *)chain->Next; + } + chain->Next = srch; + } +} + + +/*********************************************************************************************** + * CDFC::Set_CD_Drive -- sets the current CD drive letter * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 9:39AM ST : Created * + *=============================================================================================*/ +void CDFileClass::Set_CD_Drive (int drive) +{ + LastCDDrive = CurrentCDDrive; + CurrentCDDrive = drive; +} + + +/*********************************************************************************************** + * CDFileClass::Clear_Search_Drives -- Removes all record of a search path. * + * * + * Use this routine to clear out any previous path(s) set with Set_Search_Drives() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void CDFileClass::Clear_Search_Drives(void) +{ + SearchDriveType * chain; // Working pointer to path chain. + + chain = First; + while (chain) { + SearchDriveType *next; + + next = (SearchDriveType *)chain->Next; + if (chain->Path) { + free((char *)chain->Path); + } + delete chain; + + chain = next; + } + First = 0; +} + + +/*********************************************************************************************** + * CDFileClass::Set_Name -- Performs a multiple directory scan to set the filename. * + * * + * This routine will scan all the directories specified in the path list and if the file * + * was found in one of the directories, it will set the filename to a composite of the * + * correct directory and the filename. It is used to allow path searching when searching * + * for files. Typical use is to support CD-ROM drives. This routine examines the current * + * directory first before scanning through the path list. If after scanning the entire * + * path list, the file still could not be found, then the file object's name is set with * + * just the raw filename as passed to this routine. * + * * + * INPUT: filename -- Pointer to the filename to set as the name of this file object. * + * * + * OUTPUT: Returns a pointer to the final and complete filename of this file object. This * + * may have a path attached to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +char const * CDFileClass::Set_Name(char const *filename) +{ + /* + ** Try to find the file in the current directory first. If it can be found, then + ** just return with the normal file name setting process. Do the same if there is + ** no multi-drive search path. + */ + BufferIOFileClass::Set_Name(filename); + if (IsDisabled || !First || BufferIOFileClass::Is_Available()) return(File_Name()); + + /* + ** Attempt to find the file first. Check the current directory. If not found there, then + ** search all the path specifications available. If it still can't be found, then just + ** fall into the normal raw file filename setting system. + */ + SearchDriveType * srch = First; + + while (srch) { + char path[_MAX_PATH]; + + /* + ** Build a pathname to search for. + */ + strcpy(path, srch->Path); + strcat(path, filename); + + /* + ** Check to see if the file could be found. The low level Is_Available logic will + ** prompt if necessary when the CD-ROM drive has been removed. In all other cases, + ** it will return false and the search process will continue. + */ + BufferIOFileClass::Set_Name(path); + if (BufferIOFileClass::Is_Available()) { + return(File_Name()); + } + + /* + ** It wasn't found, so try the next path entry. + */ + srch = (SearchDriveType *)srch->Next; + } + + /* + ** At this point, all path searching has failed. Just set the file name to the + ** plain text passed to this routine and be done with it. + */ + BufferIOFileClass::Set_Name(filename); + return(File_Name()); +} + + +/*********************************************************************************************** + * CDFileClass::Open -- Opens the file wherever it can be found. * + * * + * This routine is similar to the RawFileClass open except that if the file is being * + * opened only for READ access, it will search all specified directories looking for the * + * file. If after a complete search the file still couldn't be found, then it is opened * + * using the normal BufferIOFileClass system -- resulting in normal error procedures. * + * * + * INPUT: filename -- Pointer to the override filename to supply for this file object. It * + * would be the base filename (sans any directory specification). * + * * + * rights -- The access rights to use when opening the file. * + * * + * OUTPUT: bool; Was the file opened successfully? If so then the filename may be different * + * than requested. The location of the file can be determined by examining the * + * filename of this file object. The filename will contain the complete * + * pathname used to open the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int CDFileClass::Open(char const *filename, int rights) +{ + CDFileClass::Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (!filename) { + Error(ENOENT, false); + } + + /* + ** If writing is requested, then multiple drive searching is not performed. + */ + if (IsDisabled || rights == WRITE) { + + BufferIOFileClass::Set_Name( filename ); + return( BufferIOFileClass::Open( rights ) ); + } + + /* + ** Perform normal multiple drive searching for the filename and open + ** using the normal procedure. + */ + Set_Name(filename); + return(BufferIOFileClass::Open(rights)); +} + + +const char *CDFileClass::Get_Search_Path(int index) +{ + if (First == NULL) { + return NULL; + } + + SearchDriveType *sd = First; + + for (int i = 0; i <= index; i++) { // We want to loop once, even if index==0 + + if (i == index) { + return sd->Path; + } + + sd = (SearchDriveType *)sd->Next; + if (sd == NULL) { + return NULL; + } + } + + return NULL; +} + + + +#ifdef NEVER +/* +** Get the drive letters if the CD's online */ +*/ +WORD cdecl GetCDDrive(VOID) +{ + _ES = FP_SEG(&cdDrive[0]); + _BX = FP_OFF(&cdDrive[0]); + _AX = 0x150d; + geninterrupt(0x2F); + return((WORD)(*cdDrive)); +} +#endif + +#if 0 +int Get_CD_Drive(void) +{ +#ifdef WIN32 + return(10); +#else + +#ifdef NEVER + for (int index = 0; index < 26; index++) { + union REGS regs; + + regs.w.ax = 0x150B; + regs.w.bx = 0; + regs.w.cx = index; + int386(0x2F, ®s, ®s); + if (regs.w.bx == 0xADAD) { + return(index); + } + } + return(0); +#else + GetCDClass temp; + return(temp.GetCDDrive()); +#endif +#endif +} + +#endif diff --git a/code/redalert/cdfile.h b/code/redalert/cdfile.h new file mode 100644 index 0000000..57f9e20 --- /dev/null +++ b/code/redalert/cdfile.h @@ -0,0 +1,116 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CDFILE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood LIbrary * + * * + * File Name : CDFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 18, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CDFILE_H +#define CDFILE_H + +#include +#include "bfiofile.h" + +/* +** This class is derived from the BufferIOFileClass. This class adds the functionality of searching +** across multiple directories or drives. It is designed for the typical case of a CD-ROM game +** were some data exists in the current directory (hard drive) and the rest exists on the CD-ROM. +** Searching for the file occurs by first examining the current directory. If the file does not +** exist there, then all the paths available are examined in turn until the file can be found. +** For opening files to write, only the current directory is examined. The directory search order +** is controlled by the path list as submitted to Set_Search_Drives(). The format of the path +** string is the same as the DOS path string. +*/ +class CDFileClass : public BufferIOFileClass +{ + public: + CDFileClass(char const *filename); + CDFileClass(void); + virtual ~CDFileClass(void) {}; + + virtual char const * Set_Name(char const *filename); + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + + void Searching(int on) {IsDisabled = !on;}; + + static bool Is_There_Search_Drives(void) {return(First != NULL);}; + static int Set_Search_Drives(char * pathlist); + static void Add_Search_Drive(char *path); + static void Clear_Search_Drives(void); + static void Refresh_Search_Drives(void); + static void Set_CD_Drive(int drive); + static int Get_CD_Drive(void) {return(CurrentCDDrive);}; + static int Get_Last_CD_Drive(void) {return(LastCDDrive);}; + + // RawPath will overflow if we keep setting the path. ST - 1/23/2019 11:12AM + static void Reset_Raw_Path(void) { RawPath[0] = 0; } + + // Need to access the paths. ST - 3/15/2019 2:14PM + static const char *Get_Search_Path(int index); + + private: + + /* + ** Is multi-drive searching disabled for this file object? + */ + unsigned IsDisabled:1; + + /* + ** This is the control record for each of the drives specified in the search + ** path. There can be many such search paths available. + */ + typedef struct { + void * Next; // Pointer to next search record. + char const * Path; // Pointer to path string. + } SearchDriveType; + + /* + ** This points to the first path record. + */ + static SearchDriveType * First; + /* + ** This is a copy of the unparsed search path list + */ + static char RawPath[512]; + + /* + ** The drive letter of the current cd drive + */ + static int CurrentCDDrive; + + /* + ** The drive letter of the last used CD drive + */ + static int LastCDDrive; +}; + +#endif + diff --git a/code/redalert/cell.cpp b/code/redalert/cell.cpp new file mode 100644 index 0000000..c3be89a --- /dev/null +++ b/code/redalert/cell.cpp @@ -0,0 +1,3310 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CELL.CPP 4 3/14/97 1:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * CellClass::CellClass -- Constructor for cell objects. * + * CellClass::Cell_Building -- Return with building at specified cell. * + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * CellClass::Cell_Coord -- Returns the coordinate of this cell. * + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit. * + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (over* + * CellClass::Overlap_Unit -- Marks cell as being overlapped by unit. * + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * CellClass::Read -- Reads a particular cell value from a save game file. * + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * CellClass::Reduce_Tiberium -- Reduces the tiberium in the cell by the amount specified. * + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * CellClass::Reserve_Cell -- Marks a cell as being occupied by the specified unit ID. * + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smooth. * + * CellClass::Wall_Update -- Updates the imagery for wall objects in cell. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/2/2019 2:50PM +*/ +#include "SidebarGlyphx.h" + +std::vector renderedFrameObjects; + +bool HasRenderedObject(const ObjectClass* technoClass) { + return std::find(renderedFrameObjects.begin(), renderedFrameObjects.end(), technoClass) != renderedFrameObjects.end(); +} + +/*********************************************************************************************** + * CellClass::CellClass -- Constructor for cell objects. * + * * + * A cell object is constructed into an empty state. It contains no specific objects, * + * templates, or overlays. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/09/1994 JLB : Created. * + * 02/20/1996 JLB : Uses initializer list. * + *=============================================================================================*/ +CellClass::CellClass(void) : + ID(Map.ID(this)), + IsPlot(false), + IsCursorHere(false), + IsMapped(false), + IsVisible(false), + IsWaypoint(false), + IsRadarCursor(false), + IsFlagged(false), + IsToShroud(false), + Jammed(0), + Trigger(NULL), + TType(TEMPLATE_NONE), + TIcon(0), + Overlay(OVERLAY_NONE), + OverlayData(0), + Smudge(SMUDGE_NONE), + SmudgeData(0), + Owner(HOUSE_NONE), + InfType(HOUSE_NONE), + OccupierPtr(0), + Land(LAND_CLEAR), + OverrideLand(LAND_NONE), + IsMappedByPlayerMask(0), + IsVisibleByPlayerMask(0) +{ + for (int zone = MZONE_FIRST; zone < MZONE_COUNT; zone++) { + Zones[zone] = 0; + } + Flag.Composite = 0; + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + Overlapper[index] = 0; + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Color -- Determine what radar color to use for this cell. * + * * + * Use this routine to determine what radar color to render a radar * + * pixel with. This routine is called many many times to render the * + * radar map, so it must be fast. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the color to display the radar pixel with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/31/1994 JLB : Takes into account any stealth characteristics of object. * + *=============================================================================================*/ +int CellClass::Cell_Color(bool override) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + BuildingClass * object = Cell_Building(); + if (object && !object->Class->IsInvisible) { + return(ColorRemaps[object->House->RemapColor].Bar); + } + + if (override) { + return(TBLACK); + } + if (LastTheater == THEATER_SNOW) { + return(::SnowColor[Land_Type()]); + } else { + return(::GroundColor[Land_Type()]); + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Techno -- Return with the unit/building at specified cell. * + * * + * Returns an object located in the cell. If there is a * + * building present, it returns a pointer to that, otherwise it returns * + * a pointer to one of the units there. If nothing is present in the * + * specified cell, then it returns NULL. * + * * + * INPUT: x,y -- Coordinate offset (from upper left corner) to use as an aid in selecting * + * the desired object within the cell. * + * * + * OUTPUT: Returns a pointer to a building or unit located in cell. If * + * nothing present, just returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +TechnoClass * CellClass::Cell_Techno(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object; + COORDINATE click; // Coordinate of click relative to cell corner. + TechnoClass * close = NULL; + long distance = 0; // Recorded closest distance. + + /* + ** Create a coordinate value that represent the pixel location within the cell. This is + ** actually the lower significant bits (leptons) of a regular coordinate value. + */ + click = XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y)); + + if (Cell_Occupier()) { + object = Cell_Occupier(); + while (object) { + if (object->Is_Techno()) { + COORDINATE coord = Coord_Fraction(object->Center_Coord()); + long dist = Distance(coord, click); + if (!close || dist < distance) { + close = (TechnoClass *)object; + distance = dist; + } + } + object = object->Next; + } + } + return(close); +} + + +/*************************************************************************** + * CellClass::Cell_Find_Object -- Returns ptr to RTTI type occupying cell * + * * + * INPUT: RTTIType the RTTI type we are searching for * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 PWG : Created. * + * 06/12/1995 JLB : Returns object class pointer. * + *=========================================================================*/ +ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(rtti != RTTI_NONE); + + ObjectClass * object = Cell_Occupier(); + + while (object != NULL) { + if (object->What_Am_I() == rtti) { + return(object); + } + object = object->Next; + } + return(NULL); +} + + +/*********************************************************************************************** + * CellClass::Cell_Building -- Return with building at specified cell. * + * * + * Given a cell, determine if there is a building associated * + * and return with a pointer to this building. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building associated with the * + * cell. If there is no building associated, then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +BuildingClass * CellClass::Cell_Building(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((BuildingClass *)Cell_Find_Object(RTTI_BUILDING)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Terrain -- Determines terrain object in cell. * + * * + * This routine is used to determine the terrain object (if any) that * + * overlaps this cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object that overlaps * + * this cell. If there is no terrain object present, then NULL * + * is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass * CellClass::Cell_Terrain(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((TerrainClass *)Cell_Find_Object(RTTI_TERRAIN)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Object -- Returns with clickable object in cell. * + * * + * This routine is used to determine which object is to be selected * + * by a player click upon the cell. Not all objects that overlap the * + * cell are selectable by the player. This routine sorts out which * + * is which and returns with the appropriate object pointer. * + * * + * INPUT: x,y -- Coordinate (from upper left corner of cell) to use as a guide when * + * selecting the object within the cell. This plays a role in those cases * + * where several objects (such as infantry) exist within the same cell. * + * * + * OUTPUT: Returns with pointer to the object clickable within the * + * cell. NULL is returned if there is no clickable object * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * CellClass::Cell_Object(int x, int y) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * ptr; + + /* + ** Hack so that aircraft landed on helipads can still be selected if directly + ** clicked on. + */ + ptr = (ObjectClass *)Cell_Find_Object(RTTI_AIRCRAFT); + if (ptr) { + return(ptr); + } + + ptr = Cell_Techno(x, y); + if (ptr) { + return(ptr); + } + ptr = Cell_Terrain(); + if (ptr) return(ptr); + return(ptr); +} + + +/*********************************************************************************************** + * CellClass::Redraw_Objects -- Redraws all objects overlapping this cell. * + * * + * This is a low level routine that marks all objects that overlap this * + * cell to be redrawn. It is necessary to call this routine whenever * + * the underlying icon has to be redrawn. * + * * + * INPUT: forced -- Should this redraw be forced even if flags * + * indicate that it would be redundant? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/20/1994 JLB : Simplified to use object pointers. * + * 12/24/1994 JLB : Only checks if cell is in view and not flagged already. * + *=============================================================================================*/ +void CellClass::Redraw_Objects(bool forced) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + + if (Map.In_View(cell) && (forced || !Map.Is_Cell_Flagged(cell))) { + + /* + ** Flag the icon to be redrawn. + */ + Map.Flag_Cell(cell); + + /* + ** Flag the main object in the cell to be redrawn. + */ + if (Cell_Occupier() != NULL) { + ObjectClass * optr = Cell_Occupier(); + while (optr != NULL && optr->IsActive) { + +#ifdef SORTDRAW + if (optr->Is_Techno() && ((TechnoClass *)optr)->Visual_Character() != VISUAL_NORMAL) { + optr->Mark(MARK_CHANGE); + } +#else + optr->Mark(MARK_CHANGE); +#endif + if (optr->Next != NULL && !optr->Next->IsActive) { + optr->Next = NULL; + } + optr = optr->Next; + } + } + +#ifdef SORTDRAW + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index]) { + assert(Overlapper[index]->IsActive); + if (Overlapper[index]->Is_Techno() && ((TechnoClass *)Overlapper[index])->Visual_Character() != VISUAL_NORMAL) { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#else + /* + ** Flag any overlapping object in this cell to be redrawn. + */ + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + if (!Overlapper[index]->IsActive) { + Overlapper[index] = NULL; + } else { + Overlapper[index]->Mark(MARK_CHANGE); + } + } + } +#endif + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Build -- Determines if cell can be built upon. * + * * + * This determines if the cell can become a proper foundation for * + * building placement. * + * * + * INPUT: loco -- The locomotion of the object trying to consider if this cell is * + * generally clear. Buildings use the value of SPEED_NONE. * + * * + * OUTPUT: bool; Is this cell generally clear (usually for building purposes)? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1994 JLB : Created. * + * 06/25/1996 JLB : Handles different locomotion types. * + * 10/05/1996 JLB : Checks for crushable walls and crushable object. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Build(SpeedType loco) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** During scenario initialization, passability is always guaranteed. + */ + if (ScenarioInit) return(true); + + /* + ** If there is an object there, then don't allow building. + */ + if (Cell_Object() != NULL) { + return(false); + } + + /* + ** Prevents a building from being placed over a flag object. + */ +#ifdef FIXIT_FLAG_CHECK + if (IsFlagged) { + return(false); + } +#endif + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes. In normal game mode, all overlays are not buildable. + */ + if (Overlay != OVERLAY_NONE && (Overlay == OVERLAY_FLAG_SPOT || !Debug_Map || OverlayTypeClass::As_Reference(Overlay).IsWall)) { + return(false); + } + + /* + ** Building over a bib is not allowed. + */ + if (Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Smudge).IsBib /* && Owner != HOUSE_NONE*/) { + return(false); + } + + /* + ** Building on certain kinds of terrain is prohibited -- bridges in particular. + ** If the locomotion type is SPEED_NONE, then this check is presumed to be + ** for the purposes of building. + */ + if (loco == SPEED_NONE) { + if (Is_Bridge_Here()) { + return(false); + } + + return(::Ground[Land_Type()].Build); + + } else { + + if (::Ground[Land_Type()].Cost[loco] == fixed(0)) { +// if (::Ground[Land_Type()].Cost[SPEED_TRACK] == fixed(0)) { + return(false); + } + return(true); + } +} + + +/*********************************************************************************************** + * CellClass::Recalc_Attributes -- Recalculates the ground type attributes for the cell. * + * * + * This routine recalculates the ground type in the cell. The speeds the find path * + * algorithm and other determinations of the cell type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 06/20/1994 JLB : Knows about template pointer in cell object. * + *=============================================================================================*/ +void CellClass::Recalc_Attributes(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Special override for interior terrain set so that a non-template or a clear template + ** is equivalent to impassable rock. + */ + if (LastTheater == THEATER_INTERIOR) { + if (TType == TEMPLATE_NONE || TType == TEMPLATE_CLEAR1) { + Land = LAND_ROCK; + return; + } + } + + /* + ** Check for wall effects. + */ + if (Overlay != OVERLAY_NONE) { + Land = OverlayTypeClass::As_Reference(Overlay).Land; + if (Land != LAND_CLEAR) return; + } + + /* + ** If there is a template associated with this cell, then fetch the + ** land type given the template type and icon number. + */ + if (TType != TEMPLATE_NONE && TType != 255) { + TemplateTypeClass const * ttype = &TemplateTypeClass::As_Reference(TType); + Land = ttype->Land_Type(TIcon); + return; + } + + /* + ** No template is the same as clear terrain. + */ + Land = LAND_CLEAR; +} + + +/*********************************************************************************************** + * CellClass::Occupy_Down -- Flag occupation of specified cell. * + * * + * This routine is used to mark the cell as being occupied by the specified object. * + * * + * INPUT: object -- The object that is to occupy the cell * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Occupy_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass * optr; + + if (object == NULL) return; + + /* + ** Always add buildings to the end of the occupation chain. This is necessary because + ** the occupation chain is a single list even though buildings occupy more than one + ** cell. If more than one building is allowed to occupy the same cell, then this chain + ** logic will fail. + */ + if (object->What_Am_I() == RTTI_BUILDING && Cell_Occupier()) { + optr = Cell_Occupier(); + while (optr->Next != NULL) { + assert(optr != object); + assert(optr->What_Am_I() != RTTI_BUILDING); + optr = optr->Next; + } + optr->Next = object; + object->Next = 0; + } else { + object->Next = Cell_Occupier(); + OccupierPtr = object; + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + // Changes for client/server multiplayer. ST - 8/2/2019 2:51PM + //if (IsMapped || Session.Type != GAME_NORMAL) { + // object->Revealed(PlayerPtr); + //} + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (IsMapped || Session.Type != GAME_NORMAL) { + object->Revealed(PlayerPtr); + } + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + if (Is_Visible(house_type)) { + HouseClass *house = HouseClass::As_Pointer(house_type); + object->Revealed(house); + } + } + } + + /* + ** Special occupy bit set. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = true; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = true; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = true; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Occupy_Up -- Removes occupation flag from the specified cell. * + * * + * This routine will lift the object from the cell and free the cell to be occupied by * + * another object. Only if the cell was previously marked with the object specified, will * + * the object be lifted off. This routine is the counterpart to Occupy_Down(). * + * * + * INPUT: object -- The object that is being lifted off. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 11/29/1994 JLB : Fixed to handle next pointer in previous object. * + *=============================================================================================*/ +void CellClass::Occupy_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + if (object == NULL) return; + + ObjectClass * optr = Cell_Occupier(); // Working pointer to the objects in the chain. + + if (optr == object) { + OccupierPtr = object->Next; + object->Next = 0; + } else { + bool found = false; + while (optr != NULL) { + if (optr->Next == object) { + optr->Next = object->Next; + object->Next = 0; + found = true; + break; + } + optr = optr->Next; + } +// assert(found); + } + Map.Radar_Pixel(Cell_Number()); + + /* + ** Special occupy bit clear. + */ + switch (object->What_Am_I()) { + case RTTI_BUILDING: + Flag.Occupy.Building = false; + break; + + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + case RTTI_UNIT: + Flag.Occupy.Vehicle = false; + break; + + case RTTI_TERRAIN: + Flag.Occupy.Monolith = false; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Down -- This routine is used to mark a cell as being spilled over (overla* + * * + * Most game objects can often have their graphic imagery spill into more than one cell * + * even though they are considered to "occupy" only one cell. All cells overlapped are * + * flagged by this routine. Using this information it is possible to keep the tactical map * + * display correct. * + * * + * INPUT: object -- The object to mark as overlapping this cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 07/04/1995 JLB : Ensures that buildings are always marked down. * + *=============================================================================================*/ +void CellClass::Overlap_Down(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + ObjectClass ** ptr = 0; + + if (!object) return; + + int index; + for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) return; + if (!Overlapper[index]) ptr = &Overlapper[index]; + } + + /* + ** Buildings must ALWAYS succeed in marking the cell as overlapped. Bump somebody + ** else out in this case. + */ + if (!ptr && object->What_Am_I() == RTTI_BUILDING) { + for (index = 0; index < ARRAY_SIZE(Overlapper); index++) { + switch (Overlapper[index]->What_Am_I()) { + case RTTI_BUILDING: + case RTTI_TERRAIN: + break; + + default: + Overlapper[index] = object; + index = ARRAY_SIZE(Overlapper); + break; + } + } + } + if (ptr) *ptr = object; + + /* + ** If being placed down on a visible square, then flag this + ** techno object as being revealed to the player. + */ + if (IsMapped) { + object->Revealed(PlayerPtr); + } +} + + +/*********************************************************************************************** + * CellClass::Overlap_Up -- Removes overlap flag for the cell. * + * * + * This is the counterpart to Overlap_Down and is used to remove the overlap flag for the * + * specified unit on the cell. * + * * + * INPUT: object -- The object to remove the overlap flag for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Overlap_Up(ObjectClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + assert(object != NULL && object->IsActive); + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] == object) { + Overlapper[index] = 0; + break; + } + } +} + + +/*********************************************************************************************** + * CellClass::Cell_Unit -- Returns with pointer to unit occupying cell. * + * * + * This routine will determine if a unit is occupying the cell and if so, return a pointer * + * to it. If there is no unit occupying the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to unit occupying cell, else NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * CellClass::Cell_Unit(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((UnitClass*)Cell_Find_Object(RTTI_UNIT)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Vessel -- Returns with pointer to a vessel located in the cell. * + * * + * Call this routine to query and return a pointer to a vessel located in the cell. If * + * there is no vessel present, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the vessel class object if one is present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * CellClass::Cell_Vessel(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((VesselClass*)Cell_Find_Object(RTTI_VESSEL)); +} + + +/*********************************************************************************************** + * CellClass::Cell_Infantry -- Returns with pointer of first infantry unit occupying the cell. * + * * + * This routine examines the cell and returns a pointer to the first infantry unit * + * that occupies it. If there is no infantry unit in the cell, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to infantry unit occupying the cell or NULL if none are * + * present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * CellClass::Cell_Infantry(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + return((InfantryClass*)Cell_Find_Object(RTTI_INFANTRY)); +} + + +#ifdef SORTDRAW +static bool _Calc_Partial_Window(int cellx, int celly, int & drawx, int & drawy) +{ + int & px = WindowList[WINDOW_PARTIAL][WINDOWX]; + int & py = WindowList[WINDOW_PARTIAL][WINDOWY]; + int & pw = WindowList[WINDOW_PARTIAL][WINDOWWIDTH]; + int & ph = WindowList[WINDOW_PARTIAL][WINDOWHEIGHT]; + int & tx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int & ty = WindowList[WINDOW_TACTICAL][WINDOWY]; + int & tw = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int & th = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + px = cellx + tx; + py = celly + ty; + pw = CELL_PIXEL_W; + ph = CELL_PIXEL_H; + + if (px < tx) { + pw -= tx - px; + px = tx; + } + if (pw < 1) return(false); + + if (py < ty) { + ph -= ty - py; + py = ty; + } + if (ph < 1) return(false); + + if (px + pw > tx + tw) { + pw -= (px + pw) - (tx + tw); + } + if (pw < 1) return(false); + + if (py + ph > ty + th) { + ph -= (py + ph) - (ty + th); + } + if (ph < 1) return(false); + + drawx = drawx - (px-tx); + drawy = drawy - (py-ty); + return(true); +} + + +static int _ocompare(const void * left, const void * right) +{ + COORDINATE lcoord = (*((ObjectClass **)left))->Sort_Y(); + COORDINATE rcoord = (*((ObjectClass **)right))->Sort_Y(); + if (lcoord < rcoord) return(-1); + if (lcoord > rcoord) return(1); + return(0); +} +#endif + + +/*********************************************************************************************** + * CellClass::Get_Template_Info -- Get some info about a template for external use * + * * + * * + * * + * * + * INPUT: Ref to info required * + * * + * OUTPUT: True if image info available * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/10/2019 5:57PM ST : Created. * + *=============================================================================================*/ +bool CellClass::Get_Template_Info(char *template_name, int &icon, void *&image_data) +{ + TemplateTypeClass const *ttype = NULL; + + if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { // Not sure why it's checking for 255 here since that's a valid tile type. ST - 6/4/2019 + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } + else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + if (ttype) { + + strcpy(template_name, ttype->IniName); + image_data = (void*)ttype->ImageData; + + return true; + } + + return false; +} + +/*********************************************************************************************** + * CellClass::Draw_It -- Draws the cell imagery at the location specified. * + * * + * This is the gruntwork cell rendering code. It draws the cell at the screen location * + * specified. This routine doesn't draw any overlapping or occupying units. It only * + * deals with the ground (cell) layer -- icon level. * + * * + * INPUT: x,y -- The screen coordinates to render the cell imagery at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 08/21/1994 JLB : Revised for simple template objects. * + * 11/01/1994 BRR : Updated placement cursor; draws actual object * + * 11/14/1994 BRR : Added remapping code to show passable areas * + * 12/02/1994 BRR : Added trigger display * + * 12/11/1994 JLB : Mixes up clear terrain through pseudo-random table. * + * 04/25/1995 JLB : Smudges drawn BELOW overlays. * + * 07/22/1996 JLB : Objects added to draw process. * + *=============================================================================================*/ +void CellClass::Draw_It(int x, int y, bool objects) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!objects) { + BStart(BENCH_CELL); + + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + CELL cell = Cell_Number(); + void * remap = NULL; + #ifdef SCENARIO_EDITOR + TemplateTypeClass * tptr; +// TriggerClass * trig; + int i; + char waypt[3]; + #endif + + CellCount++; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (TType != TEMPLATE_NONE && TType != TEMPLATE_CLEAR1 && TType != 255) { + ttype = &TemplateTypeClass::As_Reference(TType); + icon = TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = Clear_Icon(); + } + + #ifdef CHEAT_KEYS + /* + ** Draw the stamp of the template. + */ + if (Debug_Icon) { + LogicPage->Fill_Rect(Map.TacPixelX+x, Map.TacPixelY+y, Map.TacPixelX+x+ICON_PIXEL_W-1, Map.TacPixelY+y+ICON_PIXEL_H-1, Sim_Random_Pick(1, 254)); + FontXSpacing -= 2; + Fancy_Text_Print("%02X%02X\r%d%d%d\r%d %d", Map.TacPixelX+x+(ICON_PIXEL_W>>1), Map.TacPixelY+y, &GreyScheme, TBLACK, TPF_EFNT|TPF_CENTER|TPF_BRIGHT_COLOR|TPF_FULLSHADOW, + Cell_Y(cell), Cell_X(cell), + //(CurrentObject.Count() && CurrentObject[0]->Is_Techno()) ? ((TechnoClass *)CurrentObject[0])->House->Which_Zone(cell) : -1, + Zones[MZONE_NORMAL],Zones[MZONE_CRUSHER],Zones[MZONE_DESTROYER], + Overlay, OverlayData + ); + FontXSpacing += 2; + } else { + #endif + + #ifdef SCENARIO_EDITOR + /* + ** Set up the remap table for this icon. + */ + if (Debug_Map && Debug_Passable) { + if (::Ground[Land].Cost[0] == 0 || (Cell_Occupier() != NULL && + Cell_Occupier()->What_Am_I() != RTTI_INFANTRY)) { // impassable + remap = DisplayClass::FadingRed; + } else { + if (::Ground[Land].Cost[0] > fixed(1, 3)) { // pretty passable + remap = DisplayClass::FadingGreen; + } else { + remap = DisplayClass::FadingYellow; // moderately passable + } + } + } + #endif + + /* + ** This is the underlying terrain icon. + */ + if (ttype->Get_Image_Data()) { +// jmarshall - hd image should always be valid even if loading legacy assets + LogicPage->Draw_Stamp(ttype->Get_HDImage_Data(), icon, x, y, NULL, WINDOW_TACTICAL); +// jmarshall end + if (remap) { + LogicPage->Remap(x+Map.TacPixelX, y+Map.TacPixelY, ICON_PIXEL_W, ICON_PIXEL_H, remap); + } + } + + #ifdef SCENARIO_EDITOR + /* + ** Draw the map editor's "current" cell. This is the cell that can be + ** assigned attributes such as tag labels. + ** This must be draw before the placement cursor, but after drawing the + ** objects in the cell. + */ + if (Debug_Map && CurrentCell == Cell_Number()) { + LogicPage->Draw_Rect(x+Map.TacPixelX, y+Map.TacPixelY, Map.TacPixelX + x + CELL_PIXEL_W - 1, Map.TacPixelY + y + CELL_PIXEL_H - 1, YELLOW); + } + #endif + + /* + ** Redraw any smudge. + */ + if (Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + 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, DisplayClass::UnitShadow); + IsTheaterShape = false; + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map) { + /* + ** Draw the cell's Trigger mnemonic, if it has a trigger + */ + if (Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, x+Map.TacPixelX, y+Map.TacPixelY, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + + /* + ** Draw the cell's Waypoint designation if there is one. + */ + if (IsWaypoint) { + for (i = 0; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i] == Cell_Number()) { + if (i < 26) { + waypt[0] = 'A' + i; + waypt[1] = 0; + } else { + waypt[0] = 'A' + (i/26)-1; + waypt[1] = 'A' + (i % 26); + waypt[2] = 0; + } + Fancy_Text_Print(waypt, Map.TacPixelX + x + CELL_PIXEL_W / 2, + Map.TacPixelY + y + (CELL_PIXEL_H / 2) - 3, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_EFNT | TPF_CENTER|TPF_FULLSHADOW); + break; + } + } + if (Scen.Waypoint[WAYPT_HOME] == Cell_Number()) { + Fancy_Text_Print("Home", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + if (Scen.Waypoint[WAYPT_REINF] == Cell_Number()) { + Fancy_Text_Print("Reinf", Map.TacPixelX + x, Map.TacPixelY + y + (CELL_PIXEL_H) - 7, + &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_EFNT|TPF_FULLSHADOW); + } + } + } + #endif + + /* + ** Draw the placement cursor: + ** - First, draw the hash-mark cursor, so it will appear underneath + ** any cursor being drawn + ** - If the PendingObject is a template, overlay, or smudge, draw it + ** - Otherwise, it's up to the Display.Refresh_Map() routine to draw it + */ + if (IsCursorHere) { + SpeedType loco = SPEED_NONE; + if (Map.PendingObjectPtr) { + if (Map.PendingObjectPtr->What_Am_I() == RTTI_BUILDING) { + BuildingClass * obj = (BuildingClass *)(Map.PendingObjectPtr); + loco = obj->Class->Speed; + // if (*obj == STRUCT_SUB_PEN || *obj == STRUCT_SHIP_YARD || + // *obj == STRUCT_FAKE_PEN || *obj == STRUCT_FAKE_YARD) loco = SPEED_FLOAT; + } + } + + /* + ** Draw the hash-mark cursor: + */ + if (Map.ProximityCheck && Is_Clear_To_Build(loco)) { + LogicPage->Draw_Stamp(DisplayClass::TransIconsetHD, 0, x, y, NULL, WINDOW_TACTICAL); + } else { + LogicPage->Draw_Stamp(DisplayClass::TransIconsetHD, 2, x, y, NULL, WINDOW_TACTICAL); + } + + #ifdef SCENARIO_EDITOR + if (Debug_Map && Map.PendingObject) { + + switch (Map.PendingObject->What_Am_I()) { + + /* + ** Draw a template: + ** - Compute the icon offset of this cell for this template, using + ** ZoneCell+ZoneOffset to get the upper-left corner of the placement + ** cursor + ** - Draw the icon + */ + case RTTI_TEMPLATETYPE: + tptr = (TemplateTypeClass *)Map.PendingObject; + if (tptr->Get_Image_Data()) { + icon = (Cell_X(cell) - Cell_X(Map.ZoneCell + Map.ZoneOffset)) + + (Cell_Y(cell) - Cell_Y(Map.ZoneCell + Map.ZoneOffset)) * + tptr->Width; +// jmarshall - hd image should always be valid even if loading legacy assets + LogicPage->Draw_Stamp(tptr->Get_HDImage_Data(), icon, x, y, NULL, WINDOW_TACTICAL); +// jmarshall end + } + break; + + /* + ** Draw an overlay; just use the existing 'OverlayData' even though + ** it means nothing. + */ + case RTTI_OVERLAYTYPE: + OverlayTypeClass::As_Reference(((OverlayTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, OverlayData); + break; + + /* + ** Draw a smudge + */ + case RTTI_SMUDGETYPE: + SmudgeTypeClass::As_Reference(((SmudgeTypeClass *)Map.PendingObject)->Type).Draw_It(x, y, 0); + break; + + default: + break; + } + } + #endif + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (IsFlagged) { + void const * flag_remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, REMAP_NORMAL); + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); + } + + #ifdef CHEAT_KEYS + } + #endif + BEnd(BENCH_CELL); + } + +#ifdef SORTDRAW + if (objects) { + BStart(BENCH_OBJECTS); + + /* + ** Build a list of objects to draw into a working buffer. There is a + ** big presumption here -- it is presumed that if the cell is to be + ** redrawn, then all objects in the cell should properly be flagged to + ** be redrawn as well. Normally, this isn't a problem, but for subs + ** the IsToDisplay flag MUST REMAIN SET. This is because there is a + ** hack overpass after the cells are redrawn so that subs can be + ** redrawn separately. + */ + ObjectClass * optr[20 + ARRAY_SIZE(Overlapper)]; + int count = 0; + ObjectClass * object = Cell_Occupier(); + while (object != NULL) { + if (!object->IsActive) break; + optr[count] = object; + object->IsToDisplay = true; + object = object->Next; + count++; + } + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + object = Overlapper[index]; + if (object != NULL && object->IsActive) { + object->IsToDisplay = true; + optr[count] = object; + count++; + } + } + + /* + ** Sort the object list so that objects will be drawn from + ** back to front. + */ + switch (count) { + + /* + ** If there are zero or one object, then sorting is + ** unnecessary. + */ + case 0: + case 1: + break; + + /* + ** Two objects can be sorted with a single compare and swap. + */ + case 2: + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + break; + + /* + ** Three objects can be sorted with three compares and swaps. + */ + case 3: + if (optr[0]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[0], optr[2]); + } + if (optr[0]->Sort_Y() > optr[1]->Sort_Y()) { + swap(optr[0], optr[1]); + } + if (optr[1]->Sort_Y() > optr[2]->Sort_Y()) { + swap(optr[1], optr[2]); + } + break; + + /* + ** Large number of objects can be effeciently sorted by using + ** a quicksort. + */ + default: + qsort(optr, count, sizeof(optr[0]), _ocompare); + break; + } + + /* + ** Draw any objects that happen to be in or overlapping this cell. + */ + for (int index = 0; index < count; index++) { + object = optr[index]; + if (HasRenderedObject(object)) { + continue; + } + renderedFrameObjects.push_back(object); + int xx,yy; + if (object->IsToDisplay && (!object->Is_Techno() || ((TechnoClass *)object)->Visual_Character() == VISUAL_NORMAL) && Map.Coord_To_Pixel(object->Render_Coord(), xx, yy)) { + if (_Calc_Partial_Window(x, y, xx, yy)) { + object->Draw_It(xx, yy, WINDOW_PARTIAL); + if (Debug_Map) { + object->IsToDisplay = true; + } else { + object->IsToDisplay = false; + } + } + object->IsToDisplay = false; + } + } + BEnd(BENCH_OBJECTS); + } +#endif + +} + + +/*********************************************************************************************** + * CellClass::Concrete_Calc -- Calculates the concrete icon to use for the cell. * + * * + * This routine examines the cells around the current one and from this, determines what * + * concrete icon shape to use (if any). The cell data is adjusted and the cell is marked * + * for redraw if the icon changed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/01/1994 JLB : Created. * + *=============================================================================================*/ +void CellClass::Concrete_Calc(void) +{ +#ifdef OBSOLETE + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + static FacingType _even[5] = {FACING_N, FACING_S, FACING_SW, FACING_W, FACING_NW}; + static FacingType _odd[5] = {FACING_N, FACING_NE, FACING_E, FACING_SE, FACING_S}; + FacingType * ptr; // Working pointer into adjacent cell list. + int index; // Constructed bit index. + int icon; // Icon number. + bool isodd; // Is this for the odd column? + +#define OF_N 0x01 +#define OF_NE 0x02 +#define OF_E 0x04 +#define OF_SE 0x08 +#define OF_S 0x10 + +#define EF_N 0x01 +#define EF_NW 0x10 +#define EF_W 0x08 +#define EF_SW 0x04 +#define EF_S 0x02 + + /* + ** Determine if the even or odd row logic is necessary. + */ + isodd = ((Cell_Number() & 0x01) != 0); + + /* + ** Fetch correct pointer depending on whether this is for an + ** odd or even row. + */ + ptr = (isodd) ? _odd : _even; + + /* + ** Build an index according to the presence of concrete in the special + ** adjacent cells. This is a short list of adjacent cell flags since + ** only 5 adjacent cells need to be examined. The choice of which 5 + ** depends on whether this is for an even or odd column. + */ + index = 0; + for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) { + CellClass & cellptr = Adjacent_Cell(*ptr++); + + if (cellptr.Overlay == OVERLAY_CONCRETE) { + index |= (1< 0 && Land == LAND_TIBERIUM) { + if (OverlayData+1 > levels) { + OverlayData -= levels; + reducer = levels; + } else { + Overlay = OVERLAY_NONE; + reducer = OverlayData; + OverlayData = 0; + Recalc_Attributes(); + } + } + return(reducer); +} + + +/*********************************************************************************************** + * CellClass::Reduce_Wall -- Damages a wall, if damage is high enough. * + * * + * This routine will change the wall shape used for a wall if it's damaged. * + * * + * INPUT: damage -- The number of damage points the wall was hit with. If this value is * + * -1, then the entire wall at this cell will be destroyed. * + * * + * OUTPUT: bool; Was the wall destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BWG : Created. * + * 03/19/1995 JLB : Updates cell information if wall was destroyed. * + * 10/06/1996 JLB : Updates zone as necessary. * + *=============================================================================================*/ +int CellClass::Reduce_Wall(int damage) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (Overlay != OVERLAY_NONE) { + bool destroyed = false; + OverlayTypeClass const & wall = OverlayTypeClass::As_Reference(Overlay); + + if (wall.IsWall) { + + /* + ** If the damage was great enough to ensure wall destruction, reduce the wall by one + ** level (no more). Otherwise determine wall reduction based on a percentage chance + ** proportional to the damage received and the wall's strength. + */ + if (damage == -1 || damage >= wall.DamagePoints) { + destroyed = true; + } else { + destroyed = Random_Pick(0, wall.DamagePoints) < damage; + } + + /* + ** If the wall is destroyed, destroy it and check for any adjustments to + ** adjacent walls. + */ + if (destroyed) { + OverlayData+=16; + if (damage == -1 || + (OverlayData>>4) >= wall.DamageLevels || + ((OverlayData>>4) == wall.DamageLevels-1 && (OverlayData & 0xF)==0) ) { + + Owner = HOUSE_NONE; + Overlay = OVERLAY_NONE; + OverlayData = 0; + Recalc_Attributes(); + Redraw_Objects(); + Adjacent_Cell(FACING_N).Wall_Update(); + Adjacent_Cell(FACING_W).Wall_Update(); + Adjacent_Cell(FACING_S).Wall_Update(); + Adjacent_Cell(FACING_E).Wall_Update(); + Detach_This_From_All(As_Target()); + + /* + ** The zone calculation changes now for non-crushable zone sensitive + ** travellers. + */ + if (wall.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spot_Index -- returns cell sub-coord index for given COORDINATE * + * * + * INPUT: * + * coord COORDINATE to compute index for * + * * + * OUTPUT: * + * index into StoppingCoord that's closest to this coord * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1994 BR : Created. * + * 12/10/1994 JLB : Uses alternate sub-position algorithm. * + *=============================================================================================*/ +int CellClass::Spot_Index(COORDINATE coord) +{ + COORDINATE rel = Coord_Fraction(coord); // Sub coordinate value within cell. + + /* + ** If the coordinate is close enough to the center of the cell, then return + ** the center position index. + */ + if (Distance(rel, (COORDINATE)0x00800080L) < 60) { + return(0); + } + + /* + ** Since the center cell position has been eliminated, a simple comparison + ** as related to the center of the cell can be used to determine the sub + ** position. Take advantage of the fact that the sub positions are organized + ** from left to right, top to bottom. + */ + int index = 0; + if (Coord_X(rel) > 0x80) index |= 0x01; + if (Coord_Y(rel) > 0x80) index |= 0x02; + return(index+1); +} + + +/*********************************************************************************************** + * CellClass::Closest_Free_Spot -- returns free spot closest to given coord * + * * + * Similar to the CellClass::Free_Spot; this routine finds the spot in * + * the cell closest to the given coordinate, and returns the COORDINATE of * + * that spot if it's available, NULL if it's not. * + * * + * INPUT: * + * coord coordinate to check (only sub cell position examined) * + * * + * any -- If only the closest spot is desired regardless of whether it is free or * + * not, then this parameter will be true. * + * * + * OUTPUT: * + * COORDINATE of free spot, NULL if none. The coordinate return value does not alter the cell * + * coordinate data portions of the coordinate passed in. Only the lower sub-cell * + * data is altered. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 12/10/1994 JLB : Picks best of closest stopping positions. * + * 12/21/1994 JLB : Adds a mix-up factor if center location is occupied. * + *=============================================================================================*/ +COORDINATE CellClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int spot_index = Spot_Index(coord); + + /* + ** This precalculated sequence table records the closest spots to any given spot. Sequential + ** examination of these spots for availability ensures that the closest available one is + ** discovered first. + */ + static unsigned char _sequence[5][4] = { + {1,2,3,4}, + {0,2,3,4}, + {0,1,4,3}, + {0,1,4,2}, + {0,2,3,1} + }; + + /* + ** In the case of the center coordinate being requested, but is occupied, then all other + ** sublocations are equidistant. Instead of picking a static sequence of examination, the + ** order is mixed up by way of this table. + */ + static unsigned char _alternate[4][4] = { + {1,2,3,4}, + {2,3,4,1}, + {3,4,1,2}, + {4,1,2,3}, + }; + coord = Coord_Whole(coord); + + /* + ** Cells occupied by buildings or vehicles don't have any free spots. + */ + if (!any && (Flag.Occupy.Vehicle || Flag.Occupy.Monolith)) { + return(NULL); + } + + /* + ** If just the nearest position is desired regardless of whether occupied or not, + ** then just return with the stopping coordinate value. + */ + if (any || Is_Spot_Free(spot_index)) { + return(Coord_Add(coord, StoppingCoordAbs[spot_index])); + } + + /* + ** Scan through all available sub-locations in the cell in order to determine + ** the closest one to the coordinate requested. Use precalculated table so that + ** when the first free position is found, bail. + */ + unsigned char * sequence; + if (spot_index == 0) { + sequence = &_alternate[Random_Pick(0, 3)][0]; + } else { + sequence = &_sequence[spot_index][0]; + } + for (int index = 0; index < 4; index++) { + int pos = *sequence++; + + if (Is_Spot_Free(pos)) { + return(Coord_Add(coord, StoppingCoordAbs[pos])); + } + } + + /* + ** No free spot could be found so return a NULL coordinate. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * CellClass::Clear_Icon -- Calculates what the clear icon number should be. * + * * + * This support routine will determine what the clear icon number would be for the cell. * + * The icon number is determined by converting the cell number into an index into a * + * lookup table. This yields what appears to be a randomized map without the necessity of * + * generating and recording randomized map numbers. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the icon number for clear terrain if it were displayed at the * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 06/09/1995 JLB : Uses 16 entry scramble algorithm. * + *=============================================================================================*/ +int CellClass::Clear_Icon(void) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + CELL cell = Cell_Number(); + return((Cell_X(cell) & 0x03) | ((Cell_Y(cell) & 0x03) << 2)); +// return((cell & 0x03) | ((unsigned(cell)>>5) & 0x0C)); +} + + +/*********************************************************************************************** + * CellClass::Incoming -- Causes objects in cell to "run for cover". * + * * + * This routine is called whenever a great, but slow moving, threat is presented to the * + * occupants of a cell. The occupants will, in most cases, stop what they are doing and * + * try to get out of the way. * + * * + * INPUT: threat -- The coordinate source of the threat. * + * * + * forced -- If this threat is so major that the occupants should stop what * + * they are doing, then this parameter should be set to true. * + * * + * nokidding -- Override the scatter to also affect human controlled objects. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void CellClass::Incoming(COORDINATE threat, bool forced, bool nokidding) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = NULL; + + object = Cell_Occupier(); + while (object != NULL) { + + /* + ** Special check to make sure that friendly units never scatter. + */ + if (nokidding || Rule.IsScatter || (object->Is_Techno() && ((TechnoClass *)object)->House->IQ >= Rule.IQScatter)) { + object->Scatter(threat, forced, nokidding); + } + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Adjacent_Cell -- Determines the adjacent cell according to facing. * + * * + * Use this routine to return a reference to the adjacent cell in the direction specified. * + * * + * INPUT: face -- The direction to use when determining the adjacent cell. * + * * + * OUTPUT: Returns with a reference to the adjacent cell. * + * * + * WARNINGS: If the facing value is invalid, then a reference to the same cell is returned. * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +CellClass const & CellClass::Adjacent_Cell(FacingType face) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if ((unsigned)face >= FACING_COUNT) { + return(*this); + } + + //The top row doesn't have any adjacent cells to the north. - LLL + if (ID < MAP_CELL_W && (face == FACING_N || face == FACING_NE || face == FACING_NW)) { + return (*this); + } + + //The bottom row doesn't have any adjacent cells to the south. - LLL + if ((ID > MAP_CELL_TOTAL - MAP_CELL_W) && (face == FACING_S || face == FACING_SE || face == FACING_SW)) { + return (*this); + } + + CellClass const * ptr = this + AdjacentCell[face]; + if ((unsigned)ptr->Cell_Number() > MAP_CELL_TOTAL) return(*this); + return(*ptr); +} + + +/*************************************************************************** + * CellClass::Adjust_Threat -- Allows adjustment of threat at cell level * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/24/1995 PWG : Created. * + *=========================================================================*/ +void CellClass::Adjust_Threat(HousesType house, int threat_value) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + int region = Map.Cell_Region(Cell_Number()); + + for (HousesType lp = HOUSE_FIRST; lp < HOUSE_COUNT; lp ++) { + if (lp == house) continue; + + HouseClass * house_ptr = HouseClass::As_Pointer(lp); + if (house_ptr && (!house_ptr->IsHuman || !house_ptr->Is_Ally(house))) { + house_ptr->Adjust_Threat(region, threat_value); + } + } + if (Debug_Threat) { + Map.Flag_To_Redraw(true); + } +} + + +/*********************************************************************************************** + * CellClass::Tiberium_Adjust -- Adjust the look of the Tiberium for smoothing purposes. * + * * + * This routine will adjust the level of the Tiberium in the cell so that it will * + * smoothly blend with the adjacent Tiberium. This routine should only be called for * + * new Tiberium cells. Existing cells that contain Tiberium follow a different growth * + * pattern. * + * * + * INPUT: pregame -- Is this a pregame call? Such a call will mixup the Tiberium overlay * + * used. * + * * + * OUTPUT: Returns with the added Tiberium value that is now available for harvesting. * + * * + * WARNINGS: The return value is only valid for the initial placement. Tiberium growth will * + * increase the net worth of the existing Tiberium. * + * * + * HISTORY: * + * 05/16/1995 JLB : Created. * + * 02/20/1996 JLB : Takes into account the ore type. * + *=============================================================================================*/ +long CellClass::Tiberium_Adjust(bool pregame) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + if (Overlay != OVERLAY_NONE) { + if (OverlayTypeClass::As_Reference(Overlay).Land == LAND_TIBERIUM) { + static int _adj[9] = {0,1,3,4,6,7,8,10,11}; + static int _adjgem[9] = {0,0,0,1,1,1,2,2,2}; + int count = 0; + + /* + ** Mixup the Tiberium overlays so that they don't look the same. + ** Since the type of ore is known, also record the nominal + ** value per step of that ore type. + */ + bool gems = false; + int value = 0; + if (pregame) { + switch (Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + value = Rule.GoldValue; + Overlay = Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4); + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + gems = true; + value = Rule.GemValue*4; + Overlay = Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4); + break; + + default: + break; + } + } + + /* + ** Add up all adjacent cells that contain tiberium. + ** (Skip those cells which aren't on the map) + */ + for (FacingType face = FACING_FIRST; face < FACING_COUNT; face++) { + if ((unsigned)::Adjacent_Cell(Cell_Number(), face) >= MAP_CELL_TOTAL) continue; + CellClass & adj = Adjacent_Cell(face); + + if (adj.Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(adj.Overlay).Land == LAND_TIBERIUM) { + count++; + } + } + + if (gems) { + OverlayData = _adjgem[count]; + OverlayData = min(OverlayData, 2); + } else { + OverlayData = _adj[count]; + } + return((OverlayData+1) * value); + } + } + return(0); +} + +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * CellClass::Goodie_Check -- Performs crate discovery logic. * + * * + * Call this routine whenever an object enters a cell. It will check for the existence * + * of a crate and generate any "goodie" it might contain. * + * * + * INPUT: object -- Pointer to the object that is triggering this crate. * + * * + * OUTPUT: Can the object continue to enter this cell? A false return value means that the * + * cell is now occupied and must not be entered. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + * 07/08/1995 JLB : Added a bunch of goodies to the crates. * + * 06/17/1996 JLB : Revamped for Red Alert * + *=============================================================================================*/ +bool CellClass::Goodie_Check(FootClass * object) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (object != NULL && Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Overlay).IsCrate) { + bool force_mcv = false; + int force_money = 0; + int damage; + COORDINATE coord; + + /* + ** Determine the total number of shares for all the crate powerups. This is used as + ** the base pool to determine the odds from. + */ + int total_shares = 0; + for (int index = CRATE_FIRST; index < CRATE_COUNT; index++) { + total_shares += CrateShares[index]; + } + + /* + ** Pick a random crate powerup according to the shares allotted to each powerup. + ** In solo play, the bonus item is dependant upon the rules control. + */ + CrateType powerup; + if (Session.Type == GAME_NORMAL) { + + /* + ** Solo play has money amount determined by rules.ini file. + */ + force_money = Rule.SoloCrateMoney; + + if (Overlay == OVERLAY_STEEL_CRATE) { + powerup = Rule.SilverCrate; + } + + if (Overlay == OVERLAY_WOOD_CRATE) { + powerup = Rule.WoodCrate; + } + + if (Overlay == OVERLAY_WATER_CRATE) { +//Mono_Printf("%d-%s.\n", __LINE__, __FILE__); + powerup = Rule.WaterCrate; + } + + } else { + int pick = Random_Pick(1, total_shares); + + int share_count = 0; + for (powerup = CRATE_FIRST; powerup < CRATE_COUNT; powerup++) { + share_count += CrateShares[powerup]; + if (pick <= share_count) break; + } + assert(powerup != CRATE_COUNT); + + /* + ** Depending on what was picked, there might be an alternate goodie if the selected + ** goodie would have no effect. + */ + switch (powerup) { + case CRATE_UNIT: + if (object->House->CurUnits > 50) powerup = CRATE_MONEY; + break; + + case CRATE_SQUAD: + if (object->House->CurInfantry > 100) powerup = CRATE_MONEY; + break; + + case CRATE_DARKNESS: + if (object->House->IsGPSActive) powerup = CRATE_MONEY; + break; + + case CRATE_ARMOR: + if (object->ArmorBias != 1) powerup = CRATE_MONEY; + break; + + case CRATE_SPEED: + if (object->SpeedBias != 1 || object->What_Am_I() == RTTI_AIRCRAFT) powerup = CRATE_MONEY; + break; + + case CRATE_FIREPOWER: + if (object->FirepowerBias != 1 || !object->Is_Weapon_Equipped()) powerup = CRATE_MONEY; + break; + + case CRATE_REVEAL: + if (object->House->IsVisionary) { + if (object->House->IsGPSActive) { + powerup = CRATE_MONEY; + } else { + powerup = CRATE_DARKNESS; + } + } + break; + + case CRATE_CLOAK: + if (object->IsCloakable) powerup = CRATE_MONEY; + break; + +// case CRATE_HEAL_BASE: +// if (object->House->BScan == 0) powerup = CRATE_UNIT; + + case CRATE_MONEY: + break; + + case CRATE_ICBM: + case CRATE_PARA_BOMB: + case CRATE_SONAR: + if (Session.Type != GAME_NORMAL) { + if (MPSuperWeaponDisable) { + powerup = CRATE_MONEY; + } + } + break; + + case CRATE_TIMEQUAKE: + /* + ** For the time quake crate, scan through and count up all the + ** units (and infantry and ships and aircraft) and if either + ** side has very few, allow the time quake. Otherwise, + ** change the crate to money or something. Only do this for + ** multiplay - for solo play, they get what they get. First, + ** check for time - the chance for getting a time quake crate + ** should be very very low when they first start the mission, + ** but as time goes on the chance goes up. + */ + if (Session.Type != GAME_NORMAL) { + int i,ucount; + int minunits = 1000; + bool found = false; + unsigned long minutes = (Score.ElapsedTime / TIMER_MINUTE); + if (minutes > 100) minutes = 100; + if (Random_Pick(0,100-(int)minutes) == 0) { + for (i=0; i < (Session.Players.Count() + Session.Options.AIPlayers); i++) { + ucount = 0; + HouseClass * hptr = Houses.Ptr(i + HOUSE_MULTI1); + if (hptr != NULL && !hptr->IsDefeated) { + int j; + for( j=0; j < UNIT_COUNT; j++) { + ucount += hptr->QuantityU(j); + } + for( j=0; j < INFANTRY_COUNT; j++) { + ucount += hptr->QuantityI(j); + } + for( j=0; j < AIRCRAFT_COUNT; j++) { + ucount += hptr->QuantityA(j); + } + for( j=0; j < VESSEL_COUNT; j++) { + ucount += hptr->QuantityV(j); + } + int bcount = 0; + for( j=0; j < STRUCT_COUNT; j++) { + bcount += hptr->QuantityB(j); + } + ucount += bcount/2; // weight buildings less + minunits = min(minunits, ucount); + } + } + if (Random_Pick(0, minunits) == minunits) { + found = true; + } + } + + if (!found) { + powerup = CRATE_MONEY; + } + } + break; + } + /* + ** Possibly force it to be an MCV if there is + ** sufficient money and no buildings left. + */ + if ( object->House->BScan == 0 && + object->House->Available_Money() > ( (BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost + BuildingTypeClass::As_Reference(STRUCT_POWER).Cost) * object->House->CostBias) && + Session.Options.Bases && + !(object->House->UScan & UNITF_MCV)) { + powerup = CRATE_UNIT; + force_mcv = true; + } + + /* + ** If the powerup is money but there is insufficient money to build a refinery but there is a construction + ** yard available, then force the money to be enough to rebuild the refinery. + */ + if (powerup == CRATE_MONEY && (object->House->BScan & (STRUCTF_CONST|STRUCTF_REFINERY)) == STRUCTF_CONST && + object->House->Available_Money() < BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias) { + + force_money = BuildingTypeClass::As_Reference(STRUCT_REFINERY).Cost * object->House->CostBias; + } + + /* + ** Special override for water crates so that illegal goodies items + ** won't appear. + */ + if (Overlay == OVERLAY_WATER_CRATE) { + switch (powerup) { + case CRATE_UNIT: + case CRATE_SQUAD: + powerup = CRATE_MONEY; + break; + + default: + break; + } + } + } + + /* + ** Keep track of the number of each type of crate found + */ + if (Session.Type == GAME_INTERNET) { + object->House->TotalCrates->Increment_Unit_Total(powerup); + } + + /* + ** Remove the crate from the map. + */ + Map.Remove_Crate(Cell_Number()); +// Map[Cell_Number()].Overlay = OVERLAY_NONE; + + if (Session.Type != GAME_NORMAL && Rule.IsMPCrates) { + Map.Place_Random_Crate(); + } + + /* + ** Generate any corresponding animation associated with this crate powerup. + */ + if (CrateAnims[powerup] != ANIM_NONE) { + new AnimClass(CrateAnims[powerup], Cell_Coord()); + } + + /* + ** Create the effect requested. + */ + bool tospeak = false; + switch (powerup) { + case CRATE_TIMEQUAKE: + TimeQuake = true; + break; + + /* + ** Give the player money. + */ + case CRATE_MONEY: +crate_money: + if (force_money > 0) { + object->House->Refund_Money(force_money); + } else { + object->House->Refund_Money(Random_Pick(CrateData[powerup], CrateData[powerup]+900)); + } + break; + + /* + ** Shroud the world in blackness. + */ + case CRATE_DARKNESS: + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM + */ + if (object->House->IsHuman) { + Map.Shroud_The_Map(object->House); + } + break; + + /* + ** Reveal the entire map. + */ + case CRATE_REVEAL: + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:38AM + */ + object->House->IsVisionary = true; + if (object->House->IsHuman) { + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, object->House); + } + Map.Flag_To_Redraw(true); + } + break; + + /* + ** Try to create a unit where the crate was. + */ + case CRATE_UNIT: { + UnitTypeClass const * utp = NULL; + + /* + ** Give the player an MCV if he has no base left but does have more than enough + ** money to rebuild a new base. Of course, if he already has an MCV, then don't + ** give him another one. + */ + if (force_mcv) { + utp = &UnitTypeClass::As_Reference(UNIT_MCV); + } + + /* + ** If the player has a base and a refinery, but no harvester, then give him + ** a free one. + */ + if (utp == NULL && (object->House->BScan & STRUCTF_REFINERY) && !(object->House->UScan & UNITF_HARVESTER)) { + utp = &UnitTypeClass::As_Reference(UNIT_HARVESTER); + } + + /* + ** Check for special unit type override value. + */ + if (Rule.UnitCrateType != UNIT_NONE) { + utp = &UnitTypeClass::As_Reference(Rule.UnitCrateType); + } + + /* + ** If no unit type has been determined, then pick one at random. + */ + while (utp == NULL) { +#ifdef FIXIT_ANTS +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_RA_COUNT-1 -3)); +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1 -3)); +#endif +#else + UnitType utype = Random_Pick(UNIT_FIRST, (UnitType)(UNIT_COUNT-1)); +#endif + if (utype != UNIT_MCV || Session.Options.Bases) { + utp = &UnitTypeClass::As_Reference(utype); + if (utp->IsCrateGoodie && (utp->Ownable & (1 << HouseClass::As_Pointer(object->Owner())->ActLike))) { + break; + } + utp = NULL; + } + } + + if (utp != NULL) { + UnitClass * goodie_unit = (UnitClass *)utp->Create_One_Of(object->House); + if (goodie_unit != NULL) { + if (goodie_unit->Unlimbo(Cell_Coord())) { + return(false); + } + + /* + ** Try to place the object into a nearby cell if something is preventing + ** placement at the crate location. + */ + CELL cell = Map.Nearby_Location(Cell_Number(), goodie_unit->Class->Speed); + if (goodie_unit->Unlimbo(::Cell_Coord(cell))) { + return(false); + } + delete goodie_unit; + goto crate_money; + } + } + } + break; + + /* + ** Create a squad of miscellaneous composition. + */ + case CRATE_SQUAD: + for (int index = 0; index < 5; index++) { + static InfantryType _inf[] = { + INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1,INFANTRY_E1, + INFANTRY_E2, + INFANTRY_E3, + INFANTRY_RENOVATOR + }; + if (!InfantryTypeClass::As_Reference(_inf[Random_Pick(0, ARRAY_SIZE(_inf)-1)]).Create_And_Place(Cell_Number(), object->Owner())) { + if (index == 0) { + goto crate_money; + } + } + } + return(false); + + /* + ** A one para-bomb mission. + */ + case CRATE_PARA_BOMB: + if (object->House->SuperWeapon[SPC_PARA_BOMB].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A one time sonar pulse + */ + case CRATE_SONAR: + if (object->House->SuperWeapon[SPC_SONAR_PULSE].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + /* + ** A group of explosions are triggered around the crate. + */ + case CRATE_EXPLOSION: + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_HE, 0, true); + } + for (int index = 0; index < 5; index++) { + COORDINATE frag_coord = Coord_Scatter(Cell_Coord(), Random_Pick(0, 0x0200)); + new AnimClass(ANIM_FBALL1, frag_coord); + damage = CrateData[powerup]; + Explosion_Damage(frag_coord, damage, NULL, WARHEAD_HE); + } + break; + + /* + ** A napalm blast is triggered. + */ + case CRATE_NAPALM: + coord = Coord_Mid(Cell_Coord(), object->Center_Coord()); + new AnimClass(ANIM_NAPALM3, coord); + if (object != NULL) { + int d = CrateData[powerup]; + object->Take_Damage(d, 0, WARHEAD_FIRE, 0, true); + } + damage = CrateData[powerup]; + Explosion_Damage(coord, damage, NULL, WARHEAD_FIRE); + break; + + /* + ** All objects within a certain range will gain the ability to cloak. + */ + case CRATE_CLOAK: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IsCloakable = true; + } + } + break; + + /* + ** All of the player's objects heal up. + */ + case CRATE_HEAL_BASE: + if (object->IsOwnedByPlayer) { + Sound_Effect(VOC_HEAL, object->Center_Coord()); + } + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * obj = Logic[index]; + + if (obj && object->Is_Techno() && object->House->Class->House == obj->Owner()) { + obj->Strength = obj->Class_Of().MaxStrength; + } + } + break; + + + case CRATE_ICBM: + if (object->House->SuperWeapon[SPC_NUCLEAR_BOMB].Enable(true)) { + // Changes for client/server multiplayer. ST - 8/2/2019 2:56PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (object->House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, object->House); + } + } else { + if (object->IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + break; + + case CRATE_ARMOR: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj != NULL && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->ArmorBias == 1) { + fixed val = ((TechnoClass *)obj)->ArmorBias * Inverse(fixed(CrateData[powerup], 256)); + ((TechnoClass *)obj)->ArmorBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_ARMOR); + break; + + case CRATE_SPEED: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Foot() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((FootClass *)obj)->SpeedBias == 1 && obj->What_Am_I() != RTTI_AIRCRAFT) { + FootClass * foot = (FootClass *)obj; + + fixed val = foot->SpeedBias * fixed(CrateData[powerup], 256); + foot->SpeedBias = val; + if (foot->IsOwnedByPlayer) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_SPEED); + break; + + case CRATE_FIREPOWER: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius && ((TechnoClass *)obj)->FirepowerBias == 1) { + + fixed val = ((TechnoClass *)obj)->FirepowerBias * fixed(CrateData[powerup], 256); + ((TechnoClass *)obj)->FirepowerBias = val; + if (obj->Owner() == PlayerPtr->Class->House) tospeak = true; + } + } + if (tospeak) Speak(VOX_UPGRADE_FIREPOWER); + break; + + case CRATE_INVULN: + for (int index = 0; index < DisplayClass::Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = DisplayClass::Layer[LAYER_GROUND][index]; + + if (obj && obj->Is_Techno() && Distance(Cell_Coord(), obj->Center_Coord()) < Rule.CrateRadius) { + ((TechnoClass *)obj)->IronCurtainCountDown = (TICKS_PER_MINUTE * fixed(CrateData[powerup], 256)); + obj->Mark(MARK_CHANGE); + } + } + break; + + /* + ** A chronal vortex appears targetted at the triggering object. + */ + case CRATE_VORTEX: + if ( !ChronalVortex.Is_Active()) { + ChronalVortex.Appear ( Cell_Coord() ); + ChronalVortex.Set_Target ( (ObjectClass*) object ); + Sound_Effect(VOC_TESLA_ZAP, object->Center_Coord()); + } + break; + + default: + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * CellClass::Flag_Place -- Places a house flag down on the cell. * + * * + * This routine will place the house flag at this cell location. * + * * + * INPUT: house -- The house that is having its flag placed here. * + * * + * OUTPUT: Was the flag successfully placed here? * + * * + * WARNINGS: Failure to place means that the cell is impassable for some reason. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Place(HousesType house) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (!IsFlagged && Is_Clear_To_Move(SPEED_TRACK, false, false)) { + IsFlagged = true; + Owner = house; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Flag_Remove -- Removes the house flag from the cell. * + * * + * This routine will free the cell of any house flag that may be located there. * + * * + * INPUT: none * + * * + * OUTPUT: Was there a flag here that was removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Flag_Remove(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + if (IsFlagged) { + IsFlagged = false; + Owner = HOUSE_NONE; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Shimmer -- Causes all objects in the cell to shimmer. * + * * + * This routine is called when some event would cause a momentary disruption in the * + * cloaking device. All objects that are cloaked in the cell will have their cloaking * + * device shimmer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void CellClass::Shimmer(void) +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + ObjectClass * object = Cell_Occupier(); + + while (object) { + object->Do_Shimmer(); + object = object->Next; + } +} + + +/*********************************************************************************************** + * CellClass::Is_Clear_To_Move -- Determines if the cell is generally clear for travel * + * * + * This routine is called when determining general passability for purposes of zone * + * calculation. Only blockages that cannot be circumvented are considered to make a cell * + * impassable. All other obstructions can either be destroyed or are temporary. * + * * + * INPUT: loco -- The locomotion type to use when determining passablility. * + * * + * ignoreinfantry -- Should infantry in the cell be ignored for movement purposes? * + * * + * ignorevehicles -- If vehicles should be ignored, then this flag will be true. * + * * + * zone -- If specified, the zone must match this value or else movement is * + * presumed disallowed. * + * * + * check -- This specifies the zone type that this check applies to. * + * * + * OUTPUT: Is the cell generally passable to ground targeting? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 06/25/1996 JLB : Uses tracked vehicles as a basis for zone check. * + * 10/05/1996 JLB : Allows checking for crushable blockages. * + *=============================================================================================*/ +bool CellClass::Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone, MZoneType check) const +{ + assert((unsigned)Cell_Number() <= MAP_CELL_TOTAL); + + /* + ** Flying objects always consider every cell passable since they can fly over everything. + */ + if (loco == SPEED_WINGED) { + return(true); + } + + /* + ** If a zone was specified, then see if the cell is in a legal + ** zone to allow movement. + */ + if (zone != -1) { + if (zone != Zones[check]) { + return(false); + } + } + + /* + ** Check the occupy bits for passable legality. If ignore infantry is true, then + ** don't consider infnatry. + */ + int composite = Flag.Composite; + if (ignoreinfantry) { + composite &= 0xE0; // Drop the infantry occupation bits. + } + if (ignorevehicles) { + composite &= 0x5F; // Drop the vehicle/building bit. + } + if (composite != 0) { + return(false); + } + + /* + ** Fetch the land type of the cell -- to be modified and used later. + */ + LandType land = Land_Type(); + + /* + ** Walls are always considered to block the terrain for general passability + ** purposes unless this is a wall crushing check or if the checking object + ** can destroy walls. + */ + OverlayTypeClass const * overlay = NULL; + if (Overlay != OVERLAY_NONE) { + overlay = &OverlayTypeClass::As_Reference(Overlay); + } + if (overlay != NULL && overlay->IsWall) { + if (check != MZONE_DESTROYER && (check != MZONE_CRUSHER || !overlay->IsCrushable)) { + return(false); + } + + /* + ** Crushing objects consider crushable walls as clear rather than the + ** typical LAND_WALL setting. + */ + land = LAND_CLEAR; + } + + /* + ** See if the ground type is impassable to this locomotion type and if + ** so, return the error condition. + */ + if (::Ground[land].Cost[loco] == 0) { + return(false); + } + + /* + ** All checks passed, so this cell must be passable. + */ + return(true); +} + + +/*********************************************************************************************** + * CellClass::Is_Bridge_Here -- Checks to see if this is a bridge occupied cell. * + * * + * This routine will examine this cell and if there is a bridge here, it will return * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is there a bridge located in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Is_Bridge_Here(void) const +{ + switch (TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE1D: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE2D: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + case TEMPLATE_BRIDGE_3E: + case TEMPLATE_BRIDGE_3F: + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Grow -- Determines if Tiberium can grow in this cell. * + * * + * This checks the cell to see if Tiberium can grow at least one level in it. Tiberium can * + * grow only if there is Tiberium already present. It can only grow to a certain level * + * and then all further growth is suspended. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Grow(void) const +{ + if (!Rule.IsTGrowth) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData >= 11) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Spread -- Determines if Tiberium can spread from this cell. * + * * + * This routine will examine the cell and determine if there is sufficient Tiberium * + * present that Tiberium spores will spread to adjacent cells. If the Tiberium level is * + * too low, spreading will not occur. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium spread from this cell into adjacent cells? * + * * + * WARNINGS: This routine does not check to see if, in fact, there are any adjacent cells * + * available to spread to. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Spread(void) const +{ + if (!Rule.IsTSpread) return(false); + + if (Session.Type != GAME_NORMAL) { + if(!Session.Options.Tiberium) return(false); + } + + if (Land_Type() != LAND_TIBERIUM) return(false); + + if (OverlayData <= 6) return(false); + + if (Overlay != OVERLAY_GOLD1 && Overlay != OVERLAY_GOLD2 && Overlay != OVERLAY_GOLD3 && Overlay != OVERLAY_GOLD4) return(false); + + return(true); +} + + +/*********************************************************************************************** + * CellClass::Grow_Tiberium -- Grows the tiberium in the cell. * + * * + * This routine will cause the tiberium to grow in the cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did Tiberium grow in the cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Grow_Tiberium(void) +{ + if (Can_Tiberium_Grow()) { + OverlayData++; + Redraw_Objects(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Spread_Tiberium -- Spread Tiberium from this cell to an adjacent cell. * + * * + * This routine will cause the Tiberium to spread from this cell into an adjacent (random) * + * cell. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did the Tiberium spread? * + * * + * WARNINGS: If there are no adjacent cells that the tiberium can spread to, then this * + * routine will fail. * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Spread_Tiberium(bool forced) +{ + if (!forced) { + if (!Can_Tiberium_Spread()) return(false); + } + FacingType offset = Random_Pick(FACING_N, FACING_NW); + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + CellClass * newcell = &Adjacent_Cell(index+offset); + + if (newcell != NULL && newcell->Can_Tiberium_Germinate()) { + new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); + newcell->OverlayData = 0; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * CellClass::Can_Tiberium_Germinate -- Determines if Tiberium can begin growth in the cell. * + * * + * This routine will examine the cell and determine if Tiberium can start growth in it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can Tiberium grow in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Can_Tiberium_Germinate(void) const +{ + if (!Map.In_Radar(Cell_Number())) return(false); + + if (Is_Bridge_Here()) return(false); + + /* + ** Don't allow Tiberium to grow on a cell with a building unless that building is + ** invisible. In such a case, the Tiberium must grow or else the location of the + ** building will be revealed. + */ + BuildingClass const * building = Cell_Building(); + if (building != NULL && !building->Class->IsInvisible) return(false); + + if (!Ground[Land_Type()].Build) return(false); + + if (Overlay != OVERLAY_NONE) return(false); + + return(true); +} + + + + + + + + + + +/* +** Additions to CellClass to track visibility per-player. ST - 8/2/2019 2:59PM +** +** +** +** +** +*/ + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsMappedByPlayerMask |= (1 << shift); + } else { + IsMappedByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Mapped -- Set the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:09PM - ST * + *=============================================================================================*/ +void CellClass::Set_Mapped(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Mapped(player->Class->House, set); + if (Session.Type == GAME_NORMAL && player->IsHuman) { + IsMapped = set; // Also set the regular flag in single player + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HousesType house) const +{ + int shift = (int) house; + return (IsMappedByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Mapped -- Is the cell mapped for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:13PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Mapped(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Mapped(player->Class->House); + } + return false; +} + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HousesType house, bool set) +{ + int shift = (int) house; + if (set) { + IsVisibleByPlayerMask |= (1 << shift); + } else { + IsVisibleByPlayerMask &= ~(1 << shift); + } +} + + +/*********************************************************************************************** + * CellClass::Set_Visible -- Set the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +void CellClass::Set_Visible(HouseClass *player, bool set) +{ + if (player && player->Class) { + Set_Visible(player->Class->House, set); + if (Session.Type == GAME_NORMAL && player->IsHuman) { + IsVisible = set; // Also set the regular flag in single player. This is needed for rendering + } + } +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HousesType house) const +{ + int shift = (int) house; + return (IsVisibleByPlayerMask & (1 << shift)) ? true : false; +} + +/*********************************************************************************************** + * CellClass::Is_Visible -- Is the cell visible for the given player * + * * + * * + * HISTORY: * + * 3/5/2019 3:16PM - ST * + *=============================================================================================*/ +bool CellClass::Is_Visible(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Visible(player->Class->House); + } + return false; +} + +bool CellClass::Is_Jamming(HousesType house) const +{ + int shift = (int)house; + return (Jammed & (1 << shift)) ? true : false; +} + +bool CellClass::Is_Jamming(HouseClass *player) const +{ + if (player && player->Class) { + return Is_Jamming(player->Class->House); + } + return false; +} + +void CellClass::Override_Land_Type(LandType type) +{ + OverrideLand = type; +} \ No newline at end of file diff --git a/code/redalert/cell.h b/code/redalert/cell.h new file mode 100644 index 0000000..94ac7eb --- /dev/null +++ b/code/redalert/cell.h @@ -0,0 +1,332 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CELL_H +#define CELL_H + +#include "building.h" +#include "unit.h" +#include "template.h" + + +/**************************************************************************** +** Each cell on the map is controlled by the following structure. +*/ +class CellClass +{ + public: + /* + ** This is the ID number of this cell. By placing the ID number here, it doesn't have + ** be calculated. Calculating this number requires a divide and would occur about + ** 5.72031 bijillion times per second. + */ + short ID; + + /* + ** Does this cell need to be updated on the radar map? If something changes in the cell + ** that might change the radar map imagery, then this flag will be set. It gets cleared + ** when the cell graphic is updated to the radar map. + */ + unsigned IsPlot:1; + + /* + ** Does this cell contain the special placement cursor graphic? This graphic is + ** present when selecting a site for building placement. + */ + unsigned IsCursorHere:1; + + /* + ** A mapped cell has some portion of it visible. Maybe it has a shroud piece + ** over it and maybe not. + */ + unsigned IsMapped:1; + + /* + ** A visible cell means that it is completely visible with no shroud over + ** it at all. + */ + unsigned IsVisible:1; + + /* + ** Every cell can be assigned a waypoint. A waypoint can only be assigned + ** to one cell, and vice-versa. This bit simply indicates whether this + ** cell is assigned a waypoint or not. + */ + unsigned IsWaypoint:1; + + /* + ** Is this cell currently under the radar map cursor? If so then it + ** needs to be updated whenever the map is updated. + */ + unsigned IsRadarCursor:1; + + /* + ** If this cell contains a house flag, then this will be true. The actual house + ** flag it contains is specified by the Owner field. + */ + unsigned IsFlagged:1; + + /* + ** This is a working flag used to help keep track of what cells should be + ** shrouded. By using this flag it allows a single pass through the map + ** cells for determining shadow regrowth logic. + */ + unsigned IsToShroud:1; + + /* + ** This records the movement zone for this map. Movement zones share the + ** same number if they are contiguous (terrain consideration only). There + ** are basically two kinds of zones. The difference being determined by + ** walls that can be crushed by movement. A vehicle that can crush walls + ** will only consider the CrushZone. All other terrestrial travellers will + ** use the normal Zone. + */ + unsigned char Zones[MZONE_COUNT]; + + /* + ** This field controls whether an area is being jammed by a gap + ** generator. + */ + unsigned short Jammed; + + /* + ** This is the trigger ID for any trigger that might be attached to + ** this cell. + */ + CCPtr Trigger; + + /* + ** This contains the icon number and set to use for the base + ** of the terrain. All rendering on an icon occurs AFTER the icon + ** specified by this element is rendered. It is the lowest of the low. + */ + TemplateType TType; + unsigned char TIcon; + + /* + ** The second layer of 'terrain' icons is represented by a simple + ** type number and a value byte. This is sufficient for handling + ** concrete and walls. + */ + OverlayType Overlay; + unsigned char OverlayData; + + /* + ** This is used to specify any special 'stain' overlay icon. This + ** typically includes infantry bodies or other temporary marks. + */ + SmudgeType Smudge; + unsigned char SmudgeData; + + /* + ** Smudges and walls need to record ownership values. For walls, this + ** allows adjacent building placement logic to work. For smudges, it + ** allows building over smudges that are no longer attached to buildings + ** in addition to fixing the adjacent placement logic. + */ + HousesType Owner; + + /* + ** This flag tells you what type of infantry currently occupy the + ** cell or are moving into it. + */ + HousesType InfType; + + /* + ** These point to the object(s) that are located in this cell or overlap + ** this cell. + */ + private: + ObjectClass * OccupierPtr; + + public: +#ifdef SORTDRAW + ObjectClass * Overlapper[10]; +#else + ObjectClass * Overlapper[6]; +#endif + + + /* + ** Per-player view of whether a cell is mapped. One bit for each house type. ST - 8/2/2019 3:00PM + */ + unsigned int IsMappedByPlayerMask; + + /* + ** Per-player view of whether a cell is visible. One bit for each house type. ST - 8/2/2019 3:00PM + */ + unsigned int IsVisibleByPlayerMask; + + + /* + ** This array of bit flags is used to indicate which sub positions + ** within the cell are either occupied or are soon going to be + ** occupied. For vehicles, the cells that the vehicle is passing over + ** will be flagged with the vehicle bit. For infantry, the the sub + ** position the infantry is stopped at or headed toward will be marked. + ** The sub positions it passes over will NOT be marked. + */ + union { + struct { + unsigned Center:1; + unsigned NW:1; + unsigned NE:1; + unsigned SW:1; + unsigned SE:1; + unsigned Vehicle:1; // Reserved for vehicle occupation. + unsigned Monolith:1; // Some immovable blockage is in cell. + unsigned Building:1; // A building of some time (usually blocks movement). + } Occupy; + unsigned char Composite; + } Flag; + + //---------------------------------------------------------------- + CellClass(void); + CellClass(NoInitClass const & x) : Trigger(x) {} + ~CellClass(void) {OccupierPtr=0;} + + int operator == (CellClass const & cell) const {return &cell == this;} + + /* + ** Query functions. + */ + bool Can_Tiberium_Germinate(void) const; + bool Can_Tiberium_Grow(void) const; + bool Can_Tiberium_Spread(void) const; + bool Is_Bridge_Here(void) const; + RTTIType What_Am_I(void) const {return(RTTI_CELL);} + BuildingClass * Cell_Building(void) const; + CELL Cell_Number(void) const {return(ID);} + COORDINATE Cell_Coord(void) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + COORDINATE Free_Spot(void) const {return Closest_Free_Spot(Cell_Coord());} + CellClass & Adjacent_Cell(FacingType face) {return (CellClass &)((*((CellClass const *)this)).Adjacent_Cell(face));} + CellClass const & Adjacent_Cell(FacingType face) const; + InfantryClass * Cell_Infantry(void) const; + LandType Land_Type(void) const {return((OverrideLand != LAND_NONE) ? OverrideLand : Land);} + ObjectClass * Cell_Find_Object(RTTIType rtti) const; + ObjectClass * Cell_Object(int x=0, int y=0) const; + ObjectClass * Cell_Occupier(void) const {return(OccupierPtr);} + ObjectClass * Fetch_Occupier(void) const; + TARGET As_Target(void) const {return ::As_Target(Cell_Number());} + TechnoClass * Cell_Techno(int x=0, int y=0) const; + TerrainClass * Cell_Terrain(void) const; + UnitClass * Cell_Unit(void) const; + VesselClass * Cell_Vessel(void) const; + bool Goodie_Check(FootClass * object); + bool Is_Clear_To_Build(SpeedType loco = SPEED_TRACK) const; + bool Is_Clear_To_Move(SpeedType loco, bool ignoreinfantry, bool ignorevehicles, int zone=-1, MZoneType check=MZONE_NORMAL) const; + bool Is_Spot_Free(int spot_index) const {return (! (Flag.Composite & (1 << spot_index)) ); } + int Cell_Color(bool override=false) const; + int Clear_Icon(void) const; + static int Spot_Index(COORDINATE coord); + bool Get_Template_Info(char *template_name, int &icon, void *&image_data); + + + /* + ** Object placement and removal flag operations. + */ + void Occupy_Down(ObjectClass * object); + void Occupy_Up(ObjectClass * object); + void Overlap_Down(ObjectClass * object); + void Overlap_Up(ObjectClass * object); + bool Flag_Place(HousesType house); + bool Flag_Remove(void); + + /* + ** File I/O. + */ + bool Should_Save(void) const; + bool Save(Pipe & file) const; + bool Load(Straw & file); + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Display and rendering controls. + */ + void Draw_It(int x, int y, bool objects=false) const; + void Redraw_Objects(bool forced=false); + void Shimmer(void); + + /* + ** Maintenance calculation support. + */ + bool Grow_Tiberium(void); + bool Spread_Tiberium(bool forced=false); + long Tiberium_Adjust(bool pregame=false); + void Wall_Update(void); + void Concrete_Calc(void); + void Recalc_Attributes(void); + int Reduce_Tiberium(int levels); + int Reduce_Wall(int damage); + void Incoming(COORDINATE threat=0, bool forced=false, bool nokidding=false); + void Adjust_Threat(HousesType house, int threat_value); + + int operator != (CellClass const &) const {return 0;} + + + /* + ** Additional per-player functionality is needed for multiplayer. ST - 8/2/2019 3:01PM + */ + void Set_Mapped(HousesType house, bool set = true); + void Set_Mapped(HouseClass *player, bool set = true); + bool Is_Mapped(HousesType house) const; + bool Is_Mapped(HouseClass *player) const; + void Set_Visible(HousesType house, bool set = true); + void Set_Visible(HouseClass *player, bool set = true); + bool Is_Visible(HousesType house) const; + bool Is_Visible(HouseClass *player) const; + bool Is_Jamming(HousesType house) const; + bool Is_Jamming(HouseClass *player) const; + + /* + ** Override land type to fix passability issues on some maps + */ + void Override_Land_Type(LandType type); + + private: + CellClass (CellClass const &) ; + + LandType Land; // The land type of this cell. + + LandType OverrideLand; // The overriden land type of this cell. + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/checkbox.cpp b/code/redalert/checkbox.cpp new file mode 100644 index 0000000..f8029de --- /dev/null +++ b/code/redalert/checkbox.cpp @@ -0,0 +1,99 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHECKBOX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "checkbox.h" + + +/*********************************************************************************************** + * CheckBoxClass::Draw_Me -- Draws the checkbox imagery. * + * * + * This routine will draw the checkbox either filled or empty as necessary. * + * * + * INPUT: forced -- Should the check box be drawn even if it doesn't think it needs to? * + * * + * OUTPUT: Was the check box rendered? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Draw_Me(int forced) +{ + if (ToggleClass::Draw_Me(forced)) { + + Hide_Mouse(); + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, false); + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, DKGREY); + if (IsOn) { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, Y+Height-2, LTGREEN); + } + Show_Mouse(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CheckBoxClass::Action -- Handles a button action on a checkbox object. * + * * + * This routine will detect if the mouse has been clicked on the checkbox object. If so, * + * the check box state will be toggled. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- The key that resulted in this routine being called. * + * * + * OUTPUT: bool; Should normal processing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +int CheckBoxClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + if (IsOn) { + Turn_Off(); + } else { + Turn_On(); + } + } + return(ToggleClass::Action(flags, key)); +} diff --git a/code/redalert/checkbox.h b/code/redalert/checkbox.h new file mode 100644 index 0000000..115fa7d --- /dev/null +++ b/code/redalert/checkbox.h @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHECKBOX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHECKBOX.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/26/95 * + * * + * Last Update : May 26, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHECKBOX_H +#define CHECKBOX_H + +#include "toggle.h" + +class CheckBoxClass : public ToggleClass +{ + public: + CheckBoxClass(unsigned id, int x, int y) : + ToggleClass(id, x, y, 7, 7) + {}; + + virtual int Draw_Me(int forced=false); + virtual int Action(unsigned flags, KeyNumType & key); + + protected: +}; + +#endif diff --git a/code/redalert/cheklist.cpp b/code/redalert/cheklist.cpp new file mode 100644 index 0000000..9ae0ce0 --- /dev/null +++ b/code/redalert/cheklist.cpp @@ -0,0 +1,362 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHEKLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CheckListClass::Action -- action function for this class * + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * CheckListClass::CheckListClass -- constructor * + * CheckListClass::Check_Item -- [un]checks an items * + * CheckListClass::Draw_Entry -- draws a list box entry * + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spe* + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * CheckListClass::~CheckListClass -- destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * CheckListClass::CheckListClass -- constructor * + * * + * INPUT: * + * id control ID for this list box * + * x x-coord * + * y y-coord * + * w width * + * h height * + * flags mouse event flags * + * up ptr to Up-arrow shape * + * down ptr to Down-arrow shape * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +CheckListClass::CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + IsReadOnly(false) +{ +} + + +/*********************************************************************************************** + * CheckListClass::~CheckListClass -- Destructor for check list object. * + * * + * This destructor will delete all entries attached to it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +CheckListClass::~CheckListClass(void) +{ + while (CheckListClass::Count()) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(0); + + ListClass::Remove_Item(0); + delete obj; + } +} + + +/*********************************************************************************************** + * CheckListClass::Add_Item -- Adds specifies text to check list box. * + * * + * This routine will add the specified text string to the check list. * + * * + * INPUT: text -- Pointer to the text string to add to the list box. * + * * + * OUTPUT: Returns the index number where the text object was added. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1996 JLB : Created. * + *=============================================================================================*/ +int CheckListClass::Add_Item(char const * text) +{ + CheckObject * obj = new CheckObject(text, false); + return(ListClass::Add_Item((char const *)obj)); +} + + +char const * CheckListClass::Current_Item(void) const +{ + CheckObject * obj = (CheckObject *)ListClass::Current_Item(); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Get_Item -- Fetches a pointer to the text associated with the index. * + * * + * This routine will find the text associated with the entry specified and return a pointer * + * to that text. * + * * + * INPUT: index -- The entry (index) to fetch a pointer to. * + * * + * OUTPUT: Returns with the text pointer associated with the index specified. * + * * + * WARNINGS: If the index is out of range, then NULL is returned. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +char const * CheckListClass::Get_Item(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->Text); + } + return(0); +} + + +/*********************************************************************************************** + * CheckListClass::Remove_Item -- Remove the item that matches the text pointer specified. * + * * + * This routine will find the entry that matches the text pointer specified and then * + * delete that entry. * + * * + * INPUT: text -- The text pointer to use to find the exact match in the list. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Remove_Item(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + ListClass::Remove_Item(index); + delete obj; + break; + } + } +} + + +/*********************************************************************************************** + * CheckListClass::Set_Selected_Index -- Set the selected index to match the text pointer spec * + * * + * This routine will find the entry that exactly matches the text pointer specified. If * + * found, then that entry will be set as the currently selected index. * + * * + * INPUT: text -- Pointer to the text string to find the match for. * + * * + * OUTPUT: none * + * * + * WARNINGS: If an exact match to the specified text string could not be found, then the * + * currently selected index is not changed. * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +void CheckListClass::Set_Selected_Index(char const * text) +{ + for (int index = 0; index < Count(); index++) { + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && stricmp(obj->Text, text) == 0) { + Set_Selected_Index(index); + break; + } + } +} + + +/*************************************************************************** + * CheckListClass::Check_Item -- [un]checks an items * + * * + * INPUT: * + * index index of item to check or uncheck * + * checked 0 = uncheck, non-zero = check * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +void CheckListClass::Check_Item(int index, bool checked) +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj && obj->IsChecked != checked) { + obj->IsChecked = checked; + Flag_To_Redraw(); + } +} + + +/*************************************************************************** + * CheckListClass::Is_Checked -- returns checked state of an item * + * * + * INPUT: * + * index index of item to query * + * * + * OUTPUT: * + * 0 = item is unchecked, 1 = item is checked * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + * 02/14/1996 JLB : Revamped. * + *=========================================================================*/ +bool CheckListClass::Is_Checked(int index) const +{ + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + if (obj) { + return(obj->IsChecked); + } + return(false); +} + + +/*************************************************************************** + * CheckListClass::Action -- action function for this class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/16/1995 BR : Created. * + *=========================================================================*/ +int CheckListClass::Action(unsigned flags, KeyNumType &key) +{ + int rc; + + /* + ** If this is a read-only list, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** Invoke parents Action first, so it can set the SelectedIndex if needed. + */ + rc = ListClass::Action(flags, key); + + /* + ** Now, if this event was a left-press, toggle the checked state of the + ** current item. + */ + if (flags & LEFTPRESS) { + Check_Item(SelectedIndex, !Is_Checked(SelectedIndex)); + } + + return(rc); +} + + +/*************************************************************************** + * CheckListClass::Draw_Entry -- draws a list box entry * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1995 BRR : Created. * + *=========================================================================*/ +void CheckListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (index >= Count()) return; + + CheckObject * obj = (CheckObject *)ListClass::Get_Item(index); + + if (obj) { + char buffer[100] = ""; + + if (obj->IsChecked) { + buffer[0] = CHECK_CHAR; + } else { + buffer[0] = UNCHECK_CHAR; + } + buffer[1] = ' '; + sprintf(&buffer[2], obj->Text); + + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(buffer, x, y, scheme, TBLACK, flags, width, Tabs); + } +} + diff --git a/code/redalert/cheklist.h b/code/redalert/cheklist.h new file mode 100644 index 0000000..fafcb59 --- /dev/null +++ b/code/redalert/cheklist.h @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CHEKLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CHEKLIST.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * This class behaves just like the standard list box, except that if the * + * first character of a list entry is a space, clicking on it toggles the * + * space with a check-mark ('\3'). This makes each entry in the list box * + * "toggle-able". * + *-------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CHEKLIST_H +#define CHEKLIST_H + +#include "list.h" + +class CheckObject +{ + public: + CheckObject(char const * text = 0, bool checked=false) : + Text(text), + IsChecked(checked) + {}; + + char const * Text; + bool IsChecked; +}; + + +class CheckListClass : public ListClass +{ + public: + /* + ** Constructor/Destructor + */ + CheckListClass(int id, int x, int y, int w, int h, TextPrintType flags, + void const * up, void const * down); + ~CheckListClass(void); + + virtual int Add_Item(int text) {return ListClass::Add_Item(text);} + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void) const; + virtual char const * Get_Item(int index) const; + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int text) {ListClass::Remove_Item(text);} + virtual void Set_Selected_Index(char const * text); + virtual void Set_Selected_Index(int index) {ListClass::Set_Selected_Index(index);}; + + /* + ** Checkmark utility functions + */ + void Check_Item(int index, bool checked); // sets checked state of item + bool Is_Checked(int index) const; // gets checked state of item + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + /* + ** This defines the ASCII value of the checkmark character & non-checkmark + ** character. + */ + typedef enum CheckListClassEnum { + CHECK_CHAR = '\3', + UNCHECK_CHAR = ' ' + } CheckListClassEnum; + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + bool IsReadOnly; +}; + + +#endif diff --git a/code/redalert/class.cpp b/code/redalert/class.cpp new file mode 100644 index 0000000..78c3cd5 --- /dev/null +++ b/code/redalert/class.cpp @@ -0,0 +1,16 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + diff --git a/code/redalert/colrlist.cpp b/code/redalert/colrlist.cpp new file mode 100644 index 0000000..5bd76c2 --- /dev/null +++ b/code/redalert/colrlist.cpp @@ -0,0 +1,275 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COLRLIST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : April 19, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ColorListClass::Add_Item -- Adds an item to the list * + * ColorListClass::ColorListClass -- Class constructor * + * ColorListClass::Draw_Entry -- Draws one text line * + * ColorListClass::Remove_Item -- Removes an item from the list * + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * ColorListClass::~ColorListClass -- Class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ColorListClass::ColorListClass -- class constructor * + * * + * INPUT: * + * id button ID * + * x,y upper-left corner, in pixels * + * w,h width, height, in pixels * + * list ptr to array of char strings to list * + * flags flags for mouse, style of listbox * + * up,down pointers to shapes for up/down buttons * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ColorListClass::ColorListClass (int id, int x, int y, int w, int h, + TextPrintType flags, void const * up, void const * down) : + ListClass (id, x, y, w, h, flags, up, down), + Style(SELECT_HIGHLIGHT), + SelectColor(NULL) +{ +} + + +/*************************************************************************** + * ColorListClass::~ColorListClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +ColorListClass::~ColorListClass(void) +{ + Colors.Clear(); + SelectColor = 0; +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(char const * text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Add_Item -- Adds an item to the list * + * * + * INPUT: * + * text text to add to list * + * color color for item * + * * + * OUTPUT: * + * position of item in the list * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +int ColorListClass::Add_Item(int text, RemapControlType * color) +{ + Colors.Add(color); + return(ListClass::Add_Item(text)); +} + + +/*************************************************************************** + * ColorListClass::Remove_Item -- Removes an item from the list * + * * + * INPUT: * + * text ptr to item to remove * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Remove_Item(char const * text) +{ + int index = List.ID(text); + if (index != -1) { + Colors.Delete(index); + ListClass::Remove_Item(text); + } +} + + +/*************************************************************************** + * ColorListClass::Set_Selected_Style -- tells how to draw selected item * + * * + * INPUT: * + * style style to draw * + * color color to draw the special style in; -1 = use item's color * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Set_Selected_Style(SelectStyleType style, RemapControlType * color) +{ + Style = style; + SelectColor = color; +} + + +/*************************************************************************** + * ColorListClass::Draw_Entry -- Draws one text line * + * * + * INPUT: * + * index index into List of item to draw * + * x,y x,y coords to draw at * + * width maximum width allowed for text * + * selected true = this item is selected * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/19/1995 BRR : Created. * + *=========================================================================*/ +void ColorListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * color; + + /* + ** Draw a non-selected item in its color + */ + if (!selected) { + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + return; + } + + /* + ** For selected items, choose the right color & style: + */ + if (SelectColor == NULL) { + color = Colors[index]; + } else { + color = SelectColor; + } + + switch (Style) { + /* + ** NONE: Just print the string in its native color + */ + case SELECT_NORMAL: + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, + TextFlags, width, Tabs); + break; + + /* + ** HIGHLIGHT: Draw the string in the highlight color (SelectColor must + ** be set) + */ + case SELECT_HIGHLIGHT: + if (TextFlags & TPF_6PT_GRAD) { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + Conquer_Clip_Text_Print(List[index], x, y, color, TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** BOX: Draw a box around the item in the current select color + */ + case SELECT_BOX: + LogicPage->Draw_Rect (x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + break; + + /* + ** BAR: draw a color bar under the text + */ + case SELECT_BAR: + if (TextFlags & TPF_6PT_GRAD) { + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags | TPF_BRIGHT_COLOR, width, Tabs); + } else { + LogicPage->Fill_Rect(x, y, x + width - 2, y + LineHeight - 2, color->Color); + Conquer_Clip_Text_Print(List[index], x, y, Colors[index], TBLACK, TextFlags, width, Tabs); + } + break; + + /* + ** INVERT: Draw text as the background color on foreground color + */ + case SELECT_INVERT: + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, Colors[index]->Color); + break; + } +} diff --git a/code/redalert/colrlist.h b/code/redalert/colrlist.h new file mode 100644 index 0000000..1b1bbe2 --- /dev/null +++ b/code/redalert/colrlist.h @@ -0,0 +1,84 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COLRLIST.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COLRLIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COLORLIST_H +#define COLORLIST_H + +#include "list.h" + + +/*************************************************************************** +** This class adds the ability for every list item to have a different color. +*/ +class ColorListClass : public ListClass +{ + public: + /********************************************************************* + ** These enums are the ways a selected item can be drawn + */ + //lint -esym(578,SELECT_NONE) + typedef enum SelectEnum { + SELECT_NORMAL, // selected items aren't drawn differently + SELECT_HIGHLIGHT, // item is highlighted + SELECT_BOX, // draw a box around the item + SELECT_BAR, // draw a bar behind the item + SELECT_INVERT // draw the string inverted + } SelectStyleType; + + ColorListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + virtual ~ColorListClass(void); + + virtual int Add_Item(char const * text, RemapControlType * color = NULL); + virtual int Add_Item(int text, RemapControlType * color = NULL); + virtual void Remove_Item(char const * text); + + virtual void Set_Selected_Style(SelectStyleType style, RemapControlType * color = NULL); + + /* + ** This is the list of colors for each item. + */ + DynamicVectorClass Colors; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This tells how to draw the selected item. + */ + SelectStyleType Style; + RemapControlType * SelectColor; + +}; + +#endif diff --git a/code/redalert/combat.cpp b/code/redalert/combat.cpp new file mode 100644 index 0000000..66f6da9 --- /dev/null +++ b/code/redalert/combat.cpp @@ -0,0 +1,422 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBAT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBAT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 19, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Combat_Anim -- Determines explosion animation to play. * + * Explosion_Damage -- Inflict an explosion damage affect. * + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * Wide_Area_Damage -- Apply wide area damage to the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Modify_Damage -- Adjusts damage to reflect the nature of the target. * + * * + * This routine is the core of combat tactics. It implements the * + * affect various armor types have against various weapon types. By * + * careful exploitation of this table, tactical advantage can be * + * obtained. * + * * + * INPUT: damage -- The damage points to process. * + * * + * warhead -- The source of the damage points. * + * * + * armor -- The type of armor defending against the damage. * + * * + * distance -- The distance (in leptons) from the source of the damage. * + * * + * OUTPUT: Returns with the adjusted damage points to inflict upon the * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1994 JLB : Created. * + * 04/17/1994 JLB : Always does a minimum of damage. * + * 01/01/1995 JLB : Takes into account distance from damage source. * + * 04/11/1996 JLB : Changed damage fall-off formula for less damage fall-off. * + *=============================================================================================*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance) +{ + if (!damage) return(damage); + + /* + ** If there is no raw damage value to start with, then + ** there can be no modified damage either. + */ + if (Special.IsInert || !damage || warhead == WARHEAD_NONE) return(0); + + /* + ** Negative damage (i.e., heal) is always applied full strength, but only if the heal + ** effect is close enough. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (distance < 0x008) { + if(warhead != WARHEAD_MECHANICAL && armor == ARMOR_NONE) return(damage); + if(warhead == WARHEAD_MECHANICAL && armor != ARMOR_NONE) return(damage); + } +#else + if (distance < 0x008 && armor == ARMOR_NONE) return(damage); +#endif + return(0); + } + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; + + damage = damage * whead->Modifier[armor]; + + /* + ** Reduce damage according to the distance from the impact point. + */ + if (damage) { + if (!whead->SpreadFactor) { + distance /= PIXEL_LEPTON_W/4; + } else { + distance /= whead->SpreadFactor * (PIXEL_LEPTON_W/2); + } + distance = Bound(distance, 0, 16); + if (distance) { + damage = damage / distance; + } + + /* + ** Allow damage to drop to zero only if the distance would have + ** reduced damage to less than 1/4 full damage. Otherwise, ensure + ** that at least one damage point is done. + */ + if (distance < 4) { + damage = max(damage, Rule.MinDamage); + } + } + + damage = min(damage, Rule.MaxDamage); + return(damage); +} + + +/*********************************************************************************************** + * Explosion_Damage -- Inflict an explosion damage affect. * + * * + * Processes the collateral damage affects typically caused by an * + * explosion. * + * * + * INPUT: coord -- The coordinate of ground zero. * + * * + * strength -- Raw damage points at ground zero. * + * * + * source -- Source of the explosion (who is responsible). * + * * + * warhead -- The kind of explosion to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can consume some time and will affect the AI * + * of nearby enemy units (possibly). * + * * + * HISTORY: * + * 08/16/1991 JLB : Created. * + * 11/30/1991 JLB : Uses coordinate system. * + * 12/27/1991 JLB : Radius of explosion damage effect. * + * 04/13/1994 JLB : Streamlined. * + * 04/16/1994 JLB : Warhead damage type modifier. * + * 04/17/1994 JLB : Cleaned up. * + * 06/20/1994 JLB : Uses object pointers to distribute damage. * + * 06/20/1994 JLB : Source is a pointer. * + * 06/18/1996 JLB : Strength could be negative for healing effects. * + *=============================================================================================*/ +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead) +{ + CELL cell; // Cell number under explosion. + ObjectClass * object; // Working object pointer. + ObjectClass * objects[32]; // Maximum number of objects that can be damaged. + int distance; // Distance to unit. + int range; // Damage effect radius. + int count; // Number of vehicle IDs in list. + + if (!strength || Special.IsInert || warhead == WARHEAD_NONE) return; + + WarheadTypeClass const * whead = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * whead = &Warheads[warhead]; +// range = ICON_LEPTON_W*2; + range = ICON_LEPTON_W + (ICON_LEPTON_W >> 1); + cell = Coord_Cell(coord); + if ((unsigned)cell >= MAP_CELL_TOTAL) return; + + CellClass * cellptr = &Map[cell]; + ObjectClass * impacto = cellptr->Cell_Occupier(); + + /* + ** Fill the list of unit IDs that will have damage + ** assessed upon them. The units can be lifted from + ** the cell data directly. + */ + count = 0; + for (FacingType i = FACING_NONE; i < FACING_COUNT; i++) { + /* + ** Fetch a pointer to the cell to examine. This is either + ** an adjacent cell or the center cell. Damage never spills + ** further than one cell away. + */ + if (i != FACING_NONE) { + cellptr = &Map[cell].Adjacent_Cell(i); + } + + /* + ** Add all objects in this cell to the list of objects to possibly apply + ** damage to. The list stops building when the object pointer list becomes + ** full. Do not include overlapping objects; selection state can affect + ** the overlappers, and this causes multiplayer games to go out of sync. + */ + object = cellptr->Cell_Occupier(); + while (object) { + if (!object->IsToDamage && object != source) { + object->IsToDamage = true; + objects[count++] = object; + if (count >= ARRAY_SIZE(objects)) break; + } + object = object->Next; + } + if (count >= ARRAY_SIZE(objects)) break; + } + + /* + ** Sweep through the units to be damaged and damage them. When damaging + ** buildings, consider a hit on any cell the building occupies as if it + ** were a direct hit on the building's center. + */ + for (int index = 0; index < count; index++) { + object = objects[index]; + + object->IsToDamage = false; + if (object->IsActive) { + if (object->What_Am_I() == RTTI_BUILDING && impacto == object) { + distance = 0; + } else { + distance = Distance(coord, object->Center_Coord()); + } + if (object->IsDown && !object->IsInLimbo && distance < range) { + int damage = strength; + object->Take_Damage(damage, distance, warhead, source); + } + } + } + + /* + ** If there is a wall present at this location, it may be destroyed. Check to + ** make sure that the warhead is of the kind that can destroy walls. + */ + cellptr = &Map[cell]; + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsTiberium && whead->IsTiberiumDestroyer) { + cellptr->Reduce_Tiberium(strength / 10); + } + if (optr->IsWall) { + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { + Map[cell].Reduce_Wall(strength); + } + } + } + + /* + ** If there is a bridge at this location, then it may be destroyed by the + ** combat damage. + */ + if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B ) { + + if (((warhead == WARHEAD_AP || warhead == WARHEAD_HE) && Random_Pick(1, Rule.BridgeStrength) < strength)) { + Map.Destroy_Bridge_At(cell); + } + } +} + + +/*********************************************************************************************** + * Combat_Anim -- Determines explosion animation to play. * + * * + * This routine is called when a projectile impacts. This routine will determine what * + * animation should be played. * + * * + * INPUT: damage -- The amount of damage this warhead possess (warhead size). * + * * + * warhead -- The type of warhead. * + * * + * land -- The land type that this explosion is over. Sometimes, this makes * + * a difference (especially over water). * + * * + * OUTPUT: Returns with the animation to play. If no animation is to be played, then * + * ANIM_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1996 JLB : Created. * + *=============================================================================================*/ +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land) +{ + /* + ** For cases of no damage or invalid warhead, don't have any + ** animation effect at all. + */ + if (damage == 0 || warhead == WARHEAD_NONE) { + return(ANIM_NONE); + } + + static AnimType _aplist[] = { + ANIM_VEH_HIT3, // Small fragment throwing explosion -- burn/exp mix. + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_FRAG1, // Medium fragment throwing explosion -- short decay. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _helist[] = { + ANIM_VEH_HIT1, // Small fireball explosion (bulges rightward). + ANIM_VEH_HIT2, // Small fragment throwing explosion -- pop & sparkles. + ANIM_ART_EXP1, // Large fragment throwing explosion -- many sparkles. + ANIM_FBALL1, // Large fireball explosion (bulges rightward). + }; + + static AnimType _firelist[] = { + ANIM_NAPALM1, // Small napalm burn. + ANIM_NAPALM2, // Medium napalm burn. + ANIM_NAPALM3, // Large napalm burn. + }; + + static AnimType _waterlist[] = { + ANIM_WATER_EXP3, + ANIM_WATER_EXP2, + ANIM_WATER_EXP1, + }; + + WarheadTypeClass const * wptr = WarheadTypeClass::As_Pointer(warhead); +// WarheadTypeClass const * wptr = &Warheads[warhead]; + switch (wptr->ExplosionSet) { + case 6: + return(ANIM_ATOM_BLAST); + + case 2: + if (damage > 15) { + return(ANIM_PIFFPIFF); + } + return(ANIM_PIFF); + + case 4: + if (land == LAND_NONE) return(ANIM_FLAK); +// Fixed math error + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 90), 90)]); + return(_aplist[(ARRAY_SIZE(_aplist)-1) * fixed(min(damage, 90), 90)]); + + case 5: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 130), 130)]); + return(_helist[(ARRAY_SIZE(_helist)-1) * fixed(min(damage, 130), 130)]); + + case 3: + if (land == LAND_NONE) return(ANIM_FLAK); + if (land == LAND_WATER) return(_waterlist[(ARRAY_SIZE(_waterlist)-1) * fixed(min(damage, 150), 150)]); + return(_firelist[(ARRAY_SIZE(_firelist)-1) * fixed(min(damage, 150), 150)]); + + case 1: + return(ANIM_PIFF); + + default: + break; + } + return(ANIM_NONE); +} + + +/*********************************************************************************************** + * Wide_Area_Damage -- Apply wide area damage to the map. * + * * + * This routine will apply damage to a very wide area on the map. The damage will be * + * spread out from the coordinate specified by the radius specified. The amount of damage * + * will attenuate according to the distance from center. * + * * + * INPUT: coord -- The coordinate that the explosion damage will center about. * + * * + * radius -- The radius of the explosion. * + * * + * damage -- The amount of damage to apply at the center location. * + * * + * source -- Pointer to the purpetrator of the damage (if any). * + * * + * warhead -- The type of warhead that is causing the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int rawdamage, TechnoClass * source, WarheadType warhead) +{ + int cell_radius = (radius + CELL_LEPTON_W-1) / CELL_LEPTON_W; + CELL cell = Coord_Cell(coord); + + for (int x = -cell_radius; x <= cell_radius; x++) { + for (int y = -cell_radius; y <= cell_radius; y++) { + int xpos = Cell_X(cell) + x; + int ypos = Cell_Y(cell) + y; + + /* + ** If the potential damage cell is outside of the map bounds, + ** then don't process it. This unusual check method ensures that + ** damage won't wrap from one side of the map to the other. + */ + if ((unsigned)xpos > MAP_CELL_W) { + continue; + } + if ((unsigned)ypos > MAP_CELL_H) { + continue; + } + CELL tcell = XY_Cell(xpos, ypos); + if (!Map.In_Radar(tcell)) continue; + + int dist_from_center = Distance(XY_Coord(x+cell_radius, y+cell_radius), XY_Coord(cell_radius, cell_radius)); + int damage = rawdamage * Inverse(fixed(cell_radius, dist_from_center)); + Explosion_Damage(Cell_Coord(tcell), damage, source, warhead); + if (warhead == WARHEAD_FIRE && damage > 100) { + new SmudgeClass(Random_Pick(SMUDGE_SCORCH1, SMUDGE_SCORCH6), Cell_Coord(tcell)); + } + } + } +} diff --git a/code/redalert/combuf.cpp b/code/redalert/combuf.cpp new file mode 100644 index 0000000..778829f --- /dev/null +++ b/code/redalert/combuf.cpp @@ -0,0 +1,1161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBUF.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : October 23, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommBufferClass::CommBufferClass -- class constructor * + * CommBufferClass::~CommBufferClass -- class destructor * + * CommBufferClass::Init -- initializes this queue * + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * CommBufferClass::Queue_Send -- queues a message for sending * + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * CommBufferClass::Queue_Receive -- queues a received message * + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue* + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * CommBufferClass::Add_Delay -- adds a new delay value for response time* + * CommBufferClass::Avg_Response_Time -- returns average response time * + * CommBufferClass::Max_Response_Time -- returns max response time * + * CommBufferClass::Reset_Response_Time -- resets computations * + * Mono_Debug_Print -- Debug output routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include "combuf.h" +#include "connect.h" // for command names for debug output +#include "wwlib32.h" // to enable mono output + + +/*************************************************************************** + * CommBufferClass::CommBufferClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::CommBufferClass(int numsend, int numreceive, int maxlen, + int extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + MaxExtraSize = extralen; + + //------------------------------------------------------------------------ + // Allocate the queue entries + //------------------------------------------------------------------------ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + SendIndex = new int[numsend]; + ReceiveIndex = new int[numreceive]; + + //------------------------------------------------------------------------ + // Allocate queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + SendQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + SendQueue[i].ExtraBuffer = NULL; + } + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + if (MaxExtraSize > 0) { + ReceiveQueue[i].ExtraBuffer = new char[MaxExtraSize]; + } + else { + ReceiveQueue[i].ExtraBuffer = NULL; + } + } + + Init(); + +} /* end of CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::~CommBufferClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommBufferClass::~CommBufferClass() +{ + int i; + + //------------------------------------------------------------------------ + // Free queue entry buffers + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + if (SendQueue[i].ExtraBuffer) { + delete [] SendQueue[i].ExtraBuffer; + } + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + if (ReceiveQueue[i].ExtraBuffer) { + delete [] ReceiveQueue[i].ExtraBuffer; + } + } + + delete [] SendQueue; + delete [] ReceiveQueue; + + delete [] SendIndex; + delete [] ReceiveIndex; + +} /* end of ~CommBufferClass */ + + +/*************************************************************************** + * CommBufferClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Init(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + + ReceiveCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + ReceiveQueue[i].ExtraLen = 0; + + ReceiveIndex[i] = 0; + } + + //------------------------------------------------------------------------ + // Init debug values + //------------------------------------------------------------------------ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugNameCount = 0; + +} /* end of Init */ + + +/*************************************************************************** + * CommBufferClass::Init_Send_Queue -- Clears the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/23/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Init_Send_Queue(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init data members + //------------------------------------------------------------------------ + SendCount = 0; + + //------------------------------------------------------------------------ + // Init the queue entries + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + SendQueue[i].BufLen = 0; + SendQueue[i].ExtraLen = 0; + + SendIndex[i] = 0; + } + +} /* end of Init_Send_Queue */ + + +/*************************************************************************** + * CommBufferClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Send(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (SendCount==MaxSend || buflen > MaxPacketSize) + return(0); + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxSend; i++) { + if (SendQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[index].IsActive = 1; // entry is now active + SendQueue[index].IsACK = 0; // entry hasn't been ACK'd + SendQueue[index].FirstTime = 0L; // filled in by Manager when sent + SendQueue[index].LastTime = 0L; // filled in by Manager when sent + SendQueue[index].SendCount = 0L; // filled in by Manager when sent + SendQueue[index].BufLen = buflen; // save buffer size + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(SendQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(SendQueue[index].ExtraBuffer,extrabuf,extralen); + SendQueue[index].ExtraLen = extralen; + } + else { + SendQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + SendIndex[SendCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + SendCount++; + SendTotal++; + + return(1); + +} /* end of Queue_Send */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Send -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index "index" of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (SendCount==0 || SendQueue[SendIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendIndex[index]].Buffer, + SendQueue[SendIndex[index]].BufLen); + (*buflen) = SendQueue[SendIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,SendQueue[SendIndex[index]].ExtraBuffer, + SendQueue[SendIndex[index]].ExtraLen); + (*extralen) = SendQueue[SendIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + SendQueue[SendIndex[index]].IsActive = 0; + SendQueue[SendIndex[index]].IsACK = 0; + SendQueue[SendIndex[index]].FirstTime = 0L; + SendQueue[SendIndex[index]].LastTime = 0L; + SendQueue[SendIndex[index]].SendCount = 0L; + SendQueue[SendIndex[index]].BufLen = 0; + SendQueue[SendIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < SendCount - 1; i++) { + SendIndex[i] = SendIndex[i + 1]; + } + SendIndex[SendCount - 1] = 0; + SendCount--; + + return(1); + +} /* end of UnQueue_Send */ + + +/*************************************************************************** + * CommBufferClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommBufferClass::Get_Send(int index) +{ + if (SendQueue[SendIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&SendQueue[SendIndex[index]]); + } + +} /* end of Get_Send */ + + +/*************************************************************************** + * CommBufferClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * extrabuf buffer containing extra data (optional) * + * extralen length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::Queue_Receive(void *buf, int buflen, void *extrabuf, + int extralen) +{ + int i; + int index; + + //------------------------------------------------------------------------ + // Error if no room in the queue + //------------------------------------------------------------------------ + if (ReceiveCount==MaxReceive || buflen > MaxPacketSize) { + return(0); + } + + //------------------------------------------------------------------------ + // Find an empty slot + //------------------------------------------------------------------------ + index = -1; + for (i = 0; i < MaxReceive; i++) { + if (ReceiveQueue[i].IsActive==0) { + index = i; + break; + } + } + if (index == -1) + return (0); + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[index].IsActive = 1; + ReceiveQueue[index].IsRead = 0; + ReceiveQueue[index].IsACK = 0; + ReceiveQueue[index].BufLen = buflen; + + //------------------------------------------------------------------------ + // Copy the packet data + //------------------------------------------------------------------------ + memcpy(ReceiveQueue[index].Buffer,buf,buflen); + + //------------------------------------------------------------------------ + // Fill in the extra data, if there is any + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen > 0 && extralen <= MaxExtraSize) { + memcpy(ReceiveQueue[index].ExtraBuffer,extrabuf,extralen); + ReceiveQueue[index].ExtraLen = extralen; + } + else { + ReceiveQueue[index].ExtraLen = 0; + } + + //------------------------------------------------------------------------ + // Save this entry's index + //------------------------------------------------------------------------ + ReceiveIndex[ReceiveCount] = index; + + //------------------------------------------------------------------------ + // Increment counters & entry ptr + //------------------------------------------------------------------------ + ReceiveCount++; + ReceiveTotal++; + + return(1); + +} /* end of Queue_Receive */ + + +/*************************************************************************** + * CommBufferClass::UnQueue_Receive -- removes next entry from send queue * + * * + * Frees the given entry; the index given by the caller is the "active" * + * index value (ie the "nth" active entry), not the actual index in the * + * array. * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * index index of entry to un-queue * + * extrabuf buffer for extra data (optional) * + * extralen ptr to length of extra data (optional) * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retrieve * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommBufferClass::UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf, int *extralen) +{ + int i; + + //------------------------------------------------------------------------ + // Error if no entry to retrieve + //------------------------------------------------------------------------ + if (ReceiveCount==0 || ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Copy the data from the entry + //------------------------------------------------------------------------ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveIndex[index]].Buffer, + ReceiveQueue[ReceiveIndex[index]].BufLen); + (*buflen) = ReceiveQueue[ReceiveIndex[index]].BufLen; + } + + //------------------------------------------------------------------------ + // Copy the extra data + //------------------------------------------------------------------------ + if (extrabuf!=NULL && extralen!=NULL) { + memcpy(extrabuf,ReceiveQueue[ReceiveIndex[index]].ExtraBuffer, + ReceiveQueue[ReceiveIndex[index]].ExtraLen); + (*extralen) = ReceiveQueue[ReceiveIndex[index]].ExtraLen; + } + + //------------------------------------------------------------------------ + // Set entry flags + //------------------------------------------------------------------------ + ReceiveQueue[ReceiveIndex[index]].IsActive = 0; + ReceiveQueue[ReceiveIndex[index]].IsRead = 0; + ReceiveQueue[ReceiveIndex[index]].IsACK = 0; + ReceiveQueue[ReceiveIndex[index]].BufLen = 0; + ReceiveQueue[ReceiveIndex[index]].ExtraLen = 0; + + //------------------------------------------------------------------------ + // Move Indices back one + //------------------------------------------------------------------------ + for (i = index; i < ReceiveCount - 1; i++) { + ReceiveIndex[i] = ReceiveIndex[i + 1]; + } + ReceiveIndex[ReceiveCount - 1] = 0; + ReceiveCount--; + + return(1); + +} /* end of UnQueue_Receive */ + + +/*************************************************************************** + * CommBufferClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessible queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommBufferClass::Get_Receive(int index) +{ + if (ReceiveQueue[ReceiveIndex[index]].IsActive==0) { + return(NULL); + } + else { + return(&ReceiveQueue[ReceiveIndex[index]]); + } + +} /* end of Get_Receive */ + + +/*************************************************************************** + * CommBufferClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) + roundoff = 1; + MeanDelay = (DelaySum >> 8) + roundoff; + } + else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } + +} /* end of Add_Delay */ + + +/*************************************************************************** + * CommBufferClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Avg_Response_Time(void) +{ + return(MeanDelay); + +} /* end of Avg_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommBufferClass::Max_Response_Time(void) +{ + return(MaxDelay); + +} /* end of Max_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommBufferClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * CommBufferClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Configure_Debug(int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + DebugOffset = type_offset; + DebugSize = type_size; + DebugNames = names; + DebugNameStart = namestart; + DebugNameCount = namecount; + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // If few enough entries, call the verbose debug version + //------------------------------------------------------------------------ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + if (MaxSend <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } + else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + if (MaxReceive <= 48) { + num = MaxSend; + } + else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } + else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * CommBufferClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommBufferClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + //------------------------------------------------------------------------ + // Refresh the screen + //------------------------------------------------------------------------ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + //------------------------------------------------------------------------ + // Print Send Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (SendQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], SendQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + SendQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + + //------------------------------------------------------------------------ + // Print Receive Queue items + //------------------------------------------------------------------------ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + //..................................................................... + // Print an active entry + //..................................................................... + if (ReceiveQueue[i].IsActive) { + + //.................................................................. + // Get header info + //.................................................................. + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + //.................................................................. + // Decode app's ID & its name + //.................................................................. + if (DebugSize && (DebugOffset + DebugSize) <= ReceiveQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + + } + else if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + + } + else if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugNameCount > 0 && val >= 0 && val < DebugNameCount) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val - DebugNameStart], ReceiveQueue[i].IsACK); + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + } + else { + sprintf(txt + strlen(txt)," %x", + ReceiveQueue[i].IsACK); + } + + Mono_Printf("%s",txt); + } + else { + + //.................................................................. + // Entry isn't active; print blanks + //.................................................................. + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} /* end of Mono_Debug_Print2 */ + + +/************************** end of combuf.cpp ******************************/ + + diff --git a/code/redalert/combuf.h b/code/redalert/combuf.h new file mode 100644 index 0000000..035c737 --- /dev/null +++ b/code/redalert/combuf.h @@ -0,0 +1,190 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMBUF.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMBUF.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to store outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * * + * This class stores buffers in a non-sequenced order; it allows freeing * + * any entry, so the buffers can be kept clear, even if packets come in * + * out of order. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMBUF_H +#define COMBUF_H + + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet + int ExtraLen; // size of extra data + char *ExtraBuffer; // extra data buffer +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommBufferClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommBufferClass(int numsend, int numrecieve, int maxlen, + int extralen = 0); + virtual ~CommBufferClass(); + void Init(void); + void Init_Send_Queue(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Send(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen, void *extrabuf = NULL, + int extralen = 0); + int UnQueue_Receive(void *buf, int *buflen, int index, + void *extrabuf = NULL, int *extralen = NULL); + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int type_offset, int type_size, char **names, + int namestart, int namecount); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + int MaxExtraSize; // max size of extra bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + unsigned long SendTotal; // total # added to send queue + int *SendIndex; // array of Send entry indices + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + unsigned long ReceiveTotal; // total # added to receive queue + int *ReceiveIndex; // array of Receive entry indices + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugNameStart; // number of 1st ID + int DebugNameCount; // # of names in array +}; + +#endif + +/**************************** end of combuf.h ******************************/ + diff --git a/code/redalert/cominit.cpp b/code/redalert/cominit.cpp new file mode 100644 index 0000000..e33a61c --- /dev/null +++ b/code/redalert/cominit.cpp @@ -0,0 +1,48 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// +// If you link with this it will automatically call the COM initialization stuff +// + +#include "cominit.h" +//#include +//#include +//#include +#include +//#include "externs.h" +//#include "text.rh" + +//#include "WolDebug.h" + +ComInit::ComInit() +{ + //HRESULT hRes = CoInitialize(NULL); +// if (SUCCEEDED(hRes)==FALSE) +// exit(0); + HRESULT hRes = OleInitialize(NULL); +} + +ComInit::~ComInit() +{ +// CoUninitialize(); +// debugprint( "pre OleUninitialize\n" ); + OleUninitialize(); +// debugprint( "post OleUninitialize\n" ); +} + +// Creating this instance will setup all COM stuff & do cleanup on program exit +ComInit Global_COM_Initializer; + diff --git a/code/redalert/cominit.h b/code/redalert/cominit.h new file mode 100644 index 0000000..4c8814c --- /dev/null +++ b/code/redalert/cominit.h @@ -0,0 +1,31 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef COMINIT_HEADER +#define COMINIT_HEADER + +// +// Link with this to automatically initialize COM at startup +// - See cominit.cpp for more info +// + +class ComInit +{ + public: + ComInit(); + ~ComInit(); +}; + +#endif diff --git a/code/redalert/compat.h b/code/redalert/compat.h new file mode 100644 index 0000000..049a2fd --- /dev/null +++ b/code/redalert/compat.h @@ -0,0 +1,214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COMPAT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMPAT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/95 * + * * + * Last Update : March 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMPAT_H +#define COMPAT_H + + +#define BuffType BufferClass +//#define movmem(a,b,c) memmove(b,a,c) +#define ShapeBufferSize _ShapeBufferSize + +/*=========================================================================*/ +/* Define some equates for the different graphic routines we will install */ +/* later. */ +/*=========================================================================*/ +#define HIDBUFF ((void *)(0xA0000)) +#define Size_Of_Region(a, b) ((a)*(b)) + +/*=========================================================================*/ +/* Define some Graphic Routines which will only be fixed by these defines */ +/*=========================================================================*/ +#define Set_Font_Palette(a) Set_Font_Palette_Range(a, 0, 15) + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#define READ 1 // Read access. +#define WRITE 2 // Write access. + +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + +#define ERROR_WINDOW 1 +#define ErrorWindow 1 + + +//extern unsigned char *Palette; +extern unsigned char MDisabled; // Is mouse disabled? +extern WORD Hard_Error_Occured; + +/* +** This is the menu control structures. +*/ +typedef enum MenuIndexType { + MENUX, + MENUY, + ITEMWIDTH, + ITEMSHIGH, + MSELECTED, + NORMCOL, + HILITE, + MENUPADDING=0x1000 +} MenuIndexType; + + +#ifdef NEVER +#define BITSPERBYTE 8 +#define MAXSHORT 0x7FFF +#define HIBITS 0x8000 +#define MAXLONG 0x7FFFFFFFL +#define HIBITL 0x80000000 + +#define MAXINT MAXLONG +#define HIBITI HIBITL + +#define DMAXEXP 308 +#define FMAXEXP 38 +#define DMINEXP -307 +#define FMINEXP -37 + +#define MAXDOUBLE 1.797693E+308 +#define MAXFLOAT 3.37E+38F +#define MINDOUBLE 2.225074E-308 +#define MINFLOAT 8.43E-37F + +#define DSIGNIF 53 +#define FSIGNIF 24 + +#define DMAXPOWTWO 0x3FF +#define FMAXPOWTWO 0x7F +#define DEXPLEN 11 +#define FEXPLEN 8 +#define EXPBASE 2 +#define IEEE 1 +#define LENBASE 1 +#define HIDDENBIT 1 +#define LN_MAXDOUBLE 7.0978E+2 +#define LN_MINDOUBLE -7.0840E+2 +#endif + +/* These defines handle the various names given to the same color. */ +#define DKGREEN GREEN +#define DKBLUE BLUE +#define GRAY GREY +#define DKGREY GREY +#define DKGRAY GREY +#define LTGRAY LTGREY + + +class IconsetClass; +#ifndef WIN32 +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; +#endif + +inline int Get_IconSet_MapWidth(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapWidth); + } + return(0); +} + +inline int Get_IconSet_MapHeight(void const * data) +{ + if (data) { + return(((IControl_Type *)data)->MapHeight); + } + return(0); +} + +inline unsigned char const * Get_IconSet_ControlMap(void const * data) +{ + if (data) { + return((unsigned char const *)((char *)data + ((IControl_Type *)data)->ColorMap)); + } + return(0); +} + +class IconsetClass : protected IControl_Type +{ + public: + /* + ** Query functions. + */ + int Map_Width(void) const {return(MapWidth);}; + int Map_Height(void) const {return(MapHeight);}; + unsigned char * Control_Map(void) {return((unsigned char *)this + ColorMap);}; + unsigned char const * Control_Map(void) const {return((unsigned char const *)this + ColorMap);}; + int Icon_Count(void) const {return(Count);}; + int Pixel_Width(void) const {return(Width);}; + int Pixel_Height(void) const {return(Height);}; + int Total_Size(void) const {return(Size);}; + unsigned char const * Palette_Data(void) const {return((unsigned char const *)this + Palettes);}; + unsigned char * Palette_Data(void) {return((unsigned char *)this + Palettes);}; + unsigned char const * Icon_Data(void) const {return((unsigned char const *)this + Icons);}; + unsigned char * Icon_Data(void) {return((unsigned char *)this + Icons);}; + unsigned char const * Map_Data(void) const {return((unsigned char const *)this + Map);}; + unsigned char * Map_Data(void) {return((unsigned char *)this + Map);}; + unsigned char const * Remap_Data(void) const {return((unsigned char const *)this + Remaps);}; + unsigned char * Remap_Data(void) {return((unsigned char *)this + Remaps);}; + unsigned char const * Trans_Data(void) const {return((unsigned char const *)this + TransFlag);}; + unsigned char * Trans_Data(void) {return((unsigned char *)this + TransFlag);}; + + /* + ** Disallow these operations with an IconsetClass object. + */ + private: + IconsetClass & operator = (IconsetClass const &); + IconsetClass(void); + static void * operator new(size_t); +}; + + +#endif diff --git a/code/redalert/comqueue.cpp b/code/redalert/comqueue.cpp new file mode 100644 index 0000000..8dfe36a --- /dev/null +++ b/code/redalert/comqueue.cpp @@ -0,0 +1,1002 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.cpv 4.1 11 Apr 1996 18:28:16 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * CommQueueClass::Avg_Response_Time -- returns average response time * + * CommQueueClass::CommQueueClass -- class constructor * + * CommQueueClass::Configure_Debug -- sets up special debug values * + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * CommQueueClass::Init -- initializes this queue * + * CommQueueClass::Max_Response_Time -- returns max response time * + * CommQueueClass::Mono_Debug_Print -- Debug output routine * + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * CommQueueClass::Queue_Receive -- queues a received message * + * CommQueueClass::Queue_Send -- queues a message for sending * + * CommQueueClass::Reset_Response_Time -- resets computations * + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * CommQueueClass::~CommQueueClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0) +#include "function.h" + + +/*************************************************************************** + * CommQueueClass::CommQueueClass -- class constructor * + * * + * INPUT: * + * numsend # queue entries for sending * + * numreceive # queue entries for receiving * + * maxlen maximum desired packet length, in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::CommQueueClass(int numsend, int numreceive, int maxlen) +{ + int i; + + /* + ** Init variables + */ + MaxSend = numsend; + MaxReceive = numreceive; + MaxPacketSize = maxlen; + + /* + ** Allocate the queue entries + */ + SendQueue = new SendQueueType[numsend]; + ReceiveQueue = new ReceiveQueueType[numreceive]; + + /* + ** Allocate queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].Buffer = new char[maxlen]; + } + + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].Buffer = new char[maxlen]; + } + + Init(); +} + + +/*************************************************************************** + * CommQueueClass::~CommQueueClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +CommQueueClass::~CommQueueClass() +{ + int i; + + /* + ** Free queue entry buffers + */ + for (i = 0; i < MaxSend; i++) { + delete [] SendQueue[i].Buffer; + } + + for (i = 0; i < MaxReceive; i++) { + delete [] ReceiveQueue[i].Buffer; + } + + delete [] SendQueue; + delete [] ReceiveQueue; +} + + +/*************************************************************************** + * CommQueueClass::Init -- initializes this queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/20/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Init(void) +{ + int i; + + /* + ** Init data members + */ + SendTotal = 0L; + ReceiveTotal = 0L; + + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; + + SendCount = 0; + SendNext = 0; + SendEmpty = 0; + + ReceiveCount = 0; + ReceiveNext = 0; + ReceiveEmpty = 0; + + /* + ** Init the queue entries + */ + for (i = 0; i < MaxSend; i++) { + SendQueue[i].IsActive = 0; + SendQueue[i].IsACK = 0; + SendQueue[i].FirstTime = 0L; + SendQueue[i].LastTime = 0L; + SendQueue[i].SendCount = 0L; + ReceiveQueue[i].BufLen = 0; + } + for (i = 0; i < MaxReceive; i++) { + ReceiveQueue[i].IsActive = 0; + ReceiveQueue[i].IsRead = 0; + ReceiveQueue[i].IsACK = 0; + ReceiveQueue[i].BufLen = 0; + } + + /* + ** Init debug values + */ + DebugOffset = 0; + DebugSize = 0; + DebugNames = NULL; + DebugMaxNames = 0; +} + + +/*************************************************************************** + * CommQueueClass::Queue_Send -- queues a message for sending * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Send(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (SendCount==MaxSend || SendQueue[SendEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + SendQueue[SendEmpty].IsActive = 1; // entry is now active + SendQueue[SendEmpty].IsACK = 0; // entry hasn't been ACK'd + SendQueue[SendEmpty].FirstTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].LastTime = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].SendCount = 0L; // filled in by Manager when sent + SendQueue[SendEmpty].BufLen = buflen; // save buffer size + + /* + ** Copy the packet data + */ + memcpy(SendQueue[SendEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + SendCount++; + SendEmpty++; + if (SendEmpty==MaxSend) { + SendEmpty = 0; + } + + SendTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Send -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Send(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (SendCount==0 || SendQueue[SendNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,SendQueue[SendNext].Buffer,SendQueue[SendNext].BufLen); + (*buflen) = SendQueue[SendNext].BufLen; + } + + /* + ** Set entry flags + */ + SendQueue[SendNext].IsActive = 0; + SendQueue[SendNext].IsACK = 0; + SendQueue[SendNext].FirstTime = 0L; + SendQueue[SendNext].LastTime = 0L; + SendQueue[SendNext].SendCount = 0L; + SendQueue[SendNext].BufLen = 0; + SendCount--; + SendNext++; + if (SendNext==MaxSend) { + SendNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Send -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Next_Send(void) +{ + if (SendCount==0) { + return(NULL); + } else { + return(&SendQueue[SendNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Send -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +SendQueueType * CommQueueClass::Get_Send(int index) +{ + int i; + + i = SendNext + index; + if (i >= MaxSend) { + i -= MaxSend; + } + + if (SendQueue[i].IsActive==0) { + return(NULL); + } else { + return(&SendQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Queue_Receive -- queues a received message * + * * + * INPUT: * + * buf buffer containing the message * + * buflen length of 'buf' * + * * + * OUTPUT: * + * 1 = OK, 0 = no room in the queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::Queue_Receive(void *buf, int buflen) +{ + /* + ** Error if no room in the queue + */ + if (ReceiveCount==MaxReceive || ReceiveQueue[ReceiveEmpty].IsActive!=0) { + return(0); + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveEmpty].IsActive = 1; + ReceiveQueue[ReceiveEmpty].IsRead = 0; + ReceiveQueue[ReceiveEmpty].IsACK = 0; + ReceiveQueue[ReceiveEmpty].BufLen = buflen; + + /* + ** Copy the packet data + */ + memcpy(ReceiveQueue[ReceiveEmpty].Buffer,buf,buflen); + + /* + ** Increment counters & entry ptr + */ + ReceiveCount++; + ReceiveEmpty++; + if (ReceiveEmpty==MaxReceive) { + ReceiveEmpty = 0; + } + + ReceiveTotal++; + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::UnQueue_Receive -- removes next entry from send queue * + * * + * INPUT: * + * buf buffer to store entry's data in; if NULL, it's discarded * + * buflen filled in with length of entry retrieved * + * * + * OUTPUT: * + * 1 = OK, 0 = no entry to retreive * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int CommQueueClass::UnQueue_Receive(void *buf, int *buflen) +{ + /* + ** Error if no entry to retrieve + */ + if (ReceiveCount==0 || ReceiveQueue[ReceiveNext].IsActive==0) { + return(0); + } + + /* + ** Copy the data from the entry + */ + if (buf!=NULL) { + memcpy(buf,ReceiveQueue[ReceiveNext].Buffer, + ReceiveQueue[ReceiveNext].BufLen); + (*buflen) = ReceiveQueue[ReceiveNext].BufLen; + } + + /* + ** Set entry flags + */ + ReceiveQueue[ReceiveNext].IsActive = 0; + ReceiveQueue[ReceiveNext].IsRead = 0; + ReceiveQueue[ReceiveNext].IsACK = 0; + ReceiveQueue[ReceiveNext].BufLen = 0; + ReceiveCount--; + ReceiveNext++; + if (ReceiveNext==MaxReceive) { + ReceiveNext = 0; + } + + return(1); +} + + +/*************************************************************************** + * CommQueueClass::Next_Receive -- gets ptr to next entry in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to entry, NULL if there is none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Next_Receive(void) +{ + if (ReceiveCount==0) { + return(NULL); + } else { + return(&ReceiveQueue[ReceiveNext]); + } +} + + +/*************************************************************************** + * CommQueueClass::Get_Receive -- gets ptr to queue entry * + * * + * This routine gets a pointer to the indicated queue entry. The index * + * value is relative to the next-accessable queue entry; 0 = get the * + * next available queue entry, 1 = get the one behind that, etc. * + * * + * INPUT: * + * index index of entry to get (0 = 1st available) * + * * + * OUTPUT: * + * ptr to entry * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/21/1994 BR : Created. * + *=========================================================================*/ +ReceiveQueueType * CommQueueClass::Get_Receive(int index) +{ + int i; + + i = ReceiveNext + index; + if (i >= MaxReceive) { + i -= MaxReceive; + } + + if (ReceiveQueue[i].IsActive==0) { + return(NULL); + } else { + return(&ReceiveQueue[i]); + } +} + + +/*************************************************************************** + * CommQueueClass::Add_Delay -- adds a new delay value for response time * + * * + * This routine updates the average response time for this queue. The * + * computation is based on the average of the last 'n' delay values given, * + * It computes a running total of the last n delay values, then divides * + * that by n to compute the average. * + * * + * When the number of values given exceeds the max, the mean is subtracted * + * off the total, then the new value is added in. Thus, any single delay * + * value will have an effect on the total that approaches 0 over time, and * + * the new delay value contributes to 1/n of the mean. * + * * + * INPUT: * + * delay value to add into the response time computation * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Add_Delay(unsigned long delay) +{ + int roundoff = 0; + + if (NumDelay==256) { + DelaySum -= MeanDelay; + DelaySum += delay; + if ( (DelaySum & 0x00ff) > 127) { + roundoff = 1; + } + MeanDelay = (DelaySum >> 8) + roundoff; + } else { + NumDelay++; + DelaySum += delay; + MeanDelay = DelaySum / NumDelay; + } + + if (delay > MaxDelay) { + MaxDelay = delay; + } +} + + +/*************************************************************************** + * CommQueueClass::Avg_Response_Time -- returns average response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Avg_Response_Time(void) +{ + return(MeanDelay); +} + + +/*************************************************************************** + * CommQueueClass::Max_Response_Time -- returns max response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * latest computed average response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +unsigned long CommQueueClass::Max_Response_Time(void) +{ + return(MaxDelay); +} + + +/*************************************************************************** + * CommQueueClass::Reset_Response_Time -- resets computations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +void CommQueueClass::Reset_Response_Time(void) +{ + DelaySum = 0L; + NumDelay = 0L; + MeanDelay = 0L; + MaxDelay = 0L; +} + + +/*************************************************************************** + * CommQueueClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * offset ID's byte offset into packet * + * size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * maxnames max # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Configure_Debug(int offset, int size, char **names, + int maxnames) +{ + DebugOffset = offset; + DebugSize = size; + DebugNames = names; + DebugMaxNames = maxnames; +} + + +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * This routine leaves 5 lines at the top for the caller's use. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + static int send_col[] = {1,14,28}; // coords of send queue columns + static int receive_col[] = {40,54,68}; // coords of recv queue columns + int row,col; // current row,col for printing + int num; // max # items to print + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** If few enough entries, call the verbose debug version + */ + if (MaxSend <= 16) { + Mono_Debug_Print2(refresh); + return; + } + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct ACK ID Ct ACK ID Ct ACK³ ID Rd ACK ID Rd ACK ID Rd ACK³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + if (MaxSend <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (send_col[col],row + 8); + if (SendQueue[i].IsActive) { + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + Mono_Printf ("%4d %2d %d",hdr->PacketID, SendQueue[i].SendCount, + SendQueue[i].IsACK); + } else { + Mono_Printf ("____ __ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + + /* + ** Print Receive Queue items + */ + if (MaxReceive <= 48) { + num = MaxSend; + } else { + num = 48; + } + col = 0; + row = 0; + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (receive_col[col],row + 8); + if (ReceiveQueue[i].IsActive) { + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + Mono_Printf ("%4d %d %d",hdr->PacketID, ReceiveQueue[i].IsRead, + ReceiveQueue[i].IsACK); + } else { + Mono_Printf ("____ _ _ "); + } + + row++; + if (row > 15) { + row = 0; + col++; + } + } + +#else + refresh = refresh; +#endif +} + + +/*************************************************************************** + * CommQueueClass::Mono_Debug_Print2 -- Debug output; alternate format * + * * + * This routine prints more information than the other version; it's * + * called only if the number of queue entries is small enough to support * + * this format. * + * * + * INPUT: * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void CommQueueClass::Mono_Debug_Print2(int refresh) +{ +#ifdef WWLIB32_H + int i; // loop counter + char txt[80]; + int val; + + struct CommHdr { // this mirrors the CommHeaderType + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; + } *hdr; + + /* + ** Refresh the screen + */ + if (refresh) { + Mono_Clear_Screen (); + Mono_Printf("ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("³ ³\n"); + Mono_Printf("ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´\n"); + Mono_Printf("³ Send Queue ³ Receive Queue ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ID Ct Type Data Name ACK ³ ID Rd Type Data Name ACK ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("³ ³ ³\n"); + Mono_Printf("ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ"); + } + + /* + ** Print Send Queue items + */ + for (i = 0; i < MaxSend; i++) { + Mono_Set_Cursor (1,8 + i); + + /* + ** Print an active entry + */ + if (SendQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)SendQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + SendQueue[i].SendCount, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(SendQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(SendQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(SendQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", + DebugNames[val], + SendQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",SendQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + + /* + ** Print Receive Queue items + */ + for (i = 0; i < MaxReceive; i++) { + Mono_Set_Cursor (40,8 + i); + + /* + ** Print an active entry + */ + if (ReceiveQueue[i].IsActive) { + + /* + ** Get header info + */ + hdr = (CommHdr *)ReceiveQueue[i].Buffer; + hdr->MagicNumber = hdr->MagicNumber; + hdr->Code = hdr->Code; + sprintf(txt,"%4d %2d %-5s ", + hdr->PacketID, + ReceiveQueue[i].IsRead, + ConnectionClass::Command_Name(hdr->Code)); + + /* + ** Decode app's ID & its name + */ + if (DebugSize && (DebugOffset + DebugSize) <= SendQueue[i].BufLen) { + if (DebugSize==1) { + val = *(ReceiveQueue[i].Buffer + DebugOffset); + } else { + if (DebugSize==2) { + val = *((short *)(ReceiveQueue[i].Buffer + DebugOffset)); + } else { + if (DebugSize==4) { + val = *((int *)(ReceiveQueue[i].Buffer + DebugOffset)); + } + } + } + sprintf(txt + strlen(txt),"%4d ",val); + + if (DebugMaxNames && val > 0 && val < DebugMaxNames) { + sprintf(txt + strlen(txt),"%-12s %x", DebugNames[val], ReceiveQueue[i].IsACK); + } else { + sprintf(txt + strlen(txt)," %x",ReceiveQueue[i].IsACK); + } + } + } else { + + /* + ** Entry isn't active; print blanks + */ + Mono_Printf("____ __ _"); + } + } + +#else + refresh = refresh; +#endif +} + + +#endif \ No newline at end of file diff --git a/code/redalert/comqueue.h b/code/redalert/comqueue.h new file mode 100644 index 0000000..6c5d774 --- /dev/null +++ b/code/redalert/comqueue.h @@ -0,0 +1,190 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\comqueue.h_v 4.1 11 Apr 1996 18:26:02 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COMQUEUE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class's job is to queue up outgoing messages & incoming messages, * + * and serves as a storage area for various flags for ACK & Retry logic. * + * It allows the application to keep track of how many messages have * + * passed through this queue, in & out, so packets can use this as a * + * unique ID. (If packets have a unique ID, the application can use this * + * to detect re-sends.) * + * * + * The queues act as FIFO buffers (First-In, First-Out). The first entry * + * placed in a queue is the first one read from it, and so on. The * + * controlling application must ensure it places the entries on the queue * + * in the order it wants to access them. * + * * + * The queue is implemented as an array of Queue Entries. Index 0 is the * + * first element placed on the queue, and is the first retrieved. Index * + * 1 is the next, and so on. When Index 0 is retrieved, the next-available* + * entry becomes Index 1. The array is circular; when the end is reached, * + * the indices wrap around to the beginning. * + * * + * The class also contains routines to maintain a cumulative response time * + * for this queue. It's up to the caller to call Add_Delay() whenever * + * it detects that an outgoing message has been ACK'd; this class adds * + * that delay into a computed average delay over the last few message * + * delays. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef COMQUEUE_H +#define COMQUEUE_H + + + +/*--------------------------------------------------------------------------- +This is one output queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsACK : 1; // 1 = ACK received for this packet + unsigned long FirstTime; // time this packet was first sent + unsigned long LastTime; // time this packet was last sent + unsigned long SendCount; // # of times this packet has been sent + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} SendQueueType; + +/*--------------------------------------------------------------------------- +This is one input queue entry +---------------------------------------------------------------------------*/ +typedef struct { + unsigned int IsActive : 1; // 1 = this entry is ready to be processed + unsigned int IsRead : 1; // 1 = caller has read this entry + unsigned int IsACK : 1; // 1 = ACK sent for this packet + int BufLen; // size of the packet stored in this entry + char *Buffer; // the data packet +} ReceiveQueueType; + +/* +***************************** Class Declaration ***************************** +*/ +class CommQueueClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /* + ....................... Constructor/Destructor ........................ + */ + CommQueueClass(int numsend, int numrecieve, int maxlen); + virtual ~CommQueueClass(); + void Init(void); + + /* + ......................... Send Queue routines ......................... + */ + int Queue_Send(void *buf, int buflen); // add to Send queue + int UnQueue_Send(void *buf, int *buflen); // remove from Send queue + SendQueueType * Next_Send(void); // ptr to next avail entry + int Num_Send(void) {return (SendCount);} // # entries in queue + int Max_Send(void) { return (MaxSend);} // max # send queue entries + SendQueueType * Get_Send(int index); // random access to queue + unsigned long Send_Total(void) {return (SendTotal);} + + /* + ....................... Receive Queue routines ........................ + */ + int Queue_Receive(void *buf, int buflen); // add to Receive queue + int UnQueue_Receive(void *buf, int *buflen); // remove from Receive queue + ReceiveQueueType * Next_Receive(void); // ptr to next avail entry + int Num_Receive(void) {return (ReceiveCount);} // # entries in queue + int Max_Receive(void) { return (MaxReceive); } // max # recv queue entries + ReceiveQueueType * Get_Receive(int index); // random access to queue + unsigned long Receive_Total(void) {return (ReceiveTotal);} + + /* + ....................... Response time routines ........................ + */ + void Add_Delay(unsigned long delay); // accumulates response time + unsigned long Avg_Response_Time(void); // gets mean response time + unsigned long Max_Response_Time(void); // gets max response time + void Reset_Response_Time(void); // resets computations + + /* + ........................ Debug output routines ........................ + */ + void Configure_Debug(int offset, int size, char **names, int maxnames); + void Mono_Debug_Print(int refresh = 0); + void Mono_Debug_Print2(int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /* + .......................... Limiting variables ......................... + */ + int MaxSend; // max # send queue entries + int MaxReceive; // max # receive queue entries + int MaxPacketSize; // max size of a packet, in bytes + + /* + ....................... Response time variables ....................... + */ + unsigned long DelaySum; // sum of last 4 delay times + unsigned long NumDelay; // current # delay times summed + unsigned long MeanDelay; // current average delay time + unsigned long MaxDelay; // max delay ever for this queue + + /* + ........................ Send Queue variables ......................... + */ + SendQueueType * SendQueue; // incoming packets + int SendCount; // # packets in the queue + int SendNext; // next entry read from queue + int SendEmpty; // next empty spot in queue + unsigned long SendTotal; // total # added to send queue + + /* + ....................... Receive Queue variables ....................... + */ + ReceiveQueueType * ReceiveQueue; // outgoing packets + int ReceiveCount; // # packets in the queue + int ReceiveNext; // next entry read from queue + int ReceiveEmpty; // next empty spot in queue + unsigned long ReceiveTotal; // total # added to receive queue + + /* + ......................... Debugging Variables ......................... + */ + int DebugOffset; // offset into app's packet for ID + int DebugSize; // size of app's ID + char **DebugNames; // ptr to array of app-specific names + int DebugMaxNames; // max # of names in array +}; + +#endif + +/*************************** end of comqueue.h *****************************/ + diff --git a/code/redalert/confdlg.cpp b/code/redalert/confdlg.cpp new file mode 100644 index 0000000..bcfe05c --- /dev/null +++ b/code/redalert/confdlg.cpp @@ -0,0 +1,244 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.cpv 4.67 27 Aug 1996 15:46:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "confdlg.h" + + +bool ConfirmationClass::Process(int text) +{ + text; + return true; + //return(Process(Text_String(text))); +} + +#if (0) // ST - 5/8/2019 +/*********************************************************************************************** + * ConfirmationClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to confirm a deletion. * + * * + * INPUT: char * string - display in edit box. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +bool ConfirmationClass::Process(char const * string) +{ + enum { + NUM_OF_BUTTONS = 2 + }; + + char buffer[80*3]; + int result = true; + int width; + int bwidth, bheight; // button width and height + int height; + int selection = 0; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + strcpy(buffer, string); + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, 200, width, height); + width += 60; + height += 60; + int x = (320 - width) / 2; + int y = (200 - height) / 2; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max( (String_Pixel_Width( Text_String( TXT_YES ) ) + 8), 30); + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + 10, y + height - (bheight + 5), bwidth ); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + x + width - (bwidth + 10), + y + height - (bheight + 5), bwidth ); + + nobtn.Add_Tail(yesbtn); + + curbutton = 1; + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(x, y, width, height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(yesbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_NO, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(yesbtn); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + result = false; + } + } + + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_CONFIRMATION, x, y, width); + Fancy_Text_Print(buffer, x+20, y+30, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Draw the titles. + */ + yesbtn.Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = yesbtn.Input(); + + /* + ** Process Input. + */ + switch (input) { + case KeyNumType(BUTTON_YES | KN_BUTTON): + selection = BUTTON_YES; + pressed = true; + break; + + case (KN_ESC): + case KeyNumType(BUTTON_NO | KN_BUTTON): + selection = BUTTON_NO; + pressed = true; + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = NUM_OF_BUTTONS - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_YES; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_YES): + result = true; + process = false; + break; + + case (BUTTON_NO): + result = false; + process = false; + break; + + default: + break; + } + + pressed = false; + } + } + return(result); +} +#endif \ No newline at end of file diff --git a/code/redalert/confdlg.h b/code/redalert/confdlg.h new file mode 100644 index 0000000..f12a166 --- /dev/null +++ b/code/redalert/confdlg.h @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\confdlg.h_v 4.69 27 Aug 1996 15:43:36 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONFDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 30, 1995 * + * * + * Last Update : Jan 30, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef CONFDLG_H +#define CONFDLG_H + +#include "gadget.h" + +class ConfirmationClass +{ + private: + enum ConfirmationClassEnum { + BUTTON_YES=1, // Button number for "Options menu" + BUTTON_NO // Button number for "Options menu" + }; + + public: + ConfirmationClass(void) { }; + bool Process(char const * string); + bool Process(int text); +}; + +#endif diff --git a/code/redalert/connect.cpp b/code/redalert/connect.cpp new file mode 100644 index 0000000..bd679dd --- /dev/null +++ b/code/redalert/connect.cpp @@ -0,0 +1,835 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNECT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 31, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * ConnectionClass::ConnectionClass -- class constructor * + * ConnectionClass::~ConnectionClass -- class destructor * + * ConnectionClass::Init -- Initializes connection queue to empty * + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * ConnectionClass::Service -- main polling routine; services packets * + * ConnectionClass::Service_Send_Queue -- services the send queue * + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * ConnectionClass::Time -- gets current time * + * ConnectionClass::Command_Name -- returns name for a packet command * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" +#include +//#include +#include +#include "connect.h" + +//#include "WolDebug.h" + +/* +********************************* Globals *********************************** +*/ +char *ConnectionClass::Commands[PACKET_COUNT] = { + "ADATA", + "NDATA", + "ACK" +}; + + +/*************************************************************************** + * ConnectionClass::ConnectionClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::ConnectionClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen) +{ + /*------------------------------------------------------------------------ + Compute our maximum packet length + ------------------------------------------------------------------------*/ + MaxPacketLen = maxlen + sizeof(CommHeaderType); + + /*------------------------------------------------------------------------ + Assign the magic number + ------------------------------------------------------------------------*/ + MagicNum = magicnum; + + /*------------------------------------------------------------------------ + Initialize the retry time. This is the time that t2 - t1 must be greater + than before a retry will occur. + ------------------------------------------------------------------------*/ + RetryDelta = retry_delta; + + /*------------------------------------------------------------------------ + Set the maximum allowable retries. + ------------------------------------------------------------------------*/ + MaxRetries = max_retries; + + /*------------------------------------------------------------------------ + Set the timeout for this connection. + ------------------------------------------------------------------------*/ + Timeout = timeout; + + /*------------------------------------------------------------------------ + Allocate the packet staging buffer. This will be used to + ------------------------------------------------------------------------*/ + PacketBuf = new char[ MaxPacketLen ]; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (placed there + by Receive_Packet), and outgoing packets (placed there by Send_Packet). + It can optionally store "extra" bytes, which are stored along with each + packet, but aren't transmitted as part of the packet. If 'extralen' + is 0, the CommBufferClass ignores this parameter. + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen, extralen); + +} /* end of ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::~ConnectionClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +ConnectionClass::~ConnectionClass () +{ + /*------------------------------------------------------------------------ + Free memory. + ------------------------------------------------------------------------*/ + delete [] PacketBuf; + delete Queue; + +} /* end of ~ConnectionClass */ + + +/*************************************************************************** + * ConnectionClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void ConnectionClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * ConnectionClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*------------------------------------------------------------------------ + Set the magic # for the packet + ------------------------------------------------------------------------*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ------------------------------------------------------------------------*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } + else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*------------------------------------------------------------------------ + Now build the packet + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Add it to the queue; don't add any extra data with it. + ------------------------------------------------------------------------*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType), NULL, 0)) { + if (ack_req) { + NumSendAck++; + } + else { + NumSendNoAck++; + } + return(1); + } + else { + return(0); + } + +} /* end of Send_Packet */ + + +/*************************************************************************** + * ConnectionClass::Receive_Packet -- adds packet to receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /*.................................................................. + Get queue entry ptr + ..................................................................*/ + send_entry = Queue->Get_Send(i); + + /*.................................................................. + If ptr is valid, get ptr to its data + ..................................................................*/ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + } + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*..................................................................... + If there's only one slot left, don't tie up the queue with this packet + .....................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + return(0); + } + + /*..................................................................... + Error if we can't queue the packet + .....................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecNoAck++; + + return(1); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { + /*..................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + .....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { + save_packet = 0; + } + + /*..................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + .....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*..................................................................... + Queue the packet & update our LastSeqID value. + .....................................................................*/ + if (save_packet) { + /*.................................................................. + If there's only one slot left, make sure we only put a packet in it + if this packet will let us increment our LastSeqID; otherwise, we'll + get stuck, forever unable to increment LastSeqID. + ..................................................................*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { + return(0); + } + } + + /*.................................................................. + If we can't queue the packet, return; don't send an ACK. + ..................................................................*/ + if (!Queue->Queue_Receive (buf, buflen, NULL, 0)) { + return(0); + } + + NumRecAck++; + + /*.................................................................. + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ..................................................................*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................... + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ...............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*..................................................................... + Send an ACK, regardless of whether this was a resend or not. + .....................................................................*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; + Send ((char *)&ackpacket, sizeof(CommHeaderType), NULL, 0); + + return(1); + } + + return(0); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * ConnectionClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), + packetlen); + } + (*buflen) = packetlen; + return(1); + } + } + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * ConnectionClass::Service -- main polling routine; services packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error (connection is broken!) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service (void) +{ + /*------------------------------------------------------------------------ + Service the Send Queue: This [re]sends packets in the Send Queue which + haven't been ACK'd yet, and if their retry timeout has expired, and + updates the FirstTime, LastTime & SendCount values in the Queue entry. + Entries that have been ACK'd should be removed. + + Service the Receive Queue: This sends ACKs for packets that haven't + been ACK'd yet. Entries that the app has read, and have been ACK'd, + should be removed. + ------------------------------------------------------------------------*/ + if ( Service_Send_Queue() && Service_Receive_Queue() ) { + return(1); + } + else { + return(0); + } + +} /* end of Service */ + + +/*************************************************************************** + * ConnectionClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /*..................................................................... + Get this queue entry + .....................................................................*/ + send_entry = Queue->Get_Send(i); + + /*..................................................................... + If ACK has been received, unqueue it + .....................................................................*/ + if (send_entry->IsACK) { + + /*.................................................................. + Update this queue's response time + ..................................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /*.................................................................. + Unqueue the packet + ..................................................................*/ + Queue->UnQueue_Send(NULL,NULL,i,NULL,NULL); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + + /*.................................................................. + Send the message + ..................................................................*/ + Send (send_entry->Buffer, send_entry->BufLen, send_entry->ExtraBuffer, + send_entry->ExtraLen); + + /*.................................................................. + Fill in Time fields + ..................................................................*/ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + + /*.................................................................. + Update SendCount + ..................................................................*/ + send_entry->SendCount++; + + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { + return(0); + } + else { + return(1); + } + +} /* end of Service_Send_Queue */ + + +/*************************************************************************** + * ConnectionClass::Service_Receive_Queue -- services receive queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int ConnectionClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + + } + else if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + } + + return(1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * ConnectionClass::Time -- gets current time * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +unsigned long ConnectionClass::Time (void) +{ + static struct timeb mytime; // DOS time + unsigned long msec; + +#ifdef WWLIB32_H + + /*------------------------------------------------------------------------ + If the Westwood timer system has been activated, use TickCount's value + ------------------------------------------------------------------------*/ + if (TimerSystemOn) { + return(TickCount); // Westwood Library time + } + /*------------------------------------------------------------------------ + Otherwise, use the DOS timer + ------------------------------------------------------------------------*/ + else { + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + } + +#else + + /*------------------------------------------------------------------------ + If the Westwood library isn't being used, use the DOS timer. + ------------------------------------------------------------------------*/ + ftime(&mytime); + msec = (unsigned long)mytime.time * 1000L + (unsigned long)mytime.millitm; + return((msec / 100) * 6); + +#endif + +} /* end of Time */ + + +/*************************************************************************** + * ConnectionClass::Command_Name -- returns name for given packet command * + * * + * INPUT: * + * command packet Command value to get name for * + * * + * OUTPUT: * + * ptr to command name, NULL if invalid * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +char *ConnectionClass::Command_Name(int command) +{ + if (command >= 0 && command < PACKET_COUNT) { + return(Commands[command]); + } + else { + return(NULL); + } + +} /* end of Command_Name */ + +/************************** end of connect.cpp *****************************/ + + diff --git a/code/redalert/connect.h b/code/redalert/connect.h new file mode 100644 index 0000000..9bd5da2 --- /dev/null +++ b/code/redalert/connect.h @@ -0,0 +1,278 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNECT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 1, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * DESCRIPTION: * + * This class represents a single "connection" with another system. It's * + * a pure virtual base class that acts as a framework for other classes. * + * * + * This class contains a CommBufferClass member, which stores received * + * & transmitted packets. The ConnectionClass has virtual functions to * + * handle adding packets to the queue, reading them from the queue, * + * a Send routine for actually sending data, and a Receive_Packet function * + * which is used to tell the connection that a new packet has come in. * + * * + * The virtual Service routines handle all ACK & Retry logic for * + * communicating between this system & another. Thus, any class derived * + * from this class may overload the basic ACK/Retry logic. * + * * + * THE PACKET HEADER: * + * The Connection Classes prefix every packet sent with a header that's * + * local to this class. The header contains a "Magic Number" which should * + * be unique for each product, and Packet "Code", which will tell the * + * receiving end if this is DATA, or an ACK packet, and a packet ID, which * + * is a unique numerical ID for this packet (useful for detecting resends).* + * The header is stored with each packet in the send & receive Queues; * + * it's removed before it's passed back to the application, via * + * Get_Packet() * + * * + * THE CONNECTION MANAGER: * + * It is assumed that there will be a "Connection Manager" class which * + * will handle parsing incoming packets; it will then tell the connection * + * that new packets have come in, and the connection will process them in * + * whatever way it needs to for its protocol (check for resends, handle * + * ACK packets, etc). The job of the connection manager is to parse * + * incoming packets & distribute them to the connections that need to * + * store them (for multi-connection protocols). * + * * + * NOTES ON ACK/RETRY: * + * This class provides a "non-sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * but, the performance is better than a "sequenced" approach. Also, the * + * Packet ID scheme (see below) ensures that the application will read * + * the packets in the proper order. Thus, this class guarantees delivery * + * and order of deliver. * + * * + * Each packet has a unique numerical ID; the ID is set to a count of the * + * number of packets sent. Different count values are provided, for both * + * DATA_ACK & DATA_NOACK packets. This ensures that the counter can be * + * used to detect resends of DATA_ACK packets; the counters for DATA_NOACK * + * packets aren't currently used. Other counters keep track of the * + * last-sequentially-received packet ID (for DATA_ACK packets), so we * + * can check for resends & missed packets, and the last-sequentially-read * + * packet ID, so we can ensure the app reads the packets in order. * + * * + * If the protocol being used already guarantees delivery of packets, * + * no ACK is required for the packets. In this case, the connection * + * class for this protocol can overload the Service routine to avoid * + * sending ACK packets, or the Connection Manager can just mark the * + * packet as ACK'd when it adds it to the Receive Queue for the connection.* + * * + * Derived classes must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef CONNECTION_H +#define CONNECTION_H + +/* +********************************* Includes ********************************** +*/ +#include "combuf.h" + +/* +********************************** Defines ********************************** +*/ +#define CONN_DEBUG 0 +/*--------------------------------------------------------------------------- +This structure is the header prefixed to any packet sent by the application. +MagicNumber: This is a number unique to the application; it's up to the + Receive_Packet routine to check this value, to be sure we're + not getting data from some other product. This value should + be unique for each application. +Code: This will be one of the below-defined codes. +PacketID: This is a unique numerical ID for this packet. The Connection + sets this ID on all packets sent out. +---------------------------------------------------------------------------*/ +typedef struct { + unsigned short MagicNumber; + unsigned char Code; + unsigned long PacketID; +} CommHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + These are the possible values for the Code field of the CommHeaderType: + .....................................................................*/ + enum ConnectionEnum { + PACKET_DATA_ACK, // this is a data packet requiring an ACK + PACKET_DATA_NOACK, // this is a data packet not requiring an ACK + PACKET_ACK, // this is an ACK for a packet + PACKET_COUNT // for computational purposes + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + ConnectionClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout, int extralen = 0); + virtual ~ConnectionClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int * buflen); + + /*..................................................................... + The main polling routine for the connection. Should be called as often + as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine is used by the retry logic; returns the current time in + 60ths of a second. + .....................................................................*/ + static unsigned long Time (void); + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned short Magic_Num (void) { return (MagicNum); } + unsigned long Retry_Delta (void) { return (RetryDelta); } + void Set_Retry_Delta (unsigned long delta) { RetryDelta = delta;} + unsigned long Max_Retries (void) { return (MaxRetries); } + void Set_Max_Retries (unsigned long retries) { MaxRetries = retries;} + unsigned long Time_Out (void) { return (Timeout); } + void Set_TimeOut (unsigned long t) { Timeout = t;} + unsigned long Max_Packet_Len (void) { return (MaxPacketLen); } + static char * Command_Name(int command); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue(void); + virtual int Service_Receive_Queue(void); + + /*..................................................................... + This routine actually performs a hardware-dependent data send. It's + pure virtual, so it must be defined by a derived class. The routine + is protected; it's only called by the ACK/Retry logic, not the + application. + .....................................................................*/ + virtual int Send(char *buf, int buflen, void *extrabuf, + int extralen) = 0; + + /*..................................................................... + This is the maximum packet length, including our own internal header. + .....................................................................*/ + int MaxPacketLen; + + /*..................................................................... + Packet staging area; this is where the CommHeaderType gets tacked onto + the application's packet before it's sent. + .....................................................................*/ + char *PacketBuf; + + /*..................................................................... + This is the magic number assigned to this connection. It is the first + few bytes of any transmission. + .....................................................................*/ + unsigned short MagicNum; + + /*..................................................................... + This value determines the time delay before a packet is re-sent. + .....................................................................*/ + unsigned long RetryDelta; + + /*..................................................................... + This is the maximum number of retries allowed for a packet; if this + value is exceeded, the connection is probably broken. + .....................................................................*/ + unsigned long MaxRetries; + + /*..................................................................... + This is the total timeout for this connection; if this time is exceeded + on a packet, the connection is probably broken. + .....................................................................*/ + unsigned long Timeout; + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; + + /*..................................................................... + Names of all packet commands + .....................................................................*/ + static char * Commands[PACKET_COUNT]; +}; + +#endif + +/**************************** end of connect.h *****************************/ diff --git a/code/redalert/connmgr.h b/code/redalert/connmgr.h new file mode 100644 index 0000000..9bdfb0d --- /dev/null +++ b/code/redalert/connmgr.h @@ -0,0 +1,151 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONNMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager base class. This is an abstract base * + * class that's just a shell for more functional derived classes. * + * The main job of the Connection Manager classes is to parse a "pool" of * + * incoming packets, which may be from different computers, and distribute * + * those packets to Connection Classes via their Receive_Packet function. * + * * + * This class should be the only access to the network/modem for the * + * application, so if the app needs any functions to access the * + * connections or the queue's, the derived versions of this class should * + * provide them. * + * * + * It's up to the derived class to define: * + * - Service: polling routine; should Service each connection * + * - Init: initialization; should perform hardware-dependent * + * initialization, then Init each connection; this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Send_Message:sends a packet across the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * - Get_Message: gets a message from the connection (this function * + * isn't defined in this class, since the parameters will * + * be highly protocol-dependent) * + * * + * If the derived class supports multiple connections, it should provide * + * functions for creating the connections, associating them with a name * + * or ID or both, destroying them, and sending data through all or any * + * connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONNMGR_H +#define CONNMGR_H + + +/* +***************************** Class Declaration ***************************** +*/ +class ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONNECTION_NONE = -1 // value of an invalid connection ID + }; + + /*..................................................................... + Constructor/Destructor. These currently do nothing. + .....................................................................*/ + ConnManClass (void) {}; + virtual ~ConnManClass () {}; + + /*..................................................................... + The Service routine: + - Parses incoming packets, and adds them to the Receive Queue for the + Connection Class(s) for this protocol + - Invokes each connection's Service routine; returns an error if the + connection's Service routine indicates an error. + .....................................................................*/ + virtual int Service (void) = 0; + + /*..................................................................... + Sending & receiving data + .....................................................................*/ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE) = 0; + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id) = 0; + + /*..................................................................... + Connection management + .....................................................................*/ + virtual int Num_Connections(void) = 0; + virtual int Connection_ID(int index) = 0; + virtual int Connection_Index(int id) = 0; + + /*..................................................................... + Queue utility routines + .....................................................................*/ + virtual int Global_Num_Send(void) = 0; + virtual int Global_Num_Receive(void) = 0; + virtual int Private_Num_Send(int id = CONNECTION_NONE) = 0; + virtual int Private_Num_Receive(int id = CONNECTION_NONE) = 0; + + /*..................................................................... + Timing management + .....................................................................*/ + virtual void Reset_Response_Time(void) = 0; + virtual unsigned long Response_Time(void) = 0; + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) = 0; + + /*..................................................................... + Debugging + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount) = 0; +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh) = 0; +#endif + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + This abstract class contains no data members; but a derived class + will contain: + - An instance of one or more derived Connection Classes + - A buffer to store incoming packets + .....................................................................*/ +}; + +#endif diff --git a/code/redalert/conquer.cpp b/code/redalert/conquer.cpp new file mode 100644 index 0000000..b047664 --- /dev/null +++ b/code/redalert/conquer.cpp @@ -0,0 +1,5894 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONQUER.CPP 6 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONQUER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 3, 1991 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CC_Draw_Shape -- Custom draw shape handler. * + * Call_Back -- Main game maintenance callback routine. * + * Color_Cycle -- Handle the general palette color cycling. * + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * Disk_Space_Available -- returns bytes of free disk space * + * Do_Record_Playback -- handles saving/loading map pos & current object * + * Fading_Table_Name -- Builds a theater specific fading table name. * + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * Force_CD_Available -- Ensures that specified CD is available. * + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * Handle_Team -- Processes team selection command. * + * Handle_View -- Either records or restores the tactical view. * + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * Keyboard_Process -- Processes the tactical map input codes. * + * Language_Name -- Build filename for current language. * + * List_Copy -- Makes a copy of a cell offset list. * + * Main_Game -- Main game startup routine. * + * Main_Loop -- This is the main game loop (as a single loop). * + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * Message_Input -- allows inter-player message input processing * + * MixFileHandler -- Handles VQ file access. * + * Name_From_Source -- retrieves the name for the given SourceType * + * Owner_From_Name -- Convert an owner name into a bitfield. * + * Play_Movie -- Plays a VQ movie. * + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * Source_From_Name -- Converts ASCII name into SourceType. * + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * Theater_From_Name -- Converts ASCII name into a theater number. * + * Unselect_All -- Causes all selected objects to become unselected. * + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion. + * Is_Aftermath_Installed -- Function to determine the availability of the AM expansion. + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef TESTCODE +class A { + public: + enum {VAR=1}; +}; + +template +class B { + public: + enum {VAR2=T::VAR}; // this is the line in question. +}; + +B test; +#endif + + + +#include "function.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#else +#include "fakesock.h" +TcpipManagerClass Winsock; +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "ccdde.h" +#include "vortex.h" +#include "VQAMOVIE.H" +#include "AUDIOMIX.H" +#include "image.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#define PAGE_RESPOND_KEY KN_RETURN //KN_COMMA +#endif + +#ifdef MPEGMOVIE +#ifdef MCIMPEG +#include "mcimovie.h" +#endif +#include "movie.h" +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user); +#endif + +#define SHAPE_TRANS 0x40 + +void * Get_Shape_Header_Data(void * ptr); +extern bool Spawn_WChat(bool can_launch); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void); +#endif + +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef FIXIT_VERSION_3 // Stalemate games. +extern void Do_Draw(void); +#endif + +#ifdef CHEAT_KEYS +bool bNoMovies = false; +#endif + +KeyNumType g_globalKeyNumType = KN_NONE; +int g_globalKeyFlags = 0; + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +bool Main_Loop(void); +void Keyboard_Process(KeyNumType & input); +static void Message_Input(KeyNumType &input); +void Color_Cycle(void); +bool Map_Edit_Loop(void); + +extern "C" { + bool UseOldShapeDraw = false; +} + +#ifdef CHEAT_KEYS +void Dump_Heap_Pointers( void ); +void Error_In_Heap_Pointers( char * string ); +#endif +static void Do_Record_Playback(void); + +void Toggle_Formation(void); + +extern "C" { + extern char * __nheapbeg; +} + +// +// Special module globals for recording and playback +// +char TeamEvent = 0; // 0 = no event, 1,2,3 = team event type +char TeamNumber = 0; // which team was selected? (1-9) +char FormationEvent = 0; // 0 = no event, 1 = formation was toggled + +bool g_inMainMenu = false; + + + + /* -----------------10/14/96 7:29PM------------------ + + --------------------------------------------------*/ + +#if(TEN) +void TEN_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +/*********************************************************************************************** + * Main_Game -- Main game startup routine. * + * * + * This is the first official routine of the game. It handles game initialization and * + * the main game loop control. * + * * + * Initialization: * + * - Init_Game handles one-time-only inits * + * - Select_Game is responsible for initializations required for each new game played * + * (these may be different depending on whether a multiplayer game is selected, and * + * other parameters) * + * - This routine performs any un-inits required, both for each game played, and one-time * + * * + * INPUT: argc -- Number of command line arguments (including program name itself). * + * * + * argv -- Array of command line argument pointers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +void Main_Game(int argc, char * argv[]) +{ + static bool fade = true; + + /* + ** Perform one-time-only initializations + */ + if (!Init_Game(argc, argv)) { + return; + } + + /* + ** Game processing loop: + ** 1) Select which game to play, or whether to exit (don't fade the palette + ** on the first game selection, but fade it in on subsequent calls) + ** 2) Invoke either the main-loop routine, or the editor-loop routine, + ** until they indicate that the user wants to exit the scenario. + */ + while (Select_Game(fade)) { + + // ST 5/14/2019 + if (RunningAsDLL) { + return; + } + + fade = false; + ScenarioInit = 0; // Kludge. + + fade = true; + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + /* + ** Make the game screen visible, clear the keyboard buffer of spurious + ** values, and then show the mouse. This PRESUMES that Select_Game() has + ** told the map to draw itself. + */ + GamePalette.Set(FADE_PALETTE_MEDIUM); + Keyboard->Clear(); + /* + ** Only show the mouse if we're not playing back a recording. + */ + if (Session.Play) { + Hide_Mouse(); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } else { + Show_Mouse(); + } + +#ifdef WIN32 + if (Session.Type == GAME_INTERNET) { + Register_Game_Start_Time(); + GameStatisticsPacketSent = false; + PacketLater = NULL; + ConnectionLost = false; + } else { +#ifndef WOLAPI_INTEGRATION + DDEServer.Disable(); +#endif // !WOLAPI_INTEGRATION + } +#endif //WIN32 + g_inMainMenu = false; + +#ifdef SCENARIO_EDITOR + /* + ** Scenario-editor version of main-loop processing + */ + for (;;) { + /* + ** Non-scenario-editor-mode: call the game's main loop + */ + if (!Debug_Map) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + if (Main_Loop()) { + break; + } + + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + PlayerPtr->Flag_To_Lose(); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + default: + break; + } + } + } else { + + /* + ** Scenario-editor-mode: call the editor's main loop + */ + if (Map_Edit_Loop()) { + break; + } + } + } +#else + /* + ** Non-editor version of main-loop processing + */ + for (;;) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + /* + ** Call the game's main loop + */ + if (Main_Loop()) { + break; + } + + /* + ** If the SpecialDialog flag is set, invoke the given special dialog. + ** This must be done outside the main loop, since the dialog will call + ** Main_Loop(), allowing the game to run in the background. + */ + if (SpecialDialog != SDLG_NONE) { + switch (SpecialDialog) { + case SDLG_SPECIAL: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Special_Dialog(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_OPTIONS: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + Options.Process(); + Map.Revert_Mouse_Shape(); + SpecialDialog = SDLG_NONE; + break; + + case SDLG_SURRENDER: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + +/*ifdef FIXIT_VERSION_3 // Stalemate games. + case SDLG_PROPOSE_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_PROPOSE_DRAW)) { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; + + case SDLG_ACCEPT_DRAW: + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + if (Surrender_Dialog(TXT_WOL_ACCEPT_DRAW)) { + OutList.Add(EventClass(EventClass::ACCEPT_DRAW)); + } + SpecialDialog = SDLG_NONE; + Map.Revert_Mouse_Shape(); + break; +#endif +*/ + default: + break; + } + } + + Device_Present(); + } +#endif + + +#ifdef WIN32 + /* + ** Send the game stats to WChat if we haven't already done so + */ + if (!GameStatisticsPacketSent && PacketLater) { + Send_Statistics_Packet(); // After game sending if PacketLater set. + } +#endif //WIN32 + + /* + ** Scenario is done; fade palette to black + */ + BlackPalette.Set(FADE_PALETTE_SLOW); + VisiblePage.Clear(); + + /* + ** Un-initialize whatever needs it, for each game played. + ** + ** Shut down either the modem or network; they'll get re-initialized if + ** the user selections those options again in Select_Game(). This + ** "re-boots" the modem & network code, which I currently feel is safer + ** than just letting it hang around. + ** (Skip this step if we're in playback mode; the modem or net won't have + ** been initialized in that case.) + */ + if (Session.Record || Session.Play) { + Session.RecordFile.Close(); + } + + if (Session.Type == GAME_NULL_MODEM || Session.Type == GAME_MODEM) { + if (!Session.Play) { + //PG Modem_Signoff(); + } + } else { + if (Session.Type == GAME_IPX) { + if (!Session.Play) { + //PG Shutdown_Network(); + } + } + } + +#if(TEN) + + if (Session.Type == GAME_TEN) { + Shutdown_TEN(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + Shutdown_MPATH(); + //Prog_End(); + Emergency_Exit(0); + } +#endif // MPATH + + /* + ** If we're playing back, the mouse will be hidden; show it. + ** Also, set all variables back to normal, to return to the main menu. + */ + if (Session.Play) { + Show_Mouse(); + Session.Type = GAME_NORMAL; + Session.Play = 0; + } +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + if (Special.IsFromWChat) { + //PG Shutdown_Network(); // Clear up the pseudo IPX stuff +#ifndef WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Special.IsFromWChat = false; + SpawnedFromWChat = false; + DDEServer.Delete_MPlayer_Game_Info(); //Make sure we dont use the same start packet twice + Session.Type = GAME_NORMAL; //Have to do this or we will got straight to the multiplayer menu + Spawn_WChat(false); //Will switch back to Wchat. It must be there because its been poking us + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + Device_Present(); + } + + /* + ** Free the scenario description buffers + */ + Session.Free_Scenario_Descriptions(); +} + + +/*********************************************************************************************** + * Keyboard_Process -- Processes the tactical map input codes. * + * * + * This routine is used to process the input codes while the player * + * has the tactical map displayed. It handles all the keys that * + * are appropriate to that mode. * + * * + * INPUT: input -- Input code as returned from Input_Num(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1992 JLB : Created. * + * 07/04/1995 JLB : Handles team and map control hotkeys. * + *=============================================================================================*/ +void Keyboard_Process(KeyNumType & input) +{ + ObjectClass * obj; + int index; + + /* + ** Don't do anything if there is not keyboard event. + */ + if (input == KN_NONE) { + return; + } + /* + ** For network & modem, process user input for inter-player messages. + */ + Message_Input(input); + +#ifdef WIN32 + /* + ** The VK_BIT must be stripped from the "plain" value of the key so that a comparison to + ** KN_1, for example, will yield TRUE if in fact the "1" key was pressed. + */ + + KeyNumType plain = KeyNumType(input & ~(WWKEY_SHIFT_BIT|WWKEY_ALT_BIT|WWKEY_CTRL_BIT|WWKEY_VK_BIT)); + KeyNumType key = KeyNumType(input & ~WWKEY_VK_BIT); + + +#else + KeyNumType plain = KeyNumType(input & ~(KN_SHIFT_BIT|KN_ALT_BIT|KN_CTRL_BIT)); + KeyNumType key = plain; +#endif + +#ifdef CHEAT_KEYS + + if (Debug_Flag) { + HousesType h; + + switch (int(input)) { + case int(int(KN_M) | int(KN_SHIFT_BIT)): + case int(int(KN_M) | int(KN_ALT_BIT)): + case int(int(KN_M) | int(KN_CTRL_BIT)): + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + Houses.Ptr(h)->Refund_Money(10000); + } + break; + + default: + break; + } + } +#endif + +#ifdef VIRGIN_CHEAT_KEYS + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } +#endif + +#ifdef CHEAT_KEYS +#ifdef WIN32 + if (Debug_Playtest && input == (KA_W|KN_ALT_BIT)) { +#else + if (Debug_Playtest && input == (KN_W|KN_ALT_BIT)) { +#endif + PlayerPtr->Blockage = false; + PlayerPtr->Flag_To_Win(); + } + + if ((Debug_Flag || Debug_Playtest) && plain == KN_F4) { + if (Session.Type == GAME_NORMAL) { + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + } + } + + if (Debug_Flag && input == KN_SLASH) { + if (Session.Type != GAME_NORMAL) { + SpecialDialog = SDLG_SPECIAL; + input = KN_NONE; + } else { + Special_Dialog(); + } + } +#endif + + /* + ** Process prerecorded team selection. This will be an additive select + ** if the SHIFT key is held down. It will create the team if the + ** CTRL or ALT key is held down. + */ + int action = 0; +#ifdef WIN32 + if (input & WWKEY_SHIFT_BIT) action = 1; + if (input & WWKEY_ALT_BIT) action = 3; + if (input & WWKEY_CTRL_BIT) action = 2; +#else + if (input & KN_SHIFT_BIT) action = 1; + if (input & KN_ALT_BIT) action = 3; + if (input & KN_CTRL_BIT) action = 2; +#endif + + /* + ** If the "N" key is pressed, then select the next object. + */ + if (key != 0 && key == Options.KeyNext) { + if (action) { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + if (key != 0 && key == Options.KeyPrevious) { + if (action) { + obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } else { + obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + } + if (obj != NULL) { + Unselect_All(); + obj->Select(); + Map.Center_Map(); + Map.Flag_To_Redraw(true); + } + input = KN_NONE; + } + + + /* + ** All selected units will go into idle mode. + */ + if (key != 0 && key == Options.KeyStop) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to go into guard area mode. + */ + if (key != 0 && key == Options.KeyGuard) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } + input = KN_NONE; + } + + /* + ** All selected units will attempt to scatter. + */ + if (key != 0 && key == Options.KeyScatter) { + if (CurrentObject.Count()) { + for (index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } + input = KN_NONE; + } + + /* + ** Center the map around the currently selected objects. If no + ** objects are selected, then fall into the home case. + */ + if (key != 0 && (key == Options.KeyHome1 || key == Options.KeyHome2)) { + if (CurrentObject.Count()) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif + input = KN_NONE; + } else { + input = Options.KeyBase; + } + } + + /* + ** Center the map about the construction yard or construction vehicle + ** if one is present. + */ + if (key != 0 && key == Options.KeyBase) { + Unselect_All(); + + if (PlayerPtr->CurBuildings) { + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr && *building == STRUCT_CONST) { + Unselect_All(); + building->Select(); + if (building->IsLeader) break; + } + } + } + if (CurrentObject.Count() == 0 && PlayerPtr->CurUnits) { + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit != NULL && !unit->IsInLimbo && unit->House == PlayerPtr && *unit == UNIT_MCV) { + Unselect_All(); + unit->Select(); + break; + } + } + } + if (CurrentObject.Count()) { + Map.Center_Map(); + } else { + if (PlayerPtr->Center != 0) { + Map.Center_Map(PlayerPtr->Center); + } + } + Map.Flag_To_Redraw(true); + input = KN_NONE; + } + + /* + ** Toggle the status of formation for the current team + */ + if (key != 0 && key == Options.KeyFormation) { + Toggle_Formation(); + input = KN_NONE; + } + +#ifdef TOFIX + /* + ** For multiplayer, 'R' pops up the surrender dialog. + */ + if (input != 0 && input == Options.KeyResign) { + if (!PlayerLoses && /*Session.Type != GAME_NORMAL &&*/ !PlayerPtr->IsDefeated) { + SpecialDialog = SDLG_SURRENDER; + input = KN_NONE; + } + input = KN_NONE; + } +#endif + + /* + ** Handle making and breaking alliances. + */ + if (key != 0 && key == Options.KeyAlliance) { + if (Session.Type != GAME_NORMAL || Debug_Flag) { + if (CurrentObject.Count() && !PlayerPtr->IsDefeated) { + if (CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + OutList.Add(EventClass(EventClass::ALLY, CurrentObject[0]->Owner())); + } + } + } + input = KN_NONE; + } + + /* + ** Select all the units on the current display. This is equivalent to + ** drag selecting the whole view. + */ + if (key != 0 && key == Options.KeySelectView) { + Map.Select_These(0x00000000, XY_Coord(Map.TacLeptonWidth, Map.TacLeptonHeight)); + input = KN_NONE; + } + + /* + ** Toggles the repair state similarly to pressing the repair button. + */ + if (key != 0 && key == Options.KeyRepair) { + Map.Repair_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the sell state similarly to pressing the sell button. + */ + if (key != 0 && key == Options.KeySell) { + Map.Sell_Mode_Control(-1); + input = KN_NONE; + } + + /* + ** Toggles the map zoom mode similarly to pressing the map button. + */ + if (key != 0 && key == Options.KeyMap) { + Map.Zoom_Mode_Control(); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar up one slot. + */ + if (key != 0 && key == Options.KeySidebarUp) { + Map.SidebarClass::Scroll(true, -1); + input = KN_NONE; + } + + /* + ** Scrolls the sidebar down one slot. + */ + if (key != 0 && key == Options.KeySidebarDown) { + Map.SidebarClass::Scroll(false, -1); + input = KN_NONE; + } + + /* + ** Brings up the options dialog box. + */ + if (key != 0 && (key == Options.KeyOption1 || key == Options.KeyOption2)) { + Map.Help_Text(TXT_NONE); // Turns off help text. + Queue_Options(); + input = KN_NONE; + } + + /* + ** Scrolls the tactical map in the direction specified. + */ + int distance = CELL_LEPTON_W; + if (key != 0 && key == KN_LEFT) { + Map.Scroll_Map(DIR_W, distance, true); + input = KN_NONE; + } + if (key != 0 && key == KN_RIGHT) { + Map.Scroll_Map(DIR_E, distance, true); + input = KN_NONE; + } + if (key != 0 && key == KN_UP) { + Map.Scroll_Map(DIR_N, distance, true); + input = KN_NONE; + } + if (key != 0 && key == KN_DOWN) { + Map.Scroll_Map(DIR_S, distance, true); + input = KN_NONE; + } + + /* + ** Teams are handled by the 10 special team keys. The manual comparison + ** to the KN numbers is because the Windows keyboard driver can vary + ** the base code number for the key depending on the shift or alt key + ** state! + */ + if (input != 0 && (plain == Options.KeyTeam1 || plain == KN_1)) { + Handle_Team(1, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam2 || plain == KN_2)) { + Handle_Team(2, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam3 || plain == KN_3)) { + Handle_Team(3, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam4 || plain == KN_4)) { + Handle_Team(4, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam5 || plain == KN_5)) { + Handle_Team(5, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam6 || plain == KN_6)) { + Handle_Team(6, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam7 || plain == KN_7)) { + Handle_Team(7, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam8 || plain == KN_8)) { + Handle_Team(8, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam9 || plain == KN_9)) { + Handle_Team(9, action); + input = KN_NONE; + } + if (input != 0 && (plain == Options.KeyTeam10 || plain == KN_0)) { + Handle_Team(0, action); + input = KN_NONE; + } + + /* + ** Handle the bookmark hotkeys. + */ + if (input != 0 && plain == Options.KeyBookmark1 && !Debug_Map) { + Handle_View(0, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark2 && !Debug_Map) { + Handle_View(1, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark3 && !Debug_Map) { + Handle_View(2, action); + input = KN_NONE; + } + if (input != 0 && plain == Options.KeyBookmark4 && !Debug_Map) { + Handle_View(3, action); + input = KN_NONE; + } + +#ifdef CHEAT_KEYS + if (input != 0 && Debug_Flag && input && (input & KN_RLSE_BIT) == 0) { + Debug_Key(input); + } +#endif +} + + +void Toggle_Formation(void) { + +// MBL 03.23.2020: this has been copied to DLLExportClass::Team_Units_Formation_Toggle_On(), and modified as needed +#if 0 + int team = -1; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + if (team == -1) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->IsSelected) { + team = obj->Group; + if (team != -1) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + + if (team == -1) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +#endif +} + + +/*********************************************************************************************** + * Message_Input -- allows inter-player message input processing * + * * + * INPUT: * + * input key value * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=============================================================================================*/ +//#pragma off (unreferenced) +static void Message_Input(KeyNumType &input) +{ + int rc; + char txt[MAX_MESSAGE_LENGTH+32]; + int id; +// SerialPacketType * serial_packet; + //int i; + KeyNumType copy_input; + //char *msg; + + /* + ** Check keyboard input for a request to send a message. + ** The 'to' argument for Add_Edit is prefixed to the message buffer; the + ** message buffer is big enough for the 'to' field plus MAX_MESSAGE_LENGTH. + ** To send the message, calling Get_Edit_Buf retrieves the buffer minus the + ** 'to' portion. At the other end, the buffer allocated to display the + ** message must be MAX_MESSAGE_LENGTH plus the size of "From: xxx (house)". + */ +#ifdef WOLAPI_INTEGRATION + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + ( ( input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) ) || input == PAGE_RESPOND_KEY ) && + !Session.Messages.Is_Edit()) { +#else + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && input >= KN_F1 && input < (KN_F1 + Session.MaxPlayers) && !Session.Messages.Is_Edit()) { +#endif + memset (txt, 0, 40); + + /* + ** For a serial game, send a message on F1 or F4; set 'txt' to the + ** "Message:" string & add an editable message to the list. + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + if (input==KN_F1 || input==(KN_F1 + Session.MaxPlayers - 1)) { + + strcpy(txt, Text_String(TXT_MESSAGE)); // "Message:" + + Session.Messages.Add_Edit (Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } else if ((Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) && !Session.Messages.Is_Edit()) { + /* + ** For a network game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MessageAddress = IPXAddressClass(); // set to broadcast + strcpy(txt, Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + +#ifdef WOLAPI_INTEGRATION + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan && input != PAGE_RESPOND_KEY ) { +#else + } else if ((input - KN_F1) < Ipx.Num_Connections() && !Session.ObiWan) { +#endif + id = Ipx.Connection_ID(input - KN_F1); + Session.MessageAddress = (*(Ipx.Connection_Address (id))); + sprintf(txt, Text_String(TXT_TO), Ipx.Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } +#ifdef WOLAPI_INTEGRATION + else if( Session.Type == GAME_INTERNET && pWolapi && !pWolapi->bConnectionDown && input == PAGE_RESPOND_KEY ) + { + if( *pWolapi->szExternalPager ) + { + // Respond to a page from external ww online user that paged me. + // Set MessageAddress to all zeroes, as a flag to ourselves later on. + NetNumType blip; + NetNodeType blop; + memset( blip, 0, 4 ); + memset( blop, 0, 6 ); + Session.MessageAddress = IPXAddressClass( blip, blop ); + + // Tell pWolapi not to reset szExternalPager for the time being. + pWolapi->bFreezeExternalPager = true; + + sprintf( txt, Text_String( TXT_TO ), pWolapi->szExternalPager ); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + Keyboard->Clear(); + } + else + { + Session.Messages.Add_Message( NULL, 0, TXT_WOL_NOTPAGED, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( VOC_SYS_ERROR ); + } + } +#endif + } +#if(TEN) + /* + ** For a TEN game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_TEN && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.TenMessageAddress = -1; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < Ten->Num_Connections() && !Session.ObiWan) { + + id = Ten->Connection_ID(input - KN_F1); + Session.TenMessageAddress = Ten->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),Ten->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // TEN +#if(MPATH) + /* + ** For a MPATH game: + ** F1-F7 = "To (house):" (only allowed if we're not in ObiWan mode) + ** F8 = "To All:" + */ + else if (Session.Type == GAME_MPATH && !Session.Messages.Is_Edit()) { + if (input==(KN_F1 + Session.MaxPlayers - 1)) { + + Session.MPathMessageAddress = 0; // set to broadcast + strcpy(txt,Text_String(TXT_TO_ALL)); // "To All:" + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + + } else if ((input - KN_F1) < MPath->Num_Connections() && !Session.ObiWan) { + + id = MPath->Connection_ID(input - KN_F1); + Session.MPathMessageAddress = MPath->Connection_Address(id); + sprintf(txt,Text_String(TXT_TO),MPath->Connection_Name(id)); + + Session.Messages.Add_Edit(Session.ColorIdx, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, txt, 0, 232 * RESFACTOR); + + Map.Flag_To_Redraw(false); + } + } +#endif // MPATH + } + + /* + ** Process message-system input; send the message out if RETURN is hit. + */ + copy_input = input; + rc = Session.Messages.Input(input); + + /* + ** If a single character has been added to an edit buffer, update the display. + */ + if (rc == 1 && Session.Type != GAME_NORMAL) { + Map.Flag_To_Redraw(false); + } + + /* + ** If backspace was hit, redraw the map. If the edit message was removed, + ** the map must be force-drawn, since it won't be able to compute the + ** cells to redraw; otherwise, let the map compute the cells to redraw, + ** by not force-drawing it, but just setting the IsToRedraw bit. + */ + if (rc==2 && Session.Type != GAME_NORMAL) { + if (copy_input==KN_ESC) { + Map.Flag_To_Redraw(true); +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + // Just in case user was responding to a page from outside the game, and we had frozen the "szExternalPager". + pWolapi->bFreezeExternalPager = false; +#endif + } else { + Map.Flag_To_Redraw(false); + } + Map.DisplayClass::IsToRedraw = true; + } + + /* + ** Send a message + */ + if ((rc==3 || rc==4) && Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) { +#if (0) + /* + ** Serial game: fill in a SerialPacketType & send it. + ** (Note: The size of the SerialPacketType.Command must be the same as + ** the EventClass.Type!) + */ + if (Session.Type==GAME_NULL_MODEM || Session.Type==GAME_MODEM) { + serial_packet = (SerialPacketType *)NullModem.BuildBuf; + + serial_packet->Command = SERIAL_MESSAGE; + strcpy (serial_packet->Name, Session.Players[0]->Name); + serial_packet->ID = Session.ColorIdx; + + if (rc==3) { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (serial_packet->Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** Send the message, and store this message in our LastMessage + ** buffer; the computer may send us a version of it later. + */ + NullModem.Send_Message(NullModem.BuildBuf, + sizeof(SerialPacketType), 1); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + strcpy(Session.LastMessage, serial_packet->Message.Message); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { +#ifdef WOLAPI_INTEGRATION + NetNumType blip; + NetNodeType blop; + Session.MessageAddress.Get_Address( blip, blop ); + if( blip[0] + blip[1] + blip[2] + blip[3] + blop[0] + blop[1] + blop[2] + blop[3] + blop[4] + blop[5] == 0 ) + { + // This message is a response to the last person that paged me. + if( pWolapi && !pWolapi->bConnectionDown ) // (As connection may have gone down.) + { + pWolapi->Page( pWolapi->szExternalPager, Session.Messages.Get_Edit_Buf(), false ); + pWolapi->bFreezeExternalPager = false; + } + } + else +#endif + { + + /* + ** Network game: fill in a GlobalPacketType & send it. + */ + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /* + ** If 'F4' was hit, MessageAddress will be a broadcast address; send + ** the message to every player we have a connection with. + */ + if (Session.MessageAddress.Is_Broadcast()) { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + char *ptr = &Session.GPacket.Message.Buf[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + *ptr = 'X'; // force it to an odd hack so we know it was broadcast. + Enable_Secret_Units(); + } + #endif + for (i = 0; i < Ipx.Num_Connections(); i++) { + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + Ipx.Connection_Address(Ipx.Connection_ID(i))); + Ipx.Service(); + } + } else { + + /* + ** Otherwise, MessageAddress contains the exact address to send to. + ** Send to that address only. + */ + Ipx.Send_Global_Message(&Session.GPacket, + sizeof(GlobalPacketType), 1, + &Session.MessageAddress); + Ipx.Service(); + + } + + /* + ** Store this message in our LastMessage buffer; the computer may send + ** us a version of it later. + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + +#if(TEN) + /* + ** TEN game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_TEN) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + Ten->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.TenMessageAddress); + } +#endif // TEN + +#if(MPATH) + /* + ** MPATH game: fill in a GlobalPacketType & send it. + */ + else if (Session.Type == GAME_MPATH) { + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = Compute_Name_CRC(Session.GameName); + + if (rc==3) { + strcpy (Session.GPacket.Message.Buf, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + MPath->Send_Global_Message(&Session.GPacket, sizeof(GlobalPacketType), + 1, Session.MPathMessageAddress); + } +#endif // MPATH + + /* + ** Tell the map to completely update itself, since a message is now missing. + */ + Map.Flag_To_Redraw(true); +#endif + } +} +//#pragma on (unreferenced) + + +/*********************************************************************************************** + * Color_Cycle -- Handle the general palette color cycling. * + * * + * This is a maintenance routine that handles the color cycling. It should be called as * + * often as necessary to achieve smooth color cycling effects -- at least 8 times a second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/10/1994 JLB : Uses new cycle color values. * + * 12/21/1994 JLB : Handles text fade color. * + * 07/16/1996 JLB : Faster pulsing of white color. * + *=============================================================================================*/ +void Color_Cycle(void) +{ + static CDTimerClass _timer; + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + bool changed = false; + + if (Options.IsPaletteScroll) { + /* + ** Process the fading white color. It is used for the radar box and other glowing + ** game interface elements. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + InGamePalette[CC_PULSE_COLOR] = GamePalette[WHITE]; + InGamePalette[CC_PULSE_COLOR].Adjust(val, BlackColor); + + /* + ** Pulse the glowing embers between medium and dark red. + */ + InGamePalette[CC_EMBER_COLOR] = RGBClass(255, 80, 80); + InGamePalette[CC_EMBER_COLOR].Adjust(val, BlackColor); + + changed = true; + } + + /* + ** Process the color cycling effects -- water. + */ + if (!_timer) { + _timer = TIMER_SECOND/4; + + RGBClass first = InGamePalette[CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1]; + for (int index = CYCLE_COLOR_START+CYCLE_COLOR_COUNT-1; index >= CYCLE_COLOR_START; index--) { + InGamePalette[index] = InGamePalette[index-1]; + } + InGamePalette[CYCLE_COLOR_START] = first; + + changed = true; + } + + /* + ** If any of the processing functions changed the palette, then this palette must be + ** passed to the system. + */ + if (changed) { + BStart(BENCH_PALETTE); + InGamePalette.Set(); +// Set_Palette(InGamePalette); + BEnd(BENCH_PALETTE); + } + } +} + + +/*********************************************************************************************** + * Call_Back -- Main game maintenance callback routine. * + * * + * This routine handles all the "real time" processing that needs to * + * occur. This includes palette fading and sound updating. It needs * + * to be called as often as possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Call_Back(void) +{ + Device_Present(); + + /* + ** Music and speech maintenance + */ + if (SampleType) { + Sound_Callback(); + Theme.AI(); + Speak_AI(); + } + + /* + ** Network maintenance. + */ + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + IPX_Call_Back(); + } + + /* + ** Serial game maintenance. + */ + if (Session.Type == GAME_NULL_MODEM || ((Session.Type == GAME_MODEM) && Session.ModemService)) { + //NullModem.Service(); ST - 5/7/2019 + } + +#ifdef WOLAPI_INTEGRATION + // Wolapi maintenance. + if( pWolapi ) + { + if( pWolapi->bInGame ) + { + if( !pWolapi->bConnectionDown && ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT + 700; // Slower pump during games. + if( pWolapi->bConnectionDown ) + { + // Connection to server lost. + Session.Messages.Add_Message( NULL, 0, TXT_WOL_WOLAPIGONE, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + Sound_Effect( WOLSOUND_LOGOUT ); +// ajw (Wolapi object is now left around, so we can try to send game results.) +// // Kill wolapi. +// pWolapi->UnsetupCOMStuff(); +// delete pWolapi; +// pWolapi = NULL; + } + } + } + else + { + // When showing a modal dialog during chat, this pumping is turned on. It's turned off immediately following. + if( pWolapi->bPump_In_Call_Back && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->pNetUtil->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + } + } +#endif + +#if(TEN) + if (Session.Type == GAME_TEN) { + TEN_Call_Back(); + } +#endif // TEN + +#if(MPATH) + if (Session.Type == GAME_MPATH) { + MPATH_Call_Back(); + } +#endif // MPATH +} + + +void IPX_Call_Back(void) +{ +#if (0)//PG + Ipx.Service(); + + /* + ** Read packets only if the game is "closed", so we don't steal global + ** messages from the connection dialogs. + */ + if (!Session.NetOpen) { + if (Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, &Session.GAddress, &Session.GProductID)) { + + if (Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + /* + ** If this is another player signing off, remove the connection & + ** mark that player's house as non-human, so the computer will take + ** it over. + */ + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ipx.Num_Connections(); i++) { + + int id = Ipx.Connection_ID(i); + + if (Session.GAddress == (*Ipx.Connection_Address(id))) { + Destroy_Connection(id, 0); + } + } + } else { + + /* + ** Process a message from another user. + */ + + if (Session.GPacket.Command == NET_MESSAGE) { + bool msg_ok = false; + + /* + ** If NetProtect is set, make sure this message came from within + ** this game. + */ + if (!Session.NetProtect) { + msg_ok = true; + } else { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + msg_ok = true; + } else { + msg_ok = false; + } + } + + if (msg_ok) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (NewUnitsEnabled && !strncmp(Session.GPacket.Message.Buf,"XECRET UNITS ON ",15)) { + Session.GPacket.Message.Buf[0]='S'; + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } else { + Process_Global_Packet(&Session.GPacket, &Session.GAddress); + } + } + } + } + } +#endif +} + + +#if(TEN) +void TEN_Call_Back(void) +{ + int id; + + Ten->Service(); + + if (Ten->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.TenAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < Ten->Num_Connections(); i++) { + + id = Ten->Connection_ID(i); + + if (Session.TenAddress == Ten->Connection_Address(id)) { + Destroy_TEN_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // TEN + + +#if(MPATH) +void MPATH_Call_Back(void) +{ + int id; + + MPath->Service(); + + if (MPath->Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.MPathAddress)) { + + // + // If this is another player signing off, remove the connection & + // mark that player's house as non-human, so the computer will take + // it over. + // + if (Session.GPacket.Command == NET_SIGN_OFF) { + for (int i = 0; i < MPath->Num_Connections(); i++) { + + id = MPath->Connection_ID(i); + + if (Session.MPathAddress == MPath->Connection_Address(id)) { + Destroy_MPATH_Connection(id, 0); + } + } + } + + // + // Process a message from another user. + // + else if (Session.GPacket.Command == NET_MESSAGE) { + if (!Session.Messages.Concat_Message(Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, Rule.MessageDelay * TICKS_PER_MINUTE)) { + + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + Session.GPacket.Message.Color, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | + TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + /* + ** Tell the map to do a partial update (just to force the messages + ** to redraw). + */ + Map.Flag_To_Redraw(true); + + /* + ** Save this message in our last-message buffer + */ + strcpy(Session.LastMessage, Session.GPacket.Message.Buf); + } + } + } +} +#endif // MPATH + + +/*********************************************************************************************** + * Language_Name -- Build filename for current language. * + * * + * This routine attaches a language specific suffix to the base * + * filename provided. Typical use of this is when loading language * + * specific files at game initialization time. * + * * + * INPUT: basename -- Base name to append language specific * + * extension to. * + * * + * OUTPUT: Returns with pointer to completed filename. * + * * + * WARNINGS: The return pointer value is valid only until the next time * + * this routine is called. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +char const * Language_Name(char const * basename) +{ + static char _fullname[_MAX_FNAME+_MAX_EXT]; + + if (!basename) return(NULL); + + sprintf(_fullname, "%s.ENG", basename); + return(_fullname); +} + + +/*********************************************************************************************** + * Source_From_Name -- Converts ASCII name into SourceType. * + * * + * This routine is used to convert an ASCII name representing a * + * SourceType into the actual SourceType value. Typically, this is * + * used when processing the scenario INI file. * + * * + * INPUT: name -- The ASCII source name to process. * + * * + * OUTPUT: Returns with the SourceType represented by the name * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/17/1994 JLB : Created. * + *=============================================================================================*/ +SourceType Source_From_Name(char const * name) +{ + if (name) { + for (SourceType source = SOURCE_FIRST; source < SOURCE_COUNT; source++) { + if (stricmp(SourceName[source], name) == 0) { + return(source); + } + } + } + return(SOURCE_NONE); +} + + +/*********************************************************************************************** + * Name_From_Source -- retrieves the name for the given SourceType * + * * + * INPUT: * + * source SourceType to get the name for * + * * + * OUTPUT: * + * name of SourceType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/15/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Source(SourceType source) +{ + if ((unsigned)source < SOURCE_COUNT) { + return(SourceName[source]); + } + return("None"); +} + + +/*********************************************************************************************** + * Theater_From_Name -- Converts ASCII name into a theater number. * + * * + * This routine converts an ASCII representation of a theater and converts it into a * + * matching theater number. If no match was found, then THEATER_NONE is returned. * + * * + * INPUT: name -- Pointer to ASCII name to convert. * + * * + * OUTPUT: Returns with the name converted into a theater number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +TheaterType Theater_From_Name(char const * name) +{ + TheaterType index; + + if (name) { + for (index = THEATER_FIRST; index < THEATER_COUNT; index++) { + if (stricmp(name, Theaters[index].Name) == 0) { + return(index); + } + } + } + return(THEATER_NONE); +} + + +/*********************************************************************************************** + * KN_To_Facing -- Converts a keyboard input number into a facing value. * + * * + * This routine determine which compass direction is represented by the keyboard value * + * provided. It is used for map scrolling and other directional control operations from * + * the keyboard. * + * * + * INPUT: input -- The KN number to convert. * + * * + * OUTPUT: Returns with the facing type that the keyboard number represents. If it could * + * not be translated, then FACING_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +FacingType KN_To_Facing(int input) +{ + input &= ~(KN_ALT_BIT|KN_SHIFT_BIT|KN_CTRL_BIT); + switch (input) { + case KN_LEFT: + return(FACING_W); + + case KN_RIGHT: + return(FACING_E); + + case KN_UP: + return(FACING_N); + + case KN_DOWN: + return(FACING_S); + + case KN_UPLEFT: + return(FACING_NW); + + case KN_UPRIGHT: + return(FACING_NE); + + case KN_DOWNLEFT: + return(FACING_SW); + + case KN_DOWNRIGHT: + return(FACING_SE); + + default: + break; + } + return(FACING_NONE); +} + + +/*********************************************************************************************** + * Sync_Delay -- Forces the game into a 15 FPS rate. * + * * + * This routine will wait until the timer for the current frame has expired before * + * returning. It is called at the end of every game loop in order to force the game loop * + * to run at a fixed rate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will delay an amount of time according to the game speed setting. * + * * + * HISTORY: * + * 01/04/1995 JLB : Created. * + * 03/06/1995 JLB : Fixed. * + *=============================================================================================*/ +static void Sync_Delay(void) +{ + /* + ** Accumulate the number of 'spare' ticks that are frittered away here. + */ + SpareTicks += FrameTimer; + + /* + ** Delay until the frame timer expires. This forces the game loop to be regulated to a + ** speed controlled by the game options slider. + */ + while (FrameTimer) { + Color_Cycle(); + Call_Back(); + + if (SpecialDialog == SDLG_NONE) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + KeyNumType input = KN_NONE; + int x, y; + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + animFrameNum+=0.5f; // This is garbage and needs to be fixed with proper delta time bits!!! + if (!FrameTimer) { + Color_Cycle(); + Call_Back(); + } +} + + +/*********************************************************************************************** + * Main_Loop -- This is the main game loop (as a single loop). * + * * + * This function will perform one game loop. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the game end? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Check_For_Focus_Loss(void); +void Reallocate_Big_Shape_Buffer(void); +#endif //WIN32 + + +bool Main_Loop() +{ + KeyNumType input; // Player input. + int x; + int y; + int framedelay; + +Mono_Set_Cursor(0,0); + + if (!GameActive) return(!GameActive); + +#ifdef WIN32 + /* + ** Call the focus loss handler + */ + Check_For_Focus_Loss(); +#endif + + /* + ** Sync-bug trapping code + */ + if (Frame >= Session.TrapFrame) { + Session.Trap_Object(); + } + + // + // Initialize our AI processing timer + // + Session.ProcessTimer = TickCount; + +#if 1 + if (Session.TrapCheckHeap) { + Debug_Trap_Check_Heap = true; + } +#endif + +#ifdef CHEAT_KEYS + + /* + ** Update the running status debug display. + */ + Self_Regulate(); +#endif + + BStart(BENCH_GAME_FRAME); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + Theme.Queue_Song(THEME_PICK_ANOTHER); + } + + /* + ** Setup the timer so that the Main_Loop function processes at the correct rate. + */ + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // In playback mode, run as fast as possible. + // + if (Session.Play) { + FrameTimer = 0; + } else { +#ifdef FIXIT_VERSION_3 + if( !Session.DesiredFrameRate ) + Session.DesiredFrameRate = 60; // A division by zero was happening (very rare). +#endif + framedelay = TIMER_SECOND / Session.DesiredFrameRate; + FrameTimer = framedelay; + } + } else { + if (Options.GameSpeed != 0) { + FrameTimer = Options.GameSpeed + + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0) - + (PlayerPtr->Difficulty == DIFF_HARD ? 1 : 0); + } else { + FrameTimer = Options.GameSpeed + (PlayerPtr->Difficulty == DIFF_EASY ? 1 : 0); + } + } + + /* + ** Update the display, unless we're inside a dialog. + */ + if (!Session.Play) { +#ifdef WIN32 + if (SpecialDialog == SDLG_NONE && GameInFocus) { + WWMouse->Erase_Mouse(&HidPage, TRUE); +#else + if (SpecialDialog == SDLG_NONE) { +#endif + Map.Input(input, x, y); + if (input) { + Keyboard_Process(input); + } + Map.Render(); + } + } + + /* + ** Save map's position & selected objects, if we're recording the game. + */ + if (Session.Record || Session.Play) { + Do_Record_Playback(); + } + +#ifndef SORTDRAW + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will remain in sync. + */ + DisplayClass::Layer[LAYER_GROUND].Sort(); +#endif + + /* + ** AI logic operations are performed here. + */ + Logic.AI(); + TimeQuake = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!PendingTimeQuake) { + TimeQuakeCenter = 0; + } +#endif + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + // + // Measure how long it took to process the AI + // + Session.ProcessTicks += (TickCount - Session.ProcessTimer); + Session.ProcessFrames++; + + /* + ** Process all commands that are ready to be processed. + */ + Queue_AI(); + + /* + ** Keep track of elapsed time in the game. + */ + Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + //Call_Back(); + + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + +#ifdef WIN32 + + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just won. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Win(); + return(!GameActive); + } + if (PlayerLoses) { +#ifdef WIN32 + /* + ** Send the game statistics to WChat. + */ + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Player just lost. + } + + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Lose(); + return(!GameActive); + } + if (PlayerRestarts) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + Do_Restart(); + return(!GameActive); + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 && + Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + // End game in a draw. + if (Session.Type == GAME_INTERNET && !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); + } + WWMouse->Erase_Mouse(&HidPage, TRUE); + Map.Help_Text(TXT_NONE); + Do_Draw(); + return(!GameActive); + } +#endif + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Is there a memory trasher altering the map?? + */ + if (Debug_Check_Map) { + if (!Map.Validate()) { + if (WWMessageBox().Process (TEXT_MAP_ERROR, TEXT_STOP, TEXT_CONTINUE)==0) { + GameActive = 0; + } + Map.Validate(); // give debugger a chance to catch it + } + } + + +#if (0) //ST - 5/8/2019 +#ifdef WIN32 + if (Debug_MotionCapture) { + static void ** _array = 0; + static int _sequence = 0; + static int _seqsize = Rule.MovieTime * TICKS_PER_MINUTE; + + if (_array == NULL) { + _array = new void * [_seqsize]; + memset(_array, '\0', _seqsize * sizeof(void*)); + } + + if (_array == NULL) { + Debug_MotionCapture = false; + } + + static GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + + int size = SeenBuff.Get_Width() * SeenBuff.Get_Height(); + + if (_sequence < _seqsize) { + if (_array[_sequence] == NULL) { + _array[_sequence] = new char[size]; + } + + if (_array[_sequence] != NULL) { + SeenBuff.Blit(temp_page); + memmove(_array[_sequence], temp_page.Get_Buffer(), size); + } + _sequence++; + + } else { + Debug_MotionCapture = false; + + CDFileClass file; + file.Cache(200000); + char filename[30]; + + for (int index = 0; index < _sequence; index++) { + memmove(temp_page.Get_Buffer(), _array[index], size); + sprintf(filename, "cap%04d.pcx", index); + file.Set_Name(filename); + + Write_PCX_File(file, temp_page, & GamePalette); + } + + _sequence = 0; + } + } +#endif +#endif + BEnd(BENCH_GAME_FRAME); + + Scenario_MapScriptFrame(); + + Sync_Delay(); + return(!GameActive); +} + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** + * Map_Edit_Loop -- a mini-main loop for map edit mode only * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +bool Map_Edit_Loop(void) +{ + /* + ** Redraw the map. + */ + Map.Render(); + + /* + ** Get user input (keys, mouse clicks). + */ + KeyNumType input; + +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + + int x; + int y; + Map.Input(input, x, y); + + /* + ** Process keypress. + */ + if (input) { + Keyboard_Process(input); + } + + Call_Back(); // maintains Theme.AI() for music + Color_Cycle(); + + return(!GameActive); +} + + +/*************************************************************************** + * Go_Editor -- Enables/disables the map editor * + * * + * INPUT: * + * flag true = go into editor mode; false = go into game mode * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/19/1994 BR : Created. * + *=========================================================================*/ +void Go_Editor(bool flag) +{ + /* + ** Go into Scenario Editor mode + */ + if (flag) { + AudMix_SetMusicState(false); // jmarshall: disable music if we are in editor mode + Hide_Mouse(); + //ShowCursor(TRUE); + Debug_Map = true; + Debug_Unshroud = true; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Turn off the sidebar if it's on + */ + Map.Activate(0); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ +#ifdef WIN32 + HiddenPage.Clear(); +#else + HidPage.Clear(); +#endif + Map.Flag_To_Redraw(true); + Map.Render(); + + } else { + AudMix_SetMusicState(true); // jmarshall: enable music if we are going back into editor mode. + Show_Mouse(); + //ShowCursor(FALSE); + + /* + ** Go into normal game mode + */ + Debug_Map = false; + Debug_Unshroud = false; + + /* + ** Un-select any selected objects + */ + Unselect_All(); + + /* + ** Reset the map's Button list for the new mode + */ + Map.Init_IO(); + + /* + ** Force a complete redraw of the screen + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +} + +#endif + +#if (0) +/*********************************************************************************************** + * MixFileHandler -- Handles VQ file access. * + * * + * This routine is called from the VQ player when it needs to access the source file. By * + * using this routine it is possible to virtualize the file system. * + * * + * INPUT: vqa -- Pointer to the VQA handle for this animation. * + * * + * action-- The requested action to perform. * + * * + * buffer-- Optional buffer pointer as needed by the type of action. * + * * + * nbytes-- The number of bytes (if needed) for this operation. * + * * + * OUTPUT: Returns a value consistent with the action requested. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes) +{ + CCFileClass * file; + long error; + + file = (CCFileClass *)vqa->VQAio; + + /* + ** Perform the action specified by the stream command. + */ + switch (action) { + + /* + ** VQACMD_READ means read NBytes from the stream and place it in the + ** memory pointed to by Buffer. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_READ. + */ + case VQACMD_READ: + error = (file->Read(buffer, (unsigned short)nbytes) != (unsigned short)nbytes); + break; + + /* + ** VQACMD_WRITE is analogous to VQACMD_READ. + ** + ** Writing is not allowed to the VQA file, VQA library will remap the + ** error into VQAERR_WRITE. + */ + case VQACMD_WRITE: + error = 1; + break; + + /* + ** VQACMD_SEEK asks that you perform a seek relative to the current + ** position. NBytes is a signed number, indicating seek direction + ** (positive for forward, negative for backward). Buffer has no meaning + ** here. + ** + ** Any error code returned will be remapped by VQA library into + ** VQAERR_SEEK. + */ + case VQACMD_SEEK: + error = (file->Seek(nbytes, SEEK_CUR) == -1); + break; + + /* + ** VQACMD_OPEN asks that you open your stream for access. + */ + case VQACMD_OPEN: + file = new CCFileClass((char *)buffer); + + if (file != NULL && file->Is_Available()) { + error = file->Open((char *)buffer, READ); + + if (error != -1) { + vqa->VQAio = (unsigned long)file; + error = 0; + } else { + delete file; + file = 0; + error = 1; + } + } else { + error = 1; + } + break; + + case VQACMD_CLOSE: + file->Close(); + delete file; + file = 0; + vqa->VQAio = 0; + error = 0; + break; + + /* + ** VQACMD_INIT means to prepare your stream for reading. This is used for + ** certain streams that can't be read immediately upon opening, and need + ** further preparation. This operation is allowed to fail; the error code + ** will be returned directly to the client. + */ + case VQACMD_INIT: + + /* + ** IFFCMD_CLEANUP means to terminate the transaction with the associated + ** stream. This is used for streams that can't simply be closed. This + ** operation is not allowed to fail; any error returned will be ignored. + */ + case VQACMD_CLEANUP: + error = 0; + break; + + default: + error = 0; + break; + } + + return(error); +} +#endif + +void Rebuild_Interpolated_Palette(unsigned char * interpal) +{ + for (int y=0; y<255; y++) { + for (int x=y+1; x<256; x++) { + *(interpal + (y*256+x)) = *(interpal + (x*256+y)); + } + } +} + + +unsigned char * InterpolatedPalettes[100]; +BOOL PalettesRead; +unsigned PaletteCounter; + + +/*********************************************************************************************** + * Load_Interpolated_Palettes -- Loads in any precalculated palettes for hires VQs * + * * + * * + * * + * INPUT: Name of palette file * + * * + * OUTPUT: Number of palettes loaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/7/96 9:49AM ST : Created * + *=============================================================================================*/ +int Load_Interpolated_Palettes(char const * filename, BOOL add) +{ + int num_palettes=0; + int i; + int start_palette; + + PalettesRead = FALSE; + CCFileClass file(filename); + + if (!add) { + for (i=0; i < ARRAY_SIZE(InterpolatedPalettes); i++) { + InterpolatedPalettes[i]=NULL; + } + start_palette=0; + } else { + for (start_palette = 0; start_palette < ARRAY_SIZE(InterpolatedPalettes); start_palette++) { + if (!InterpolatedPalettes[start_palette]) break; + } + } + + /* + ** Hack another interpolated palette if the requested one is + ** not present. + */ + if (!file.Is_Available()) { + file.Set_Name("AAGUN.VQP"); + } + + if (file.Is_Available()) { + + file.Open(READ); + file.Read(&num_palettes , 4); + + for (i=0; i < num_palettes; i++) { + InterpolatedPalettes[i+start_palette] = (unsigned char *)malloc (65536); + memset (InterpolatedPalettes[i+start_palette], 0, 65536); + for (int y = 0; y < 256; y++) { + file.Read (InterpolatedPalettes[i+start_palette] + y*256 , y+1); + } + + Rebuild_Interpolated_Palette(InterpolatedPalettes[i+start_palette]); + } + + PalettesRead = TRUE; + file.Close(); + } + PaletteCounter = 0; + return (num_palettes); +} + + +void Free_Interpolated_Palettes(void) +{ + for (int i = 0; i < ARRAY_SIZE(InterpolatedPalettes) ;i++) { + if (InterpolatedPalettes[i]) { + free(InterpolatedPalettes[i]); + InterpolatedPalettes[i]=NULL; + } + } +} + + +/*********************************************************************************************** + * Play_Movie -- Plays a VQ movie. * + * * + * Use this routine to play a VQ movie. It will dispatch the specified movie to the * + * VQ player. The routine will not return until the movie has finished playing. * + * * + * INPUT: name -- The name of the movie file (sans ".VQA"). * + * * + * theme -- The identifier for an optional theme that should be played in the * + * background while this VQ plays. * + * * + * clrscrn -- 'true' if to clear the screen when the movie is over * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/19/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +extern void Suspend_Audio_Thread(void); +extern void Resume_Audio_Thread(void); + +#ifdef MOVIE640 +extern GraphicBufferClass VQ640; +#endif +#endif + +extern void Play_Movie_GlyphX(const char * movie_name, ThemeType theme, bool immediate); + +void Play_Movie(char const * name, ThemeType theme, bool clrscrn, bool immediate) + { + #ifdef MPEGMOVIE + //theme = theme; + //clrscrn = clrscrn; + if( Using_DVD() ) + { + if (PlayMpegMovie(name)) + return; + } + #endif + + #ifdef CHEAT_KEYS + // Mono_Printf("Movie: %s\n", name); + #endif //CHEAT_KEYS + /* + ** Don't play movies in editor mode + */ + if (Debug_Map) { + return; + } + #ifdef CHEAT_KEYS + // Mono_Printf("A\n"); + #endif //CHEAT_KEYS + /* + ** Don't play movies in multiplayer mode + */ + if (Session.Type != GAME_NORMAL) { + return; + } + #ifdef CHEAT_KEYS + //Mono_Printf("b\n"); + #endif //CHEAT_KEYS + + if (name) { + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, name, ".smk"); + #ifdef WIN32 + char palname [_MAX_FNAME+_MAX_EXT]; + _makepath(palname , NULL, NULL, name, ".smk"); + #endif //WIN32 + #ifdef CHEAT_KEYS + // Mono_Set_Cursor(0, 0);Mono_Printf("[%s]", fullname); + #endif + + //if (!CCFileClass(fullname).Is_Available()){ + // #ifdef CHEAT_KEYS + // // Mono_Printf("fullname: %s\n", fullname); + // #endif //CHEAT_KEYS + // return; + //} + /* + ** Reset the anim control structure. + */ + Anim_Init(); + + /* + ** Prepare to play a movie. First hide the mouse and stop any score that is playing. + ** While the score (if any) is fading to silence, fade the palette to black as well. + ** When the palette has finished fading, wait until the score has finished fading + ** before launching the movie. + */ + Hide_Mouse(); + Theme.Queue_Song(theme); + if (PreserveVQAScreen == 0 && !clrscrn) { + BlackPalette.Set(FADE_PALETTE_MEDIUM); + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + PreserveVQAScreen = 0; + Keyboard->Clear(); + + _VQAHandle * vqa = NULL; + + + //#ifdef WIN32 + //#ifdef MOVIE640 + //if(IsVQ640) { + // AnimControl.ImageWidth = 640; + // AnimControl.ImageHeight = 400; + // AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + //} else { + // AnimControl.ImageWidth = 320; + // AnimControl.ImageHeight = 200; + // AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); + //} + //#endif + //#endif + // + //if (!Debug_Quiet && Get_Digi_Handle() != -1) { + // AnimControl.OptionFlags |= VQAOPTF_AUDIO; + //} else { + // AnimControl.OptionFlags &= ~VQAOPTF_AUDIO; + //} + + if ((vqa = VQA_Alloc()) != NULL) { + //VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, fullname) == 0) { + Brokeout = false; + #ifdef WIN32 + //Suspend_Audio_Thread(); + #ifdef MOVIE640 + if(!IsVQ640) { + Load_Interpolated_Palettes(palname); + } + #else + Load_Interpolated_Palettes(palname); + #endif + //Set_Palette(BlackPalette); + SysMemPage.Clear(); + InMovie = true; + #endif //WIN32 + VQA_Play(vqa); + VQA_Close(vqa); + #ifdef WIN32 + //Resume_Audio_Thread(); + InMovie = FALSE; + #ifdef MOVIE640 + if(!IsVQ640) { + Free_Interpolated_Palettes(); + } + #else + Free_Interpolated_Palettes(); + #endif + IsVQ640 = false; + Set_Primary_Buffer_Format(); + #endif //WIN32 + + /* + ** Any movie that ends prematurely must have the screen + ** cleared to avoid any unexpected palette glitches. + */ + if (Brokeout) { + clrscrn = true; + VisiblePage.Clear(); + Brokeout = false; + } + } else { + #ifndef NDEBUG + bool error = true; + assert(error); + #endif + } + + VQA_Free(vqa); + } else { + assert(vqa != NULL); + } + #ifdef CHEAT_KEYS + //Mono_Printf("d"); + #endif //CHEAT_KEYS + /* + ** Presume that the screen is left in a garbage state as well as the palette + ** being in an unknown condition. Recover from this by clearing the screen and + ** forcing the palette to black. + */ + if (clrscrn) { + VisiblePage.Clear(); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + } + Show_Mouse(); + } +} + + +void Play_Movie(VQType name, ThemeType theme, bool clrscrn, bool immediate) +{ + if (name != VQ_NONE) { + if (name == VQ_REDINTRO) { + IsVQ640 = true; + } + Play_Movie(VQName[name], theme, clrscrn, immediate); + IsVQ640 = false; + } +} + + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +extern LPDIRECTDRAWPALETTE PalettePtr; + +bool PlayMpegMovie(const char* name) + { + char path[MAX_PATH]; + CCFileClass file; + const char* filename; + +#ifdef CHEAT_KEYS + if( bNoMovies ) + return true; +#endif + + sprintf(path, "movies\\%.8s.%.3s", name, "mpg"); + filename = file.Set_Name(path); + + if (!file.Is_Available()) + { + #if(1) + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + sprintf(path, "Couldn't find %s\n", filename); + WWMessageBox().Process(path); + #endif + return false; + } + + // Stop theme music + if (Misc_Focus_Loss_Function) + Misc_Focus_Loss_Function(); + + // Release primary surface + VisiblePage.Un_Init(); + + #ifdef MCIMPEG + if (MciMovie && MpgSettings && (MpgSettings->GetDeviceName() != NULL)) + { + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_NORMAL); + + if (!MciMovie->Open(filename, MpgSettings->GetDeviceName())) + { + WWMessageBox().Process("Couldn't open movie.\n"); + } + else if (!MciMovie->Play(MainWindow)) + { + WWMessageBox().Process("Couldn't play movie.\n"); + } + + DirectDrawObject->SetCooperativeLevel(MainWindow, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + } + else + #endif + { + DDSURFACEDESC ddsd; + IDirectDrawSurface* primary = NULL; + bool modeChange = false; + RECT rect; + + if (FAILED(DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 16))) + { + WWMessageBox().Process("Couldn't change display mode.\n"); + } + else + { + // Create primary surface reference + memset(&ddsd, 0, sizeof(ddsd)); + ddsd.dwSize = sizeof(ddsd); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + if (FAILED(DirectDrawObject->CreateSurface(&ddsd, &primary, NULL))) + { + WWMessageBox().Process("Couldn't create primary movie surface.\n"); + } + else + { + rect.top = rect.left = 0; + rect.bottom = ScreenHeight; + rect.right = ScreenWidth; + + MpgSetCallback(MpegCallback, NULL); + MpgPlay(filename, DirectDrawObject, primary, &rect); + + if (primary) + primary->Release(); + + } + + DirectDrawObject->SetDisplayMode(ScreenWidth, ScreenHeight, 8); + } + } + + // Restore surfaces + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE|GBC_VIDEOMEM)); + PaletteSurface->SetPalette(PalettePtr); + AllSurfaces.Set_Surface_Focus(true); + AllSurfaces.Restore_Surfaces(); + return true; + } + + +MPG_RESPONSE far __stdcall MpegCallback(MPG_CMD cmd, LPVOID data, LPVOID user) + { + static IDirectDrawPalette* _palette = NULL; + + user = user; + + switch (cmd) + { + case MPGCMD_ERROR: + WWMessageBox().Process((char const *)data); + break; + + case MPGCMD_INIT: + VisiblePage.Clear(); + break; + + case MPGCMD_CLEANUP: + VisiblePage.Clear(); + + if (_palette != NULL) + { + PaletteSurface->SetPalette(_palette); + _palette->Release(); + _palette = NULL; + } + break; + + case MPGCMD_PALETTE: + if (FAILED(PaletteSurface->GetPalette(&_palette))) + { + WWMessageBox().Process("Couldn't get primary palette.\n"); + } + else + { + if (FAILED(PaletteSurface->SetPalette((IDirectDrawPalette*)data))) + { + WWMessageBox().Process("Couldn't set movie palette.\n"); + } + } + break; + + case MPGCMD_UPDATE: + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) + { + if (Keyboard->Get() == KN_ESC) + { + Keyboard->Clear(); + return MPGRES_QUIT; + } + + Keyboard->Clear(); + } + + if (!GameInFocus) + { + MpgPause(); + + while (!GameInFocus) + { + Check_For_Focus_Loss(); + } + + MpgResume(); + return MPGRES_LOSTFOCUS; + } + break; + + default: + break; + } + + return MPGRES_CONTINUE; + } +#endif + +/*********************************************************************************************** + * Unselect_All -- Causes all selected objects to become unselected. * + * * + * This routine will unselect all objects that are currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void Unselect_All(void) +{ + //while (CurrentObject.Count()) { + // CurrentObject[0]->Unselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + while (CurrentObject.Count()) { + + int count_before = CurrentObject.Count(); + CurrentObject[0]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[0]); + } + } + //End of change - JAS 6/28/2019 + +} + + +void Unselect_All_Except(ObjectClass* object) +{ + int index = 0; + while (index < CurrentObject.Count()) { + + if (CurrentObject[index] == object) { + index++; + continue; + } + + int count_before = CurrentObject.Count(); + CurrentObject[index]->Unselect(); + + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Unselect_All failed to remove an object"); + CurrentObject.Delete(CurrentObject[index]); + } + } +} + + +/*********************************************************************************************** + * Fading_Table_Name -- Builds a theater specific fading table name. * + * * + * This routine builds a standard fading table name. This name is dependant on the theater * + * being played, since each theater has its own palette. * + * * + * INPUT: base -- The base name of this fading table. The base name can be no longer than * + * seven characters. * + * * + * theater -- The theater that this fading table is specific to. * + * * + * OUTPUT: Returns with a pointer to the constructed fading table filename. This pointer is * + * valid until this function is called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +char const * Fading_Table_Name(char const * base, TheaterType theater) +{ + static char _buffer[_MAX_FNAME+_MAX_EXT]; + char root[_MAX_FNAME]; + + sprintf(root, "%1.1s%s", Theaters[theater].Root, base); + _makepath(_buffer, NULL, NULL, root, ".MRF"); + return(_buffer); +} + + +/*********************************************************************************************** + * Get_Radar_Icon -- Builds and alloc a radar icon from a shape file * + * * + * INPUT: void const * shapefile - pointer to a key framed shapefile * + * int shapenum - shape to extract from shapefile * + * * + * OUTPUT: void const * - 3/3 icon set of shape from file * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + * 05/10/1995 JLB : Handles a null shapefile pointer. * + *=============================================================================================*/ +void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor) +{ + static int _offx[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + static int _offy[]={ 0, 0, -1, 1, 0, -1, 1, -1, 1}; + int lp,framelp; + char pixel; + + char * retval = NULL; + char * buffer = NULL; + + /* + ** If there is no shape file, then there can be no radar icon imagery. + */ + if (!shapefile) return(NULL); + +#if (0) + CCPalette.Set(); + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(shapefile, shapenum, 64, 64, WINDOW_MAIN, SHAPE_WIN_REL); +#endif + + /* + ** Get the pixel width and height of the frame we built. This will + ** be used to extract icons and build pixels. + */ + int pixel_width = Get_Build_Frame_Width( shapefile ); + int pixel_height = Get_Build_Frame_Height( shapefile ); + + /* + ** Find the width and height in icons, adjust these by half an + ** icon because the artists may be sloppy and miss the edge of an + ** icon one way or the other. + */ + int icon_width = (pixel_width + 12) / 24; + int icon_height = (pixel_height + 12) / 24; + + /* + ** If we have been told to build as many frames as possible, then + ** find out how many frames there are to build. + */ + if (frames == -1) frames = Get_Build_Frame_Count( shapefile ); + + /* + ** Allocate a position to store our icons. If the alloc fails then + ** we don't add these icons to the set. + **/ + buffer = new char[(icon_width * icon_height * 9 * frames)+2]; + if (!buffer) return(NULL); + + /* + ** Save off the return value so that we can return it to the calling + ** function. + */ + retval = (char *)buffer; + *buffer++ = (char)icon_width; + *buffer++ = (char)icon_height; + int val = 24/zoomfactor; + + for (framelp = 0; framelp < frames; framelp ++) { + /* + ** Build the current frame. If the frame can not be built then we + ** just need to skip past this set of icons and try to build the + ** next frame. + */ +#ifdef WIN32 + void * ptr; + if ((ptr = (void *)(Build_Frame(shapefile, shapenum + framelp, SysMemPage.Get_Buffer()))) != NULL) { + ptr = Get_Shape_Header_Data(ptr); +#else //WIN#@ + if (Build_Frame(shapefile, shapenum + framelp, HidPage.Get_Buffer()) <= (unsigned long)HidPage.Get_Size() ) { +#endif //WIN32 + + /* + ** Loop through the icon width and the icon height building icons + ** into the buffer pointer. When the getx or gety falls outside of + ** the width and height of the shape, just insert transparent pixels. + */ + for (int icony = 0; icony < icon_height; icony ++) { + for (int iconx = 0; iconx < icon_width; iconx ++) { +#ifdef WIN32 + + for (int y = 0; y < zoomfactor; y++) { + for (int x = 0; x < zoomfactor; x++) { + int getx = (iconx * 24) + (x * val) + (zoomfactor / 2); + int gety = (icony * 24) + (y * val) + (zoomfactor / 2); + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)ptr + ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); + +#else //WIN32 + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + int getx = (iconx * 24) + (x << 3) + 4; + int gety = (icony * 24) + (y << 3) + 4; + if ((getx < pixel_width) && (gety < pixel_height)) { + for (lp = 0; lp < 9; lp ++) { + pixel = *(char *)((char *)HidPage.Get_Buffer(), ((gety - _offy[lp]) * pixel_width) + getx-_offx[lp]); +#endif //WIN32 + if (pixel == LTGREEN) pixel = 0; + if (pixel) { + break; + } + } + *buffer++ = pixel; + } else { + *buffer++ = 0; + } + } + } + } + } + } else { + buffer += icon_width * icon_height * 9; + } + } + return(retval); +} + + +extern 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); +extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation); + +void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, NULL, HOUSE_NONE); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + +void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale, char override_owner) +{ + if (window == WINDOW_VIRTUAL) { + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, rotation, virtualscale, shape_file_name, override_owner); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + + +void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) +{ + if (window == WINDOW_VIRTUAL) { + DLL_Draw_Pip_Intercept(object, shapenum); + return; + } + + CC_Draw_Shape(shapefile, shapenum, x, y, window, flags, fadingdata, ghostdata, rotation); +} + +// jmarshall - this is for hd textures +void CC_Draw_Shape(Image_t* image, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const* fadingdata, void const* ghostdata, DirType rotation) { + int predoffset; + + /* + ** Special kludge for E3 to prevent crashes + */ + if ((flags & SHAPE_GHOST) && (!ghostdata)) { + ghostdata = DisplayClass::SpecialGhost; + } + //if ((flags & SHAPE_FADING) && (!fadingdata)) { + // fadingdata = DisplayClass::FadingShade; + //} + + static unsigned char* _xbuffer = 0; + + if (!_xbuffer) { + _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; + } + + if (image != NULL && shapenum != -1) { + + int width = image->renderwidth[shapenum]; + int height = image->renderheight[shapenum]; + +#ifdef NEVER + /* + ** Perform a quick clip check against the destination rectangle. + */ + if (flags & SHAPE_CENTER) { + if (x - width / 2 >= WindowList[window][WINDOWWIDTH]) return; + if (y - width / 2 >= WindowList[window][WINDOWHEIGHT]) return; + if (x + width / 2 < 0) return; + if (y + height / 2 < 0) return; + + } + else { + if (x >= WindowList[window][WINDOWWIDTH]) return; + if (y >= WindowList[window][WINDOWHEIGHT]) return; + if (x + width < 0) return; + if (y + height < 0) return; + } +#endif + + + + { + //GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + // WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + // WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + // WindowList[window][WINDOWWIDTH], + // WindowList[window][WINDOWHEIGHT]); + + UseOldShapeDraw = false; + /* + ** Rotation handler. + */ +// jmarshall: todo HD rotation code! +// if (rotation != DIR_N) { +// +// /* +// ** Get the raw shape data without the new header and flag to use the old shape drawing +// */ +// UseOldShapeDraw = true; +// +// if (Debug_Rotate) { +//#if (0)//PG +// GraphicBufferClass src(width, height, buffer); +// width *= 2; +// height *= 2; +// memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); +// GraphicBufferClass dst(width, height, _xbuffer); +// Rotate_Bitmap(&src, &dst, rotation); +// buffer = _xbuffer; +//#endif +// } +// else { +// +// BitmapClass bm(width, height, buffer); +// width *= 2; +// height *= 2; +// memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); +// GraphicBufferClass gb(width, height, _xbuffer); +// TPoint2D pt(width / 2, height / 2); +// +// gb.Scale_Rotate(bm, pt, 0x0100, (256 - (rotation - 64))); +// buffer = _xbuffer; +// } +// } +// jmarshall end + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING | SHAPE_PREDATOR)) == (SHAPE_FADING | SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING | SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = DisplayClass::SpecialGhost; + } + + predoffset = Frame; + + //if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + // predoffset = -predoffset; + //} + + Buffer_Enable_HD_Texture(true); + //if (draw_window.Lock()) { + if ((flags & (SHAPE_GHOST | SHAPE_FADING)) == (SHAPE_GHOST | SHAPE_FADING)) { + Buffer_Frame_To_Page(shapenum, x, y, width, height, image, window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } + else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(shapenum, x, y, width, height, image, window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } + else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(shapenum, x, y, width, height, image, window, flags | SHAPE_TRANS, predoffset); + } + else { + Buffer_Frame_To_Page(shapenum, x, y, width, height, image, window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + // draw_window.Unlock(); + //} + Buffer_Enable_HD_Texture(false); + } + } +} +// jmarshall end + + +/*********************************************************************************************** + * CC_Draw_Shape -- Custom draw shape handler. * + * * + * All draw shape calls will route through this function. It handles all draws for * + * C&C. Such draws always occur to the logical page and assume certain things about * + * the parameters passed. * + * * + * INPUT: shapefile -- Pointer to the shape data file. This data file contains all the * + * embedded shapes. * + * * + * shapenum -- The shape number within the shapefile that will be drawn. * + * * + * x,y -- The pixel coordinates to draw the shape. * + * * + * window -- The clipping window to use. * + * * + * flags -- The custom draw shape flags. This controls how the parameters * + * are used (if any). * + * * + * fadingdata -- If SHAPE_FADING is desired, then this points to the fading * + * data table. * + * * + * ghostdata -- If SHAPE_GHOST is desired, then this points to the ghost remap * + * table. * + * * + * rotation -- Rotation to apply to the shape (DIR_N = no rotation at all). * + * * + * scale -- 24.8 fixed point scale factor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation) +{ + int predoffset; +#ifdef WIN32 + unsigned long shape_pointer; +#endif //WIN32 + + + /* + ** Special kludge for E3 to prevent crashes + */ + if ((flags & SHAPE_GHOST) && (!ghostdata)) { + ghostdata = DisplayClass::SpecialGhost; + } + if ((flags & SHAPE_FADING) && (!fadingdata)) { + fadingdata = DisplayClass::FadingShade; + } + + static unsigned char * _xbuffer = 0; + + if (!_xbuffer) { + _xbuffer = new unsigned char[SHAPE_BUFFER_SIZE]; + } + + if (shapefile != NULL && shapenum != -1) { + + int width = Get_Build_Frame_Width(shapefile); + int height = Get_Build_Frame_Height(shapefile); + +#ifdef NEVER + /* + ** Perform a quick clip check against the destination rectangle. + */ + if (flags & SHAPE_CENTER) { + if (x-width/2 >= WindowList[window][WINDOWWIDTH]) return; + if (y-width/2 >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width/2 < 0) return; + if (y+height/2 < 0) return; + + } else { + if (x >= WindowList[window][WINDOWWIDTH]) return; + if (y >= WindowList[window][WINDOWHEIGHT]) return; + if (x+width < 0) return; + if (y+height < 0) return; + } +#endif + + +#ifdef WIN32 + /* + ** In WIn95, build shape returns a pointer to the shape not its size + */ + + Image_t* shape_image = NULL; + shape_pointer = Build_Frame(shapefile, shapenum, _ShapeBuffer); + + if (shape_pointer) { + //GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + // WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + // WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + // WindowList[window][WINDOWWIDTH], + // WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *) shape_pointer; //Get_Shape_Header_Data((void*)shape_pointer); + +#else //WIN32 + if ( Build_Frame(shapefile, shapenum, _ShapeBuffer ) <= (unsigned long)_ShapeBufferSize) { + GraphicViewPortClass draw_window(LogicPage, + WindowList[window][WINDOWX], + WindowList[window][WINDOWY], + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + unsigned char * buffer = (unsigned char *)_ShapeBuffer; +#endif //WIN32 + + UseOldShapeDraw = false; + /* + ** Rotation handler. + */ + if (rotation != DIR_N) { + + /* + ** Get the raw shape data without the new header and flag to use the old shape drawing + */ + UseOldShapeDraw = true; +#ifdef WIN32 + buffer = (unsigned char *) Get_Shape_Header_Data((void*)shape_pointer); +#endif + + if (Debug_Rotate) { +#if (0)//PG + GraphicBufferClass src(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass dst(width, height, _xbuffer); + Rotate_Bitmap(&src, &dst, rotation); + buffer = _xbuffer; +#endif + } else { + + BitmapClass bm(width, height, buffer); + width *= 2; + height *= 2; + memset(_xbuffer, '\0', SHAPE_BUFFER_SIZE); + GraphicBufferClass gb(width, height, _xbuffer); + TPoint2D pt(width/2, height/2); + + gb.Scale_Rotate(bm, pt, 0x0100, (256-(rotation-64))); + buffer = _xbuffer; + } + } + + { + char tmp[512]; + sprintf(tmp, "s_%d_%d_%d_%d_%d", shapefile, shapenum, rotation, fadingdata, flags); + + if (flags & SHAPE_FADING) { + shape_image = Image_CreateImageFrom8Bit(tmp, width, height, (unsigned char*)buffer, (unsigned char*)fadingdata); + } + else { + shape_image = Image_CreateImageFrom8Bit(tmp, width, height, (unsigned char*)buffer, NULL); + } + } + + /* + ** Special shadow drawing code (used for aircraft and bullets). + */ + if ((flags & (SHAPE_FADING|SHAPE_PREDATOR)) == (SHAPE_FADING|SHAPE_PREDATOR)) { + flags = flags & ~(SHAPE_FADING|SHAPE_PREDATOR); + flags = flags | SHAPE_GHOST; + ghostdata = DisplayClass::SpecialGhost; + } + + predoffset = Frame; + + //if (x > ( WindowList[window][WINDOWWIDTH] << 2)) { + // predoffset = -predoffset; + //} + + { + if ((flags & (SHAPE_GHOST|SHAPE_FADING)) == (SHAPE_GHOST|SHAPE_FADING)) { + Buffer_Frame_To_Page(-1, x, y, width, height, shape_image, window, flags | SHAPE_TRANS, ghostdata, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_FADING) { + Buffer_Frame_To_Page(-1, x, y, width, height, shape_image, window, flags | SHAPE_TRANS, fadingdata, 1, predoffset); + } else { + if (flags & SHAPE_PREDATOR) { + Buffer_Frame_To_Page(-1, x, y, width, height, shape_image, window, flags | SHAPE_TRANS, predoffset); + } else { + Buffer_Frame_To_Page(-1, x, y, width, height, shape_image, window, flags | SHAPE_TRANS, ghostdata, predoffset); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Shape_Dimensions -- Determine the minimum rectangle for the shape. * + * * + * This routine will calculate (using brute forced) the minimum rectangle that will * + * enclose the pixels of the shape. This rectangle will be relative to the upper left * + * corner of the maximum shape size. By using this minimum rectangle, it is possible to * + * greatly optimize the map 'dirty rectangle' logic. * + * * + * INPUT: shapedata -- Pointer to the shape data block. * + * * + * shapenum -- The shape number to examine. Each shape would have a different * + * dimension rectangle. * + * * + * OUTPUT: Returns with the rectangle that encloses the shape. * + * * + * WARNINGS: This routine uses brute force and is slow. It is presumed that the results * + * will be cached for subsiquent reuse. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Shape_Dimensions(void const * shapedata, int shapenum) +{ + Rect rect; + + if (shapedata == NULL || shapenum < 0 || shapenum > Get_Build_Frame_Count(shapedata)) { + return(rect); + } + + char * shape; +#ifdef WIN32 + void * sh = (void *)Build_Frame(shapedata, shapenum, _ShapeBuffer); + if (sh == NULL) return(rect); +// shape = (char *)sh; + shape = (char *)Get_Shape_Header_Data(sh); +#else + Build_Frame(shapedata, shapenum, _ShapeBuffer); + shape = (char *)_ShapeBuffer; +#endif + + int width = Get_Build_Frame_Width(shapedata); + int height = Get_Build_Frame_Height(shapedata); + + rect.X = 0; + rect.Y = 0; + int xlimit = width-1; + int ylimit = height-1; + + /* + ** Find top edge of the shape. + */ + for (int y = 0; y <= ylimit; y++) { + for (int x = 0; x <= xlimit; x++) { + if (shape[y*width + x] != 0) { + rect.Y = y; + rect.X = x; + y = ylimit+1; + break; + } + } + } + + /* + ** Find bottom edge of the shape. + */ + for (int y = ylimit; y >= rect.Y; y--) { + for (int x = xlimit; x >= 0; x--) { + if (shape[y*width + x] != 0) { + rect.Height = (y-rect.Y)+1; + xlimit = x; + y = rect.Y-1; + break; + } + } + } + + /* + ** Find left edge of the shape. + */ + for (int x = 0; x < rect.X; x++) { + for (int y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.X = x; + x = rect.X; + break; + } + } + } + + /* + ** Find the right edge of the shape. + */ + for (int x = width-1; x >= xlimit; x--) { + for (int y = rect.Y; y < rect.Y+rect.Height; y++) { + if (shape[y*width + x] != 0) { + rect.Width = (x-rect.X)+1; + x = xlimit-1; + break; + } + } + } + + /* + ** Normalize the rectangle around the center of the shape. + */ + rect.X -= width / 2; + rect.Y -= height / 2; + + /* + ** Return with the minimum rectangle that encloses the shape. + */ + return(rect); +} + + +/*********************************************************************************************** + * Fetch_Techno_Type -- Convert type and ID into TechnoTypeClass pointer. * + * * + * This routine will convert the supplied RTTI type number and the ID value into a valid * + * TechnoTypeClass pointer. If there is an error in conversion, then NULL is returned. * + * * + * INPUT: type -- RTTI type of the techno class object. * + * * + * id -- Integer representation of the techno sub type number. * + * * + * OUTPUT: Returns with a pointer to the techno type class object specified or NULL if the * + * conversion could not occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id) +{ + switch (type) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitTypeClass::As_Reference(UnitType(id))); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselTypeClass::As_Reference(VesselType(id))); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingTypeClass::As_Reference(StructType(id))); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryTypeClass::As_Reference(InfantryType(id))); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftTypeClass::As_Reference(AircraftType(id))); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * VQ_Call_Back -- Maintenance callback used for VQ movies. * + * * + * This routine is called every frame of the VQ movie as it is being played. If this * + * routine returns non-zero, then the movie will stop. * + * * + * INPUT: buffer -- Pointer to the image buffer for the current frame. * + * * + * frame -- The frame number about to be displayed. * + * * + * OUTPUT: Should the movie be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 JLB : Created. * + *=============================================================================================*/ +#ifdef WIN32 +void VQA_PauseAudio(void) {}; +void Check_VQ_Palette_Set(void); + +extern GraphicBufferClass VQ640; +extern bool IsVQ640; +long VQ_Call_Back(unsigned char *, long ) +{ + return 0; +#if (0)//PG + int key = 0; + if (Keyboard->Check()) { + key = Keyboard->Get(); + Keyboard->Clear(); + } + Check_VQ_Palette_Set(); +#ifdef MOVIE640 + if(IsVQ640) { + VQ640.Blit(SeenBuff); + } else { + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); + } +#else + Interpolate_2X_Scale(&SysMemPage, &SeenBuff, NULL); +#endif + //Call_Back(); + + if ((BreakoutAllowed || Debug_Flag) && key == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + + if (!GameInFocus) { + VQA_PauseAudio(); + while (!GameInFocus) { + Check_For_Focus_Loss(); + } + } + return(false); +#endif +} + +#else //WIN32 + +long VQ_Call_Back(unsigned char *, long ) +{ + Call_Back(); + if ((BreakoutAllowed || Debug_Flag) && Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + Keyboard->Clear(); + Brokeout = true; + return(true); + } + Keyboard->Clear(); + } + return(false); +} +#endif //WIN32 + + +/*********************************************************************************************** + * Handle_Team -- Processes team selection command. * + * * + * This routine will handle creation and selection of pseudo teams that the player can * + * create or control. A team in this sense is an arbitrary grouping of units such that * + * rapid selection control is allowed. * + * * + * INPUT: team -- The logical team number to process. * + * * + * action-- The action to perform on this team: * + * 0 - Toggle the select state for all members of this team. * + * 1 - Select the members of this team. * + * 2 - Make all selected objects members of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_Team(int team, int action) +{ + int index; + + // + // Recording support + // + if (Session.Record) { + TeamNumber = (char)team; + TeamEvent = (char)action + 1; + } + + AllowVoice = true; + switch (action) { + + /* + ** Toggle the team selection. If the team is selected, then merely unselect it. If the + ** team is not selected, then unselect all others before selecting this team. + */ + case 3: + case 0: + + /* + ** If a non team member is currently selected, then deselect all objects + ** before selecting this team. + */ + if (CurrentObject.Count()) { + if (CurrentObject[0]->Is_Foot() && ((FootClass *)CurrentObject[0])->Group != team) { + Unselect_All(); + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + + /* + ** Center the map around the team if the ALT key was pressed too. + */ + if (action == 3) { + Map.Center_Map(); +#ifdef WIN32 + Map.Flag_To_Redraw(true); +#endif //WIn32 + } + break; + + /* + ** Additive selection of team. + */ + case 1: + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->Group == team && obj->House->IsPlayerControl) { + if (!obj->Is_Selected_By_Player()) { + obj->Select(); + AllowVoice = false; + } + } + } + break; + + /* + ** Create the team. + */ + case 2: { + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = -1; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) { + obj->Group = team; + obj->Mark(MARK_CHANGE); + } + } + } + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl && + (obj->Group == team) && (obj->Is_Selected_By_Player()) ) { + + /* + ** When a team is first created, they're created without a + ** formation offset, so they will not be created in + ** formation. Later, if they're assigned a formation, the + ** XFormOffset & YFormOffset numbers will change to valid + ** offsets, and they'll be formationed. + */ +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House->IsPlayerControl) { + if (obj->Group == team) obj->Group = 0xFF; + if (obj->Is_Selected_By_Player()) obj->Group = team; + if (obj->Group == team && obj->Is_Selected_By_Player()) { +#if(1) + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; +#else +#if(1) +// Old always-north formation stuff + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; +#else +// New method: save direction and distance rather than x & y offset + obj->XFormOffset = ::Direction(As_Coord(center), obj->Center_Coord()); + obj->YFormOffset = ::Distance (As_Coord(center), obj->Center_Coord()); +#endif +#endif + } + } + } + break; + } + + default: + break; + } + AllowVoice = true; +} + + +/*********************************************************************************************** + * Handle_View -- Either records or restores the tactical view. * + * * + * This routine is used to record or restore the current map tactical view. * + * * + * INPUT: view -- The view number to work with. * + * * + * action-- The action to perform with this view number. * + * 0 = Restore the view to this previously remembered location. * + * 1 = Record the current view location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void Handle_View(int view, int action) +{ + if ((unsigned)view < ARRAY_SIZE(Scen.Views)) { + if (action == 0) { + + Map.Set_Tactical_Position(Coord_Whole(Cell_Coord(Scen.Views[view] - (MAP_CELL_W * 4 * RESFACTOR) - (5*RESFACTOR)))); + +#ifdef WIN32 + /* + ** Win95 scrolling logic cant handle just jumps in screen position so redraw the lot. + */ + Map.Flag_To_Redraw (true); +#endif //WIN32 + } else { + Scen.Views[view] = Coord_Cell(Map.TacticalCoord) + (MAP_CELL_W*4*RESFACTOR) + (5*RESFACTOR); + } + } +} + + +#ifndef ROR_NOT_READY +#define ROR_NOT_READY 21 +#endif + +static char * _CD_Volume_Label[] = { + "CD1", + "CD2", + "CD3", + "CD4", + // Denzil 4/15/98 + #ifdef DVD + "CD1", // ajw - Pushes RADVD to position 5, to match enum in Force_CD_Available(). 4 will never be returned here. + "RADVD", + #endif +}; +static int _Num_Volumes = ARRAY_SIZE(_CD_Volume_Label); + + +#ifdef WIN32 +/*********************************************************************************************** + * Get_CD_Index -- returns the volume type of the CD in the given drive * + * * + * * + * * + * INPUT: drive number * + * timeout * + * * + * OUTPUT: 0 = gdi * + * 1 = nod * + * 2 = covert or CS * + * 3 = Aftermath + * 5 = DVD + * -1 = non C&C * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 5:27PM ST : Created * + * 01/20/97 V.Grippi added CS support * + *=============================================================================================*/ +int Get_CD_Index (int cd_drive, int timeout) + { + char volume_name[128]; + char buffer[128]; + unsigned filename_length; + unsigned misc_dword; + int count = 0; + + CountDownTimerClass timer; + + timer.Set(timeout); + + /* + ** Get the volume label. If we get a 'not ready' error then retry for the timeout + ** period. + */ + for (;;) + { + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + if (GetVolumeInformation ((char const *)buffer, &volume_name[0] , + (unsigned long)sizeof(volume_name), (unsigned long *)NULL , + (unsigned long *)&filename_length, (unsigned long *)&misc_dword, + (char *)NULL, (unsigned long)0)) + { + /* + ** Try opening 'movies.mix' to verify that the CD is really there and is what + ** it says it is. + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + + HANDLE handle = CreateFile(buffer, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], volume_name)) + return(i); + } + } + else + { + if (!count) + count++; + else + return -1; + } + } + else + { + /* + ** Failed to get the volume label on a known CD drive. + ** If this is a CD changer it may require time to swap the disks so dont return + ** immediately if the error is ROR_NOT_READY + */ + if (!timer.Time()) + return -1; + + int val = GetLastError(); + + if (val != ROR_NOT_READY) + return -1; + } + } + } +#else +int Get_CD_Index(int cd_drive, int) + { + char buffer[128]; + + /* + ** We need to do this twice because of the possibilities of a directory + ** being cached. If this is so, it will only be discovered when we + ** actually attempt to read a file from the drive. + */ + if(cd_drive) for (int count = 0; count < 2; count ++) + { + struct find_t ft; + int file; + int open_failed; + + /* + ** Create a path for the cd drive and attempt to read the volume label from + ** it. + */ + sprintf(buffer, "%c:\\", 'A' + cd_drive); + + /* + ** If we are able to read the volume label, this is good but not enough. + ** Further verification must be done. + */ + if (!_dos_findfirst(buffer, _A_VOLID, &ft)) + { + /* + ** Since some versions of disk cacheing software will cache the CD's + ** directory tracks, we may think the CD is in the drive when it is + ** actually not. To resolve this we must attempt to open a file on + ** the cd. Opening a file will always update the directory tracks + ** (suposedly). + */ + sprintf(buffer, "%c:\\main.mix", 'A' + cd_drive); + open_failed = _dos_open(buffer, O_RDONLY|SH_DENYNO, &file); + + if (!open_failed) + { + _dos_close(file); + + /* + ** Hey some times the stupid dos driver appends a period to the + ** name if it is eight characters long. If the last char is a + ** period then erase it. + */ + if (ft.name[strlen(ft.name)-1] == '.') + { + ft.name[strlen(ft.name)-1] = 0; + } + + /* + ** Match the volume label to the list of known C&C volume labels. + */ + for (int i = 0 ; i < _Num_Volumes; i++) + { + if (!stricmp(_CD_Volume_Label[i], ft.name)) + return (i); + } + } + } + } + + return -1; + } +#endif + + +/*********************************************************************************************** + * Force_CD_Available -- Ensures that specified CD is available. * + * * + * Call this routine when you need to ensure that the specified CD is actually in the * + * CD-ROM drive. * + * * + * INPUT: cd -- The CD that must be available. This will either be "0" for the GDI CD, or * + * "1" for the Nod CD. If either CD will qualify, then pass in "-1". * + * 0 = CD1 + * 1 = CD2 + * 2 = Counterstrike + * 3 = Aftermath + * 4 = Counterstrike or Aftermath + * 5 = DVD + * -1 = Any CD + * -2 = Local Harddisk + * * + * OUTPUT: Is the CD inserted and available? If false is returned, then this indicates that * + * the player pressed . * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1995 JLB : Created. * + * 05/22/1996 ST : Handles multiple CD drives / CD changers * + * 01/20/1997 V.Grippi added expansion cd message + *=============================================================================================*/ + +#if (1) //ST - 5/13/2019 +bool Force_CD_Available(int cd) +{ + static int _last = -1; + + if (_last != cd || cd == ALWAYS_RELOAD_CD) { + if (cd != ALWAYS_RELOAD_CD) { + _last = cd; + } + Theme.Stop(); + + // if (ConquerMix) delete ConquerMix; + if (MoviesMix) { + delete MoviesMix; + MoviesMix = 0; + } + if (GeneralMix) { + delete GeneralMix; + GeneralMix = 0; + } + if (ScoreMix) { + delete ScoreMix; + ScoreMix = 0; + } + if (MainMix) { + delete MainMix; + MainMix = 0; + } + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + // ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + else + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + } + + return true; +} + + +#endif + +#if (0) //ST - 5/13/2019 + +typedef enum { + CD_LOCAL = -2, + CD_ANY = -1, + CD_SOVIET = 0, + CD_ALLIED, + CD_COUNTERSTRIKE, + CD_AFTERMATH, + CD_CS_OR_AM, + CD_DVD +} CD_VOLUME; + +#ifdef FIXIT_VERSION_3 + +#ifndef DVD +#error DVD must be defined! +#endif + +bool Force_CD_Available( int cd_desired ) // ajw +{ + static int _last = -1; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + "ALERTE ROUGE DVD", +}; +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1", + "ALARMSTUFE ROT CD2", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + "ALARMSTUFE ROT DVD", + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + "RED ALERT DVD", + }; +#endif + + + + int new_cd_drive = 0; + int cd_current; + int current_drive; + + ThemeType theme_playing = THEME_NONE; + + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd_desired == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_current = Get_CD_Index(current_drive, 1*60); + +// debugprint("Get_CD_Index just returned %d\n", cd_current); +// debugprint("We are checking for %d\n", cd_desired); +// debugprint("current_drive = %d\n", current_drive); + + if( Using_DVD() ) + { + // All requested cd indexes get rerouted to the DVD. + cd_desired = CD_DVD; +// if( RequiredCD != -1 ) +// RequiredCD = CD_DVD; // Just seems like a good idea. Not sure if necessary. ajw + } + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the current CD is requested or any CD will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) // Else we have already checked this cd. + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_current = Get_CD_Index(last_drive, 10*60); + + if (cd_current >= 0 ) + { + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + // If the cd is present or any cd will work + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + int drive_search_timeout = 2*60; + + for (;;) + { + char buffer[128]; + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + int cd_drive = CDList.Get_Next_CD_Drive(); + cd_current = Get_CD_Index(cd_drive, drive_search_timeout); + + if (cd_current >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + // Require CS or AM + if( cd_desired == CD_CS_OR_AM ) + { + // If the cd is CS or AM then change request to whatever + // is present. + if( cd_current == CD_COUNTERSTRIKE || cd_current == CD_AFTERMATH ) + cd_desired = cd_current; + } + + if( cd_desired == cd_current || cd_desired == CD_ANY ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + if( cd_desired == CD_CS_OR_AM ) + cd_desired = CD_AFTERMATH; + + if( cd_desired == CD_DVD ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + } + else if( cd_desired == CD_COUNTERSTRIKE || cd_desired == CD_AFTERMATH ) + { + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd_desired]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd_desired]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd_desired]); + #endif + #endif + } + else if( cd_desired == CD_ANY ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd_desired+1, _cd_name[cd_desired]); + } + else // 0 or 1 + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd_desired+1, _cd_name[cd_desired]); + } + + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); +#ifdef FIXIT_VERSION_3 + while (hidden--) Hide_Mouse(); +#else + Hide_Mouse(); +#endif + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_current; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + if (cd_desired == 4) cd_desired--; + + // ajw - Added condition of cd_desired != 5 to the following if. + // Reason: This was triggering before Init_Secondary_Mixfiles(), which was screwing up the mixfile system somehow. + // + // Since the DVD is the only disk that can possibly be required when Using_DVD(), I never have to reload the mix + // files here, because no other disk could ever have been asked for. And if not Using_DVD(), cd_desired will never + // be equal to 5. So this is safe. + if (cd_desired > -1 && _last != cd_desired && cd_desired != 5) + { + _last = cd_desired; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + if (CCFileClass("MOVIES1.MIX").Is_Available()) + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + else + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + return(true); +} + +#else // FIXIT_VERSION_3 not defined + +bool Force_CD_Available(int cd) +{ + static int _last = -1; +// static char _palette[768]; +// static char _hold[256]; + static void *font; +#ifdef FRENCH + static char * _cd_name[] = { + "ALERTE ROUGE CD1", + "ALERTE ROUGE CD2", + "CD Missions Taiga", + "CD Missions M.A.D.", + + // Denzil 4/15/98 + #ifdef DVD + "ALERTE ROUGE DVD", + #endif +}; + +#endif +#ifdef GERMAN + static char * _cd_name[] = { + "ALARMSTUFE ROT CD1 einlegen", + "ALARMSTUFE ROT CD2 einlegen", + "CD Gegenangriff einlegen", + "CD TRANS einlegen", + + // Denzil 4/15/98 + #ifdef DVD + "ALARMSTUFE ROT DVD einlegen", + #endif + }; +#endif +#ifdef ENGLISH + static char * _cd_name[] = { + "RED ALERT DISK 1", + "RED ALERT DISK 2", + "CounterStrike CD", + "Aftermath CD", + + // Denzil 4/15/98 + #ifdef DVD + "RED ALERT DVD", + #endif + }; +#endif + + int new_cd_drive = 0; + int cd_index; + char buffer[128]; + int cd_drive; + int current_drive; + int drive_search_timeout; + + ThemeType theme_playing = THEME_NONE; + +//#ifdef FIXIT_ANTS +// if(Scen.ScenarioName[2] == 'A') +// cd = 2; +//#endif + /* + ** If the required CD is set to -2 then it means that the file is present + ** on the local hard drive and we shouldn't have to worry about it. + */ + if (cd == CD_LOCAL) return(true); + + /* + ** Find out if the CD in the current drive is the one we are looking for + */ + current_drive = CCFileClass::Get_CD_Drive(); + cd_index = Get_CD_Index(current_drive, 1*60); + + #ifdef CHEAT_KEYS + // Mono_Printf("Get_CD_Index just returned %d\n", cd_index); + // Mono_Printf("We are checking for %d\n", cd); + // Mono_Printf("current_drive = %d\n", current_drive); + #endif //CHEAT_KEYS + + #ifdef DVD // Denzil + // CD1 and CD2 are ignored, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == CD_CS_OR_AM) + { + // If the current cd is CS or AM then change request to whatever + // is present. + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If the current drive is the DVD then requests for CD1 and CD2 are okay + if (cd_index == 4) + { + // CD1, CD2 & DVD requests + if (cd == 0 || cd == 1 || cd == 5) + { + cd_index = cd; + } + } + #endif + + // If the current CD is requested or any CD will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is still in the CD drive we used last time + */ + new_cd_drive = current_drive; + } + } + + /* + ** Flag that we will have to restart the theme + */ + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + + // Check the last drive + if (!new_cd_drive) + { + /* + ** Check the last CD drive we used if it's different from the current one + */ + int last_drive = CCFileClass::Get_Last_CD_Drive(); + + /* + ** Make sure the last drive is valid and it isn't the current drive + */ + if (last_drive && last_drive != CCFileClass::Get_CD_Drive()) + { + /* + ** Find out if there is a C&C cd in the last drive and if so is it the one we are looking for + ** Give it a nice big timeout so the CD changer has time to swap the discs + */ + cd_index = Get_CD_Index(last_drive, 10*60); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2 disks, force DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0 ) + { + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If CS or AM was the last drive then use it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + // If DVD is in drive + if (cd_index == 4) + { + // CD1, CD2 and DVD requests are all on the DVD + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + // If the cd is present or any cd will work + if (cd == cd_index || cd == -1 ) + { + /* + ** The required CD is in the CD drive we used last time + */ + new_cd_drive = last_drive; + } + } + } + } + + /* + ** Lordy. No sign of that blimming CD anywhere. Search all the CD drives + ** then if we still can't find it prompt the user to insert it. + */ + if (!new_cd_drive) + { + /* + ** Small timeout for the first pass through the drives + */ + drive_search_timeout = 2*60; + + for (;;) + { + /* + ** Search all present CD drives for the required disc. + */ + for (int i = 0 ; i < CDList.Get_Number_Of_Drives(); i++) + { + cd_drive = CDList.Get_Next_CD_Drive(); + cd_index = Get_CD_Index(cd_drive, drive_search_timeout); + + #ifdef DVD // Denzil + // Ignore CD1 and CD2, force the DVD + if (cd_index == 0 || cd_index == 1) + cd_index = -1; + #endif + + if (cd_index >= 0) + { + /* + ** We found a C&C cd - lets see if it was the one we were looking for + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Require CS or AM + if (cd == 4) + { + // If the disk is CS or AM then request it + if (cd_index == 2 || cd_index == 3) + { + cd = cd_index; + } + } + #endif + + #ifdef DVD // Denzil + if (cd_index == 4) + { + if ((cd == 0) || (cd == 1) || (cd == 5)) + { + cd_index = cd; + } + } + #endif + + if (cd == cd_index || cd == -1 || cd == -2 ) + { + /* + ** Woohoo! The disk was in a different cd drive. Refresh the search path list + * and return. + */ + new_cd_drive = cd_drive; + break; + } + } + } + + /* + ** A new disc has become available so break + */ + if (new_cd_drive) break; + + /* + ** Increase the timeout for subsequent drive searches. + */ + drive_search_timeout = 5*60; + + /* + ** Prompt to insert the CD into the drive. + */ + //V.Grippi + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(cd == 4) cd--; + + // CS or AM + if(cd == 2 || cd == 3) + { + #else + if(cd == 2) + { + #endif + + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[cd]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[cd]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[cd]); + #endif + #endif + } + else + { + #ifdef DVD + #ifdef FRENCH + sprintf(buffer, "InsŠrez le %s", _cd_name[4]); + #else + #ifdef GERMAN + sprintf(buffer, "Bitte %s", _cd_name[4]); + #else + sprintf(buffer, "Please insert the %s", _cd_name[4]); + #endif + #endif + #else + if (cd == -1 ) + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_1), cd+1, _cd_name[cd]); + } + else + { + sprintf(buffer, Text_String(TXT_CD_DIALOG_2), cd+1, _cd_name[cd]); + } + #endif + } + + #ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(SeenBuff); + #else + GraphicBufferClass * oldpage = Set_Logic_Page(SeenBuff); + #endif + theme_playing = Theme.What_Is_Playing(); + Theme.Stop(); + int hidden = Get_Mouse_State(); + font = (void *)FontPtr; + + /* + ** Only set the palette if necessary. + */ + if (PaletteClass::CurrentPalette[1].Red_Component() + + PaletteClass::CurrentPalette[1].Blue_Component() + + PaletteClass::CurrentPalette[1].Green_Component() == 0) + { + GamePalette.Set(); + } + + Keyboard->Clear(); + + while (Get_Mouse_State()) Show_Mouse(); + + if (WWMessageBox().Process(buffer, TXT_OK, TXT_CANCEL, TXT_NONE, TRUE) == 1) + { + Set_Logic_Page(oldpage); + Hide_Mouse(); + return(false); + } + + while (hidden--) Hide_Mouse(); + Set_Font(font); + Set_Logic_Page(oldpage); + } + } + + CurrentCD = cd_index; + + CCFileClass::Set_CD_Drive(new_cd_drive); + CCFileClass::Refresh_Search_Drives(); + + /* + ** If it broke out of the query for CD-ROM loop, then this means that the + ** CD-ROM has been inserted. + */ + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cd == 4) cd--; + #endif +// if (cd > -3 && _last != cd) { + if (cd > -1 && _last != cd) + { + _last = cd; + + Theme.Stop(); + +// if (ConquerMix) delete ConquerMix; + if (MoviesMix) delete MoviesMix; + if (GeneralMix) delete GeneralMix; + if (ScoreMix) delete ScoreMix; + if (MainMix) delete MainMix; + + MainMix = new MFCD("MAIN.MIX", &FastKey); + + assert(MainMix != NULL); +// ConquerMix = new MFCD("CONQUER.MIX", &FastKey); + + if (CCFileClass("MOVIES1.MIX").Is_Available()) + { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); + } + else + { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); + } + assert(MoviesMix != NULL); + GeneralMix = new MFCD("GENERAL.MIX", &FastKey); + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + } + + if (theme_playing != THEME_NONE) + { + Theme.Queue_Song(theme_playing); + } + + return(true); + } + + +#endif // FIXIT_VERSION_3 +#endif // ST - 5/13/2019 + + +/*************************************************************************** + * DISK_SPACE_AVAILABLE -- returns bytes of free disk space * + * * + * INPUT: none * + * * + * OUTPUT: returns amount of free disk space * + * * + * HISTORY: * + * 08/11/1995 PWG : Created. * + *=========================================================================*/ +unsigned long Disk_Space_Available(void) +{ + return 0x7fffffff; //ST - 5/8/2019 +#if (0) + struct diskfree_t diskdata; + unsigned drive; + + _dos_getdrive(&drive); + _dos_getdiskfree(drive, &diskdata); + + return(diskdata.avail_clusters * diskdata.sectors_per_cluster * diskdata.bytes_per_sector); +#endif +} + + +/*********************************************************************************************** + * Do_Record_Playback -- handles saving/loading map pos & current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Do_Record_Playback(void) +{ + int count; + TARGET tgt; + int i; + COORDINATE coord; + ObjectClass * obj; + unsigned long sum; + unsigned long sum2; + unsigned long ltgt; + + /* + ** Record a game + */ + if (Session.Record) { + + /* + ** Save the map's location + */ + Session.RecordFile.Write(&Map.DesiredTacticalCoord, + sizeof (Map.DesiredTacticalCoord)); + + /* + ** Save the current object list count + */ + count = CurrentObject.Count(); + Session.RecordFile.Write(&count, sizeof(count)); + + /* + ** Save a CRC of the selected-object list. + */ + sum = 0; + for (i = 0; i < count; i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + Session.RecordFile.Write (&sum, sizeof(sum)); + + /* + ** Save all selected objects. + */ + for (i = 0; i < count; i++) { + tgt = CurrentObject[i]->As_Target(); + Session.RecordFile.Write (&tgt, sizeof(tgt)); + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Write (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Write (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Write (&FormationEvent, sizeof(FormationEvent)); + Session.RecordFile.Write (TeamMaxSpeed, sizeof(TeamMaxSpeed)); + Session.RecordFile.Write (TeamSpeed, sizeof(TeamSpeed)); + Session.RecordFile.Write (&FormMove, sizeof(FormMove)); + Session.RecordFile.Write (&FormSpeed, sizeof(FormSpeed)); + Session.RecordFile.Write (&FormMaxSpeed, sizeof(FormMaxSpeed)); + TeamEvent = 0; + TeamNumber = 0; + FormationEvent = 0; + } + + /* + ** Play back a game ("attract" mode) + */ + if (Session.Play) { + + /* + ** Read & set the map's location. + */ + if (Session.RecordFile.Read(&coord, sizeof(coord))==sizeof(coord)) { + if (coord != Map.DesiredTacticalCoord) { + Map.Set_Tactical_Position(coord); + } + } + + if (Session.RecordFile.Read(&count, sizeof(count))==sizeof(count)) { + /* + ** Compute a CRC of the current object-selection list. + */ + sum = 0; + for (i = 0; i < CurrentObject.Count(); i++) { + ltgt = (unsigned long)(CurrentObject[i]->As_Target()); + sum += ltgt; + } + + /* + ** Load the CRC of the objects on disk; if it doesn't match, select + ** all objects as they're loaded. + */ + Session.RecordFile.Read (&sum2, sizeof(sum2)); + if (sum2 != sum) { + Unselect_All(); + } + + AllowVoice = true; + + for (i = 0; i < count; i++) { + if (Session.RecordFile.Read (&tgt, sizeof(tgt))==sizeof(tgt)) { + obj = As_Object(tgt); + if (obj && (sum2 != sum)) { + obj->Select(); + AllowVoice = false; + } + } + } + + AllowVoice = true; + + } + + // + // Save team-selection and formation events + // + Session.RecordFile.Read (&TeamEvent, sizeof(TeamEvent)); + Session.RecordFile.Read (&TeamNumber, sizeof(TeamNumber)); + Session.RecordFile.Read (&FormationEvent, sizeof(FormationEvent)); + if (TeamEvent) { + Handle_Team(TeamNumber, TeamEvent - 1); + } + if (FormationEvent) { + Toggle_Formation(); + } + +Session.RecordFile.Read (TeamMaxSpeed, sizeof(TeamMaxSpeed)); +Session.RecordFile.Read (TeamSpeed, sizeof(TeamSpeed)); +Session.RecordFile.Read (&FormMove, sizeof(FormMove)); +Session.RecordFile.Read (&FormSpeed, sizeof(FormSpeed)); +Session.RecordFile.Read (&FormMaxSpeed, sizeof(FormMaxSpeed)); + /* + ** The map isn't drawn in playback mode, so draw it here. + */ + Map.Render(); + } +} + + +/*********************************************************************************************** + * Hires_Load -- Allocates memory for, and loads, a resolution dependant file. * + * * + * * + * * + * INPUT: Name of file to load * + * * + * OUTPUT: Ptr to loaded file * + * * + * WARNINGS: Caller is responsible for releasing the memory allocated * + * * + * * + * HISTORY: * + * 5/13/96 3:20PM ST : Created * + *=============================================================================================*/ +void * Hires_Load(char * name) +{ + char filename[30]; + int length; + void * return_ptr; + +#ifdef WIN32 + sprintf(filename, "H%s", name); +#else + strcpy(filename, name); +#endif + CCFileClass file (filename); + + if (file.Is_Available()) { + + length = file.Size(); + return_ptr = new char[length]; + file.Read(return_ptr, length); + return (return_ptr); + + } else { + return (NULL); + } +} + + +/*********************************************************************************************** + * Crate_From_Name -- Given a crate name convert it to a crate type. * + * * + * Use this routine to convert an ASCII crate name into a crate type. If no match could * + * be found, then CRATE_MONEY is assumed. * + * * + * INPUT: name -- Pointer to the crate name text to convert into a crate type. * + * * + * OUTPUT: Returns with the crate name converted into a crate type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +CrateType Crate_From_Name(char const * name) +{ + if (name != NULL) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + if (stricmp(name, CrateNames[crate]) == 0) return(crate); + } + } + return(CRATE_MONEY); +} + + +/*********************************************************************************************** + * Owner_From_Name -- Convert an owner name into a bitfield. * + * * + * This will take an owner specification and convert it into a bitfield that represents * + * it. Sometimes this will be just a single house bit, but other times it could be * + * all the allies or soviet house bits combined. * + * * + * INPUT: text -- Pointer to the text to convert into a house bitfield. * + * * + * OUTPUT: Returns with the houses specified. The value is in the form of a bit field with * + * one bit per house type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int Owner_From_Name(char const * text) +{ + int ownable = 0; + if (stricmp(text, "soviet") == 0) { + ownable |= HOUSEF_SOVIET; + } else { + if (stricmp(text, "allies") == 0 || stricmp(text, "allied") == 0) { + ownable |= HOUSEF_ALLIES; + } else { + HousesType h = HouseTypeClass::From_Name(text); + if (h != HOUSE_NONE && (h < HOUSE_MULTI1 || h > HOUSE_MULTI8)) { + ownable |= (1 << h); + } + } + } + return(ownable); +} + + +/*********************************************************************************************** + * Shake_The_Screen -- Dispatcher that shakes the screen. * + * * + * This routine will shake the game screen the number of shakes requested. * + * * + * INPUT: shakes -- The number of shakes to shake the screen. * + * house -- House to perform the shake for (or HOUSE_NONE if all players). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 BWG : Created. * + *=============================================================================================*/ +void Shake_The_Screen(int shakes, HousesType house) +{ + for (char h = HOUSE_FIRST; h < HOUSE_COUNT; ++h) { + if ((house != HOUSE_NONE) && (h != house)) { + continue; + } + HouseClass* hptr = HouseClass::As_Pointer((HousesType)h); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + hptr->ScreenShakeTime = CDTimerClass(hptr->ScreenShakeTime + shakes + shakes); + hptr->ScreenShakeTime.Start(); + } + } +#if (0) +#ifdef WIN32 + shakes += shakes; + + Hide_Mouse(); + SeenPage.Blit(HidPage); + int oldyoff = 0; + int newyoff = 0; + while(shakes--) { + int x = TickCount; +// CountDownTimer = 1; + do { + newyoff = Sim_Random_Pick(0,2) - 1; + } while (newyoff == oldyoff); + switch (newyoff) { + case -1: + HidPage.Blit(SeenPage, 0,2, 0,0, 640,398); + break; + case 0: + HidPage.Blit(SeenPage); + break; + case 1: + HidPage.Blit(SeenPage, 0,0, 0,2, 640,398); + break; + } while (x == TickCount); +// } while (CountDownTimer != 0) ; + } + HidPage.Blit(SeenPage); + Show_Mouse(); +#else + Shake_Screen(shakes); +#endif +#endif +} + + +/*********************************************************************************************** + * List_Copy -- Makes a copy of a cell offset list. * + * * + * This routine will make a copy of a cell offset list. It will only copy the significant * + * elements of the list limited by the maximum length specified. * + * * + * INPUT: source -- Pointer to a cell offset list. * + * * + * len -- The maximum number of cell offset elements to store in to the * + * destination list pointer. * + * * + * dest -- Pointer to the destination list to store the copy into. * + * * + * OUTPUT: none * + * * + * WARNINGS: Ensure that the destination list is large enough to hold the list copy. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void List_Copy(short const * source, int len, short * dest) +{ + if (dest == NULL || dest == NULL) { + return; + } + + while (len > 0) { + *dest = *source; + if (*dest == REFRESH_EOL) break; + dest++; + source++; + len--; + } +} + + + +#if 0 +// +// Boy, this function sure is crummy +// +void Crummy(int crumb1, int crumb2) +{ + if (Debug_Check_Map && Debug_Heap_Dump) { + Mono_Printf("Hi, I'm Crummy. And so are these: %d, %d\n",crumb1,crumb2); + } +} +#endif + + + +/*********************************************************************************************** + * Game_Registry_Key -- Returns pointer to string containing the registry subkey for the game. + * This is located under HKEY_LOCAL_MACHINE. + * HISTORY: + * 11/19/98 ajw : Created + *=============================================================================================*/ +const char* Game_Registry_Key() +{ +#ifdef ENGLISH + static char szKey[] = "SOFTWARE\\Westwood\\Red Alert Windows 95 Edition"; +#else +#ifdef GERMAN + static char szKey[] = "SOFTWARE\\Westwood\\Alarmstufe Rot Windows 95 Edition"; +#else + static char szKey[] = "SOFTWARE\\Westwood\\Alerte Rouge version Windows 95"; +#endif +#endif + return szKey; +} + + +/*********************************************************************************************** + * Is_Counterstrike_Installed -- Function to determine the availability of the CS expansion * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Counterstrike is present * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 11:39PM ST : Created * + *=============================================================================================*/ +bool Is_Counterstrike_Installed (void) +{ + return true; // Remasters always have Counterstrike. ST - 10/18/2019 11:06AM + +#if (0) + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "CStrikeInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +#endif +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + *=============================================================================================*/ +bool Is_Aftermath_Installed (void) +{ + return true; // Remasters always have Aftermath. ST - 10/18/2019 11:06AM + +#if (0) + // ajw 9/29/98 + static bool bAlreadyChecked = false; + static bool bInstalled = false; + + if( !bAlreadyChecked ) + { + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "AftermathInstalled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + bAlreadyChecked = true; + } + return bInstalled; + +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +#endif +} +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +void Enable_Secret_Units(void) +{ +#if 0 + SecretUnitsEnabled=true; + UnitTypeClass::As_Reference(UNIT_PHASE).Level=10; + VesselTypeClass::As_Reference(VESSEL_CARRIER).Level=10; + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } +#endif +} +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ) +{ + // Calls Force_CD_Available based on type of scenario. szName is assumed to be an official scenario here. + if( Is_Mission_Counterstrike( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 4...\n" ); + return Force_CD_Available( 4 ); + } + else if( Is_Mission_Aftermath( (char*)szName ) ) + { +// debugprint( "Force_Scenario_Available requiring disk 3...\n" ); + return Force_CD_Available( 3 ); + } + return true; +} +#endif \ No newline at end of file diff --git a/code/redalert/conquer.h b/code/redalert/conquer.h new file mode 100644 index 0000000..8fb1b6d --- /dev/null +++ b/code/redalert/conquer.h @@ -0,0 +1,571 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define TXT_NONE 0 // +#define TXT_CREDIT_FORMAT 1 // %3d.%02d +#define TXT_TIME_FORMAT_HOURS 2 // Temps:%02d:%02d:%02d +#define TXT_TIME_FORMAT_NO_HOURS 3 // Temps:%02d:%02d +#define TXT_BUTTON_SELL 4 // Vente +#define TXT_SELL 5 // Vente structure +#define TXT_BUTTON_REPAIR 6 // R‚paration +#define TXT_YOU 7 // Vous : +#define TXT_ENEMY 8 // Ennemi : +#define TXT_BUILD_DEST 9 // Bƒtiments d‚truits par +#define TXT_UNIT_DEST 10 // Unit‚s d‚truites par +#define TXT_TIB_HARV 11 // Minerai r‚colt‚ par +#define TXT_SCORE_1 12 // Score: %d +#define TXT_YES 13 // Oui +#define TXT_NO 14 // Non +#define TXT_SCENARIO_WON 15 // Mission Accomplie +#define TXT_SCENARIO_LOST 16 // Mission Echou‚e +#define TXT_START_NEW_GAME 17 // Nouvelle partie +#define TXT_INTRO 18 // Intro/Preview +#define TXT_CANCEL 19 // Annuler +#define TXT_ROCK 20 // Rocher +#define TXT_CIVILIAN 21 // Civil +#define TXT_JP 22 // Equipe de confinement +#define TXT_OK 23 // OK +#define TXT_TREE 24 // Arbre +#define TXT_LEFT 25 //  +#define TXT_RIGHT 26 //  +#define TXT_UP 27 //  +#define TXT_DOWN 28 //  +#define TXT_CLEAR 29 // Effacer +#define TXT_WATER 30 // Eau +#define TXT_ROAD 31 // Route +#define TXT_SLOPE 32 // Pente +#define TXT_PATCH 33 // Patch +#define TXT_RIVER 34 // RiviŠre +#define TXT_LOAD_MISSION 35 // Charger Mission +#define TXT_SAVE_MISSION 36 // Sauvegarder Mission +#define TXT_DELETE_MISSION 37 // Effacer Mission +#define TXT_LOAD_BUTTON 38 // Charger +#define TXT_SAVE_BUTTON 39 // Sauvegarder +#define TXT_DELETE_BUTTON 40 // Effacer +#define TXT_GAME_CONTROLS 41 // Contr“les +#define TXT_SOUND_CONTROLS 42 // Son +#define TXT_RESUME_MISSION 43 // Reprendre Mission +#define TXT_VISUAL_CONTROLS 44 // Affichage +#define TXT_QUIT_MISSION 45 // Abandonner Mission +#define TXT_EXIT_GAME 46 // Quitter le jeu +#define TXT_OPTIONS 47 // Options +#define TXT_SQUISH 48 // D‚bris humains +#define TXT_CRATER 49 // CratŠre +#define TXT_SCORCH 50 // Marque de br–lure +#define TXT_BRIGHTNESS 51 // Luminosit‚ : +#define TXT_MUSIC 52 // Musique +#define TXT_VOLUME 53 // Effets sonores +#define TXT_TINT 54 // Teintes : +#define TXT_CONTRAST 55 // Contraste : +#define TXT_SPEED 56 // Vitesse du jeu : +#define TXT_SCROLLRATE 57 // Vitesse d‚filement : +#define TXT_COLOR 58 // Couleur : +#define TXT_RETURN_TO_GAME 59 // Revenir au jeu +#define TXT_ENEMY_SOLDIER 60 // Soldat ennemi +#define TXT_ENEMY_VEHICLE 61 // V‚hicule ennemi +#define TXT_ENEMY_STRUCTURE 62 // Structure ennemie +#define TXT_LTANK 63 // Tank l‚ger +#define TXT_MTANK 64 // Tank lourd +#define TXT_MTANK2 65 // Tank moyen +#define TXT_HTANK 66 // Tank Mammouth +#define TXT_SAM 67 // Missiles SAM +#define TXT_JEEP 68 // Ranger +#define TXT_TRANS 69 // H‚licoptŠre Chinook +#define TXT_HARVESTER 70 // Collecteur minerai +#define TXT_ARTY 71 // Artillerie +#define TXT_E1 72 // Mitrailleurs +#define TXT_E2 73 // Grenadiers +#define TXT_E3 74 // Bazookas +#define TXT_E4 75 // Lance-flammes +#define TXT_HELI 76 // H‚licoptŠre d'assaut +#define TXT_ORCA 77 // Hind +#define TXT_APC 78 // VBT +#define TXT_GUARD_TOWER 79 // Tour de garde +#define TXT_COMMAND 80 // D“me radar +#define TXT_HELIPAD 81 // H‚liport +#define TXT_AIRSTRIP 82 // Piste d'atterrissage +#define TXT_STORAGE 83 // Silo minerai +#define TXT_CONST_YARD 84 // Chantier de construction +#define TXT_REFINERY 85 // Raffinerie de minerai +#define TXT_CIV1 86 // Eglise +#define TXT_CIV2 87 // Chez Hans et Gretel +#define TXT_CIV3 88 // Manoir d'Hewitt +#define TXT_CIV4 89 // Maison de Ricktor +#define TXT_CIV5 90 // Maison de Gretchin +#define TXT_CIV6 91 // La grange +#define TXT_CIV7 92 // Pub Damon +#define TXT_CIV8 93 // Maison de Fran +#define TXT_CIV9 94 // Usine d'instruments +#define TXT_CIV10 95 // Fabricant de jouets +#define TXT_CIV11 96 // Maison de Ludwig +#define TXT_CIV12 97 // Meules de foin +#define TXT_CIV13 98 // Meule de foin +#define TXT_CIV14 99 // Champ de bl‚ +#define TXT_CIV15 100 // Champ en friche +#define TXT_CIV16 101 // Champ de ma‹s +#define TXT_CIV17 102 // Champ de c‚leri +#define TXT_CIV18 103 // Champ de pommes de terre +#define TXT_CIV20 104 // Maison de Sala +#define TXT_CIV21 105 // Maison d'Abdul +#define TXT_CIV22 106 // Le Pub Barjo de Pablo +#define TXT_CIV23 107 // Puits du village +#define TXT_CIV24 108 // Marchand de chameaux +#define TXT_CIV25 109 // Eglise +#define TXT_CIV26 110 // Maison d'Ali +#define TXT_CIV27 111 // Ted le Marchand +#define TXT_CIV28 112 // Maison de Menelik +#define TXT_CIV29 113 // Maison du pasteur John +#define TXT_CIV30 114 // Puits du village +#define TXT_CIV31 115 // Hutte du gu‚risseur +#define TXT_CIV32 116 // Hutte de Rikitikitembo +#define TXT_CIV33 117 // Hutte de Roarke +#define TXT_CIV34 118 // Hutte de Moubasa' +#define TXT_CIV35 119 // Hutte d'Aksoum +#define TXT_CIV36 120 // Hutte de Mambo +#define TXT_CIV37 121 // Le studio +#define TXT_CIVMISS 122 // Centre technologique +#define TXT_TURRET 123 // Tourelle +#define TXT_GUNBOAT 124 // Aviso-torpilleur +#define TXT_MCV 125 // V‚hicule de construction +#define TXT_POWER 126 // Centrale ‚lectrique +#define TXT_ADVANCED_POWER 127 // Centrale ‚lectrique avanc‚e +#define TXT_HOSPITAL 128 // H“pital +#define TXT_BARRACKS 129 // Caserne +#define TXT_PUMP 130 // Pompe +#define TXT_TANKER 131 // P‚trolier +#define TXT_SANDBAG_WALL 132 // Sacs de sable +#define TXT_CYCLONE_WALL 133 // Cl“ture grillag‚e +#define TXT_BRICK_WALL 134 // Mur de b‚ton +#define TXT_BARBWIRE_WALL 135 // Cl“ture barbel‚e +#define TXT_WOOD_WALL 136 // BarriŠre de bois +#define TXT_WEAPON_FACTORY 137 // Usine d'armement +#define TXT_AGUARD_TOWER 138 // Tour de garde avanc‚e +#define TXT_BIO_LAB 139 // Laboratoire biologique +#define TXT_FIX_IT 140 // Centre de service +#define TXT_TAB_SIDEBAR 141 // Contr“les +#define TXT_TAB_BUTTON_CONTROLS 142 // Options +#define TXT_TAB_BUTTON_DATABASE 143 // Base de donn‚es +#define TXT_SHADOW 144 // Terrain inconnu +#define TXT_OPTIONS_MENU 145 // Menu des options +#define TXT_STOP 146 // Stop +#define TXT_PLAY 147 // Lect +#define TXT_SHUFFLE 148 // Al‚at. +#define TXT_REPEAT 149 // R‚p‚ter +#define TXT_MUSIC_VOLUME 150 // Musique : +#define TXT_SOUND_VOLUME 151 // Effets sonores : +#define TXT_ON 152 // Oui +#define TXT_OFF 153 // Non +#define TXT_MULTIPLAYER_GAME 154 // Jeu Multijoueurs +#define TXT_NO_FILES 155 // Pas de fichiers disponibles +#define TXT_DELETE_SINGLE_FILE 156 // Voulez-vous effacer ce +#define TXT_DELETE_MULTIPLE_FILES 157 // Voulez-vous effacer %d +#define TXT_RESET_MENU 158 // D‚faut +#define TXT_CONFIRM_EXIT 159 // Voulez-vous abandonner la +#define TXT_MISSION_DESCRIPTION 160 // Description de la mission +#define TXT_C1 161 // Joe +#define TXT_C2 162 // Barry +#define TXT_C3 163 // Shelly +#define TXT_C4 164 // Maria +#define TXT_C5 165 // Karen +#define TXT_C6 166 // Steve +#define TXT_C7 167 // Phil +#define TXT_C8 168 // Dwight +#define TXT_C9 169 // Erik +#define TXT_EINSTEIN 170 // Prof. Einstein +#define TXT_BIB 171 // Cour +#define TXT_FASTER 172 // Rapide +#define TXT_SLOWER 173 // Lent +#define TXT_AIR_STRIKE 174 // Attaque a‚rienne +#define TXT_STEEL_CRATE 175 // Caisse d'acier +#define TXT_WOOD_CRATE 176 // Caisse de bois +#define TXT_WATER_CRATE 177 // Caisse flottante +#define TXT_FLAG_SPOT 178 // Emplacement du drapeau +#define TXT_UNABLE_READ_SCENARIO 179 // Lecture sc‚nario impossible +#define TXT_ERROR_LOADING_GAME 180 // Erreur de chargement ! +#define TXT_OBSOLETE_SAVEGAME 181 // Sauvegarde obsolŠte +#define TXT_MUSTENTER_DESCRIPTION 182 // Vous devez entrer une +#define TXT_ERROR_SAVING_GAME 183 // Erreur de sauvegarde ! +#define TXT_DELETE_FILE_QUERY 184 // Effacer ce fichier ? +#define TXT_EMPTY_SLOT 185 // [EMPLACEMENT VIDE] +#define TXT_SELECT_MPLAYER_GAME 186 // Choix du jeu Multijoueurs +#define TXT_MODEM_SERIAL 187 // Modem/S‚rie +#define TXT_NETWORK 188 // R‚seau +#define TXT_INIT_NET_ERROR 189 // Initialisation r‚seau +#define TXT_JOIN_NETWORK_GAME 190 // Rejoindre jeu en r‚seau +#define TXT_NEW 191 // Nouveau +#define TXT_JOIN 192 // Joindre +#define TXT_SEND_MESSAGE 193 // Envoi Message +#define TXT_YOUR_NAME 194 // Votre nom : +#define TXT_SIDE_COLON 195 // Camp : +#define TXT_COLOR_COLON 196 // Couleur : +#define TXT_GAMES 197 // Parties +#define TXT_PLAYERS 198 // Joueurs +#define TXT_SCENARIO_COLON 199 // Sc‚nario : +#define TXT_NOT_FOUND 200 // >> NON TROUVE << +#define TXT_START_CREDITS_COLON 201 // Cr‚dits : +#define TXT_BASES_COLON 202 // Bases : +#define TXT_TIBERIUM_COLON 203 // Minerai : +#define TXT_CRATES_COLON 204 // Caisses : +#define TXT_AI_PLAYERS_COLON 205 // Joueurs IA : +#define TXT_REQUEST_DENIED 206 // Demande refus‚e. +#define TXT_UNABLE_PLAY_WAAUGH 207 // Impossible de jouer, +#define TXT_NOTHING_TO_JOIN 208 // Aucune partie disponible ! +#define TXT_NAME_ERROR 209 // Vous devez entrer un nom ! +#define TXT_DUPENAMES_NOTALLOWED 210 // Les noms identiques ne sont +#define TXT_YOURGAME_OUTDATED 211 // La version de votre jeu est +#define TXT_DESTGAME_OUTDATED 212 // Version du jeu de +#define TXT_THATGUYS_GAME 213 // Partie de %s +#define TXT_THATGUYS_GAME_BRACKET 214 // [Partie de %s] +#define TXT_NETGAME_SETUP 215 // Configuration du jeu en +#define TXT_REJECT 216 // Rejeter +#define TXT_CANT_REJECT_SELF 217 // Vous ne pouvez pas vous +#define TXT_SELECT_PLAYER_REJECT 218 // Vous devez s‚lectionner un +#define TXT_BASES 219 // Bases +#define TXT_CRATES 220 // Caisses +#define TXT_AI_PLAYERS 221 // Joueur IA +#define TXT_SCENARIOS 222 // Sc‚narios +#define TXT_CREDITS_COLON 223 // Cr‚dits : +#define TXT_ONLY_ONE 224 // Un seul joueur ? +#define TXT_OOPS 225 // Oops ! +#define TXT_TO 226 // Pour %s : +#define TXT_TO_ALL 227 // Pour tous +#define TXT_MESSAGE 228 // Message : +#define TXT_CONNECTION_LOST 229 // Perte de connexion avec %s +#define TXT_LEFT_GAME 230 // %s a quitt‚ le jeu. +#define TXT_PLAYER_DEFEATED 231 // %s a ‚t‚ vaincu ! +#define TXT_WAITING_CONNECT 232 // Attente de connexion... +#define TXT_NULL_CONNERR_CHECK_CABLES 233 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_REDIALING 234 // Erreur de connexion ! +#define TXT_MODEM_CONNERR_WAITING 235 // Erreur de connexion ! +#define TXT_SELECT_SERIAL_GAME 236 // Choix du jeu en s‚rie +#define TXT_DIAL_MODEM 237 // Appel +#define TXT_ANSWER_MODEM 238 // Attente appel +#define TXT_NULL_MODEM 239 // Null Modem +#define TXT_SETTINGS 240 // ParamŠtres +#define TXT_PORT_COLON 241 // Port : +#define TXT_IRQ_COLON 242 // IRQ : +#define TXT_BAUD_COLON 243 // Bauds : +#define TXT_INIT_STRING 244 // ChaŒne d'initialisation +#define TXT_CWAIT_STRING 245 // ChaŒne d'attente d'appel +#define TXT_TONE_BUTTON 246 // Tonalit‚ +#define TXT_PULSE_BUTTON 247 // Impulsions +#define TXT_HOST_SERIAL_GAME 248 // H“te du jeu en s‚rie +#define TXT_OPPONENT_COLON 249 // Adversaire : +#define TXT_USER_SIGNED_OFF 250 // Utilisateur reparti ! +#define TXT_JOIN_SERIAL_GAME 251 // Rejoindre jeu en s‚rie +#define TXT_PHONE_LIST 252 // R‚pertoire +#define TXT_ADD 253 // Ajouter +#define TXT_EDIT 254 // Editer +#define TXT_DIAL 255 // Appel +#define TXT_DEFAULT 256 // D‚faut +#define TXT_DEFAULT_SETTINGS 257 // D‚faut +#define TXT_CUSTOM_SETTINGS 258 // Autres paramŠtres +#define TXT_PHONE_LISTING 259 // R‚pertoire +#define TXT_NAME_COLON 260 // Nom : +#define TXT_NUMBER_COLON 261 // Num‚ro : +#define TXT_UNABLE_FIND_MODEM 262 // Modem non d‚tect‚. V‚rifiez +#define TXT_NO_CARRIER 263 // Pas de porteuse. +#define TXT_LINE_BUSY 264 // Ligne occup‚e. +#define TXT_NUMBER_INVALID 265 // Num‚ro incorrect. +#define TXT_SYSTEM_NOT_RESPONDING 266 // L'autre systŠme ne r‚pond +#define TXT_OUT_OF_SYNC 267 // Mauvaise synchronisation ! +#define TXT_PACKET_TOO_LATE 268 // Paquet re‡u trop tard ! +#define TXT_PLAYER_LEFT_GAME 269 // L'autre joueur a quitt‚ le +#define TXT_FROM 270 // De %s:%s +#define TXT_SCORE_TIME 271 // TEMPS : +#define TXT_SCORE_LEAD 272 // COMMANDEMENT : +#define TXT_SCORE_EFFI 273 // EFFICACITE : +#define TXT_SCORE_TOTA 274 // SCORE TOTAL : +#define TXT_SCORE_CASU 275 // PERTES : +#define TXT_SCORE_NEUT 276 // NEUTRE : +#define TXT_SCORE_BUIL 277 // BATIMENTS PERDUS +#define TXT_SCORE_BUIL1 278 // BATIMENTS +#define TXT_SCORE_BUIL2 279 // PERDUS : +#define TXT_SCORE_TOP 280 // MEILLEURS SCORES +#define TXT_SCORE_ENDCRED 281 // CREDITS DE FIN : +#define TXT_SCORE_TIMEFORMAT1 282 // %dh %dm +#define TXT_SCORE_TIMEFORMAT2 283 // %dm +#define TXT_DIALING 284 // Appel... +#define TXT_DIALING_CANCELED 285 // Appel annul‚ +#define TXT_WAITING_FOR_CALL 286 // Attente d'appel... +#define TXT_ANSWERING_CANCELED 287 // Attente d'appel annul‚e +#define TXT_E6 288 // Ing‚nieurs +#define TXT_E8 289 // Espion +#define TXT_MODEM_OR_LOOPBACK 290 // Pas de cƒble Null Modem +#define TXT_MAP 291 // Carte +#define TXT_BLOSSOM_TREE 292 // Arbre en fleurs +#define TXT_RESTATE_MISSION 293 // Briefing +#define TXT_COMPUTER 294 // Joueur IA +#define TXT_COUNT 295 // Nombre : +#define TXT_LEVEL 296 // Niveau : +#define TXT_OPPONENT 297 // Adversaire +#define TXT_KILLS_COLON 298 // Vict.: +#define TXT_VIDEO 299 // Vid‚o +#define TXT_C10 300 // Scientifique +#define TXT_CAPTURE_THE_FLAG 301 // Capture drapeau +#define TXT_OBJECTIVE 302 // Objectifs de mission +#define TXT_MISSION 303 // Mission +#define TXT_NO_SAVES 304 // Pas de sauvegardes +#define TXT_CIVILIAN_BUILDING 305 // Bƒtiment civil +#define TXT_TECHNICIAN 306 // Technicien +#define TXT_NO_SAVELOAD 307 // Sauvegarde interdite en +#define TXT_DELPHI 308 // Agent Sp‚cial 1 +#define TXT_TO_REPLAY 309 // Voulez-vous recommencer +#define TXT_RECONN_TO 310 // Reconnexion vers %s. +#define TXT_PLEASE_WAIT 311 // Attendez %02d secondes. +#define TXT_SURRENDER 312 // Voulez-vous vous rendre ? +#define TXT_SEL_TRANS 313 // CHOIX DE LA TRANSMISSION +#define TXT_GAMENAME_MUSTBE_UNIQUE 314 // Les sauvegardes ne peuvent +#define TXT_GAME_IS_CLOSED 315 // Partie ferm‚e. +#define TXT_NAME_MUSTBE_UNIQUE 316 // Les noms doivent ˆtre tous +#define TXT_RECONNECTING_TO 317 // Reconnexion vers %s +#define TXT_WAITING_FOR_CONNECTIONS 318 // Attente de connexions... +#define TXT_TIME_ALLOWED 319 // Temps autoris‚ : %02d +#define TXT_PRESS_ESC 320 // Appuyez sur Echap pour +#define TXT_JUST_YOU_AND_ME 321 // De l'ordinateur : Il ne +#define TXT_CAPTURE_THE_FLAG_COLON 322 // Capture du drapeau : +#define TXT_CHAN 323 // Agent Sp‚cial 2 +#define TXT_HAS_ALLIED 324 // %s s'est alli‚(e) avec %s +#define TXT_AT_WAR 325 // %s d‚clare la guerre … %s +#define TXT_SEL_TARGET 326 // Choisissez un cible +#define TXT_RESIGN 327 // Abandonner +#define TXT_TIBERIUM_FAST 328 // Le minerai pousse trŠs +#define TXT_ANSWERING 329 // R‚ponse en cours... +#define TXT_INITIALIZING_MODEM 330 // Initialisation Modem... +#define TXT_SCENARIOS_DO_NOT_MATCH 331 // Les sc‚narios ne +#define TXT_POWER_OUTPUT 332 // Production d'‚nergie +#define TXT_POWER_OUTPUT_LOW 333 // Production d'‚nergie +#define TXT_CONTINUE 334 // Continuer +#define TXT_QUEUE_FULL 335 // Saturation des donn‚es … +#define TXT_SPECIAL_WARNING 336 // %s a modifi‚ les options de +#define TXT_CD_DIALOG_1 337 // Placez un CD d'Alerte Rouge +#define TXT_CD_DIALOG_2 338 // Placez le CD %d (%s) dans +#define TXT_CD_ERROR1 339 // Alerte Rouge n'a pas +#define TXT_NO_SOUND_CARD 340 // Pas de carte sonore +#define TXT_UNKNOWN 341 // INCONNU +#define TXT_OLD_GAME 342 // (ancien) +#define TXT_NO_SPACE 343 // Espace disque insuffisant +#define TXT_MUST_HAVE_SPACE 344 // Vous devez disposer de %d +#define TXT_RUN_SETUP 345 // Lancez d'abord le programme +#define TXT_WAITING_FOR_OPPONENT 346 // Attente adversaire +#define TXT_SELECT_SETTINGS 347 // Choisissez l'option +#define TXT_PRISON 348 // Prison +#define TXT_GAME_WAS_SAVED 349 // Mission sauvegard‚e +#define TXT_SPACE_CANT_SAVE 350 // Espace disque insuffisant +#define TXT_INVALID_PORT_ADDRESS 351 // Port/Adresse invalide. COM +#define TXT_INVALID_SETTINGS 352 // paramŠtres Port et/ou IRQ +#define TXT_IRQ_ALREADY_IN_USE 353 // IRQ d‚j… utilis‚ +#define TXT_ABORT 354 // Oui +#define TXT_RESTART 355 // Recommencer +#define TXT_RESTARTING 356 // Mission relanc‚e. Attendez +#define TXT_LOADING 357 // Chargement de la mission. +#define TXT_ERROR_IN_INITSTRING 358 // Erreur chaŒne +#define TXT_SHADOW_COLON 359 // Ombre +#define TXT_AVMINE 360 // Mine Anti-V‚hicule +#define TXT_APMINE 361 // Mine Anti-Personnel +#define TXT_NEW_MISSIONS 362 // Nouvelles missions +#define TXT_THIEF 363 // Voleur +#define TXT_MRJ 364 // Brouilleur de radar +#define TXT_GAP_GENERATOR 365 // G‚n‚rateur d'ombre +#define TXT_PILLBOX 366 // Bunker +#define TXT_CAMOPILLBOX 367 // Bunker camoufl‚ +#define TXT_CHRONOSPHERE 368 // ChronosphŠre +#define TXT_ENGLAND 369 // Roy. Uni +#define TXT_GERMANY 370 // Allemagne +#define TXT_SPAIN 371 // Espagne +#define TXT_USSR 372 // URSS +#define TXT_UKRAINE 373 // Ukraine +#define TXT_GREECE 374 // GrŠce +#define TXT_FRANCE 375 // France +#define TXT_TURKEY 376 // Turquie +#define TXT_SHORE 377 // Rivage +#define TXT_PLACE_OBJECT 378 // Choisir objet +#define TXT_SS 379 // Sous-marin +#define TXT_DD 380 // Contre-torpilleur +#define TXT_CA 381 // Croiseur +#define TXT_TRANSPORT 382 // Transport +#define TXT_PT 383 // Aviso-torpilleur +#define TXT_LOBBY 384 // Hall +#define TXT_CHANNEL_GAMES 385 // Parties +#define TXT_SAVING_GAME 386 // Sauvegarder partie... +#define TXT_GAME_FULL 387 // La partie est au complet. +#define TXT_MUST_SELECT_GAME 388 // Vous devez s‚lectionner une +#define TXT_S_PLAYING_S 389 // %s joue contre %s +#define TXT_ONLY_HOST_CAN_MODIFY 390 // Seul l'h“te peut modifier +#define TXT_GAME_CANCELLED 391 // La partie a ‚t‚ annul‚e. +#define TXT_S_FORMED_NEW_GAME 392 // %s a initi‚ une nouvelle +#define TXT_GAME_NOW_IN_PROGRESS 393 // La partie de %s est +#define TXT_TESLA 394 // Bobine de Tesla +#define TXT_MGG 395 // G‚n‚rateur d'ombre mobile +#define TXT_FLAME_TURRET 396 // Tour lance-flammes +#define TXT_AAGUN 397 // Canon Anti-Avion +#define TXT_KENNEL 398 // Niche +#define TXT_SOVIET_TECH 399 // Centre Technique +#define TXT_BADGER 400 // Bombardier +#define TXT_MIG 401 // Mig +#define TXT_YAK 402 // Yak +#define TXT_FENCE 403 // Barbel‚s +#define TXT_MEDIC 404 // M‚decin +#define TXT_SABOTEUR 405 // Saboteur +#define TXT_GENERAL 406 // G‚n‚ral +#define TXT_E7 407 // Tanya +#define TXT_PARA_BOMB 408 // Parabombes +#define TXT_PARA_INFANTRY 409 // Parachutistes +#define TXT_PARA_SABOTEUR 410 // Saboteur parachutiste +#define TXT_SHIP_YARD 411 // Chantier naval +#define TXT_SUB_PEN 412 // Port sous-marin +#define TXT_SCENARIO_OPTIONS 413 // Options Sc‚nario +#define TXT_SPY_MISSION 414 // Avion espion +#define TXT_U2 415 // Avion espion +#define TXT_GUARD_DOG 416 // Chien d'attaque +#define TXT_SPY_INFO 417 // Info Espion +#define TXT_BUILDNGS 418 // Bƒtiments +#define TXT_UNITS 419 // Unit‚s +#define TXT_INFANTRY 420 // Infanterie +#define TXT_AIRCRAFT 421 // Avion +#define TXT_TRUCK 422 // Camion d'approvisionnement +#define TXT_INVUL 423 // Module d'invuln‚rabilit‚ +#define TXT_IRON_CURTAIN 424 // Rideau de Fer +#define TXT_ADVANCED_TECH 425 // Centre technique avanc‚ +#define TXT_V2_LAUNCHER 426 // Lance-roquettes V2 +#define TXT_FORWARD_COM 427 // Poste de commandement +#define TXT_DEMOLITIONER 428 // Bombardeur +#define TXT_MINE_LAYER 429 // Poseur de mines +#define TXT_FAKE_CONST 430 // Chantier de construction +#define TXT_FAKE_WEAP 431 // Usine d'armement leurre +#define TXT_FAKE_YARD 432 // Chantier naval leurre +#define TXT_FAKE_PEN 433 // Port sous-marin leurre +#define TXT_FAKE_RADAR 434 // D“me radar leurre +#define TXT_THEME_BIGF 435 // Bigfoot +#define TXT_THEME_CRUS 436 // La r‚volte +#define TXT_THEME_FAC1 437 // A l'attaque 1 +#define TXT_THEME_FAC2 438 // A l'attaque 2 +#define TXT_THEME_HELL 439 // Marche de l'enfer +#define TXT_THEME_RUN1 440 // Sauve-qui-peut +#define TXT_THEME_SMSH 441 // La d‚bƒcle +#define TXT_THEME_TREN 442 // Tranch‚es +#define TXT_THEME_WORK 443 // Les professionnels +#define TXT_THEME_AWAIT 444 // Attente +#define TXT_THEME_DENSE_R 445 // Dense +#define TXT_THEME_MAP 446 // S‚lection carte +#define TXT_THEME_FOGGER1A 447 // Fogger +#define TXT_THEME_MUD1A 448 // Boue +#define TXT_THEME_RADIO2 449 // Radio 2 +#define TXT_THEME_ROLLOUT 450 // Laminage +#define TXT_THEME_SNAKE 451 // Serpent +#define TXT_THEME_TERMINAT 452 // Extermination +#define TXT_THEME_TWIN 453 // Jumeau +#define TXT_THEME_VECTOR1A 454 // Vecteur +#define TXT_TEAM_MEMBERS 455 // Equipiers +#define TXT_BRIDGE 456 // Pont +#define TXT_BARREL 457 // Baril +#define TXT_GOODGUY 458 // Amical +#define TXT_BADGUY 459 // Ennemi +#define TXT_GOLD 460 // Or +#define TXT_GEMS 461 // Gemmes +#define TXT_TEASER 462 // Film titre +#define TXT_MOVIES 463 // Films +#define TXT_INTERIOR 464 // Int‚rieur +#define TXT_SONAR_PULSE 465 // Signal sonar +#define TXT_MSLO 466 // Silo de missiles +#define TXT_GPS_SATELLITE 467 // Satellite GPS +#define TXT_NUCLEAR_BOMB 468 // Bombe atomique +#define TXT_EASY 469 // Facile +#define TXT_HARD 470 // Difficile +#define TXT_NORMAL 471 // Normal +#define TXT_DIFFICULTY 472 // S‚lectionnez un niveau de +#define TXT_ALLIES 473 // Alli‚s +#define TXT_SOVIET 474 // Soviets +#define TXT_THEME_INTRO 475 // ThŠme Intro +#define TXT_SHADOW_REGROWS 476 // Progr. ombre +#define TXT_ORE_SPREADS 477 // Progr. minerai +#define TXT_THEME_SCORE 478 // Musiques +#define TXT_INTERNET 479 // Internet +#define TXT_ICE 480 // Glace +#define TXT_CRATE 481 // Caisses +#define TXT_SKIRMISH 482 // Escarmouche +#define TXT_CHOOSE 483 // Choisissez votre camp. +#define TXT_MINERALS 484 // Min‚raux pr‚cieux +#define TXT_IGNORE 485 // Ignorer +#define TXT_ERROR_NO_RESP 486 // Erreur - le modem ne r‚pond +#define TXT_ERROR_NO_RESCODE 487 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_INIT 488 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_VERB 489 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_ECHO 490 // Erreur - Le modem n'a pas +#define TXT_ERROR_NO_DISABLE 491 // Erreur - Impossible de +#define TXT_ERROR_TOO_MANY 492 // Erreur - Trop d'erreurs +#define TXT_ERROR_ERROR 493 // Erreur - Le modem a +#define TXT_ERROR_TIMEOUT 494 // Erreur - Temps d'attente de +#define TXT_ACCOMPLISHED 495 // Accompli +#define TXT_CLICK_CONTINUE 496 // Cliquez pour continuer +#define TXT_RECEIVING_SCENARIO 497 // R‚ception du sc‚nario de +#define TXT_SENDING_SCENARIO 498 // Envoi du sc‚nario aux +#define TXT_NO_FLOW_CONTROL_RESPONSE 499 // Erreur - Le modem n'a pas +#define TXT_NO_COMPRESSION_RESPONSE 500 // Erreur - Le modem n'a pas +#define TXT_NO_ERROR_CORRECTION_RESPONSE 501 // Erreur - Le modem n'a pas +#define TXT_EXPLAIN_REGISTRATION 502 // Pour jouer … Alerte Rouge +#define TXT_ERROR_UNABLE_TO_RUN_WCHAT 503 // Erreur - Impossible +#define TXT_REGISTER 504 // Enregistrer +#define TXT_ORE_MINE 505 // Gisement Minerai +#define TXT_NO_REGISTERED_MODEM 506 // Aucun modem configur‚ +#define TXT_CHRONOSHIFT 507 // D‚placement chronoporte +#define TXT_UNABLE_TO_OPEN_PORT 508 // Adresse invalide ou en +#define TXT_NO_DIAL_TONE 509 // Pas de tonalit‚. +#define TXT_NO_EXPANSION_SCENARIO 510 // Erreur - L'autre joueur n'a +#define TXT_STAND_BY 511 // Patientez SVP... +#define TXT_THEME_CREDITS 512 // Musique du g‚n‚rique de fin +#define TXT_POWER_AAGUN 513 // Puissance faible: Canon(s) +#define TXT_POWER_TESLA 514 // Puissance faible: Bobine(s) +#define TXT_LOW_POWER 515 // Puissance Faible +#define TXT_COMMANDER 516 // Commandant: +#define TXT_BATTLES_WON 517 // Parties gagn‚es: +#define TXT_MISMATCH 518 // Fichier de donn‚es du jeu +#define TXT_SCENARIO_ERROR 519 // Votre version de jeu +#define TXT_CONNECTING 520 // Connecting +#define TXT_MODEM_INITIALISATION 521 // Initialisation du Modem +#define TXT_DATA_COMPRESSION 522 // Compression des Donn‚es +#define TXT_ERROR_CORRECTION 523 // Correction d'Erreur +#define TXT_HARDWARE_FLOW_CONTROL 524 // Contr“le Mat‚riel du Flux +#define TXT_ADVANCED 525 // Avanc‚ +#define TXT_THEME_2ND_HAND 526 // Seconde main +#define TXT_THEME_ARAZOID 527 // Arazo‹de +#define TXT_THEME_BACKSTAB 528 // Retour … l'envoyeur +#define TXT_THEME_CHAOS2 529 // Chaos2 +#define TXT_THEME_SHUT_IT 530 // Fermez-la ! +#define TXT_THEME_TWINMIX1 531 // Visite de courtoisie +#define TXT_THEME_UNDER3 532 // A couvert +#define TXT_THEME_VR2 533 // VR2 +#define TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING 534 // L'autre systŠme ne r‚pond +#define TXT_ASK_EMERGENCY_SAVE_HUNG_UP 535 // L'autre systŠme a +#define TXT_NO_REG_APP 536 // Alerte Rouge n'a pu +#define TXT_NO_CS_SCENARIOS 537 // Un joueur de la partie n'a +#define TXT_MISSILESUB 538 // Sous-marin MS +#define TXT_SHOCKTROOPER 539 // Electrocuteur +#define TXT_MECHANIC 540 // M‚canicien +#define TXT_CHRONOTANK 541 // Chrono Tank +#define TXT_TESLATANK 542 // Tank Tesla +#define TXT_MAD 543 // Tank M.A.D. +#define TXT_DEMOTRUCK 544 // Camion de d‚molition +#define TXT_PHASETRANSPORT 545 // Transport Cam‚l‚on +#define TXT_THEME_BOG 546 // Mar‚cages +#define TXT_THEME_FLOAT_V2 547 // Volutes +#define TXT_THEME_GLOOM 548 // T‚nŠbres +#define TXT_THEME_GRNDWIRE 549 // Terrain min‚ +#define TXT_THEME_RPT 550 // M‚caniciens 2 +#define TXT_THEME_SEARCH 551 // Battue +#define TXT_THEME_TRACTION 552 // Traction +#define TXT_THEME_WASTELND 553 // Chaos +#define TXT_CARRIER 554 // H‚liport Mobile +#define TXT_INSUFFICIENT_FUNDS 555 \ No newline at end of file diff --git a/code/redalert/console.cpp b/code/redalert/console.cpp new file mode 100644 index 0000000..a246994 --- /dev/null +++ b/code/redalert/console.cpp @@ -0,0 +1,305 @@ +// CONSOLE.CPP +// + +#include "imgui.h" +#include "FUNCTION.H" + +char console_text[16384]; +int console_text_len = 0; +char command_text[512]; + +#define MAX_ARGS 80 + +static int cmd_argc; +static char* cmd_argv[MAX_ARGS]; +static char* cmd_null_string = ""; +static char* cmd_args = NULL; + +char com_token[1024]; +int com_argc; +char** com_argv; + +typedef struct cmd_function_s +{ + struct cmd_function_s* next; + char* name; + xcommand_t function; +} cmd_function_t; + +static bool executeConsoleCommand = false; + +static cmd_function_t* cmd_functions; // possible commands to execute + +/* +============ +Cmd_AddCommand +============ +*/ +void Cmd_AddCommand(char* cmd_name, xcommand_t function) +{ + cmd_function_t* cmd; + + // fail if the command already exists + for (cmd = cmd_functions; cmd; cmd = cmd->next) + { + if (!strcmp(cmd_name, cmd->name)) + { + Console_Printf("Cmd_AddCommand: %s already defined\n", cmd_name); + return; + } + } + + cmd = (cmd_function_t *)malloc(sizeof(cmd_function_t)); + cmd->name = cmd_name; + cmd->function = function; + cmd->next = cmd_functions; + cmd_functions = cmd; +} + + +/* +============== +Cmd_Argc +============== +*/ +int Cmd_Argc() { + return cmd_argc; +} + +/* +============== +Cmd_Argv +============== +*/ +char* Cmd_Argv(int index) { + return cmd_argv[index]; +} + +/* +============== +COM_Parse + +Parse a token out of a string +============== +*/ +char* COM_Parse(char* data) +{ + int c; + int len; + + len = 0; + com_token[0] = 0; + + if (!data) + return NULL; + + // skip whitespace +skipwhite: + while ((c = *data) <= ' ') + { + if (c == 0) + return NULL; // end of file; + data++; + } + + // skip // comments + if (c == '/' && data[1] == '/') + { + while (*data && *data != '\n') + data++; + goto skipwhite; + } + + + // handle quoted strings specially + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c == '\"' || !c) + { + com_token[len] = 0; + return data; + } + com_token[len] = c; + len++; + } + } + + // parse single characters + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') + { + com_token[len] = c; + len++; + com_token[len] = 0; + return data + 1; + } + + // parse a regular word + do + { + com_token[len] = c; + data++; + len++; + c = *data; + if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') + break; + } while (c > 32); + + com_token[len] = 0; + return data; +} + + +/* +============ +Cmd_TokenizeString + +Parses the given string into command line tokens. +============ +*/ +void Cmd_TokenizeString(char* text) +{ + int i; + + // clear the args from the last string + for (i = 0; i < cmd_argc; i++) + free(cmd_argv[i]); + + cmd_argc = 0; + cmd_args = NULL; + + while (1) + { + // skip whitespace up to a /n + while (*text && *text <= ' ' && *text != '\n') + { + text++; + } + + if (*text == '\n') + { // a newline seperates commands in the buffer + text++; + break; + } + + if (!*text) + return; + + if (cmd_argc == 1) + cmd_args = text; + + text = COM_Parse(text); + if (!text) + return; + + if (cmd_argc < MAX_ARGS) + { + cmd_argv[cmd_argc] = (char *)malloc(strlen(com_token) + 1); + strcpy(cmd_argv[cmd_argc], com_token); + cmd_argc++; + } + } + +} + +/* +==================== +Console_Init +==================== +*/ +void Console_Init(void) { + memset(command_text, 0, sizeof(command_text)); + memset(console_text, 0, sizeof(console_text)); + console_text_len = 0; +} + +/* +==================== +Console_Render +==================== +*/ +void Console_Printf(const char* fmt, ...) { + va_list argptr; + char msg[4096]; + + va_start(argptr, fmt); + vsprintf(msg, fmt, argptr); + va_end(argptr); + + //int len = strlen(msg); + //strcpy(&console_text[console_text_len], msg); + + OutputDebugStringA(msg); + //console_text_len += len; +} + +/* +==================== +ExecuteConsoleCommand +==================== +*/ +void ExecuteConsoleCommand(void) { + cmd_function_t* cmd; + + if (strlen(command_text) <= 0) + return; + + Console_Printf("%s\n", command_text); + Cmd_TokenizeString(command_text); + + memset(command_text, 0, sizeof(command_text)); + + for (cmd = cmd_functions; cmd; cmd = cmd->next) + { + if (!strcmp(Cmd_Argv(0), cmd->name)) + { + cmd->function(); + return; + } + } + + Console_Printf("Unknown command %s\n", Cmd_Argv(0)); +} + +/* +==================== +Console_Render +==================== +*/ +void Console_Render(void) { + bool ScreenActive; + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(420, 270)); + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::Begin("Developer Console", &ScreenActive, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); + ImVec2 textres(420, 180); + ImGui::PushID("ConsoleTxt"); + ImGui::InputTextMultiline("", &console_text[0], sizeof(console_text), textres, ImGuiInputTextFlags_ReadOnly); + ImGui::PopID(); + ImGui::Text("Commands"); + ImGui::PushID("CmdTxt"); + ImGui::InputText("", &command_text[0], sizeof(command_text)); + ImGui::PopID(); + ImGui::SameLine(); + if (ImGui::Button("Execute")) { + executeConsoleCommand = true; + } + ImGui::End(); +} + +/* +================= +Console_Tick +================= +*/ +void Console_Tick(void) { + if (executeConsoleCommand) { + ExecuteConsoleCommand(); + executeConsoleCommand = false; + } +} \ No newline at end of file diff --git a/code/redalert/const.cpp b/code/redalert/const.cpp new file mode 100644 index 0000000..eb2efcc --- /dev/null +++ b/code/redalert/const.cpp @@ -0,0 +1,845 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONST.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 20, 1993 * + * * + * Last Update : September 20, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the access passwords used to activate cheat mode, editor mode, +** and special game options. +*/ +unsigned long const PlayCodes[] = { + 0xE0792D6D, // Dwight Okahara + 0x90046ECF, // Paul S. Mudra + 0xC3EE9A26, // Frank Klepaki + 0xED382178, // Ed Del Castillo + 0L +}; + +unsigned long const CheatCodes[] = { + 0xA0E2AB53, // Joseph Hewitt + 0x00532693, // Mike Lightner + 0x7DDFF824, // Joe Bostic + 0x2CB5CF01, // Phil Gorrow + 0xB5B63531, // Bill Randolph + 0xDFABC23A, // Adam Isgreen + 0x52B19A22, // Erik Yeo + 0xBE79088C, // David Dettmer + 0xB216AE7E, // Barry Green + 0x0E07B213, // Steve Tall + 0L +}; + + +unsigned long const EditorCodes[] = { + 0xA2C09326, // Erik Yeo + 0x1F944BB3, // Mike Lightner + 0xDE07154D, // Adam Isgreen + 0x0E07B213, // Steve Tall + 0x16B170B1, // Joe Bostic + 0L +}; + + +/*********************************************************************************************** +** Unit order names. These names correspond to the player selectable orders +** a unit can have. The system initiated orders have no use for the ASCII name +** associated, but they are listed here for completeness sake. +*/ +char const * Missions[MISSION_COUNT] = { + "Sleep", + "Attack", + "Move", + "QMove", + "Retreat", + "Guard", + "Sticky", + "Enter", + "Capture", + "Harvest", + "Area Guard", + "Return", + "Stop", + "Ambush", + "Hunt", + "Unload", + "Sabotage", + "Construction", + "Selling", + "Repair", + "Rescue", + "Missile", + "Harmless" +}; + + +/*************************************************************************** +** Special weapon names. +*/ +#ifdef SCENARIO_EDITOR +char const * const SpecialWeaponName[SPC_COUNT] = { + "Sonar Pulse", + "Nuclear Missile", + "Chronosphere", + "Parachute Bomb", + "Paratroopers", + "Recon Plane", + "Iron Curtain", + "GPS Satellite" +}; +#endif +int const SpecialWeaponHelp[SPC_COUNT] = { + TXT_SONAR_PULSE, + TXT_NUCLEAR_BOMB, + TXT_CHRONOSHIFT, + TXT_PARA_BOMB, + TXT_PARA_INFANTRY, + TXT_SPY_MISSION, + TXT_INVUL, + TXT_GPS_SATELLITE +}; +char const * const SpecialWeaponFile[SPC_COUNT] = { + "SONR", + "ATOM", + "WARP", + "PBMB", + "PINF", + "CAM", + "INFX", + "GPSS" +}; + + +/*************************************************************************** +** Type of quarry to search out and attack. These values are used for team +** attack missions. +*/ +char const * const QuarryName[QUARRY_COUNT] = { + "N/A", + "Anything", + "Buildings - any", + "Harvesters", + "Infantry", + "Vehicles - any", + "Ships - any", + "Factories", + "Base Defenses", + "Base Threats", + "Power Facilities", + "Fake Buildings" +}; + + +/*************************************************************************** +** These are the text names for the formation types. +*/ +char const * const FormationName[FORMATION_COUNT] = { + "None", + + "Tight", + "Loose", + "Wedge North", + "Wedge East", + "Wedge South", + "Wedge West", + "Line N/S", + "Line E/W" +}; + + +/*************************************************************************** +** These are the ASCII names for the reinforcement sources. +*/ +char const * const SourceName[SOURCE_COUNT] = +{ + "North", + "East", + "South", + "West", + "Air" +}; + + +/*************************************************************************** +** These are the text names for the various armor types a unit may possess. +*/ +char const * const ArmorName[ARMOR_COUNT] = { + "none", + "wood", + "light", + "heavy", + "concrete" +}; + + +// HACK ALERT! This unused text string is here to stop Watcom from crashing. There is some +// magic text heap length that causes a crash before the code executes. This dummy string +// changes the text heap length enough to stop the crash. Who knows why, but it works. +char * __test__ = "alskdfjlasdfjkajsdfkja;sldjfklasj9awutreqjfnfdkvnldzlknvadsjgflkasdjfkajsdfas"; + + +/*************************************************************************** +** The list of VQ filenames. +*/ +char const * const VQName[VQ_COUNT] = { + "AAGUN", + "MIG", + "SFROZEN", + "AIRFIELD", + "BATTLE", + "BMAP", + "BOMBRUN", + "DPTHCHRG", + "GRVESTNE", + "MONTPASS", + "MTNKFACT", + "CRONTEST", + "OILDRUM", + "ALLIEND", + "RADRRAID", + "SHIPYARD", // MISSING + "SHORBOMB", + "SITDUCK", + "SLNTSRVC", + "SNOWBASE", + "EXECUTE", + "REDINTRO", // low res. + "NUKESTOK", + "V2ROCKET", + "SEARCH", + "BINOC", + "ELEVATOR", + "FROZEN", + "MCV", + "SHIPSINK", + "SOVMCV", + "TRINITY", + "ALLYMORF", + "APCESCPE", + "BRDGTILT", + "CRONFAIL", + "STRAFE", + "DESTROYR", + "DOUBLE", + "FLARE", + "SNSTRAFE", + "LANDING", + "ONTHPRWL", + "OVERRUN", + "SNOWBOMB", + "SOVCEMET", + "TAKE_OFF", + "TESLA", + "SOVIET8", + "SPOTTER", + "ALLY1", + "ALLY2", + "ALLY4", + "SOVFINAL", + "ASSESS", + "SOVIET10", + "DUD", + "MCV_LAND", + "MCVBRDGE", + "PERISCOP", + "SHORBOM1", + "SHORBOM2", + "SOVBATL", + "SOVTSTAR", + "AFTRMATH", + "SOVIET11", + "MASASSLT", + "REDINTRO", // High res. Intro + "SOVIET1", + "SOVIET2", + "SOVIET3", + "SOVIET4", + "SOVIET5", + "SOVIET6", + "SOVIET7", + "PROLOG", + "AVERTED", + "COUNTDWN", + "MOVINGIN", + "ALLY10", + "ALLY12", + "ALLY5", + "ALLY6", + "ALLY8", + "TANYA1", + "TANYA2", + "ALLY10B", + "ALLY11", + "ALLY14", + "ALLY9", + "SPY", + "TOOFAR", + "SOVIET12", + "SOVIET13", + "SOVIET9", + "BEACHEAD", + "SOVIET14", + "SIZZLE", //MISSING + "SIZZLE2", //MISSING + "ANTEND", + "ANTINTRO", + + + //2019/11/12 JAS - Added for Retaliation movies + + "RETALIATION_ALLIED1", + "RETALIATION_ALLIED2", + "RETALIATION_ALLIED3", + "RETALIATION_ALLIED4", + "RETALIATION_ALLIED5", + "RETALIATION_ALLIED6", + "RETALIATION_ALLIED7", + "RETALIATION_ALLIED8", + "RETALIATION_ALLIED9", + "RETALIATION_ALLIED10", + + "RETALIATION_SOVIET1", + "RETALIATION_SOVIET2", + "RETALIATION_SOVIET3", + "RETALIATION_SOVIET4", + "RETALIATION_SOVIET5", + "RETALIATION_SOVIET6", + "RETALIATION_SOVIET7", + "RETALIATION_SOVIET8", + "RETALIATION_SOVIET9", + "RETALIATION_SOVIET10", + "RETALIATION_WINA", + "RETALIATION_WINS", + "RETALIATION_ANTS" +}; + + +/*************************************************************************** +** Relative coordinate offsets from the center of a cell for each +** of the legal positions that an object in a cell may stop at. Only infantry +** are allowed to stop at other than the center of the cell. +*/ +COORDINATE const StoppingCoordAbs[5] = { + 0x00800080L, // center + 0x00400040L, // upper left + 0x004000C0L, // upper right + 0x00C00040L, // lower left + 0x00C000C0L // lower right +}; + + +/*************************************************************************** +** Converts pixel values (cell relative) into the appropriate lepton (sub cell) +** value. This is used to convert pixel (screen) coordinates into the underlying +** coordinate system. +*/ +unsigned char const Pixel2Lepton[24] = { + 0x00,0x0B,0x15,0x20,0x2B,0x35,0x40,0x4B, + 0x55,0x60,0x6B,0x75,0x80,0x8B,0x95,0xA0, + 0xAB,0xB5,0xC0,0xCB,0xD5,0xE0,0xEB,0xF5 +}; + + +/*************************************************************************** +** This array is used to index a facing in order to retrieve a cell +** offset that, when added to another cell, will achieve the adjacent cell +** in the indexed direction. +*/ +CELL const AdjacentCell[FACING_COUNT] = { + -(MAP_CELL_W), // North + -(MAP_CELL_W-1), // North East + 1, // East + MAP_CELL_W+1, // South East + MAP_CELL_W, // South + MAP_CELL_W-1, // South West + -1, // West + -(MAP_CELL_W+1) // North West +}; + +COORDINATE const AdjacentCoord[FACING_COUNT] = { + 0xFF000000L, + 0xFF000100L, + 0x00000100L, + 0x01000100L, + 0x01000000L, + 0x0100FF00L, + 0x0000FF00L, + 0xFF00FF00L +}; + + +/*************************************************************************** +** This specifies the odds of receiving the various random crate power +** ups. The odds are expressed as "shares" of 100 percent. +*/ +int CrateShares[CRATE_COUNT] = { + 50, // CRATE_MONEY + 20, // CRATE_UNIT + 3, // CRATE_PARA_BOMB + 1, // CRATE_HEAL_BASE + 3, // CRATE_CLOAK + 5, // CRATE_EXPLOSION + 5, // CRATE_NAPALM + 20, // CRATE_SQUAD + 1, // CRATE_DARKNESS + 1, // CRATE_REVEAL + 3, // CRATE_SONAR + 10, // CRATE_ARMOR + 10, // CRATE_SPEED + 10, // CRATE_FIREPOWER + 1, // CRATE_ICBM + 1, // CRATE_TIMEQUAKE + 3, // CRATE_INVULN + 5 // CRATE_VORTEX +}; + +AnimType CrateAnims[CRATE_COUNT] = { + ANIM_NONE, // CRATE_MONEY + ANIM_NONE, // CRATE_UNIT + ANIM_NONE, // CRATE_PARA_BOMB + ANIM_NONE, // CRATE_HEAL_BASE + ANIM_NONE, // CRATE_CLOAK + ANIM_NONE, // CRATE_EXPLOSION + ANIM_NONE, // CRATE_NAPALM + ANIM_NONE, // CRATE_SQUAD + ANIM_NONE, // CRATE_DARKNESS + ANIM_NONE, // CRATE_REVEAL + ANIM_NONE, // CRATE_SONAR + ANIM_NONE, // CRATE_ARMOR + ANIM_NONE, // CRATE_SPEED + ANIM_NONE, // CRATE_FIREPOWER + ANIM_NONE, // CRATE_ICBM + ANIM_NONE, // CRATE_TIMEQUAKE + ANIM_NONE, // CRATE_INVULN + ANIM_NONE // CRATE_VORTEX +}; + +int CrateData[CRATE_COUNT] = { + 0, // CRATE_MONEY + 0, // CRATE_UNIT + 0, // CRATE_PARA_BOMB + 0, // CRATE_HEAL_BASE + 0, // CRATE_CLOAK + 0, // CRATE_EXPLOSION + 0, // CRATE_NAPALM + 0, // CRATE_SQUAD + 0, // CRATE_DARKNESS + 0, // CRATE_REVEAL + 0, // CRATE_SONAR + 0, // CRATE_ARMOR + 0, // CRATE_SPEED + 0, // CRATE_FIREPOWER + 0, // CRATE_ICBM + 0, // CRATE_TIMEQUAKE + 0, // CRATE_INVULN + 0 // CRATE_VORTEX +}; + +char const * const CrateNames[CRATE_COUNT] = { + "Money", + "Unit", + "ParaBomb", + "HealBase", + "Cloak", + "Explosion", + "Napalm", + "Squad", + "Darkness", + "Reveal", + "Sonar", + "Armor", + "Speed", + "Firepower", + "ICBM", + "TimeQuake", + "Invulnerability", + "ChronalVortex" +}; + + +/*************************************************************************** +** This converts 0..255 facing values into either 8, 16, or 32 facing values. +** Note: a simple shift won't suffice because 0..255 facing values should +** be converted to the CLOSEST appropriate facing, NOT rounded down to the +** nearest facing. +*/ +unsigned char const Facing8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + + +unsigned char const Facing16[256] = { + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2, + 2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4, + 4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,0,0,0,0,0,0,0,0 +}; + + +signed char const Rotation16[256] = { + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1, + 0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1 +}; + + +/* +** This table incorporates a compensating factor for the distortion caused +** by 3D-Studio when it tries to render 45% angles. +*/ +unsigned char const Facing32[256] = { + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3, + 3,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8, + 8,8,8,9,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16, + 16,16,16,16,16,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19, + 19,20,20,20,20,20,20,21,21,21,21,21,21,21,22,22,22,22,22,22,22,23,23,23,23,23,23,23,24,24,24,24, + 24,24,24,25,25,25,25,25,25,25,26,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,0,0,0,0,0,0 +}; + + +#ifdef OBSOLETE +unsigned char const Facing32[256] = { + 0,0,0,0, + 1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8, + 9,9,9,9,9,9,9,9, + 10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11, + 12,12,12,12,12,12,12,12, + 13,13,13,13,13,13,13,13, + 14,14,14,14,14,14,14,14, + 15,15,15,15,15,15,15,15, + 16,16,16,16,16,16,16,16, + 17,17,17,17,17,17,17,17, + 18,18,18,18,18,18,18,18, + 19,19,19,19,19,19,19,19, + 20,20,20,20,20,20,20,20, + 21,21,21,21,21,21,21,21, + 22,22,22,22,22,22,22,22, + 23,23,23,23,23,23,23,23, + 24,24,24,24,24,24,24,24, + 25,25,25,25,25,25,25,25, + 26,26,26,26,26,26,26,26, + 27,27,27,27,27,27,27,27, + 28,28,28,28,28,28,28,28, + 29,29,29,29,29,29,29,29, + 30,30,30,30,30,30,30,30, + 31,31,31,31,31,31,31,31, + 0,0,0,0 +}; +#endif + + +/*************************************************************************** +** These are the movement costs (in ticks at fastest speed) to enter each +** of the given terrain cells. +*/ + +int const GroundColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + 141, // "Clear" terrain. + 141, // Road terrain. + 172, // Water. + 21, // Impassable rock. + 21, // Wall (blocks movement). + 158, // Tiberium field. + 141, // Beach terrain. + 141, // Rocky terrain. + 174 // Rocky riverbed. +}; + +#ifdef NEVER +int const GroundColor[LAND_COUNT] = { + 46, // "Clear" terrain. + 44, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + 64, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; + +int const SnowColor[LAND_COUNT] = { + WHITE, // "Clear" terrain. + LTGRAY, // Road terrain. + BLUE, // Water. + DKGREY, // Impassable rock. + DKGREY, // Wall (blocks movement). + 158, // Tiberium field. + LTGRAY, // Beach terrain. + DKGREY, // Rocky terrain. + DKGREY // Rocky riverbed. +}; +#endif + +GroundType Ground[LAND_COUNT]; + + +/*************************************************************************** +** These are the names of the theaters. +*/ +TheaterDataType const Theaters[THEATER_COUNT] = { + {"TEMPERATE","TEMPERAT","TEM"}, + {"SNOW","SNOW","SNO"}, + {"INTERIOR","INTERIOR","INT"}, +}; + + +unsigned char const RemapCiv2[256] = { + 0,1,2,3,4,5,6,209,8,9,10,11,12,13,12,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,187,188,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,209, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,167, 13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv4[256] = { + 0,1,2,3,4,5,6,187,8,9,10,11,12,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,118,110,119, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,188,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv5[256] = { + 0,1,2,3,4,5,6,109,8,9,10,11,131,13,14,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,177,110,178, // 96..111 + 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,111,207, // 192..207 + 208,209,182,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv6[256] = { + 0,1,2,3,4,5,6,120,8,9,10,11,12,13,238,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,236,206,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,111, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv7[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,157,212,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,7, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,118,119,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv8[256] = { + 0,1,2,3,4,5,6,182,8,9,10,11,12,13,131,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,215,7,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,182, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,198,199,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,111,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv9[256] = { + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,7,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,163,165,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,200, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,111,13,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapCiv10[256] = { + 0,1,2,3,4,5,6,137,8,9,10,11,12,13,15,15, // 0..15 + 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, // 16..31 + 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, // 32..47 + 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63, // 48..63 + 64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79, // 64..79 + 80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95, // 80..95 + 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111, // 96..111 + 112,113,114,115,116,117,129,131,120,121,122,123,124,125,126,127, // 112..127 + 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, // 128..143 + 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,137, // 144..159 + 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175, // 160..175 + 176,177,178,179,180,181,182,183,184,185,186,163,165,189,190,191, // 176..191 + 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207, // 192..207 + 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, // 208..223 + 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, // 224..239 + 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 // 240..255 +}; + +unsigned char const RemapEmber[256] = { +#define CEC CC_EMBER_COLOR + 0,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,BLACK,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC, + CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC,CEC +}; + + +//char const Keys[] = +// "[PublicKey]\n" +// "1=AgkCbXo9sKMHOBk=\n" +//#ifdef CHEAT_KEYS +// "[PrivateKey]\n" +// "1=AggxFU55vc7LYQ==\n" +//#endif +// "\n"; + +char const Keys[] = +"[PublicKey]\n" +"1=AihRvNoIbTn85FZRYNZRcT+i6KpU+maCsEqr3Q5q+LDB5tH7Tz2qQ38V\n" +#ifdef CHEAT_KEYS +"[PrivateKey]\n" +"1=AigKVje8mROcR8QixnxUEF5b29Curkq01DNDWCdOG99XBqH79OaCiTCB\n" +#endif +"\n"; diff --git a/code/redalert/control.cpp b/code/redalert/control.cpp new file mode 100644 index 0000000..1279eb0 --- /dev/null +++ b/code/redalert/control.cpp @@ -0,0 +1,223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONTROL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : December 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ControlClass::Action -- Normal action for control gadget objects. * + * ControlClass::ControlClass -- Constructor for control class objects. * + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Constructor for control class objects. * + * * + * This is the normal constructor for control class objects. At this level, it only needs * + * to record the ID number assigned to this button. * + * * + * INPUT: id -- The ID number for this gadget. If the ID number specified is 0, then * + * this tells the system that no special ID code should be returned. * + * * + * x,y -- Pixel coordinate of upper left corner of gadget's region. * + * * + * w,h -- Pixel dimensions of the gadget's region. * + * * + * flags -- The input event flags that this gadget recognizes. * + * * + * sticky-- This this a "sticky" gadget? A sticky gadget is one that takes over the * + * gadget list while the mouse button is held down, if the mouse button was * + * initially clicked over its region. This is the behavior of "normal" * + * buttons in Windows. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags, int sticky) : + GadgetClass(x, y, w, h, flags, sticky), + ID(id), + Peer(0) +{ +} + + +/*********************************************************************************************** + * ControlClass::ControlClass -- Copy constructor for control gadget. * + * * + * This copy constructor for a control gadget is used create a duplicate gadget that * + * is functionally similar. * + * * + * INPUT: control -- Reference to the gadget that is to be copied. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/05/1995 JLB : Created. * + *=============================================================================================*/ +ControlClass::ControlClass(ControlClass const & control) : + GadgetClass(control), + ID(control.ID), + Peer(control.Peer) +{ +} + +/*********************************************************************************************** + * ControlClass::Action -- Normal action for control gadget objects. * + * * + * This function gets called when the input event that this control gadget is looking for * + * occurs. In such a case, the return key code value is changed to the gadget's ID number * + * with the special button bit flag attached. * + * * + * INPUT: flags -- The event that triggered this function call. If this value is NULL, then * + * this is a forced (probably due to the sticky flag) call and the key code * + * is not altered. * + * * + * key -- Reference to the key code that will be returned by the controlling * + * Input() function. * + * * + * OUTPUT: bool; Should further list processing be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Action(unsigned flags, KeyNumType & key) +{ + + /* + ** Only if the flags indicate that a recognized action has occurred, do the + ** normal processing of this gadget and set return value to the gadget ID. + */ + if (flags) { + if (ID) { + key = (KeyNumType)(ID | KN_BUTTON); + } else { + key = KN_NONE; + } + } + + /* + ** If there is a peer link established, inform that gadget of this + ** action call. + */ + if (Peer) { + Peer->Peer_To_Peer(flags, key, *this); + } + + return(GadgetClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ControlClass::Make_Peer -- Assigns a peer gadget to this gadget. * + * * + * This function will assign another gadget to this one. That other gadget will receive * + * notification of any Action() call to this gadget. Presumably, this is how one gadget * + * can automatically adapt to changes in another. Say for example, a slider bar can affect * + * the list box it is attached to. * + * * + * INPUT: gadget -- The gadget to inform when any Action() function is called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ControlClass::Make_Peer(GadgetClass & gadget) +{ + Peer = &gadget; +} + + +/*********************************************************************************************** + * ControlClass::Get_ID -- Gets the ID number for this gadget. * + * * + * This function will query and return with the ID number for this gadget. It is primarily * + * used by the Extract_Gadget() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ID number for this gadget. If zero is returned, this means that * + * no ID was assigned to this gadget. This is a special case since a zero value will * + * never be returned as a pseudo-key as is done with non-zero values. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +unsigned ControlClass::Get_ID(void) const +{ + return(ID); +} + + +/*********************************************************************************************** + * ControlClass::Draw_Me -- Draw logic for the control class object. * + * * + * This is called when the control object might need to be redrawn or when redrawing is * + * necessary. Since at this level of the class hierarchy, no actual drawing occurs, this * + * routine doesn't perform any rendering. It does, however, inform any peer attached * + * object that a Draw_Me function has been called. Presumably, the attached peer gadget * + * might very well need to be redrawn as a result of some action by this gadget. Since this * + * gadget might, more than likely, be of the "sticky" variety, a normal call to Draw_Me * + * for the other gadget will not occur. It must rely on the call by this routine in order * + * to update correctly. A typical example of this would be a slider that is attached to * + * a list box. As the slider is being drug around, the attached list box must be redrawn. * + * * + * INPUT: forced -- Should the redraw be forced regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the gadget redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ControlClass::Draw_Me(int forced) +{ + if (Peer) { + Peer->Draw_Me(); + } + return(GadgetClass::Draw_Me(forced)); +} diff --git a/code/redalert/control.h b/code/redalert/control.h new file mode 100644 index 0000000..ac48b37 --- /dev/null +++ b/code/redalert/control.h @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CONTROL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONTROL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CONTROL_H +#define CONTROL_H + +#include "gadget.h" + +/*************************************************************************** + * ControlClass -- Region tracking class * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: 0 = new scenario created, -1 = not * + * WARNINGS: This class is Abstract (cannot make an instance of it) * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + *=========================================================================*/ +class ControlClass : public GadgetClass +{ + public: + ControlClass(NoInitClass const & x) : GadgetClass(x) {}; + ControlClass(unsigned id, int x, int y, int w, int h, unsigned flags=LEFTPRESS|RIGHTPRESS, int sticky=false); + ControlClass(ControlClass const & control); + + virtual void Make_Peer(GadgetClass & gadget); + + /* + ** Render support function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** This is the ID number for this control gadget. This number is used to generate + ** a special pseudo-key when the gadget detects valid input. + */ + unsigned ID; + + protected: + virtual unsigned Get_ID(void) const; + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This points to the peer button to inform when something happens to this + ** gadget. + */ + GadgetClass * Peer; +}; + +#endif + diff --git a/code/redalert/coord.cpp b/code/redalert/coord.cpp new file mode 100644 index 0000000..73a1786 --- /dev/null +++ b/code/redalert/coord.cpp @@ -0,0 +1,565 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/COORD.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : COORD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + * Support code to handle the coordinate system is located in this module. * + * Routines here will be called QUITE frequently during play and must be * + * as efficient as possible. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * + * Coord_Cell -- Convert a coordinate into a cell number. * + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * Distance -- Determines the cell distance between two cells. * + * Distance -- Determines the lepton distance between two coordinates. * + * Distance -- Fetch distance between two target values. * + * Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +// Cosine lookup table +const __int16 cosTable[255] = { + 0x0000, 0x0327, 0x064E, 0x0973, 0x0C98, 0x0FBA, 0x12DA, 0x15F7, 0x1911, 0x1C26, 0x1F38, 0x2244, 0x254B, 0x284D, 0x2B48, + 0x2E3C, 0x3129, 0x340F, 0x36EC, 0x39C1, 0x3C8D, 0x3F50, 0x4209, 0x44B7, 0x475B, 0x49F3, 0x4C81, 0x4F02, 0x5177, 0x53DF, + 0x563A, 0x5888, 0x5AC9, 0x5CFB, 0x5F1E, 0x6133, 0x6339, 0x652F, 0x6715, 0x68EC, 0x6AB2, 0x6C68, 0x6E0D, 0x6FA0, 0x7123, + 0x7293, 0x73F2, 0x753F, 0x767A, 0x77A2, 0x78B8, 0x79BB, 0x7AAB, 0x7B88, 0x7C51, 0x7D08, 0x7DAB, 0x7E3A, 0x7EB6, 0x7F1E, + 0x7F73, 0x7FB3, 0x7FE0, 0x7FF9, 0x7FFE, 0x7FEF, 0x7FCC, 0x7F95, 0x7F4B, 0x7EED, 0x7E7B, 0x7DF5, 0x7D5C, 0x7CAF, 0x7BEF, + 0x7B1C, 0x7A35, 0x793C, 0x782F, 0x7710, 0x75DF, 0x749B, 0x7345, 0x71DD, 0x7064, 0x6ED9, 0x6D3C, 0x6B8F, 0x69D1, 0x6803, + 0x6624, 0x6436, 0x6238, 0x602A, 0x5E0E, 0x5BE3, 0x59AA, 0x5763, 0x550E, 0x52AD, 0x503E, 0x4DC3, 0x4B3B, 0x48A8, 0x460A, + 0x4361, 0x40AD, 0x3DF0, 0x3B29, 0x3858, 0x357F, 0x329D, 0x2FB4, 0x2CC3, 0x29CB, 0x26CD, 0x23C9, 0x20BF, 0x1DB0, 0x1A9C, + 0x1784, 0x1469, 0x114A, 0x0E29, 0x0B06, 0x07E1, 0x04BA, 0x0193, 0xFE6D, 0xFB46, 0xF81F, 0xF4FA, 0xF1D7, 0xEEB6, 0xEB97, + 0xE87C, 0xE564, 0xE250, 0xDF41, 0xDC37, 0xD933, 0xD635, 0xD33D, 0xD04C, 0xCD63, 0xCA81, 0xC7A8, 0xC4D7, 0xC210, 0xBF53, + 0xBC9F, 0xB9F6, 0xB758, 0xB4C5, 0xB23D, 0xAFC2, 0xAD53, 0xAAF2, 0xA89D, 0xA656, 0xA41D, 0xA1F2, 0x9FD6, 0x9DC8, 0x9BCA, + 0x99DC, 0x97FD, 0x962F, 0x9471, 0x92C4, 0x9127, 0x8F9C, 0x8E23, 0x8CBB, 0x8B65, 0x8A21, 0x88F0, 0x87D1, 0x86C4, 0x85CB, + 0x84E4, 0x8411, 0x8351, 0x82A4, 0x820B, 0x8185, 0x8113, 0x80B5, 0x806B, 0x8034, 0x8011, 0x8002, 0x8007, 0x8020, 0x804D, + 0x808D, 0x80E2, 0x814A, 0x81C6, 0x8255, 0x82F8, 0x83AF, 0x8478, 0x8555, 0x8645, 0x8748, 0x885E, 0x8986, 0x8AC1, 0x8C0E, + 0x8D6D, 0x8EDD, 0x9060, 0x91F3, 0x9398, 0x954E, 0x9714, 0x98EB, 0x9AD1, 0x9CC7, 0x9ECD, 0xA0E2, 0xA305, 0xA537, 0xA778, + 0xA9C6, 0xAC21, 0xAE89, 0xB0FE, 0xB37F, 0xB60D, 0xB8A5, 0xBB49, 0xBDF7, 0xC0B0, 0xC373, 0xC63F, 0xC914, 0xCBF1, 0xCED7, + 0xD1C4, 0xD4B8, 0xD7B3, 0xDAB5, 0xDDBC, 0xE0C8, 0xE3DA, 0xE6EF, 0xEA09, 0xED26, 0xF046, 0xF368, 0xF68D, 0xF9B2, 0xFCD9, +}; + +// Sine lookup table +const __int16 sinTable[255] = { + 0x7FFF, 0x7FF5, 0x7FD7, 0x7FA5, 0x7F5F, 0x7F06, 0x7E99, 0x7E18, 0x7D84, 0x7CDC, 0x7C21, 0x7B52, 0x7A71, 0x797C, 0x7874, + 0x775A, 0x762D, 0x74EE, 0x739C, 0x7239, 0x70C4, 0x6F3D, 0x6DA5, 0x6BFC, 0x6A42, 0x6878, 0x669D, 0x64B3, 0x62B9, 0x60AF, + 0x5E97, 0x5C6F, 0x5A3A, 0x57F6, 0x55A5, 0x5346, 0x50DB, 0x4E63, 0x4BDE, 0x494E, 0x46B3, 0x440C, 0x415B, 0x3EA0, 0x3BDB, + 0x390D, 0x3636, 0x3356, 0x306F, 0x2D80, 0x2A8A, 0x278D, 0x248A, 0x2182, 0x1E74, 0x1B61, 0x184B, 0x1530, 0x1212, 0x0EF2, + 0x0BCF, 0x08AA, 0x0584, 0x025D, 0xFF37, 0xFC0F, 0xF8E9, 0xF5C3, 0xF29F, 0xEF7E, 0xEC5E, 0xE942, 0xE629, 0xE315, 0xE005, + 0xDCF9, 0xD9F4, 0xD6F4, 0xD3FA, 0xD108, 0xCE1C, 0xCB39, 0xC85D, 0xC58B, 0xC2C1, 0xC001, 0xBD4B, 0xBA9F, 0xB7FE, 0xB568, + 0xB2DE, 0xB060, 0xADEE, 0xAB89, 0xA931, 0xA6E6, 0xA4AA, 0xA27B, 0xA05B, 0x9E4A, 0x9C48, 0x9A56, 0x9873, 0x96A1, 0x94DF, + 0x932D, 0x918D, 0x8FFE, 0x8E80, 0x8D13, 0x8BB9, 0x8A70, 0x893A, 0x8817, 0x8706, 0x8607, 0x851C, 0x8444, 0x837F, 0x82CE, + 0x822F, 0x81A5, 0x812E, 0x80CB, 0x807B, 0x8040, 0x8018, 0x8004, 0x8004, 0x8018, 0x8040, 0x807B, 0x80CB, 0x812E, 0x81A5, + 0x822F, 0x82CE, 0x837F, 0x8444, 0x851C, 0x8607, 0x8706, 0x8817, 0x893A, 0x8A70, 0x8BB9, 0x8D13, 0x8E80, 0x8FFE, 0x918D, + 0x932D, 0x94DF, 0x96A1, 0x9873, 0x9A56, 0x9C48, 0x9E4A, 0xA05B, 0xA27B, 0xA4AA, 0xA6E6, 0xA931, 0xAB89, 0xADEE, 0xB060, + 0xB2DE, 0xB568, 0xB7FE, 0xBA9F, 0xBD4B, 0xC001, 0xC2C1, 0xC58B, 0xC85D, 0xCB39, 0xCE1C, 0xD108, 0xD3FA, 0xD6F4, 0xD9F4, + 0xDCF9, 0xE005, 0xE315, 0xE629, 0xE942, 0xEC5E, 0xEF7E, 0xF29F, 0xF5C3, 0xF8E9, 0xFC0F, 0xFF37, 0x025D, 0x0584, 0x08AA, + 0x0BCF, 0x0EF2, 0x1212, 0x1530, 0x184B, 0x1B61, 0x1E74, 0x2182, 0x248A, 0x278D, 0x2A8A, 0x2D80, 0x306F, 0x3356, 0x3636, + 0x390D, 0x3BDB, 0x3EA0, 0x415B, 0x440C, 0x46B3, 0x494E, 0x4BDE, 0x4E63, 0x50DB, 0x5346, 0x55A5, 0x57F6, 0x5A3A, 0x5C6F, + 0x5E97, 0x60AF, 0x62B9, 0x64B3, 0x669D, 0x6878, 0x6A42, 0x6BFC, 0x6DA5, 0x6F3D, 0x70C4, 0x7239, 0x739C, 0x74EE, 0x762D, + 0x775A, 0x7874, 0x797C, 0x7A71, 0x7B52, 0x7C21, 0x7CDC, 0x7D84, 0x7E18, 0x7E99, 0x7F06, 0x7F5F, 0x7FA5, 0x7FD7, 0x7FF5, +}; + +/*********************************************************************************************** + * Coord_Cell -- Convert a coordinate into a cell number. * + * * + * This routine will convert the specified coordinate value into a cell number. This is * + * useful to determine the map index number into the cell array that corresponds to a * + * particular coordinate. * + * * + * INPUT: coord -- The coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that corresponds to the coordinate specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +CELL Coord_Cell(COORDINATE coord) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell; + cell.Sub.Y = ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell; + return(cell.Cell); +// return(XY_Cell(((COORD_COMPOSITE)coord).Sub.X, ((COORD_COMPOSITE)composite).Sub.Y)); +} + + +/*********************************************************************************************** + * Distance -- Fetch distance between two target values. * + * * + * This routine will determine the lepton distance between the two specified target * + * values. * + * * + * INPUT: target1 -- First target value. * + * * + * target2 -- Second target value. * + * * + * OUTPUT: Returns with the lepton distance between the two target values. * + * * + * WARNINGS: Be sure that the targets are legal before calling this routine. Otherwise, the * + * return value is meaningless. * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +int Distance(TARGET target1, TARGET target2) +{ + return(Distance(As_Coord(target1), As_Coord(target2))); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance(COORDINATE coord1, COORDINATE coord2) +{ + int diff1, diff2; + + diff1 = Coord_Y(coord1) - Coord_Y(coord2); + if (diff1 < 0) diff1 = -diff1; + diff2 = Coord_X(coord1) - Coord_X(coord2); + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Determines the offset list for cell spillage/occupation. * + * * + * This routine will take an arbitrary position and object size and return with a list of * + * cell offsets from the current cell for all cells that are overlapped by the object. The * + * first cell offset is always zero, so to just get the adjacent spill cell list, add one * + * to the return pointer. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * maxsize -- The maximum width/height of the object (pixels). * + * * + * OUTPUT: Returns with a pointer to a spillage list. * + * * + * WARNINGS: The algorithm is limited to working with a maxsize of 48 or less. Larger values * + * will generate an incomplete overlap list. * + * * + * HISTORY: * + * 11/06/1993 JLB : Created. * + * 03/25/1994 JLB : Added width optimization. * + * 04/29/1994 JLB : Converted to C. * + * 06/03/1994 JLB : Converted to general purpose spillage functionality. * + * 01/07/1995 JLB : Manually calculates spillage list for large objects. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, int maxsize) +{ + static short const _MoveSpillage[(int)FACING_COUNT+1][5] = { + {0, -MAP_CELL_W, REFRESH_EOL, 0, 0}, // N + {0, -MAP_CELL_W, 1, -(MAP_CELL_W-1), REFRESH_EOL}, // NE + {0, 1, REFRESH_EOL, 0, 0}, // E + {0, 1, MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL}, // SE + {0, MAP_CELL_W, REFRESH_EOL, 0, 0}, // S + {0, -1, MAP_CELL_W, MAP_CELL_W-1, REFRESH_EOL}, // SW + {0, -1, REFRESH_EOL, 0, 0}, // W + {0, -1, -MAP_CELL_W, -(MAP_CELL_W+1), REFRESH_EOL}, // NW + {0, REFRESH_EOL, 0, 0, 0} // non-moving. + }; + static short _manual[10]; +//; 00 = on axis +//; 01 = below axis +//; 10 = above axis +//; 11 = undefined + static signed char const _SpillTable[16] = {8,6,2,-1,0,7,1,-1,4,5,3,-1,-1,-1,-1,-1}; + int index=0; + int x,y; + + /* + ** For mondo-enourmo-gigundo objects, use a prebuilt mammoth table + ** that covers a 5x5 square region. + */ + if (maxsize > ICON_PIXEL_W * 2) { + static short const _gigundo[] = { + -((2*MAP_CELL_W)-2),-((2*MAP_CELL_W)-1),-((2*MAP_CELL_W)),-((2*MAP_CELL_W)+1),-((2*MAP_CELL_W)+2), + -((1*MAP_CELL_W)-2),-((1*MAP_CELL_W)-1),-((1*MAP_CELL_W)),-((1*MAP_CELL_W)+1),-((1*MAP_CELL_W)+2), + -((0*MAP_CELL_W)-2),-((0*MAP_CELL_W)-1),-((0*MAP_CELL_W)),-((0*MAP_CELL_W)+1),-((0*MAP_CELL_W)+2), + ((1*MAP_CELL_W)-2),((1*MAP_CELL_W)-1),((1*MAP_CELL_W)),((1*MAP_CELL_W)+1),((1*MAP_CELL_W)+2), + +((2*MAP_CELL_W)-2),+((2*MAP_CELL_W)-1),+((2*MAP_CELL_W)),+((2*MAP_CELL_W)+1),+((2*MAP_CELL_W)+2), + REFRESH_EOL + }; + return(&_gigundo[0]); + } + + /* + ** For very large objects, build the overlap list by hand. This is time consuming, but + ** not nearly as time consuming as drawing even a single cell unnecessarily. + */ + if (maxsize > ICON_PIXEL_W) { + maxsize = min(maxsize, (ICON_PIXEL_W*2))/2; + + x = (ICON_PIXEL_W * Coord_XLepton(coord)) / ICON_LEPTON_W; + y = (ICON_PIXEL_H * Coord_YLepton(coord)) / ICON_LEPTON_H; + int left = x-maxsize; + int right = x+maxsize; + int top = y-maxsize; + int bottom = y+maxsize; + + _manual[index++] = 0; + if (left < 0) _manual[index++] = -1; + if (right >= ICON_PIXEL_W) _manual[index++] = 1; + if (top < 0) _manual[index++] = -MAP_CELL_W; + if (bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W; + if (left < 0 && top < 0) _manual[index++] = -(MAP_CELL_W+1); + if (right >= ICON_PIXEL_W && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W+1; + if (left < 0 && bottom >= ICON_PIXEL_H) _manual[index++] = MAP_CELL_W-1; + if (right >= ICON_PIXEL_H && top < 0) _manual[index++] = -(MAP_CELL_W-1); + _manual[index] = REFRESH_EOL; + return(&_manual[0]); + } + + /* + ** Determine the number of leptons "leeway" allowed this unit. + */ + int posval = Pixel2Lepton[(ICON_PIXEL_W-maxsize)/2]; + + x = Coord_XLepton(coord) - 0x0080; + y = Coord_YLepton(coord) - 0x0080; + if (y > posval) index |= 0x08; // Spilling South. + if (y < -posval) index |= 0x04; // Spilling North. + if (x > posval) index |= 0x02; // Spilling East. + if (x < -posval) index |= 0x01; // Spilling West. + + return(&_MoveSpillage[_SpillTable[index]][0]); +} + + +/*********************************************************************************************** + * Coord_Spillage_List -- Calculate a spillage list for the dirty rectangle specified. * + * * + * Given a center coordinate and a dirty rectangle, calcuate a cell offset list for * + * determining such things as overlap and redraw logic. Optionally, the center cell * + * location will not be part of the list. * + * * + * INPUT: coord -- The center coordinate that the dirty rectangle is based off of. * + * * + * rect -- Reference to the dirty rectangle. * + * * + * nocenter -- If true, then the center cell offset will not be part of the spillage * + * list returned. This is handy when the center cell is known to be * + * processed by some other method and it can be safely and efficiently * + * ignored by the list generated. * + * * + * OUTPUT: Returns with a pointer to the spillage list that corresponds to the data * + * specified. This is a pointer to a static buffer and as such it will only be valid * + * until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter) +{ + if (!rect.Is_Valid()) { + static short const _list[] = {REFRESH_EOL}; + return(_list); + } + + CELL coordcell = Coord_Cell(coord); + LEPTON x = Coord_X(coord); + LEPTON y = Coord_Y(coord); + + /* + ** Add the rectangle values to the coordinate in order to normalize the start and end + ** corners of the rectangle. The values are now absolute to the real game world rather + ** than relative to the coordinate. + */ + LEPTON_COMPOSITE startx; + LEPTON_COMPOSITE starty; + LEPTON_COMPOSITE endx; + LEPTON_COMPOSITE endy; + startx.Raw = (int)x + (short)Pixel_To_Lepton(rect.X); + starty.Raw = (int)y + (short)Pixel_To_Lepton(rect.Y); + endx.Raw = startx.Raw + Pixel_To_Lepton(rect.Width-1); + endy.Raw = starty.Raw + Pixel_To_Lepton(rect.Height-1); + + /* + ** Determine the upper left and lower right cell indexes. This is a simple conversion from + ** their lepton counterpart. These cells values are used to form the bounding box for the + ** map offset list. + */ + int cellx = startx.Sub.Cell; + int cellx2 = endx.Sub.Cell; + int celly = starty.Sub.Cell; + int celly2 = endy.Sub.Cell; + + /* + ** Generate the spillage list by counting off the rows and colums of the cells + ** that are affected. This is easy since the upper left and lower right corner cells + ** are known. + */ + int count = 0; + static short _spillagelist[128]; + short * ptr = _spillagelist; + for (int yy = celly; yy <= celly2; yy++) { + for (int xx = cellx; xx <= cellx2; xx++) { + short offset = (XY_Cell(xx, yy) - coordcell); + if (!nocenter || offset != 0) { + *ptr++ = offset; + count++; + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + } + if (count+2 >= ARRAY_SIZE(_spillagelist)) break; + } + + /* + ** Cap the list with the end of list marker and then return a pointer + ** to the completed list. + */ + *ptr = REFRESH_EOL; + return(_spillagelist); +} + + +/*********************************************************************************************** + * Coord_Move -- Moves a coordinate an arbitrary direction for an arbitrary distance * + * * + * This function will move a coordinate in a using SIN and COS arithmetic. * + * * + * INPUT: start -- The starting coordinate. * + * * + * dir -- The direction to move the coordinate. * + * * + * distance -- The distance to move the coordinate position (in leptons). * + * * + * OUTPUT: Returns the new coordinate position. * + * * + * WARNINGS: This routine uses multiplies -- use with caution. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE Coord_Move(COORDINATE start, register DirType dir, unsigned short distance) +{ + Move_Point(*(short *)&start, *(((short *)&start)+1), dir, distance); + return(start); +} + + +/*********************************************************************************************** + * Coord_Scatter -- Determines a random coordinate from an anchor point. * + * * + * This routine will perform a scatter algorithm on the specified * + * anchor point in order to return with another coordinate that is * + * randomly nearby the original. Typical use of this would be for * + * missile targeting. * + * * + * INPUT: coord -- This is the anchor coordinate. * + * * + * distance -- This is the distance in pixels that the scatter * + * should fall within. * + * * + * lock -- bool; Convert the new coordinate into a center * + * cell based coordinate? * + * * + * OUTPUT: Returns with a new coordinate that is nearby the original. * + * * + * WARNINGS: Maximum pixel scatter distance is 255. * + * * + * HISTORY: * + * 02/01/1992 JLB : Created. * + * 05/13/1992 JLB : Only uses Random(). * + *=============================================================================================*/ +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock) +{ + COORDINATE newcoord; + + newcoord = Coord_Move(coord, Random_Pick(DIR_N, DIR_MAX), distance); + + if (newcoord & HIGH_COORD_MASK) newcoord = coord; + + if (lock) { + newcoord = Coord_Snap(newcoord); + } + + return(newcoord); +} + +void Move_Point(short& x, short& y, register DirType dir, unsigned short distance) +{ + x += ( cosTable[dir] * distance ) / SHRT_MAX; + y += -( sinTable[dir] * distance ) / SHRT_MAX; +} + +int __cdecl calcx(signed short param1, short distance) +{ + __asm { + + //#pragma aux calcx parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax, 1 + rcl dx, 1 + mov al, ah + mov ah, dl + cwd + } +} + + +int __cdecl calcy(signed short param1, short distance) +{ + __asm { + + //#pragma aux calcy parm [ax] [bx] \ + + movzx eax, [param1] + mov bx, [distance] + imul bx + shl ax, 1 + rcl dx, 1 + mov al, ah + mov ah, dl + cwd + neg eax + } +} + + +void Normal_Move_Point(short& x, short& y, register DirType dir, unsigned short distance) +{ + static unsigned char const CosTable[256] = { + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + }; + + static unsigned char const SinTable[256] = { + 0x7f,0x7e,0x7e,0x7e,0x7e,0x7e,0x7d,0x7d, + 0x7c,0x7b,0x7b,0x7a,0x79,0x78,0x77,0x76, + 0x75,0x74,0x72,0x71,0x70,0x6e,0x6c,0x6b, + 0x69,0x67,0x66,0x64,0x62,0x60,0x5e,0x5b, + 0x59,0x57,0x55,0x52,0x50,0x4e,0x4b,0x49, + 0x46,0x43,0x41,0x3e,0x3b,0x39,0x36,0x33, + 0x30,0x2d,0x2a,0x27,0x24,0x21,0x1e,0x1b, + 0x18,0x15,0x12,0x0f,0x0c,0x09,0x06,0x03, + + 0x00,0xfd,0xfa,0xf7,0xf4,0xf1,0xee,0xeb, + 0xe8,0xe5,0xe2,0xdf,0xdc,0xd9,0xd6,0xd3, + 0xd0,0xcd,0xca,0xc7,0xc5,0xc2,0xbf,0xbd, + 0xba,0xb7,0xb5,0xb2,0xb0,0xae,0xab,0xa9, + 0xa7,0xa5,0xa2,0xa0,0x9e,0x9c,0x9a,0x99, + 0x97,0x95,0x94,0x92,0x91,0x8f,0x8e,0x8c, + 0x8b,0x8a,0x89,0x88,0x87,0x86,0x85,0x85, + 0x84,0x83,0x83,0x82,0x82,0x82,0x82,0x82, + + 0x82,0x82,0x82,0x82,0x82,0x82,0x83,0x83, + 0x84,0x85,0x85,0x86,0x87,0x88,0x89,0x8a, + 0x8b,0x8c,0x8e,0x8f,0x90,0x92,0x94,0x95, + 0x97,0x99,0x9a,0x9c,0x9e,0xa0,0xa2,0xa5, + 0xa7,0xa9,0xab,0xae,0xb0,0xb2,0xb5,0xb7, + 0xba,0xbd,0xbf,0xc2,0xc5,0xc7,0xca,0xcd, + 0xd0,0xd3,0xd6,0xd9,0xdc,0xdf,0xe2,0xe5, + 0xe8,0xeb,0xee,0xf1,0xf4,0xf7,0xfa,0xfd, + + 0x00,0x03,0x06,0x09,0x0c,0x0f,0x12,0x15, + 0x18,0x1b,0x1e,0x21,0x24,0x27,0x2a,0x2d, + 0x30,0x33,0x36,0x39,0x3b,0x3e,0x41,0x43, + 0x46,0x49,0x4b,0x4e,0x50,0x52,0x55,0x57, + 0x59,0x5b,0x5e,0x60,0x62,0x64,0x65,0x67, + 0x69,0x6b,0x6c,0x6e,0x6f,0x71,0x72,0x74, + 0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7b, + 0x7c,0x7d,0x7d,0x7e,0x7e,0x7e,0x7e,0x7e, + }; + distance = distance; // Keep LINT quiet. + + // + // Have to declare table as unsigned otherwise MSVC complains, but we need to treat the actual values as signed. + // + static const char* _sin_table = (char*)&SinTable[0]; + static const char* _cos_table = (char*)&CosTable[0]; + + x += calcx(_cos_table[dir], distance); + + y += calcy(_sin_table[dir] / 2, distance); +} \ No newline at end of file diff --git a/code/redalert/coorda.h b/code/redalert/coorda.h new file mode 100644 index 0000000..912c662 --- /dev/null +++ b/code/redalert/coorda.h @@ -0,0 +1,141 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S I N C ** +;*************************************************************************** +;* * +;* Project Name : Command & Conquer * +;* * +;* File Name : COORDA.ASM * +;* * +;* Programmer : Barry W. Green * +;* * +;* Start Date : February 17, 1995 * +;* * +;* Last Update : February 17, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + +#ifndef COORD_A_H + +//IDEAL +//P386 +//MODEL USE32 FLAT + +//global C Cardinal_To_Fixed :NEAR +//global C Fixed_To_Cardinal :NEAR + +// CODESEG +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/256 as the lowest and up to * +;* 256 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal); + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed); + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + + + + +#endif COORD_A_H \ No newline at end of file diff --git a/code/redalert/crate.cpp b/code/redalert/crate.cpp new file mode 100644 index 0000000..874f1db --- /dev/null +++ b/code/redalert/crate.cpp @@ -0,0 +1,182 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRATE.CPP 3 3/04/97 3:12p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CrateClass::Remove_It -- Removes the crate from wherever it is. * + * * + * This routine will remove the crate from whereever it happens to be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the crate found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Remove_It(void) +{ + if (Is_Valid()) { + Get_Crate(Cell); + Make_Invalid(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Create_Crate -- Create a crate in the cell specified. * + * * + * This will create a crate in the cell specified. If the crate could not be crated there * + * then 'false' will be returned. * + * * + * INPUT: cell -- The desired cell to place the crate in. * + * * + * OUTPUT: bool; Was the crate created and placed in the cell? * + * * + * WARNINGS: It is quite possible for the crate not to have been placed. Only the most clear * + * locations are valid for crate placement. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Create_Crate(CELL cell) +{ + /* + ** Remove any existing crate that this crate class is tracking. + */ + Remove_It(); + + /* + ** Try to place a new crate at the cell specified. + */ + if (Put_Crate(cell)) { + Cell = cell; + Timer = Random_Pick(Rule.CrateTime * (TICKS_PER_MINUTE/2), Rule.CrateTime * (TICKS_PER_MINUTE*2)); + Timer.Start(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Put_Crate -- Generates crate overlay at cell specified. * + * * + * This helpter routine will examine the cell and place the appropriate crate type into * + * the cell specified. If the overlay could not be generated, then 'false' is returned. * + * * + * INPUT: cell -- The cell to generate the crate overlay in. * + * * + * OUTPUT: bool; Was the crate overlay generated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + * 10/14/1996 JLB : Takes reference to cell so that tracking can occur. * + *=============================================================================================*/ +bool CrateClass::Put_Crate(CELL & cell) +{ + int old = ScenarioInit; + ScenarioInit = 0; + + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + while (cellptr->Overlay != OVERLAY_NONE && !cellptr->Is_Clear_To_Build(SPEED_FLOAT) && !cellptr->Is_Clear_To_Build(SPEED_FOOT)) { + cell = Map.Pick_Random_Location(); + + if (Percent_Chance(100 * Rule.WaterCrateChance)) { + cell = Map.Nearby_Location(cell, SPEED_FLOAT); + } else { + cell = Map.Nearby_Location(cell, SPEED_TRACK); + } + cellptr = &Map[cell]; + } + + if (cellptr->Is_Clear_To_Build(SPEED_FLOAT)) { + new OverlayClass(OVERLAY_WATER_CRATE, cell); + } else { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + ScenarioInit = old; + return(true); + } + + ScenarioInit = old; + return(false); +} + + +/*********************************************************************************************** + * CrateClass::Get_Crate -- Pick up a crate from the cell specified. * + * * + * This will remove the crate from the cell specified. * + * * + * INPUT: cell -- The cell to examine and remove any crate overlays present. * + * * + * OUTPUT: bool; Was a crate overlay found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool CrateClass::Get_Crate(CELL cell) +{ + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + + if (cellptr->Overlay == OVERLAY_WOOD_CRATE || + cellptr->Overlay == OVERLAY_STEEL_CRATE || + cellptr->Overlay == OVERLAY_WATER_CRATE) { + + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + return(true); + } + } + return(false); +} diff --git a/code/redalert/crate.h b/code/redalert/crate.h new file mode 100644 index 0000000..29727ac --- /dev/null +++ b/code/redalert/crate.h @@ -0,0 +1,76 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRATE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/26/96 * + * * + * Last Update : August 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CRATE_H +#define CRATE_H + +#include "ftimer.h" +#include "jshell.h" + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +class CrateClass { + public: + CrateClass(void) : Timer(NoInitClass()), Cell(-1) {} + void Init(void) {Make_Invalid();} + bool Create_Crate(CELL cell); + bool Is_Here(CELL cell) const {return(Is_Valid() && cell == Cell);} + bool Remove_It(void); + bool Is_Expired(void) const {return(Is_Valid() && Timer == 0);} + bool Is_Valid(void) const {return(Cell != -1);} + + private: + static bool Put_Crate(CELL & cell); + static bool Get_Crate(CELL cell); + + void Make_Invalid(void) {Cell = -1;Timer.Stop();} + + CDTimerClass Timer; + CELL Cell; +}; + + +#endif diff --git a/code/redalert/crc.cpp b/code/redalert/crc.cpp new file mode 100644 index 0000000..395d95e --- /dev/null +++ b/code/redalert/crc.cpp @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "crc.h" + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits one byte of data to the CRC engine. * + * * + * This routine will take the specified byte of data and submit it to the CRC engine * + * for processing. This routine is designed to be as fast as possible since the typical * + * use of this routine is to feed one of presumably many byte sized chunks of data to the * + * CRC engine. * + * * + * INPUT: datum -- One byte of data to submit to the CRC engine. * + * * + * OUTPUT: none * + * * + * WARNINGS: If possible, use the buffer/size operator to submit data rather than repeated * + * calls to this routine. * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +void CRCEngine::operator() (char datum) +{ + StagingBuffer.Buffer[Index++] = datum; + + if (Index == sizeof(long)) { + CRC = Value(); + StagingBuffer.Composite = 0; + Index = 0; + } +} + + +/*********************************************************************************************** + * CRCEngine::operator() -- Submits an arbitrary data block to the CRC engine. * + * * + * This routine will submit the specified block to the CRC engine. The block can be of * + * arbitrary length. * + * * + * INPUT: buffer -- Pointer to the buffer that contains the data. The buffer will not * + * be modified. * + * * + * length -- The length of the buffer (in bytes). * + * * + * OUTPUT: Returns with the current CRC value accumulated so far. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +long CRCEngine::operator() (void const * buffer, int length) +{ + if (buffer != NULL && length > 0) { + char const * dataptr = (char const *)buffer; + int bytes_left = length; + + /* + ** If there are any leader bytes (needed to fill the staging buffer) + ** then process those by first using them to fill up the staging + ** buffer. The bulk of the data block will be processed by the high + ** speed longword processing loop. + */ + while (bytes_left && Buffer_Needs_Data()) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + + /* + ** Perform the fast 'bulk' processing by reading long word sized + ** data blocks. + */ + long const * longptr = (long const *)dataptr; + int longcount = bytes_left / sizeof(long); // Whole 'long' elements remaining. + while (longcount--) { + CRC = _lrotl(CRC, 1) + *longptr++; + bytes_left -= sizeof(long); + } + + /* + ** If there are remainder bytes, then process these by adding them + ** to the staging buffer. + */ + dataptr = (char const *)longptr; + while (bytes_left) { + operator()(*dataptr); + dataptr++; + bytes_left--; + } + } + + /* + ** Return the current CRC value. + */ + return(Value()); +} diff --git a/code/redalert/crc.h b/code/redalert/crc.h new file mode 100644 index 0000000..df24964 --- /dev/null +++ b/code/redalert/crc.h @@ -0,0 +1,118 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRC.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/02/96 * + * * + * Last Update : March 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRC_H +#define CRC_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +/* +** This is a CRC engine class. It will process submitted data and generate a CRC from it. +** Well, actually, the value returned is not a true CRC. However, it shares the same strength +** characteristic and is faster to generate than the traditional CRC. This object is treated like +** a method class. If it is called as a function (using the function operator), it will return +** the CRC value. There are other function operators to submit data for processing. +*/ +class CRCEngine { + public: + + // Constructor for CRC engine (it can have an override initial CRC value). + CRCEngine(long initial=0) : CRC(initial), Index(0) { + StagingBuffer.Composite = 0; + }; + + // Fetches CRC value. + long operator() (void) const {return(Value());}; + + // Submits one byte sized datum to the CRC accumulator. + void operator() (char datum); + + // Submits an arbitrary buffer to the CRC accumulator. + long operator() (void const * buffer, int length); + + // Implicit conversion operator so this object appears like a 'long integer'. + operator long(void) const {return(Value());}; + + protected: + + bool Buffer_Needs_Data(void) const { + return(Index != 0); + }; + + long Value(void) const { + if (Buffer_Needs_Data()) { + return(_lrotl(CRC, 1) + StagingBuffer.Composite); + } + return(CRC); + }; + + /* + ** Current accumulator of the CRC value. This value doesn't take into + ** consideration any pending data in the staging buffer. + */ + long CRC; + + /* + ** This is the sub index into the staging buffer used to keep track of + ** partial data blocks as they are submitted to the CRC engine. + */ + int Index; + + /* + ** This is the buffer that holds the incoming partial data. When the buffer + ** is filled, the value is transformed into the CRC and the buffer is flushed + ** in preparation for additional data. + */ + union { + long Composite; + char Buffer[sizeof(long)]; + } StagingBuffer; +}; + +#endif + diff --git a/code/redalert/crcpipe.cpp b/code/redalert/crcpipe.cpp new file mode 100644 index 0000000..5b53e15 --- /dev/null +++ b/code/redalert/crcpipe.cpp @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCPIPE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCPipe::Result -- Fetches the current CRC of the data. * + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcpipe.h" + + +/*********************************************************************************************** + * CRCPipe::Put -- Retrieves the data bytes specified and calculates CRC on it. * + * * + * This routine will fetch the number of bytes requested from the straw. The data is * + * not modified by this straw segment, but it is examined by the CRC engine in order to * + * keep an accurate CRC of the data that passes through this routine. * + * * + * INPUT: source -- Pointer to the buffer that will hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number is * + * less than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCPipe::Put(void const * source, int slen) +{ + CRC(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * CRCPipe::Result -- Fetches the current CRC of the data. * + * * + * This routine will return the CRC of the data that has passed through the pipe up to * + * this time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCPipe::Result(void) const +{ + return(CRC()); +} + diff --git a/code/redalert/crcpipe.h b/code/redalert/crcpipe.h new file mode 100644 index 0000000..78d5652 --- /dev/null +++ b/code/redalert/crcpipe.h @@ -0,0 +1,63 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCPIPE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCPIPE_H +#define CRCPIPE_H + +#include "pipe.h" +#include "crc.h" + +/* +** This class doesn't modify the data being piped through, but it does examine it and build +** a CRC value from the data. +*/ +class CRCPipe : public Pipe +{ + public: + CRCPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCPipe(CRCPipe & rvalue); + CRCPipe & operator = (CRCPipe const & pipe); +}; + +#endif diff --git a/code/redalert/crcstraw.cpp b/code/redalert/crcstraw.cpp new file mode 100644 index 0000000..d6420b8 --- /dev/null +++ b/code/redalert/crcstraw.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "crcstraw.h" + + +/*********************************************************************************************** + * CRCStraw::Get -- Fetch the data requested and calculate CRC on it. * + * * + * This routine will fetch the number of bytes requested. The data will not be modified * + * by this straw segment, but the CRC engine will examine the data so as to keep an * + * accurate CRC value. * + * * + * INPUT: source -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored in the buffer. If this number is * + * less than that requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int CRCStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + CRC(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * CRCStraw::Result -- Returns with the CRC of all data passed through the straw. * + * * + * This routine will return the CRC value of the data that has passed through this straw * + * segment. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the CRC value of the data this straw segment has seen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long CRCStraw::Result(void) const +{ + return(CRC()); +} diff --git a/code/redalert/crcstraw.h b/code/redalert/crcstraw.h new file mode 100644 index 0000000..0bb94c9 --- /dev/null +++ b/code/redalert/crcstraw.h @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CRCSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CRCSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CRCSTRAW_H +#define CRCSTRAW_H + +#include "straw.h" +#include "crc.h" + +/* +** This class will build a CRC value from the data stream that is drawn through this class. +** The data is not modified, but it is examined as it passes through. +*/ +class CRCStraw : public Straw +{ + public: + CRCStraw(void) {} + virtual int Get(void * source, int slen); + + // Calculate and return the CRC value. + long Result(void) const; + + protected: + CRCEngine CRC; + + private: + CRCStraw(CRCStraw & rvalue); + CRCStraw & operator = (CRCStraw const & pipe); +}; + + +#endif diff --git a/code/redalert/credits.cpp b/code/redalert/credits.cpp new file mode 100644 index 0000000..834476e --- /dev/null +++ b/code/redalert/credits.cpp @@ -0,0 +1,236 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREDITS.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDITS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 17, 1994 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CreditClass::AI -- Handles updating the credit display. * + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CreditClass::CreditClass -- Default constructor for the credit class object. * + * * + * This is the constructor for the credit class object. It merely sets the credit display * + * state to null. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +CreditClass::CreditClass(void) : + Credits(0), + Current(0), + IsToRedraw(false), + IsUp(false), + IsAudible(false), + Countdown(0) +{ +} + + +/*********************************************************************************************** + * CreditClass::Graphic_Logic -- Handles the credit redraw logic. * + * * + * This routine should be called whenever the main game screen is to be updated. It will * + * check to see if the credit display should be redrawn. If so, it will redraw it. * + * * + * INPUT: forced -- Should the credit display be redrawn regardless of whether the redraw * + * flag is set? This is typically the case when the screen needs to be * + * redrawn from scratch. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +//#define XX (320-120) +//#define WW 50 +void CreditClass::Graphic_Logic(bool forced) +{ + if (forced || IsToRedraw) + { + BStart(BENCH_TABS); + + int xx = SeenBuff.Get_Width() - (120 * RESFACTOR) + ( 80 * RESFACTOR ); + + /* + ** Play a sound effect when the money display changes, but only if a sound + ** effect was requested. + */ + if (IsAudible) { + if (IsUp) { + Sound_Effect(VOC_MONEY_UP, fixed(1, 2)); + } else { + Sound_Effect(VOC_MONEY_DOWN, fixed(1, 2)); + } + } + + /* + ** Display the new current value. + */ + TabClass::Draw_Credits_Tab(); + Fancy_Text_Print("%ld", xx, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL, Current); + + if (Scen.MissionTimer.Is_Active()) + { + long secs = Scen.MissionTimer / TICKS_PER_SECOND; + long mins = secs / 60; + long hours = mins / 60; + secs %= 60; + mins %= 60; + + /* + ** Speak mission timer reminders. + */ + VoxType vox = VOX_NONE; + if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; + if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; + if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; + if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; + if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; + if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; + if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; + if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; + if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; + if (vox != VOX_NONE) { + Speak(vox); + Map.FlasherTimer = 7; + } + + xx = SeenBuff.Get_Width() - (120 * RESFACTOR); + if (hours) { + Fancy_Text_Print(TXT_TIME_FORMAT_HOURS, xx, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, hours, mins, secs); + } else { + Fancy_Text_Print(TXT_TIME_FORMAT_NO_HOURS, xx, 0, &MetalScheme, TBLACK, TPF_METAL12|TPF_CENTER|TPF_USE_GRAD_PAL, mins, secs); + } + } + + IsToRedraw = false; + IsAudible = false; + BEnd(BENCH_TABS); + } +} + + +/*********************************************************************************************** + * CreditClass::AI -- Handles updating the credit display. * + * * + * This routine handles the logic that controls the rate of credit change in the credit * + * display. It doesn't actually redraw the credit display, but will flag it to be redrawn * + * if it detects that a change is to occur. * + * * + * INPUT: forced -- Should the credit display immediately reflect the current credit * + * total for the player? This is usually desired when initially loading * + * a scenario or saved game. * + * player_ptr -- Player to calculate visible credits for * + * logic_only -- If true, don't flag map for redraw * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + * 10/16/2019 ST : Added house and logic parameters so we can call this from HouseClass::AI * + *=============================================================================================*/ +void CreditClass::AI(bool forced, HouseClass *player_ptr, bool logic_only) +{ + static int _last = 0; + + if (!forced && !logic_only && Frame == _last) return; + if (!logic_only) { + _last = Frame; + } + + if (player_ptr == NULL) { + return; + } + + Credits = player_ptr->Available_Money(); + + /* + ** Make sure that the credit counter doesn't drop below zero. + */ + Credits = max(Credits, 0L); + + if (Scen.MissionTimer.Is_Active() || Scen.MissionTimer) { + IsToRedraw = true; + Map.Flag_To_Redraw(false); + } + + if (Current == Credits) return; + + if (forced) { + IsAudible = false; + Current = Credits; + } else { + + if (Countdown) Countdown--; + if (Countdown) return; + + /* + ** Determine the amount to change the display toward the + ** desired value. + */ + int adder = Credits - Current; + + if (adder > 0) { + Countdown = 1; + } else { + Countdown = 3; + } + + adder = ABS(adder); + adder >>= 3; + adder = Bound(adder, 1, 71+72); + if (Current > Credits) adder = -adder; + Current += adder; + if (Current-adder != Current) { + IsAudible = true; + IsUp = (adder > 0); + } + } + IsToRedraw = true; + + if (!logic_only) { + Map.Flag_To_Redraw(false); + } +} \ No newline at end of file diff --git a/code/redalert/credits.h b/code/redalert/credits.h new file mode 100644 index 0000000..28709d4 --- /dev/null +++ b/code/redalert/credits.h @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREDITS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREDITS_H +#define CREDITS_H + +class HouseClass; +extern HouseClass *PlayerPtr; + +/**************************************************************************** +** The animating credit counter display is controlled by this class. +*/ +class CreditClass { + public: + long Credits; // Value of credits trying to update display to. + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + CreditClass(void); + CreditClass(NoInitClass const & ) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Update(bool forced=false, bool redraw=false); + + void Graphic_Logic(bool forced=false); + void AI(bool forced=false, HouseClass *player_ptr = PlayerPtr, bool logic_only = false); // Added house and logic_only parameters. ST - 10/16/2019 2:26PM + + long Current; // Credit value currently displayed. + + unsigned IsToRedraw:1; + unsigned IsUp:1; + unsigned IsAudible:1; + + private: + int Countdown; // Delay between ticks. +}; + +#endif diff --git a/code/redalert/crew.cpp b/code/redalert/crew.cpp new file mode 100644 index 0000000..27c3091 --- /dev/null +++ b/code/redalert/crew.cpp @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/code/redalert/crew.h b/code/redalert/crew.h new file mode 100644 index 0000000..ecfcf39 --- /dev/null +++ b/code/redalert/crew.h @@ -0,0 +1,67 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CREW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CREW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef CREW_H +#define CREW_H + + +/**************************************************************************** +** This class handles the basic crew logic. This includes hero tracking, +** crew bail-out, and attached object logic. +*/ +class CrewClass +{ + public: + /* + ** This keeps track of the number of "kills" the unit as accumulated. + ** When it reaches a certain point, the unit improves. + */ + unsigned short Kills; + + /* + ** Constructors, Destructors, and overloaded operators. + */ + CrewClass(void) : Kills(0) {}; + CrewClass(NoInitClass const &) {}; + ~CrewClass(void) {}; + + int Made_A_Kill(void) { + Kills++; + return(Kills); + }; + + private: +}; + +#endif diff --git a/code/redalert/cstraw.cpp b/code/redalert/cstraw.cpp new file mode 100644 index 0000000..538e5e6 --- /dev/null +++ b/code/redalert/cstraw.cpp @@ -0,0 +1,97 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CSTRAW.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CacheStraw::Get -- Fetch data from the data source. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "cstraw.h" +#include + + +/*********************************************************************************************** + * CacheStraw::Get -- Fetch data from the data source. * + * * + * This will supply the data quantity requested. It performs a regulating influence on the * + * data requests passed through it. The data is requested from the next straw in the * + * chain such that the data stream is requested in chunks. This serves to lessen the * + * impact of multiple small data requests. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * slen -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the number of data bytes stored into the buffer specified. If this * + * number is less than that requested, it indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1996 JLB : Created. * + *=============================================================================================*/ +int CacheStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + + /* + ** Keep processing the data request until there is no more data to supply or the request + ** has been fulfilled. + */ + while (slen > 0) { + + /* + ** First try to fetch the data from data previously loaded into the buffer. + */ + if (Length > 0) { + int tocopy = (Length < slen) ? Length : slen; + memmove(source, ((char *)BufferPtr.Get_Buffer()) + Index, tocopy); + slen -= tocopy; + Index += tocopy; + total += tocopy; + Length -= tocopy; + source = (char*)source + tocopy; + } + if (slen == 0) break; + + /* + ** Since there is more to be fulfilled yet the holding buffer is empty, + ** refill the buffer with a fresh block of data from the source. + */ + Length = Straw::Get(BufferPtr, BufferPtr.Get_Size()); + Index = 0; + if (Length == 0) break; + } + } + return(total); +} diff --git a/code/redalert/cstraw.h b/code/redalert/cstraw.h new file mode 100644 index 0000000..ac37fbd --- /dev/null +++ b/code/redalert/cstraw.h @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/CSTRAW.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/10/96 * + * * + * Last Update : November 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef CSTRAW_H +#define CSTRAW_H + +#include "straw.h" +#include "buff.h" + +/* +** This class handles transfer of data by perform regulated requests for data from the next +** class in the chain. It performs no translation on the data. By using this segment in a +** straw chain, data throughput can be regulated. This can yield great performance increases +** when dealing with a file source. +*/ +class CacheStraw : public Straw +{ + public: + CacheStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0), Length(0) {} + CacheStraw(int length=4096) : BufferPtr(length), Index(0), Length(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; + int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + CacheStraw(CacheStraw & rvalue); + CacheStraw & operator = (CacheStraw const & pipe); +}; + + + +#endif + diff --git a/code/redalert/dde.cpp b/code/redalert/dde.cpp new file mode 100644 index 0000000..bd48148 --- /dev/null +++ b/code/redalert/dde.cpp @@ -0,0 +1,462 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.CPP * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Instance_Class::InstanceClass -- class constructor * + * Instance_Class::InstanceClass -- class destructor * + * Instance_Class::Enable_Callback -- enables local processing of pokes * + * Instance_Class::Register_Servers -- registers a local DDE DNS service * + * Instance_Class::Cleanup_App -- currently does nothing * + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * Instance_Class::Open_Poke_Connection -- pokes some data to server * + * Instance_Class::Close_Poke_Connectionp -- closes connection to remote * + * Instance_Class::Poke_Server -- sends a chunk of data to remote * + * Instance_Class::dde_callback -- processes DDE transactions * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 +#include +#include "dde.h" + +/*************************************************************************** + * These are static members of Instance_Class + *=========================================================================*/ + +DWORD Instance_Class::id_inst; // instance identifier set by DdeInitialize +BOOL Instance_Class::process_pokes; // controls response to pokes +char Instance_Class::ascii_name[32]; // name of server + +#if (0) //ST - 5/8/2019 +static BOOL CALLBACK (*Instance_Class::callback) ( + LPBYTE pointer, // pointer to received data + long length // length of received data or advisory flag + ) = NULL; +#endif + +/*************************************************************************** + * Instance_Class::InstanceClass -- class constructor * + * * + * INPUT: * + * name1 null terminated ASCII client name * + * name1 null terminated ASCII server name * + * * + * OUTPUT: * + * dde_error = TRUE if error occurs when initializing DDE * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::Instance_Class( LPSTR name1, LPSTR name2 ) +{ + name1; name2; + return; + +#if (0) //ST - 5/8/2019 + dde_error = FALSE; // no errors + process_pokes = FALSE; // disable pokes in callback + + id_inst = 0; // set to 0 for first time through + conv_handle = 0; // conversation handle reset + + lstrcpy( ascii_name, name1 ); // keep a record of ASCII name + + if ( DdeInitialize( + (LPDWORD) &id_inst, // instance identifier + dde_callback, + APPCLASS_STANDARD | // filter server messages + CBF_FAIL_SELFCONNECTIONS, // prevent from connecting with self + 0) != DMLERR_NO_ERROR) { // reserved + dde_error = TRUE; // flag an error + } + + local_name = DdeCreateStringHandle( + id_inst, // instance identifier + name1, // string to register + CP_WINANSI); // Windows ANSI code page + + remote_name = DdeCreateStringHandle( + id_inst, // instance identifier + name2, // string to register + CP_WINANSI); // Windows ANSI code page + + poke_topic = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE TOPIC", // System topic + CP_WINANSI); // Windows ANSI code page + + poke_item = DdeCreateStringHandle( + id_inst, // instance identifier + "POKE ITEM", // System topic + CP_WINANSI); // Windows ANSI code page + + system_topic = DdeCreateStringHandle( + id_inst, // instance identifier + SZDDESYS_TOPIC, // System topic + CP_WINANSI); // Windows ANSI code page +#endif +} + +/*************************************************************************** + * Instance_Class::~Instance_Class -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +Instance_Class::~Instance_Class() +{ + DdeUninitialize( id_inst ); +} + +/*************************************************************************** + * Instance_Class::Enable_Callback -- enables user callback * + * * + * INPUT: * + * TRUE = enable poke processing * + * FALSE = disable poke processing * + * * + * OUTPUT: * + * echos the input * + * * + * WARNINGS: * + * user callback must be explicitly enabled. Disbabled by default. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Enable_Callback( BOOL flag ) // enable or disable callback +{ + return (process_pokes = flag); + +} + +/*************************************************************************** + * Instance_Class::Register_Server -- registers a local DDE DNS service * + * * + * INPUT: * + * BOOL CALLBACK ( *callback_fnc) ( LPBYTE, DWORD) = user poke callbacl * + * * + * OUTPUT: * + * TRUE == success * + * FALSE == failed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ +#if (0) //ST - 5/8/2019 +BOOL Instance_Class::Register_Server( BOOL CALLBACK ( *callback_fnc) (LPBYTE, long) ) +{ + + if (DdeNameService( id_inst, local_name, 0L, DNS_REGISTER ) != 0L) { + callback = callback_fnc; + return ( TRUE ); + } else { + return ( FALSE ); + } +} +#endif + +/*************************************************************************** + * Instance_Class::Test_Server_Running -- does a trial connect to remote * + * * + * INPUT: * + * name = HSZ string handle of server name. * + * * + * OUTPUT: * + * TRUE == successfully connected to remote * + * FALSE == failed to connect * + * * + * WARNINGS: * + * - Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * - Disconects before exiting. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Test_Server_Running( HSZ name ) +{ + + if( Open_Poke_Connection( name ) == TRUE) { + Close_Poke_Connection(); + return( TRUE ); + } else { + return( FALSE ); + } +} + +/*************************************************************************** + * Instance_Class::Open_Poke_Connection -- open a connection to server * + * * + * INPUT: * + * name = HSZ server name. * + * * + * OUTPUT: * + * TRUE == successfully opened connection * + * FALSE == failed to connect * + * * + * WARNINGS: * + * Can be called for local or remote server but of course will * + * fail if a called for local and local server is not "up". * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Open_Poke_Connection( HSZ name ) +{ + conv_handle = DdeConnect( + id_inst, // instance identifier + name, // service name string handle + poke_topic, // topic string handle + (PCONVCONTEXT) NULL);// use default context + + if (conv_handle == NULL) { + return FALSE; + } else { + return TRUE; + } +} + +/*************************************************************************** + * Instance_Class::Close_Poke_Connection -- closes poke connection * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * TRUE == successfully closed connection * + * FALSE == failed to close connection for some reason * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +BOOL Instance_Class::Close_Poke_Connection( void ) +{ + if( conv_handle ) { + HCONV temp_handle = conv_handle; + conv_handle = NULL; + return( DdeDisconnect( temp_handle )); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::Poke_Server -- pokes some data to server * + * * + * INPUT: * + * poke_data points to data to send to remote * + * poke_length length of buffer to send * + * * + * OUTPUT: * + * TRUE == successfully poked the data * + * FALSE == failed to connect * + * * + * WARNINGS: * + * has a 3 second timeout (change POKE_TIMEOUT, in milliseconds) * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +#define POKE_TIMEOUT 60*1000 // 60 sec timeout + +BOOL Instance_Class::Poke_Server( LPBYTE poke_data, DWORD poke_length ) +{ + + if( DdeClientTransaction( + + poke_data, // address of data to pass to server + poke_length, // length of data + conv_handle, // handle of conversation + poke_topic, // handle of item name string + CF_TEXT, // no special clipboard data format + XTYP_POKE, // transaction type + POKE_TIMEOUT, // time-out duration (millisecs) + (LPDWORD) NULL // address of transaction result (don't check) + ) == 0) { + + return( FALSE); + } else { + return( TRUE ); + } +} + +/*************************************************************************** + * Instance_Class::dde_callback -- callback dde event handler * + * * + * INPUT: * + * dde_event transaction type * + * uFmt clipboard data format * + * hconv handle of the conversation * + * hsz1 handle of a string * + * hsz2 handle of a string * + * hdata handle of a global memory object * + * dwData1 transaction-specific data * + * dwData2 transaction-specific data * + * * + * OUTPUT: * + * context specific HDDEDATA object * + * * + * WARNINGS: * + * NOTE: declared as HDDEDATA CALLBACK which means PASCAL parameters * + * * + * HISTORY: * + * 6/1/1996 SW : Created. * + *=========================================================================*/ + +HDDEDATA CALLBACK Instance_Class::dde_callback( + + UINT dde_event, // transaction type + UINT uFmt, // clipboard data format + HCONV , // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD , // transaction-specific data + DWORD // transaction-specific data + ) +{ + dde_event; + uFmt; + hsz1; + hsz2; + hdata; + return (HDDEDATA)NULL; + +#if (0) // ST 5/8/2019 + if (!Instance_Class::callback){ + return (HDDEDATA) NULL; + } + + switch ( dde_event ) { + + case XTYP_REGISTER: + case XTYP_UNREGISTER: + + return (HDDEDATA) NULL; + + case XTYP_ADVDATA: + return (HDDEDATA) DDE_FACK; + + case XTYP_XACT_COMPLETE: + + return (HDDEDATA) NULL; + + case XTYP_DISCONNECT: + + Instance_Class::callback( NULL, DDE_ADVISE_DISCONNECT); + return (HDDEDATA) NULL; + + case XTYP_CONNECT: { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz2, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, Instance_Class::ascii_name)) { + return (HDDEDATA) NULL; + } + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) NULL; + } + + Instance_Class::callback( NULL, DDE_ADVISE_CONNECT); + return (HDDEDATA) TRUE; + } + + case XTYP_POKE: + + if (Instance_Class::process_pokes == FALSE ) { + return (HDDEDATA) DDE_FNOTPROCESSED; // processing disabled + } else { + + char buffer[32]; + + DdeQueryString (Instance_Class::id_inst, hsz1, buffer, sizeof (buffer), 0) ; + + if (0 != strcmp (buffer, "POKE TOPIC")) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } else if (uFmt == CF_TEXT) { // make sure it's CF_TEXT + + BOOL processed; + BYTE FAR *pdata; + DWORD dw_length; + + if ( (pdata = DdeAccessData( hdata, &dw_length)) == NULL ) { + return (HDDEDATA) DDE_FNOTPROCESSED; + } + + processed = Instance_Class::callback((LPBYTE) pdata, dw_length); + + DdeUnaccessData( hdata ); + + if (processed == TRUE) { + return (HDDEDATA) DDE_FACK; + } else { + return (HDDEDATA) NULL; + } + + } + } + + default: + return (HDDEDATA) NULL; + } +#endif +} + +#endif //WIN32 diff --git a/code/redalert/dde.h b/code/redalert/dde.h new file mode 100644 index 0000000..1d29bea --- /dev/null +++ b/code/redalert/dde.h @@ -0,0 +1,172 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Dynamic Data Encapsulation * + * * + * File Name : DDE.H * + * * + * Programmer : Steve Wetherill * + * * + * Start Date : June 1, 1996 * + * * + * Last Update : June 8, 1996 [SW] * + * * + *-------------------------------------------------------------------------* + * * + * This is the DDE (Instance_Class) which provides a simple CLIENT/SERVER * + * DDE model for data transactions between Windows applications. * + * This is a fairly naieve implementation allowing only one client/server * + * per Instance_Class object. * + * * + * Typical uses for this class are: * + * * + * i. Robust verification of whether an application is running * + * ii. Data transfer between applications * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* +***************************** Class defines ***************************** +*/ + +#ifndef __DDE_H +#define __DDE_H + +#define DDE_ADVISE_CONNECT -1 // advisory "client has connected" +#define DDE_ADVISE_DISCONNECT -2 // advisory "client has disconnected" + +/* +***************************** Class Declaration ***************************** +*/ + +class Instance_Class { + + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + + /*..................................................................... + Constructor: + - takes null terminated ASCII strings names for client and server + .....................................................................*/ + + Instance_Class( // constructor + LPSTR, // null terminated local sever name string + LPSTR // null terminated remote server name string + ); + + /*..................................................................... + Destructor: + .....................................................................*/ + ~Instance_Class(void); // the destructor + + /*..................................................................... + Send data routine: + - sends an unsolicited packet of data to the remote server + .....................................................................*/ + BOOL Poke_Server( LPBYTE, DWORD); + + /*..................................................................... + Send data routine: + - sets up DNS for the server and registers a user callback to handle + incoming data + .....................................................................*/ + BOOL Register_Server( BOOL (CALLBACK *)(LPBYTE, long)); + + /*..................................................................... + Does a trial connect to the remote server. + - used to determine whether server is alive or not (and thus running) + .....................................................................*/ + BOOL Test_Server_Running( HSZ ); + + /*..................................................................... + Enables user callback (disabled by default) + .....................................................................*/ + BOOL Enable_Callback( BOOL ); // enable or disable callback + + /*..................................................................... + Open a connection for sending data to remote server + .....................................................................*/ + BOOL Open_Poke_Connection( HSZ ); + + /*..................................................................... + Close connection with remote server + .....................................................................*/ + BOOL Close_Poke_Connection( void ); + + // + // static members + // + + /*..................................................................... + User callback - called upon receipt of incoming data (static member!) + .....................................................................*/ + static BOOL (CALLBACK *callback) ( + + LPBYTE pointer, // pointer to received data + long length // if >0 length of received data + // if <0 + // -1 == client connect detected + // -2 == client disconnect detected + ); + + /*..................................................................... + DDE callback, called when DDEML has an event for us + .....................................................................*/ + static HDDEDATA CALLBACK dde_callback( + + UINT uType, // transaction type + UINT uFmt, // clipboard data format + HCONV hconv, // handle of the conversation + HSZ hsz1, // handle of a string + HSZ hsz2, // handle of a string + HDDEDATA hdata, // handle of a global memory object + DWORD dwData1, // transaction-specific data + DWORD dwData2 // transaction-specific data + ); + HANDLE instance; // this application's instance + HWND hwnd; // valid window handle + + /*..................................................................... + member variables + .....................................................................*/ + + static DWORD id_inst; // instance identifier set by DdeInitialize + static BOOL process_pokes; // controls response to pokes + static char ascii_name[32]; // name of server + + // + // non-static member variables + // + + HSZ remote_name; // string handle for remote server name + HSZ local_name; // string handle for local server name + HSZ system_topic; // string handle for the "system" topic + HSZ poke_topic; // string handle for poking data to server topic + HSZ poke_item; // string handle for poking data to server item + + HCONV conv_handle; // conversation handle + BOOL dde_error; // error flag + +}; + +#endif + + \ No newline at end of file diff --git a/code/redalert/debug.cpp b/code/redalert/debug.cpp new file mode 100644 index 0000000..411f228 --- /dev/null +++ b/code/redalert/debug.cpp @@ -0,0 +1,558 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DEBUG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEBUG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 18, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Self_Regulate -- Regulates the logic timer to result in smooth animation. * + * Debug_Key -- Debug mode keyboard processing. * + * Bench_Time -- Convert benchmark timer into descriptive string. * + * Benchmarks -- Display the performance tracking benchmarks. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#include + + +#ifdef CHEAT_KEYS + + +static CDTimerClass DebugTimer; + +int VortexFrame = -1; + +/*********************************************************************************************** + * Debug_Key -- Debug mode keyboard processing. * + * * + * If debugging is enabled, then this routine will be called for every keystroke that the * + * game doesn't recognize. These extra keys usually perform some debugging function. * + * * + * INPUT: input -- The key code that was pressed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Debug_Key(unsigned input) +{ + static int map_x = -1; + static int map_y = -1; + static int map_width = -1; + static int map_height = -1; + + if (!input || input & KN_BUTTON) return; + + /* + ** Processing of normal keystrokes. + */ + if (Debug_Flag) { + + switch (input) { + + case KN_BACKSPACE: + + if (ChronalVortex.Is_Active()) { + ChronalVortex.Disappear(); + } else { + int xxxx = Get_Mouse_X() + Map.TacPixelX; + int yyyy = Get_Mouse_Y() + Map.TacPixelY; + CELL cell = Map.DisplayClass::Click_Cell_Calc(xxxx,yyyy); + ChronalVortex.Appear ( Cell_Coord (cell) ); + } + break; + +#ifdef WIN32 + case KN_J: + Debug_MotionCapture = true; + break; + +#ifdef OBSOLETE + case KN_K: + /* + ** time to create a screen shot using the PCX code (if it works) + */ + if (!Debug_MotionCapture) { + GraphicBufferClass temp_page( SeenBuff.Get_Width(), + SeenBuff.Get_Height(), + NULL, + SeenBuff.Get_Width() * SeenBuff.Get_Height()); + CDFileClass file; + char filename[30]; + + SeenBuff.Blit(temp_page); + for (int lp = 0; lp < 99; lp ++) { + sprintf(filename, "scrsht%02d.pcx", lp); + file.Set_Name(filename); + if (!file.Is_Available()) break; + } + + file.Cache(200000); + Write_PCX_File(file, temp_page, & GamePalette); + Sound_Effect(VOC_BEEP); + } + break; +#endif +#endif + + case KN_P: + { + for (SpecialWeaponType spc = SPC_FIRST; spc < SPC_COUNT; spc++) { + PlayerPtr->SuperWeapon[spc].Enable(true, true); + PlayerPtr->SuperWeapon[spc].Forced_Charge(true); + Map.Add(RTTI_SPECIAL, spc); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + case KN_I: + { + Map.Flash_Power(); + Map.Flash_Money(); + } + break; + + case KN_O: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_HIND, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_B: + { + AircraftClass * air = new AircraftClass(AIRCRAFT_LONGBOW, PlayerPtr->Class->House); + if (air) { + air->Height = 0; + air->Unlimbo(Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()), DIR_N); + } + } + break; + + case KN_GRAVE: + { + WarheadType warhead = Random_Pick(WARHEAD_HE, WARHEAD_FIRE); + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + int damage = 1000; + new AnimClass(Combat_Anim(damage, warhead, Map[coord].Land_Type()), coord); + Explosion_Damage(coord, damage, NULL, warhead); + } + break; + + case KN_C: + Debug_Cheat = (Debug_Cheat == false); + PlayerPtr->IsRecalcNeeded = true; + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + if (!ScenarioInit) { + Map.Recalc(); + for (int index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + } + break; + + case (int)KN_Z|(int)KN_ALT_BIT: + if (map_x == -1) { + map_x = Map.MapCellX; + map_y = Map.MapCellY; + map_width = Map.MapCellWidth; + map_height = Map.MapCellHeight; + Map.MapCellX = 1; + Map.MapCellY = 1; + Map.MapCellWidth = MAP_CELL_W-2; + Map.MapCellHeight = MAP_CELL_H-2; + } else { + Map.MapCellX = map_x; + Map.MapCellY = map_y; + Map.MapCellWidth = map_width; + Map.MapCellHeight = map_height; + map_x = -1; + map_y = -1; + map_width = -1; + map_height = -1; + } + break; + + case KN_M: + if (Debug_Flag) { + if (MonoClass::Is_Enabled()) { + MonoClass::Disable(); + } else { + MonoClass::Enable(); + } + } + break; + + case (int)KN_W|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Win(); + break; + + case (int)KN_L|(int)KN_ALT_BIT: + PlayerPtr->Flag_To_Lose(); + break; + + case KN_DELETE: + if (CurrentObject.Count()) { + Map.Recalc(); + //CurrentObject[0]->Detach_All(); + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)CurrentObject[0])->Sell_Back(1); + } else { + ObjectClass * object = CurrentObject[0]; + object->Unselect(); + object->Limbo(); + delete object; + } + } + break; + + case (int)KN_DELETE|(int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + Map.Recalc(); + int damage = 50; + CurrentObject[0]->Take_Damage(damage, 0, WARHEAD_SA); + } + break; + + case KN_INSERT: + if (CurrentObject.Count()) { + Map.PendingObject = &CurrentObject[0]->Class_Of(); + if (Map.PendingObject) { + Map.PendingHouse = CurrentObject[0]->Owner(); + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(HouseClass::As_Pointer(Map.PendingHouse)); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + } + } + } + break; + + case KN_LBRACKET: + case KN_F11: + if (MonoPage == DMONO_FIRST) { + MonoPage = DMonoType(DMONO_COUNT-1); + } else { + MonoPage = DMonoType(MonoPage - 1); + } + DebugTimer = 0; + break; + + case KN_RBRACKET: + case KN_F12: + MonoPage = DMonoType(MonoPage + 1); + if (MonoPage == DMONO_COUNT) { + MonoPage = DMONO_FIRST; + } + DebugTimer = 0; + break; + + case KN_V: + case KN_F3: + Debug_Icon = (Debug_Icon == false); + Map.Flag_To_Redraw(true); + break; + + /* + ** Reveal entire map to player. + */ +// case KN_F4: +// if (Session.Type == GAME_NORMAL) { +// Debug_Unshroud = (Debug_Unshroud == false); +// Map.Flag_To_Redraw(true); +// } +// break; + + /* + ** Shows sight and fire range in the form of circles emanating from the currently + ** selected unit. The white circle is for sight range, the red circle is for + ** fire range. + */ + case KN_F7: + if (CurrentObject.Count() && CurrentObject[0]->Is_Techno()) { + TechnoTypeClass const & ttype = (TechnoTypeClass const &)CurrentObject[0]->Class_Of(); + int sight = ((int)ttype.SightRange)<<8; + int weapon = 0; + if (ttype.PrimaryWeapon != NULL) weapon = ttype.PrimaryWeapon->Range; + Set_Logic_Page(SeenBuff); + COORDINATE center = CurrentObject[0]->Center_Coord(); + COORDINATE center2 = CurrentObject[0]->Fire_Coord(0); + + for (int r = 0; r < 255; r += 10) { + int x,y,x1,y1; + DirType r1 = (DirType)r; + DirType r2 = (DirType)((r+10) & 0xFF); + + if (Map.Coord_To_Pixel(Coord_Move(center, r1, sight), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center, r2, sight), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, WHITE); + } + if (Map.Coord_To_Pixel(Coord_Move(center2, r1, weapon), x, y)) { + Map.Coord_To_Pixel(Coord_Move(center2, r2, weapon), x1, y1); + LogicPage->Draw_Line(x, y+8, x1, y1+8, RED); + } + } + } + break; + + case ((int)KN_F4): + Debug_Unshroud = (Debug_Unshroud == false); + Map.Flag_To_Redraw(true); + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * Bench_Time -- Convert benchmark timer into descriptive string. * + * * + * This routine will take the values of the benchmark timer specified and build a string * + * that displays the average time each event consumed as well as the ranking of how much * + * time that event took (total) during the tracking duration (one second?). * + * * + * INPUT: btype -- The benchmark to convert to a descriptive string. * + * * + * OUTPUT: Returns with a pointer to the descriptive string of the benchmark specified. * + * * + * WARNINGS: The value returned is a pointer to a static buffer. As such, it is only valid * + * until the next time that this routine is called. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static char const * Bench_Time(BenchType btype) +{ + //static char buffer[32]; + // + //int rootcount = Benches[BENCH_GAME_FRAME].Count(); + //if (rootcount == 0) rootcount = 1; + //int roottime = Benches[BENCH_GAME_FRAME].Value(); + //int count = Benches[btype].Count(); + //int time = Benches[btype].Value(); + //if (count > 0 && count * time > roottime * rootcount) time = roottime / count; + //int percent = 0; + //if (roottime != 0 && rootcount != 0) { + // percent = ((count * time) * 99) / (roottime * rootcount); + //} + //if (percent > 99) percent = 99; + //sprintf(buffer, "%-2d%% %7d", percent, time); + //return(buffer); + return ""; +} + + +/*********************************************************************************************** + * Benchmarks -- Display the performance tracking benchmarks. * + * * + * This will display the benchmarks for the various processes that are being tracked. The * + * display will indicate the fraction that each process is consuming out of the entire * + * process time as well as the time consumed by each individual event. The total fraction * + * is useful for determing what should be optimized. The individual time is useful for * + * guaging the effectiveness of optimization changes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the display will use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +static void Benchmarks(MonoClass * mono) +{ + //static bool _first = true; + //if (_first) { + // _first = false; + // mono->Clear(); + // mono->Set_Cursor(0, 0); + // mono->Print(Text_String(TXT_DEBUG_PERFORMANCE)); + // if (Benches == NULL) { + // mono->Set_Cursor(20, 15); + // mono->Printf(TXT_NO_PENTIUM); + // } + //} + // + //if (Benches != NULL) { + // mono->Set_Cursor(1, 2);mono->Printf("%s", Bench_Time(BENCH_FINDPATH)); + // mono->Set_Cursor(1, 4);mono->Printf("%s", Bench_Time(BENCH_GREATEST_THREAT)); + // mono->Set_Cursor(1, 6);mono->Printf("%s", Bench_Time(BENCH_AI)); + // mono->Set_Cursor(1, 8);mono->Printf("%s", Bench_Time(BENCH_PCP)); + // mono->Set_Cursor(1, 10);mono->Printf("%s", Bench_Time(BENCH_EVAL_OBJECT)); + // mono->Set_Cursor(1, 12);mono->Printf("%s", Bench_Time(BENCH_EVAL_CELL)); + // mono->Set_Cursor(1, 14);mono->Printf("%s", Bench_Time(BENCH_EVAL_WALL)); + // mono->Set_Cursor(1, 16);mono->Printf("%s", Bench_Time(BENCH_MISSION)); + // + // mono->Set_Cursor(14, 2);mono->Printf("%s", Bench_Time(BENCH_CELL)); + // mono->Set_Cursor(14, 4);mono->Printf("%s", Bench_Time(BENCH_OBJECTS)); + // mono->Set_Cursor(14, 6);mono->Printf("%s", Bench_Time(BENCH_ANIMS)); + // + // mono->Set_Cursor(27, 2);mono->Printf("%s", Bench_Time(BENCH_PALETTE)); + // + // mono->Set_Cursor(40, 2);mono->Printf("%s", Bench_Time(BENCH_GSCREEN_RENDER)); + // mono->Set_Cursor(40, 4);mono->Printf("%s", Bench_Time(BENCH_SIDEBAR)); + // mono->Set_Cursor(40, 6);mono->Printf("%s", Bench_Time(BENCH_RADAR)); + // mono->Set_Cursor(40, 8);mono->Printf("%s", Bench_Time(BENCH_TACTICAL)); + // mono->Set_Cursor(40, 10);mono->Printf("%s", Bench_Time(BENCH_POWER)); + // mono->Set_Cursor(40, 12);mono->Printf("%s", Bench_Time(BENCH_SHROUD)); + // mono->Set_Cursor(40, 14);mono->Printf("%s", Bench_Time(BENCH_TABS)); + // mono->Set_Cursor(40, 16);mono->Printf("%s", Bench_Time(BENCH_BLIT_DISPLAY)); + // + // mono->Set_Cursor(66, 2);mono->Printf("%7d", Benches[BENCH_RULES].Value()); + // mono->Set_Cursor(66, 4);mono->Printf("%7d", Benches[BENCH_SCENARIO].Value()); + // + // for (BenchType index = BENCH_FIRST; index < BENCH_COUNT; index++) { + // if (index != BENCH_RULES && index != BENCH_SCENARIO) Benches[index].Reset(); + // } + //} +} + + +/*********************************************************************************************** + * Self_Regulate -- Regulates the logic timer to result in smooth animation * + * * + * The self regulation process checks the number of frames displayed * + * per second and from this determines the amount of time to devote * + * to internal logic processing. By adjusting the time allotted to * + * internal processing, smooth animation can be maintained. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: In order for this routine to work properly it MUST be * + * called every display loop. * + * * + * HISTORY: * + * 07/31/1991 JLB : Created. * + * 07/05/1994 JLB : Handles new monochrome system. * + *=============================================================================================*/ +#define UPDATE_INTERVAL TIMER_SECOND +void Self_Regulate(void) +{ + static ObjectClass * _lastobject = 0; + static bool _first=true; + + if (DebugTimer == 0) { + DebugTimer = UPDATE_INTERVAL; + + if (MonoClass::Is_Enabled()) { + if (_first) { + _first = false; + for (DMonoType index = DMONO_FIRST; index < DMONO_COUNT; index++) { + MonoArray[index].Clear(); + } + } + + /* + ** Always update the stress tracking mono display even if it + ** currently isn't visible. + */ + Logic.Debug_Dump(&MonoArray[DMONO_STRESS]); + + MonoClass * mono = &MonoArray[MonoPage]; + mono->Set_Default_Attribute(MonoClass::NORMAL); + mono->View(); + + switch (MonoPage) { + case DMONO_EVENTS: + Benchmarks(mono); + break; + + case DMONO_OBJECT: + mono->Clear(); + + /* + ** Display the status of the currently selected object. + */ + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject) { + _lastobject->Debug_Dump(mono); + } + break; + + case DMONO_STRESS: +#ifdef OBSOLETE + mono->Set_Cursor(0, 20); + mono->Printf( + "Heap size:%10ld \r" + "Largest: %10ld \r" + "Ttl Free: %10ld \r" + "Frag: %10ld \r", + Heap_Size(MEM_NORMAL), + Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL), + Total_Ram_Free(MEM_NORMAL)-Ram_Free(MEM_NORMAL) + ); +#endif + break; + + case DMONO_HOUSE: + mono->Clear(); + + if (CurrentObject.Count()) { + _lastobject = CurrentObject[0]; + } + if (_lastobject && !_lastobject->IsActive) { + _lastobject = 0; + } + if (_lastobject && _lastobject->Is_Techno()) { + ((TechnoClass *)_lastobject)->House->Debug_Dump(mono); + } + break; + + default: + break; + } + + mono->Set_Cursor(0, 0); + } + } +} +#endif diff --git a/code/redalert/debug.h b/code/redalert/debug.h new file mode 100644 index 0000000..547e3a5 --- /dev/null +++ b/code/redalert/debug.h @@ -0,0 +1,15 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + diff --git a/code/redalert/defines.h b/code/redalert/defines.h new file mode 100644 index 0000000..cb64fe5 --- /dev/null +++ b/code/redalert/defines.h @@ -0,0 +1,3631 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DEFINES.H 4 3/07/97 9:55a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DEFINES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef DEFINES_H +#define DEFINES_H + +/********************************************************************** +** Language control: define the desired language for this build. +*/ +//#define ENGLISH 1 +//#define FRENCH 1 +//#define GERMAN 1 +//#define SPAIN 1 (never used) +// - Language define is now passed in from the makefile. - + +/********************************************************************** +** Controls the nature of the game and its abilities. Only define +** one of these values. +** +** Internal version -- complete with scenario editor. +** Playtest version -- no editor but does have minimal cheat keys. +** Release version -- no editor or cheat keys -- all debugging info removed. +*/ +//#define INTERNAL_VERSION +//#define PLAYTEST_VERSION +#define RELEASE_VERSION + +/********************************************************************** +** ColinM +** Set this to enable dongle protection +*/ +//#define DONGLE + + +// Enable 640x400 VQ movie capability in WIN32 mode +#define MOVIE640 + + +//#if (GERMAN | FRENCH) +//#define BOGUSCD +//#endif + +#define FIXIT_SCORE_CRASH // Fixes score screen crash +#define FIXIT_MULTI_SAVE // Fixes multiplayer save/load +#define FIXIT_NO_COMP_ALLY // Prevent ally with computer +#define FIXIT_DESTNET // Fixes -destnet parameter in Win95 +#define FIXIT_RANDOM_GAME // Fixes random seed at start of multiplayer games +#define FIXIT_FORCE_CD // Forces correct CD load after scenario #1 +#define FIXIT_IP_CRASH // Fixes crash if internet game aborts too quickly +#define FIXIT_IP_STATS // Fixes so vessels show up in internet stat info +#define FIXIT_NAME_OVERRIDE // Allows changing of unit names +#define FIXIT_RADAR_JAMMED // Fixes unjamming by merely starting to build a radar facility +#define FIXIT_CAPTURE_BIB // Fixes so that if fake is captured, you still can't build off of it. +#define FIXIT_BASE_ANNOUNCE // Fixes so player controlled buildings count as base when attacked. +#define FIXIT_APTIVA_MODEM // Fixes crash with Aptiva modem. +#define FIXIT_FLAG_CHECK // Disable placing building over a flag. + +#define FIXIT_ANTS // Adds Ant Units + +#define FIXIT_CSII // Adds Aftermath CounterStrike II units +// ajw 9/28/98 - Note about FIXIT_CSII. Changes seem to have been made for Aftermath ("Counterstrike II") that: a) were +// bug fixes that should never be rolled back, b) change the nature of the game, at least in multi-player. This meant +// that the "Red Alert" executable ( == Counterstrike executable ) could no longer be built. Apparently, at the time, +// this was justified, as it was believed that no further patches to the RA executable would ever be necessary. +// Given that Denzil's DVD changes and my WOLAPI integration are essentially a patch, we've got a problem. +// We've decided to level the field and make sure every who gets or patches to the new version of Red Alert, CS, AM, (and +// their DVD equivalent(s)) will have the same executable. So we're assuming that all of the FIXIT_CSII changes are +// permanent (as, in fact, all prior FIXIT_'s are - makes me wonder why the old non-compiling code has to hang around +// forever), and fixing the code so that the assumption "this is an Aftermath game" is no longer hard-coded, but can +// change at runtime. (Which is what should have been done when Aftermath was created.) +// +#define FIXIT_CARRIER // Adds Aftermath aircraft carrier +#define FIXIT_PHASETRANSPORT // Adds Aftermath cloaking APC +// ajw - Discovered that engineer changing fields were specifically left out of aftrmath.ini, thus this has no effect. +// Engineer changes (and other game rule changes) are in mplayer.ini, which was loaded before aftermath-only mplayer games. +#define FIXIT_ENGINEER // Adds Engineer rules.ini overrides + +//#define FIXIT_FAST_LOAD // Enables faster INI loading + +// These fixes will cause the game to go out of sync. +//#define FIXIT_ENGINEER_CAPTURE // If building not allied, will still capture if engineer not allied with building. +//#define FIXIT_HELI_LANDING // Fixes so new helicopters land at free helipad +//#define FIXIT_MINE_PASSABLE // Fixes units not driving onto mines + +/* Turn on these changes for the 1.08 patch */ +#define FIXIT_PATCH_108 + +#ifdef FIXIT_PATCH_108 +#define STEVES_LOAD_OVERRIDE // Allows loading of CONQUER.ENG instead of from mix file. +#define FIXIT_DIFFICULTY // Fixes no difficulty level for CStrike missions +#define FIXIT_VERSION // Fixes version playability for 1.04, 1.07 & 1.08 +#define FIXIT_MODEM_LOAD_CRASH // Fixes crash after loading a modem game when names are the same +#define FIXIT_PHONELIST_CRASH // Fixes crash when clicking on an empty phonelist +#endif + +// Denotes changes made for version 3 - reunification of all existing versions and undoing of Aftermath divergence. - ajw +#define FIXIT_VERSION_3 +#define DVD + +// Define DVD to turn on RADVD additions/changes - Denzil +#ifdef DVD +//#define INTERNET_OFF +//#define MPEGMOVIE //PG +//#define MCIMPEG +#endif + +// Test to see if partial object drawing is any faster. +//#define PARTIAL +#define SORTDRAW + +/********************************************************************** +** If the scenario editor to to be active in this build then uncomment +** the following #define line. +*/ +#ifdef INTERNAL_VERSION +#define SCENARIO_EDITOR +#endif + + +/********************************************************************** +** This define enables the full set of cheat keys and special +** command line options. +*/ +#if defined(INTERNAL_VERSION) || defined(PLAYTEST_VERSION) +#define CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, the special Virgin limited cheat keys +** are enabled. This allows the "cheat" parameter and then only +** allows the ALT-W to win the mission. +*/ +#ifdef PLAYTEST_VERSION +#define VIRGIN_CHEAT_KEYS +#endif + + +/********************************************************************** +** If this is defined, then the network code will be enabled. +*/ +#define NETWORK +#define TIMING_FIX 1 + + +/********************************************************************** +** Define this to 1 to enable MPath-specific code. Do not define +** TEN at the same time. +*/ +#define MPATH 0 + + +/********************************************************************** +** Define this to 1 to enable TEN-specific code. Do not define +** MPATH at the same time. +*/ +#define TEN 0 + + +/********************************************************************** +** If this is defined, the DoList is "mirrored", for memory trasher +** detection. +*/ +#ifdef CHEAT_KEYS +//#define MIRROR_QUEUE +#endif + + +/********************************************************************** +** This define tells the Version Number class to use the date/time-based +** version numbering system. If this define is not set, the actual +** major/minor version numbers will be used. +*/ +//#define DEV_VERSION +//#define DEV_VER_NAME + + +/********************************************************************** +** This define enables a special additional foreign-version-number +** after the other version number, for display purposes only. +*/ +#if !defined(ENGLISH) +#define FOREIGN_VERSION +#define FOREIGN_VERSION_NUMBER 7 +#endif + + +/********************************************************************** +** This is the multiplier factor to convert low resution coordinates +** into their actual resolution counterparts. +*/ +#ifdef WIN32 +#define RESFACTOR 2 +#else +//#undef SCENARIO_EDITOR +#define RESFACTOR 1 +#endif + + +#define SIDEBAR_WID 80 + + +/********************************************************************** +** Optional parameter control for special options. +*/ + +/* +** Enable the set of limited cheat key options. +*/ +#ifdef VIRGIN_CHEAT_KEYS +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif + +/* +** Enable the full set of cheat key options. +*/ +#ifdef CHEAT_KEYS +#ifndef PARM_PLAYTEST +#define PARM_PLAYTEST 0xF7DDC227 // "PLAYTEST" +#endif +#endif + + +#define PARM_INSTALL 0xD95C68A2 // "FROMINSTALL" + + +// +// Allow normal game play in the MPath version +// +#if(MPATH) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +// +// Allow normal game play in the TEN version +// +#if(TEN) +#define PARM_ALLOW_SOLO 0xc901c9db // AllowSoloPlayOptions +#endif + +/********************************************************************** +** Defines for verifying free disk space +*/ +#define INIT_FREE_DISK_SPACE 8388608 +#define SAVE_GAME_DISK_SPACE (INIT_FREE_DISK_SPACE - (1024*4096)) +//#define SAVE_GAME_DISK_SPACE 100000 + + +/********************************************************************** +** This is the complete list of VQs allowed to be played in the game. +*/ +typedef enum VQType { + VQ_NONE=-1, + VQ_AAGUN, + VQ_MIG, + VQ_SFROZEN, + VQ_AIRFIELD, + VQ_BATTLE, + VQ_BMAP, + VQ_BOMBRUN, + VQ_DPTHCHRG, + VQ_GRVESTNE, + VQ_MONTPASS, + VQ_MTNKFACT, + VQ_CRONTEST, + VQ_OILDRUM, + VQ_ALLYEND, + VQ_RADRRAID, + VQ_SHIPYARD, + VQ_SHORBOMB, + VQ_SITDUCK, + VQ_SLNTSRVC, + VQ_SNOWBASE, + VQ_EXECUTE, + VQ_TITLE, // Low res. + VQ_NUKESTOK, + VQ_V2ROCKET, + VQ_SEARCH, + VQ_BINOC, + VQ_ELEVATOR, + VQ_FROZEN, + VQ_MCV, + VQ_SHIPSINK, + VQ_SOVMCV, + VQ_TRINITY, + VQ_ALLYMORF, + VQ_APCESCPE, + VQ_BRDGTILT, + VQ_CRONFAIL, + VQ_STRAFE, + VQ_DESTROYR, + VQ_DOUBLE, + VQ_FLARE, + VQ_SNSTRAFE, + VQ_LANDING, + VQ_ONTHPRWL, + VQ_OVERRUN, + VQ_SNOWBOMB, + VQ_SOVCEMET, + VQ_TAKE_OFF, + VQ_TESLA, + VQ_SOVIET8, + VQ_SPOTTER, + VQ_SCENE1, + VQ_SCENE2, + VQ_SCENE4, + VQ_SOVFINAL, + VQ_ASSESS, + VQ_SOVIET10, + VQ_DUD, + VQ_MCV_LAND, + VQ_MCVBRDGE, + VQ_PERISCOP, + VQ_SHORBOM1, + VQ_SHORBOM2, + VQ_SOVBATL, + VQ_SOVTSTAR, + VQ_AFTRMATH, + VQ_SOVIET11, + VQ_MASASSLT, + VQ_REDINTRO, // High res + VQ_SOVIET1, + VQ_SOVIET2, + VQ_SOVIET3, + VQ_SOVIET4, + VQ_SOVIET5, + VQ_SOVIET6, + VQ_SOVIET7, + VQ_INTRO_MOVIE, + VQ_AVERTED, + VQ_COUNTDWN, + VQ_MOVINGIN, + VQ_ALLIED10, + VQ_ALLIED12, + VQ_ALLIED5, + VQ_ALLIED6, + VQ_ALLIED8, + VQ_TANYA1, + VQ_TANYA2, + VQ_ALLY10B, + VQ_ALLY11, + VQ_ALLY14, + VQ_ALLY9, + VQ_SPY, + VQ_TOOFAR, + VQ_SOVIET12, + VQ_SOVIET13, + VQ_SOVIET9, + VQ_BEACHEAD, + VQ_SOVIET14, + VQ_SIZZLE, + VQ_SIZZLE2, + VQ_ANTEND, + VQ_ANTINTRO, + + //2019/11/12 JAS - Added for Retaliation movies + VQ_RETALIATION_ALLIED1, + VQ_RETALIATION_ALLIED2, + VQ_RETALIATION_ALLIED3, + VQ_RETALIATION_ALLIED4, + VQ_RETALIATION_ALLIED5, + VQ_RETALIATION_ALLIED6, + VQ_RETALIATION_ALLIED7, + VQ_RETALIATION_ALLIED8, + VQ_RETALIATION_ALLIED9, + VQ_RETALIATION_ALLIED10, + + VQ_RETALIATION_SOVIET1, + VQ_RETALIATION_SOVIET2, + VQ_RETALIATION_SOVIET3, + VQ_RETALIATION_SOVIET4, + VQ_RETALIATION_SOVIET5, + VQ_RETALIATION_SOVIET6, + VQ_RETALIATION_SOVIET7, + VQ_RETALIATION_SOVIET8, + VQ_RETALIATION_SOVIET9, + VQ_RETALIATION_SOVIET10, + VQ_RETALIATION_WINA, + VQ_RETALIATION_WINS, + VQ_RETALIATION_ANTS, + + VQ_COUNT, + VQ_FIRST=0 +} VQType; + + +/********************************************************************** +** These enumerations are used to implement RTTI. The target system +** uses these and thus there can be no more RTTI types than can fit +** in the exponent of a target value. +*/ +typedef enum RTTIType : unsigned char { + RTTI_NONE=0, + RTTI_AIRCRAFT, + RTTI_AIRCRAFTTYPE, + RTTI_ANIM, + RTTI_ANIMTYPE, + RTTI_BUILDING, + RTTI_BUILDINGTYPE, + RTTI_BULLET, + RTTI_BULLETTYPE, + RTTI_CELL, + RTTI_FACTORY, + RTTI_HOUSE, + RTTI_HOUSETYPE, + RTTI_INFANTRY, + RTTI_INFANTRYTYPE, + RTTI_OVERLAY, + RTTI_OVERLAYTYPE, + RTTI_SMUDGE, + RTTI_SMUDGETYPE, + RTTI_SPECIAL, + RTTI_TEAM, + RTTI_TEAMTYPE, + RTTI_TEMPLATE, + RTTI_TEMPLATETYPE, + RTTI_TERRAIN, + RTTI_TERRAINTYPE, + RTTI_TRIGGER, + RTTI_TRIGGERTYPE, + RTTI_UNIT, + RTTI_UNITTYPE, + RTTI_VESSEL, + RTTI_VESSELTYPE, + + RTTI_COUNT +} RTTIType; + + +/********************************************************************** +** These are the difficulty settings of the game. +*/ +typedef enum DiffType : unsigned char { + DIFF_EASY, + DIFF_NORMAL, + DIFF_HARD, + + DIFF_COUNT, + DIFF_FIRST=0 +} DiffType; + + +/********************************************************************** +** This is the size of the speech buffer. This value should be as large +** as the largest speech sample, plus a few bytes for overhead +** (16 bytes is sufficient). +*/ +#define SPEECH_BUFFER_SIZE 50000L + + +/********************************************************************** +** The theater mixfiles are cached into a buffer of this size. Ensure +** that the size specified is at least as large as the largest +** theater mixfile data block. +*/ +#define THEATER_BUFFER_SIZE 1100000L + + +/********************************************************************** +** This is the size of the shape buffer. This buffer is used as a staging +** buffer for the shape drawing technology. It MUST be as big as the +** largest shape (uncompressed) that will be drawn. If this value is +** changed, be sure to update the makefile and rebuild all of the shape +** data files. +*/ +#define SHAPE_BUFFER_SIZE 131072L + + +/********************************************************************** +** Filenames of the data files it can create at run time. +*/ +#define FAME_FILE_NAME "HALLFAME.DAT" +#define NET_SAVE_FILE_NAME "SAVEGAME.NET" +#define CONFIG_FILE_NAME "REDALERT.INI" + + +/********************************************************************** +** Map controls. The map is composed of square elements called 'cells'. +** All larger elements are build upon these. +*/ + +#define HIGH_COORD_MASK 0x80008000L + +// Size of the map in cells. +#define MAP_CELL_W 128 +#define MAP_CELL_H 128 +#define MAP_CELL_TOTAL (MAP_CELL_W*MAP_CELL_H) + +#define REFRESH_EOL 32767 // This number ends a refresh/occupy offset list. +#define REFRESH_SIDEBAR 32766 // This number flags that sidebar needs refreshing. + + +/**************************************************************************** +** These are custom C&C specific types. The CELL is used for map coordinate +** with cell resolution. The COORDINATE type is used for map coordinates that +** have a lepton resolution. CELL is more efficient when indexing into the map +** and when size is critical. COORDINATE is more efficient when dealing with +** accuracy and object movement. +*/ +typedef unsigned short LEPTON; +typedef union { + LEPTON Raw; + struct { +#ifdef BIG_ENDIAN + unsigned char Cell; + unsigned char Lepton; +#else + unsigned char Lepton; + unsigned char Cell; +#endif + } Sub; +} LEPTON_COMPOSITE; + +typedef unsigned long COORDINATE; +typedef union { + COORDINATE Coord; + struct { +#ifdef BIG_ENDIAN + LEPTON_COMPOSITE Y; + LEPTON_COMPOSITE X; +#else + LEPTON_COMPOSITE X; + LEPTON_COMPOSITE Y; +#endif + } Sub; +} COORD_COMPOSITE; + +typedef signed short CELL; +#define SLUFF_BITS (sizeof(CELL)*CHAR_BIT)-(14) +typedef union { + CELL Cell; + struct { +#ifdef BIG_ENDIAN +#if SLUFF_BITS + /* + ** Unused upper bits will cause problems on a big-endian machine unless they + ** are deliberately accounted for. + */ + unsigned sluff:SLUF_BITS; +#endif + unsigned Y:7; + unsigned X:7; +#else + unsigned X:7; + unsigned Y:7; +#endif + } Sub; +} CELL_COMPOSITE; + +typedef int WAYPOINT; + + +/********************************************************************** +** This is the target composit information. Notice that with an RTTI_NONE +** and an index value of 0, the target value returned is identical with +** TARGET_NONE. This is by design and is necessary. +*/ +typedef long TARGET; + +#define TARGET_MANTISSA 24 // Bits of value precision. +#define TARGET_EXPONENT 8 +typedef union { + TARGET Target; + struct { +#ifdef BIG_ENDIAN + unsigned Exponent:TARGET_EXPONENT; + unsigned Mantissa:TARGET_MANTISSA; +#else + unsigned Mantissa:TARGET_MANTISSA; + unsigned Exponent:TARGET_EXPONENT; +#endif + } Sub; +} TARGET_COMPOSITE; + + +inline TARGET Build_Target(RTTIType kind, int value) +{ + TARGET_COMPOSITE target; + + target.Target = 0; + target.Sub.Exponent = kind; + target.Sub.Mantissa = value; + return(target.Target); +} + + +#define TARGET_NONE ((TARGET)0) + + +/* +** The map is broken down into regions of this specified dimensions. +*/ +#define REGION_WIDTH 4 +#define REGION_HEIGHT 4 +#define MAP_REGION_WIDTH (((MAP_CELL_W + (REGION_WIDTH -1)) / REGION_WIDTH)+2) +#define MAP_REGION_HEIGHT (((MAP_CELL_H + (REGION_WIDTH -1)) / REGION_HEIGHT)+2) +#define MAP_TOTAL_REGIONS (MAP_REGION_WIDTH * MAP_REGION_HEIGHT) + + +/********************************************************************** +** This enumerates the various known fear states for infantry units. +** At these stages, certain events or recovery actions are performed. +*/ +typedef enum FearType : unsigned char { + FEAR_NONE=0, // No fear at all (default state). + FEAR_ANXIOUS=10, // Something makes them scared. + FEAR_SCARED=100, // Scared enough to take cover. + FEAR_PANIC=200, // Run away! Run away! + FEAR_MAXIMUM=255 // Scared to death. +} FearType; + + +/********************************************************************** +** When a moving object moves, the Per_Cell_Process function is called +** at various times during the move. Certain operations must be +** performed at different stages of the move. This enum specifies the +** different conditions under which the Per_Cell_Process function is +** called. +*/ +typedef enum PCPType : unsigned char { + PCP_ROTATION, // When sitting in place and performing rotations. + PCP_DURING, // While moving between two cells. + PCP_END, // When the 'center' of a cell is reached during movement. +} PCPType; + + +/********************************************************************** +** A base is broken up into several zones. This type enumerates the +** various zones. +*/ +typedef enum ZoneType : char { + ZONE_CORE, // Center of base. + ZONE_NORTH, // North section. + ZONE_EAST, // East section. + ZONE_SOUTH, // South section. + ZONE_WEST, // West section. + + ZONE_COUNT, + ZONE_FIRST=0, + ZONE_NONE=-1 +} ZoneType; + + +/********************************************************************** +** The map is prescanned to mark of movement zones according to certain +** movement characteristics. This enum specifies those characteristics +** and movement zones kept track of. +*/ +typedef enum MZoneType : unsigned char { + MZONE_NORMAL, // Normal terrestrial objects (can't crush walls). + MZONE_CRUSHER, // Can crush crushable wall types. + MZONE_DESTROYER, // Can destroy walls. + MZONE_WATER, // Water based objects. + + MZONE_COUNT, + MZONE_FIRST=0 +} MZoneType; + +#define MZONEF_NORMAL (1<(static_cast(a) | static_cast(b));} + +inline ThreatType operator&(ThreatType a, ThreatType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ThreatType operator~(ThreatType a) +{return static_cast(~static_cast(a));} + + +#define THREAT_GROUND (THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_INFANTRY) + + +/********************************************************************** +** These return values are used when determine if firing is legal. +** By examining this value it can be determined what should be done +** to fix the reason why firing wasn't allowed. +*/ +typedef enum FireErrorType : unsigned char{ + FIRE_OK, // Weapon is allowed to fire. + FIRE_AMMO, // No ammo available to fire? + FIRE_FACING, // Not correctly facing target? + FIRE_REARM, // It is busy rearming? + FIRE_ROTATING, // Is it in process of rotating? + FIRE_ILLEGAL, // Is it targeting something illegal? + FIRE_CANT, // Is this unit one that cannot fire anything? + FIRE_MOVING, // Is it moving and not allowed to fire while moving? + FIRE_RANGE, // Is the target out of range? + FIRE_CLOAKED, // Is the shooter currently cloaked? + FIRE_BUSY // Is shooter currently doing something else? +} FireErrorType; + + +/********************************************************************** +** If an object can cloak, then it will be in one of these states. +** For objects that cannot cloak, they will always be in the +** UNCLOAKED state. This state controls how the object transitions between +** cloaked and uncloaked conditions. +*/ +typedef enum CloakType : unsigned char { + UNCLOAKED, // Completely visible (normal state). + CLOAKING, // In process of cloaking. + CLOAKED, // Completely cloaked (invisible). + UNCLOAKING // In process of uncloaking. +} CloakType; + + +/********************************************************************** +** For units that are cloaking, these value specify the visual character +** of the object. +*/ +typedef enum VisualType : unsigned char{ + VISUAL_NORMAL, // Completely visible -- normal. + VISUAL_INDISTINCT, // The edges shimmer and become indistinct. + VISUAL_DARKEN, // Color and texture is muted along with shimmering. + VISUAL_SHADOWY, // Body is translucent in addition to shimmering. + VISUAL_RIPPLE, // Just a ripple (true predator effect). + VISUAL_HIDDEN // Nothing at all is visible. +} VisualType; + + +/********************************************************************** +** These missions enumerate the various state machines that can apply to +** a game object. Only one of these state machines is active at any one +** time. +*/ +typedef enum MissionType : char { + MISSION_NONE=-1, + + MISSION_SLEEP, // Do nothing whatsoever. + MISSION_ATTACK, // Attack nearest enemy. + MISSION_MOVE, // Guard location or unit. + MISSION_QMOVE, // A queue list movement mission. + MISSION_RETREAT, // Return home for R & R. + MISSION_GUARD, // Stay still. + MISSION_STICKY, // Stay still -- never recruit. + MISSION_ENTER, // Move into object cooperatively. + MISSION_CAPTURE, // Move into in order to capture. + MISSION_HARVEST, // Hunt for and collect nearby Tiberium. + MISSION_GUARD_AREA, // Active guard of area. + MISSION_RETURN, // Head back to refinery. + MISSION_STOP, // Sit still. + MISSION_AMBUSH, // Wait until discovered. + MISSION_HUNT, // Active search and destroy. + MISSION_UNLOAD, // Search for and deliver cargo. + MISSION_SABOTAGE, // Move into in order to destroy. + MISSION_CONSTRUCTION, // Building buildup operation. + MISSION_DECONSTRUCTION, // Building builddown operation. + MISSION_REPAIR, // Repair process mission. + MISSION_RESCUE, + MISSION_MISSILE, + MISSION_HARMLESS, // Sit around and don't appear like a threat. + + MISSION_COUNT, + MISSION_FIRST=0 +} MissionType; + + +/********************************************************************** +** These are the enumerated animation sequences that a building may +** be processing. These serve to control the way that a building +** appears. +*/ +typedef enum BStateType : char { + BSTATE_NONE=-1, + BSTATE_CONSTRUCTION, // Construction animation. + BSTATE_IDLE, // Idle animation. + BSTATE_ACTIVE, // Animation when building is "doing its thing". + BSTATE_FULL, // Special alternate active state. + BSTATE_AUX1, // Auxiliary animation. + BSTATE_AUX2, // Auxiliary animation. + + BSTATE_COUNT +} BStateType; + + +/********************************************************************** +** Whenever a unit is selected and a click occurs over another object +** or terrain element, there is some action to initiate. This specifies +** the different types of actions possible. This also controls how the +** mouse cursor looks when "hovering" over the spot that clicking would +** occur at. +*/ +typedef enum ActionType : unsigned char { + ACTION_NONE, // Either undefined action or "do nothing". + ACTION_MOVE, // Can move there or at least try to. + ACTION_NOMOVE, // Special case for movable object, but illegal mouse position. + ACTION_ENTER, // Special case for infantry->APC or vehicle->Repair facility. + ACTION_SELF, // Self select special case. + ACTION_ATTACK, // Can attack or fire upon it in some fashion. + ACTION_HARVEST, // Special harvest mode. + ACTION_SELECT, // Would change selection to specified object. + ACTION_TOGGLE_SELECT,// Toggles select state of the object. + ACTION_CAPTURE, // The unit will try to capture the object. + ACTION_REPAIR, // The target object should be repaired. + ACTION_SELL, // The target building should be sold back. + ACTION_SELL_UNIT, // The target unit should be sold back. + ACTION_NO_SELL, // No sell or no repair. + ACTION_NO_REPAIR, // No sell or no repair. + ACTION_SABOTAGE, // The unit will try to sabotage/destroy the object. + ACTION_PARA_BOMB, // Parachute bomb strike. + ACTION_PARA_INFANTRY,// Parachute infantry strike. + ACTION_PARA_SABOTEUR,// Parachute saboteur strike. + ACTION_NUKE_BOMB, // That target object should be blasted. + ACTION_AIR_STRIKE, // That target object should be blasted. + ACTION_CHRONOSPHERE, // That target object should be teleported. + ACTION_CHRONO2, // Teleport it to the given coordinates now. + ACTION_IRON_CURTAIN, // That target object should be invulnerable. + ACTION_SPY_MISSION, // Photo recon mission. + ACTION_GUARD_AREA, // Guard the area/object clicked on. + ACTION_HEAL, // Heal the infantryman clicked on. + ACTION_DAMAGE, // Enter and damage building. + ACTION_GREPAIR, // Enter and complete repair building. + ACTION_NO_DEPLOY, + ACTION_NO_ENTER, + ACTION_NO_GREPAIR, + ACTION_TOGGLE_PRIMARY, // Toggle the primary status of the factory. + + ACTION_COUNT +} ActionType; + + +/********************************************************************** +** When a unit gets damaged, the result of the damage is returned as +** this type. It can range from no damage taken to complete destruction. +*/ +typedef enum ResultType : unsigned char { + RESULT_NONE, // No damage was taken by the target. + RESULT_LIGHT, // Some damage was taken, but no state change occurred. + RESULT_HALF, // Damaged to below half strength (only returned on transition). + RESULT_MAJOR, // Damaged down to 1 hit point. + RESULT_DESTROYED // Damaged to complete destruction. +} ResultType; + + +#ifdef OBSOLETE +/********************************************************************** +** These are the special concrete control defines. They enumerate the +** sequence order of the concrete icons in the concrete art file. +*/ +// DEBUG === convert this to be zero based so that a nulled cell is the +// default cell. +enum ConcreteEnum { + C_NONE=-1, + C_LEFT=0, + C_RIGHT=1, + C_RIGHT_UPDOWN=2, + C_LEFT_UPDOWN=3, + C_UP_RIGHT=4, + C_UP_LEFT=5, + C_DOWN_RIGHT=6, + C_DOWN_LEFT=7, + C_RIGHT_DOWN=8, + C_LEFT_DOWN=9, + C_RIGHT_UP=10, + C_LEFT_UP=11, + C_UPDOWN_RIGHT=12, + C_UPDOWN_LEFT=13 +}; +#endif + + +/********************************************************************** +** Units that move can move at different speeds. These enumerate the +** different speeds that a unit can move. +*/ +typedef enum MPHType : unsigned char { + MPH_IMMOBILE=0, + MPH_VERY_SLOW=5, // 2 + MPH_KINDA_SLOW=6, // 3 + MPH_SLOW=8, // 4 + MPH_SLOW_ISH=10, // 5 + MPH_MEDIUM_SLOW=12, // 6 + MPH_MEDIUM=18, // 9 + MPH_MEDIUM_FAST=30, // 12 + MPH_MEDIUM_FASTER=35, // 14 + MPH_FAST=40, // 16 + MPH_ROCKET=60, // 24 + MPH_VERY_FAST=100, // 40 + MPH_LIGHT_SPEED=255 // 100 +} MPHType; + + +/********************************************************************** +** The houses that can be played are listed here. Each has their own +** personality and strengths. +*/ +typedef enum HousesType : char { + HOUSE_NONE=-1, + HOUSE_SPAIN, // Gold (unremapped) + HOUSE_GREECE, // LtBlue + HOUSE_USSR, // Red + HOUSE_ENGLAND, // Green + HOUSE_UKRAINE, // Orange + HOUSE_GERMANY, // Grey + HOUSE_FRANCE, // Blue + HOUSE_TURKEY, // Brown + HOUSE_GOOD, // Global Defense Initiative + HOUSE_BAD, // Brotherhood of Nod + HOUSE_NEUTRAL, // Civilians + HOUSE_JP, // Disaster Containment Team + HOUSE_MULTI1, // Multi-Player house #1 + HOUSE_MULTI2, // Multi-Player house #2 + HOUSE_MULTI3, // Multi-Player house #3 + HOUSE_MULTI4, // Multi-Player house #4 + HOUSE_MULTI5, // Multi-Player house #5 + HOUSE_MULTI6, // Multi-Player house #6 + HOUSE_MULTI7, // Multi-Player house #7 + HOUSE_MULTI8, // Multi-Player house #8 + HOUSE_COUNT, + HOUSE_FIRST=0 +} HousesType; + +//inline HousesType operator++(HousesType &, int) {return (HousesType)(int; +inline HousesType operator++(HousesType &ht) { ht = (HousesType)(((int)ht)+1); return ht; } + +#define HOUSEF_ALLIES (HOUSEF_ENGLAND|HOUSEF_SPAIN|HOUSEF_GREECE|HOUSEF_GERMANY|HOUSEF_FRANCE|HOUSEF_TURKEY|HOUSEF_GOOD) +#define HOUSEF_SOVIET (HOUSEF_USSR|HOUSEF_UKRAINE|HOUSEF_BAD) +#define HOUSEF_OTHERS (HOUSEF_NEUTRAL|HOUSEF_JP|HOUSEF_MULTI1|HOUSEF_MULTI2|HOUSEF_MULTI3|HOUSEF_MULTI4|HOUSEF_MULTI5|HOUSEF_MULTI6|HOUSEF_MULTI7|HOUSEF_MULTI8) +#define HOUSEF_NONE 0 + +#define HOUSEF_ENGLAND (1L< 2 players. +*/ +typedef enum ScenarioPlayerEnum : char +{ + SCEN_PLAYER_NONE = -1, + SCEN_PLAYER_SPAIN, + SCEN_PLAYER_GREECE, + SCEN_PLAYER_USSR, + SCEN_PLAYER_JP, + SCEN_PLAYER_2PLAYER, + SCEN_PLAYER_MPLAYER, + SCEN_PLAYER_COUNT, + SCEN_PLAYER_FIRST = 0 +} ScenarioPlayerType; + +inline ScenarioPlayerType operator++(ScenarioPlayerType &, int); + + +/********************************************************************** +** These are the directional parameters for a scenario. +*/ +typedef enum ScenarioDirEnum : char +{ + SCEN_DIR_NONE = -1, + SCEN_DIR_EAST, + SCEN_DIR_WEST, + SCEN_DIR_COUNT, + SCEN_DIR_FIRST = 0 +} ScenarioDirType; + +inline ScenarioDirType operator++(ScenarioDirType &, int); + + +/********************************************************************** +** These are the random variations of a scenario. +*/ +typedef enum ScenarioVarEnum : char +{ + SCEN_VAR_NONE = -1, + SCEN_VAR_A, + SCEN_VAR_B, + SCEN_VAR_C, + SCEN_VAR_D, + SCEN_VAR_COUNT, // comes before the Lose value! + SCEN_VAR_LOSE, + SCEN_VAR_FIRST = 0 +} ScenarioVarType; + +//inline ScenarioVarType operator++(ScenarioVarType &, int); +inline ScenarioVarType operator++(ScenarioVarType &n) { n = (ScenarioVarType)(((int)n) + 1); return n; } + + +/********************************************************************** +** The objects to be drawn on the map are grouped into layers. These +** enumerated values specify those layers. The ground layer is sorted +** from back to front. +*/ +typedef enum LayerType : char { + LAYER_NONE=-1, + LAYER_SURFACE, // Flat on the ground (no sorting or apparent vertical height). + LAYER_GROUND, // Touching the ground type object (units & buildings). + LAYER_AIR, // Flying above the ground (explosions & flames). + LAYER_TOP, // Topmost layer (aircraft & bullets). + + LAYER_COUNT, + LAYER_FIRST=0 +} LayerType; + +//PG inline LayerType operator++(LayerType &, int); +inline LayerType operator++(LayerType &n) { n = (LayerType)(((int)n) + 1); return n; } + + +/********************************************************************** +** This enumerates the various bullet types. These types specify bullet's +** visual and explosive characteristics. +*/ +typedef enum BulletType : char { + BULLET_NONE=-1, + + BULLET_INVISIBLE, + BULLET_CANNON, + BULLET_ACK, + BULLET_TORPEDO, + BULLET_FROG, + BULLET_HEAT_SEEKER, + BULLET_LASER_GUIDED, + BULLET_LOBBED, + BULLET_BOMBLET, + BULLET_BALLISTIC, + BULLET_PARACHUTE, + BULLET_FIREBALL, + BULLET_DOG, + BULLET_CATAPULT, + BULLET_AAMISSILE, + BULLET_GPS_SATELLITE, + BULLET_NUKE_UP, + BULLET_NUKE_DOWN, + + BULLET_COUNT, + BULLET_FIRST=0 +} BulletType; + +//PG inline BulletType operator++(BulletType &, int); +inline BulletType operator++(BulletType &n) { n = (BulletType)(((int)n) + 1); return n; } + + +/********************************************************************** +** All game buildings (structures) are enumerated here. This includes +** civilian structures as well. +*/ +typedef enum StructType : char { + STRUCT_NONE=-1, + STRUCT_ADVANCED_TECH, + STRUCT_IRON_CURTAIN, + STRUCT_WEAP, + STRUCT_CHRONOSPHERE, + STRUCT_PILLBOX, + STRUCT_CAMOPILLBOX, + STRUCT_RADAR, + STRUCT_GAP, + STRUCT_TURRET, + STRUCT_AAGUN, + STRUCT_FLAME_TURRET, + STRUCT_CONST, + STRUCT_REFINERY, + STRUCT_STORAGE, + STRUCT_HELIPAD, + STRUCT_SAM, + STRUCT_AIRSTRIP, + STRUCT_POWER, + STRUCT_ADVANCED_POWER, + STRUCT_SOVIET_TECH, + STRUCT_HOSPITAL, + STRUCT_BARRACKS, + STRUCT_TENT, + STRUCT_KENNEL, + STRUCT_REPAIR, + STRUCT_BIO_LAB, + STRUCT_MISSION, + STRUCT_SHIP_YARD, + STRUCT_SUB_PEN, + STRUCT_MSLO, + STRUCT_FORWARD_COM, + STRUCT_TESLA, + + /* + ** All buildings that are never used as a prerequisite + ** for construction, follow this point. Typically, this is + ** limited to civilian structures. Also, the following + ** buildings are NEVER used in the availability bit field + ** record that each house maintains. i.e., STRUCTF_???? + ** bit checking will never occur with the following + ** building types. + */ + STRUCT_FAKEWEAP, + STRUCT_FAKECONST, + STRUCT_FAKE_YARD, + STRUCT_FAKE_PEN, + STRUCT_FAKE_RADAR, + + STRUCT_SANDBAG_WALL, + STRUCT_CYCLONE_WALL, + STRUCT_BRICK_WALL, + STRUCT_BARBWIRE_WALL, + STRUCT_WOOD_WALL, + STRUCT_FENCE, + + STRUCT_AVMINE, + STRUCT_APMINE, + STRUCT_V01, + STRUCT_V02, + STRUCT_V03, + STRUCT_V04, + STRUCT_V05, + STRUCT_V06, + STRUCT_V07, + STRUCT_V08, + STRUCT_V09, + STRUCT_V10, + STRUCT_V11, + STRUCT_V12, + STRUCT_V13, + STRUCT_V14, + STRUCT_V15, + STRUCT_V16, + STRUCT_V17, + STRUCT_V18, + STRUCT_PUMP, + STRUCT_V20, + STRUCT_V21, + STRUCT_V22, + STRUCT_V23, + STRUCT_V24, + STRUCT_V25, + STRUCT_V26, + STRUCT_V27, + STRUCT_V28, + STRUCT_V29, + STRUCT_V30, + STRUCT_V31, + STRUCT_V32, + STRUCT_V33, + STRUCT_V34, + STRUCT_V35, + STRUCT_V36, + STRUCT_V37, + STRUCT_BARREL, + STRUCT_BARREL3, + +#ifdef FIXIT_ANTS + STRUCT_QUEEN, + STRUCT_LARVA1, + STRUCT_LARVA2, +#endif + +#ifdef RA_AF + STRUCT_AFV01, +#endif + STRUCT_COUNT, + STRUCT_FIRST=0 +} StructType; + +//PG inline StructType operator++(StructType &, int); +inline StructType operator++(StructType &n) { n = (StructType)(((int)n) + 1); return n; } + +#define STRUCTF_NONE 0L +#define STRUCTF_ADVANCED_TECH (1L << STRUCT_ADVANCED_TECH) +#define STRUCTF_IRON_CURTAIN (1L << STRUCT_IRON_CURTAIN) +#define STRUCTF_WEAP (1L << STRUCT_WEAP) +#define STRUCTF_CHRONOSPHERE (1L << STRUCT_CHRONOSPHERE) +#define STRUCTF_PILLBOX (1L << STRUCT_PILLBOX) +#define STRUCTF_CAMOPILLBOX (1L << STRUCT_CAMOPILLBOX) +#define STRUCTF_RADAR (1L << STRUCT_RADAR) +#define STRUCTF_GAP (1L << STRUCT_GAP) +#define STRUCTF_TURRET (1L << STRUCT_TURRET) +#define STRUCTF_AAGUN (1L << STRUCT_AAGUN) +#define STRUCTF_FLAME_TURRET (1L << STRUCT_FLAME_TURRET) +#define STRUCTF_CONST (1L << STRUCT_CONST) +#define STRUCTF_REFINERY (1L << STRUCT_REFINERY) +#define STRUCTF_STORAGE (1L << STRUCT_STORAGE) +#define STRUCTF_HELIPAD (1L << STRUCT_HELIPAD) +#define STRUCTF_SAM (1L << STRUCT_SAM) +#define STRUCTF_AIRSTRIP (1L << STRUCT_AIRSTRIP) +#define STRUCTF_POWER (1L << STRUCT_POWER) +#define STRUCTF_ADVANCED_POWER (1L << STRUCT_ADVANCED_POWER) +#define STRUCTF_SOVIET_TECH (1L << STRUCT_SOVIET_TECH) +#define STRUCTF_HOSPITAL (1L << STRUCT_HOSPITAL) +#define STRUCTF_BARRACKS (1L << STRUCT_BARRACKS) +#define STRUCTF_TENT (1L << STRUCT_TENT) +#define STRUCTF_KENNEL (1L << STRUCT_KENNEL) +#define STRUCTF_REPAIR (1L << STRUCT_REPAIR) +#define STRUCTF_BIO_LAB (1L << STRUCT_BIO_LAB) +#define STRUCTF_MISSION (1L << STRUCT_MISSION) +#define STRUCTF_SHIP_YARD (1L << STRUCT_SHIP_YARD) +#define STRUCTF_SUB_PEN (1L << STRUCT_SUB_PEN) +#define STRUCTF_MSLO (1L << STRUCT_MSLO) +#define STRUCTF_FAKECONST (1L << STRUCT_FAKECONST) +#define STRUCTF_FAKEWEAP (1L << STRUCT_FAKEWEAP) + + +/********************************************************************** +** The overlays are enumerated here. An overlay functions similarly to +** a transparent icon. It is placed over the terrain but usually falls +** "under" buildings, trees, and units. +*/ +typedef enum OverlayType : char { + OVERLAY_NONE=-1, + OVERLAY_SANDBAG_WALL, // Piled sandbags. + OVERLAY_CYCLONE_WALL, // Chain-link fence. + OVERLAY_BRICK_WALL, // Solid concrete wall. + OVERLAY_BARBWIRE_WALL, // Barbed-wire wall. + OVERLAY_WOOD_WALL, // Wooden fence. + OVERLAY_GOLD1, + OVERLAY_GOLD2, + OVERLAY_GOLD3, + OVERLAY_GOLD4, + OVERLAY_GEMS1, + OVERLAY_GEMS2, + OVERLAY_GEMS3, + OVERLAY_GEMS4, + OVERLAY_V12, // Haystacks + OVERLAY_V13, // Haystack + OVERLAY_V14, // Wheat field + OVERLAY_V15, // Fallow field + OVERLAY_V16, // Corn field + OVERLAY_V17, // Celery field + OVERLAY_V18, // Potato field + OVERLAY_FLAG_SPOT, // Flag start location. + OVERLAY_WOOD_CRATE, // Wooden goodie crate. + OVERLAY_STEEL_CRATE, // Steel goodie crate. + OVERLAY_FENCE, // New fangled fence. + OVERLAY_WATER_CRATE, // Water goodie crate. + + OVERLAY_COUNT, + OVERLAY_FIRST=0 +} OverlayType; + +//PG inline OverlayType operator++(OverlayType &, int); + + +/********************************************************************** +** This specifies the infantry in the game. The "E" designation is +** similar to the army classification of enlisted soldiers. +*/ +typedef enum InfantryType : char { + INFANTRY_NONE=-1, + INFANTRY_E1, // Mini-gun armed. + INFANTRY_E2, // Grenade thrower. + INFANTRY_E3, // Rocket launcher. + INFANTRY_E4, // Flame thrower equipped. + INFANTRY_RENOVATOR, // Engineer. + INFANTRY_TANYA, // Saboteur. + INFANTRY_SPY, // Spy. + INFANTRY_THIEF, // Thief. + INFANTRY_MEDIC, // Field Medic. + INFANTRY_GENERAL, // Field Marshal. + INFANTRY_DOG, // Soviet attack dog + + INFANTRY_C1, // Civilian + INFANTRY_C2, // Civilian + INFANTRY_C3, // Civilian + INFANTRY_C4, // Civilian + INFANTRY_C5, // Civilian + INFANTRY_C6, // Civilian + INFANTRY_C7, // Civilian + INFANTRY_C8, // Civilian + INFANTRY_C9, // Civilian + INFANTRY_C10, // Nikumba + INFANTRY_EINSTEIN, // Einstein + INFANTRY_DELPHI, // Agent "Delphi" + INFANTRY_CHAN, // Dr. Chan + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CounterStrike II only! + INFANTRY_SHOCK, // Shock Trooper + INFANTRY_MECHANIC, +#endif + + INFANTRY_COUNT, + INFANTRY_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + INFANTRY_RA_COUNT = INFANTRY_SHOCK +#endif +} InfantryType; + +#define INFANTRYF_DOG (1L << INFANTRY_DOG) + +//PG inline InfantryType operator++(InfantryType &, int); + + +/********************************************************************** +** The game units are enumerated here. These include not only traditional +** vehicles, but also hovercraft and gunboats. +*/ +typedef enum UnitType : char { + UNIT_NONE=-1, + UNIT_HTANK, // Mammoth tank. + UNIT_MTANK, // Heavy tank. + UNIT_MTANK2, // Medium tank. + UNIT_LTANK, // Light tank ('Bradly'). + UNIT_APC, // APC. + UNIT_MINELAYER, // Mine-laying vehicle. + UNIT_JEEP, // 4x4 jeep replacement. + UNIT_HARVESTER, // Resource gathering vehicle. + UNIT_ARTY, // Artillery unit. + UNIT_MRJ, // Mobile Radar Jammer. + UNIT_MGG, // Mobile Gap Generator + UNIT_MCV, // Mobile construction vehicle. + UNIT_V2_LAUNCHER, // V2 rocket launcher. + UNIT_TRUCK, // Convoy truck + +#ifdef FIXIT_ANTS + UNIT_ANT1, // Warrior ant. + UNIT_ANT2, // Warrior ant. + UNIT_ANT3, // Warrior ant. +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// CS II ONLY! + UNIT_CHRONOTANK, // Chrono-shifting tank + UNIT_TESLATANK, // Tesla-equipped tank + UNIT_MAD, // Timequake tank + UNIT_DEMOTRUCK, // Jihad truck +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + UNIT_PHASE, // cloaking APC for special missions +#endif +#endif + + UNIT_COUNT, + UNIT_FIRST=0 +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +, + UNIT_RA_COUNT = UNIT_CHRONOTANK +#endif +} UnitType; + +//PG inline UnitType operator++(UnitType &, int); + +#define UNITF_HTANK (1L<(static_cast(a) | static_cast(b));} + +inline TextPrintType operator&(TextPrintType a, TextPrintType b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline TextPrintType operator~(TextPrintType a) +{return static_cast(~static_cast(a));} + +// Standard button text print flags. +#define TPF_BUTTON (TPF_CENTER|TPF_6PT_GRAD|TPF_NOSHADOW) +#define TPF_EBUTTON (TPF_CENTER|TPF_EFNT|TPF_NOSHADOW) +#define TPF_TEXT (TPF_6PT_GRAD|TPF_NOSHADOW) + + +/********************************************************************** +** These control the maximum number of objects in the game. Make sure that these +** maximums never exceed the maximum value for the "ID" element in the +** object class. +*/ +#define BUILDING_MAX 500 // Lasts for hours. +#define HOUSE_MAX (HOUSE_COUNT+1) // Lasts entire scenario. +#define INFANTRY_MAX 500 // Lasts for minutes. +#define UNIT_MAX 500 // Lasts for minutes. +#define VESSEL_MAX 100 // Lasts for minutes. +#define TEAMTYPE_MAX 60 // Lasts forever. + +// Save filename description. +#define DESCRIP_MAX 44 // 40 chars + CR + LF + CTRL-Z + NULL + +#define CONQUER_PATH_MAX 12 // Number of cells to look ahead for movement. + +#define EACH_INFANTRY_MAX (INFANTRY_MAX/5) // Default maximum any one player can have. +#define EACH_UNIT_MAX (UNIT_MAX/5) // Default maximum any one player can have. +#define EACH_BUILDING_MAX (BUILDING_MAX/5) // Default maximum any one player can build. +#define EACH_VESSEL_MAX (VESSEL_MAX/5) // Default maximum any one player can build. + + +/********************************************************************** +** Terrain can be of these different classes. At any point in the game +** a particular piece of ground must fall under one of these classifications. +** This is true, even if it is undergoing a temporary transition. +*/ +typedef enum LandType : char { + LAND_CLEAR, // "Clear" terrain. + LAND_ROAD, // Road terrain. + LAND_WATER, // Water. + LAND_ROCK, // Impassable rock. + LAND_WALL, // Wall (blocks movement). + LAND_TIBERIUM, // Tiberium field. + LAND_BEACH, // Beach terrain. + LAND_ROUGH, // Rocky terrain. + LAND_RIVER, // Rocky riverbed. + + LAND_COUNT, + LAND_NONE=-1, + LAND_FIRST=0 +} LandType; + + +/********************************************************************** +** The theaters of operation are as follows. +*/ +typedef enum TheaterType : char { + THEATER_NONE=-1, + THEATER_TEMPERATE, + THEATER_SNOW, + THEATER_INTERIOR, + + THEATER_COUNT, + THEATER_FIRST=0 +} TheaterType; + +//inline TheaterType operator++(TheaterType &, int); +inline TheaterType operator++(TheaterType &n) { n = (TheaterType)(((int)n)+1); return n; } + + +#define THEATERF_TEMPERATE (1<id) + + +#define MAX_LOG_LEVEL 10 + +// Maximum number of multi players possible. +#define MAX_PLAYERS 8 // max # of players we can have + +// Maximum number of teams +#define MAX_TEAMS 10 + +#endif \ No newline at end of file diff --git a/code/redalert/descdlg.cpp b/code/redalert/descdlg.cpp new file mode 100644 index 0000000..863ca69 --- /dev/null +++ b/code/redalert/descdlg.cpp @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DESCDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DescriptionClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "descdlg.h" + + +/*********************************************************************************************** + * DescriptionClass::Process -- Handles all the options graphic interface. * + * * + * This dialog uses an edit box to "fill-out" a description. * + * * + * INPUT: char *string - return answer here. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void DescriptionClass::Process(char * string) +{ + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, BUTTON_Y); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, 0, BUTTON_Y); + + cancelbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3)*2; + optionsbtn.X = OPTION_X + ((OPTION_WIDTH - optionsbtn.Width)/3); + optionsbtn.Add_Tail(cancelbtn); + + EditClass edit( + BUTTON_EDIT, + string, + 31, + TPF_6PT_GRAD, + 0, + EDIT_Y, + EDIT_W); + + edit.Set_Focus(); + edit.X = OPTION_X + (OPTION_WIDTH - edit.Width)/2, + optionsbtn.Add_Tail(edit); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT, GadgetClass::LEFTPRESS); + optionsbtn.Add_Tail(dialog); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, 320, 200, GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + optionsbtn.Add_Tail(background); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + Window_Hide_Mouse(WINDOW_EDITOR); + + /* + ** Draw the background + */ + Window_Box (WINDOW_EDITOR, BOXSTYLE_BORDER); // has border, raised up + Draw_Caption(TXT_MISSION_DESCRIPTION, OPTION_X, OPTION_Y, OPTION_WIDTH); + + /* + ** Draw the titles + */ + optionsbtn.Draw_All(); + Window_Show_Mouse(); + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = optionsbtn.Input(); + + /* + ** Process Input + */ + switch (input) { + + case KN_RETURN: + case KeyNumType(BUTTON_OPTIONS|KN_BUTTON): + strtrim(string); + process = false; + break; + + case KN_ESC: + case KeyNumType(BUTTON_CANCEL|KN_BUTTON): + string[0]= NULL; + strtrim(string); + process = false; + break; + + case KeyNumType(BUTTON_EDIT|KN_BUTTON): + break; + + default: + break; + } + } +} + diff --git a/code/redalert/descdlg.h b/code/redalert/descdlg.h new file mode 100644 index 0000000..b3b83fd --- /dev/null +++ b/code/redalert/descdlg.h @@ -0,0 +1,66 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DESCDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DESCDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 26, 1995 * + * * + * Last Update : Jan 26, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------*/ + +#ifndef DESCDLG_H +#define DESCDLG_H + +#include "gadget.h" + +class DescriptionClass +{ + private: + + enum DescriptionClassEnum { + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2) & ~7), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+32, // Title's x pos + TEXT_Y=OPTION_Y+32, // Add 11 for each following line + BUTTON_OPTIONS=1, // Button number for "Ok" + BUTTON_CANCEL, + BUTTON_EDIT, + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + EDIT_Y =OPTION_Y+50, + EDIT_W =180 //204, + }; + + public: + DescriptionClass(void) {}; + void Process(char *string); +}; + +#endif + + diff --git a/code/redalert/dial8.cpp b/code/redalert/dial8.cpp new file mode 100644 index 0000000..cf09d61 --- /dev/null +++ b/code/redalert/dial8.cpp @@ -0,0 +1,316 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIAL8.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Dial8Class::Action -- action routine for Dial8Class * + * Dial8Class::Dial8Class -- constructor for the facing dial * + * Dial8Class::Draw_Me -- render routine for Dial8Class * + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * Dial8Class::Dial8Class -- constructor for the facing dial * + * * + * INPUT: * + * id button ID * + * x,y,w,h dimensions in window-relative pixels * + * dir numerical initial facing value (0-255); this is the * + * value returned by WWLIB Desired_Facing8() * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +Dial8Class::Dial8Class(int id, int x, int y, int w, int h, DirType dir) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTHELD | LEFTRELEASE, true) +{ + /* + ** Center coordinates. + */ + FaceX = X + (Width / 2); + FaceY = Y + (Height / 2); + + /* + ** Init directions. + */ + Direction = dir; // 0 - 255 + Facing = Dir_Facing(Direction); // 0 - 7 + OldFacing = Facing; // 0 - 7 + + /* + ** Compute the drawing dimensions: a 45-degree angle intersects a unity- + ** radius circle at (.707,.707). Make the decorations 8/10 of the radius, + ** and the line extend to 6/10 of the radius. Use Width/2 for x-radius, + ** Height/2 for y-radius. + */ + FacePoint[0][0] = FaceX; + FacePoint[0][1] = FaceY - (h * 8 / 2) / 10; + + FacePoint[1][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[1][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FacePoint[2][0] = FaceX + (w * 8 / 2) / 10; + FacePoint[2][1] = FaceY; + + FacePoint[3][0] = FaceX + (w * 7 * 8 / 2) / 100; + FacePoint[3][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[4][0] = FaceX; + FacePoint[4][1] = FaceY + (h * 8 / 2) / 10; + + FacePoint[5][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[5][1] = FaceY + (h * 7 * 8 / 2) / 100; + + FacePoint[6][0] = FaceX - (w * 8 / 2) / 10; + FacePoint[6][1] = FaceY; + + FacePoint[7][0] = FaceX - (w * 7 * 8 / 2) / 100; + FacePoint[7][1] = FaceY - (h * 7 * 8 / 2) / 100; + + FaceLine[0][0] = FaceX; + FaceLine[0][1] = FaceY - (h * 6 / 2) / 10; + + FaceLine[1][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[1][1] = FaceY - (h * 7 * 6 / 2) / 100; + + FaceLine[2][0] = FaceX + (w * 6 / 2) / 10; + FaceLine[2][1] = FaceY; + + FaceLine[3][0] = FaceX + (w * 7 * 6 / 2) / 100; + FaceLine[3][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[4][0] = FaceX; + FaceLine[4][1] = FaceY + (h * 6 / 2) / 10; + + FaceLine[5][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[5][1] = FaceY + (h * 7 * 6 / 2) / 100; + + FaceLine[6][0] = FaceX - (w * 6 / 2) / 10; + FaceLine[6][1] = FaceY; + + FaceLine[7][0] = FaceX - (w * 7 * 6 / 2) / 100; + FaceLine[7][1] = FaceY - (h * 7 * 6 / 2) / 100; +} + + +/*************************************************************************** + * Dial8Class::Action -- activation function for Dial8Class * + * * + * INPUT: * + * flags the reason we're being called * + * key the KN_number that was pressed * + * * + * OUTPUT: * + * true = event was processed, false = event not processed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Action(unsigned flags, KeyNumType &key) +{ + static int is_sel = 0; + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + if (flags & LEFTPRESS) { + is_sel = 1; + } + + /* + ** If left mouse is clicked or held, and the dial has changed its direction, + ** invoke the parent Action routine: + ** GadgetClass::Action handles Sticky processing, & sets IsToRepaint if any + ** flag bits are set. + ** ControlClass::Action handles Peer_To_Peer notification, and substitutes + ** 'key' with the button ID if any flags are set, or 0 if no flags are set + */ + if (flags & LEFTPRESS || ((flags & LEFTHELD) && is_sel)) { + /* + ** Get new dial position (0-255) + */ + Direction = (DirType)Desired_Facing8(FaceX, FaceY, Get_Mouse_X(), Get_Mouse_Y()); + + /* + ** Convert to Facing value (0-7). + */ + Facing = Dir_Facing(Direction); + + /* + ** If it's moved, redraw. + */ + if (Facing!=OldFacing) { + OldFacing = Facing; + ControlClass::Action(flags, key); + return(true); + + } else { + + /* + ** Dial hasn't moved; kill the event & return + */ + key = KN_NONE; + ControlClass::Action(0, key); + return(true); + } + + } else { + + /* + ** Otherwise, no events have occurred; kill the event if it's a LEFTRELEASE, + ** and return + */ + if (flags & LEFTRELEASE) { + key = KN_NONE; + is_sel = 0; + } + return(ControlClass::Action(0, key)); + } +} + + +/*************************************************************************** + * Dial8Class::Draw_Me -- custom render routine for Dial8Class * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state* + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/06/1995 BR : Created. * + *=========================================================================*/ +int Dial8Class::Draw_Me(int forced) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Redraw if parent indicates a redraw is needed + */ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + + /* + ** Draw background & decorations. + */ + Draw_Box(X, Y, Width, Height, BOXSTYLE_DOWN, true); + for (int i=0; i<8; i++) { + Draw_Box(FacePoint[i][0] - 1, FacePoint[i][1] -1, 3, 3, BOXSTYLE_RAISED, false); + } + + /* + ** Draw the hand & its shadow. + */ + LogicPage->Draw_Line(FaceX+1, FaceY+1, FaceLine[Facing][0]+1, FaceLine[Facing][1]+1, scheme->Shadow); + LogicPage->Draw_Line(FaceX, FaceY, FaceLine[Facing][0], FaceLine[Facing][1], scheme->Highlight); + + /* + ** Restore the mouse. + */ + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * Dial8Class::Get_Direction -- retrieves direction (0-255) of dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * DirType dial is pointing to * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +DirType Dial8Class::Get_Direction(void) const +{ + return(Direction); +} + + +/*************************************************************************** + * Dial8Class::Set_Direction -- sets current direction (0-255) of dial * + * * + * INPUT: * + * DirType to set dial to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void Dial8Class::Set_Direction(DirType dir) +{ + Direction = dir; + Facing = Dir_Facing(Direction); + OldFacing = Facing; + Flag_To_Redraw(); +} diff --git a/code/redalert/dial8.h b/code/redalert/dial8.h new file mode 100644 index 0000000..61183de --- /dev/null +++ b/code/redalert/dial8.h @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIAL8.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIAL8.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DIAL8_H +#define DIAL8_H + +class Dial8Class : public ControlClass +{ + public: + /* + ** Constructor/Destructor + */ + Dial8Class(int id, int x, int y, int w, int h, DirType dir); + + /* + ** Get/Set the direction the dial is currently pointing + */ + DirType Get_Direction(void) const; + void Set_Direction(DirType dir); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + protected: + /* + ** Overloaded event processing routine + */ + virtual int Action(unsigned flags, KeyNumType &key); + + private: + int FaceX; // x-coord of center of face + int FaceY; // y-coord of center of face + int FacePoint[8][2]; // coords of the little dial decorations + int FaceLine[8][2]; // coords for drawing the dial hand + DirType Direction; // 0-255 numerical direction of dial + FacingType Facing; // numerical facing direction of dial (0 - 7) + FacingType OldFacing; // previous Facing value + +}; + +#endif + diff --git a/code/redalert/dialog.cpp b/code/redalert/dialog.cpp new file mode 100644 index 0000000..001ab27 --- /dev/null +++ b/code/redalert/dialog.cpp @@ -0,0 +1,1050 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DIALOG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DIALOG.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clip_Text_Print -- Prints text with clipping and support. * + * Dialog_Box -- draws a dialog background box * + * Display_Place_Building -- Displays the "place building" dialog box. * + * Display_Select_Target -- Displays the "choose target" prompt. * + * Display_Status -- Display the player scenario status box. * + * Draw_Box -- Displays a highlighted box. * + * Draw_Caption -- Draws a caption on a dialog box. * + * Fancy_Text_Print -- Prints text with a drop shadow. * + * Plain_Text_Print -- Prints text without using a color scheme * + * Redraw_Needed -- Determine if sidebar needs to be redrawn. * + * Render_Bar_Graph -- Renders a specified bargraph. * + * Simple_Text_Print -- Prints text with a drop shadow. * + * Window_Box -- Draws a fancy box over the specified window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "Image.h" +#include "defines.h" //VG 10/17/96 + +unsigned char * Font_Palette(int color); + + +/*********************************************************************************************** + * Dialog_Box -- draws a dialog background box * + * * + * INPUT: * + * x,y,w,h the usual * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/26/1995 BR : Created. * + * 07/31/1996 JLB : Uses shapes to draw the box. * + *=============================================================================================*/ +void Dialog_Box(int x, int y, int w, int h) +{ +// Try to expand the box a little taller and a little wider to make room for +// the dialog box graphics in the DOS version. +#ifndef WIN32 + x = max(0, x-4); + y = max(0, y-4); + w = min(w+8, 320-x); + h = min(h+8, 200-y); +#endif + + WindowList[WINDOW_PARTIAL][WINDOWX] = x; + WindowList[WINDOW_PARTIAL][WINDOWY] = y; + WindowList[WINDOW_PARTIAL][WINDOWWIDTH] = w; + WindowList[WINDOW_PARTIAL][WINDOWHEIGHT] = h; + + /* + ** Always draw to the hidpage and then blit forward. + */ +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(HidPage); +#endif + + /* + ** Draw the background block. + */ + int cx = w/2; + int cy = h/2; +// jmarshall + //void const * shapedata = MFCD::Retrieve("DD-BKGND.SHP"); +//#ifdef WIN32 +// CC_Draw_Shape(shapedata, 0, cx-312, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); +// CC_Draw_Shape(shapedata, 1, cx, cy-192, WINDOW_PARTIAL, SHAPE_WIN_REL); +// CC_Draw_Shape(shapedata, 2, cx-312, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); +// CC_Draw_Shape(shapedata, 3, cx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); +//#else +// CC_Draw_Shape(shapedata, 0, cx-156, cy-96, WINDOW_PARTIAL, SHAPE_WIN_REL); +//#endif + GL_SetClipRect(x, y, w, h); + if (g_inMainMenu) + { + Image_t* ddbkgrnd = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-red-0000.png"); + Image_t* ddbkgrnd_1 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-red-0001.png"); + Image_t* ddbkgrnd_2 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-red-0002.png"); + Image_t* ddbkgrnd_3 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-red-0003.png"); + GL_RenderImage(ddbkgrnd, x, y, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_1, x + 312, y, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_2, x, y + 192, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_3, x + 312, y + 192, ddbkgrnd->width, ddbkgrnd->height); + } + else + { + Image_t* ddbkgrnd = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-0000.png"); + Image_t* ddbkgrnd_1 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-0001.png"); + Image_t* ddbkgrnd_2 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-0002.png"); + Image_t* ddbkgrnd_3 = Image_LoadImage("ui/dd-bkgrnd/dd-bkgnd-0003.png"); + GL_RenderImage(ddbkgrnd, x, y, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_1, x + 312, y, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_2, x, y + 192, ddbkgrnd->width, ddbkgrnd->height); + GL_RenderImage(ddbkgrnd_3, x + 312, y + 192, ddbkgrnd->width, ddbkgrnd->height); + } +// jmarshall end + /* + ** Draw the side strips. + */ + //shapedata = MFCD::Retrieve("DD-EDGE.SHP"); + Image_t* ddedge = Image_LoadImage("ui/dd-edge/dd-edge-0000.png"); + Image_t* ddedge_1 = Image_LoadImage("ui/dd-edge/dd-edge-0001.png"); + for (int yy = 0; yy < h; yy += 6) { + //CC_Draw_Shape(shapedata, 0, 7*RESFACTOR, yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 1, w-((7+8)*RESFACTOR), yy, WINDOW_PARTIAL, SHAPE_WIN_REL); + GL_RenderImage(ddedge, x + (7 * RESFACTOR), y + yy, ddedge->width, ddedge->height); + GL_RenderImage(ddedge_1, x + (w - ((7 + 8) * RESFACTOR)), y + yy, ddedge->width, ddedge->height); + } + + /* + ** Draw the border bars. + */ + //shapedata = MFCD::Retrieve("DD-LEFT.SHP"); + Image_t* ddleft = Image_LoadImage("ui/dd-edge/dd-left-0000.png"); + GL_RenderImage(ddleft, x, y + (cy - 100 * RESFACTOR), ddleft->width * 0.5, ddleft->height * 0.5); + GL_RenderImage(ddleft, x, y + cy, ddleft->width * 0.5, ddleft->height * 0.5); + //CC_Draw_Shape(shapedata, 0, 0, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 0, 0, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + //shapedata = MFCD::Retrieve("DD-RIGHT.SHP"); + Image_t* ddright = Image_LoadImage("ui/dd-edge/dd-right-0000.png"); + int rightx = w - (7*RESFACTOR); +#ifndef WIN32 + rightx--; +#endif + GL_RenderImage(ddright, x + rightx, y + (cy - 100 * RESFACTOR), ddleft->width * 0.5, ddleft->height * 0.5); + GL_RenderImage(ddright, x + rightx, y + cy, ddleft->width * 0.5, ddleft->height * 0.5); + //CC_Draw_Shape(shapedata, 0, rightx, cy-100*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 0, rightx, cy, WINDOW_PARTIAL, SHAPE_WIN_REL); + + //shapedata = MFCD::Retrieve("DD-BOTM.SHP"); + Image_t* ddbotm = Image_LoadImage("ui/dd-edge/dd-botm-0000.png"); + GL_RenderImage(ddbotm, x + (cx - 160 * RESFACTOR), y + (h - 8 * RESFACTOR), ddbotm->width / 2, ddbotm->height / 2); + GL_RenderImage(ddbotm, x + (cx), y + (h - 8 * RESFACTOR), ddbotm->width / 2, ddbotm->height / 2); + //CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 0, cx, h-8*RESFACTOR, WINDOW_PARTIAL, SHAPE_WIN_REL); + + Image_t* ddtop = Image_LoadImage("ui/dd-edge/dd-top-0000.png"); + //CC_Draw_Shape(shapedata, 0, cx-160*RESFACTOR, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 0, cx, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + GL_RenderImage(ddtop, x + (cx - 160 * RESFACTOR), y, ddtop->width / 2, ddtop->height / 2); + GL_RenderImage(ddtop, x + (cx), y, ddtop->width / 2, ddtop->height / 2); + + /* + ** Draw the corner caps. + */ + //shapedata = MFCD::Retrieve("DD-CRNR.SHP"); + Image_t* ddcrnr0 = Image_LoadImage("ui/dd-edge/dd-crnr-0000.png"); + Image_t* ddcrnr1 = Image_LoadImage("ui/dd-edge/dd-crnr-0001.png"); + Image_t* ddcrnr2 = Image_LoadImage("ui/dd-edge/dd-crnr-0002.png"); + Image_t* ddcrnr3 = Image_LoadImage("ui/dd-edge/dd-crnr-0003.png"); + GL_RenderImage(ddcrnr0, x, y, 23, 24); + GL_RenderImage(ddcrnr1, x + (w - (12 * RESFACTOR - 1)), y, 23, 24); + GL_RenderImage(ddcrnr2, x, y + (h - (12 * RESFACTOR)), 23, 24); + GL_RenderImage(ddcrnr3, x + (w - (12 * RESFACTOR - 1)), y + (h - (12 * RESFACTOR)), 23, 24); + + //CC_Draw_Shape(shapedata, 0, 0, 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 1, w-(12*RESFACTOR-1), 0, WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 2, 0, h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + //CC_Draw_Shape(shapedata, 3, w-(12*RESFACTOR-1), h-(12*RESFACTOR), WINDOW_PARTIAL, SHAPE_WIN_REL); + +//#ifdef WIN32 +// WWMouse->Draw_Mouse(&HidPage); +// HidPage.Blit(SeenBuff, x, y, x, y, w, h, false); +// WWMouse->Erase_Mouse(&HidPage, FALSE); +//#else +//// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, Map.ShadowPage->Get_Buffer()); +// Hide_Mouse(); +// HidPage.Blit(SeenBuff); +// Show_Mouse(); +//// Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ((GraphicBufferClass*)Map.Shadow_Address())->Get_Buffer()); +//#endif + Set_Logic_Page(oldpage); + + GL_ResetClipRect(); +} + + +/*********************************************************************************************** + * Draw_Box -- Displays a highlighted box. * + * * + * This will draw a highlighted box to the logicpage. It can * + * optionally fill the box with a color as well. This is a low level * + * function and thus, it doesn't do any graphic mode color adjustments. * + * * + * INPUT: x,y -- Upper left corner of the box to be drawn (pixels). * + * * + * w,h -- Width and height of box (in pixels). * + * * + * up -- Is the box rendered in the "up" stated? * + * * + * filled-- Is the box to be filled. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 JLB : Created. * + * 05/30/1992 JLB : Embedded color codes. * + * 07/31/1992 JLB : Depressed option added. * + *=============================================================================================*/ +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + // Filler, Shadow, Hilite, Corner colors + + BoxStyleType const ButtonColors[BOXSTYLE_COUNT] = { + { scheme->Background, scheme->Highlight, scheme->Shadow, scheme->Corners}, // Down + { scheme->Background, scheme->Shadow, scheme->Highlight, scheme->Corners}, // Raised + { DKGREY, WHITE, BLACK, DKGREY}, // Disabled down + { DKGREY, BLACK, LTGREY, DKGREY}, // Disabled up + { BLACK, scheme->Box, scheme->Box, BLACK}, // List box + { BLACK, scheme->Box, scheme->Box, BLACK}, // Dialog box + }; + + w--; + h--; + BoxStyleType const &style = ButtonColors[up]; + + if (filled) { +// jmarshall + //LogicPage->Fill_Rect( x, y, x+w, y+h, style.Filler); + GL_FillRect(style.Filler, x, y, w, h); +// jmarshall end + } + + switch (up) { + case (BOXSTYLE_BOX): + LogicPage->Draw_Rect(x, y, x+w, y+h, style.Highlight); + break; + + case (BOXSTYLE_BORDER): + LogicPage->Draw_Rect(x+1, y+1, x+w-1, y+h-1, style.Highlight); + break; + + default: + LogicPage->Draw_Line(x, y+h, x+w, y+h, style.Shadow); + LogicPage->Draw_Line(x+w, y, x+w, y+h, style.Shadow); + + LogicPage->Draw_Line(x, y, x+w, y, style.Highlight); + LogicPage->Draw_Line(x, y, x, y+h, style.Highlight); + + LogicPage->Put_Pixel(x, y+h, style.Corner); + LogicPage->Put_Pixel(x+w, y, style.Corner); + break; + } +} + + +/*********************************************************************************************** + * Format_Window_String -- Separates a String into Lines. * + * This function will take a long string and break it up into lines * + * which are not longer then the window width. Any character < ' ' is * + * considered a new line marker and will be replaced by a NULL. * + * * + * INPUT: char *String - string to be formated. * + * int maxlinelen - Max length of any line in pixels. * + * * + * OUTPUT: int - number of lines string is. * + * * + * WARNINGS: The string passed in will be modified - NULLs will be put * + * into each position that will be a new line. * + * * + * HISTORY: * + * 03/27/1992 SB : Created. * + * 05/18/1995 JLB : Greatly revised for new font system. * + * 09/04/1996 BWG : Added '@' is treated as a carriage return for width calculations. * + *=============================================================================================*/ +int Format_Window_String(char * string, int maxlinelen, int & width, int & height) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + /* + ** Look for special line break character and force a line break when it is + ** discovered. + */ + if (*string == '@') { + *string = '\r'; + } + + // While the current line is less then the max length... + while (linelen < maxlinelen && *string != '\r' && *string != '\0' && *string != '@') { + linelen += Char_Pixel_Width(*string++); + } + + // if the line is to long... + if (linelen >= maxlinelen) { + + /* + ** Back up to an appropriate location to break. + */ + while (*string != ' ' && *string != '\r' && *string != '\0' && *string != '@') { + linelen -= Char_Pixel_Width(*string--); + } + + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *string++ = '\r'; + } + } + return(lines); +} + + +/*********************************************************************************************** + * Window_Box -- Draws a fancy box over the specified window. * + * * + * This routine will draw a fancy (shaded) box over the specified * + * window. This is the effect used to give the polished look to * + * screen rectangles without having to use art. * + * * + * INPUT: window -- Specified window to fill and border. * + * * + * style -- The style to render the window. * + * * + * OUTPUT: none * + * * + * WARNINGS: The rendering is done to the LogicPage. * + * * + * HISTORY: * + * 03/03/1992 JLB : Created. * + * 07/31/1992 JLB : Cool raised border effect. * + * 06/08/1994 JLB : Takes appropriate enumeration parameters. * + *=============================================================================================*/ +void Window_Box(WindowNumberType window, BoxStyleEnum style) +{ + int x = WindowList[window][WINDOWX]; + int y = WindowList[window][WINDOWY]; + int w = WindowList[window][WINDOWWIDTH]; + int h = WindowList[window][WINDOWHEIGHT]; + + /* + ** If it is to be rendered to the seenpage, then + ** hide the mouse. + */ + if (LogicPage == (&SeenBuff)) Conditional_Hide_Mouse(x ,y, x+w, y+h); + + Draw_Box(x, y, w, h, style, true); + + /* + ** Restore the mouse if it has been hidden and return. + */ + if (LogicPage == &SeenBuff) Conditional_Show_Mouse(); +} + + +/*********************************************************************************************** + * Simple_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * The C&C gradient font colors are as follows: * + * 0 transparent (background) * + * 1 foreground color for mono-color fonts only * + * 2 shadow under characters ("drop shadow") * + * 3 shadow all around characters ("full shadow") * + * 4-10 unused * + * 11 top row * + * 12 next row * + * 13 next row * + * 14 next row * + * 15 bottom row * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + *=============================================================================================*/ +void Simple_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag) +{ + static int yspace=0; // Y spacing adjustment for font. + static int xspace=0; // Spacing adjustment for font. + void const * font=0; // Font to use. + int shadow; // Requested shadow value. + unsigned char fontpalette[16]; // Working font palette array. + int forecolor; + + if (fore == NULL) { + fore = &ColorRemaps[PCOLOR_RED]; + } + + /* + ** Init the font palette to the given background color + */ + memset(&fontpalette[0], back, 16); + + forecolor = fore->Color; + + /* + ** A gradient font always requires special fixups for the palette + */ + int point = (flag & (TextPrintType)0x000F); + if (point == TPF_VCR || point == TPF_6PT_GRAD || point == TPF_METAL12 || point == TPF_EFNT || point == TPF_TYPE) { + + /* + ** If a gradient palette is specified, copy the remap table directly, otherwise + ** use the foreground color as the entire font remap color. + */ + if (flag & TPF_USE_GRAD_PAL) { + memcpy(fontpalette, fore->FontRemap, 16); + forecolor = fore->Color; + if (point == TPF_TYPE) { + forecolor = fontpalette[1]; + } + } else { + memset(&fontpalette[4], fore->Color, 12); + forecolor = fore->Color; + } + + /* + ** Medium color: set all font colors to a medium value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_MEDIUM_COLOR) { + forecolor = fore->Color; + memset(&fontpalette[4], fore->Color, 12); + } + + /* + ** Bright color: set all font colors to a bright value. This flag + ** overrides any gradient effects. + */ + if (flag & TPF_BRIGHT_COLOR) { + forecolor = fore->Bright; + memset(&fontpalette[4], fore->BrightColor, 12); + } + } + + /* + ** Change the current font if it differs from the font desired. + */ +#ifdef WIN32 + xspace = 1; +#else + xspace = 0; +#endif + yspace = 0; + + switch (point) { + case TPF_SCORE: + font = ScoreFontPtr; + break; + + case TPF_METAL12: + font = Metal12FontPtr; + //xspace += 1; + break; + + case TPF_MAP: + font = MapFontPtr; + xspace -= 1; + break; + + case TPF_VCR: + font = VCRFontPtr; + break; + + case TPF_6PT_GRAD: + font = GradFont6Ptr; + xspace -= 1; + break; + + case TPF_3POINT: + xspace += 1; + font = Font3Ptr; + flag = flag & ~(TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_NOSHADOW); + break; + + case TPF_6POINT: + font = Font6Ptr; + xspace -= 1; + break; + + case TPF_EFNT: + font = EditorFont; +#ifdef WIN32 + yspace += 1; + xspace -= 1; +#endif + xspace -= 1; + break; + + case TPF_8POINT: + font = Font8Ptr; +#ifdef WIN32 + xspace -= 2; + yspace -= 4; +#else + xspace -= 1; + yspace -= 2; +#endif + break; + + case TPF_LED: +#ifdef WIN32 + xspace -= 4; +#else + xspace -= 2; +#endif + font = FontLEDPtr; + break; + + case TPF_TYPE: + font = TypeFontPtr; + xspace -= 1; + +#ifdef WOLAPI_INTEGRATION + xspace -= 2; + yspace += 2; +#else // I am implicitly assuming that TPF_TYPE was no longer being used, before I came along, despite the following. ajw +#ifdef GERMAN + yspace += 4; //VG 10/17/96 +#endif +#endif + + break; + + default: + font = FontPtr; + break; + } + + /* + ** Change the current font palette according to the dropshadow flags. + */ + shadow = (flag & (TPF_NOSHADOW|TPF_DROPSHADOW|TPF_FULLSHADOW|TPF_LIGHTSHADOW)); + switch (shadow) { + + /* + ** The text is rendered plain. + */ + case TPF_NOSHADOW: + fontpalette[2] = back; + fontpalette[3] = back; + xspace -= 1; +#ifdef WIN32 + yspace -= 2; +#else + yspace -= 1; +#endif + break; + + /* + ** The text is rendered with a simple + ** drop shadow. + */ + case TPF_DROPSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Special engraved text look for the options + ** dialog system. + */ + case TPF_LIGHTSHADOW: + fontpalette[2] = ((14 * 16) + 7)+1; + fontpalette[3] = back; + xspace -= 1; + break; + + /* + ** Each letter is surrounded by black. This is used + ** when the text will be over a non-plain background. + */ + case TPF_FULLSHADOW: + fontpalette[2] = BLACK; + fontpalette[3] = BLACK; + xspace -= 1; + break; + + default: + break; + } + if (point != TPF_TYPE) { + fontpalette[0] = back; + fontpalette[1] = fore->Color; + } + + /* + ** Set the font and spacing according to the values they should be. + */ + FontXSpacing = xspace; + FontYSpacing = yspace; + Set_Font(font); + Set_Font_Palette(fontpalette); + + /* + ** Display the (centered) message if there is one. + */ + if (text && *text) { + switch (flag & (TPF_CENTER|TPF_RIGHT)) { + case TPF_CENTER: + x -= String_Pixel_Width(text)>>1; + break; + + case TPF_RIGHT: + x -= String_Pixel_Width(text); + break; + + default: + break; + } + + if (x < (unsigned)LogicPage->Get_Width() && y < (unsigned)LogicPage->Get_Height()) { + LogicPage->Print(text, x, y, forecolor, back); +// LogicPage->Print(text, x, y, fore->Color, back); + } + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Text number to print. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 11/29/1994 JLB : Created * + *=============================================================================================*/ +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If the text number is valid, then process it. + */ + if (text != TXT_NONE) { + va_start(arg, flag); + + /* + ** The text string must be locked since the vsprintf function doesn't know + ** how to handle EMS pointers. + */ + char const * tptr = Text_String(text); + vsprintf(buffer, tptr, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are to be changed, since the text number is TXT_NONE. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Fancy_Text_Print -- Prints text with a drop shadow. * + * * + * This routine functions like Text_Print, but will render a drop * + * shadow (in black). * + * * + * INPUT: text -- Pointer to text to render. * + * * + * x,y -- Pixel coordinate for to print text. * + * * + * fore -- Foreground color. * + * * + * back -- Background color. * + * * + * flag -- Text print control flags. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is much slower than normal text print and * + * if rendered to the SEENPAGE, the intermediate rendering * + * steps could be visible. * + * * + * HISTORY: * + * 12/24/1991 JLB : Created. * + * 10/26/94 JLB : Handles font X spacing in a more friendly manner. * + * 11/29/1994 JLB : Separated actual draw action. * + *=============================================================================================*/ +void Fancy_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...) +{ + char buffer[512]; // Working staging buffer. + va_list arg; // Argument list var. + + /* + ** If there is a valid text string pointer then build the final string into the + ** working buffer before sending it to the simple string printing routine. + */ + if (text) { + + /* + ** Since vsprintf doesn't know about EMS pointers, be sure to surround this + ** call with locking code. + */ + va_start(arg, flag); + vsprintf(buffer, text, arg); + va_end(arg); + + Simple_Text_Print(buffer, x, y, fore, back, flag); + } else { + + /* + ** Just the flags are desired to be changed, so call the simple print routine with + ** a NULL text pointer. + */ + Simple_Text_Print((char const *)0, x, y, fore, back, flag); + } +} + + +/*********************************************************************************************** + * Clip_Text_Print -- Prints text with clipping and support. * + * * + * Use this routine to print text that that should be clipped at an arbitrary right margin * + * as well as possibly recognizing characters. Typical users of this routine would * + * be list boxes. * + * * + * INPUT: text -- Reference to the text to print. * + * * + * x,y -- Pixel coordinate of the upper left corner of the text position. * + * * + * fore -- The foreground color to use. * + * * + * back -- The background color to use. * + * * + * flag -- The text print flags to use. * + * * + * width -- The maximum pixel width to draw the text. Extra characters beyond this * + * point will not be printed. * + * * + * tabs -- Optional pointer to a series of pixel tabstop positions. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void Conquer_Clip_Text_Print(char const * text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, int width, int const * tabs) +{ + char buffer[512]; + + if (text) { + strcpy(buffer, text); + + /* + ** Set the font and spacing characteristics according to the flag + ** value passed in. + */ + //PG_TO_FIX + //Simple_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, flag); + + char * source = &buffer[0]; + unsigned offset = 0; + int processing = true; + while (processing && offset < (unsigned) width) { + char * ptr = strchr(source, '\t'); + + /* + ** Zap the tab character. It will be processed later. + */ + if (ptr) { + *ptr = '\0'; + } + + if (*source) { + + /* + ** Scan forward until the end of the string is reached or the + ** maximum width, whichever comes first. + */ + int w = 0; + char * bptr = source; + do { + w += Char_Pixel_Width(*bptr++); + } while (*bptr && offset+w < (unsigned)width); + + /* + ** If the maximum width has been exceeded, then remove the last + ** character and signal that further processing is not necessary. + */ + if (offset+w >= (unsigned)width) { + bptr--; + w -= Char_Pixel_Width(*bptr); + *bptr = '\0'; + processing = 0; + } + + /* + ** Print this text block and advance the offset accordingly. + */ + Simple_Text_Print(source, x+offset, y, fore, back, flag); + offset += w; + } + + /* + ** If a was the terminator for this text block, then advance + ** to the next tabstop. + */ + if (ptr) { + if (tabs) { + while ((int)offset > *tabs) { + tabs++; + } + offset = *tabs; + } else { + offset = ((offset+1 / 50) + 1) * 50; + } + source = ptr+1; + } else { + break; + } + } + } +} + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + +/*************************************************************************** + * Plain_Text_Print -- Prints text without using a color scheme * + * * + * INPUT: * + * text text to print * + * x,y coords to print at * + * fore desired foreground color * + * back desired background color * + * flag text print control flags * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not use the gradient control flag with this routine! For * + * a gradient appearance, use Fancy_Text_Print. * + * Despite this routine's name, it is actually faster to call * + * Fancy_Text_Print than this routine. * + * * + * HISTORY: * + * 01/05/1996 BRR : Created. * + *=========================================================================*/ +void Plain_Text_Print(char const * text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...) +{ + RemapControlType scheme; + + memset(&scheme, 0, sizeof(RemapControlType)); + memset(&(scheme.FontRemap[4]), fore, 12); + + scheme.BrightColor = fore; + scheme.Color = fore; + scheme.Shadow = fore; + scheme.Background = fore; + scheme.Corners = fore; + scheme.Highlight = fore; + scheme.Box = fore; + scheme.Bright = fore; + scheme.Underline = fore; + scheme.Bar = fore; + + Fancy_Text_Print(text, x, y, &scheme, back, flag); +} + + + +unsigned char * Font_Palette(int color) +{ + static unsigned char _fpalette[16]; + + memset(_fpalette, '\0', sizeof(_fpalette)); + memset(&_fpalette[11], color, 5); + return(_fpalette); +} + + + +/*********************************************************************************************** + * Draw_Caption -- Draws a caption on a dialog box. * + * * + * This routine draws the caption text and any fancy filigree that the dialog may require. * + * * + * INPUT: text -- The text of the caption. This is the text number. * + * * + * x,y -- The dialog box X and Y pixel coordinate of the upper left corner. * + * * + * w -- The width of the dialog box (in pixels). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + *=============================================================================================*/ +void Draw_Caption(int text, int x, int y, int w) +{ + Draw_Caption(Text_String(text), x, y, w); +} + + +void Draw_Caption(char const * text, int x, int y, int w) +{ + /* + ** Draw the caption. + */ + if (text != NULL && *text != '\0') { + if (Debug_Map) { + Fancy_Text_Print(text, w/2 + x, (2 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_EFNT|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } else { + Fancy_Text_Print(text, w/2 + x, (8 * RESFACTOR) + y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + int length = String_Pixel_Width(text); + LogicPage->Draw_Line((x+(w/2))-(length/2), y+FontHeight+FontYSpacing + (8 * RESFACTOR), (x+(w/2))+(length/2), + y+FontHeight+FontYSpacing + (8 * RESFACTOR), GadgetClass::Get_Color_Scheme()->Box); + } + } +} diff --git a/code/redalert/dibapi.h b/code/redalert/dibapi.h new file mode 100644 index 0000000..9fa1609 --- /dev/null +++ b/code/redalert/dibapi.h @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef DIBAPI_H +#define DIBAPI_H +/* + * dibapi.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * BitmapToDIB() - Creates a DIB from a bitmap + * ChangeBitmapFormat() - Changes a bitmap to a specified DIB format + * ChangeDIBFormat() - Changes a DIB's BPP and/or compression format + * CopyScreenToBitmap() - Copies entire screen to a standard Bitmap + * CopyScreenToDIB() - Copies entire screen to a DIB + * CopyWindowToBitmap() - Copies a window to a standard Bitmap + * CopyWindowToDIB() - Copies a window to a DIB + * CreateDIBPalette() - Creates a palette from a DIB + * CreateDIB() - Creates a new DIB + * DestroyDIB() - Deletes DIB when finished using it + * DIBError() - Displays message box with error message + * DIBHeight() - Gets the DIB height + * DIBNumColors() - Calculates number of colors in the DIB's color table + * DIBToBitmap() - Creates a bitmap from a DIB + * DIBWidth() - Gets the DIB width + * FindDIBBits() - Sets pointer to the DIB bits + * GetSystemPalette() - Gets the current palette + * LoadDIB() - Loads a DIB from a file + * PaintBitmap() - Displays standard bitmap in the specified DC + * PaintDIB() - Displays DIB in the specified DC + * PalEntriesOnDevice() - Gets the number of palette entries + * PaletteSize() - Calculates the buffer size required by a palette + * PrintDIB() - Prints the specified DIB + * PrintScreen() - Prints the entire screen + * PrintWindow() - Prints all or part of a window + * SaveDIB() - Saves the specified dib in a file + * + * See the file DIBAPI.TXT for more information about these functions. + * + * ajw added + * LoadDIB_FromMemory() - Loads a DIB from BMP file data located at a location in memory. + * + */ + + +/* Handle to a DIB */ +#define HDIB HANDLE + + +/* Print Area selection */ +#define PW_WINDOW 1 +#define PW_CLIENT 2 + + +/* Print Options selection */ +#define PW_BESTFIT 1 +#define PW_STRETCHTOPAGE 2 +#define PW_SCALE 3 + +/* DIB Macros*/ + +// WIDTHBYTES performs DWORD-aligning of DIB scanlines. The "bits" +// parameter is the bit count for the scanline (biWidth * biBitCount), +// and this macro returns the number of DWORD-aligned bytes needed +// to hold those bits. + +#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + +/* Error constants */ +enum { + ERR_MIN = 0, // All error #s >= this value + ERR_NOT_DIB = 0, // Tried to load a file, NOT a DIB! + ERR_MEMORY, // Not enough memory! + ERR_READ, // Error reading file! + ERR_LOCK, // Error on a GlobalLock()! + ERR_OPEN, // Error opening a file! + ERR_CREATEPAL, // Error creating palette. + ERR_GETDC, // Couldn't get a DC. + ERR_CREATEDDB, // Error create a DDB. + ERR_STRETCHBLT, // StretchBlt() returned failure. + ERR_STRETCHDIBITS, // StretchDIBits() returned failure. + ERR_SETDIBITSTODEVICE, // SetDIBitsToDevice() failed. + ERR_STARTDOC, // Error calling StartDoc(). + ERR_NOGDIMODULE, // Couldn't find GDI module in memory. + ERR_SETABORTPROC, // Error calling SetAbortProc(). + ERR_STARTPAGE, // Error calling StartPage(). + ERR_NEWFRAME, // Error calling NEWFRAME escape. + ERR_ENDPAGE, // Error calling EndPage(). + ERR_ENDDOC, // Error calling EndDoc(). + ERR_SETDIBITS, // Error calling SetDIBits(). + ERR_FILENOTFOUND, // Error opening file in GetDib() + ERR_INVALIDHANDLE, // Invalid Handle + ERR_DIBFUNCTION, // Error on call to DIB function + ERR_MAX // All error #s < this value + }; + + + +/* Function prototypes */ + +HDIB FAR BitmapToDIB (HBITMAP hBitmap, HPALETTE hPal); +HDIB FAR ChangeBitmapFormat (HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal); +HDIB FAR ChangeDIBFormat (HDIB hDIB, WORD wBitCount, + DWORD dwCompression); +HBITMAP FAR CopyScreenToBitmap (LPRECT); +HDIB FAR CopyScreenToDIB (LPRECT); +HBITMAP FAR CopyWindowToBitmap (HWND, WORD); +HDIB FAR CopyWindowToDIB (HWND, WORD); +HPALETTE FAR CreateDIBPalette (HDIB hDIB); +HDIB FAR CreateDIB(DWORD, DWORD, WORD); +WORD FAR DestroyDIB (HDIB); +void FAR DIBError (int ErrNo); +DWORD FAR DIBHeight (LPCSTR lpDIB); +WORD FAR DIBNumColors (LPCSTR lpDIB); +HBITMAP FAR DIBToBitmap (HDIB hDIB, HPALETTE hPal); +DWORD FAR DIBWidth (LPCSTR lpDIB); +LPSTR FAR FindDIBBits (LPCSTR lpDIB); +HPALETTE FAR GetSystemPalette (void); +HDIB FAR LoadDIB (LPSTR); +BOOL FAR PaintBitmap (HDC, LPRECT, HBITMAP, LPRECT, HPALETTE); +BOOL FAR PaintDIB (HDC, LPRECT, HDIB, LPRECT, HPALETTE); +int FAR PalEntriesOnDevice (HDC hDC); +WORD FAR PaletteSize (LPCSTR lpDIB); +WORD FAR PrintDIB (HDIB, WORD, WORD, WORD, LPSTR); +WORD FAR PrintScreen (LPRECT, WORD, WORD, WORD, LPSTR); +WORD FAR PrintWindow (HWND, WORD, WORD, WORD, WORD, LPSTR); +WORD FAR SaveDIB (HDIB, LPSTR); + +// ajw added +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ); + +#endif diff --git a/code/redalert/dibfile.cpp b/code/redalert/dibfile.cpp new file mode 100644 index 0000000..a8598d2 --- /dev/null +++ b/code/redalert/dibfile.cpp @@ -0,0 +1,702 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//******************************************************************* +// +// file.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// SaveDIB() - Saves the specified dib in a file +// LoadDIB() - Loads a DIB from a file +// DestroyDIB() - Deletes DIB when finished using it +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample Files (and/or any modified version) in +// any way you find useful, provided that you agree that +// Microsoft has no warranty obligations or liability for any +// Sample Application Files which are modified. +// +//******************************************************************* +#if (0) // ST - 5/8/2019 + +#include +#include +#include +#include +#include +#include +#include +#include "dibutil.h" +#include "dibapi.h" + +//#include "WolDebug.h" + +/* + * Dib Header Marker - used in writing DIBs to files + */ +#define DIB_HEADER_MARKER ((WORD) ('M' << 8) | 'B') + +/********************************************************************* + * + * Local Function Prototypes + * + *********************************************************************/ + + +HANDLE ReadDIBFile(int); +BOOL MyRead(int, LPSTR, DWORD); +BOOL SaveDIBFile(void); +BOOL WriteDIB(LPSTR, HANDLE); +DWORD PASCAL MyWrite(int, VOID FAR *, DWORD); + +/************************************************************************* + * + * LoadDIB() + * + * Loads the specified DIB from a file, allocates memory for it, + * and reads the disk file into the memory. + * + * + * Parameters: + * + * LPSTR lpFileName - specifies the file to load a DIB from + * + * Returns: A handle to a DIB, or NULL if unsuccessful. + * + * NOTE: The DIB API were not written to handle OS/2 DIBs; This + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * + *************************************************************************/ + + +HDIB FAR LoadDIB(LPSTR lpFileName) +{ + HDIB hDIB; + int hFile; + OFSTRUCT ofs; + + /* + * Set the cursor to a hourglass, in case the loading operation + * takes more than a sec, the user will know what's going on. + */ + + SetCursor(LoadCursor(NULL, IDC_WAIT)); + if ((hFile = OpenFile(lpFileName, &ofs, OF_READ)) != -1) + { + hDIB = ReadDIBFile(hFile); + _lclose(hFile); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return hDIB; + } + else + { +// DIBError(ERR_FILENOTFOUND); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + return NULL; + } +} + + +/************************************************************************* + * + * SaveDIB() + * + * Saves the specified DIB into the specified file name on disk. No + * error checking is done, so if the file already exists, it will be + * written over. + * + * Parameters: + * + * HDIB hDib - Handle to the dib to save + * + * LPSTR lpFileName - pointer to full pathname to save DIB under + * + * Return value: 0 if successful, or one of: + * ERR_INVALIDHANDLE + * ERR_OPEN + * ERR_LOCK + * + * History: + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will not save a file if it is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Taken from DIBVIEW (which was taken + * from SHOWDIB) + * 1/30/92 Mark Bader Fixed problem of writing too many + * bytes to the file + * 6/24/92 Mark Bader Added check for OS/2 DIB + * + *************************************************************************/ + + +WORD FAR SaveDIB(HDIB hDib, LPSTR lpFileName) +{ + BITMAPFILEHEADER bmfHdr; // Header for Bitmap file + LPBITMAPINFOHEADER lpBI; // Pointer to DIB info structure + int fh; // file handle for opened file + OFSTRUCT of; // OpenFile structure + DWORD dwDIBSize; + DWORD dwError; // Error return from MyWrite + + if (!hDib) + return ERR_INVALIDHANDLE; + fh = OpenFile(lpFileName, &of, OF_CREATE | OF_READWRITE); + if (fh == -1) + return ERR_OPEN; + + /* + * Get a pointer to the DIB memory, the first of which contains + * a BITMAPINFO structure + */ + lpBI = (LPBITMAPINFOHEADER)GlobalLock(hDib); + if (!lpBI) + return ERR_LOCK; + + // Check to see if we're dealing with an OS/2 DIB. If so, don't + // save it because our functions aren't written to deal with these + // DIBs. + + if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) + { + GlobalUnlock(hDib); + return ERR_NOT_DIB; + } + + /* + * Fill in the fields of the file header + */ + + /* Fill in file type (first 2 bytes must be "BM" for a bitmap) */ + bmfHdr.bfType = DIB_HEADER_MARKER; // "BM" + + // Calculating the size of the DIB is a bit tricky (if we want to + // do it right). The easiest way to do this is to call GlobalSize() + // on our global handle, but since the size of our global memory may have + // been padded a few bytes, we may end up writing out a few too + // many bytes to the file (which may cause problems with some apps, + // like HC 3.0). + // + // So, instead let's calculate the size manually. + // + // To do this, find size of header plus size of color table. Since the + // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains + // the size of the structure, let's use this. + + dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPSTR)lpBI); // Partial Calculation + + // Now calculate the size of the image + + if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) { + + // It's an RLE bitmap, we can't calculate size, so trust the + // biSizeImage field + + dwDIBSize += lpBI->biSizeImage; + } + else { + DWORD dwBmBitsSize; // Size of Bitmap Bits only + + // It's not RLE, so size is Width (DWORD aligned) * Height + + dwBmBitsSize = WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * lpBI->biHeight; + + dwDIBSize += dwBmBitsSize; + + // Now, since we have calculated the correct size, why don't we + // fill in the biSizeImage field (this will fix any .BMP files which + // have this field incorrect). + + lpBI->biSizeImage = dwBmBitsSize; + } + + + // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER) + + bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); + bmfHdr.bfReserved1 = 0; + bmfHdr.bfReserved2 = 0; + + /* + * Now, calculate the offset the actual bitmap bits will be in + * the file -- It's the Bitmap file header plus the DIB header, + * plus the size of the color table. + */ + bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + + PaletteSize((LPSTR)lpBI); + + /* Write the file header */ + _lwrite(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER)); + + /* + * Write the DIB header and the bits -- use local version of + * MyWrite, so we can write more than 32767 bytes of data + */ + dwError = MyWrite(fh, (LPSTR)lpBI, dwDIBSize); + GlobalUnlock(hDib); + _lclose(fh); + + if (dwError == 0) + return ERR_OPEN; // oops, something happened in the write + else + return 0; // Success code +} + + +/************************************************************************* + * + * DestroyDIB () + * + * Purpose: Frees memory associated with a DIB + * + * Returns: Nothing + * + * History: Date Author Reason + * 9/15/91 Mark Bader Created + * + *************************************************************************/ + + +WORD FAR DestroyDIB(HDIB hDib) +{ + GlobalFree(hDib); + return 0; +} + + +//************************************************************************ +// +// Auxiliary Functions which the above procedures use +// +//************************************************************************ + + +/************************************************************************* + * + * Function: ReadDIBFile (int) + * + * Purpose: Reads in the specified DIB file into a global chunk of + * memory. + * + * Returns: A handle to a dib (hDIB) if successful. + * NULL if an error occurs. + * + * Comments: BITMAPFILEHEADER is stripped off of the DIB. Everything + * from the end of the BITMAPFILEHEADER structure on is + * returned in the global memory handle. + * + * + * NOTE: The DIB API were not written to handle OS/2 DIBs, so this + * function will reject any file that is not a Windows DIB. + * + * History: Date Author Reason + * 9/15/91 Mark Bader Based on DIBVIEW + * 6/25/92 Mark Bader Added check for OS/2 DIB + * 7/21/92 Mark Bader Added code to deal with bfOffBits + * field in BITMAPFILEHEADER + * 9/11/92 Mark Bader Fixed Realloc Code to free original mem + * + *************************************************************************/ + +HANDLE ReadDIBFile(int hFile) +{ + BITMAPFILEHEADER bmfHeader; + DWORD dwBitsSize; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + /* + * get length of DIB in bytes for use when reading + */ + + dwBitsSize = filelength(hFile); + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + + hDIB = GlobalAlloc(GMEM_MOVEABLE, + (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); + + if (!hDIB) return NULL; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + if (!lpbi) + { + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file + + if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) + goto ErrExit; + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + goto ErrExit; + + // read the BITMAPINFOHEADER + + if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) + goto ErrExit; + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + goto ErrExit; + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) + * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + + nNumColors * sizeof(RGBQUAD) + + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + goto ErrExitNoUnlock; //MPB + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table + _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) + _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + + if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) + goto OKExit; + + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} + +/************************************************************************* + + Function: MyRead (int, LPSTR, DWORD) + + Purpose: Routine to read files greater than 64K in size. + + Returns: TRUE if successful. + FALSE if an error occurs. + + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + +*************************************************************************/ + + +BOOL MyRead(int hFile, LPSTR lpBuffer, DWORD dwSize) +{ + char huge *lpInBuf = (char huge *)lpBuffer; + int nBytes; + + /* + * Read in the data in 32767 byte chunks (or a smaller amount if it's + * the last chunk of data read) + */ + + while (dwSize) + { + nBytes = (int)(dwSize > (DWORD)32767 ? 32767 : LOWORD (dwSize)); + if (_lread(hFile, (LPSTR)lpInBuf, nBytes) != (WORD)nBytes) + return FALSE; + dwSize -= nBytes; + lpInBuf += nBytes; + } + return TRUE; +} + + +/**************************************************************************** + + FUNCTION : MyWrite(int fh, VOID FAR *pv, DWORD ul) + + PURPOSE : Writes data in steps of 32k till all the data is written. + Normal _lwrite uses a WORD as 3rd parameter, so it is + limited to 32767 bytes, but this procedure is not. + + RETURNS : 0 - If write did not proceed correctly. + number of bytes written otherwise. + + History: Date Author Reason + 9/15/91 Mark Bader Based on DIBVIEW + + ****************************************************************************/ + + +DWORD PASCAL MyWrite(int iFileHandle, VOID FAR *lpBuffer, DWORD dwBytes) +{ + DWORD dwBytesTmp = dwBytes; // Save # of bytes for return value + BYTE huge *hpBuffer = (BYTE huge *)lpBuffer; // make a huge pointer to the data + + /* + * Write out the data in 32767 byte chunks. + */ + + while (dwBytes > 32767) + { + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)32767) != 32767) + return 0; + dwBytes -= 32767; + hpBuffer += 32767; + } + + /* Write out the last chunk (which is < 32767 bytes) */ + if (_lwrite(iFileHandle, (LPSTR)hpBuffer, (WORD)dwBytes) != (WORD)dwBytes) + return 0; + return dwBytesTmp; +} + +// ajw added +// Added to allow "loading" from a location in memory. +// A modification of ReadDIBFile(), above. +//*********************************************************************************************** +HDIB LoadDIB_FromMemory( const unsigned char* pData, DWORD dwBitsSize ) +{ + BITMAPFILEHEADER bmfHeader; + UINT nNumColors; // Number of colors in table + HANDLE hDIB; + HANDLE hDIBtmp; // Used for GlobalRealloc() //MPB + LPBITMAPINFOHEADER lpbi; + DWORD offBits; + + const unsigned char* const pDataStart = pData; + const unsigned char* pDataEnd = pData + dwBitsSize; // One char past end of "file". + + // Allocate memory for header & color table. We'll enlarge this + // memory as needed. + +// debugprint( "LoadDIB_FromMemory, GlobalAlloc\n" ); + hDIB = GlobalAlloc(GMEM_MOVEABLE, (DWORD)(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD))); +// debugprint( "hDIB from GlobalALloc is %i\n", hDIB ); + + if (!hDIB) + { +// debugprint( "LoadDIB_FromMemory error: failed alloc\n" ); + return NULL; + } + +// debugprint( "LoadDIB_FromMemory, lpbi Lock\n" ); + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); +// debugprint( "lpbi is %i\n", lpbi ); + if (!lpbi) + { +// debugprint( "LoadDIB_FromMemory error: failed lock\n" ); + GlobalFree(hDIB); + return NULL; + } + + // read the BITMAPFILEHEADER from our file +// if (sizeof (BITMAPFILEHEADER) != _lread (hFile, (LPSTR)&bmfHeader, sizeof (BITMAPFILEHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPFILEHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPFILEHEADER %i bytes\n", sizeof( BITMAPFILEHEADER ) ); + memcpy( &bmfHeader, pData, sizeof( BITMAPFILEHEADER ) ); + pData += sizeof( BITMAPFILEHEADER ); + + if (bmfHeader.bfType != 0x4d42) /* 'BM' */ + { +// debugprint( "LoadDIB_FromMemory error: no BM\n" ); + goto ErrExit; + } + + // read the BITMAPINFOHEADER +// if (sizeof(BITMAPINFOHEADER) != _lread (hFile, (LPSTR)lpbi, sizeof(BITMAPINFOHEADER))) +// goto ErrExit; + if( pData + sizeof( BITMAPINFOHEADER ) >= pDataEnd ) + { +// debugprint( "LoadDIB_FromMemory error: bad size 2\n" ); + goto ErrExit; + } +// debugprint( "LoadDIB_FromMemory, memcpy BITMAPINFOHEADER %i bytes\n", sizeof( BITMAPINFOHEADER ) ); + memcpy( lpbi, pData, sizeof( BITMAPINFOHEADER ) ); + pData += sizeof( BITMAPINFOHEADER ); + + // Check to see that it's a Windows DIB -- an OS/2 DIB would cause + // strange problems with the rest of the DIB API since the fields + // in the header are different and the color table entries are + // smaller. + // + // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. + + if (lpbi->biSize == sizeof(BITMAPCOREHEADER)) + { +// debugprint( "LoadDIB_FromMemory error: lpbi->biSize bad\n" ); + goto ErrExit; + } + + if( lpbi->biCompression != BI_RGB ) + { +// debugprint( "LoadDIB_FromMemory error: Image is compressed\n" ); + goto ErrExit; + } + + // Now determine the size of the color table and read it. Since the + // bitmap bits are offset in the file by bfOffBits, we need to do some + // special processing here to make sure the bits directly follow + // the color table (because that's the format we are susposed to pass + // back) + nNumColors = (UINT)lpbi->biClrUsed; + if (!nNumColors) + { + // no color table for 24-bit, default size otherwise + if (lpbi->biBitCount != 24) + nNumColors = 1 << lpbi->biBitCount; /* standard size table */ + } + + // fill in some default values if they are zero + if (lpbi->biClrUsed == 0) + lpbi->biClrUsed = nNumColors; + +// debugprint( "biSizeImage is %i. I would say it was %i, because the bpp is %i.\n", lpbi->biSizeImage, ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight, lpbi->biBitCount ); + if (lpbi->biSizeImage == 0) + { + lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; + } + + // get a proper-sized buffer for header, color table and bits + GlobalUnlock(hDIB); +// debugprint( "LoadDIB_FromMemory, GlobalReAlloc: lpbi->biSize=%i, nNumColors=%i, lpbi->biSizeImage=%i\n", lpbi->biSize, nNumColors,lpbi->biSizeImage ); + hDIBtmp = GlobalReAlloc(hDIB, lpbi->biSize + nNumColors * sizeof(RGBQUAD) + lpbi->biSizeImage, 0); + + if (!hDIBtmp) // can't resize buffer for loading + { +// debugprint( "LoadDIB_FromMemory error: realloc failed\n" ); + goto ErrExitNoUnlock; //MPB + } + else + hDIB = hDIBtmp; + + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // read the color table +// _lread (hFile, (LPSTR)(lpbi) + lpbi->biSize, nNumColors * sizeof(RGBQUAD)); +// debugprint( "LoadDIB_FromMemory, memcpy color table %i colors, so %i bytes\n", nNumColors, nNumColors * sizeof(RGBQUAD) ); + memcpy( (LPSTR)(lpbi) + lpbi->biSize, pData, nNumColors * sizeof(RGBQUAD) ); + pData += nNumColors * sizeof(RGBQUAD); + + // offset to the bits from start of DIB header + offBits = lpbi->biSize + nNumColors * sizeof(RGBQUAD); + + // If the bfOffBits field is non-zero, then the bits might *not* be + // directly following the color table in the file. Use the value in + // bfOffBits to seek the bits. + + if (bmfHeader.bfOffBits != 0L) +// _llseek(hFile, bmfHeader.bfOffBits, SEEK_SET); + pData = pDataStart + bmfHeader.bfOffBits; + +// debugprint( "bmfHeader.bfOffBits is %i\n", bmfHeader.bfOffBits ); + +// if (MyRead(hFile, (LPSTR)lpbi + offBits, lpbi->biSizeImage)) +// goto OKExit; +// debugprint( "Checking that pData(%i) + biSizeImage(%i), which is %i, is equal to pDataEnd(%i)\n", +// pData, lpbi->biSizeImage, pData + lpbi->biSizeImage, pDataEnd ); +// if( pData + lpbi->biSizeImage != pDataEnd ) condition relaxed +// { +// debugprint( "LoadDIB_FromMemory error: bad size 3\n" ); +// goto ErrExit; +// } + +// debugprint( "LoadDIB_FromMemory, memcpy the bits, %i bytes. Image is w %i, h.%i\n", +// lpbi->biSizeImage, lpbi->biWidth, lpbi->biHeight ); +// debugprint( "Writing to lpbi (%i) + offBits (%i)\n", lpbi, offBits ); + + memcpy( (LPSTR)lpbi + offBits, pData, lpbi->biSizeImage ); +// pData += lpbi->biSizeImage; +// if( pData != pDataEnd ) // Should end up one byte past end of data. - condition relaxed +// debugprint( "LoadDIB_FromMemory: ERROR! Ended up at %i instead of %i\n", pData, pDataEnd ); + goto OKExit; + +ErrExit: + GlobalUnlock(hDIB); +ErrExitNoUnlock: + GlobalFree(hDIB); +// debugprint( "LoadDIB_FromMemory Error!\n" ); + return NULL; + +OKExit: + GlobalUnlock(hDIB); + return hDIB; +} +#endif \ No newline at end of file diff --git a/code/redalert/dibutil.cpp b/code/redalert/dibutil.cpp new file mode 100644 index 0000000..0235285 --- /dev/null +++ b/code/redalert/dibutil.cpp @@ -0,0 +1,1314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//********************************************************************** +// +// dibutil.c +// +// Source file for Device-Independent Bitmap (DIB) API. Provides +// the following functions: +// +// CreateDIB() - Creates new DIB +// FindDIBBits() - Sets pointer to the DIB bits +// DIBWidth() - Gets the width of the DIB +// DIBHeight() - Gets the height of the DIB +// PaletteSize() - Calculates the buffer size required by a palette +// DIBNumColors() - Calculates number of colors in the DIB's color table +// CreateDIBPalette() - Creates a palette from a DIB +// DIBToBitmap() - Creates a bitmap from a DIB +// BitmapToDIB() - Creates a DIB from a bitmap +// PalEntriesOnDevice()- Gets the number of palette entries of a device +// GetSystemPalette() - Returns a handle to the current system palette +// AllocRoomForDIB() - Allocates memory for a DIB +// ChangeDIBFormat() - Changes a DIB's BPP and/or compression format +// ChangeBitmapFormat()- Changes a bitmap to a DIB with specified BPP and +// compression format +// +// Development Team: Mark Bader +// Patrick Schreiber +// Garrett McAuliffe +// Eric Flo +// Tony Claflin +// +// Written by Microsoft Product Support Services, Developer Support. +// COPYRIGHT: +// +// (C) Copyright Microsoft Corp. 1993. All rights reserved. +// +// You have a royalty-free right to use, modify, reproduce and +// distribute the Sample Files (and/or any modified version) in +// any way you find useful, provided that you agree that +// Microsoft has no warranty obligations or liability for any +// Sample Application Files which are modified. +// +//********************************************************************** +#if (0) // ST - 5/8/2019 +/* header files */ +#include +#include +#include "dibapi.h" +#include "dibutil.h" +#include + + +/************************************************************************* + * + * CreateDIB() + * + * Parameters: + * + * DWORD dwWidth - Width for new bitmap, in pixels + * DWORD dwHeight - Height for new bitmap + * WORD wBitCount - Bit Count for new DIB (1, 4, 8, or 24) + * + * Return Value: + * + * HDIB - Handle to new DIB + * + * Description: + * + * This function allocates memory for and initializes a new DIB by + * filling in the BITMAPINFOHEADER, allocating memory for the color + * table, and allocating memory for the bitmap bits. As with all + * HDIBs, the header, colortable and bits are all in one contiguous + * memory block. This function is similar to the CreateBitmap() + * Windows API. + * + * The colortable and bitmap bits are left uninitialized (zeroed) in the + * returned HDIB. + * + * + * History: Date Author Reason + * 3/20/92 Mark Bader Created + * + ************************************************************************/ + +HDIB FAR CreateDIB(DWORD dwWidth, DWORD dwHeight, WORD wBitCount) +{ + BITMAPINFOHEADER bi; // bitmap header + LPBITMAPINFOHEADER lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HDIB hDIB; + DWORD dwBytesPerLine; // Number of bytes per scanline + + + // Make sure bits per pixel is valid + if (wBitCount <= 1) + wBitCount = 1; + else if (wBitCount <= 4) + wBitCount = 4; + else if (wBitCount <= 8) + wBitCount = 8; + else if (wBitCount <= 24) + wBitCount = 24; + else + wBitCount = 4; // set default value to 4 if parameter is bogus + + // initialize BITMAPINFOHEADER + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = dwWidth; // fill in width from parameter + bi.biHeight = dwHeight; // fill in height from parameter + bi.biPlanes = 1; // must be 1 + bi.biBitCount = wBitCount; // from parameter + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; // 0's here mean "default" + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + // calculate size of memory block required to store the DIB. This + // block should be big enough to hold the BITMAPINFOHEADER, the color + // table, and the bits + + dwBytesPerLine = WIDTHBYTES(wBitCount * dwWidth); + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + (dwBytesPerLine * dwHeight); + + // alloc memory block to store our bitmap + hDIB = GlobalAlloc(GHND, dwLen); + + // major bummer if we couldn't get memory block + if (!hDIB) + { + return NULL; + } + + // lock memory and get pointer to it + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + + // use our bitmap info structure to fill in first part of + // our DIB with the BITMAPINFOHEADER + *lpbi = bi; + + // Since we don't know what the colortable and bits should contain, + // just leave these blank. Unlock the DIB and return the HDIB. + + GlobalUnlock(hDIB); + + /* return handle to the DIB */ + return hDIB; +} + + + +/************************************************************************* + * + * FindDIBBits() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * LPSTR - pointer to the DIB bits + * + * Description: + * + * This function calculates the address of the DIB's bits and returns a + * pointer to the DIB bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +LPSTR FAR FindDIBBits(LPCSTR lpDIB) +{ + return (LPSTR)(lpDIB + *(LPDWORD)lpDIB + PaletteSize(lpDIB)); +} + + +/************************************************************************* + * + * DIBWidth() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - width of the DIB + * + * Description: + * + * This function gets the width of the DIB from the BITMAPINFOHEADER + * width field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * width field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBWidth(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether Win 3.0 and OS/2) */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB width if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biWidth; + else /* it is an OS/2 DIB, so return its width */ + return (DWORD)lpbmc->bcWidth; +} + + +/************************************************************************* + * + * DIBHeight() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * DWORD - height of the DIB + * + * Description: + * + * This function gets the height of the DIB from the BITMAPINFOHEADER + * height field if it is a Windows 3.0-style DIB or from the BITMAPCOREHEADER + * height field if it is an OS/2-style DIB. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +DWORD FAR DIBHeight(LPCSTR lpDIB) +{ + LPBITMAPINFOHEADER lpbmi; // pointer to a Win 3.0-style DIB + LPBITMAPCOREHEADER lpbmc; // pointer to an OS/2-style DIB + + /* point to the header (whether OS/2 or Win 3.0 */ + + lpbmi = (LPBITMAPINFOHEADER)lpDIB; + lpbmc = (LPBITMAPCOREHEADER)lpDIB; + + /* return the DIB height if it is a Win 3.0 DIB */ + if (lpbmi->biSize == sizeof(BITMAPINFOHEADER)) + return lpbmi->biHeight; + else /* it is an OS/2 DIB, so return its height */ + return (DWORD)lpbmc->bcHeight; +} + + +/************************************************************************* + * + * PaletteSize() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - size of the color palette of the DIB + * + * Description: + * + * This function gets the size required to store the DIB's palette by + * multiplying the number of colors by the size of an RGBQUAD (for a + * Windows 3.0-style DIB) or by the size of an RGBTRIPLE (for an OS/2- + * style DIB). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR PaletteSize(LPCSTR lpDIB) +{ + /* calculate the size required by the palette */ + if (IS_WIN30_DIB (lpDIB)) + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBQUAD)); + else + return (WORD FAR)(DIBNumColors(lpDIB) * sizeof(RGBTRIPLE)); +} + + +/************************************************************************* + * + * DIBNumColors() + * + * Parameter: + * + * LPSTR lpDIB - pointer to packed-DIB memory block + * + * Return Value: + * + * WORD - number of colors in the color table + * + * Description: + * + * This function calculates the number of colors in the DIB's color table + * by finding the bits per pixel for the DIB (whether Win3.0 or OS/2-style + * DIB). If bits per pixel is 1: colors=2, if 4: colors=16, if 8: colors=256, + * if 24, no colors in color table. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +WORD FAR DIBNumColors(LPCSTR lpDIB) +{ + WORD wBitCount; // DIB bit count + + /* If this is a Windows-style DIB, the number of colors in the + * color table can be less than the number of bits per pixel + * allows for (i.e. lpbi->biClrUsed can be set to some value). + * If this is the case, return the appropriate value. + */ + + if (IS_WIN30_DIB(lpDIB)) + { + DWORD dwClrUsed; + + dwClrUsed = ((LPBITMAPINFOHEADER)lpDIB)->biClrUsed; + if (dwClrUsed) + return (WORD)dwClrUsed; + } + + /* Calculate the number of colors in the color table based on + * the number of bits per pixel for the DIB. + */ + if (IS_WIN30_DIB(lpDIB)) + wBitCount = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount; + else + wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount; + + /* return number of colors based on bits per pixel */ + switch (wBitCount) + { + case 1: + return 2; + + case 4: + return 16; + + case 8: + return 256; + + default: + return 0; + } +} + + +/************************************************************************* + * + * CreateDIBPalette() + * + * Parameter: + * + * HDIB hDIB - specifies the DIB + * + * Return Value: + * + * HPALETTE - specifies the palette + * + * Description: + * + * This function creates a palette from a DIB by allocating memory for the + * logical palette, reading and storing the colors from the DIB's color table + * into the logical palette, creating a palette from this logical palette, + * and then returning the palette's handle. This allows the DIB to be + * displayed using the best possible colors (important for DIBs with 256 or + * more colors). + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +HPALETTE FAR CreateDIBPalette(HDIB hDIB) +{ + LPLOGPALETTE lpPal; // pointer to a logical palette + HANDLE hLogPal; // handle to a logical palette + HPALETTE hPal = NULL; // handle to a palette + int i, wNumColors; // loop index, number of colors in color table + LPSTR lpbi; // pointer to packed-DIB + LPBITMAPINFO lpbmi; // pointer to BITMAPINFO structure (Win3.0) + LPBITMAPCOREINFO lpbmc; // pointer to BITMAPCOREINFO structure (OS/2) + BOOL bWinStyleDIB; // flag which signifies whether this is a Win3.0 DIB + + /* if handle to DIB is invalid, return NULL */ + + if (!hDIB) + return NULL; + + /* lock DIB memory block and get a pointer to it */ + lpbi = (LPSTR)GlobalLock(hDIB); + + /* get pointer to BITMAPINFO (Win 3.0) */ + lpbmi = (LPBITMAPINFO)lpbi; + + /* get pointer to BITMAPCOREINFO (OS/2 1.x) */ + lpbmc = (LPBITMAPCOREINFO)lpbi; + + /* get the number of colors in the DIB */ + wNumColors = DIBNumColors(lpbi); + + /* is this a Win 3.0 DIB? */ + bWinStyleDIB = IS_WIN30_DIB(lpbi); + if (wNumColors) + { + /* allocate memory block for logical palette */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * + wNumColors); + + /* if not enough memory, clean up and return NULL */ + if (!hLogPal) + { + GlobalUnlock(hDIB); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set version and number of palette entries */ + lpPal->palVersion = PALVERSION; + lpPal->palNumEntries = (WORD)wNumColors; + + /* store RGB triples (if Win 3.0 DIB) or RGB quads (if OS/2 DIB) + * into palette + */ + for (i = 0; i < wNumColors; i++) + { + if (bWinStyleDIB) + { + lpPal->palPalEntry[i].peRed = lpbmi->bmiColors[i].rgbRed; + lpPal->palPalEntry[i].peGreen = lpbmi->bmiColors[i].rgbGreen; + lpPal->palPalEntry[i].peBlue = lpbmi->bmiColors[i].rgbBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + else + { + lpPal->palPalEntry[i].peRed = lpbmc->bmciColors[i].rgbtRed; + lpPal->palPalEntry[i].peGreen = lpbmc->bmciColors[i].rgbtGreen; + lpPal->palPalEntry[i].peBlue = lpbmc->bmciColors[i].rgbtBlue; + lpPal->palPalEntry[i].peFlags = 0; + } + } + + /* create the palette and get handle to it */ + hPal = CreatePalette(lpPal); + + /* if error getting handle to palette, clean up and return NULL */ + if (!hPal) + { + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + return NULL; + } + } + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + GlobalUnlock(hDIB); + + /* return handle to DIB's palette */ + return hPal; +} + + +/************************************************************************* + * + * DIBToBitmap() + * + * Parameters: + * + * HDIB hDIB - specifies the DIB to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HBITMAP - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a bitmap from a DIB using the specified palette. + * If no palette is specified, default is used. + * + * NOTE: + * + * The bitmap returned from this funciton is always a bitmap compatible + * with the screen (e.g. same bits/pixel and color planes) rather than + * a bitmap with the same attributes as the DIB. This behavior is by + * design, and occurs because this function calls CreateDIBitmap to + * do its work, and CreateDIBitmap always creates a bitmap compatible + * with the hDC parameter passed in (because it in turn calls + * CreateCompatibleBitmap). + * + * So for instance, if your DIB is a monochrome DIB and you call this + * function, you will not get back a monochrome HBITMAP -- you will + * get an HBITMAP compatible with the screen DC, but with only 2 + * colors used in the bitmap. + * + * If your application requires a monochrome HBITMAP returned for a + * monochrome DIB, use the function SetDIBits(). + * + * Also, the DIBpassed in to the function is not destroyed on exit. This + * must be done later, once it is no longer needed. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 3/27/92 Mark Bader Added comments about resulting + * bitmap format + * + ************************************************************************/ + + +HBITMAP FAR DIBToBitmap(HDIB hDIB, HPALETTE hPal) +{ + LPSTR lpDIBHdr, lpDIBBits; // pointer to DIB header, pointer to DIB bits + HBITMAP hBitmap; // handle to device-dependent bitmap + HDC hDC; // handle to DC + HPALETTE hOldPal = NULL; // handle to a palette + + /* if invalid handle, return NULL */ + + if (!hDIB) + return NULL; + + /* lock memory block and get a pointer to it */ + lpDIBHdr = (LPSTR)GlobalLock(hDIB); + + /* get a pointer to the DIB bits */ + lpDIBBits = FindDIBBits(lpDIBHdr); + + /* get a DC */ + hDC = GetDC(NULL); + if (!hDC) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + return NULL; + } + + /* select and realize palette */ + if (hPal) + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* create bitmap from DIB info. and bits */ + hBitmap = CreateDIBitmap(hDC, (LPBITMAPINFOHEADER)lpDIBHdr, CBM_INIT, + lpDIBBits, (LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS); + + /* restore previous palette */ + if (hOldPal) + SelectPalette(hDC, hOldPal, FALSE); + + /* clean up */ + ReleaseDC(NULL, hDC); + GlobalUnlock(hDIB); + + /* return handle to the bitmap */ + return hBitmap; +} + + +/************************************************************************* + * + * BitmapToDIB() + * + * Parameters: + * + * HBITMAP hBitmap - specifies the bitmap to convert + * + * HPALETTE hPal - specifies the palette to use with the bitmap + * + * Return Value: + * + * HDIB - identifies the device-dependent bitmap + * + * Description: + * + * This function creates a DIB from a bitmap using the specified palette. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/10/91 Patrick Schreiber Added bits per pixel validation + * and check GetObject return value + * + ************************************************************************/ + + +HDIB FAR BitmapToDIB(HBITMAP hBitmap, HPALETTE hPal) +{ + BITMAP bm; // bitmap structure + BITMAPINFOHEADER bi; // bitmap header + BITMAPINFOHEADER FAR *lpbi; // pointer to BITMAPINFOHEADER + DWORD dwLen; // size of memory block + HANDLE hDIB, h; // handle to DIB, temp handle + HDC hDC; // handle to DC + WORD biBits; // bits per pixel + + /* check if bitmap handle is valid */ + + if (!hBitmap) + return NULL; + + /* fill in BITMAP structure, return NULL if it didn't work */ + if (!GetObject(hBitmap, sizeof(bm), (LPSTR)&bm)) + return NULL; + + /* if no palette is specified, use default palette */ + if (hPal == NULL) + hPal = GetStockObject(DEFAULT_PALETTE); + + /* calculate bits per pixel */ + biBits = (WORD)( bm.bmPlanes * bm.bmBitsPixel ); + + /* make sure bits per pixel is valid */ + if (biBits <= 1) + biBits = 1; + else if (biBits <= 4) + biBits = 4; + else if (biBits <= 8) + biBits = 8; + else /* if greater than 8-bit, force to 24-bit */ + biBits = 24; + + /* initialize BITMAPINFOHEADER */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = bm.bmWidth; + bi.biHeight = bm.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = biBits; + bi.biCompression = BI_RGB; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* calculate size of memory block required to store BITMAPINFO */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi); + + /* get a DC */ + hDC = GetDC(NULL); + + /* select and realize our palette */ + hPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* alloc memory block to store our bitmap */ + hDIB = GlobalAlloc(GHND, dwLen); + + /* if we couldn't get memory block */ + if (!hDIB) + { + /* clean up and return NULL */ + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* use our bitmap info. to fill BITMAPINFOHEADER */ + *lpbi = bi; + + /* call GetDIBits with a NULL lpBits param, so it will calculate the + * biSizeImage field for us + */ + GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, NULL, (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS); + + /* get the info. returned by GetDIBits and unlock memory block */ + bi = *lpbi; + GlobalUnlock(hDIB); + + /* if the driver did not fill in the biSizeImage field, make one up */ + if (bi.biSizeImage == 0) + bi.biSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * biBits) * bm.bmHeight; + + /* realloc the buffer big enough to hold all the bits */ + dwLen = bi.biSize + PaletteSize((LPSTR)&bi) + bi.biSizeImage; + h = GlobalReAlloc(hDIB, dwLen, 0); + if (h) + hDIB = h; + else + { + /* clean up and return NULL */ + GlobalFree(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + + /* lock memory block and get pointer to it */ + lpbi = (BITMAPINFOHEADER FAR *)GlobalLock(hDIB); + + /* call GetDIBits with a NON-NULL lpBits param, and actualy get the + * bits this time + */ + if (GetDIBits(hDC, hBitmap, 0, (WORD)bi.biHeight, (LPSTR)lpbi + (WORD)lpbi + ->biSize + PaletteSize((LPSTR)lpbi), (LPBITMAPINFO)lpbi, + DIB_RGB_COLORS) == 0) + { + /* clean up and return NULL */ + GlobalUnlock(hDIB); + hDIB = NULL; + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + return NULL; + } + bi = *lpbi; + + /* clean up */ + GlobalUnlock(hDIB); + SelectPalette(hDC, hPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + /* return handle to the DIB */ + return hDIB; +} + + +/************************************************************************* + * + * PalEntriesOnDevice() + * + * Parameter: + * + * HDC hDC - device context + * + * Return Value: + * + * int - number of palette entries on device + * + * Description: + * + * This function gets the number of palette entries on the specified device + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * + ************************************************************************/ + + +int FAR PalEntriesOnDevice(HDC hDC) +{ + int nColors; // number of colors + + /* Find out the number of palette entries on this + * device. + */ + + nColors = GetDeviceCaps(hDC, SIZEPALETTE); + + /* For non-palette devices, we'll use the # of system + * colors for our palette size. + */ + if (!nColors) + nColors = GetDeviceCaps(hDC, NUMCOLORS); + assert(nColors); + return nColors; +} + + +/************************************************************************* + * + * GetSystemPalette() + * + * Parameters: + * + * None + * + * Return Value: + * + * HPALETTE - handle to a copy of the current system palette + * + * Description: + * + * This function returns a handle to a palette which represents the system + * palette. The system RGB values are copied into our logical palette using + * the GetSystemPaletteEntries function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 9/15/91 Patrick Schreiber Added header and comments + * 12/20/91 Mark Bader Added GetSystemPaletteEntries call + * + ************************************************************************/ + + +HPALETTE FAR GetSystemPalette(void) +{ + HDC hDC; // handle to a DC + static HPALETTE hPal = NULL; // handle to a palette + HANDLE hLogPal; // handle to a logical palette + LPLOGPALETTE lpLogPal; // pointer to a logical palette + int nColors; // number of colors + + /* Find out how many palette entries we want. */ + + hDC = GetDC(NULL); + if (!hDC) + return NULL; + nColors = PalEntriesOnDevice(hDC); // Number of palette entries + + /* Allocate room for the palette and lock it. */ + hLogPal = GlobalAlloc(GHND, sizeof(LOGPALETTE) + nColors * sizeof( + PALETTEENTRY)); + + /* if we didn't get a logical palette, return NULL */ + if (!hLogPal) + return NULL; + + /* get a pointer to the logical palette */ + lpLogPal = (LPLOGPALETTE)GlobalLock(hLogPal); + + /* set some important fields */ + lpLogPal->palVersion = (WORD)PALVERSION; + lpLogPal->palNumEntries = (WORD)nColors; + + /* Copy the current system palette into our logical palette */ + + GetSystemPaletteEntries(hDC, 0, nColors, + (LPPALETTEENTRY)(lpLogPal->palPalEntry)); + + /* Go ahead and create the palette. Once it's created, + * we no longer need the LOGPALETTE, so free it. + */ + + hPal = CreatePalette(lpLogPal); + + /* clean up */ + GlobalUnlock(hLogPal); + GlobalFree(hLogPal); + ReleaseDC(NULL, hDC); + + return hPal; +} + + +/************************************************************************* + * + * AllocRoomForDIB() + * + * Parameters: + * + * BITMAPINFOHEADER - bitmap info header stucture + * + * HBITMAP - handle to the bitmap + * + * Return Value: + * + * HDIB - handle to memory block + * + * Description: + * + * This routine takes a BITMAPINOHEADER, and returns a handle to global + * memory which can contain a DIB with that header. It also initializes + * the header portion of the global memory. GetDIBits() is used to determine + * the amount of room for the DIB's bits. The total amount of memory + * needed = sizeof(BITMAPINFOHEADER) + size of color table + size of bits. + * + * History: Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/11/91 Patrick Schreiber Added header and some comments + * + ************************************************************************/ + +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap) +{ + DWORD dwLen; + HANDLE hDIB; + HDC hDC; + LPBITMAPINFOHEADER lpbi; + HANDLE hTemp; + + /* Figure out the size needed to hold the BITMAPINFO structure + * (which includes the BITMAPINFOHEADER and the color table). + */ + + dwLen = bi.biSize + PaletteSize((LPSTR) &bi); + hDIB = GlobalAlloc(GHND,dwLen); + + /* Check that DIB handle is valid */ + if (!hDIB) + return NULL; + + /* Set up the BITMAPINFOHEADER in the newly allocated global memory, + * then call GetDIBits() with lpBits = NULL to have it fill in the + * biSizeImage field for us. + */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + *lpbi = bi; + + hDC = GetDC(NULL); + GetDIBits(hDC, hBitmap, 0, (WORD) bi.biHeight, + NULL, (LPBITMAPINFO) lpbi, DIB_RGB_COLORS); + ReleaseDC(NULL, hDC); + + /* If the driver did not fill in the biSizeImage field, + * fill it in -- NOTE: this is a bug in the driver! + */ + if (lpbi->biSizeImage == 0) + lpbi->biSizeImage = WIDTHBYTES((DWORD)lpbi->biWidth * lpbi->biBitCount) * + lpbi->biHeight; + + /* Get the size of the memory block we need */ + dwLen = lpbi->biSize + PaletteSize((LPSTR) &bi) + lpbi->biSizeImage; + + /* Unlock the memory block */ + GlobalUnlock(hDIB); + + /* ReAlloc the buffer big enough to hold all the bits */ + hTemp = GlobalReAlloc(hDIB,dwLen,0); + if (hTemp) + return hTemp; + else + { + /* Else free memory block and return failure */ + GlobalFree(hDIB); + return NULL; + } +} + + +/************************************************************************* + * + * ChangeDIBFormat() + * + * Parameter: + * + * HDIB - handle to packed-DIB in memory + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert the bits per pixel and/or the compression + * format of the specified DIB. Note: If the conversion was unsuccessful, + * we return NULL. The original DIB is left alone. Don't use code like the + * following: + * + * hMyDIB = ChangeDIBFormat(hMyDIB, 8, BI_RLE4); + * + * The conversion will fail, but hMyDIB will now be NULL and the original + * DIB will now hang around in memory. We could have returned the old + * DIB, but we wanted to allow the programmer to check whether this + * conversion succeeded or failed. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * + ************************************************************************/ + +HDIB FAR ChangeDIBFormat(HDIB hDIB, WORD wBitCount, DWORD dwCompression) +{ + HDC hDC; // Handle to DC + HBITMAP hBitmap; // Handle to bitmap + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap info + HDIB hNewDIB = NULL; // Handle to new DIB + HPALETTE hPal, hOldPal; // Handle to palette, prev pal + WORD DIBBPP, NewBPP; // DIB bits per pixel, new bpp + DWORD DIBComp, NewComp;// DIB compression, new compression + + /* Check for a valid DIB handle */ + if (!hDIB) + return NULL; + + /* Get the old DIB's bits per pixel and compression format */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDIB); + DIBBPP = ((LPBITMAPINFOHEADER)lpbi)->biBitCount; + DIBComp = ((LPBITMAPINFOHEADER)lpbi)->biCompression; + GlobalUnlock(hDIB); + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure */ + if (wBitCount == 0) + { + NewBPP = DIBBPP; + if ((dwCompression == BI_RLE4 && NewBPP == 4) || + (dwCompression == BI_RLE8 && NewBPP == 8) || + (dwCompression == BI_RGB)) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Save the old DIB's palette */ + hPal = CreateDIBPalette(hDIB); + if (!hPal) + return NULL; + + /* Convert old DIB to a bitmap */ + hBitmap = DIBToBitmap(hDIB, hPal); + if (!hBitmap) + { + DeleteObject(hPal); + return NULL; + } + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* Get a DC and select/realize our palette in it */ + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + + if (hNewDIB) + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + + DeleteObject(hBitmap); + DeleteObject(hPal); + + return hNewDIB; +} + + +/************************************************************************* + * + * ChangeBitmapFormat() + * + * Parameter: + * + * HBITMAP - handle to a bitmap + * + * WORD - desired bits per pixel + * + * DWORD - desired compression format + * + * HPALETTE - handle to palette + * + * Return Value: + * + * HDIB - handle to the new DIB if successful, else NULL + * + * Description: + * + * This function will convert a bitmap to the specified bits per pixel + * and compression format. The bitmap and it's palette will remain + * after calling this function. + * + * History: + * + * Date Author Reason + * 6/01/91 Garrett McAuliffe Created + * 12/10/91 Patrick Schreiber Modified from converting RGB to RLE8 + * to converting RGB/RLE to RGB/RLE. + * Added wBitCount and dwCompression + * parameters. Also added header and + * comments. + * 12/11/91 Patrick Schreiber Destroy old DIB if conversion was + * successful. + * 12/16/91 Patrick Schreiber Modified from converting DIB to new + * DIB to bitmap to new DIB. Added palette + * parameter. + * + ************************************************************************/ + +HDIB FAR ChangeBitmapFormat(HBITMAP hBitmap, + WORD wBitCount, + DWORD dwCompression, + HPALETTE hPal) +{ + HDC hDC; // Screen DC + HDIB hNewDIB=NULL; // Handle to new DIB + BITMAP Bitmap; // BITMAP data structure + BITMAPINFOHEADER bi; // Bitmap info. header + LPBITMAPINFOHEADER lpbi; // Pointer to bitmap header + HPALETTE hOldPal=NULL; // Handle to palette + WORD NewBPP; // New bits per pixel + DWORD NewComp; // New compression format + + /* Check for a valid bitmap handle */ + if (!hBitmap) + return NULL; + + /* Validate wBitCount and dwCompression + * They must match correctly (i.e., BI_RLE4 and 4 BPP or + * BI_RLE8 and 8BPP, etc.) or we return failure + */ + if (wBitCount == 0) + { + NewComp = dwCompression; + if (NewComp == BI_RLE4) + NewBPP = 4; + else if (NewComp == BI_RLE8) + NewBPP = 8; + else /* Not enough info */ + return NULL; + } + else if (wBitCount == 1 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else if (wBitCount == 4) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE4) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 8) + { + NewBPP = wBitCount; + if (dwCompression == BI_RGB || dwCompression == BI_RLE8) + NewComp = dwCompression; + else + return NULL; + } + else if (wBitCount == 24 && dwCompression == BI_RGB) + { + NewBPP = wBitCount; + NewComp = BI_RGB; + } + else + return NULL; + + /* Get info about the bitmap */ + GetObject(hBitmap, sizeof(BITMAP), (LPSTR)&Bitmap); + + /* Fill in the BITMAPINFOHEADER appropriately */ + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = Bitmap.bmWidth; + bi.biHeight = Bitmap.bmHeight; + bi.biPlanes = 1; + bi.biBitCount = NewBPP; + bi.biCompression = NewComp; + bi.biSizeImage = 0; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + /* Go allocate room for the new DIB */ + hNewDIB = AllocRoomForDIB(bi, hBitmap); + if (!hNewDIB) + return NULL; + + /* Get a pointer to the new DIB */ + lpbi = (LPBITMAPINFOHEADER)GlobalLock(hNewDIB); + + /* If we have a palette, get a DC and select/realize it */ + if (hPal) + { + hDC = GetDC(NULL); + hOldPal = SelectPalette(hDC, hPal, FALSE); + RealizePalette(hDC); + } + + /* Call GetDIBits and get the new DIB bits */ + if (!GetDIBits(hDC, hBitmap, 0, (WORD) lpbi->biHeight, + (LPSTR)lpbi + (WORD)lpbi->biSize + PaletteSize((LPSTR)lpbi), + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS)) + { + GlobalUnlock(hNewDIB); + GlobalFree(hNewDIB); + hNewDIB = NULL; + } + + /* Clean up and return */ + if (hOldPal) + { + SelectPalette(hDC, hOldPal, TRUE); + RealizePalette(hDC); + ReleaseDC(NULL, hDC); + } + + if (hNewDIB) + { + /* Unlock the new DIB's memory block */ + GlobalUnlock(hNewDIB); + } + + return hNewDIB; +} +#endif \ No newline at end of file diff --git a/code/redalert/dibutil.h b/code/redalert/dibutil.h new file mode 100644 index 0000000..9b3dc60 --- /dev/null +++ b/code/redalert/dibutil.h @@ -0,0 +1,38 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + * dibutil.h + * + * Copyright (c) 1991 Microsoft Corporation. All rights reserved. + * + * Header file for Device-Independent Bitmap (DIB) API. Provides + * function prototypes and constants for the following functions: + * + * AllocRoomForDIB() - Allocates memory for a DIB + * + */ + + +/* DIB constants */ +#define PALVERSION 0x300 + +/* DIB macros */ +#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi)) == sizeof(BITMAPINFOHEADER)) +#define RECTWIDTH(lpRect) ((lpRect)->right - (lpRect)->left) +#define RECTHEIGHT(lpRect) ((lpRect)->bottom - (lpRect)->top) + +/* function prototypes */ +HANDLE AllocRoomForDIB(BITMAPINFOHEADER bi, HBITMAP hBitmap); diff --git a/code/redalert/display.cpp b/code/redalert/display.cpp new file mode 100644 index 0000000..0687890 --- /dev/null +++ b/code/redalert/display.cpp @@ -0,0 +1,5109 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DISPLAY.CPP 3 3/09/97 8:04p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * DisplayClass::DisplayClass -- Default constructor for display class. * + * DisplayClass::Draw_It -- Draws the tactical map. * + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * DisplayClass::Flag_To_Redraw -- Flags the display so that it will be redrawn as soon as poss* + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * DisplayClass::Init_Clear -- Clears the display to a known state. * + * DisplayClass::Init_IO -- Creates the map's button list * + * DisplayClass::Init_Theater -- Theater-specific initialization * + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * DisplayClass::Next_Object -- Searches for next object on display. * + * DisplayClass::One_Time -- Performs any special one time initializations. * + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +/* +** These layer control elements are used to group the displayable objects +** so that proper overlap can be obtained. +*/ +LayerClass DisplayClass::Layer[LAYER_COUNT]; + +/* +** Fading tables +*/ +unsigned char DisplayClass::FadingBrighten[256]; +unsigned char DisplayClass::FadingShade[256]; +unsigned char DisplayClass::FadingWayDark[256]; +unsigned char DisplayClass::FadingLight[256]; +unsigned char DisplayClass::FadingGreen[256]; +unsigned char DisplayClass::FadingYellow[256]; +unsigned char DisplayClass::FadingRed[256]; +unsigned char DisplayClass::TranslucentTable[(MAGIC_COL_COUNT+1)*256]; +unsigned char DisplayClass::WhiteTranslucentTable[(1+1)*256]; +unsigned char DisplayClass::MouseTranslucentTable[(4+1)*256]; +void const * DisplayClass::TransIconset; +// jmarshall +Image_t* DisplayClass::TransIconsetHD; +// jmarshall end +unsigned char DisplayClass::UnitShadow[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; +unsigned char DisplayClass::SpecialGhost[2*256]; + +void const * DisplayClass::ShadowShapes; +unsigned char DisplayClass::ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + +/* +** Bit array of cell redraw flags +*/ +BooleanVectorClass DisplayClass::CellRedraw; + +/* +** The main button that intercepts user input to the map +*/ +DisplayClass::TacticalClass DisplayClass::TacButton; + +// +// We need a way to bypass visible view checks when we are running in the context of GlyphX without using the +// internal C&C renderer. We shouldn't know or care what the user is actually looking at +// ST - 4/17/2019 9:01AM +// +bool DisplayClass::IgnoreViewConstraints = false; + + +static int const TEX_X = 0; +static int const TEX_Y = 6; +static int const TEX_W = 14; + +//Added for getting the input for special character keys from the client +// - 6/26/2019 JAS +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + + +/*********************************************************************************************** + * DisplayClass::DisplayClass -- Default constructor for display class. * + * * + * This constructor for the display class just initializes some of the display settings. * + * Most settings are initialized with the correct values at the time that the Init function * + * is called. There are some cases where default values are wise and this routine fills * + * those particular ones in. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + *=============================================================================================*/ +DisplayClass::DisplayClass(void) : + TacticalCoord(0), + TacLeptonWidth(0), + TacLeptonHeight(0), + ZoneCell(0), + ZoneOffset(0), + CursorSize(0), + ProximityCheck(false), + PendingObjectPtr(0), + PendingObject(0), + PendingHouse(HOUSE_NONE), + TacPixelX(0), + TacPixelY(0), + DesiredTacticalCoord(0), + IsToRedraw(true), + IsRepairMode(false), + IsSellMode(false), + IsTargettingMode(SPC_NONE), + IsRubberBand(false), + IsTentative(false), + IsShadowPresent(false), + BandX(0), + BandY(0), + NewX(0), + NewY(0) +{ + ShadowShapes = 0; + TransIconset = 0; + TransIconsetHD = 0; + + Set_View_Dimensions(0, 8 * RESFACTOR, ScreenWidth, ScreenHeight); +} + + +/*********************************************************************************************** + * DisplayClass::One_Time -- Performs any special one time initializations. * + * * + * This routine is called from the game initialization process. It is to perform any one * + * time initializations necessary for the map display system. It allocates the staging * + * buffer needed for the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called ONCE and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Handles layer system now. * + * 06/02/1994 JLB : Takes care of misc display tables and data allocation. * + *=============================================================================================*/ +void DisplayClass::One_Time(void) +{ + MapClass::One_Time(); + + /* + ** Init the CellRedraw bit array. Do not do this in the constructor, since the + ** BooleanVector may not have been constructed yet. + */ + CellRedraw.Resize(MAP_CELL_TOTAL); + + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].One_Time(); + } + + /* + ** Load the generic transparent icon set. + */ + TransIconset = MFCD::Retrieve("TRANS.ICN"); +// jmarshall + TransIconsetHD = Load_Stamp("TRANS_ICON", TransIconset); +// jmarshall end + + #ifndef NDEBUG + RawFileClass file("SHADOW.SHP"); + if (file.Is_Available()) { + ShadowShapes = Load_Alloc_Data(file); + } else { + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + } + #else + ShadowShapes = MFCD::Retrieve("SHADOW.SHP"); + #endif + + Set_View_Dimensions(0, 0); +} + + +/*********************************************************************************************** + * DisplayClass::Init_Clear -- clears the display to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_Clear(void) +{ + MapClass::Init_Clear(); + + /* + ** Clear any object being placed + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + CursorSize = 0; + IsTargettingMode = SPC_NONE; + IsRepairMode = false; + IsRubberBand = false; + IsTentative = false; + IsSellMode = false; + + /* + ** Empty all the display's layers + */ + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + Layer[layer].Init(); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_IO -- clears & re-builds the map's button list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Init_IO(void) +{ + MapClass::Init_IO(); + /* + ** Re-attach our buttons to the main map button list, only in non-edit mode. + */ + if (!Debug_Map) { + TacButton.Zap(); + Add_A_Button(TacButton); + } +} + + +/*********************************************************************************************** + * DisplayClass::Init_Theater -- Performs theater-specific initialization (mixfiles, etc) * + * * + * INPUT: * + * theater new theater * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + * 05/07/1996 JLB : Added translucent tables. * + *=============================================================================================*/ +void DisplayClass::Init_Theater(TheaterType theater) +{ + char fullname[16]; + static TLucentType const MouseCols[4] = { + {BLACK, BLACK, 110, 0}, + {WHITE, WHITE, 110, 0}, + {LTGREY, LTGREY, 110, 0}, + {DKGREY, DKGREY, 110, 0} + }; + static TLucentType const MagicCols[MAGIC_COL_COUNT] = { + {32,32,110,0}, + {33,33,110,0}, + {34,34,110,0}, + {35,35,110,0}, + {36,36,110,0}, + {37,37,110,0}, + {38,38,110,0}, + {39,39,110,0}, + {BLACK, BLACK, 200, 0}, + {WHITE, BLACK, 40, 0}, + {LTGREY, BLACK, 80, 0}, + {DKGREY, BLACK, 140, 0}, + {LTGREEN, BLACK,130,0} + }; + static TLucentType const WhiteCols[1] = { + {1, WHITE, 80, 0} + }; + static TLucentType const ShadowCols[SHADOW_COL_COUNT] = { + {WHITE+1, BLACK,130,0}, + {WHITE, BLACK,170,0}, + {LTGRAY, BLACK,250,0}, + {DKGRAY, BLACK,250,0} + }; + static TLucentType const UShadowCols[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,130,0} + }; + static TLucentType const UShadowColsAir[USHADOW_COL_COUNT] = { + {LTGREEN, WHITE,0,0} + }; + static TLucentType const UShadowColsSnow[USHADOW_COL_COUNT] = { + {LTGREEN, BLACK,75,0} + }; + + /* + ** Invoke parent's init routine. + */ + MapClass::Init_Theater(theater); + + /* + ** Save the new theater value + */ + Scen.Theater = theater; + + /* + ** Unload old mixfiles, and cache the new ones + */ + sprintf(fullname, "%s.MIX", Theaters[theater].Root); + +#ifndef WIN32 +LastTheater = THEATER_NONE; +#endif + + if (Scen.Theater != LastTheater) { + if (TheaterData != NULL) { + delete TheaterData; + } + TheaterData = new MFCD(fullname, &FastKey); + assert(TheaterData != NULL); + + bool theaterload = TheaterData->Cache(TheaterBuffer); + assert(theaterload); +// LastTheater = Scen.Theater; + } + + /* + ** Load the custom palette associated with this theater. + ** The fading palettes will have to be generated as well. + */ + sprintf(fullname, "%s.PAL", Theaters[theater].Root); + PaletteClass const * ptr = (PaletteClass *)MFCD::Retrieve(fullname); + + GamePalette = * ptr; + + OriginalPalette = GamePalette; + + Build_Fading_Table(GamePalette.Get_Data(), FadingGreen, GREEN, 110); + + Build_Fading_Table(GamePalette.Get_Data(), FadingYellow, YELLOW, 140); + + Build_Fading_Table(GamePalette.Get_Data(), FadingRed, RED, 140); + + Build_Translucent_Table(GamePalette, &MouseCols[0], 4, MouseTranslucentTable); + + Build_Translucent_Table(GamePalette, &MagicCols[0], MAGIC_COL_COUNT, TranslucentTable); + + Build_Translucent_Table(GamePalette, &WhiteCols[0], 1, WhiteTranslucentTable); + + Build_Translucent_Table(GamePalette, &ShadowCols[0], SHADOW_COL_COUNT, ShadowTrans); + + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsAir[0], USHADOW_COL_COUNT, UnitShadowAir); + memcpy(&UnitShadowAir[256], ColorRemaps[PCOLOR_GOLD].RemapTable, sizeof(ColorRemaps[PCOLOR_GOLD].RemapTable)); + if (theater == THEATER_SNOW) { + Conquer_Build_Translucent_Table(GamePalette, &UShadowColsSnow[0], USHADOW_COL_COUNT, UnitShadow); + } else { + Conquer_Build_Translucent_Table(GamePalette, &UShadowCols[0], USHADOW_COL_COUNT, UnitShadow); + } + + if (theater == THEATER_SNOW) { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 75); + } else { + Conquer_Build_Fading_Table(GamePalette, FadingShade, BLACK, 130); + } + + Conquer_Build_Fading_Table(GamePalette, FadingLight, WHITE, 85); + + /* + ** Create the shadow color used by aircraft. + */ + Conquer_Build_Fading_Table(GamePalette, &SpecialGhost[256], BLACK, 100); + for (int index = 0; index < 256; index++) { + SpecialGhost[index] = 0; + } + + Make_Fading_Table(GamePalette, FadingBrighten, WHITE, 25); + + Make_Fading_Table(GamePalette, FadingWayDark, DKGRAY, 192); + + /* + ** Adjust the palette according to the visual control option settings. + */ + Options.Fixup_Palette(); +} + + +/*********************************************************************************************** + * DisplayClass::Text_Overlap_List -- Creates cell overlap list for specified text string. * + * * + * This routine is used to create an overlap list that specifies all the cells that are * + * covered by the specified text string. This overlap list is used to handle map refresh * + * logic. * + * * + * INPUT: text -- Pointer to the text that would appear on the map and must have an * + * overlap list generated. * + * * + * x,y -- The coordinates that the text would appear (upper left corner). * + * * + * OUTPUT: Returns with a pointer to an overlap list that covers all cells "under" the text * + * if were displayed at the coordinates specified. The list is actually a series of * + * offsets from the display's upper left corner cell number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 12/07/1994 JLB : Sidebar fixup. * + * 08/13/1995 JLB : Optimized for variable sized help text. * + *=============================================================================================*/ +short const * DisplayClass::Text_Overlap_List(char const * text, int x, int y) const +{ + static short _list[60]; + int count = ARRAY_SIZE(_list); + + if (text != NULL) { + short * ptr = &_list[0]; + int len = String_Pixel_Width(text)+CELL_PIXEL_W; + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth); + + /* + ** If the help text would spill into the sidebar, then flag this fact, but + ** shorten the apparent length so that the icon list calculation will + ** function correctly. + */ + if (x+len >= TacPixelX+Lepton_To_Pixel(TacLeptonWidth)) { + len = right-x; + *ptr++ = REFRESH_SIDEBAR; + count--; + } + + /* + ** Build the list of overlap cell offset values according to the text + ** coordinate and the length. + */ + if (x <= right) { + CELL ul = Click_Cell_Calc(x, y-1); + CELL lr = Click_Cell_Calc(x+len-1, Bound(y+24, TacPixelY, TacPixelY+Lepton_To_Pixel(TacLeptonHeight) - 1)); + + if (ul == -1) ul = Click_Cell_Calc(x, y); + + if (ul != -1 && lr != -1) { + for (int yy = Cell_Y(ul); yy <= Cell_Y(lr); yy++) { + for (int xx = Cell_X(ul); xx <= Cell_X(lr); xx++) { + *ptr++ = XY_Cell(xx, yy) - Coord_Cell(TacticalCoord); + count--; + if (count < 2) break; + } + if (count < 2) break; + } + } + } + + *ptr = REFRESH_EOL; + } + return(_list); +} + + +/*********************************************************************************************** + * DisplayClass::Set_View_Dimensions -- Sets the tactical display screen coordinates. * + * * + * Use this routine to set the tactical map screen coordinates and dimensions. This routine * + * is typically used when the screen size or position changes as a result of the sidebar * + * changing position or appearance. * + * * + * INPUT: x,y -- The X and Y pixel position on the screen for the tactical map upper left * + * corner. * + * * + * width -- The width of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * height-- The height of the tactical display (in icons). If this parameter is * + * omitted, then the width will be as wide as the screen will allow. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/1994 JLB : Created. * + * 06/27/1995 JLB : Adjusts tactical map position if necessary. * + *=============================================================================================*/ +void DisplayClass::Set_View_Dimensions(int x, int y, int width, int height) +{ + if (width == -1) width = SeenBuff.Get_Width(); + TacLeptonWidth = Pixel_To_Lepton( width - x); + + if (height == -1) height = SeenBuff.Get_Height(); + TacLeptonHeight = Pixel_To_Lepton(height - y); + + /* + ** Adjust the tactical cell if it is now in an invalid position + ** because of the changed dimensions. + */ + int xx = Coord_X(TacticalCoord) - (MapCellX * CELL_LEPTON_W); + int yy = Coord_Y(TacticalCoord) - (MapCellY * CELL_LEPTON_H); + + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, MapCellWidth * CELL_LEPTON_W, MapCellHeight * CELL_LEPTON_H); + + Set_Tactical_Position(XY_Coord(xx + (MapCellX * CELL_LEPTON_W), yy + (MapCellY * CELL_LEPTON_H))); + + TacPixelX = x; + TacPixelY = y; + WindowList[WINDOW_TACTICAL][WINDOWX] = x; + WindowList[WINDOW_TACTICAL][WINDOWY] = y; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = Lepton_To_Pixel(TacLeptonWidth); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = Lepton_To_Pixel(TacLeptonHeight); + if (Window == WINDOW_TACTICAL) { + Change_Window(0); + Change_Window(Window); + } + IsToRedraw = true; + Flag_To_Redraw(false); + + TacButton.X = TacPixelX; + TacButton.Y = TacPixelY; + TacButton.Width = Lepton_To_Pixel(TacLeptonWidth); + TacButton.Height = Lepton_To_Pixel(TacLeptonHeight); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Shape -- Changes the shape of the terrain square cursor. * + * * + * This routine is used to set up the terrain cursor according to the size of the object * + * that is to be placed down. The terrain cursor looks like an arbitrary collection of * + * hatched square overlays. Typical use is when placing buildings. * + * * + * INPUT: list -- A pointer to the list that contains offsets to the cells that are to * + * be marked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1994 JLB : Created. * + * 06/26/1995 JLB : Puts placement cursor into static buffer. * + *=============================================================================================*/ +void DisplayClass::Set_Cursor_Shape(short const * list) +{ + if (CursorSize) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + ZoneOffset = 0; + + if (list) { + int w,h; + static short _list[50]; + + memcpy(_list, list, sizeof(_list)); + CursorSize = _list; + Get_Occupy_Dimensions (w, h, CursorSize); + ZoneOffset = -(((h/2)*MAP_CELL_W)+(w/2)); + Cursor_Mark(ZoneCell+ZoneOffset, true); + } else { + CursorSize = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Passes_Proximity_Check -- Determines if building placement is near friendly sq* + * * + * This routine is used by the building placement cursor logic to determine whether the * + * at the current cursor position if the building would be adjacent to another friendly * + * building. In cases where this is not true, then the building cannot be placed at all. * + * This determination is returned by the function. * + * * + * INPUT: object -- The building object that the current placement system is examining. * + * * + * house -- The house to base the proximity check upon. Typically this is the * + * player's house, but in multiplay, the computer needs to check for * + * proximity as well. * + * * + * list -- Pointer to the building's offset list. * + * * + * trycell -- The cell to base the offset list on. * + * * + * OUTPUT: bool; Can the pending building object be placed at the present cursor location * + * checking only for proximity to friendly buildings? If this isn't for a * + * building type object, then this routine always returns true. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/06/1994 JLB : Created. * + * 06/07/1994 JLB : Handles concrete check. * + * 10/11/1994 BWG : Added IsProximate check for ore refineries * + *=============================================================================================*/ +bool DisplayClass::Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const +{ + short const * ptr; + int retval = -1; + bool noradar = false; + //bool nomapped = false; // Not used. ST - 8/6/2019 10:51AM + bool shipyard = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = false; + } + + /* + ** In editor mode, the proximity check always passes. + */ + if (Debug_Map) { + return(true); + } + + if (list == NULL || trycell == 0) { + return(true); + } + + if (object == NULL || object->What_Am_I() != RTTI_BUILDINGTYPE) { + return(true); + } + + BuildingTypeClass const * building = (BuildingTypeClass const *)object; + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + ptr = list; +// ptr = CursorSize; + CELL cell = trycell; +// CELL cell = ZoneCell; + if (building->Adjacent == 1) { + while (*ptr != REFRESH_EOL && (retval == -1) ) { + cell = trycell + *ptr++; +// cell = ZoneCell + ZoneOffset + *ptr++; + + if (!In_Radar(cell)) { + retval = false; + noradar = true; + break; + } + + for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; facing++) { + CELL newcell = Adjacent_Cell(cell, facing); + + if (!In_Radar(newcell)) continue; + + // Code has no effect. ST - 8/6/2019 10:51AM + //if (!(*this)[newcell].IsMapped) { + // nomapped = true; + //} + BuildingClass * base = (*this)[newcell].Cell_Building(); + + /* + ** The special cell ownership flag allows building adjacent + ** to friendly walls and bibs even though there is no official + ** building located there. + */ + //BG: Modified so only walls can be placed next to walls - buildings can't. + //JLB: Except for bibs, in which case buildings can be placed next to these. + if (building->IsWall || + ((*this)[newcell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newcell].Smudge).IsBib)) { + + if ((*this)[newcell].Owner == house) { + retval = true; + break; + } + } + + // we've found a building... + if (base != NULL && base->House->Class->House == house && base->Class->IsBase) { + retval = true; + break; + } + + /* BG: modifications to allow buildings one cell away from other buildings. + ** This is done by scanning each cell that fails the check (hence getting + ** to this point) and looking at the n/s/e/w adjacent cells to see if they + ** have buildings in them. If they do, and they match us, then succeed. + */ + if (retval != -1) break; + + for (FacingType newface = FACING_N; newface < FACING_COUNT; newface++) { + CELL newercell = Adjacent_Cell(newcell, newface); + + if (building->IsWall || + ((*this)[newercell].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference((*this)[newercell].Smudge).IsBib)) { + + if ((*this)[newercell].Owner == house) { + retval = true; + break; + } + } + + TechnoClass * newbase = (*this)[newercell].Cell_Techno(); + + // we've found a building... + if (newbase != NULL && newbase->What_Am_I() == RTTI_BUILDING && newbase->House->Class->House == house && ((BuildingClass const *)newbase)->Class->IsBase) { + retval = true; + break; + } + } + if (retval != -1) break; + } + } + } + + if (retval == -1) retval = false; + + if (house == PlayerPtr->Class->House) { + PassedProximity = (retval != false); + } + + /* + ** If this object has special dispensation to be placed further than one cell from + ** other regular buildings, then check for this case now. Only bother to check if + ** it hasn't already been given permission to be placed down. + */ + if (!retval && !noradar && object->What_Am_I() == RTTI_BUILDINGTYPE) { + + // For land mines, let's make it check proximity within 10 squares + if (building->Adjacent > 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj != NULL && !obj->IsInLimbo && obj->House->Class->House == house && obj->Class->IsBase) { + int centdist = ::Distance(obj->Center_Coord(), Cell_Coord(cell)); + centdist /= CELL_LEPTON_W; + centdist -= (obj->Class->Width() + obj->Class->Height()) / 2; + if (centdist <= building->Adjacent) { + retval = true; + break; + } + } + } + } + } + + return((bool)retval); +} + + +/*********************************************************************************************** + * DisplayClass::Set_Cursor_Pos -- Controls the display and animation of the tac cursor. * + * * + * This routine controls the location, display, and animation of the * + * tactical map cursor. * + * * + * INPUT: pos -- Position to move the cursor do. If -1 is passed then * + * the cursor will just be hidden. If the position * + * passed is the same as the last position passed in, * + * then animation could occur (based on timers). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + * 06/08/1994 JLB : If position is -1, then follow mouse. * + * 02/28/1995 JLB : Forces placement cursor to fit on map. * + *=============================================================================================*/ +CELL DisplayClass::Set_Cursor_Pos(CELL pos) +{ + CELL prevpos; // Last position of cursor (for jump-back reasons). + + /* + ** Follow the mouse position if no cell number is provided. + */ + if (pos == -1) { + pos = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + } + + if (CursorSize == NULL) { + prevpos = ZoneCell; + ZoneCell = pos; + return(prevpos); + } + + /* + ** Adjusts the position so that the placement cursor is never part way off the + ** tactical map. + */ + int w,h; + Get_Occupy_Dimensions(w, h, CursorSize); + + int x = Cell_X(pos + ZoneOffset); + int y = Cell_Y(pos + ZoneOffset); + + if (x < Coord_XCell(TacticalCoord)) x = Coord_XCell(TacticalCoord); + if (y < Coord_YCell(TacticalCoord)) y = Coord_YCell(TacticalCoord); + if (x+w >= Coord_XCell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)) x = Coord_XCell(TacticalCoord)+Lepton_To_Cell(TacLeptonWidth)-w; + if (y+h >= Coord_YCell(TacticalCoord) + Lepton_To_Cell(TacLeptonHeight)) y = Coord_YCell(TacticalCoord)+Lepton_To_Cell(TacLeptonHeight)-h; + pos = XY_Cell(x, y) - ZoneOffset; + + /* + ** This checks to see if NO animation or drawing is to occur and, if so, + ** exits. + */ + if (pos == ZoneCell) return(pos); + + prevpos = ZoneCell; + + /* + ** If the cursor is visible, then handle the graphic update. + ** Otherwise, just update the global position of the cursor. + */ + if (CursorSize != NULL) { + + /* + ** Erase the old cursor (if it exists) AND the cursor is moving. + */ + if (pos != ZoneCell && ZoneCell != -1) { + Cursor_Mark(ZoneCell+ZoneOffset, false); + } + + /* + ** Render the cursor (could just be animation). + */ + if (pos != -1) { + Cursor_Mark(pos+ZoneOffset, true); + } + } + ZoneCell = pos; + ProximityCheck = Passes_Proximity_Check(PendingObject, PendingHouse, CursorSize, ZoneCell+ZoneOffset); + + return(prevpos); +} + + +/*********************************************************************************************** + * DisplayClass::Get_Occupy_Dimensions -- computes width & height of the given occupy list * + * * + * INPUT: * + * w ptr to fill in with height * + * h ptr to fill in with width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/31/1995 BRR : Created. * + *=============================================================================================*/ +void DisplayClass::Get_Occupy_Dimensions(int & w, int & h, short const * list) const +{ + int min_x = MAP_CELL_W; + int max_x = -MAP_CELL_W; + int min_y = MAP_CELL_H; + int max_y = -MAP_CELL_H; + int x,y; + + w = 0; + h = 0; + + if (!list) { + /* + ** Loop through all cell offsets, accumulating max & min x- & y-coords + */ + while (*list != REFRESH_EOL) { + /* + ** Compute x & y coords of the current cell offset. We can't use Cell_X() + ** & Cell_Y(), because they use shifts to compute the values, and if the + ** offset is negative we'll get a bogus coordinate! + */ + x = (*list) % MAP_CELL_W; + y = (*list) / MAP_CELL_H; + + max_x = max(max_x, x); + min_x = min(min_x, x); + max_y = max(max_y, y); + min_y = min(min_y, y); + + list++; + } + + w = max(1, max_x - min_x + 1); + h = min(1, max_y - min_y + 1); + } +} + + +/*********************************************************************************************** + * DisplayClass::Cursor_Mark -- Set or resets the cursor display flag bits. * + * * + * This routine will clear or set the cursor display bits on the map. * + * If the bit is set, then the cursor will be rendered on that map * + * icon. * + * * + * INPUT: pos -- Position of the upper left corner of the cursor. * + * * + * on -- Should the bit be turned on? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that every call to set the bits is matched by a * + * corresponding call to clear the bits. * + * * + * HISTORY: * + * 09/04/1991 JLB : Created. * + * 06/02/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DisplayClass::Cursor_Mark(CELL pos, bool on) +{ + CELL const * ptr; + CellClass * cellptr; + + if (pos == -1) return; + + /* + ** For every cell in the CursorSize list, invoke its Redraw_Objects and + ** toggle its IsCursorHere flag + */ + ptr = CursorSize; + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + if (on) { + cellptr->IsCursorHere = true; + } else { + cellptr->IsCursorHere = false; + } + } + } + + /* + ** For every cell in the PendingObjectPtr's Overlap_List, invoke its + ** Redraw_Objects routine. + */ + if (PendingObjectPtr && PendingObjectPtr->IsActive) { + ptr = PendingObjectPtr->Overlap_List(); + while (*ptr != REFRESH_EOL) { + CELL cell = pos + *ptr++; + if (In_Radar(cell)) { + cellptr = &(*this)[cell]; + cellptr->Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::AI -- Handles the maintenance tasks for the map display. * + * * + * This routine is called once per game display frame (15 times per second). It handles * + * the mouse shape tracking and map scrolling as necessary. * + * * + * INPUT: input -- The next key just fetched from the input queue. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: Modifies the input code if necessary. When the input code is consumed, it gets * + * set to 0. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1994 JLB : Created. * + * 06/02/1994 JLB : Filters mouse click input. * + * 06/07/1994 JLB : Fixed so template click will behave right. * + * 10/14/1994 JLB : Changing cursor shape over target. * + * 12/31/1994 JLB : Takes mouse coordinates as parameters. * + * 06/27/1995 JLB : Breaks out of rubber band mode if mouse leaves map. * + *=============================================================================================*/ +void DisplayClass::AI(KeyNumType & input, int x, int y) +{ + if ( + IsRubberBand && + (Get_Mouse_X() < TacPixelX || + Get_Mouse_Y() < TacPixelY || + Get_Mouse_X() >= (TacPixelX + Lepton_To_Pixel(TacLeptonWidth)) || + Get_Mouse_Y() >= (TacPixelY + Lepton_To_Pixel(TacLeptonHeight)))) { + Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); + } + + MapClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Submit -- Adds a game object to the map rendering system. * + * * + * This routine is used to add an arbitrary (but tangible) game object to the map. It will * + * be rendered (made visible) once it is submitted to this function. This function builds * + * the list of game objects that get rendered each frame as necessary. It is possible to * + * submit the game object to different rendering layers. All objects in a layer get drawn * + * at the same time. Using this layer method it becomes possible to have objects "below" * + * other objects. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * layer -- The layer to add the object to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + * 05/31/1994 JLB : Sorts object position if this is for the ground layer. * + *=============================================================================================*/ +void DisplayClass::Submit(ObjectClass const * object, LayerType layer) +{ + if (object) { + Layer[layer].Submit(object, (layer == LAYER_GROUND)); + } +} + + +/*********************************************************************************************** + * DisplayClass::Remove -- Removes a game object from the rendering system. * + * * + * Every object that is to disappear from the map must be removed from the rendering * + * system. * + * * + * INPUT: object -- The object to remove. * + * * + * layer -- The layer to remove it from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Improved layer system. * + *=============================================================================================*/ +void DisplayClass::Remove(ObjectClass const * object, LayerType layer) +{ + assert(object != 0); + assert(object->IsActive); + + if (object) { + Layer[layer].Delete((ObjectClass *)object); + } +} + + +/*********************************************************************************************** + * DisplayClass::Click_Cell_Calc -- Determines cell from screen X & Y. * + * * + * This routine is used to determine the cell that is located at the * + * screen pixel coordinates given. Typical use is when the player * + * clicks with the mouse on the tactical map. * + * * + * INPUT: x,y -- Screen pixel coordinates. * + * * + * OUTPUT: Returns with cell that is under the coordinates specified. * + * If the coordinate specified is outside of the tactical * + * map, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL DisplayClass::Click_Cell_Calc(int x, int y) const +{ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/5/2019 11:56AM + if (IgnoreViewConstraints || (unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + COORDINATE tcoord = XY_Coord(Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))), Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord)))); + return(Coord_Cell(Coord_Add(tcoord, XY_Coord(x, y)))); + } + return(-1); +} + + +/*********************************************************************************************** + * DisplayClass::Scroll_Map -- Scroll the tactical map in desired direction. * + * * + * This routine is used to scroll the tactical map view in the desired * + * direction. It can also be used to determine if scrolling would be * + * legal without actually performing any scrolling action. * + * * + * INPUT: facing -- The direction to scroll the tactical map. * + * * + * distance -- The distance in leptons to scroll the map. * + * * + * really -- Should the map actually be scrolled? If false, * + * then only the legality of a scroll is checked. * + * * + * OUTPUT: bool; Would scrolling in the desired direction be possible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/20/1994 JLB : Converted to member function. * + * 08/09/1995 JLB : Added distance parameter. * + * 08/10/1995 JLB : Any direction scrolling. * + *=============================================================================================*/ +bool DisplayClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + /* + ** If the distance is invalid then no further checking is required. Bail + ** with a no-can-do flag. + */ + if (distance == 0) return(false); + FacingType crude = Dir_Facing(facing); + + if (Coord_X(TacticalCoord) == Cell_To_Lepton(MapCellX) && crude != FACING_W) { + if (crude == FACING_SW) facing = DIR_S; + if (crude == FACING_NW) facing = DIR_N; + } + if (Coord_Y(TacticalCoord) == Cell_To_Lepton(MapCellY) && crude != FACING_N) { + if (crude == FACING_NW) facing = DIR_W; + if (crude == FACING_NE) facing = DIR_E; + } + if (Coord_X(TacticalCoord) + TacLeptonWidth == Cell_To_Lepton(MapCellX+MapCellWidth) && crude != FACING_E) { + if (crude == FACING_NE) facing = DIR_N; + if (crude == FACING_SE) facing = DIR_S; + } + if (Coord_Y(TacticalCoord) + TacLeptonHeight == Cell_To_Lepton(MapCellY+MapCellHeight) && crude != FACING_S) { + if (crude == FACING_SE) facing = DIR_E; + if (crude == FACING_SW) facing = DIR_W; + } + + /* + ** Determine the coordinate that it wants to scroll to. + */ + COORDINATE coord = Coord_Move(TacticalCoord, facing, distance); + + /* + ** Clip the new coordinate to the edges of the game world. + */ + int xx = (int)(short)Coord_X(coord) - (short)Cell_To_Lepton(MapCellX); + int yy = (int)(short)Coord_Y(coord) - (short)Cell_To_Lepton(MapCellY); + bool shifted = Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + if (xx < 0) { + xx = 0; + shifted = true; + } + if (yy < 0) { + yy = 0; + shifted = true; + } + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + /* + ** If the desired scroll was bound by the edge of the map, then adjust the distance to more accurately + ** reflect the actual distance moved. + */ + if (shifted) { + distance = Distance(TacticalCoord, coord); + } + + /* + ** If the new coordinate is the same as the old, then no scrolling would occur. + */ + if (!distance || coord == TacticalCoord) return(false); + + /* + ** Since the new coordinate is different than the old one, possibly adjust the real + ** tactical map accordingly. + */ + if (really) { + Set_Tactical_Position(coord); + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Scrolled map REQUIRES all top layer units to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + + } +// jmarshall - force update the map when we scroll, this fixes the sidebar. + Map.Redraw_Tab(); + Map.Flag_To_Redraw(true); +// jmarshall end + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Cells -- Redraws all cells in list. * + * * + * This routine is used to flag all cells in the specified list for * + * redrawing. * + * * + * INPUT: cell -- The origin cell that the list is offset from. * + * * + * list -- Pointer to a list of offsets from the origin cell. * + * Each cell so specified is flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is rather slow (by definition). * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 08/01/1994 JLB : Simplified. * + *=============================================================================================*/ +void DisplayClass::Refresh_Cells(CELL cell, short const * list) +{ + short tlist[36]; + + if (*list == REFRESH_SIDEBAR) { + list++; + } + + List_Copy(list, ARRAY_SIZE(tlist), tlist); + short * tt = tlist; + int count = 0; + while (*tt != REFRESH_EOL) { + if (count >= ARRAY_SIZE(tlist)) { // Added overrun check. ST - 8/14/2019 3:14PM + break; + } + CELL newcell = cell + *tt++; + if (In_Radar(newcell)) { + (*this)[newcell].Redraw_Objects(); + } + count++; + } +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + * 08/05/2019 ST : Added house parameter so we can do this per player ** + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell, HouseClass *house) const +{ + static char const _shadow[256]={ + -1,33, 2, 2,34,37, 2, 2, + 4,26, 6, 6, 4,26, 6, 6, + 35,45,17,17,38,41,17,17, + 4,26, 6, 6, 4,26, 6, 6, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 32,36,25,25,44,40,25,25, + 19,30,20,20,19,30,20,20, + 39,43,29,29,42,46,29,29, + 19,30,20,20,19,30,20,20, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2 + }; + + int index = 0, value = -1; + + /* + ** Don't map cells that are at the edges. This solves + ** problem of accessing cells off the bounds of map and into + ** who-knows-what memory. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ((unsigned)(Cell_X(cell)-1) >= MAP_CELL_W-2) return(-1); + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); +#else + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); +#endif + //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + CellClass const * cellptr = &(*this)[cell]; + + /* + ** Presume solid black if that is what is here already. + */ + if (!cellptr->Is_Visible(house) && !cellptr->Is_Mapped(house)) value = -2; + + if (cellptr->Is_Mapped(house) /*&& !cellptr->IsVisible*/) { + /* + ** Build an index into the lookup table using all 8 surrounding cells. + ** We're mapping a revealed cell and we only care about the existence + ** of black cells. Bit numbering starts at the upper-right corner and + ** goes around the cell clockwise, so 0x80 = directly north. + */ + cell -= MAP_CELL_W + 1; cellptr -= MAP_CELL_W + 1; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x40; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x80; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x01; + cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x20; + cell += 2; cellptr += 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x02; + cell += MAP_CELL_W - 2; cellptr += MAP_CELL_W - 2; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x10; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x08; + cell++; cellptr++; + if (!cellptr->Is_Mapped(house) && In_Radar(cell)) index |= 0x04; + + value = _shadow[index]; + } + return(value); +} + + +#if (0) // Old code for reference. ST - 8/15/2019 10:25AM +/*********************************************************************************************** + * DisplayClass::Cell_Shadow -- Determine what shadow icon to use for the cell. * + * * + * This routine will examine the specified cell and adjacent cells to * + * determine what shadow icon to use. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns with the shadow icon to use. -2= all black. * + * -1= map cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1994 JLB : Created. * + * 04/04/1994 JLB : Revamped for new shadow icon method. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +int DisplayClass::Cell_Shadow(CELL cell) const +{ + static char const _shadow[256]={ + -1,33, 2, 2,34,37, 2, 2, + 4,26, 6, 6, 4,26, 6, 6, + 35,45,17,17,38,41,17,17, + 4,26, 6, 6, 4,26, 6, 6, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 32,36,25,25,44,40,25,25, + 19,30,20,20,19,30,20,20, + 39,43,29,29,42,46,29,29, + 19,30,20,20,19,30,20,20, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + 8,21,10,10,27,31,10,10, + 12,23,14,14,12,23,14,14, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + + 1, 1, 3, 3,16,16, 3, 3, + 5, 5, 7, 7, 5, 5, 7, 7, + 24,24,18,18,28,28,18,18, + 5, 5, 7, 7, 5, 5, 7, 7, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2, + 9, 9,11,11,22,22,11,11, + 13,13,-2,-2,13,13,-2,-2 + }; + + int index = 0, value = -1; + + /* + ** Don't map cells that are at the top or bottom edge. This solves + ** problem of accessing cells off the top or bottom of the map and into + ** who-knows-what memory. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ((unsigned)(Cell_Y(cell)-1) >= MAP_CELL_H-2) return(-1); +#else + if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-1); +#endif + //if ((unsigned)(Cell_Y(cell)-1) > MAP_CELL_H-2) return(-2); + + CellClass const * cellptr = &(*this)[cell]; + + /* + ** Presume solid black if that is what is here already. + */ + if (!cellptr->IsVisible && !cellptr->IsMapped) value = -2; + + if (cellptr->IsMapped /*&& !cellptr->IsVisible*/) { + /* + ** Build an index into the lookup table using all 8 surrounding cells. + ** We're mapping a revealed cell and we only care about the existence + ** of black cells. Bit numbering starts at the upper-right corner and + ** goes around the cell clockwise, so 0x80 = directly north. + */ + cellptr-= MAP_CELL_W + 1; + if (!cellptr->IsMapped) index |= 0x40; + cellptr++; + if (!cellptr->IsMapped) index |= 0x80; + cellptr++; + if (!cellptr->IsMapped) index |= 0x01; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x20; + cellptr += 2; + if (!cellptr->IsMapped) index |= 0x02; + cellptr += MAP_CELL_W - 2; + if (!cellptr->IsMapped) index |= 0x10; + cellptr++; + if (!cellptr->IsMapped) index |= 0x08; + cellptr++; + if (!cellptr->IsMapped) index |= 0x04; + + value = _shadow[index]; + } + return(value); +} +#endif + +/*********************************************************************************************** + * DisplayClass::Map_Cell -- Mark specified cell as having been mapped. * + * * + * This routine maps the specified cell. The cell must not already * + * have been mapped and the mapping player must be the human. * + * This routine will update any adjacent cell map icon as appropriate. * + * * + * INPUT: cell -- The cell to be mapped. * + * * + * house -- The player that is doing the mapping. * + * * + * OUTPUT: bool; Was action taken to map this cell? * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/24/1994 JLB : Takes pointer to HouseClass. * + * 02/20/1996 JLB : Allied units reveal the map for the player. * + * 08/05/2019 ST : Use per-player mapping so we can track the shroud for all players * + *=============================================================================================*/ +bool DisplayClass::Map_Cell(CELL cell, HouseClass * house, bool check_radar_spied, bool and_for_allies) +{ + // OK for house not to be PlayerPtr. ST - 8/6/2019 10:05AM + //if (house != PlayerPtr || !In_Radar(cell)) return(false); + if (house == NULL || !In_Radar(cell)) return(false); + + if (!house->IsHuman) { + if (!ShareAllyVisibility || !and_for_allies && !check_radar_spied) { + return false; + } + } + + /* + ** First check for the condition where we're spying on a house's radar + ** facility, to see if his mapping is applicable to us. + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + // Original code. ST - 8/15/2019 10:26AM + if (house && house != PlayerPtr) { + if (house->RadarSpied & (1<<(PlayerPtr->Class->House))) house = PlayerPtr; + if (Session.Type == GAME_NORMAL && house->Is_Ally(PlayerPtr)) house = PlayerPtr; + } + } else { + // Version to work with any human player, not just PlayerPtr + if (house && check_radar_spied) { + + for (int i=0 ; iPlayer.ID); + if (player_ptr->IsHuman) { + if (house->RadarSpied & (1<<(player_ptr->Class->House))) { + Map_Cell(cell, player_ptr, false, false); + } + } + } + } + } + + /* + ** Maybe also recurse to map for allies + */ + if (ShareAllyVisibility && and_for_allies && Session.Type == GAME_GLYPHX_MULTIPLAYER) { + + for (int i=0 ; iPlayer.ID); + if (player_ptr && player_ptr->IsActive && player_ptr->IsHuman) { + if (player_ptr != house && house->Is_Ally(player_ptr)) { + Map_Cell(cell, player_ptr, check_radar_spied, false); + } + } + } + } + + CellClass * cellptr = &(*this)[cell]; + + /* + ** Don't bother remapping this cell if it is already mapped. + */ +#if (1) + if (cellptr->Is_Mapped(house)) { + if (!cellptr->Is_Visible(house)) { + cellptr->Redraw_Objects(); + } + return(false); + } +#else + if (cellptr->IsMapped) { + if (!cellptr->IsVisible) { + cellptr->Redraw_Objects(); + } + return(false); + } +#endif + /* + ** Mark the cell as being mapped. This must be done first because + ** if the IsVisible flag must be set, then it might affect the + ** adjacent cell processing. + */ + // Set per player. ST - 8/6/2019 10:18AM + cellptr->Set_Mapped(house); + cellptr->Redraw_Objects(); + if (Cell_Shadow(cell, house) == -1) { + cellptr->Set_Visible(house); + } + + /* + ** Check out all adjacent cells to see if they need + ** to be mapped as well. This is necessary because of the + ** "unique" method of showing shadowed cells. Many combinations + ** are not allowed, and to fix this, just map the cells until + ** all is ok. + */ + int xx = Cell_X(cell); + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + int shadow; + CELL c; + int xdiff; + + c = Adjacent_Cell(cell, dir); + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)c >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(c) - xx; + xdiff = ABS(xdiff); + if (xdiff > 1) continue; + + CellClass * cptr = &(*this)[c]; + cptr->Redraw_Objects(); + +#if (1) + // New client/server friendly code + if (c != cell && !cptr->Is_Visible(house)) { + shadow = Cell_Shadow(c, house); + + if (shadow == -1) { + if (!cptr->Is_Mapped(house)) { + Map_Cell(c, house, check_radar_spied, false); + } else { + cptr->Set_Visible(house); + } + } else { + if (shadow != -2 && !cptr->Is_Mapped(house)) { + Map_Cell(c, house, check_radar_spied, false); + } + } + } +#else + // Old peer/peer code + if (c != cell && !cptr->IsVisible) { + shadow = Cell_Shadow(c); + + if (shadow == -1) { + if (!cptr->IsMapped) { + Map_Cell(c, house); + } else { + cptr->IsVisible = true; + } + } else { + if (shadow != -2 && !cptr->IsMapped) { + Map_Cell(c, house); + } + } + } +#endif + } + + TechnoClass * tech = (*this)[cell].Cell_Techno(); + if (tech) { + tech->Revealed(house); + } + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Coord_To_Pixel -- Determines X and Y pixel coordinates. * + * * + * This is the routine that figures out the location on the screen for * + * a specified coordinate. It is one of the fundamental routines * + * necessary for rendering the game objects. It performs some quick * + * tests to see if the coordinate is in a visible region and returns * + * this check as a boolean value. * + * * + * INPUT: coord -- The coordinate to check. * + * * + * x,y -- Reference to the pixel coordinates that this * + * coordinate would be when rendered. * + * * + * OUTPUT: bool; Is this coordinate in a visible portion of the map? * + * * + * WARNINGS: If the coordinate is not in a visible portion of the * + * map, then this X and Y parameters are not set. * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + * 12/15/1994 JLB : Converted to member function. * + * 01/07/1995 JLB : Uses inline functions to extract coord components. * + * 08/09/1995 JLB : Uses new coordinate system. * + *=============================================================================================*/ +#define EDGE_ZONE (CELL_LEPTON_W*2) +bool DisplayClass::Coord_To_Pixel(COORDINATE coord, int &x, int &y) const +{ + if (coord) { + int xtac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(TacticalCoord))); + int xoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_X(coord))); + + xoff = (xoff+EDGE_ZONE) - xtac; + if ((int)xoff <= TacLeptonWidth + EDGE_ZONE*2) { + int ytac = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(TacticalCoord))); + int yoff = Pixel_To_Lepton(Lepton_To_Pixel(Coord_Y(coord))); + + yoff = (yoff+EDGE_ZONE) - ytac; + if ((int)yoff <= TacLeptonHeight + EDGE_ZONE*2) { + x = Lepton_To_Pixel(xoff)-CELL_PIXEL_W*2; + y = Lepton_To_Pixel(yoff)-CELL_PIXEL_H*2; + return(true); + } + } + } + return(false); +} + +/*********************************************************************************************** + * DisplayClass::Push_Onto_TacMap -- Moves x & y coords to being on tactical map * + * * + * This routine expects a line to be drawn between SOURCE & DEST, so it pushes the coords to * + * be within the region bounded by TacMapX,Y - + TacMapW,H. * + * * + * INPUT: source, dest -- References to the coordinates to check. * + * * + * * + * OUTPUT: bool; Are these coordinates in a visible portion of the map? * + * Returns true if the pushed source & dest are visible, but if neither are * + * within the map, then it returns false. * + * * + * * + * HISTORY: * + * 03/27/1995 BWG : Created. * + *=============================================================================================*/ +bool DisplayClass::Push_Onto_TacMap(COORDINATE & source, COORDINATE & dest) +{ + if (!source || !dest) return(false); + + int x1 = Coord_X(source); + int y1 = Coord_Y(source); + int x2 = Coord_X(dest); + int y2 = Coord_Y(dest); + int left = Coord_X(TacticalCoord); + int right = Coord_X(TacticalCoord) + TacLeptonWidth; + int top = Coord_Y(TacticalCoord); + int bottom = Coord_Y(TacticalCoord) + TacLeptonHeight; + + if (x1 < left && x2 < left) return(false); + if (x1 > right && x2 > right) return(false); + if (y1 < top && y2 < top) return(false); + if (y1 > bottom && y2 > bottom) return(false); + + x1 = Bound(x1, left, right); + x2 = Bound(x2, left, right); + y1 = Bound(y1, top, bottom); + y2 = Bound(y2, top, bottom); + + source = XY_Coord(x1, y1); + dest = XY_Coord(x2, y2); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Cell_Object -- Determines what has been clicked on. * + * * + * This routine is used to determine what the player has clicked on. * + * It is passed the cell that the click was on and it then examines * + * the cell and returns with a pointer to the object that is there. * + * * + * INPUT: cell -- The cell that has been clicked upon. * + * * + * x,y -- Optional offsets from the upper left corner of the cell to be used in * + * determining exactly which object in the cell is desired. * + * * + * OUTPUT: Returns with a pointer to the object that is "clickable" in * + * the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) const +{ + return(*this)[cell].Cell_Object(x, y); +} + + +/*********************************************************************************************** + * DisplayClass::Draw_It -- Draws the tactical map. * + * * + * This will draw the tactical map at the recorded position. This * + * routine is used whenever the tactical map moves or needs to be * + * completely redrawn. It will handle making the necessary adjustments * + * to accomodate a moving cursor. * + * * + * INPUT: forced -- bool; force redraw of the entire display? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1991 JLB : Created. (benchmark = 292) * + * 04/15/1991 JLB : Added _cell2meta[] reference array (206) * + * 04/15/1991 JLB : Added actual map reference for terrain (207) * + * 04/16/1991 JLB : _cell2meta converted to int (194) * + * 04/16/1991 JLB : References actual CellIcon[] array (204) * + * 04/16/1991 JLB : Cell size increased to 16 x 16 (167) * + * 04/17/1991 JLB : Cell based tactical map rendering (165) * + * 04/22/1991 JLB : Uses Draw_Stamp() for icon rendering (426) * + * 04/22/1991 JLB : Draw_Stamp uses LogicPage now (276) * + * 04/23/1991 JLB : Map active location cursor (334) * + * 05/02/1991 JLB : Added smoothing and 3 icons sets (431) * + * 05/22/1991 JLB : Broken into Draw_Map() and Refresh_Map(). * + * 09/14/1991 JLB : Uses Refresh_Cell when new cells scroll onto display. * + * 05/12/1992 JLB : Destination page support. * + * 02/14/1994 JLB : Revamped. * + * 05/01/1994 JLB : Converted to member function. * + * 12/15/1994 JLB : Updated to work with display hierarchy. * + * 12/24/1994 JLB : Examines redraw bit intelligently. * + * 12/24/1994 JLB : Combined with old Refresh_Map() function. * + * 01/10/1995 JLB : Rubber band drawing. * + *=============================================================================================*/ + void DisplayClass::Draw_It(bool forced) +{ + int x,y; // Working cell index values. + + MapClass::Draw_It(forced); + + if (IsToRedraw || forced) { + BStart(BENCH_TACTICAL); + IsToRedraw = false; + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + Refresh_Band(); + + /* + ** Mark all cells under the vortex to be redrawn + */ + ChronalVortex.Set_Redraw(); + + + /* + ** If the multiplayer message system is displaying one or more messages, + ** flag all cells covered by the messages to redraw. This will prevent + ** messages from smearing the map if it scrolls. + */ + int num = Session.Messages.Num_Messages(); + if (num > 0) { + CELL cell; + for (cell = Coord_Cell(TacticalCoord); cell < Coord_Cell(TacticalCoord) + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + if (num > 1) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*2; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*2 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 2) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*3; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*3 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + if (num > 3) { + for (cell = Coord_Cell(TacticalCoord) + MAP_CELL_W*4; + cell < Coord_Cell(TacticalCoord) + MAP_CELL_W*4 + Lepton_To_Cell(TacLeptonWidth)+1; cell++) { + (*this)[cell].Redraw_Objects(); + } + } + } + + /* + ** Check for a movement of the tactical map. If there has been some + ** movement, then part (or all) of the icons must be redrawn. + */ + if (Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_X(TacticalCoord)) || + Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)) != Lepton_To_Pixel(Coord_Y(TacticalCoord))) { + + int xmod = Lepton_To_Pixel(Coord_X(DesiredTacticalCoord)); + int ymod = Lepton_To_Pixel(Coord_Y(DesiredTacticalCoord)); + + int oldx = Lepton_To_Pixel(Coord_X(TacticalCoord))-xmod; // Old relative offset. + int oldy = Lepton_To_Pixel(Coord_Y(TacticalCoord))-ymod; + + int oldw = Lepton_To_Pixel(TacLeptonWidth)-ABS(oldx); // Replicable width. + int oldh = Lepton_To_Pixel(TacLeptonHeight)-ABS(oldy); // Replicable height. + + if (oldw < 1) forced = true; + if (oldh < 1) forced = true; + + +#ifdef WIN32 //For WIN32 only redraw the edges of the map that move into view + + /* + ** Work out which map edges need to be redrawn + */ + BOOL redraw_right = (oldx < 0) ? true : false; //Right hand edge + BOOL redraw_left = (oldx > 0) ? true : false; //Left hand edge + BOOL redraw_bottom= (oldy < 0) ? true : false; //Bottom edge + BOOL redraw_top = (oldy > 0) ? true : false; //Top edge + + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + /* + ** If hid page is in video memory then blit from the seen page to avoid blitting + ** an overlapped region. + */ + if (HidPage.Get_IsDirectDraw()) { + Hide_Mouse(); + SeenBuff.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + Show_Mouse(); + } else { + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } + + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** + ** Set the 'redraw stamp' bit for any cells that could not be copied. + ** + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + + if (abs(oldx) < 0x25 && abs(oldy) < 0x25) { + + /* + ** The width of the area we redraw depends on the scroll speed + */ + int extra_x = (abs(oldx)>=16) ? 2 : 1; + int extra_y = (abs(oldy)>=16) ? 2 : 1; + + /* + ** Flag the cells across the top of the visible area if required + */ + if (redraw_top) { + for (y = starty; y <= starty+CELL_PIXEL_H*extra_y; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells across the bottom of the visible area if required + */ + if (redraw_bottom) { + for (y = Lepton_To_Pixel(TacLeptonHeight)-CELL_PIXEL_H*(1+extra_y); y <= Lepton_To_Pixel(TacLeptonHeight)+CELL_PIXEL_H*3; y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the left of the visible area if required + */ + if (redraw_left) { + for (x = startx; x <= startx + CELL_PIXEL_W*extra_x; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + /* + ** Flag the cells down the right of the visible area if required + */ + if (redraw_right) { + for (x = Lepton_To_Pixel(TacLeptonWidth)-CELL_PIXEL_W*(extra_x+1); x <= Lepton_To_Pixel(TacLeptonWidth)+CELL_PIXEL_W*3; x += CELL_PIXEL_W) { + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) (*this)[c].Redraw_Objects(true); + } + } + } + + } else { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } + + +#else //WIN32 + /* + ** Blit any replicable block to avoid having to drawstamp. + */ + if (!forced && (oldw != Lepton_To_Pixel(TacLeptonWidth) || oldh != Lepton_To_Pixel(TacLeptonHeight))) { + Set_Cursor_Pos(-1); + + HidPage.Blit(HidPage, + ((oldx < 0) ? -oldx : 0) +TacPixelX, + ((oldy < 0) ? -oldy : 0) +TacPixelY, + ((oldx < 0) ? 0 : oldx) +TacPixelX, + ((oldy < 0) ? 0 : oldy) +TacPixelY, + oldw, + oldh); + } else { + forced = true; + } + + if (oldx < 0) oldx = 0; + if (oldy < 0) oldy = 0; + + /* + ** Record new map position for future reference. + */ + ScenarioInit++; + Set_Tactical_Position(DesiredTacticalCoord); + ScenarioInit--; + + if (!forced) { + + /* + ** Set the 'redraw stamp' bit for any cells that could not be copied. + */ + int startx = -Lepton_To_Pixel(Coord_XLepton(TacticalCoord)); + int starty = -Lepton_To_Pixel(Coord_YLepton(TacticalCoord)); + oldw -= 24; + oldh -= 24; + for (y = starty; y <= Lepton_To_Pixel(TacLeptonHeight)+((CELL_PIXEL_H*2)); y += CELL_PIXEL_H) { + for (x = startx; x <= Lepton_To_Pixel(TacLeptonWidth)+((CELL_PIXEL_W*2)); x += CELL_PIXEL_W) { + if (x <= oldx || x >= oldx+oldw || y <= oldy || y >= oldy+oldh) { + CELL c = Click_Cell_Calc(Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1) + TacPixelX, + Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1) + TacPixelY); + + if (c > 0) { + (*this)[c].Redraw_Objects(true); + } + } + } + } + } + + } else { + + /* + ** Set the tactical coordinate just in case the desired tactical has changed but + ** not enough to result in any visible map change. This is likely to occur with very + ** slow scroll rates. + */ + ScenarioInit++; + if (DesiredTacticalCoord != TacticalCoord) { + Set_Tactical_Position(DesiredTacticalCoord); + } + ScenarioInit--; + } +#endif + + /* + ** If the entire tactical map is forced to be redrawn, then set all the redraw flags + ** and let the normal processing take care of the rest. + */ + if (forced) { + CellRedraw.Set(); + } + + /* + ** The first order of business is to redraw all the underlying icons that are + ** flagged to be redrawn. + */ + if (HidPage.Lock()) { + Redraw_Icons(); + + /* + ** Draw the infantry bodies in this special layer. + */ +// for (int index = 0; index < Anims.Count(); index++) { +// AnimClass * anim = Anims.Ptr(index); +// if (*anim >= ANIM_CORPSE1 && *anim <= ANIM_CORPSE3) { +// anim->Render(forced); +// } +// } + +#ifdef SORTDRAW + /* + ** Draw the vortex effect over the terrain + */ + ChronalVortex.Render(); + + Redraw_OIcons(); +#endif + + HidPage.Unlock(); + } + +#ifndef WIN32 + /* + ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom + ** area one line below the screen. This causes the predator effect to work on any + ** shape drawn at the bottom of the screen. + */ + HidPage.Blit(HidPage, 0, HidPage.Get_Height()-1, 0, HidPage.Get_Height(), HidPage.Get_Width(), 1, false); +#endif + + if (HidPage.Lock()) { + +#ifndef SORTDRAW + /* + ** Draw the vortex effect over the terrain + */ + ChronalVortex.Render(); +#endif + + /* + ** Redraw the game objects layer by layer. The layer drawing occurs on the ground layer + ** first and then followed by all the layers in increasing altitude. + */ + BStart(BENCH_OBJECTS); + for (LayerType layer = LAYER_FIRST; layer < LAYER_COUNT; layer++) { + for (int index = 0; index < Layer[layer].Count(); index++) { + ObjectClass * ptr = Layer[layer][index]; + +#ifdef SORTDRAW + /* + ** Techno objects are drawn as part of the cell redraw process since techno + ** objects in the ground layer are handled by the Occupier and Overlapper + ** pointer lists. + */ + if (!Debug_Map && ptr->Is_Techno() && layer == LAYER_GROUND && ((TechnoClass*)ptr)->Visual_Character() == VISUAL_NORMAL) continue; +#endif + +// if (ptr->What_Am_I() == RTTI_ANIM && *((AnimClass*)ptr) >= ANIM_CORPSE1 && *((AnimClass*)ptr) <= ANIM_CORPSE3) { +// continue; +// } + assert(ptr->IsActive); + ptr->Render(forced); + } + } + BEnd(BENCH_OBJECTS); + + //ChronalVortex.Render(); + /* + ** Finally, redraw the shadow overlay as necessary. + */ + BStart(BENCH_SHROUD); + Redraw_Shadow(); + BEnd(BENCH_SHROUD); + } + HidPage.Unlock(); + +#ifdef SORTDRAW + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + Layer[LAYER_GROUND][index]->IsToDisplay = false; + } +#endif + + /* + ** Draw the rubber band over the top of it all. + */ + if (IsRubberBand) { + LogicPage->Draw_Rect(BandX+TacPixelX, BandY+TacPixelY, NewX+TacPixelX, NewY+TacPixelY, WHITE); + } + + /* + ** Clear the redraw flags so that normal redraw flag setting can resume. + */ + CellRedraw.Reset(); + +#ifdef SCENARIO_EDITOR + /* + ** If we're placing an object (PendingObject is non-NULL), and that object + ** is NOT an icon, smudge, or overlay, draw it here. + ** Terrain, Buildings & Aircraft aren't drawn at the cell's center coord; + ** they're drawn at the upper left coord, so I have to AND the coord value + ** with 0xFF00FF00 to strip off the lepton coordinates, but leave the + ** cell coordinates. + */ + if (Debug_Map && PendingObjectPtr) { + PendingObjectPtr->Coord = PendingObjectPtr->Class_Of().Coord_Fixup(Cell_Coord(ZoneCell + ZoneOffset)); + PendingObjectPtr->Render(true); + } +#endif + BEnd(BENCH_TACTICAL); + } +} + + +/*********************************************************************************************** + * DisplayClass::Redraw_Icons -- Draws all terrain icons necessary. * + * * + * This routine will redraw all of the terrain icons that are flagged * + * to be redrawn. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + * 06/20/1994 JLB : Uses cell drawing support function. * + * 12/06/1994 JLB : Scans tactical view in separate row/column loops * + * 12/24/1994 JLB : Uses the cell bit flag array to determine what to redraw. * + *=============================================================================================*/ +void DisplayClass::Redraw_Icons(void) +{ + IsShadowPresent = false; + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + //if (cellptr->IsMapped || Debug_Unshroud) { + if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + cellptr->Draw_It(xpixel, ypixel); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + //if (!cellptr->IsVisible && !Debug_Unshroud) { + if (!cellptr->Is_Visible(PlayerPtr) && !Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + IsShadowPresent = true; + } + } + } + } + } +} + + +#ifdef SORTDRAW +void DisplayClass::Redraw_OIcons(void) +{ + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redraw are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + //if (cellptr->IsMapped || Debug_Unshroud) { + if (cellptr->Is_Mapped(PlayerPtr) || Debug_Unshroud) { // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + cellptr->Draw_It(xpixel, ypixel, true); + } + } + } + } + } +} +#endif + + +/*********************************************************************************************** + * DisplayClass::Redraw_Shadow -- Draw the shadow overlay. * + * * + * This routine is called after all other tactical map rendering takes place. It draws * + * the shadow map over the tactical map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 08/06/1995 JLB : Clips the fill rect if necessary. * + *=============================================================================================*/ +void DisplayClass::Redraw_Shadow(void) +{ + if (IsShadowPresent) { + for (int y = -Coord_YLepton(TacticalCoord); y <= TacLeptonHeight; y += CELL_LEPTON_H) { + for (int x = -Coord_XLepton(TacticalCoord); x <= TacLeptonWidth; x += CELL_LEPTON_W) { + COORDINATE coord = Coord_Add(TacticalCoord, XY_Coord(x, y)); + CELL cell = Coord_Cell(coord); + coord = Coord_Whole(Cell_Coord(cell)); + + /* + ** Only cells flagged to be redrawn are examined. + */ + if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &(*this)[coord]; + //if (cellptr->IsVisible) continue; + if (cellptr->Is_Visible(PlayerPtr)) continue; // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + int shadow = -2; + //if (cellptr->IsMapped) { + if (cellptr->Is_Mapped(PlayerPtr)) { // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + shadow = Cell_Shadow(cell, PlayerPtr); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:44AM + } + if (shadow >= 0) { + CC_Draw_Shape(ShadowShapes, shadow, xpixel, ypixel, WINDOW_TACTICAL, SHAPE_GHOST, NULL, ShadowTrans); + } else { + if (shadow != -1) { + int ww = CELL_PIXEL_W; + int hh = CELL_PIXEL_H; + + if (Clip_Rect(&xpixel, &ypixel, &ww, &hh, Lepton_To_Pixel(TacLeptonWidth), Lepton_To_Pixel(TacLeptonHeight)) >= 0) { + LogicPage->Fill_Rect(TacPixelX+xpixel, TacPixelY+ypixel, TacPixelX+xpixel+ww-1, TacPixelY+ypixel+hh-1, BLACK); + } + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Next_Object -- Searches for next object on display. * + * * + * This utility routine is used to find the "next" object from the object specified. This * + * is typically used when is pressed and the current object shifts. * + * * + * INPUT: object -- The current object to base the "next" calculation off of. * + * * + * OUTPUT: Returns with a pointer to the next object. If there is no objects available, * + * then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Next_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (unsigned uindex = 0; uindex < (unsigned)Layer[LAYER_GROUND].Count(); uindex++) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Prev_Object -- Searches for the previous object on the map. * + * * + * This routine will search for the previous object. Previous is defined as the one listed * + * before the specified object in the ground layer. If there is no specified object, then * + * the last object in the ground layer is returned. * + * * + * INPUT: object -- Pointer to the object that "previous" is to be defined from. * + * * + * OUTPUT: Returns with a pointer to the object previous to the specified one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * DisplayClass::Prev_Object(ObjectClass * object) const +{ + ObjectClass * firstobj = NULL; + bool foundmatch = false; + + if (object == NULL) { + foundmatch = true; + } + for (int uindex = Layer[LAYER_GROUND].Count()-1; uindex >= 0; uindex--) { + ObjectClass * obj = Layer[LAYER_GROUND][uindex]; + + /* + ** Verify that the object can be selected by and is owned by the player. + */ + if (obj != NULL && obj->Is_Players_Army()) { + if (firstobj == NULL) firstobj = obj; + if (foundmatch) return(obj); + if (object == obj) foundmatch = true; + } + } + + return(firstobj); +} + + +/*********************************************************************************************** + * DisplayClass::Pixel_To_Coord -- converts screen coord to COORDINATE * + * * + * INPUT: * + * x,y pixel coordinates to convert * + * * + * OUTPUT: * + * COORDINATE of pixel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/09/1994 BR : Created. * + * 12/06/1994 JLB : Uses map dimension variables in display class. * + * 12/10/1994 JLB : Uses union to speed building coordinate value. * + *=============================================================================================*/ +COORDINATE DisplayClass::Pixel_To_Coord(int x, int y) const +{ + /* + ** Normalize the pixel coordinates to be relative to the upper left corner + ** of the tactical map. The coordinates are expressed in leptons. + */ + x -= TacPixelX; + x = Pixel_To_Lepton(x); + y -= TacPixelY; + y = Pixel_To_Lepton(y); + + /* + ** If pixel coordinate is over the tactical map, then translate it into a coordinate + ** value. If not, then just return with NULL. + */ + // Possibly ignore the view constraints if we aren't using the internal renderer. ST - 8/6/2019 10:47AM + //if ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight) { + if (IgnoreViewConstraints || ((unsigned)x < TacLeptonWidth && (unsigned)y < TacLeptonHeight)) { + return(Coord_Add(TacticalCoord, XY_Coord(x, y))); + } + return(0); +} + + +/*********************************************************************************************** + * DisplayClass::Calculated_Cell -- Fetch a map cell based on specified method. * + * * + * Find a cell meeting the specified requirements. This function is * + * used for scenario reinforcements. * + * * + * INPUT: dir -- Method of picking a map cell. * + * * + * waypoint -- Closest waypoint to use for finding appropriate map edge. * + * * + * cell -- Cell to find closest edge to if waypoint not specified. * + * * + * loco -- The locomotion of the reinforcements that are trying to enter. * + * * + * zonecheck -- Is zone checking required? * + * * + * mzone -- The movement zone type to check against (only if zone checking). * + * * + * OUTPUT: Returns with the calculated cell. If 0, then this indicates * + * that no legal cell was found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/11/1994 JLB : Revamped. * + * 05/18/1994 JLB : Converted to member function. * + * 12/18/1995 JLB : Handles edge preference scan. * + * 06/24/1996 JLB : Removed Dune II legacy code. * + * 06/25/1996 JLB : Rewrote and greatly simplified. * + * 10/05/1996 JLB : Checks for zone and crushable status. * + *=============================================================================================*/ +CELL DisplayClass::Calculated_Cell(SourceType dir, WAYPOINT waypoint, CELL cell, SpeedType loco, bool zonecheck, MZoneType mzone) const +{ + bool vert = false; + bool horz = false; + int x = 0; + int y = 0; + CELL punt = 0; // If all else fails, return this cell location. + int zone = -1; // Tentative zone for legality checking. + + /* + ** Waypoint edge detection for ground based reinforcements that have a waypoint origin are + ** determined by finding the closest map edge to the waypoint. Reinforcement location + ** scanning starts from that position. + */ + CELL trycell = -1; + if (waypoint != -1) { + trycell = Scen.Waypoint[waypoint]; + } + if (trycell == -1) { + trycell = cell; + } + + /* + ** If zone checking is requested, then find the correct zone to use. + */ + if (zonecheck && trycell != -1) { + zone = (*this)[trycell].Zones[mzone]; + } + + /* + ** If the cell or waypoint specified as been detected as legal, then set up the map edge + ** scanning values accordingly. + */ + if (trycell != -1) { + x = Cell_X(trycell) - MapCellX; + x = min(x, (-Cell_X(trycell) + (MapCellX+MapCellWidth))); + + y = Cell_Y(trycell) - MapCellY; + y = min(y, (-Cell_Y(trycell) + (MapCellY+MapCellHeight))); + + if (x < y) { + vert = true; + horz = false; + if ((Cell_X(trycell)-MapCellX) < MapCellWidth/2) { + x = -1; + } else { + x = MapCellWidth; + } + y = Cell_Y(trycell) - MapCellY; + + } else { + + vert = false; + horz = true; + if ((Cell_Y(trycell)-MapCellY) < MapCellHeight/2) { + y = -1; + } else { + y = MapCellHeight; + } + x = Cell_X(trycell) - MapCellX; + } + } + + /* + ** If no map edge can be inferred from the waypoint, then go with the + ** map edge specified by the edge parameter. + */ + if (!vert && !horz) { + switch (dir) { + default: + case SOURCE_NORTH: + horz = true; + y = -1; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_SOUTH: + horz = true; + y = MapCellHeight; + x = Random_Pick(0, MapCellWidth-1); + break; + + case SOURCE_EAST: + vert = true; + x = MapCellWidth; + y = Random_Pick(0, MapCellHeight-1); + break; + + case SOURCE_WEST: + vert = true; + x = -1; + y = Random_Pick(0, MapCellHeight-1); + break; + } + } + + /* + ** Determine the default reinforcement cell if all else fails. + */ + punt = XY_Cell(x + MapCellX, y + MapCellY); + + /* + ** Scan through the vertical and horizontal edges of the map looking for + ** a relatively clear cell for object placement. The cell scanned is + ** from the edge position specified by the X and Y variables. + */ + if (vert) { + int modifier = (x > MapCellX) ? -1 : 1; + + for (int index = 0; index < MapCellHeight; index++) { + CELL trycell = XY_Cell(x + MapCellX, ((y + index) % MapCellHeight) + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + if (horz) { + int modifier = (y > MapCellY) ? -MAP_CELL_W : MAP_CELL_W; + + for (int index = 0; index < MapCellWidth; index++) { + CELL trycell = XY_Cell(((x + index) % MapCellWidth) + MapCellX, y + MapCellY); + + if (Good_Reinforcement_Cell(trycell, trycell+modifier, loco, zone, mzone)) { + return(trycell); + } + } + } + + /* + ** If there was no success in finding a suitable reinforcement edge cell, then return + ** with the default 'punt' cell location. + */ + return(punt); +} + + +/*********************************************************************************************** + * DisplayClass::Good_Reinforcement_Cell -- Checks cell for renforcement legality. * + * * + * This routine will check the secified cell (given the specified conditions) and determine * + * if that is a good cell for reinforcement purposes. It checks for passability of the cell * + * as well as zone and whether blocking walls can be crushed. * + * * + * INPUT: outcell -- The cell that is just outside the edge of the map. * + * * + * incell -- The cell that is just inside the edge of the map. * + * * + * loco -- The locomotion type of the reinforcement. * + * * + * zone -- The zone that the eventual movement destination lies. A reinforcement * + * edge must fall within the same zone. * + * * + * mzone -- The zone check type to check against (if zone checking required) * + * * + * OUTPUT: bool; Is the specified cell good for reinforcement purposes? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const +{ + /* + ** If the map edge location is not clear for object placement, then this is not + ** a good cell for reinforcement purposes. + */ + if (!(*this)[outcell].Is_Clear_To_Move(loco, false, false)) { + return(false); + } + + /* + ** If it looks like the on-map cell cannot be driven on to, then return with + ** the failure code. + */ + if (!(*this)[incell].Is_Clear_To_Move(loco, false, false, zone, mzone)) { + return(false); + } + + /* + ** If the reinforcement cell is already occupied, then return a failure code. + */ + if ((*this)[outcell].Cell_Techno() != NULL) { + return(false); + } + if ((*this)[incell].Cell_Techno() != NULL) return(false); + + /* + ** All tests have passed, return with success code. + */ +//Mono_Printf("<%04X>\n", incell);Keyboard->Get(); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Select_These -- All selectable objects in region are selected. * + * * + * Use this routine to simultaneously select all objects within the coordinate region * + * specified. This routine is used by the multi-select rubber band handler. * + * * + * INPUT: coord1 -- Coordinate of one corner of the selection region. * + * * + * coord2 -- The opposite corner of the selection region. * + * * + * additive -- Does this add to the existing selection or replace it. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 04/25/1995 JLB : Limited to non-building type. * + * 03/06/1996 JLB : Allows selection of aircraft with bounding box. * + *=============================================================================================*/ +static bool should_exclude_from_selection(ObjectClass* obj) +{ + return (obj->What_Am_I() == RTTI_UNIT) && + (((UnitClass *)obj)->Class->IsToHarvest || + *((UnitClass *)obj) == UNIT_MCV); +} + +void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool additive) +{ + COORDINATE tcoord = TacticalCoord; //Cell_Coord(TacticalCell) & 0xFF00FF00L; + + coord1 = Coord_Add(tcoord, coord1); + coord2 = Coord_Add(tcoord, coord2); + int x1 = Coord_X(coord1); + int x2 = Coord_X(coord2); + int y1 = Coord_Y(coord1); + int y2 = Coord_Y(coord2); + + /* + ** Ensure that coordinate number one represents the upper left corner + ** and coordinate number two represents the lower right corner. + */ + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + /* + ** Sweep through all ground layer objects and select the ones within the + ** bounding box. + */ + if (!additive) { + Unselect_All(); + } + AllowVoice = true; + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * obj = Layer[LAYER_GROUND][index]; + COORDINATE ocoord = obj->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are allowed to be selected, and are within the bounding box. + */ + HouseClass * hptr = HouseClass::As_Pointer(obj->Owner()); + if ( obj->Class_Of().IsSelectable && + obj->What_Am_I() != RTTI_BUILDING && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = (hptr != NULL) && hptr->IsPlayerControl; + AllowVoice &= is_player_controlled; + if (obj->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** Select any aircraft with the bounding box. + */ + for (int air_index = 0; air_index < Aircraft.Count(); air_index++) { + AircraftClass * aircraft = Aircraft.Ptr(air_index); + COORDINATE ocoord = aircraft->Center_Coord(); + int x = Coord_X(ocoord); + int y = Coord_Y(ocoord); + + /* + ** Only try to select objects that are allowed to be selected, and are within the bounding box. + */ + if ( aircraft->Class->IsSelectable && + !aircraft->Is_Selected_By_Player() && + x >= x1 && x <= x2 && y >= y1 && y <= y2) { + bool old_allow_voice = AllowVoice; + bool is_player_controlled = aircraft->House->IsPlayerControl; + AllowVoice &= is_player_controlled; + if (aircraft->Select(true)) { + if (is_player_controlled) { + old_allow_voice = false; + } + } + AllowVoice = old_allow_voice; + } + } + + /* + ** If a mix of player and non-player controlled units were selected, make sure non-player controlled units are de-selected + */ + bool player_controlled_units = false, non_player_controlled_units = false; + for (int i = 0; (i < CurrentObject.Count()) && (!player_controlled_units || !non_player_controlled_units); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (hptr->IsPlayerControl) { + player_controlled_units = true; + } + else { + non_player_controlled_units = true; + } + } + if (player_controlled_units && non_player_controlled_units) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + HouseClass * hptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + if (!hptr->IsPlayerControl) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + + /* + ** If player-controlled units are non-additively selected, remove harvesters and MCVs if they aren't the only types of units selected + */ + if (!additive && player_controlled_units) { + bool any_to_exclude = false, all_to_exclude = true; + for (int i = 0; i < CurrentObject.Count(); ++i) { + bool exclude = should_exclude_from_selection(CurrentObject[i]); + any_to_exclude |= exclude; + all_to_exclude &= exclude; + } + if (any_to_exclude && !all_to_exclude) { + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (should_exclude_from_selection(CurrentObject[i])) { + int count_before = CurrentObject.Count(); + CurrentObject[i]->Unselect(); + if (count_before <= CurrentObject.Count()) { + GlyphX_Debug_Print("Select_These failed to remove an object"); + CurrentObject.Delete(CurrentObject[i]); + } + --i; + } + } + } + } + + AllowVoice = true; +} + + +/*********************************************************************************************** + * DisplayClass::Refresh_Band -- Causes all cells under the rubber band to be redrawn. * + * * + * Use this routine to flag all cells that are covered in some fashion by the multi-unit * + * select "rubber band" to be redrawn. This is necessary whenever the rubber band changes * + * size or is being removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Refresh_Band(void) +{ + if (IsRubberBand) { + + /* + ** In rubber band mode, mark all cells under the "rubber band" to be + ** redrawn. + */ + int x1 = BandX+TacPixelX; + int y1 = BandY+TacPixelY; + int x2 = NewX+TacPixelX; + int y2 = NewY+TacPixelY; + + if (x1 > x2) { + int temp = x1; + x1 = x2; + x2 = temp; + } + if (y1 > y2) { + int temp = y1; + y1 = y2; + y2 = temp; + } + + CELL cell; + for (int y = y1; y <= y2+CELL_PIXEL_H; y += CELL_PIXEL_H) { + cell = Click_Cell_Calc(x1, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(x2, Bound(y, 0, TacPixelY+Lepton_To_Pixel(TacLeptonHeight))); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + for (int x = x1; x <= x2+CELL_PIXEL_W; x += CELL_PIXEL_W) { + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y1); + if (cell != -1) (*this)[cell].Redraw_Objects(); + + cell = Click_Cell_Calc(Bound(x, 0, TacPixelX+Lepton_To_Pixel(TacLeptonWidth)), y2); + if (cell != -1) (*this)[cell].Redraw_Objects(); + } + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Action -- Processes input for the tactical map. * + * * + * This routine handles the input directed at the tactical map. Since input, in this * + * regard, includes even the presence of the mouse over the tactical map, this routine * + * is called nearly every game frame. It handles adjusting the mouse shape as well as * + * giving orders to units. * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/17/1995 JLB : Created. * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Action(unsigned flags, KeyNumType & key) +{ + int x,y; // Sub cell pixel coordinates. + bool shadow; + ObjectClass * object = 0; + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + + bool edge = (y == 0 || x == 0 || x == SeenBuff.Get_Width()-1 || y == SeenBuff.Get_Height()-1); + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + if (coord) + { + //shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 8/6/2019 10:49AM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + if (object != NULL && object->Is_Techno() && !((TechnoClass *)object)->IsOwnedByPlayer && (((TechnoClass *)object)->Cloak == CLOAKED || ((TechnoClass *)object)->Techno_Type_Class()->IsInvisible)) { + object = NULL; + } + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object != NULL) { + action = Best_Object_Action(object); + } else { + action = Best_Object_Action(cell); + } + + // this updates the mouse cursor + Map.Mouse_Left_Up(cell, shadow, object, action); + } else { + + if (object && object->Class_Of().IsSelectable) { + action = ACTION_SELECT; + } + + if (Map.IsRepairMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Repair()) { + action = ACTION_REPAIR; + } else { + action = ACTION_NO_REPAIR; + } + } + + if (Map.IsSellMode) { + if (object && object->Owner() == PlayerPtr->Class->House && object->Can_Demolish()) { + if (object->What_Am_I() == RTTI_BUILDING) { + action = ACTION_SELL; + } else { + action = ACTION_SELL_UNIT; + } + } else { + + /* + ** Check to see if the cursor is over an owned wall. + */ + if (Map[cell].Overlay != OVERLAY_NONE && + OverlayTypeClass::As_Reference(Map[cell].Overlay).IsWall && + Map[cell].Owner == PlayerPtr->Class->House) { + action = ACTION_SELL; + } else { + action = ACTION_NO_SELL; + } + } + } + + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + action = ACTION_NUKE_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_BOMB) { + action = ACTION_PARA_BOMB; + } + + if (Map.IsTargettingMode == SPC_PARA_INFANTRY) { + action = ACTION_PARA_INFANTRY; + } + + if (Map.IsTargettingMode == SPC_SPY_MISSION) { + action = ACTION_SPY_MISSION; + } + + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + action = ACTION_IRON_CURTAIN; + } + + if (Map.IsTargettingMode == SPC_CHRONOSPHERE) { + action = ACTION_CHRONOSPHERE; + } + + if (Map.IsTargettingMode == SPC_CHRONO2) { + action = ACTION_CHRONO2; + if (shadow) action = ACTION_NOMOVE; + ObjectClass const * tobject = As_Object(PlayerPtr->UnitToTeleport); + + /* + ** Determine if the object can be teleported to the destination cell. + */ + if (tobject != NULL && tobject->Is_Techno()) { + TechnoClass const * uobject = (TechnoClass const *)tobject; + if (!uobject->Can_Teleport_Here(cell)) { +// if (((UnitClass *)As_Object(PlayerPtr->UnitToTeleport))->Can_Enter_Cell(cell, FACING_NONE) != MOVE_OK) { + action = ACTION_NOMOVE; + } + + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else { // If the object is no longer valid, cancel targetting mode. + action = ACTION_NOMOVE; + Map.IsTargettingMode = SPC_NONE; + } +#endif + } + + if (Map.PendingObject) { + action = ACTION_NONE; + } + } + + /* + ** Move any cursor displayed. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + /* + ** A right mouse button press cancels the current action or selection. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** Make sure that if the mouse button has been released and the map doesn't know about it, + ** then it must be informed. Do this by faking a mouse release event. + */ + if ((flags & LEFTUP) && Map.IsRubberBand) { + flags |= LEFTRELEASE; + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (!edge) { + if (flags & LEFTUP) { + Map.Mouse_Left_Up(cell, shadow, object, action); + } + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTRELEASE) { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + + /* + ** When the mouse is first pressed on the map, then record the mouse + ** position so that a proper check before going into rubber band + ** mode can be made. Rubber band mode starts when the mouse is + ** held down and moved a certain minimum distance. + */ + if (!edge && (flags & LEFTPRESS)) { + Map.Mouse_Left_Up(cell, shadow, object, action); + Map.Mouse_Left_Press(x, y); + } + + /* + ** While the mouse is being held down, determine if rubber band mode should + ** start. If rubber band mode is already active, then update the size + ** and flag the map to redraw it. + */ + if (flags & LEFTHELD) { + Map.Mouse_Left_Held(x, y); + } + } + + return (GadgetClass::Action(0, key)); +} + + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Selection_At_Mouse -- Object selection * + * * + * Selects any objects at the current mouse position. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Selection_At_Mouse(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + + x = UserInput.Mouse.X; + y = UserInput.Mouse.Y; + + if (x == 0 || y == SeenBuff.Get_Height() || x == SeenBuff.Get_Width()) + { + edge = true; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + if (coord) { + bool shadow = (!Map[cell].Is_Visible(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (object != nullptr) + { + bool shiftdown = UserInput.KeyB.Shift == true; + + if (shiftdown) + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_TOGGLE_SELECT); + } + else + { + Map.Mouse_Left_Release(cell, x, y, object, ACTION_SELECT); + } + + } + else + { + Unselect_All(); + } + } + + return 0; +} + +/*********************************************************************************************** + * DisplayClass::TacticalClass::Command_Object -- Commanding Units * + * * + * Issues a command to the currently selected unit. * + * * + * * + * INPUT: flags -- The gadget event flags that triggered the call to this function. * + * * + * key -- A reference to the keyboard event (if any). * + * * + * OUTPUT: bool; Should processing be aborted on any succeeding buttons in the chain? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/17 JAS * + *=============================================================================================*/ +int DisplayClass::TacticalClass::Command_Object(unsigned flags, KeyNumType & key) +{ + int x, y; // Sub cell pixel coordinates. + bool edge = false; + + x = UserInput.Mouse.X; + y = UserInput.Mouse.Y; + + if (x == 0 || y == SeenBuff.Get_Height() || x == SeenBuff.Get_Width()) + { + edge = true; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + ActionType action = ACTION_NONE; + + if (coord) { + bool shadow = (!Map[cell].Is_Mapped(PlayerPtr) && !Debug_Unshroud); // Use PlayerPtr since we won't be rendering in MP. ST - 3/6/2019 2:49PM + x -= Map.TacPixelX; + y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + + ObjectClass* object = nullptr; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Close_Object(coord); + } + + if (CurrentObject.Count()) { + if (object) { + action = Best_Object_Action(object); + } + else { + action = Best_Object_Action(cell); + } + } + + if (action != ACTION_SELECT) + { + Map.Mouse_Left_Release(cell, x, y, object, action); + } + } + return 0; +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Right_Press -- Handles the right mouse button press. * + * * + * This routine is called when the right mouse button is pressed. This action is supposed * + * to cancel whatever mode or process is active. If there is nothing to cancel, then it * + * will default to unselecting any units that might be currently selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Right_Press(void) +{ + if (PendingObjectPtr && PendingObjectPtr->Is_Techno()) { + //PendingObjectPtr->Transmit_Message(RADIO_OVER_OUT); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + } else { + if (IsRepairMode) { + IsRepairMode = false; + } else { + if (IsSellMode) { + IsSellMode = false; + } else { + if (IsTargettingMode != SPC_NONE) { + IsTargettingMode = SPC_NONE; + } else { + Unselect_All(); + } + } + } + } + + // If it breaks... call 228. + Set_Default_Mouse(MOUSE_NORMAL, Map.IsSmall); +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Up -- Handles the left mouse "cruising" over the map. * + * * + * This routine is called continuously while the mouse is over the tactical map but there * + * are no mouse buttons pressed. Typically, this adjusts the mouse shape and the pop-up * + * help text. * + * * + * INPUT: shadow -- Is the mouse hovering over shadowed terrain? * + * * + * object -- Pointer to the object that the mouse is currently over (may be NULL). * + * * + * action -- This is the action that the currently selected object (if any) will * + * perform if the left mouse button were clicked at this location. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 07/05/1995 JLB : Removed pop up help text for shadow and terrain after #3. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall) +{ + IsTentative = false; + + TARGET target = TARGET_NONE; + if (object != NULL) { + target = object->As_Target(); + } else { + if (cell != -1) { + target = As_Target(cell); + } + } + + /* + ** Don't allow selection of an object that is located in shadowed terrain. + ** In fact, just show the normal move cursor in order to keep the shadowed + ** terrain a mystery. + */ + if (shadow) { + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_NONE: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + + case ACTION_NO_SELL: + case ACTION_SELL: + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + case ACTION_NOMOVE: + if (CurrentObject.Count()) { + MouseType mouse_type = MOUSE_NO_MOVE; + for (int i = 0; i < CurrentObject.Count(); ++i) { + if (CurrentObject[i]->What_Am_I() != RTTI_AIRCRAFT) { + mouse_type = MOUSE_CAN_MOVE; + break; + } + } + Set_Default_Mouse(mouse_type, wsmall); + break; + } + // Fall into next case for non aircraft object types. + + default: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + } + } else { + + /* + ** Change the mouse shape according to the default action that will occur + ** if the mouse button were clicked at this location. + */ + switch (action) { + case ACTION_NO_DEPLOY: + Set_Default_Mouse(MOUSE_NO_DEPLOY, wsmall); + break; + + case ACTION_NO_ENTER: + Set_Default_Mouse(MOUSE_NO_ENTER, wsmall); + break; + + case ACTION_NO_GREPAIR: + Set_Default_Mouse(MOUSE_NO_GREPAIR, wsmall); + break; + + case ACTION_DAMAGE: + Set_Default_Mouse(MOUSE_DAMAGE, wsmall); + break; + + case ACTION_GREPAIR: + Set_Default_Mouse(MOUSE_GREPAIR, wsmall); + break; + + case ACTION_TOGGLE_SELECT: + case ACTION_SELECT: + Set_Default_Mouse(MOUSE_CAN_SELECT, wsmall); + break; + + case ACTION_MOVE: + Set_Default_Mouse(MOUSE_CAN_MOVE, wsmall); + break; + + case ACTION_GUARD_AREA: + Set_Default_Mouse(MOUSE_AREA_GUARD, wsmall); + break; + + case ACTION_ATTACK: + if (Target_Legal(target) && CurrentObject.Count() == 1 && CurrentObject[0]->Is_Techno() && ((TechnoClass *)CurrentObject[0])->In_Range(target, 0)) { + Set_Default_Mouse(MOUSE_STAY_ATTACK, wsmall); + break; + } + // fall into next case. + + case ACTION_HARVEST: + Set_Default_Mouse(MOUSE_CAN_ATTACK, wsmall); + break; + + case ACTION_SABOTAGE: + Set_Default_Mouse(MOUSE_DEMOLITIONS, wsmall); + break; + + case ACTION_ENTER: + case ACTION_CAPTURE: + Set_Default_Mouse(MOUSE_ENTER, wsmall); + break; + + case ACTION_NOMOVE: + Set_Default_Mouse(MOUSE_NO_MOVE, wsmall); + break; + + case ACTION_NO_SELL: + Set_Default_Mouse(MOUSE_NO_SELL_BACK, wsmall); + break; + + case ACTION_NO_REPAIR: + Set_Default_Mouse(MOUSE_NO_REPAIR, wsmall); + break; + + case ACTION_SELF: + Set_Default_Mouse(MOUSE_DEPLOY, wsmall); + break; + + case ACTION_REPAIR: + Set_Default_Mouse(MOUSE_REPAIR, wsmall); + break; + + case ACTION_SELL_UNIT: + Set_Default_Mouse(MOUSE_SELL_UNIT, wsmall); + break; + + case ACTION_SELL: + Set_Default_Mouse(MOUSE_SELL_BACK, wsmall); + break; + + case ACTION_NUKE_BOMB: + Set_Default_Mouse(MOUSE_NUCLEAR_BOMB, wsmall); + break; + + case ACTION_AIR_STRIKE: + case ACTION_PARA_BOMB: + case ACTION_PARA_INFANTRY: + case ACTION_SPY_MISSION: + case ACTION_IRON_CURTAIN: + Set_Default_Mouse(MOUSE_AIR_STRIKE, wsmall); + break; + + case ACTION_CHRONOSPHERE: + Set_Default_Mouse(MOUSE_CHRONO_SELECT, wsmall); + break; + + case ACTION_CHRONO2: + Set_Default_Mouse(MOUSE_CHRONO_DEST, wsmall); + break; + + case ACTION_HEAL: + Set_Default_Mouse(MOUSE_HEAL, wsmall); + break; + + case ACTION_TOGGLE_PRIMARY: + Set_Default_Mouse(MOUSE_DEPLOY, wsmall); + break; + + default: + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + break; + } + } + + /* + ** Never display help text if the mouse is held over the radar map. + */ + if (wsmall) { + return; + } + + /* + ** Give a generic help message when over shadow terrain. + */ + if (shadow) { + Help_Text(TXT_SHADOW); + } else { + + /* + ** If the mouse is held over objects on the map, then help text may + ** pop up that tells what the object is. This call informs the help + ** system of the text name for the object under the mouse. + */ + if (object != NULL) { + int text; + int color = LTGREY; + + /* + ** Fetch the appropriate background color for help text. + */ + if (PlayerPtr->Is_Ally(object)) { + color = GREEN; + } else { + if (object->Owner() == HOUSE_NONE || object->Owner() == HOUSE_NEUTRAL) { + color = LTGREY; + } else { + color = PINK; + } + } + + /* + ** Fetch the name of the object. If it is an enemy object, then + ** the exact identity is glossed over with a generic text. + */ + text = object->Full_Name(); + if (object->Is_Techno() && !((TechnoTypeClass const &)object->Class_Of()).IsNominal) + { + + if (!((TechnoClass *)object)->House->Is_Ally(PlayerPtr)) + { + switch (object->What_Am_I()) { + case RTTI_INFANTRY: + text = TXT_ENEMY_SOLDIER; + break; + + case RTTI_UNIT: + text = TXT_ENEMY_VEHICLE; + break; + + case RTTI_BUILDING: + text = TXT_ENEMY_STRUCTURE; + break; + } + } + } + + if ( object->What_Am_I() != RTTI_TERRAIN ) { + Help_Text(text, -1, -1, color); + } else { + Help_Text(TXT_NONE); + } + } else { + if ((*this)[cell].Land_Type() == LAND_TIBERIUM) { + Help_Text(TXT_MINERALS); + } else { + Help_Text(TXT_NONE); + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Release -- Handles the left mouse button release. * + * * + * This routine is called when the left mouse button is released over the tactical map. * + * The release event is the workhorse of the game. Most actions occur at the moment of * + * mouse release. * + * * + * INPUT: cell -- The cell that the mouse is over. * + * * + * x,y -- The mouse pixel coordinate. * + * * + * object -- Pointer to the object that the mouse is over. * + * * + * action -- The action that the currently selected object (if any) will * + * perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + * 03/27/1995 JLB : Handles sell and repair actions. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall) +{ + if (PendingObjectPtr) { + /* + ** Try to place the pending object onto the map. + */ + if (ProximityCheck) { + OutList.Add(EventClass(EventClass::PLACE, PendingObjectPtr->What_Am_I(), cell + ZoneOffset)); + } else { + Speak(VOX_DEPLOY); + } + + } else { + + if (IsRubberBand) { + Refresh_Band(); + Select_These(XYP_Coord(BandX, BandY), XYP_Coord(x, y)); + + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + IsRubberBand = false; + IsTentative = false; + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); + + } else { + + /* + ** Toggle the select state of the object. + */ + if (action == ACTION_TOGGLE_SELECT) { + if (!object || !CurrentObject.Count()) { + action = ACTION_SELECT; + } else { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } else { + object->Select(); + } + } + } + + /* + ** Selection of other object action. + */ + if (action == ACTION_SELECT || (action == ACTION_NONE && object && object->Class_Of().IsSelectable && !object->Is_Selected_By_Player())) { + if (object->Is_Selected_By_Player()) { + object->Unselect(); + } + if (object->Select()) { + Unselect_All_Except(object); + Set_Default_Mouse(MOUSE_NORMAL, wsmall); + } + } + + /* + ** If an action was detected as possible, then pass this action event + ** to all selected objects. + */ + if (action != ACTION_NONE && action != ACTION_SELECT && action != ACTION_TOGGLE_SELECT) { + + /* + ** Pass the action to all the selected objects. But first, redetermine + ** what action that object should perform. This, seemingly redundant + ** process, is necessary since multiple objects could be selected and each + ** might perform a different action when the click occurs. + */ + bool doflash = true; + AllowVoice = true; + FormMove = false; + FormSpeed = SPEED_WHEEL; + FormMaxSpeed = MPH_LIGHT_SPEED; + + if ( (action == ACTION_MOVE || action == ACTION_NOMOVE) && CurrentObject.Count()) { + + /* + ** Scan all units. If any are selected that shouldn't be, or aren't + ** selected but should be, then this is not a formation move. + */ + int group = 254; // init to invalid group # + + if (CurrentObject[0]->Is_Foot()) { + group = ((FootClass *)CurrentObject[0])->Group; + } + + /* + ** Presume this is a formation move unless something is detected + ** that will prevent it. + */ + FormMove = true; + + /* + ** First scan through all the selected units to make sure that they + ** are all of the same team and have been assigned a particular formation + */ + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tobject = CurrentObject[index]; + + /* + ** Only moveable (i.e., FootClass) objects can ever be in a formation + ** so if a selected object isn't of a FootClass type then it can't be + ** a formation move. + */ + if (tobject->Is_Foot() == false) { + FormMove = false; + break; + } + + /* + ** If the object is not part of the same team as the rest of the + ** selected group, or it just plain has never been assigned a + ** formation offset, then it can't be a formation move. + */ + FootClass const * foot = (FootClass *)tobject; + if (foot->Group != group || foot->XFormOffset == 0x80000000) { + FormMove = false; + break; + } + + /* + ** Determine the formation speed on the presumption that this + ** will turn out to be a formation move. + */ + MPHType maxspeed = foot->Techno_Type_Class()->MaxSpeed; + if (maxspeed < FormMaxSpeed) { + FormMaxSpeed = maxspeed; + FormSpeed = foot->Techno_Type_Class()->Speed; + } + } + + /* + ** Loop through all objects (that can theoretically be part of a team) and + ** if there are any that are part of the currently selected team, but + ** are not currently selected themselves, then this will force this move + ** to NOT be a formation move. + */ + if (FormMove) { + for (int index = 0; index < ::Logic.Count(); index++) { + ObjectClass const * obj = ::Logic[index]; + + /* + ** If the object is selected, then it has already been scanned + ** by the previous loop. + */ + if (obj->Is_Selected_By_Player()) continue; + + /* + ** Only consider footclass objects. + */ + if (!obj->Is_Foot()) continue; + + FootClass const * foot = (FootClass *)obj; + + /* + ** Only consider objects that are owned by the player. + */ + if (!foot->IsOwnedByPlayer) continue; + + /* + ** If another member of this team has been discovered and + ** it isn't selected, then the formation move cannot take + ** place. + */ + if (foot->Group == group) { + FormMove = false; + break; + } + } + } + } + + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass * tobject = CurrentObject[index]; + + if (object != NULL) { + tobject->Active_Click_With(tobject->What_Action(object), object); + } else { + + /* + ** Trap for formation moves: if this unit is part of a + ** formation (being part of a team qualifies) and they're + ** told to move, adjust the target destination so they stay + ** in formation when they arrive. + */ + CELL newmove = cell; + int whatami = tobject->What_Am_I(); + if (action == ACTION_MOVE && tobject->Is_Foot()) { + int oldisform; + FootClass * foot = (FootClass *)tobject; + oldisform = foot->IsFormationMove; + foot->IsFormationMove = FormMove; + if (FormMove && foot->Group != 255 ) { + newmove = foot->Adjust_Dest(cell); + } + foot->IsFormationMove = oldisform; + } + tobject->Active_Click_With(tobject->What_Action(cell), newmove); + } + AllowVoice = false; + } + AllowVoice = true; + FormMove = false; + + if (action == ACTION_REPAIR && object->What_Am_I() == RTTI_BUILDING) { + OutList.Add(EventClass(EventClass::REPAIR, TargetClass(object))); + } + if (action == ACTION_SELL_UNIT && object) { + switch (object->What_Am_I()) { + case RTTI_AIRCRAFT: + case RTTI_UNIT: + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + break; + + default: + break; + } + + } + if (action == ACTION_SELL) { + if (object) { + OutList.Add(EventClass(EventClass::SELL, TargetClass(object))); + } else { + OutList.Add(EventClass(EventClass::SELLCELL, cell)); +// OutList.Add(EventClass(EventClass::SELL, ::As_Target(cell))); + } + } + + if (action == ACTION_NUKE_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_NUCLEAR_BOMB, cell)); + } + + if (action == ACTION_PARA_BOMB) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_BOMB, cell)); + } + if (action == ACTION_PARA_INFANTRY) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_PARA_INFANTRY, cell)); + } + if (action == ACTION_SPY_MISSION) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SPY_MISSION, cell)); + } + if (action == ACTION_IRON_CURTAIN) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_IRON_CURTAIN, cell)); + } + if (action == ACTION_CHRONOSPHERE) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONOSPHERE, cell)); + } + if (action == ACTION_CHRONO2) { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_CHRONO2, cell)); + } + } + + IsTentative = false; + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Press -- Handles the left mouse button press. * + * * + * Handle the left mouse button press while over the tactical map. If it isn't is * + * repair or sell mode, then a tentative transition to rubber band mode is flagged. If the * + * mouse moves a sufficient distance from this recorded position, then rubber band mode * + * is officially started. * + * * + * INPUT: x,y -- The mouse coordinates at the time of the press. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Press(int x, int y) +{ + if (!IsRepairMode && !IsSellMode && IsTargettingMode == SPC_NONE && !PendingObject) { + IsTentative = true; + BandX = x; + BandY = y; + NewX = x; + NewY = y; + } +} + + +/*********************************************************************************************** + * DisplayClass::Mouse_Left_Held -- Handles the left button held down. * + * * + * This routine is called continuously while the left mouse button is held down over * + * the tactical map. This handles the rubber band mode detection and dragging. * + * * + * INPUT: x,y -- The mouse coordinate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/24/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Mouse_Left_Held(int x, int y) +{ + if (IsRubberBand) { + if (x != NewX || y != NewY) { + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + Refresh_Band(); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + } + } else { + + /* + ** If the mouse is still held down while a tentative extended select is possible, then + ** check to see if the mouse has moved a sufficient distance in order to activate + ** extended select mode. + */ + if (IsTentative) { + + /* + ** The mouse must have moved a minimum distance before rubber band mode can be + ** initiated. + */ + if (ABS(x - BandX) > 4 || ABS(y - BandY) > 4) { + IsRubberBand = true; + x = Bound(x, 0, Lepton_To_Pixel(TacLeptonWidth)-1); + y = Bound(y, 0, Lepton_To_Pixel(TacLeptonHeight)-1); + NewX = x; + NewY = y; + IsToRedraw = true; + Flag_To_Redraw(false); + + /* + ** Stretching the rubber band requires all objects to be redrawn. + */ + int index; + for (index = 0; index < Layer[LAYER_TOP].Count(); index++) { + Layer[LAYER_TOP][index]->Mark(MARK_CHANGE); + } + for (index = 0; index < Layer[LAYER_AIR].Count(); index++) { + Layer[LAYER_AIR][index]->Mark(MARK_CHANGE); + } + + } + } + } +} + + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +extern int GlyphXClientSidebarWidthInLeptons; + +/*********************************************************************************************** + * DisplayClass::Set_Tactical_Position -- Sets the tactical view position. * + * * + * This routine is used to set the tactical view position. The requested position is * + * clipped to the map dimensions as necessary. * + * * + * INPUT: coord -- The coordinate desired for the upper left corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Set_Tactical_Position(COORDINATE coord) +{ + /* + ** Bound the desired location to fit the legal map edges. + */ + int xx = (int)Coord_X(coord) - (int)Cell_To_Lepton(MapCellX); + int yy = (int)Coord_Y(coord) - (int)Cell_To_Lepton(MapCellY); + +// Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth) + GlyphXClientSidebarWidthInLeptons, Cell_To_Lepton(MapCellHeight)); // Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM + Confine_Rect(&xx, &yy, TacLeptonWidth, TacLeptonHeight, Cell_To_Lepton(MapCellWidth), Cell_To_Lepton(MapCellHeight)); + coord = XY_Coord(xx + Cell_To_Lepton(MapCellX), yy + Cell_To_Lepton(MapCellY)); + + if (ScenarioInit) { + TacticalCoord = coord; + } + DesiredTacticalCoord = coord; + + IsToRedraw = true; + Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * DisplayClass::Compute_Start_Pos -- Computes player's start pos from unit coords. * + * * + * Use this function in multiplayer games, to compute the scenario starting Tactical Pos. * + * * + * INPUT: none * + * * + * OUTPUT: x, y -- Player starting location * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/28/1995 JLB : Commented. * + * 06/26/1995 JLB : Fixed building loop. * + * 10/20/1996 JLB : Doesn't wrap. * + *=============================================================================================*/ +void DisplayClass::Compute_Start_Pos(long& x, long& y) +{ + /* + ** Find the summation cell-x & cell-y for all the player's units, infantry, + ** and buildings. Buildings are weighted so that they count 16 times more + ** than units or infantry. + */ + x = 0; + y = 0; + long num = 0; + int i; + for (i = 0; i < Infantry.Count(); i++) { + InfantryClass * infp = Infantry.Ptr(i); + if (!infp->IsInLimbo && infp->Is_Owned_By_Player()) { + x += (long)Coord_XCell(infp->Coord); + y += (long)Coord_YCell(infp->Coord); + num++; + } + } + + for (i = 0; i < Units.Count(); i++) { + UnitClass * unitp = Units.Ptr(i); + if (!unitp->IsInLimbo && unitp->Is_Owned_By_Player()) { + x += (long)Coord_XCell(unitp->Coord); + y += (long)Coord_YCell(unitp->Coord); + num++; + } + } + + for (i = 0; i < Buildings.Count(); i++) { + BuildingClass * bldgp = Buildings.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { + x += (((long)Coord_XCell(bldgp->Coord)) * 16); + y += (((long)Coord_YCell(bldgp->Coord)) * 16); + num += 16; + } + } + + for (i = 0; i < Vessels.Count(); i++) { + VesselClass * bldgp = Vessels.Ptr(i); + if (!bldgp->IsInLimbo && bldgp->Is_Owned_By_Player()) { + x += (((long)Coord_XCell(bldgp->Coord))); + y += (((long)Coord_YCell(bldgp->Coord))); + num++; + } + } + + /* + ** Divide each coord by 'num' to compute the average value + */ + if (num > 0) { + x /= num; + } else { + x = 0; + } + + if (num > 0) { + y /= num; + } else { + y = 0; + } +} + + +/*********************************************************************************************** + * DisplayClass::Sell_Mode_Control -- Controls the sell mode. * + * * + * This routine will control the sell mode for the player. * + * * + * INPUT: control -- The mode to set the sell state to. * + * 0 = Turn sell mode off. * + * 1 = Turn sell mode on. * + * -1 = Toggle sell mode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Sell_Mode_Control(int control) +{ + bool mode = IsSellMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsSellMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsSellMode && !PendingObject) { + IsRepairMode = false; + if (mode && PlayerPtr->BScan) { + IsSellMode = true; + Unselect_All(); + } else { + IsSellMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Repair_Mode_Control -- Controls the repair mode. * + * * + * This routine is used to control the repair mode for the player. * + * * + * INPUT: control -- The mode to set the repair to. * + * 0 = Turn repair off. * + * 1 = Turn repair on. * + * -1= Toggle repair state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Repair_Mode_Control(int control) +{ + bool mode = IsRepairMode; + switch (control) { + case 0: + mode = false; + break; + + case -1: + mode = (IsRepairMode == false); + break; + + case 1: + mode = true; + break; + } + + if (mode != IsRepairMode && !PendingObject) { + IsSellMode = false; + if (mode && PlayerPtr->BScan) { + IsRepairMode = true; + Unselect_All(); + } else { + IsRepairMode = false; + Revert_Mouse_Shape(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::In_View -- Determines if cell is visible on screen. * + * * + * Use this routine to determine if the specified cell is visible on * + * the display. This is a useful fact, since many display operations * + * can be skipped if the cell is not visible. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: bool; Is this cell visible on the display? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1994 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DisplayClass::In_View(register CELL cell) const +{ + if (cell & 0xC000) return(false); + + COORDINATE coord = Coord_Whole(Cell_Coord(cell)); + COORDINATE tcoord = Coord_Whole(TacticalCoord); + + if ((Coord_X(coord) - Coord_X(tcoord)) > TacLeptonWidth+CELL_LEPTON_W-1) return(false); + if ((Coord_Y(coord) - Coord_Y(tcoord)) > TacLeptonHeight+CELL_LEPTON_H-1) return(false); + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Closest_Free_Spot -- Finds the closest cell sub spot that is free. * + * * + * Use this routine to find the sub cell spot closest to the coordinate specified that is * + * free from occupation. Typical use of this is for infantry destination calculation. * + * * + * INPUT: coord -- The coordinate to use as the starting point when finding the closest * + * free spot. * + * * + * any -- Ignore occupation and just return the closest sub cell spot? * + * * + * OUTPUT: Returns with the coordinate of the closest free (possibly) sub cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE DisplayClass::Closest_Free_Spot(COORDINATE coord, bool any) const +{ + if (coord & HIGH_COORD_MASK) { + return(0x00800080); + } + return Map[coord].Closest_Free_Spot(coord, any); +} + + +/*********************************************************************************************** + * DisplayClass::Is_Spot_Free -- Determines if cell sub spot is free of occupation. * + * * + * Use this routine to determine if the coordinate (rounded to the nearest sub cell * + * position) is free for placement. Typical use of this would be for infantry placement. * + * * + * INPUT: coord -- The coordinate to examine for "freeness". The coordinate is rounded to * + * the nearest free sub cell spot. * + * * + * OUTPUT: Is the sub spot indicated by the coordinate free from previous occupation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool DisplayClass::Is_Spot_Free(COORDINATE coord) const +{ + // This can't be right. Copy/paste error, maybe? ST - 5/8/2019 + //if (coord & HIGH_COORD_MASK) { + // return(0x00800080); + //} + return Map[coord].Is_Spot_Free(CellClass::Spot_Index(coord)); +} + + +/*********************************************************************************************** + * DisplayClass::Center_Map -- Centers the map about the currently selected objects * + * * + * This routine will average the position of all the selected objects and then center * + * the map about those objects. * + * * + * INPUT: center -- The is an optional center about override coordinate. If specified, * + * then the map will be centered about that coordinate. Otherwise it * + * will center about the average location of all selected objects. * + * * + * OUTPUT: The center coordinate. * + * * + * WARNINGS: The map position changes by this routine. * + * * + * HISTORY: * + * 08/22/1995 JLB : Created. * + * 09/16/1996 JLB : Takes coordinate to center about (as override). * + *=============================================================================================*/ +COORDINATE DisplayClass::Center_Map(COORDINATE center) +{ + int x = 0; +// unsigned x = 0; + int y = 0; +// unsigned y = 0; + bool centerit = false; + + if (CurrentObject.Count()) { + + for (int index = 0; index < CurrentObject.Count(); index++) { + COORDINATE coord = CurrentObject[index]->Center_Coord(); + + x += Coord_X(coord); + y += Coord_Y(coord); + } + + x /= CurrentObject.Count(); + y /= CurrentObject.Count(); + centerit = true; + } + + if (center != 0L) { + x = Coord_X(center); + y = Coord_Y(center); + centerit = true; + } + + if (centerit) { + center = XY_Coord(x, y); + + x = x - (int)TacLeptonWidth/2; + if (x < Cell_To_Lepton(MapCellX)) x = Cell_To_Lepton(MapCellX); + + y = y - (int)TacLeptonHeight/2; + if (y < Cell_To_Lepton(MapCellY)) y = Cell_To_Lepton(MapCellY); + + Set_Tactical_Position(XY_Coord(x, y)); + + return center; + } + + return 0; +} + + +/*********************************************************************************************** + * DisplayClass::Encroach_Shadow -- Causes the shadow to creep back by one cell. * + * * + * This routine will cause the shadow to creep back by one cell. Multiple calls to this * + * routine will result in the shadow becoming more and more invasive until only the sight * + * range of player controlled units will keep the shadow pushed back. * + * * + * INPUT: none * + * house -- Player to apply shroud to * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/16/1995 JLB : Created. * + * 08/06/2019 ST: Added house parameter for multiplayer * + *=============================================================================================*/ +void DisplayClass::Encroach_Shadow(HouseClass * house) +{ + CELL cell; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Is_Visible(house) || !cellptr->Is_Mapped(house)) continue; + + cellptr->IsToShroud = true; // IsToShroud isn't used outside this function. ST - 8/6/2019 2:28PM + } + + /* + ** Mark all shadow edge cells to be fully shrouded. All adjacent mapped + ** cell should become partially shrouded. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (!In_Radar(cell)) continue; + + if ((*this)[cell].IsToShroud) { + (*this)[cell].IsToShroud = false; + Shroud_Cell(cell, house); + } + } + + All_To_Look(house); + + Flag_To_Redraw(true); +} + + +/*********************************************************************************************** + * DisplayClass::Shroud_Cell -- Returns the specified cell into the shrouded condition. * + * * + * This routine is called to add the shroud back to the cell specified. Typical of this * + * would be when the shroud is to regenerate. * + * * + * INPUT: cell -- The cell that the shroud is to be regenerated upon. * + * house -- Player to apply shroud to * + * * + * OUTPUT: none * + * * + * WARNINGS: Adjacent cells might be affected by this routine. The affect is determined * + * according to the legality of the partial shadow artwork. In the illegal cases * + * the adjacent cell might become shrouded as well. * + * * + * HISTORY: * + * 10/17/1995 JLB : Created. * + * 06/17/1996 JLB : Modified to handle the new shadow pieces. * + * 08/06/2019 ST: Added house parameter for multiplayer * + *=============================================================================================*/ +void DisplayClass::Shroud_Cell(CELL cell, HouseClass * house) +{ + if (house->IsGPSActive) { + if ( (*this)[cell].Is_Jamming(house) ) { + return; + } + } + if (!In_Radar(cell)) return; + + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Is_Mapped(house)) { + + cellptr->Set_Mapped(house, false); + cellptr->Set_Visible(house, false); + cellptr->Redraw_Objects(); + + /* + ** Check adjacent cells. There might be some weird combination of + ** shrouded cells such that more cells must be shrouded in order for + ** this to work. + */ + for (FacingType dir = FACING_FIRST; dir < FACING_COUNT; dir++) { + CELL c = Adjacent_Cell(cell, dir); + CellClass * cptr = &(*this)[c]; + + /* + ** If this adjacent cell must be completely shrouded as a result + ** of the map change, yet it isn't already shrouded, then recursively + ** shroud that cell. + */ + if (c != cell) { + cptr->Set_Visible(house, false); + } + + /* + ** Always redraw the cell because, more than likely, the shroud + ** edge will change shape because of the map change. + */ + cptr->Redraw_Objects(); + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Read_INI -- Reads map control data from INI file. * + * * + * This routine is used to read the map control data from the INI * + * file. * + * * + * INPUT: buffer -- Pointer to the loaded INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: The TriggerClass INI data must have been read before calling this function. * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Read_INI(CCINIClass & ini) +{ + /* + ** Read the map dimensions. + */ + char const * const name = "Map"; + int x = ini.Get_Int(name, "X", 1); + int y = ini.Get_Int(name, "Y", 1); + int w = ini.Get_Int(name, "Width", MAP_CELL_W-2); + int h = ini.Get_Int(name, "Height", MAP_CELL_H-2); + +#ifndef FIXIT_VERSION_3 // Map size no longer restricted. + +#ifdef FIXIT_CSII // checked - ajw + if(Session.Type >= GAME_MODEM && Session.Type <= GAME_INTERNET && PlayingAgainstVersion < VERSION_AFTERMATH_CS) { + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + ** BG Note: only do this for multiplayer games against non-AfterMath. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } + } +#else + /* + ** HACK ALERT: + ** Force the map to be limited to the size that 96x96 would be. If the + ** size is greater (due to hacking?) then shrink it down to legal size. + */ + if (w * h > 96 * 96) { + h -= (((w*h) - (96*96)) / w) + 1; + } +#endif + +#endif // !FIXIT_VERSION_3 + + Set_Map_Dimensions( x, y, w, h ); + + /* + ** The theater is determined at this point. There is specific data that + ** is custom to this data. Load the custom data (as it related to terrain) + ** at this point. + */ + Scen.Theater = ini.Get_TheaterType(name, "Theater", THEATER_TEMPERATE); + if (Scen.Theater == THEATER_NONE) { + Scen.Theater = THEATER_TEMPERATE; + } + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 + if (Scen.Theater != LastTheater) { + Reset_Theater_Shapes(); + } +#endif //WIN32 + + /* + ** Now that the theater is known, init the entire map hierarchy + */ + Init(Scen.Theater); + + /* + ** Special initializations occur when the theater is known. + */ + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + /* + ** Read the Waypoint entries. + */ + for (int i = 0; i < WAYPT_COUNT; i++) { + char buf[20]; + sprintf(buf, "%d", i); + Scen.Waypoint[i] = ini.Get_Int("Waypoints", buf, -1); + + if (Scen.Waypoint[i] != -1) { + (*this)[Scen.Waypoint[i]].IsWaypoint = 1; + } + } + + /* + ** Set the starting position (do this after Init(), which clears the cells' + ** IsWaypoint flags). + */ + if (Scen.Waypoint[WAYPT_HOME] == -1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + 5*RESFACTOR, MapCellY + 4*RESFACTOR); + } + + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); + + /* + ** Loop through all CellTrigger entries. + */ + int len = ini.Entry_Count("CellTriggers"); + for (int index = 0; index < len; index++) { + + /* + ** Get a cell trigger and cell assignment. + */ + char const * cellentry = ini.Get_Entry("CellTriggers", index); + TriggerTypeClass * tp = ini.Get_TriggerType("CellTriggers", cellentry); + CELL cell = atoi(cellentry); + + if (tp != NULL && !(*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt) { + tt->AttachCount++; + tt->Cell = cell; + (*this)[cell].Trigger = tt; + } + } + } + + /* + ** Read the map template data. + */ + static char const * const MAPPACK = "MapPack"; + len = ini.Get_UUBlock(MAPPACK, _staging_buffer, sizeof(_staging_buffer)); + BufferStraw bstraw(_staging_buffer, len); + Map.Read_Binary(bstraw); + + LastTheater = Scen.Theater; +} + + +/*********************************************************************************************** + * DisplayClass::Write_INI -- Write the map data to the INI file specified. * + * * + * This routine will output all the data of this map to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI handler to store the map data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: Any existing map data in the INI database will be replaced by this function. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Write_INI(CCINIClass & ini) +{ + char entry[20]; + + /* + ** Save the map parameters. + */ + static char const * const NAME = "Map"; + ini.Clear(NAME); + ini.Put_TheaterType(NAME, "Theater", Scen.Theater); + ini.Put_Int(NAME, "X", MapCellX); + ini.Put_Int(NAME, "Y", MapCellY); + ini.Put_Int(NAME, "Width", MapCellWidth); + ini.Put_Int(NAME, "Height", MapCellHeight); + + /* + ** Save the Waypoint entries. + */ + static char const * const WAYNAME = "Waypoints"; + ini.Clear(WAYNAME); + for (int i = 0; i < WAYPT_COUNT; i++) { + if (Scen.Waypoint[i] != -1) { + sprintf(entry, "%d", i); + ini.Put_Int(WAYNAME, entry, Scen.Waypoint[i]); + } + } + + /* + ** Save the cell's triggers. + */ + static char const * const CELLTRIG = "CellTriggers"; + ini.Clear(CELLTRIG); + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger.Is_Valid()) { + TriggerClass * tp = (*this)[cell].Trigger; + if (tp != NULL) { + + /* + ** Generate entry name. + */ + sprintf(entry, "%d", cell); + + /* + ** Save entry. + */ + ini.Put_TriggerType(CELLTRIG, entry, tp->Class); + } + } + } + + /* + ** Write the map template data out to the ini file. + */ + static char const * const MAPPACK = "MapPack"; + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + int len = Map.Write_Binary(bpipe); + ini.Clear(MAPPACK); + if (len) { + ini.Put_UUBlock(MAPPACK, _staging_buffer, len); + } +} + + +/*********************************************************************************************** + * DisplayClass::All_To_Look -- Direct all objects to look around for the player. * + * * + * This routine will scan through all objects and tell them to look if they are supposed * + * to be able to reveal the map for the player. This routine may be necessary in cases * + * of gap generator reshroud logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 08/06/2019 ST : Added house parameter so it can work for multiple players * + *=============================================================================================*/ +void DisplayClass::All_To_Look(HouseClass *house, bool units_only) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (tech->House == house) { + if (tech->Is_Discovered_By_Player(house)) { + object->Look(); + } + } else { + //if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)) { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house)) { + tech->Look(); + } + } + } + } +} + + +/* +** Added house parameter for client/server multiplayer. ST - 8/12/2019 11:48AM +** +** +*/ +void DisplayClass::Constrained_Look(COORDINATE center, LEPTON distance, HouseClass *house) +{ + for (int index = 0; index < Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Layer[LAYER_GROUND][index]; + if (object != NULL && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + +// if (tech->What_Am_I() == RTTI_BUILDING && units_only) continue; + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (tech->House->IsPlayerControl) { + if (tech->IsDiscoveredByPlayer && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + object->Look(); + } + } else { + + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr) && + Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + + tech->Look(); + } + } + } else { + + if (tech->House->IsHuman) { + + if (tech->House == house) { + if (tech->Is_Discovered_By_Player(house) && Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + object->Look(); + } + } else { + if (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(house) && + Distance(tech->Center_Coord(), center) <= (tech->Techno_Type_Class()->SightRange * CELL_LEPTON_W) + distance) { + tech->Look(); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * DisplayClass::Flag_Cell -- Flag the specified cell to be redrawn. * + * * + * This will flag the cell to be redrawn. * + * * + * INPUT: cell -- The cell to be flagged. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/20/1996 JLB : Created. * + *=============================================================================================*/ +void DisplayClass::Flag_Cell(CELL cell) +{ + Flag_To_Redraw(false); + IsToRedraw = true; + CellRedraw[cell] = true; +} + +static ActionType _priority_actions[] = { + ACTION_ATTACK, + ACTION_ENTER, + ACTION_HEAL, + ACTION_REPAIR, + ACTION_SABOTAGE, + ACTION_CAPTURE, + ACTION_MOVE +}; + +static int get_action_priority(ActionType action) +{ + for (int i = 0; i < ARRAY_LENGTH(_priority_actions); ++i) { + if (_priority_actions[i] == action) { + return i; + } + } + return INT_MAX; +} + +template +static int index_of(const DynamicVectorClass& list, T* object) +{ + for (int i = 0; i < list.Count(); i++) { + if (list[i] == object) { + return i; + } + } + return -1; +} + +template +static ObjectClass* Best_Object_With_ActionT(DynamicVectorClass& objects, T subject) +{ + DynamicVectorClass checked_types; + + if (objects.Count()) { + int best_priority = INT_MAX; + ObjectClass* best_object = objects[0]; + for (int i = 0; i < objects.Count(); ++i) { + ObjectClass* object = objects[i]; + const ObjectTypeClass* type = &object->Class_Of(); + if (index_of(checked_types, type) != -1) { + continue; + } + checked_types.Add(type); + ActionType action = object->What_Action(subject); + int priority = get_action_priority(action); + if (priority < best_priority) { + best_priority = priority; + best_object = object; + if (best_priority == 0) { + break; + } + } + } + return best_object; + } + return NULL; +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, const ObjectClass* object) +{ + return Best_Object_With_ActionT(objects, object); +} + +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell) +{ + return Best_Object_With_ActionT(objects, cell); +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, const ObjectClass* object) +{ + ObjectClass* obj = Best_Object_With_Action(objects, object); + return (obj != NULL) ? obj->What_Action(object) : ACTION_NONE; +} + +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell) +{ + ObjectClass* obj = Best_Object_With_Action(objects, cell); + return (obj != NULL) ? obj->What_Action(cell) : ACTION_NONE; +} + +ObjectClass* Best_Object_With_Action(const ObjectClass* object) +{ + return Best_Object_With_Action(CurrentObject.Raw(), object); +} + +ObjectClass* Best_Object_With_Action(CELL cell) +{ + return Best_Object_With_Action(CurrentObject.Raw(), cell); +} + +ActionType Best_Object_Action(const ObjectClass* object) +{ + return Best_Object_Action(CurrentObject.Raw(), object); +} + +ActionType Best_Object_Action(CELL cell) +{ + return Best_Object_Action(CurrentObject.Raw(), cell); +} \ No newline at end of file diff --git a/code/redalert/display.h b/code/redalert/display.h new file mode 100644 index 0000000..ddd2cb0 --- /dev/null +++ b/code/redalert/display.h @@ -0,0 +1,342 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DISPLAY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DISPLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 1, 1994 * + * * + * Last Update : May 1, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "map.h" +#include "layer.h" + +struct Image_t; + +#define ICON_PIXEL_W 24 +#define ICON_PIXEL_H 24 +#define ICON_LEPTON_W 256 +#define ICON_LEPTON_H 256 +#define CELL_PIXEL_W ICON_PIXEL_W +#define CELL_PIXEL_H ICON_PIXEL_H +#define CELL_LEPTON_W ICON_LEPTON_W +#define CELL_LEPTON_H ICON_LEPTON_H + +// ----------------------------------------------------------- +#define PIXEL_LEPTON_W (ICON_LEPTON_W/ICON_PIXEL_W) +#define PIXEL_LEPTON_H (ICON_LEPTON_H/ICON_PIXEL_H) + +#define SIDE_BAR_TAC_WIDTH 10 +#define SIDE_BAR_TAC_HEIGHT 8 + +extern COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2); + +class DisplayClass: public MapClass +{ + public: + friend class DLLExportClass; // ST - 5/13/2019 + + /* + ** The tactical map display position is indicated by the cell of the + ** upper left hand corner. These should not be altered directly. Use + ** the Set_Tactical_Position function instead. + */ + COORDINATE TacticalCoord; + + /* + ** The dimensions (in cells) of the visible window onto the game map. This tactical + ** map is how the player interacts and views the game world. + */ + LEPTON TacLeptonWidth; + LEPTON TacLeptonHeight; + + /* + ** These layer control elements are used to group the displayable objects + ** so that proper overlap can be obtained. + */ + static LayerClass Layer[LAYER_COUNT]; + + /* + ** This records the position and shape of a placement cursor to display + ** over the map. This cursor is used when placing buildings and also used + ** extensively by the scenario editor. + */ + CELL ZoneCell; + short ZoneOffset; + short const *CursorSize; + short CursorShapeSave[256]; // For save/load + bool ProximityCheck; // Is proximity check ok? + + /* + ** This holds the building type that is about to be placed upon the map. + ** It is only valid during the building placement state. The PendingLegal + ** flag is updated as the cursor moves and it reflects the legality of + ** placing the building at the desired location. + */ + ObjectClass * PendingObjectPtr; + ObjectTypeClass const * PendingObject; + HousesType PendingHouse; + + static unsigned char FadingBrighten[256]; + static unsigned char FadingShade[256]; + static unsigned char FadingWayDark[256]; + static unsigned char FadingLight[256]; + static unsigned char FadingGreen[256]; + static unsigned char FadingYellow[256]; + static unsigned char FadingRed[256]; + static unsigned char TranslucentTable[(MAGIC_COL_COUNT+1)*256]; + static unsigned char WhiteTranslucentTable[(1+1)*256]; + static unsigned char MouseTranslucentTable[(4+1)*256]; + static void const *TransIconset; +// jmarshall + static Image_t* TransIconsetHD; +// jmarshall end + static unsigned char UnitShadow[(USHADOW_COL_COUNT+1)*256]; + static unsigned char UnitShadowAir[(USHADOW_COL_COUNT+1)*256]; + static unsigned char SpecialGhost[2*256]; + + //------------------------------------------------------------------------- + DisplayClass(void); + DisplayClass(NoInitClass const & x) : MapClass(x) {}; + + virtual void Read_INI(CCINIClass & ini); + void Write_INI(CCINIClass & ini); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** General display/map/interface support functionality. + */ + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + + /* + ** Added functionality. + */ + void All_To_Look(HouseClass *house, bool units_only=false); // Added house parameter so it can work for multiple players. ST - 8/6/2019 2:30PM + void Constrained_Look(COORDINATE coord, LEPTON distance, HouseClass *house); // Added house parameter for client/server multiplayer. ST - 8/12/2019 3:25PM + void Shroud_Cell(CELL cell, HouseClass *house); + void Encroach_Shadow(HouseClass *house); + COORDINATE Center_Map(COORDINATE center=0L); + virtual bool Map_Cell(CELL cell, HouseClass *house, bool check_radar_spied = true, bool and_for_allies = true); // Added check_radar_spied parameter to prevent recursion. ST - 8/6/2019 10:16AM. Added and_for_allies ST - 10/31/2019 1:18PM + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void Help_Text(int , int =-1, int =-1, int =YELLOW, bool =false) {}; + virtual MouseType Get_Mouse_Shape(void) const = 0; + virtual bool Scroll_Map(DirType facing, int & distance, bool really); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_View_Dimensions(int x, int y, int width=-1, int height=-1); + + /* + ** Pending object placement control. + */ + virtual void Put_Place_Back(TechnoClass * ) {}; // Affects 'pending' system. + void Cursor_Mark(CELL pos, bool on); + void Set_Cursor_Shape(short const * list); + CELL Set_Cursor_Pos(CELL pos = -1); + void Get_Occupy_Dimensions(int & w, int & h, short const *list) const; + + /* + ** Tactical map only functionality. + */ + virtual void Set_Tactical_Position(COORDINATE coord); + void Refresh_Band(void); + void Select_These(COORDINATE coord1, COORDINATE coord2, bool additive = false); + COORDINATE Pixel_To_Coord(int x, int y) const; + bool Coord_To_Pixel(COORDINATE coord, int & x, int & y) const; + bool Push_Onto_TacMap(COORDINATE &source, COORDINATE &dest); + void Remove(ObjectClass const * object, LayerType layer); + void Submit(ObjectClass const * object, LayerType layer); + CELL Calculated_Cell(SourceType dir, WAYPOINT waypoint=-1, CELL cell=-1, SpeedType loco=SPEED_FOOT, bool zonecheck=true, MZoneType mzone=MZONE_NORMAL) const; + bool In_View(register CELL cell) const; + bool Passes_Proximity_Check(ObjectTypeClass const * object, HousesType house, short const * list, CELL trycell) const; + ObjectClass * Cell_Object(CELL cell, int x=0, int y=0) const; + ObjectClass * Next_Object(ObjectClass * object) const; + ObjectClass * Prev_Object(ObjectClass * object) const; + int Cell_Shadow(CELL cell, HouseClass *house) const; + short const * Text_Overlap_List(char const * text, int x, int y) const; + bool Is_Spot_Free(COORDINATE coord) const; + COORDINATE Closest_Free_Spot(COORDINATE coord, bool any=false) const; + void Sell_Mode_Control(int control); + void Repair_Mode_Control(int control); + + virtual void Flag_Cell(CELL cell); +// jmarshall + bool Is_Cell_Flagged(CELL cell) const { return true; } +// jmarshall end + /* + ** Computes starting position based on player's units' Coords. + */ + void Compute_Start_Pos(long& x, long& y); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + virtual void Mouse_Right_Press(void); + virtual void Mouse_Left_Press(int x, int y); + virtual void Mouse_Left_Up(CELL cell, bool shadow, ObjectClass * object, ActionType action, bool wsmall = false); + virtual void Mouse_Left_Held(int x, int y); + virtual void Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * object, ActionType action, bool wsmall = false); + + public: + /* + ** This is the pixel offset for the upper left corner of the tactical map. + */ + int TacPixelX; + int TacPixelY; + + /* + ** This is the coordinate that the tactical map should be in at next available opportunity. + */ + COORDINATE DesiredTacticalCoord; + + /* + ** If something in the tactical map is to be redrawn, this flag is set to true. + */ + unsigned IsToRedraw:1; + + /* + ** If the player is currently wielding a wrench (to select buildings for repair), + ** then this flag is true. In such a state, normal movement and combat orders + ** are preempted. + */ + unsigned IsRepairMode:1; + + /* + ** If the player is currently in "sell back" mode, then this flag will be + ** true. While in this mode, anything clicked on will be sold back to the + ** "factory". + */ + unsigned IsSellMode:1; + + /* + ** If the player is currently in ion cannon targeting mode, then this + ** flag will be true. While in this mode, anything clicked on will be + ** be destroyed by the ION cannon. + */ + SpecialWeaponType IsTargettingMode; + + protected: + + /* + ** If it is currently in rubber band mode (multi unit selection), then this + ** flag will be true. While in such a mode, normal input is preempted while + ** the extended selection is in progress. + */ + unsigned IsRubberBand:1; + + /* + ** The moment the mouse is held down, this flag gets set. If the mouse is dragged + ** a sufficient distance while held down, then true rubber band mode selection + ** can begin. Using a minimum distance prevents accidental rubber band selection + ** mode from being initiated. + */ + unsigned IsTentative:1; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class TacticalClass : public GadgetClass { + public: + TacticalClass(void) : GadgetClass(0,0,0,0,KEYBOARD|LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + int Selection_At_Mouse(unsigned flags, KeyNumType & key); + int Command_Object(unsigned flags, KeyNumType & key); + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + friend class TacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ +public: //ST - 1/21/2019 11:59AM + static TacticalClass TacButton; + + private: + + /* + ** This is a utility flag that is set during the icon draw process only if there + ** was at least one shadow icon detected that should be redrawn. When the shadow + ** drawing logic is to take place, but this flag is false, then the shadow drawing + ** will be skipped since it would perform no function. + */ + unsigned IsShadowPresent:1; + + /* + ** Rubber band mode consists of stretching a box from the anchor point (specified + ** here) to the current cursor position. + */ + int BandX,BandY; + int NewX,NewY; + + static void const *ShadowShapes; + static unsigned char ShadowTrans[(SHADOW_COL_COUNT+1)*256]; + + void Redraw_Icons(void); + void Redraw_OIcons(void); + void Redraw_Shadow(void); + + /* + ** This bit array is used to flag cells to be redrawn. If the icon needs to + ** be redrawn for a cell, then the corresponding flag will be true. + */ + static BooleanVectorClass CellRedraw; + + bool Good_Reinforcement_Cell(CELL outcell, CELL incell, SpeedType loco, int zone, MZoneType mzone) const; + + // + // We need a way to bypass visible view checks when we are running in the context of GlyphX without using the + // internal C&C renderer. We shouldn't know or care what the user is actually looking at + // ST - 4/17/2019 9:01AM + // + static bool IgnoreViewConstraints; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; + +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/dllinterface.cpp b/code/redalert/dllinterface.cpp new file mode 100644 index 0000000..48daa36 --- /dev/null +++ b/code/redalert/dllinterface.cpp @@ -0,0 +1,8470 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** DLLInterfac.cpp +** +** This is where we implement the API expected by the Instance Server. +** +** The Instance Server will pass in requests for loading and starting maps, control input from players, +** and requests for game simulation and rendering states. +** +** +*/ + +// Exception handling isn't enabled +#pragma warning (disable : 4530) //warning C4530: C++ exception handler used, but unwind semantics are not enabled. + +#include +#include +#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" + +#include + + + +/* +** Externs +*/ +//extern int DLL_Startup(const char * command_line); +extern void Reallocate_Big_Shape_Buffer(void); +extern bool ProgEndCalled; +extern int Write_PCX_File(char* name, GraphicViewPortClass& pic, unsigned char* palette ); +extern void Color_Cycle(void); + +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum); +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags = 0, void const * ghostdata = NULL); + +typedef void (__cdecl* CNC_Event_Callback_Type)(const EventCallbackStruct &event); +typedef unsigned __int64 uint64; +typedef __int64 int64; + + + + +/* +** Audio defines +** +** +** +** +** +*/ +// For compatibility with Watcom in audio enums +#pragma warning (disable : 4091) + +// From RedAlert\Audio.cpp +enum ContextType; +extern struct SoundEffectNameStruct { + char const *Name; // Digitized voice file name. + int Priority; // Playback priority of this sample. + ContextType Where; // In what game context does this sample exist. +} SoundEffectName[VOC_COUNT]; + +// From RedAlert\Audio.cpp +extern char const* Speech[VOX_COUNT]; + +// From RedAlert\Audio.cpp +typedef enum { + IN_NOVAR, // No variation or alterations allowed. + IN_VAR // Infantry variance response modification. +}; + + + + +/* +** Misc defines +** +** +** +** +** +*/ +#define GAME_TO_PLAY Session.Type +#define MULTIPLAYER_COUNT Session.Players.Count() +#define KEYBOARD Keyboard + +#define RANDOM_START_POSITION 0x7f + + + + +/* +** DLL Interface +** +** +** +** +** +*/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in); +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback); +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules); +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]); +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); +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); +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); +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); +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); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode); +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); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id); +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); +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); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index); +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); +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); +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id); +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type); +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty); +extern "C" __declspec(dllexport) void __cdecl CNC_Restore_Carryover_Objects(const CarryoverObjectStruct* objects); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 player_id); +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time); + + + + +/* +** Class to implement the interface, and contain additional game state required by the conversion from peer/peer to client/server +** +** +** +** +** +*/ +class DLLExportClass { + public: + + static void Init(void); + static void Shutdown(void); + static void Config(const CNCRulesDataStruct& rules); + static void Add_Mod_Path(const char *mod_path); + static void Set_Home_Cell(int x, int y, uint64 player_id); + static void Set_Content_Directory(const char *dir); + + static bool Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Start_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Hold_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id); + static bool Start_Placement(uint64 player_id, int buildable_type, int buildable_id); + static BuildingClass *Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id); + static bool Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static void Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out); + 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); + static void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); + static bool Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); + static bool Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id); + static bool Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y); + static bool Create_Control_Group(unsigned char control_group_index); + static bool Add_To_Control_Group(unsigned char control_group_index); + static bool Toggle_Control_Group_Selection(unsigned char control_group_index); + static bool Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id); + static bool Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance); + static void Calculate_Start_Positions(void); + static void Computer_Message(bool last_player_taunt); + + static void Repair_Mode(uint64 player_id); + static void Repair(uint64 player_id, int object_id); + static void Sell_Mode(uint64 player_id); + static void Sell(uint64 player_id, int object_id); + static void Repair_Sell_Cancel(uint64 player_id); + + static void Scatter_Selected(uint64 player_id); + static void Select_Next_Unit(uint64 player_id); + static void Select_Previous_Unit(uint64 player_id); + static void Selected_Guard_Mode(uint64 player_id); + static void Selected_Stop(uint64 player_id); + static void Team_Units_Formation_Toggle_On(uint64 player_id); + static void Units_Queued_Movement_Toggle(uint64 player_id, bool toggle); + + static void Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output); + static bool Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + static bool Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size); + + + static void Set_Event_Callback(CNC_Event_Callback_Type event_callback) {EventCallback = event_callback;} + static void Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy = false); + static void Debug_Spawn_All(int x, int y); + static bool Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y); + static void Debug_Kill_Unit(int x, int y); + static void Debug_Heal_Unit(int x, int y); + + static void On_Play_Movie(const char * movie_name, ThemeType theme, bool immediate); + static void On_Display_Briefing_Text(); + + static void On_Sound_Effect(const HouseClass* player_ptr, int sound_effect_index, const char* extension, int variation, COORDINATE coord); + static void On_Speech(const HouseClass* player_ptr, int speech_index); + static void On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id); + static void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name); + static void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + static void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + + static void On_Game_Over(uint64 glyphx_player_id, bool player_wins); + static void On_Multiplayer_Game_Over(void); + + static void On_Debug_Output(const char *debug_text); + + static void On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); + + static void On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y); + + static void Glyphx_Queue_AI(); + + static void Store_Carryover_Objects(); + + static void Force_Human_Team_Wins(uint64 quitting_player_id); + + /* + ** Player context switching for input/output + */ + static bool Set_Player_Context(uint64 glyphx_player, bool force = false); + static void Reset_Player_Context(void); + static void Adjust_Internal_View(bool force_ignore_view_constraints = false); + static void Logic_Switch_Player_Context(ObjectClass *object); + static void Logic_Switch_Player_Context(HouseClass *house); + static void Refresh_Player_Control_Flags(void); + static __int64 Get_GlyphX_Player_ID(const HouseClass *house); + + static void Recalculate_Placement_Distances(); + + static void Reset_Sidebars(void); + + static SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr = NULL); + + static uint64 GlyphxPlayerIDs[MAX_PLAYERS]; + + + + static const void *Get_Shadow_Shapes(void) {return Map.ShadowShapes;} + static const unsigned char *Get_Shadow_Trans(void) {return &Map.ShadowTrans[0];} + + static bool Legacy_Render_Enabled(void); + + static bool Get_Input_Key_State(KeyNumType key); + + static void Set_Special_Key_Flags(unsigned char special_key_flags); + static void Clear_Special_Key_Flags(); + + static bool Load(Straw &file); + static bool Save(Pipe &file); + static void Code_Pointers(void); + static void Decode_Pointers(void); + + static bool Get_Game_Over() { return GameOver; } + + private: + static void Calculate_Single_Player_Score(EventCallbackStruct&); + + static int RA_Calculate_Leadership( HousesType player_house, int units_lost, int buildings_lost ); + static int RA_Calculate_Economy( long available_money, int stolen_buildings_credits, unsigned harvested_credits, long initial_credits ); + static int RA_Calculate_Score( int uspoints, int leadership, int economy ); + + static void Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type); + static void Convert_Special_Weapon_Type(SpecialWeaponType weapon_type, DllSuperweaponTypeEnum& dll_weapon_type, char* weapon_name); + static void Fill_Sidebar_Entry_From_Special_Weapon(CNCSidebarEntryStruct& sidebar_entry_out, SuperClass*& super_weapon_out, SpecialWeaponType weapon_type); + + static void Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance); + + static int CurrentDrawCount; + static int TotalObjectCount; + static int SortOrder; + static CNCObjectListStruct *ObjectList; + + static CNC_Event_Callback_Type EventCallback; + + + static int CurrentLocalPlayerIndex; + + static bool GameOver; + + static std::set MessagesSent; + + /* + ** Pseudo sidebars for players in multiplayer + */ + static SidebarGlyphxClass MultiplayerSidebars[MAX_PLAYERS]; + + static CELL MultiplayerStartPositions[MAX_PLAYERS]; + + static BuildingTypeClass *PlacementType[MAX_PLAYERS]; + + static unsigned char PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; + + static unsigned char SpecialKeyFlags[MAX_PLAYERS]; + + /* + ** Mod directories + */ + static DynamicVectorClass ModSearchPaths; + +}; + + + +/* +** DLLExportClass static data +** +** +** +** +** +*/ +int DLLExportClass::CurrentDrawCount = 0; +int DLLExportClass::TotalObjectCount = 0; +int DLLExportClass::SortOrder = 0; +CNCObjectListStruct *DLLExportClass::ObjectList = NULL; +SidebarGlyphxClass DLLExportClass::MultiplayerSidebars [MAX_PLAYERS]; +uint64 DLLExportClass::GlyphxPlayerIDs[MAX_PLAYERS] = {0xffffffffl}; +int DLLExportClass::CurrentLocalPlayerIndex = -1; +CELL DLLExportClass::MultiplayerStartPositions[MAX_PLAYERS]; +BuildingTypeClass *DLLExportClass::PlacementType[MAX_PLAYERS]; +unsigned char DLLExportClass::PlacementDistance[MAX_PLAYERS][MAP_CELL_TOTAL]; +unsigned char DLLExportClass::SpecialKeyFlags[MAX_PLAYERS] = { 0U }; +DynamicVectorClass DLLExportClass::ModSearchPaths; +std::set DLLExportClass::MessagesSent; +bool DLLExportClass::GameOver = false; + + + +/* +** Global variables +** +** +** +** +** +*/ +int DLLForceMouseX = 0; +int DLLForceMouseY = 0; + +CNC_Event_Callback_Type DLLExportClass::EventCallback = NULL; + +// Needed to accomodate Glyphx client sidebar. ST - 4/12/2019 5:29PM +int GlyphXClientSidebarWidthInLeptons = 0; + +bool MPlayerIsHuman[MAX_PLAYERS]; +int MPlayerTeamIDs[MAX_PLAYERS]; +int MPlayerStartLocations[MAX_PLAYERS]; + +bool MPSuperWeaponDisable = false; +bool ShareAllyVisibility = true; +bool UseGlyphXStartLocations = true; + + +int GetRandSeed() +{ + using namespace std::chrono; + time_point time_since_epoch = system_clock::now(); + auto microseconds_since_epoch = floor(time_since_epoch); + + return abs( static_cast(microseconds_since_epoch.time_since_epoch().count()) ); +} + + + +void Play_Movie_GlyphX(const char * movie_name, ThemeType theme, bool immediate = false) +{ + if ((movie_name[0] == 'x' || movie_name[0] == 'X') && movie_name[1] == 0) { + return; + } + + DLLExportClass::On_Play_Movie(movie_name, theme, immediate); +} + +void Display_Briefing_Text_GlyphX() +{ + DLLExportClass::On_Display_Briefing_Text(); +} + +void On_Message(const char* message, float timeout_seconds, int64 id) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_DIRECT, id); +} + +void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name) +{ + DLLExportClass::On_Update_Map_Cell(cell_x, cell_y, template_type_name); +} + +void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type) +{ + DLLExportClass::On_Special_Weapon_Targetting(player_ptr, weapon_type); +} + +void On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + DLLExportClass::On_Ping(player_ptr, coord); +} + +void On_Defeated_Message(const char* message, float timeout_seconds) +{ + DLLExportClass::On_Message(PlayerPtr, message, timeout_seconds, MESSAGE_TYPE_PLAYER_DEFEATED, -1); +} + +void GlyphX_Debug_Print(const char *debug_text) +{ + DLLExportClass::On_Debug_Output(debug_text); +} + +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + DLLExportClass::On_Achievement(player_ptr, achievement_type, achievement_reason); +} + + + + + +/************************************************************************************************** +* CNC_Version -- Check DLL/Server version +* +* In: Version expected +* +* Out: Actual version +* +* +* +* History: 4/9/2020 2:12PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) unsigned int __cdecl CNC_Version(unsigned int version_in) +{ + // Unreferenced, but potentially useful to know which version the server is expecting + version_in; + + return CNC_DLL_API_VERSION; +} + + + +/************************************************************************************************** +* CNC_Init -- Initialize the .DLL +* +* In: Command line +* +* Out: +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line, CNC_Event_Callback_Type event_callback) +{ + //DLLExportClass::Set_Content_Directory(NULL); + // + //DLL_Startup(command_line); + // + //// MBL + //DLLExportClass::Set_Event_Callback( event_callback ); + // + //DLLExportClass::Init(); +} + + + +/************************************************************************************************** +* DLL_Shutdown -- Shutdown the .DLL +* +* In: +* +* Out: +* +* +* +* History: 2/20/2020 1:58PM - ST +**************************************************************************************************/ +void DLL_Shutdown(void) +{ + DLLExportClass::Shutdown(); +} + + + + + +/************************************************************************************************** +* CNC_Config -- Configure the plugin +* +* In: Configuration data +* +* Out: +* +* +* +* History: 10/03/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Config(const CNCRulesDataStruct& rules) +{ + DLLExportClass::Config(rules); +} + + + + +/************************************************************************************************** +* CNC_Add_Mod_Path -- Add a path to load mod files from +* +* In: Path to load mods from +* +* Out: +* +* +* +* History: 2/20/2020 2:04PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Add_Mod_Path(const char *mod_path) +{ + DLLExportClass::Add_Mod_Path(mod_path); +} + + + + + +/************************************************************************************************** +* CNC_Get_Visible_Page -- Get the screen buffer 'SeenBuff' from the game +* +* In: If buffer_in is null, just return info about page +* +* Out: false if not changed since last call +* +* +* +* History: 1/3/2019 11:33AM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Visible_Page(unsigned char *buffer_in, unsigned int &width, unsigned int &height) +{ + if (!DLLExportClass::Legacy_Render_Enabled() || (buffer_in == NULL)) { + return false; + } + + /* + ** Assume the seen page viewport is the same size as the page + */ + + GraphicBufferClass *gbuffer = HidPage.Get_Graphic_Buffer(); + if (gbuffer == NULL) { + return false; + } + + int view_port_width = Map.MapCellWidth * CELL_PIXEL_W; + int view_port_height = Map.MapCellHeight * CELL_PIXEL_H; + + if (view_port_width == 0 || view_port_height == 0) { + return false; + } + + unsigned char *raw_buffer = (unsigned char*) gbuffer->Get_Buffer(); + long raw_size = gbuffer->Get_Size(); + if (raw_buffer == NULL || gbuffer->Get_Width() < view_port_width || gbuffer->Get_Height() < view_port_height) { + return false; + } + + width = view_port_width; + height = view_port_height; + + int pitch = gbuffer->Get_Width(); + for (int i = 0; i < view_port_height; ++i, buffer_in += view_port_width, raw_buffer += pitch) { + memcpy(buffer_in, raw_buffer, view_port_width); + } + + return true; +} + + + + +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Palette(unsigned char(&palette_in)[256][3]) +{ + memcpy(palette_in, CurrentPalette, sizeof(palette_in)); + return true; +} + + + + +/************************************************************************************************** +* CNC_Set_Multiplayer_Data -- Set up for a multiplayer match +* +* In: Multiplayer data +* +* Out: false if data is bad +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +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) +{ + + if (num_players <= 0) { + return false; + } + + if (num_players > min(MAX_PLAYERS, max_players)) { + return false; + } + + DLLExportClass::Init(); + + Session.Options.Bases = game_options.MPlayerBases; // 1 = bases are on for this scenario + Session.Options.Credits = game_options.MPlayerCredits; // # credits everyone gets + Session.Options.Tiberium = game_options.MPlayerTiberium; // 1 = tiberium enabled for this scenario + Session.Options.Goodies = game_options.MPlayerGoodies; // 1 = goodies enabled for this scenario + Session.Options.Ghosts = game_options.MPlayerGhosts; // 1 = houses with no players will still play + //MPlayerSolo = game_options.MPlayerSolo; // 1 = allows a single-player net game + Session.Options.UnitCount = game_options.MPlayerUnitCount; // # units for non-base multiplayer scenarios + + Special.IsShadowGrow = game_options.MPlayerShadowRegrow; + Special.IsCaptureTheFlag = game_options.CaptureTheFlag; + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + Session.Options.ScenarioIndex = scenario_index; + + Special.IsMCVDeploy = game_options.IsMCVDeploy; + Special.UseMCVDeploy = true; + + MPSuperWeaponDisable = !game_options.EnableSuperweapons; // Are superweapons available + + //Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + + Special.IsEarlyWin = game_options.DestroyStructures; + + /* + ** Enable Counterstrike/Aftermath units + */ + OverrideNewUnitsEnabled = game_options.MPlayerAftermathUnits; + + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + for (int i=0 ; iName, player_info.Name, MPLAYER_NAME_MAX); + who->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated + who->Player.House = (HousesType)player_info.House; + who->Player.Color = (PlayerColorType) player_info.ColorIndex; + Session.Players.Add (who); + + /* + ** Player IDs are done differently in Red Alert vs. TD. + ** In TD, the ID is created from the house/color combination + ** In RA, the ID is HOUSE_MULTI1 + the index into the Session.Players vector + */ + DLLExportClass::GlyphxPlayerIDs[i] = player_info.GlyphxPlayerID; + + MPlayerIsHuman[i] = !player_info.IsAI; + MPlayerTeamIDs[i] = player_info.Team; + MPlayerStartLocations[i] = player_info.StartLocationIndex; + + /* + ** Temp fix for custom maps that don't have valid start positions set from matchmaking + */ + if (i > 0 && MPlayerStartLocations[i] == 0 && MPlayerStartLocations[0] == 0) { + MPlayerStartLocations[i] = i; + } + } + + /* + ** Force smart defense always on for multiplayer/skirmish + */ + Rule.IsSmartDefense = true; + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + Unselect_All(); + + return true; +} + +extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + switch (object_type_id) + { + case INFANTRY: + { + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Infantry.ID((InfantryClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case UNIT: + { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Units.ID((UnitClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case AIRCRAFT: + { + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * obj = Aircraft.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Aircraft.ID((AircraftClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case BUILDING: + { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * obj = Buildings.Ptr(index); + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Buildings.ID((BuildingClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + case VESSEL: + { + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + + if (obj + && !obj->IsInLimbo + && obj->House->IsPlayerControl + && Vessels.ID((VesselClass*)obj) == object_to_select_id) + { + if (!obj->Is_Selected_By_Player()) + { + obj->Select(); + AllowVoice = false; + } + return true; + } + } + } + break; + } + + return false; +} + + +/************************************************************************************************** +* GlyphX_Assign_Houses -- Replacement for Assign_Houses in INI.CPP / SCNEARIO.CPP +* +* In: +* +* Out: +* +* +* +* History: 8/8/2019 12:37PM - ST +**************************************************************************************************/ +void GlyphX_Assign_Houses(void) +{ + /* + ** RA version + */ + extern int _build_tech[11]; + + int assigned[MAX_PLAYERS]; + bool preassigned; + int i,j,random_start_location; + HousesType house, house2; + HouseClass * housep, *housep2; + int lowest_color; + int index; + + srand(timeGetTime()); + + /* + ** Assign random start positions if needed. + */ + int random_start_locations[26]; + int num_start_locations = 0; + int num_random_start_locations = 0; + for (i = 0; i < 26; i++) { + if (Scen.Waypoint[i] != -1) { + preassigned = false; + for (j = 0; !preassigned && (j < Session.Players.Count()); j++) { + if (MPlayerStartLocations[j] == num_start_locations) { + preassigned = true; + } + } + if (!preassigned) { + random_start_locations[num_random_start_locations] = num_start_locations; + num_random_start_locations++; + } + num_start_locations++; + } + } + + if (num_random_start_locations > 1) { + for (i = 0; i < num_random_start_locations - 1; i++) { + j = i + rand() / (RAND_MAX / (num_random_start_locations - i) + 1); + int t = random_start_locations[j]; + random_start_locations[j] = random_start_locations[i]; + random_start_locations[i] = t; + } + } + + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + for (i = 0; i < MAX_PLAYERS; i++) { + assigned[i] = 0; + } + + random_start_location = 0; + +// debugprint( "Assign_Houses()\n" ); + //------------------------------------------------------------------------ + // Assign each player in 'Players' to a multiplayer house. Players will + // be sorted by their chosen color value (this value must be unique among + // all the players). + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + + //..................................................................... + // Find the player with the lowest color index + //..................................................................... + index = 0; + lowest_color = 255; + for (j = 0; j < Session.Players.Count(); j++) { + //.................................................................. + // If we've already assigned this house, skip it. + //.................................................................. + if (assigned[j]) { + continue; + } + if (Session.Players[j]->Player.Color < lowest_color) { + lowest_color = Session.Players[j]->Player.Color; + index = j; + } + } + + //..................................................................... + // Mark this player as having been assigned. + //..................................................................... + assigned[index] = 1; + + //..................................................................... + // Assign the lowest-color'd player to the next available slot in the + // HouseClass array. + //..................................................................... + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#ifdef WOLAPI_INTEGRATION + // Make another copy of name, permanent throughout entire game. + strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#endif + housep->IsHuman = MPlayerIsHuman[index]; + housep->IsPlayerControl = housep->IsHuman; + + if (!housep->IsHuman) { + housep->IsStarted = true; + strncpy(housep->IniName, Text_String(TXT_COMPUTER), HOUSE_NAME_MAX); + housep->IQ = Rule.MaxIQ; + //housep->Control.TechLevel = _build_tech[BuildLevel]; + } + + + housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), + Session.Players[index]->Player.House, Session.Options.Credits); + + /* + ** Set the start location override + */ + if (MPlayerStartLocations[index] != RANDOM_START_POSITION) { + housep->StartLocationOverride = MPlayerStartLocations[index]; + } else { + if (random_start_location < num_random_start_locations) { + housep->StartLocationOverride = random_start_locations[random_start_location++]; + } else { + housep->StartLocationOverride = -1; + } + } + + if (index == 0) { + PlayerPtr = housep; + } + /* + ** Convert the build level into an actual tech level to assign to the house. + ** There isn't a one-to-one correspondence. + */ + housep->Control.TechLevel = _build_tech[BuildLevel]; + + housep->Assign_Handicap(Scen.Difficulty); + + //..................................................................... + // Record where we placed this player + //..................................................................... + Session.Players[index]->Player.ID = house; + +// debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); + } + + for (i = Session.Players.Count(); i < Rule.MaxPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (housep != NULL) { + housep->IsDefeated = true; + } + } + + for (i = 0; i < Session.Players.Count(); i++) { + + house = Session.Players[i]->Player.ID; + housep = HouseClass::As_Pointer(house); + + if (housep) { + + int team = MPlayerTeamIDs[i]; + + for (int j=0 ; j < Session.Players.Count(); j++) { + + if (i != j) { + + if (team == MPlayerTeamIDs[j]) { + + house2 = Session.Players[j]->Player.ID; + housep2 = HouseClass::As_Pointer(house2); + + if (housep2) { + housep->Make_Ally(house2); + } + } + } + } + } + } + +} + + +/************************************************************************************************** +* CNC_Set_Home_Cell -- Allows overriding the start position for the camera +* +* +* History: 2/14/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Home_Cell(int x, int y, uint64 player_id) +{ + DLLExportClass::Set_Home_Cell(x, y, player_id); +} + + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map -> WITHOUT SPECIFYING A SCENARIO VARIATION (SCEN_VAR) +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* +* History: 7/10/2019 - LLL +**************************************************************************************************/ +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) +{ + 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); +} + + +/************************************************************************************************** +* CNC_Start_Instance -- Load and start a cnc map +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* Renamed and modified to accept a scenario variation 7/10/2019 - LLL +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +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) +{ + if (game_type == NULL) { + return false; + } + + if (faction == NULL) { + return false; + } + + if (content_directory == NULL) { + return false; + } + + ScenarioPlayerType scen_player = SCEN_PLAYER_NONE; + + if (stricmp(faction, "SPAIN") == 0) { + scen_player = SCEN_PLAYER_SPAIN; + Whom = HOUSE_GOOD; + } + + if (stricmp(faction, "GREECE") == 0 || stricmp(faction, "ALLY") == 0) { + scen_player = SCEN_PLAYER_GREECE; + Whom = HOUSE_GOOD; + } + + if (stricmp(faction, "USSR") == 0) { + scen_player = SCEN_PLAYER_USSR; + Whom = HOUSE_BAD; + } + + + + DLLExportClass::Set_Content_Directory(content_directory); + + BuildLevel = build_level; + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GAME_TO_PLAY = GAME_NORMAL; + if (scen_player == SCEN_PLAYER_NONE) { + return false; + } + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + scen_player = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + Force_CD_Available(ALWAYS_RELOAD_CD); + + if (override_map_name && strlen(override_map_name)) { + strcpy(Scen.ScenarioName, override_map_name); + } else { + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + } + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + KEYBOARD->MouseQX = 100; + KEYBOARD->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + Seed = GetRandSeed(); + Scen.RandomNumber = Seed; + + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + + if (GAME_TO_PLAY == GAME_NORMAL) { + MPSuperWeaponDisable = false; + } else { + if (MPSuperWeaponDisable) { + /* + ** Write over the tecb level settings we just loaded from the Rules ini + */ + Rule.GPSTechLevel = 100; + Rule.ParaInfantryTechLevel = 100; + Rule.SpyPlaneTechLevel = 100; + Rule.ParaBombTechLevel = 100; + Rule.ChronoTechLevel = 100; + } + } + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } + + Map.Flag_To_Redraw(true); + + Set_Palette(GamePalette.Get_Data()); + Map.Render(); + + Set_Palette(GamePalette.Get_Data()); + + return true; +} + + +/************************************************************************************************** +* CNC_Read_INI -- Load an ini file into the supplied buffer +* +* In: Map initialization parameters +* +* Out: false if ini load failed +* +* +* History: 12/16/2019 11:44AM - ST +**************************************************************************************************/ +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) +{ + if (content_directory == NULL) { + return false; + } + + DLLExportClass::Set_Content_Directory(content_directory); + + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + ScenarioPlayerType scen_player = SCEN_PLAYER_MPLAYER; + + Force_CD_Available(ALWAYS_RELOAD_CD); + + if (override_map_name && strlen(override_map_name)) { + strcpy(Scen.ScenarioName, override_map_name); + } else { + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + } + + + if (_ini_buffer_size < _ShapeBufferSize) { + GlyphX_Debug_Print("INI file buffer may be too small"); + return false; + } + + if (!ini_buffer) { + GlyphX_Debug_Print("No INI file buffer"); + return false; + } + + memset(ini_buffer, _ini_buffer_size, 0); + + CCFileClass file(Scen.ScenarioName); + if (!file.Is_Available()) { + GlyphX_Debug_Print("Failed to find scenario file"); + GlyphX_Debug_Print(Scen.ScenarioName); + return(false); + + } else { + + GlyphX_Debug_Print("Opened scenario file"); + GlyphX_Debug_Print(Scen.ScenarioName); + + int bytes_read = file.Read(ini_buffer, _ini_buffer_size-1); + if (bytes_read == _ini_buffer_size - 1) { + GlyphX_Debug_Print("INI file buffer is too small"); + return false; + } + } + + /* + ** Ini buffer should be zero terminated + */ + if ((int) strlen(ini_buffer) >= _ini_buffer_size) { + GlyphX_Debug_Print("INI file buffer overrun"); + return false; + } + + return true; +} + + +/************************************************************************************************** +* CNC_Start_Custom_Instance -- Load and start a custom map +* +* In: Map initialization parameters +* +* Out: false if map load failed +* +* +* +* History: 2019/10/28 - JAS +**************************************************************************************************/ +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) +{ + + DLLExportClass::Set_Content_Directory(content_directory); + + char fullname[_MAX_FNAME + _MAX_EXT]; + + snprintf(fullname, _MAX_FNAME + _MAX_EXT, "%s%s.mpr", directory_path, scenario_name); + + + char name_buffer[128]; + char digest_buffer[32]; + + CCFileClass file(fullname); + + INIClass ini; + ini.Load(file); + + ini.Get_String("Basic", "Name", "No Name", name_buffer, sizeof(name_buffer)); + ini.Get_String("Digest", "1", "No Digest", digest_buffer, sizeof(digest_buffer)); + Session.Scenarios.Add(new MultiMission(fullname, name_buffer, digest_buffer, ini.Get_Bool("Basic", "Official", false), false)); + + + BuildLevel = build_level; + + ScenarioPlayerType scen_player; + strncpy(Scen.ScenarioName, fullname, _MAX_FNAME + _MAX_EXT); + + if (multiplayer) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + } else { + GAME_TO_PLAY = GAME_NORMAL; + } + scen_player = SCEN_PLAYER_MPLAYER; + + //Scen.Scenario = scenario_index; + //ScenarioDirType scen_dir = (ScenarioDirType)scenario_direction; + + //if (stricmp(game_type, "GAME_NORMAL") == 0) { + // GAME_TO_PLAY = GAME_NORMAL; + //} + //else { + // if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + // GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + // scen_player = SCEN_PLAYER_MPLAYER; + // } + // else { + // return false; + // } + //} + + ///* + //** Load the scenario. Specify variation 'A' for the editor; for the game, + //** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + //** Skip this if we've already loaded a save-game. + //*/ + + //Force_CD_Available(ALWAYS_RELOAD_CD); + + //if (override_map_name && strlen(override_map_name)) { + // strcpy(Scen.ScenarioName, override_map_name); + //} + //else { + // Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)scenario_variation); + //} + + HiddenPage.Clear(); + VisiblePage.Clear(); + + /* + ** Set the mouse to some position where it's not going to scroll, or do something else wierd. + */ + DLLForceMouseX = 100; + DLLForceMouseY = 100; + KEYBOARD->MouseQX = 100; + KEYBOARD->MouseQY = 100; + + GlyphXClientSidebarWidthInLeptons = 0; + + Seed = GetRandSeed(); + Scen.RandomNumber = Seed; + + if (!Start_Scenario(Scen.ScenarioName)) { + return(false); + } + + DLLExportClass::Reset_Sidebars(); + DLLExportClass::Reset_Player_Context(); + DLLExportClass::Calculate_Start_Positions(); + + /* + ** Make sure the scroll constraints are applied. This is important for GDI 1 where the map isn't wide enough for the screen + */ + COORDINATE origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(1, 0)); + Map.Set_Tactical_Position(origin_coord); + origin_coord = Coord_Add(Map.TacticalCoord, XY_Coord(-1, 0)); + Map.Set_Tactical_Position(origin_coord); + + + if (GAME_TO_PLAY == GAME_NORMAL) { + MPSuperWeaponDisable = false; + } + else { + if (MPSuperWeaponDisable) { + /* + ** Write over the tecb level settings we just loaded from the Rules ini + */ + Rule.GPSTechLevel = 100; + Rule.ParaInfantryTechLevel = 100; + Rule.SpyPlaneTechLevel = 100; + Rule.ParaBombTechLevel = 100; + Rule.ChronoTechLevel = 100; + } + } + + /* + ** Hide the SeenBuff; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + + //Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + Set_Logic_Page(SeenBuff); + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } + Map.Flag_To_Redraw(true); + + Set_Palette(GamePalette.Get_Data()); + Map.Render(); + + Set_Palette(GamePalette.Get_Data()); + + return true; +} + + +bool Debug_Write_Shape_Type(const ObjectTypeClass *type, int shapenum) +{ + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + CCFileClass file; + + if (type->ImageData != NULL) { + + sprintf(buffer, "%s_%d", type->IniName, shapenum); + _makepath(fullname, NULL, NULL, buffer, ".PCX"); + + return Debug_Write_Shape(fullname, type->ImageData, shapenum); + } + + return false; +} + + +bool Debug_Write_Shape(const char *file_name, void const * shapefile, int shapenum, int flags, void const * ghostdata) +{ + /* + ** Build frame returns a pointer now instead of the shapes length + */ +// jmarshall - do we need this? + //char *shape_pointer = (char*) Build_Frame(shapefile , shapenum , _ShapeBuffer); + //if (shape_pointer == NULL) { + // return false;; + //} + //if (Get_Last_Frame_Length() > _ShapeBufferSize) { + // return false;; + //} + // + //int width = Get_Build_Frame_Width(shapefile); + //int height = Get_Build_Frame_Height(shapefile); + // + //GraphicBufferClass temp_gbuffer(width, height); + //GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, width, height); + // + //WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + //WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + //WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = width; + //WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = height; + // + //static const char _shape_trans = 0x40; + // + //if (flags == 0) { + // Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, SHAPE_NORMAL|SHAPE_WIN_REL|_shape_trans); //, ghostdata, predoffset); + //} else { + // Buffer_Frame_To_Page(0, 0, width, height, shape_pointer, temp_viewport, flags, ghostdata); + //} + //Write_PCX_File((char*)file_name, temp_viewport, (unsigned char*)GamePalette.Get_Data()); +// jmarshall end + return true; +} + + + +/************************************************************************************************** +* CNC_Advance_Instance -- Process one logic frame +* +* In: +* +* Out: Is game still playing? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Advance_Instance(uint64 player_id) +{ + //DLLExportClass::Set_Event_Callback(event_callback); + + if (Frame <= 10) { // Don't spam forever, but useful to know that we actually started advancing + GlyphX_Debug_Print("CNC_Advance_Instance - RA"); + } + + /* + ** 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. + ** Might make tbe bugs more repeatable and consistent. ST - 3/15/2019 11:58AM + */ + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + } else { + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0]); + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TimeQuake = PendingTimeQuake; + PendingTimeQuake = false; +#else + TimeQuake = false; +#endif + + /* + ** Allocate extra memory for uncompressed shapes as needed + */ + Reallocate_Big_Shape_Buffer(); + + /* + ** If there is no theme playing, but it looks like one is required, then start one + ** playing. This is usually the symptom of there being no transition score. + */ + //if (SampleType && Theme.What_Is_Playing() == THEME_NONE) { + // Theme.Queue_Song(THEME_PICK_ANOTHER); + //} + + /* + ** Update the display, unless we're inside a dialog. + */ + //if (SpecialDialog == SDLG_NONE && GameInFocus) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + //Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + /* + ** The main loop passes these in uninitialized. ST - 2/7/2019 4:36PM + */ + KeyNumType input = KN_NONE; // Player input. + int x = 0; + int y = 0; + Map.Input(input, x, y); + //if (input) { + // Keyboard_Process(input); + //} + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + /* + ** Process the sidebar. ST - 4/18/2019 11:59AM + */ + HouseClass *old_player_ptr = PlayerPtr; + for (int i=0 ; iPlayer.ID); + DLLExportClass::Logic_Switch_Player_Context(player_ptr); + Sidebar_Glyphx_AI(player_ptr, input); + } + DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); + } + + //} + + /* + ** Sort the map's ground layer by y-coordinate value. This is done + ** outside the IsToRedraw check, for the purposes of game sync'ing + ** between machines; this way, all machines will sort the Map's + ** layer in the same way, and any processing done that's based on + ** the order of this layer will sync on different machines. + */ + Map.Layer[LAYER_GROUND].Sort(); + + /* + ** AI logic operations are performed here. + */ + //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 + static bool FirstUpdate = GAME_TO_PLAY != GAME_GLYPHX_MULTIPLAYER;; + if (!FirstUpdate) { + HouseClass *old_player_ptr = PlayerPtr; + Logic.Clear_Recently_Created_Bits(); + Logic.AI(); + DLLExportClass::Logic_Switch_Player_Context(old_player_ptr); + } + FirstUpdate = false; + + TimeQuake = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!PendingTimeQuake) { + TimeQuakeCenter = 0; + } +#endif + + /* + ** Manage the inter-player message list. If Manage() returns true, it means + ** a message has expired & been removed, and the entire map must be updated. + */ + if (Session.Messages.Manage()) { +#ifdef WIN32 + HiddenPage.Clear(); +#else //WIN32 + HidPage.Clear(); +#endif //WIN32 + Map.Flag_To_Redraw(true); + } + + /* + ** Process all commands that are ready to be processed. + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + Queue_AI(); + } else { + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::Glyphx_Queue_AI(); + + /* + ** Process the sidebar. ST - 3/22/2019 2:07PM + */ + for (int i=0 ; iPlayer.ID); + Sidebar_Glyphx_Recalc(player_ptr); + } + } + } + + /* + ** Keep track of elapsed time in the game. + */ + //Score.ElapsedTime += TIMER_SECOND / TICKS_PER_SECOND; + + /* + ** Perform any win/lose code as indicated by the global control flags. + */ + /* + ** Check for player wins or loses according to global event flag. + */ + if (PlayerWins) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerLoses = false; + PlayerWins = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + GlyphX_Debug_Print("PlayerWins = true"); + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, true); + } + + //DLLExportClass::Set_Event_Callback(NULL); + return false; + } + if (PlayerLoses) { + + //WWMouse->Erase_Mouse(&HidPage, TRUE); + PlayerWins = false; + PlayerLoses = false; + PlayerRestarts = false; + Map.Help_Text(TXT_NONE); + GlyphX_Debug_Print("PlayerLoses = true"); + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + DLLExportClass::On_Multiplayer_Game_Over(); + } else { + DLLExportClass::On_Game_Over(player_id, false); + } + + //DLLExportClass::Set_Event_Callback(NULL); + return false; + } + + /* + ** The frame logic has been completed. Increment the frame + ** counter. + */ + Frame++; + + /* + ** Very rarely, the human players will get a message from the computer. + ** + ** This was disabled in the RA source code, so copying over the functionality from TD + */ + if (GAME_TO_PLAY != GAME_NORMAL && Session.Options.Ghosts && IRandom(0,10000) == 1) { + DLLExportClass::Computer_Message(false); + } + + if (ProgEndCalled) { + GlyphX_Debug_Print("ProgEndCalled - GameActive = false"); + GameActive = false; + } + + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + + //Sync_Delay(); + //DLLExportClass::Set_Event_Callback(NULL); + Color_Cycle(); + return(GameActive); +} + + + +/************************************************************************************************** +* CNC_Save_Load -- Process a save or load game action +* +* In: +* +* Out: Success? +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const char *file_path_and_name, const char *game_type) +{ + bool result = false; + + if (save) { + result = Save_Game(file_path_and_name, "internal"); + } else { + + if (game_type == NULL) { + return false; + } + + if (stricmp(game_type, "GAME_NORMAL") == 0) { + GAME_TO_PLAY = GAME_NORMAL; + } else { + if (stricmp(game_type, "GAME_GLYPHX_MULTIPLAYER") == 0) { + GAME_TO_PLAY = GAME_GLYPHX_MULTIPLAYER; + //ScenPlayer = SCEN_PLAYER_MPLAYER; + } else { + return false; + } + } + + while (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + result = Load_Game(file_path_and_name); + + DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true); + Set_Logic_Page(SeenBuff); + VisiblePage.Clear(); + Map.Flag_To_Redraw(true); + if (DLLExportClass::Legacy_Render_Enabled()) { + Map.Render(); + } + Set_Palette(GamePalette.Get_Data()); + } + + return result; +} + + +/************************************************************************************************** +* CNC_Set_Difficulty -- Set game difficulty +* +* In: +* +* Out: +* +* +* +* History: 10/02/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + Set_Scenario_Difficulty(difficulty); + } +} + + +/************************************************************************************************** +* CNC_Restore_Carryover_Objects +* +* History: 11/15/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Restore_Carryover_Objects(const CarryoverObjectStruct* objects) +{ + //Delete the list + while (Carryover) { + CarryoverClass* cptr = (CarryoverClass*)Carryover->Get_Next(); + delete Carryover; + Carryover = cptr; + } + + //Populate the list + const CarryoverObjectStruct* next_object = objects; + while (next_object) { + CarryoverClass* cptr = new CarryoverClass(); + + cptr->RTTI = (RTTIType)next_object->RTTI; + 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 + cptr->Cell = (CELL)next_object->Cell; + cptr->House = (HousesType)next_object->House; + cptr->Strength = next_object->Strength; + + next_object = next_object->Next; + + if (Carryover == NULL) { + Carryover = cptr; + } + else { + cptr->Add_Tail(*Carryover); + } + } +} + + + +/************************************************************************************************** +* CNC_Handle_Player_Switch_To_AI -- Renamed 3/9/2020 - LLL +* CNC_Handle_Player_Disconnect -- Handle player disconnected during multiplayer game +* +* In: +* +* Out: +* +* +* +* History: 12/3/2019 1:46PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id) +{ + if (PlayerWins || PlayerLoses || DLLExportClass::Get_Game_Over()) { + return; + } + + HousesType house; + HouseClass *ptr; + + GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI"); + + if (GAME_TO_PLAY == GAME_NORMAL) { + return; + } + + if (player_id != 0) { + DLLExportClass::Set_Player_Context(player_id); + + if (PlayerPtr) { + PlayerPtr->WasHuman = true; + PlayerPtr->IsHuman = false; + PlayerPtr->IQ = Rule.MaxIQ; + strcpy (PlayerPtr->IniName, Text_String(TXT_COMPUTER)); + + PlayerPtr->IsBaseBuilding = true; + + /* + ** Start the unload mission for MCVs + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr) { + if (*obj == UNIT_MCV) { + obj->Assign_Mission(MISSION_GUARD); + obj->Assign_Target(TARGET_NONE); + obj->Assign_Destination(TARGET_NONE); + obj->Assign_Mission(MISSION_UNLOAD); + obj->Commence(); + } + } + } + + DLLExportClass::On_Message(PlayerPtr, "", 60.0f, MESSAGE_TYPE_PLAYER_DISCONNECTED, -1); + + /* + ** Send the disconnect taunt message + */ + int human_count = 0; + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MULTIPLAYER_COUNT); house++) { + ptr = HouseClass::As_Pointer(house); + + if (ptr && ptr->IsHuman && !ptr->IsDefeated) { + human_count++; + } + } + + if (human_count == 1) { + DLLExportClass::Computer_Message(true); + } + } + } +} + + +/************************************************************************************************** +* CNC_Handle_Human_Team_Wins +* +* History: 3/10/2020 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 quitting_player_id) +{ + GlyphX_Debug_Print("CNC_Handle_Human_Team_Wins"); + DLLExportClass::Force_Human_Team_Wins(quitting_player_id); +} + + +/************************************************************************************************** +* CNC_Start_Mission_Timer +* +* History: 11/25/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time) +{ + if (GameActive) + { + Scen.MissionTimer = time; + + if (!Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Start(); + } + + Map.Redraw_Tab(); + } +} + + +/************************************************************************************************** +* DLLExportClass::Init -- Init the class +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Init(void) +{ + for (int i=0 ; i= VOC_FIRST && sound_effect_index < VOC_COUNT ) + { + strncpy( new_event.SoundEffect.SoundEffectName, SoundEffectName[ sound_effect_index ].Name , 16); + if ( extension != NULL ) + { + strncat( new_event.SoundEffect.SoundEffectName, extension , 16); + } + new_event.SoundEffect.SoundEffectPriority = SoundEffectName[ sound_effect_index ].Priority; + new_event.SoundEffect.SoundEffectContext = SoundEffectName[ sound_effect_index ].Where; + } + else + { + strncpy( new_event.SoundEffect.SoundEffectName, "BADINDEX", 16 ); + new_event.SoundEffect.SoundEffectPriority = -1; + new_event.SoundEffect.SoundEffectContext = -1; + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Speech -- Called when C&C wants to play a speech line +* +* In: +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Speech(const HouseClass* player_ptr, int speech_index) +{ + // player_ptr could be NULL + + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_SPEECH; + new_event.Speech.SpeechIndex = speech_index; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + if (speech_index >= VOX_FIRST && speech_index < VOX_COUNT) + { + strncpy(new_event.Speech.SpeechName, Speech[speech_index], 16); + } + else + { + strncpy(new_event.Speech.SpeechName, "BAD_SPEECH_INDEX", 16); + } + + EventCallback(new_event); +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Leadership -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Leadership( HousesType player_house, int units_lost, int buildings_lost ) +{ + int house = (player_house == HOUSE_USSR || player_house == HOUSE_UKRAINE); // 0 or 1 + + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass* object = Logic[index]; + HousesType owner = object->Owner(); + if ((house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE)) { + leadership++; + } + else { + if ((!house) && (object->Owner() == HOUSE_GREECE)) { + leadership++; + } + } + } + if (!leadership) leadership++; + leadership = 100 * fixed(leadership, (units_lost + buildings_lost + leadership)); + leadership = min(150, leadership); + + return leadership; +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Economy -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Economy( long available_money, int stolen_buildings_credits, unsigned harvested_credits, long initial_credits ) +{ + int economy = 100 * fixed((unsigned)available_money + 1 + stolen_buildings_credits, harvested_credits + (unsigned)initial_credits + 1); + economy = min(economy, 150); + + return economy; +} + +/************************************************************************************************** +* DLLExportClass::RA_Calculate_Score -- +* +* History: 10.30.2019 MBL (Based on LLL's Calculate_Single_Player_Score()) +**************************************************************************************************/ +int DLLExportClass::RA_Calculate_Score( int uspoints, int leadership, int economy ) +{ + int score_total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (score_total < -9999) score_total = -9999; + score_total = min(score_total, 99999); + + return score_total; +} + +/************************************************************************************************** +* DLLExportClass::CalculateScore* +* +* History: 10/16/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Calculate_Single_Player_Score(EventCallbackStruct& event) +{ + //Adapted from Red Alert SCORE.CPP Presentation() - LLL + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 + + int good_units_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->UnitsLost; + int bad_units_lost = (HouseClass::As_Pointer(HOUSE_BAD))->UnitsLost; + int neutral_units_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->UnitsLost; + int good_buildings_lost = (HouseClass::As_Pointer(HOUSE_GOOD))->BuildingsLost; + int bad_buildings_lost = (HouseClass::As_Pointer(HOUSE_BAD))->BuildingsLost; + int neutral_buildings_lost = (HouseClass::As_Pointer(HOUSE_NEUTRAL))->BuildingsLost; + int good_credits_harvested = (HouseClass::As_Pointer(HOUSE_GOOD))->HarvestedCredits; + int bad_credits_harvested = (HouseClass::As_Pointer(HOUSE_BAD))->HarvestedCredits; + + int uspoints = 0; + + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass *hows = HouseClass::As_Pointer(hous); + if (hous == HOUSE_USSR || hous == HOUSE_BAD || hous == HOUSE_UKRAINE) { + bad_units_lost += hows->UnitsLost; + bad_buildings_lost += hows->BuildingsLost; + } + else { + good_units_lost += hows->UnitsLost; + good_buildings_lost += hows->BuildingsLost; + } + if (PlayerPtr->Is_Ally(hous)) { + uspoints += hows->PointTotal; + } + } + // if(uspoints < 0) uspoints = 0; + // uspoints += 1000; //BG 1000 bonus points for winning mission + + /* + ** Bias the base score upward according to the difficulty level. + */ + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + + /* + ** Determine leadership rating. + */ + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + HousesType owner = object->Owner(); + if ((house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE)) { + leadership++; + } + else { + if ((!house) && (object->Owner() == HOUSE_GREECE)) { + leadership++; + } + } + } + if (!leadership) leadership++; + leadership = 100 * fixed(leadership, (house ? bad_units_lost + bad_buildings_lost + leadership : good_units_lost + good_buildings_lost + leadership)); + leadership = min(150, leadership); + + /* + ** Determine economy rating. + */ + // int init = PlayerPtr->Control.InitialCredits; + // int cred = PlayerPtr->Available_Money(); + + int economy = 100 * fixed((unsigned)PlayerPtr->Available_Money() + 1 + PlayerPtr->StolenBuildingsCredits, PlayerPtr->HarvestedCredits + (unsigned)PlayerPtr->Control.InitialCredits + 1); + economy = min(economy, 150); + + int score_total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (score_total < -9999) score_total = -9999; + score_total = min(score_total, 99999); + + //Score Stats + event.GameOver.Leadership = leadership; + event.GameOver.Efficiency = economy; + event.GameOver.Score = score_total; + event.GameOver.CategoryTotal = uspoints; + event.GameOver.NODKilled = bad_units_lost; + event.GameOver.GDIKilled = good_units_lost; + event.GameOver.CiviliansKilled = neutral_units_lost; + event.GameOver.NODBuildingsDestroyed = bad_buildings_lost; + event.GameOver.GDIBuildingsDestroyed = good_buildings_lost; + event.GameOver.CiviliansBuildingsDestroyed = neutral_buildings_lost; + event.GameOver.RemainingCredits = PlayerPtr->Available_Money(); + + if (Scen.IsSkipScore) { + event.GameOver.Score = -1; + } +} + + +/************************************************************************************************** +* DLLExportClass::On_Message -- Called when the game wants to display a message (ex. tutorial text) +* +* In: +* +* Out: +* +* +* +* History: 10/16/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Message(const HouseClass* player_ptr, const char* message, float timeout_seconds, EventCallbackMessageEnum message_type, int64 message_id) +{ + if (EventCallback == NULL) + { + return; + } + + const char* p_msg = message; + + std::string localized_text_name; + if (message_id != -1) { + if (message_id == TXT_INSUFFICIENT_FUNDS) { + localized_text_name = "TEXT_INSUFFICIENT_FUNDS_MESSAGE"; + } + else if (message_id == TXT_LOW_POWER) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_001"; + } + else if (message_id == TXT_POWER_TESLA) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_002"; + } + else if (message_id == TXT_POWER_AAGUN) { + localized_text_name = "TEXT_LOW_POWER_MESSAGE_003"; + } + else { + if (MessagesSent.find(message_id) != MessagesSent.end()) { + return; + } + + MessagesSent.insert(message_id); + + localized_text_name = "TEXT_RA_TUTORIAL_MESSAGE_"; + localized_text_name += std::to_string(message_id); + } + p_msg = localized_text_name.c_str(); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_MESSAGE; + new_event.Message.Message = p_msg; + new_event.Message.TimeoutSeconds = timeout_seconds; + new_event.Message.MessageType = message_type; + new_event.Message.MessageParam1 = message_id; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Update_Map_Cell -- Called when an individual map cell template is updated +* +* +* History: 11/7/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name) +{ + if (EventCallback == NULL) + { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_UPDATE_MAP_CELL; + new_event.UpdateMapCell.CellX = cell_x; + new_event.UpdateMapCell.CellY = cell_y; + strncpy(new_event.UpdateMapCell.TemplateTypeName, template_type_name, 32); + new_event.UpdateMapCell.TemplateTypeName[31] = '\0'; + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Special_Weapon_Targetting -- Called when the server initiates targetting +* +* +* History: 11/19/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type) +{ + if (EventCallback == NULL) + { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_SPECIAL_WEAPON_TARGETTING; + new_event.SpecialWeaponTargetting.Type = RTTI_SPECIAL; + new_event.SpecialWeaponTargetting.ID = weapon_type; + Convert_Special_Weapon_Type(weapon_type, new_event.SpecialWeaponTargetting.WeaponType, new_event.SpecialWeaponTargetting.Name); + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Ping -- Called when a radar ping is needed +* +* +* History: 05/15/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::On_Ping(const HouseClass* player_ptr, COORDINATE coord) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_PING; + new_event.Ping.CoordX = Coord_X(coord); + new_event.Ping.CoordY = Coord_Y(coord); + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) + { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Game_Over -- Called when the C&C campaign game finishes +* +* +* History: 6/19/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::On_Game_Over(uint64 glyphx_Player_id, bool player_wins) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + SaveTanya = IsTanyaDead; + Scen.CarryOverTimer = Scen.MissionTimer; + // int timer = Scen.MissionTimer; + + Scen.CarryOverMoney = PlayerPtr->Credits; + + DLLExportClass::Store_Carryover_Objects(); + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_GAME_OVER; + new_event.GlyphXPlayerID = glyphx_Player_id; + new_event.GameOver.PlayerWins = player_wins; + new_event.GameOver.AfterScoreMovieName = ""; + new_event.GameOver.Multiplayer = false; + new_event.GameOver.MultiPlayerTotalPlayers = 0; + + Calculate_Single_Player_Score(new_event); + + VQType movie = player_wins ? Scen.WinMovie : Scen.LoseMovie; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName = VQName[movie]; + } else { + new_event.GameOver.MovieName = ""; + } + + movie = player_wins ? Scen.WinMovie2 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName2 = VQName[movie]; + } + else { + new_event.GameOver.MovieName2 = ""; + } + + movie = player_wins ? Scen.WinMovie3 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName3 = VQName[movie]; + } + else { + new_event.GameOver.MovieName3 = ""; + } + + movie = player_wins ? Scen.WinMovie4 : VQ_NONE; + if (movie > VQ_NONE && movie < VQ_COUNT) { + new_event.GameOver.MovieName4 = VQName[movie]; + } + else { + new_event.GameOver.MovieName4 = ""; + } + + //Campaign win & credits + if (Scen.IsEndOfGame) { + if (PlayerPtr->ActLike == HOUSE_USSR) { + new_event.GameOver.AfterScoreMovieName = VQName[VQ_SOVFINAL]; + } + else { + new_event.GameOver.AfterScoreMovieName = VQName[VQ_ALLYEND]; + } + } + + new_event.GameOver.SabotagedStructureType = -1; + new_event.GameOver.TimerRemaining = Scen.MissionTimer.Was_Started() ? Scen.MissionTimer.Value() : -1; + + EventCallback(new_event); +} + + + +/************************************************************************************************** +* DLLExportClass::On_Multiplayer_Game_Over -- Called when the C&C multiplayer game finishes +* +* +* History: 6/19/2019 - LLL +* History: 10/31/2019 - MBL - Adding the multi-player score stats support for debrief +**************************************************************************************************/ +void DLLExportClass::On_Multiplayer_Game_Over(void) +{ + if (EventCallback == NULL) { + return; + } + + GameOver = true; + + EventCallbackStruct event; + + event.EventType = CALLBACK_EVENT_GAME_OVER; + + // Includes AI's for skirmish + int player_count = Session.Players.Count(); + + // Multiplayer players data for debrief stats + + event.GameOver.Multiplayer = true; + event.GameOver.MultiPlayerTotalPlayers = player_count; + + for ( int player_index = 0; player_index < player_count; player_index ++ ) + { + HouseClass* player_ptr = HouseClass::As_Pointer(Session.Players[ player_index ]->Player.ID); + if ( player_ptr != NULL ) + { + HousesType player_house = PlayerPtr->Class->House; + + int uspoints = 0; + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass* hows = HouseClass::As_Pointer(hous); + if (player_ptr->Is_Ally(hous)) { + uspoints += hows->PointTotal; + } + } + // if(uspoints < 0) uspoints = 0; + // uspoints += 1000; //BG 1000 bonus points for winning mission + + // N/A for multi-player + #if 0 + // Bias the base score upward according to the difficulty level. + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + #endif + + int leadership = RA_Calculate_Leadership( player_ptr->Class->House, player_ptr->UnitsLost, player_ptr->BuildingsLost ); + int economy = RA_Calculate_Economy( player_ptr->Available_Money(), player_ptr->StolenBuildingsCredits, player_ptr->HarvestedCredits, player_ptr->Control.InitialCredits ); + int total_score = RA_Calculate_Score( uspoints, leadership, economy ); + + int units_killed = 0; + int structures_killed = 0; + for ( unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++ ) + { + units_killed += player_ptr->UnitsKilled[ house_index ]; + structures_killed += player_ptr->BuildingsKilled[ house_index ]; + } + + // Populate and copy the multiplayer player data structure + + GameOverMultiPlayerStatsStruct multi_player_data; + + multi_player_data.GlyphXPlayerID = Get_GlyphX_Player_ID( player_ptr ); + multi_player_data.IsHuman = (player_ptr->IsHuman || player_ptr->WasHuman); + multi_player_data.WasHuman = player_ptr->WasHuman; + multi_player_data.IsWinner = !player_ptr->IsDefeated; + multi_player_data.Efficiency = economy; + multi_player_data.Score = total_score; + multi_player_data.ResourcesGathered = player_ptr->HarvestedCredits; + multi_player_data.TotalUnitsKilled = units_killed; + multi_player_data.TotalStructuresKilled = structures_killed; + + if ( player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ) + { + event.GameOver.MultiPlayerPlayersData[ player_index ] = multi_player_data; + } + } + } + for ( int player_index = player_count; player_index < GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED; player_index ++ ) + { + memset( &event.GameOver.MultiPlayerPlayersData[ player_index ], 0, sizeof( GameOverMultiPlayerStatsStruct ) ); + } + + // Single-player N/A stuff + + event.GameOver.MovieName = ""; + event.GameOver.MovieName2 = ""; + event.GameOver.MovieName3 = ""; + event.GameOver.MovieName4 = ""; + event.GameOver.AfterScoreMovieName = ""; + event.GameOver.Leadership = 0; + event.GameOver.Efficiency = 0; + event.GameOver.Score = 0; + event.GameOver.NODKilled = 0; + event.GameOver.GDIKilled = 0; + event.GameOver.CiviliansKilled = 0; + event.GameOver.NODBuildingsDestroyed = 0; + event.GameOver.GDIBuildingsDestroyed = 0; + event.GameOver.CiviliansBuildingsDestroyed = 0; + event.GameOver.RemainingCredits = 0; + event.GameOver.SabotagedStructureType = 0; + event.GameOver.TimerRemaining = -1; + + // Trigger an event for each human player + for (int i=0 ; iPlayer.ID); + if (player_ptr->IsHuman) { + event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + event.GameOver.PlayerWins = !player_ptr->IsDefeated; + event.GameOver.RemainingCredits = player_ptr->Available_Money(); + EventCallback(event); + } + } +} + + +/************************************************************************************************** +* DLLExportClass::On_Achievement -- Called when something achievement-related happens +* +* In: Type of achievement, reason this happened +* +* Out: +* +* +* +* History: 11/11/2019 11:37AM - ST +**************************************************************************************************/ +void DLLExportClass::On_Achievement(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_ACHIEVEMENT; + new_event.Achievement.AchievementType = achievement_type; + new_event.Achievement.AchievementReason = achievement_reason; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +void DLLExportClass::On_Center_Camera(const HouseClass* player_ptr, int coord_x, int coord_y) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_CENTER_CAMERA; + new_event.CenterCamera.CoordX = coord_x; + new_event.CenterCamera.CoordY = coord_y; + + new_event.GlyphXPlayerID = 0; + if (player_ptr != NULL) { + new_event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr); + } + + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::On_Debug_Output -- Called when C&C wants to print debug output +* +* In: String to print to GlyphX log system +* +* Out: +* +* +* +* History: 2/20/2019 2:39PM - ST +**************************************************************************************************/ +void DLLExportClass::On_Debug_Output(const char *debug_text) +{ + if (EventCallback == NULL) { + return; + } + + EventCallbackStruct new_event; + new_event.EventType = CALLBACK_EVENT_DEBUG_PRINT; + new_event.DebugPrint.PrintString = debug_text; + EventCallback(new_event); +} + + +/************************************************************************************************** +* DLLExportClass::Force_Human_Team_Wins +* +* History: 3/10/2020 - LL +**************************************************************************************************/ +void DLLExportClass::Force_Human_Team_Wins(uint64 quitting_player_id) +{ + int winning_team = -1; + + //Find the first human's multiplayer team. + for (int i = 0; i < Session.Players.Count(); i++) + { + if (GlyphxPlayerIDs[i] != quitting_player_id) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class && house_class->IsHuman && !house_class->IsDefeated) { + winning_team = MPlayerTeamIDs[i]; + break; + } + } + } + + //Mark all players not on that team as defeated. + for (int i = 0; i < Session.Players.Count(); i++) + { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass* house_class = HouseClass::As_Pointer(house_type); + if (house_class) { + house_class->IsDefeated = MPlayerTeamIDs[i] != winning_team; + } + } + + PlayerWins = true; +} + + +/************************************************************************************************** +* CNC_Get_Game_State -- Get game state +* +* In: Type of state requested +* Player perspective +* Buffer to contain game state +* Size of buffer +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Game_State(GameStateRequestEnum state_type, uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + bool got_state = false; + + switch (state_type) { + + case GAME_STATE_LAYERS: + { + got_state = DLLExportClass::Get_Layer_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_SIDEBAR: + { + got_state = DLLExportClass::Get_Sidebar_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_PLACEMENT: + { + got_state = DLLExportClass::Get_Placement_State(player_id, buffer_in, buffer_size); + break; + } + + case GAME_STATE_DYNAMIC_MAP: + got_state = DLLExportClass::Get_Dynamic_Map_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_SHROUD: + got_state = DLLExportClass::Get_Shroud_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_OCCUPIER: + got_state = DLLExportClass::Get_Occupier_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_PLAYER_INFO: + got_state = DLLExportClass::Get_Player_Info_State(player_id, buffer_in, buffer_size); + break; + + case GAME_STATE_STATIC_MAP: + { + if (buffer_size < sizeof(CNCMapDataStruct)) { + got_state = false; + break; + } + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + CNCMapDataStruct *map_data = (CNCMapDataStruct *)buffer_in; + + map_data->OriginalMapCellX = map_cell_x; + map_data->OriginalMapCellY = map_cell_y; + map_data->OriginalMapCellWidth = map_cell_width; + map_data->OriginalMapCellHeight = map_cell_height; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + map_data->MapCellX = map_cell_x; + map_data->MapCellY = map_cell_y; + map_data->MapCellWidth = map_cell_width; + map_data->MapCellHeight = map_cell_height; + + map_data->Theater = (CnCTheaterType) Scen.Theater; + + char* dot_ptr = strchr(Scen.ScenarioName, '.'); + const int count = (dot_ptr != nullptr) ? (dot_ptr - Scen.ScenarioName) : sizeof(Scen.ScenarioName); + memset(map_data->ScenarioName, 0, sizeof(map_data->ScenarioName)); + strncpy(map_data->ScenarioName, Scen.ScenarioName, min(sizeof(map_data->ScenarioName) - 1, count)); + + int cell_index = 0; + char cell_name[_MAX_PATH]; + char icon_number[32]; + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + CellClass * cellptr = &Map[cell]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) { + itoa(icon, icon_number, 10); + strncat(cell_name, "_i", 32); + strncat(cell_name, icon_number, 32); + strncat(cell_name, ".tga", 32); + + CNCStaticCellStruct &cell_info = map_data->StaticCells[cell_index++]; + strncpy(cell_info.TemplateTypeName, cell_name, 32); + cell_info.IconNumber = icon; + } + } + } + + got_state = true; + break; + } + + default: + { + got_state = false; + break; + } + } + + return got_state; +} + + +/************************************************************************************************** +* CNC_Handle_Game_Request +* +* Callback for when the requested movie is done playing. +* +* 7/23/2019 - LLL +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Request(GameRequestEnum request_type) +{ + switch (request_type) + { + case INPUT_GAME_MOVIE_DONE: + break; + } +} + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Game_Settings_Request(int health_bar_display_mode, int resource_bar_display_mode) +{ + if (!DLLExportClass::Legacy_Render_Enabled()) { + return; + } + + RulesClass::eHealthBarDisplayMode new_hb_mode = (RulesClass::eHealthBarDisplayMode)health_bar_display_mode; + if (new_hb_mode != Rule.HealthBarDisplayMode) { + Rule.HealthBarDisplayMode = new_hb_mode; + Map.Flag_To_Redraw(true); + } + + RulesClass::eResourceBarDisplayMode new_rb_mode = (RulesClass::eResourceBarDisplayMode)resource_bar_display_mode; + if (new_rb_mode != Rule.ResourceBarDisplayMode) { + Rule.ResourceBarDisplayMode = new_rb_mode; + Map.Flag_To_Redraw(true); + } +} + + + + + + +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) +{ + DLLExportClass::DLL_Draw_Intercept(shape_number, x, y, width, height, flags, object, rotation, scale, shape_file_name, override_owner); +} + + +void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + DLLExportClass::DLL_Draw_Pip_Intercept(object, pip); +} + + +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) +{ + CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount]; + Convert_Type(object, new_object); + if (new_object.Type == UNKNOWN) { + return; + } + + CNCObjectStruct* base_object = NULL; + char sub_object = 0; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + if (base_object == NULL) { + base_object = &draw_object; + } + sub_object++; + } + } + + new_object.CNCInternalObjectPointer = (void*)object; + new_object.OccupyListLength = 0; + new_object.SortOrder = SortOrder++; + + strncpy(new_object.TypeName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + + if (shape_file_name != NULL) { + strncpy(new_object.AssetName, shape_file_name, CNC_OBJECT_ASSET_NAME_LENGTH); + } + else { + strncpy(new_object.AssetName, object->Class_Of().Graphic_Name(), CNC_OBJECT_ASSET_NAME_LENGTH); + } + + new_object.Owner = (base_object != NULL) ? ((override_owner != HOUSE_NONE) ? override_owner : base_object->Owner) : (char)object->Owner(); + + HouseClass* owner_house = nullptr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && (hptr->Class->House == new_object.Owner)) { + owner_house = hptr; + break; + } + } + + new_object.RemapColor = (owner_house != nullptr) ? owner_house->RemapColor : -1; + + if (base_object == NULL) { + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + + if (new_object.Type == BUILDING) { + BuildingClass *building = (BuildingClass*)object; + if (building->BState == BSTATE_CONSTRUCTION) { + strncat(new_object.AssetName, "MAKE", CNC_OBJECT_ASSET_NAME_LENGTH); + } + const BuildingTypeClass *building_type = building->Class; + short const *occupy_list = building_type->Occupy_List(); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && new_object.OccupyListLength < MAX_OCCUPY_CELLS) { + new_object.OccupyList[new_object.OccupyListLength] = *occupy_list; + new_object.OccupyListLength++; + occupy_list++; + } + } + } + + COORDINATE coord = object->Render_Coord(); + CELL cell = Coord_Cell(coord); + int dimx, dimy; + object->Class_Of().Dimensions(dimx, dimy); + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = object->Height; + new_object.DrawFlags = flags; + new_object.SubObject = 0; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Rotation = (unsigned char)rotation; + new_object.Scale = scale; + new_object.FlashingFlags = 0; + new_object.Cloak = (CurrentDrawCount > 0) ? root_object.Cloak : UNCLOAKED; + new_object.VisibleFlags = CNCObjectStruct::VISIBLE_FLAGS_ALL; + new_object.SpiedByFlags = 0U; + + new_object.IsSelectable = object->Class_Of().IsSelectable; + new_object.IsSelectedMask = object->IsSelectedMask; + new_object.MaxStrength = object->Class_Of().MaxStrength; + new_object.Strength = object->Strength; + new_object.CellX = (CurrentDrawCount > 0) ? root_object.CellX : Cell_X(cell); + new_object.CellY = (CurrentDrawCount > 0) ? root_object.CellY : Cell_Y(cell); + new_object.CenterCoordX = Coord_X(object->Center_Coord()); + new_object.CenterCoordY = Coord_Y(object->Center_Coord()); + new_object.DimensionX = dimx; + new_object.DimensionY = dimy; + new_object.SimLeptonX = (CurrentDrawCount > 0) ? root_object.SimLeptonX : 0; + new_object.SimLeptonY = (CurrentDrawCount > 0) ? root_object.SimLeptonY : 0; + new_object.BaseObjectID = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING) && (root_object.Type != VESSEL)) ? root_object.ID : 0; + new_object.BaseObjectType = ((CurrentDrawCount > 0) && (root_object.Type != BUILDING) && (root_object.Type != VESSEL)) ? root_object.Type : UNKNOWN; + new_object.NumLines = 0; + new_object.RecentlyCreated = object->IsRecentlyCreated; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.CanDemolish = object->Can_Demolish(); + new_object.CanDemolishUnit = object->Can_Demolish_Unit(); + new_object.CanRepair = object->Can_Repair(); + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + HousesType house = hptr->Class->House; + DynamicVectorClass& selected_objects = CurrentObject.Raw(house); + if (selected_objects.Count() > 0) { + Logic_Switch_Player_Context(hptr); + Convert_Action_Type(Best_Object_Action(selected_objects, object), (selected_objects.Count() == 1) ? selected_objects[0] : NULL, object->As_Target(), new_object.ActionWithSelected[house]); + } + } + } + Logic_Switch_Player_Context(old_player_ptr); + + RTTIType what_is_object = object->What_Am_I(); + + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = false; + new_object.IsFactory = false; + new_object.IsPrimaryFactory = false; + new_object.IsNominal = false; + new_object.IsDog = false; + new_object.IsIronCurtain = false; + new_object.IsAntiGround = false; + new_object.IsAntiAircraft = false; + new_object.IsSubSurface = false; + new_object.IsFake = false; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + + bool is_building = what_is_object == RTTI_BUILDING; + if (is_building) { + const BuildingClass* building = static_cast(object); + new_object.IsRepairing = building->IsRepairing; + new_object.IsFactory = building->Class->Is_Factory(); + new_object.IsPrimaryFactory = building->IsLeader; + new_object.IsFake = building->Class->IsFake; + } + + if (object->Is_Techno()) { + const TechnoClass* techno_object = static_cast(object); + const TechnoTypeClass *ttype = techno_object->Techno_Type_Class(); + + new_object.MaxSpeed = (unsigned char)ttype->MaxSpeed; + new_object.IsALoaner = techno_object->IsALoaner; + new_object.IsNominal = ttype->IsNominal; + new_object.MaxPips = ttype->Max_Pips(); + new_object.IsAntiGround = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsAntiGround; + new_object.IsAntiAircraft = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsAntiAircraft; + new_object.IsSubSurface = (ttype->PrimaryWeapon != NULL) && (ttype->PrimaryWeapon->Bullet != NULL) && ttype->PrimaryWeapon->Bullet->IsSubSurface; + new_object.IsIronCurtain = techno_object->IronCurtainCountDown > 0; + + int full_name = techno_object->Full_Name(); + if (full_name < 0) + { + new_object.OverrideDisplayName = Text_String(full_name); + } + + HouseClass* old_player_ptr = PlayerPtr; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && hptr->IsHuman) { + Logic_Switch_Player_Context(hptr); + HousesType house = hptr->Class->House; + new_object.CanMove[house] = techno_object->Can_Player_Move(); + new_object.CanFire[house] = techno_object->Can_Player_Fire(); + } + } + Logic_Switch_Player_Context(old_player_ptr); + } + + new_object.ControlGroup = (unsigned char)(-1); + new_object.IsInFormation = false; + new_object.CanPlaceBombs = false; + + if (object->Is_Foot()) { + const FootClass* foot = static_cast(object); + new_object.ControlGroup = foot->Group; + new_object.IsInFormation = foot->XFormOffset != 0x80000000UL; + } + + bool is_infantry = what_is_object == RTTI_INFANTRY; + if (is_infantry) { + const InfantryClass* infantry = static_cast(object); + new_object.IsDog = infantry->Class->IsDog; + new_object.CanPlaceBombs = infantry->Class->IsBomber; + } + + new_object.CanHarvest = false; + bool is_unit = what_is_object == RTTI_UNIT; + if (is_unit) { + const UnitClass* unit = static_cast(object); + if (unit->Class->Type == UNIT_HARVESTER) + { + new_object.CanHarvest = true; + } + + new_object.IsDumping = unit->IsDumping; + } + + new_object.IsFixedWingedAircraft = false; + bool is_aircraft = what_is_object == RTTI_AIRCRAFT; + if (is_aircraft) { + const AircraftClass* aircraft = static_cast(object);; + new_object.IsFixedWingedAircraft = aircraft->Class->IsFixedWing; + } + + switch (what_is_object) + { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + case RTTI_UNIT: + case RTTI_UNITTYPE: + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + { + const TechnoClass* techno_object = static_cast(object); + new_object.FlashingFlags = techno_object->Get_Flashing_Flags(); + new_object.Cloak = techno_object->Cloak; + new_object.SpiedByFlags = techno_object->Spied_By(); + + if (techno_object->Techno_Type_Class()->IsInvisible) { + // Hide for enemy players + HouseClass* owner = HouseClass::As_Pointer(object->Owner()); + if (owner != nullptr) { + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* hptr = Houses.Ptr(i); + if ((hptr != nullptr) && hptr->IsActive && !owner->Is_Ally(hptr)) { + new_object.VisibleFlags &= ~(1 << hptr->Class->House); + } + } + } + } + } + break; + case RTTI_ANIM: + case RTTI_ANIMTYPE: + { + const AnimClass* anim_object = static_cast(object); + new_object.Owner = anim_object->OwnerHouse; + new_object.RemapColor = -1; + new_object.VisibleFlags = anim_object->Get_Visible_Flags(); + + const AnimTypeClass& anim_type = static_cast(anim_object->Class_Of()); + if (anim_type.VirtualName != NULL) { + strncpy(new_object.AssetName, anim_type.VirtualName, CNC_OBJECT_ASSET_NAME_LENGTH); + } + } + break; + case RTTI_TERRAIN: + case RTTI_TERRAINTYPE: + { + if (strncmp(new_object.AssetName, "MINE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0) { + strncpy(new_object.AssetName, "OREMINE", CNC_OBJECT_ASSET_NAME_LENGTH); + } + } + break; + } + + } + else { + new_object.MaxStrength = 0; + new_object.MaxSpeed = 0; + new_object.Strength = 0; + new_object.CellX = base_object->CellX; + new_object.CellY = base_object->CellY; + new_object.CenterCoordX = base_object->CenterCoordX; + new_object.CenterCoordY = base_object->CenterCoordY; + new_object.DimensionX = base_object->DimensionX; + new_object.DimensionY = base_object->DimensionY; + new_object.IsSelectable = false; + new_object.IsSelectedMask = 0U; + new_object.SimLeptonX = base_object->SimLeptonX; + new_object.SimLeptonY = base_object->SimLeptonY; + + new_object.PositionX = x; + new_object.PositionY = y; + new_object.Width = width; + new_object.Height = height; + new_object.Altitude = base_object->Altitude; + new_object.DrawFlags = flags; + new_object.ShapeIndex = (unsigned short)shape_number; + new_object.IsTheaterSpecific = IsTheaterShape; + new_object.Rotation = (unsigned char)rotation; + new_object.Scale = scale; + new_object.SubObject = sub_object; + new_object.BaseObjectID = base_object->ID; + new_object.BaseObjectType = base_object->Type; + + new_object.FlashingFlags = base_object->FlashingFlags; + new_object.Cloak = base_object->Cloak; + new_object.OccupyListLength = 0; + new_object.NumPips = 0; + new_object.MaxPips = 0; + new_object.NumLines = 0; + new_object.IsRepairing = false; + new_object.IsDumping = false; + new_object.IsALoaner = base_object->IsALoaner; + new_object.CanDemolish = base_object->CanDemolish; + new_object.CanDemolishUnit = base_object->CanDemolishUnit; + new_object.CanRepair = base_object->CanRepair; + new_object.RecentlyCreated = base_object->RecentlyCreated; + new_object.IsFactory = base_object->IsFactory; + new_object.IsPrimaryFactory = base_object->IsPrimaryFactory; + new_object.IsAntiGround = base_object->IsAntiGround; + new_object.IsAntiAircraft = base_object->IsAntiAircraft; + new_object.IsSubSurface = base_object->IsSubSurface; + new_object.IsNominal = base_object->IsNominal; + new_object.IsDog = base_object->IsDog; + new_object.IsIronCurtain = base_object->IsIronCurtain; + new_object.IsInFormation = false; + new_object.CanHarvest = base_object->CanHarvest; + new_object.CanPlaceBombs = base_object->CanPlaceBombs; + new_object.ControlGroup = base_object->ControlGroup; + new_object.VisibleFlags = base_object->VisibleFlags; + new_object.SpiedByFlags = base_object->SpiedByFlags; + new_object.IsFixedWingedAircraft = base_object->IsFixedWingedAircraft; + new_object.IsFake = base_object->IsFake; + new_object.ProductionAssetName[0] = '\0'; + new_object.OverrideDisplayName = "\0"; + memset(new_object.CanMove, false, sizeof(new_object.CanMove)); + memset(new_object.CanFire, false, sizeof(new_object.CanFire)); + memset(new_object.ActionWithSelected, DAT_NONE, sizeof(new_object.ActionWithSelected)); + } + + CurrentDrawCount++; +} + + + +void DLLExportClass::DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +{ + CNCObjectStruct* base_object = NULL; + for (int i = 0; i < CurrentDrawCount; ++i) { + CNCObjectStruct& draw_object = ObjectList->Objects[TotalObjectCount + i]; + if (draw_object.CNCInternalObjectPointer == object) { + base_object = &draw_object; + break; + } + } + + if ((base_object != NULL) && (base_object->NumPips < MAX_OBJECT_PIPS)) { + base_object->Pips[base_object->NumPips] = pip; + base_object->NumPips++; + base_object->MaxPips = max(base_object->MaxPips, base_object->NumPips); + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get game objects from the layers +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Layer_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + player_id; + + static int _export_count = 0; + + bool got_state = false; + + ObjectList = (CNCObjectListStruct*) buffer_in; + + TotalObjectCount = 0; + + /* + ** Get a reference draw coordinate for cells + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + if (map_cell_x > 0) { + map_cell_x--; + } + if (map_cell_y > 0) { + map_cell_y--; + } + + SortOrder = 0; + + /* + ** Get the ground layer first and then followed by all the layers in increasing altitude. + */ + for (int layer = 0; layer < DLL_LAYER_COUNT; layer++) { + + for (int index = 0; index < Map.Layer[layer].Count(); index++) { + + ObjectClass *object = Map.Layer[layer][index]; + if (object->IsActive) { + + unsigned int memory_needed = sizeof(CNCObjectListStruct); + memory_needed += (TotalObjectCount + 10) * sizeof(CNCObjectStruct); + if (memory_needed >= buffer_size) { + return false; + } + + if (object->Is_Techno()) { + /* + ** Skip units tethered to buildings, since the building will draw them itself + */ + TechnoClass* techno_object = static_cast(object); + TechnoClass* contact_object = techno_object->In_Radio_Contact() ? techno_object->Contact_With_Whom() : nullptr; + 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) { + continue; + } + + /* + ** Skip units tethered to vessels, since the vessel will draw them itself + */ + if ((contact_object != nullptr) && (contact_object->What_Am_I() == RTTI_VESSEL) && !contact_object->Is_Door_Closed() && contact_object->IsTethered && !techno_object->IsInLimbo) { + continue; + } + } + + if (Debug_Map || Debug_Unshroud || (object->IsDown && !object->IsInLimbo)) { + int x, y; + Map.Coord_To_Pixel(object->Render_Coord(), x, y); + + /* + ** Call to Draw_It can result in multiple callbacks to the draw intercept + */ + CurrentDrawCount = 0; + object->Draw_It(x, y, WINDOW_VIRTUAL); + + /* + ** If the root object is a factory, then the last base object is the object in production (rendered after infiltrated buildings when selected). + ** The root object is updated with the production asset name, but otherwise a separate object isn't created. + ** This only occurs in skirmish and multiplayer. + */ + if ((GAME_TO_PLAY != GAME_NORMAL) && (CurrentDrawCount > 0)) { + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + if (root_object.IsFactory) { + BuildingClass* building = (BuildingClass*)root_object.CNCInternalObjectPointer; + FactoryClass* factory = building->House->IsHuman ? building->House->Fetch_Factory(building->Class->ToBuild) : building->Factory; + if (factory != nullptr) { + for (int i = CurrentDrawCount - 1; i > 0; --i) { + CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + i]; + if (base_object.SubObject) { + continue; + } + strncpy(root_object.ProductionAssetName, base_object.TypeName, CNC_OBJECT_ASSET_NAME_LENGTH); + void* production_object = base_object.CNCInternalObjectPointer; + int new_draw_count = i; + for (int j = i + 1; j < CurrentDrawCount; ++j) { + CNCObjectStruct& cnc_object = ObjectList->Objects[TotalObjectCount + j]; + if (cnc_object.CNCInternalObjectPointer != production_object) { + memcpy(ObjectList->Objects + TotalObjectCount + new_draw_count, &cnc_object, sizeof(CNCObjectStruct)); + new_draw_count++; + } + } + memset(ObjectList->Objects + TotalObjectCount + new_draw_count, 0, (CurrentDrawCount - new_draw_count) * sizeof(CNCObjectStruct)); + CurrentDrawCount = new_draw_count; + break; + } + } + } + } + + /* + ** Shadows need to be rendered before the base object so they appear underneath, + ** even though they get drawn as sub-objects (after the base object) + */ + for (int i = 1; i < CurrentDrawCount; ++i) { + CNCObjectStruct& sub_object = ObjectList->Objects[TotalObjectCount + i]; + if (!sub_object.SubObject) { + continue; + } + static const int shadow_flags = SHAPE_PREDATOR | SHAPE_FADING; + if (((sub_object.DrawFlags & shadow_flags) == shadow_flags) || (strncmp(sub_object.AssetName, "WAKE", CNC_OBJECT_ASSET_NAME_LENGTH) == 0)) { + if ((strncmp(sub_object.AssetName, "RROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0) && + (strncmp(sub_object.AssetName, "LROTOR", CNC_OBJECT_ASSET_NAME_LENGTH) != 0)) { + for (int j = i - 1; j >= 0; --j) { + CNCObjectStruct& base_object = ObjectList->Objects[TotalObjectCount + j]; + if (!base_object.SubObject && (base_object.CNCInternalObjectPointer == sub_object.CNCInternalObjectPointer)) { + int sort_order = base_object.SortOrder; + base_object.SortOrder = sub_object.SortOrder; + sub_object.SortOrder = sort_order; + break; + } + } + } + } + } + + TotalObjectCount += CurrentDrawCount; + } + } + } + } + + ObjectList->Count = TotalObjectCount; + + if (ObjectList->Count) { + _export_count++; + return true; + } + + return false; +} + + + + +void DLLExportClass::Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out) +{ + object_out.Type = UNKNOWN; + object_out.ID = -1; + + if (object == NULL) { + return; + } + + RTTIType type = object->What_Am_I(); + + switch (type) { + default: + break; + + case RTTI_VESSEL: + object_out.Type = VESSEL; + object_out.ID = Vessels.ID((VesselClass*)object); + break; + + case RTTI_INFANTRY: + object_out.Type = INFANTRY; + object_out.ID = Infantry.ID((InfantryClass*)object); + break; + + case RTTI_UNIT: + object_out.Type = UNIT; + object_out.ID = Units.ID((UnitClass*)object); + break; + + case RTTI_AIRCRAFT: + object_out.Type = AIRCRAFT; + object_out.ID = Aircraft.ID((AircraftClass*)object); + break; + + case RTTI_BUILDING: + object_out.Type = BUILDING; + object_out.ID = Buildings.ID((BuildingClass*)object); + break; + + case RTTI_BULLET: + object_out.Type = BULLET; + object_out.ID = Bullets.ID((BulletClass*)object); + break; + + case RTTI_ANIM: + object_out.Type = ANIM; + object_out.ID = Anims.ID((AnimClass*)object); + break; + + case RTTI_SMUDGE: + object_out.Type = SMUDGE; + object_out.ID = Smudges.ID((SmudgeClass*)object); + break; + + case RTTI_TERRAIN: + object_out.Type = TERRAIN; + object_out.ID = Terrains.ID((TerrainClass*)object); + break; + } +} + + + + + + +/************************************************************************************************** +* CNC_Handle_Input -- Process input to the game +* +* In: +* +* +* +* +* Out: Game state returned in buffer +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +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) +{ + + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (input_event) { + + /* + ** Special keys have changed + */ + case INPUT_REQUEST_SPECIAL_KEYS: + { + DLLExportClass::Set_Special_Key_Flags(special_key_flags); + break; + } + + /* + ** The mouse is moving + */ + case INPUT_REQUEST_MOUSE_MOVE: + { + if (!DLLExportClass::Legacy_Render_Enabled()) { + break; + } + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + if (coord) { + //x -= Map.TacPixelX; + //y -= Map.TacPixelY; + + /* + ** Cause any displayed cursor to move along with the mouse cursor. + */ + if (cell != Map.ZoneCell) { + Map.Set_Cursor_Pos(cell); + } + } + break; + } + + /* + ** Player left-clicked + */ + case INPUT_REQUEST_MOUSE_LEFT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::LEFTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right-clicked (on up) + */ + case INPUT_REQUEST_MOUSE_RIGHT_CLICK: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE | KN_RLSE_BIT); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTRELEASE, 100, 100); + } + break; + } + + /* + ** Player right button down + */ + case INPUT_REQUEST_MOUSE_RIGHT_DOWN: + { + DLLExportClass::Adjust_Internal_View(); + + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + KeyNumType key = (KeyNumType)(KN_RMOUSE); + + if (Map.Pixel_To_Coord(x1, y1)) { + //DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, x1, y1); + DisplayClass::TacButton.Clicked_On(key, GadgetClass::RIGHTPRESS, 100, 100); + } + break; + } + + + /* + ** Player drag selected + */ + case INPUT_REQUEST_MOUSE_AREA: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYP_Coord(x1, y1), XYP_Coord(x2, y2), false); + break; + } + + case INPUT_REQUEST_MOUSE_AREA_ADDITIVE: + { + DLLExportClass::Adjust_Internal_View(); + Map.Select_These(XYP_Coord(x1, y1), XYP_Coord(x2, y2), true); + break; + } + + case INPUT_REQUEST_SELL_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + KEYBOARD->MouseQX = x1; + KEYBOARD->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + PlayerPtr->Sell_Wall(cell); + + break; + } + + case INPUT_REQUEST_SELECT_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + Keyboard->MouseQX = x1; + Keyboard->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + + DisplayClass::TacButton.Selection_At_Mouse(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + case INPUT_REQUEST_COMMAND_AT_POSITION: + { + DLLExportClass::Adjust_Internal_View(); + DLLForceMouseX = x1; + DLLForceMouseY = y1; + Keyboard->MouseQX = x1; + Keyboard->MouseQY = y1; + + COORDINATE coord = Map.Pixel_To_Coord(x1, y1); + CELL cell = Coord_Cell(coord); + + if (Map.Pixel_To_Coord(x1, y1)) + { + KeyNumType key = (KeyNumType)(KN_LMOUSE | KN_RLSE_BIT); + DisplayClass::TacButton.Command_Object(GadgetClass::LEFTRELEASE, key); + } + + break; + } + + default: + break; + } +} + + + + +/************************************************************************************************** +* CNC_Handle_Structure_Request -- Process requests to repair and sell structures. +* +* In: +* +* +* Out: +* +* +* +* History: 4/29/2019 - LLL +**************************************************************************************************/ + +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Structure_Request(StructureRequestEnum request_type, uint64 player_id, int object_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_STRUCTURE_REPAIR_START: + DLLExportClass::Repair_Mode(player_id); + break; + case INPUT_STRUCTURE_REPAIR: + DLLExportClass::Repair(player_id, object_id); + break; + case INPUT_STRUCTURE_SELL_START: + DLLExportClass::Sell_Mode(player_id); + break; + case INPUT_STRUCTURE_SELL: + DLLExportClass::Sell(player_id, object_id); + break; + case INPUT_STRUCTURE_CANCEL: + DLLExportClass::Repair_Sell_Cancel(player_id); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Unit_Request -- Process requests on selected units. +* +* In: +* +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Unit_Request(UnitRequestEnum request_type, uint64 player_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case INPUT_UNIT_SCATTER: + DLLExportClass::Scatter_Selected(player_id); + break; + case INPUT_UNIT_SELECT_NEXT: + DLLExportClass::Select_Next_Unit(player_id); + break; + case INPUT_UNIT_SELECT_PREVIOUS: + DLLExportClass::Select_Previous_Unit(player_id); + break; + case INPUT_UNIT_GUARD_MODE: + DLLExportClass::Selected_Guard_Mode(player_id); + break; + case INPUT_UNIT_STOP: + DLLExportClass::Selected_Stop(player_id); + break; + case INPUT_UNIT_FORMATION_TOGGLE: + DLLExportClass::Team_Units_Formation_Toggle_On(player_id); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_ON: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, true); + break; + case INPUT_UNIT_QUEUED_MOVEMENT_OFF: + // Red Alert Only + DLLExportClass::Units_Queued_Movement_Toggle(player_id, false); + break; + default: + break; + } +} + + + +/************************************************************************************************** +* CNC_Handle_Sidebar_Request -- Process an input request to the sidebar +* +* In: +* +* +* Out: +* +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +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) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) { + + case SIDEBAR_REQUEST_START_CONSTRUCTION: + DLLExportClass::Start_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + DLLExportClass::Hold_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + DLLExportClass::Cancel_Construction(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_START_PLACEMENT: + DLLExportClass::Start_Placement(player_id, buildable_type, buildable_id); + break; + + case SIDEBAR_REQUEST_PLACE: + DLLExportClass::Place(player_id, buildable_type, buildable_id, cell_x, cell_y); + break; + + case SIDEBAR_CANCEL_PLACE: + DLLExportClass::Cancel_Placement(player_id, buildable_type, buildable_id); + break; + + default: + break; + } +} + +/************************************************************************************************** +* CNC_Handle_SuperWeapon_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +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) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON: + DLLExportClass::Place_Super_Weapon(player_id, buildable_type, buildable_id, x1, y1); + break; + } +} + +/************************************************************************************************** +* CNC_Handle_ControlGroup_Request +* +* In: +* +* +* Out: +* +* +* +* History: +**************************************************************************************************/ +extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (request_type) + { + case CONTROL_GROUP_REQUEST_CREATE: + DLLExportClass::Create_Control_Group(control_group_index); + break; + case CONTROL_GROUP_REQUEST_TOGGLE: + DLLExportClass::Toggle_Control_Group_Selection(control_group_index); + break; + case CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION: + DLLExportClass::Add_To_Control_Group(control_group_index); + break; + + } +} + + + + + +/************************************************************************************************** +* DLLExportClass::Get_Layer_State -- Get a snapshot of the sidebar state +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCSidebarStruct *sidebar = (CNCSidebarStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*sidebar); // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + sidebar->Credits = 0; + sidebar->CreditsCounter = 0; + sidebar->Tiberium = 0; + sidebar->MaxTiberium = 0; + sidebar->PowerProduced = 0; + sidebar->PowerDrained = 0; + sidebar->RepairBtnEnabled = false; + sidebar->SellBtnEnabled = false; + sidebar->RadarMapActive = false; + sidebar->MissionTimer = Scen.MissionTimer.Is_Active() ? (Scen.MissionTimer / TICKS_PER_SECOND) : -1; + + sidebar->UnitsKilled = 0; + sidebar->BuildingsKilled = 0; + sidebar->UnitsLost = 0; + sidebar->BuildingsLost = 0; + sidebar->TotalHarvestedCredits = 0; + + if (PlayerPtr) { + sidebar->Credits = PlayerPtr->Credits; + sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Current; // Timed display + // sidebar->CreditsCounter = PlayerPtr->VisibleCredits.Credits; // Actual + sidebar->Tiberium = PlayerPtr->Tiberium; + sidebar->MaxTiberium = PlayerPtr->Capacity; + sidebar->PowerProduced = PlayerPtr->Power; + sidebar->PowerDrained = PlayerPtr->Drain; + + sidebar->RepairBtnEnabled = PlayerPtr->BScan > 0; + sidebar->SellBtnEnabled = PlayerPtr->BScan > 0; + sidebar->RadarMapActive = PlayerPtr->Radar == RADAR_ON; + + + // A. Get the DestroyedBuildings and DestroyedInfantry stats if they are available at this point + if (PlayerPtr->DestroyedBuildings) { + for ( int index = 0; index < PlayerPtr->DestroyedBuildings->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedBuildings->Get_Unit_Total( index ); + sidebar->BuildingsKilled += count; + } + } + if (PlayerPtr->DestroyedInfantry) { + for ( int index = 0; index < PlayerPtr->DestroyedInfantry->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedInfantry->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedUnits) { + for ( int index = 0; index < PlayerPtr->DestroyedUnits->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedUnits->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + if (PlayerPtr->DestroyedAircraft) { + for ( int index = 0; index < PlayerPtr->DestroyedAircraft->Get_Unit_Count(); index ++ ) + { + unsigned int count = (unsigned int) PlayerPtr->DestroyedAircraft->Get_Unit_Total( index ); + sidebar->UnitsKilled += count; // Includes Infantry, Vehicles, Aircraft + } + } + + // B. If the DestroyedBuildings and DestroyedInfantry stats seemed to be unvailable, this is another way to do it + // 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 + // We can't just do it this way for everything, as it does not work for all cases + if (sidebar->BuildingsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->BuildingsKilled += PlayerPtr->BuildingsKilled[ house_index ]; + } + } + if (sidebar->UnitsKilled == 0) + { + for (unsigned int house_index = 0; house_index < HOUSE_COUNT; house_index ++) + { + sidebar->UnitsKilled += PlayerPtr->UnitsKilled[ house_index ]; // Includes Infantry, Vehicles, Aircraft + } + } + + + + sidebar->UnitsLost = PlayerPtr->UnitsLost; + sidebar->BuildingsLost = PlayerPtr->BuildingsLost; + sidebar->TotalHarvestedCredits = PlayerPtr->HarvestedCredits; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = Map.Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = Map.Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = Map.Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = Map.Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(Map.Column[c].Buildables[b].BuildableType, Map.Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60; + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + + bool isbusy = false; + + switch (Map.Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + isbusy |= Units.Avail() <= 0; + sidebar_entry.Type = UNIT_TYPE; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + isbusy |= Aircraft.Avail() <= 0; + sidebar_entry.Type = AIRCRAFT_TYPE; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + sidebar_entry.Fake = build_type->IsFake; + } + break; + + case RTTI_VESSELTYPE: + sidebar_entry.Type = VESSEL_TYPE; + isbusy = (PlayerPtr->VesselFactory != -1); + isbusy |= Vessels.Avail() <= 0; + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + Fill_Sidebar_Entry_From_Special_Weapon(sidebar_entry, super_weapon, (SpecialWeaponType)Map.Column[c].Buildables[b].BuildableID); + break; + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + int fnumber = Map.Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + + } else { + + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + /* + ** Get each sidebar column + */ + for (int c = 0 ; c < 2 ; c++) { + + sidebar->EntryCount[c] = context_sidebar->Column[c].BuildableCount; + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + + CNCSidebarEntryStruct &sidebar_entry = sidebar->Entries[entry_index++]; + if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) { + return false; + } + + sidebar_entry.AssetName[0] = 0; + sidebar_entry.Type = UNKNOWN; + sidebar_entry.BuildableID = context_sidebar->Column[c].Buildables[b].BuildableID; + sidebar_entry.BuildableType = context_sidebar->Column[c].Buildables[b].BuildableType; + sidebar_entry.BuildableViaCapture = context_sidebar->Column[c].Buildables[b].BuildableViaCapture; + sidebar_entry.Fake = false; + + TechnoTypeClass const * tech = Fetch_Techno_Type(context_sidebar->Column[c].Buildables[b].BuildableType, context_sidebar->Column[c].Buildables[b].BuildableID); + + sidebar_entry.SuperWeaponType = SW_NONE; + + if (tech) { + sidebar_entry.Cost = tech->Cost; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = tech->Time_To_Build(); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60; + strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + } else { + sidebar_entry.Cost = 0; + sidebar_entry.AssetName[0] = 0; + } + + SuperClass* super_weapon = nullptr; + bool isbusy = false; + + switch (context_sidebar->Column[c].Buildables[b].BuildableType) { + case RTTI_INFANTRYTYPE: + sidebar_entry.Type = INFANTRY_TYPE; + isbusy = (PlayerPtr->InfantryFactory != -1); + isbusy |= Infantry.Avail() <= 0; + break; + + case RTTI_UNITTYPE: + isbusy = (PlayerPtr->UnitFactory != -1); + isbusy |= Units.Avail() <= 0; + sidebar_entry.Type = UNIT_TYPE; + break; + + case RTTI_AIRCRAFTTYPE: + isbusy = (PlayerPtr->AircraftFactory != -1); + isbusy |= Aircraft.Avail() <= 0; + sidebar_entry.Type = AIRCRAFT_TYPE; + break; + + case RTTI_BUILDINGTYPE: + { + isbusy = (PlayerPtr->BuildingFactory != -1); + isbusy |= Buildings.Avail() <= 0; + sidebar_entry.Type = BUILDING_TYPE; + + const BuildingTypeClass* build_type = static_cast(tech); + sidebar_entry.PowerProvided = build_type->Power - build_type->Drain; + sidebar_entry.Fake = build_type->IsFake; + break; + } + + case RTTI_VESSELTYPE: + isbusy = (PlayerPtr->VesselFactory != -1); + isbusy |= Vessels.Avail() <= 0; + sidebar_entry.Type = VESSEL_TYPE; + break; + + default: + sidebar_entry.Type = UNKNOWN; + break; + + case RTTI_SPECIAL: + Fill_Sidebar_Entry_From_Special_Weapon(sidebar_entry, super_weapon, (SpecialWeaponType)sidebar_entry.BuildableID); + break; + } + + if (super_weapon != nullptr) + { + sidebar_entry.Progress = (float)super_weapon->Anim_Stage() / (float)SuperClass::ANIMATION_STAGES; + sidebar_entry.Completed = super_weapon->Is_Ready(); + sidebar_entry.Constructing = super_weapon->Anim_Stage() != SuperClass::ANIMATION_STAGES; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.PlacementListLength = 0; + sidebar_entry.PowerProvided = 0; + sidebar_entry.BuildTime = super_weapon->Get_Recharge_Time(); + } + else + { + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + FactoryClass * factory = NULL; + if (tech && fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + sidebar_entry.Completed = false; + sidebar_entry.Constructing = false; + sidebar_entry.ConstructionOnHold = false; + sidebar_entry.Progress = 0.0f; + sidebar_entry.Busy = isbusy; + sidebar_entry.PlacementListLength = 0; + + if (factory) { + if (factory->Is_Building()) { + sidebar_entry.Constructing = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + sidebar_entry.Completed = factory->Has_Completed(); + } + else { + sidebar_entry.Completed = factory->Has_Completed(); + + if (!sidebar_entry.Completed) + { + sidebar_entry.ConstructionOnHold = true; + sidebar_entry.Progress = (float)factory->Completion() / (float)FactoryClass::STEP_COUNT; + } + + if (sidebar_entry.Completed && sidebar_entry.Type == BUILDING_TYPE) { + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*)tech; + short const *occupy_list = building_type->Occupy_List(true); + if (occupy_list) { + while (*occupy_list != REFRESH_EOL && sidebar_entry.PlacementListLength < MAX_OCCUPY_CELLS) { + sidebar_entry.PlacementList[sidebar_entry.PlacementListLength] = *occupy_list; + sidebar_entry.PlacementListLength++; + occupy_list++; + } + } + } + } + } + } + } + } + } + } + } + + + + return true; +} + + +void DLLExportClass::Convert_Action_Type(ActionType type, ObjectClass* object, TARGET target, DllActionTypeEnum& dll_type) +{ + switch (type) + { + case ACTION_NONE: + default: + dll_type = DAT_NONE; + break; + case ACTION_MOVE: + dll_type = DAT_MOVE; + break; + case ACTION_NOMOVE: + dll_type = DAT_NOMOVE; + break; + case ACTION_ENTER: + dll_type = DAT_ENTER; + break; + case ACTION_SELF: + dll_type = DAT_SELF; + break; + case ACTION_ATTACK: + if (Target_Legal(target) && (object != NULL) && object->Is_Techno() && ((TechnoClass*)object)->In_Range(target, 0)) { + dll_type = DAT_ATTACK; + } + else { + dll_type = DAT_ATTACK_OUT_OF_RANGE; + } + break; + case ACTION_GUARD_AREA: + dll_type = DAT_GUARD; + break; + case ACTION_HARVEST: + dll_type = DAT_ATTACK; + break; + case ACTION_SELECT: + case ACTION_TOGGLE_SELECT: + dll_type = DAT_SELECT; + break; + case ACTION_CAPTURE: + dll_type = DAT_CAPTURE; + break; + case ACTION_DAMAGE: + dll_type = DAT_DAMAGE; + break; + case ACTION_SABOTAGE: + dll_type = DAT_SABOTAGE; + break; + case ACTION_HEAL: + dll_type = DAT_HEAL; + break; + case ACTION_TOGGLE_PRIMARY: + dll_type = DAT_TOGGLE_PRIMARY; + break; + case ACTION_NO_DEPLOY: + dll_type = DAT_CANT_DEPLOY; + break; + case ACTION_GREPAIR: + dll_type = DAT_REPAIR; + break; + case ACTION_NO_GREPAIR: + dll_type = DAT_CANT_REPAIR; + break; + } +} + + +void DLLExportClass::Convert_Special_Weapon_Type(SpecialWeaponType weapon_type, DllSuperweaponTypeEnum& dll_weapon_type, char* weapon_name) +{ + switch (weapon_type) + { + case SPC_SONAR_PULSE: + dll_weapon_type = SW_SONAR_PULSE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_SonarPulse", 16); + } + break; + case SPC_NUCLEAR_BOMB: + dll_weapon_type = SW_NUKE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Nuke", 16); + } + break; + case SPC_CHRONOSPHERE: + dll_weapon_type = SW_CHRONOSPHERE; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Chrono", 16); + } + break; + case SPC_PARA_BOMB: + dll_weapon_type = SW_PARA_BOMB; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_ParaBomb", 16); + } + break; + case SPC_PARA_INFANTRY: + dll_weapon_type = SW_PARA_INFANTRY; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_ParaInfantry", 16); + } + break; + case SPC_SPY_MISSION: + dll_weapon_type = SW_SPY_MISSION; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_SpyMission", 16); + } + break; + case SPC_IRON_CURTAIN: + dll_weapon_type = SW_IRON_CURTAIN; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_IronCurtain", 16); + } + break; + case SPC_GPS: + dll_weapon_type = SW_GPS; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_GPS", 16); + } + break; + case SPC_CHRONO2: + dll_weapon_type = SW_CHRONOSPHERE_DESTINATION; + if (weapon_name != NULL) + { + strncpy(weapon_name, "SW_Chrono2", 16); + } + break; + default: + dll_weapon_type = SW_UNKNOWN; + if (weapon_name != NULL) + { + weapon_name[0] = '\0'; + } + break; + } +} + + +void DLLExportClass::Fill_Sidebar_Entry_From_Special_Weapon(CNCSidebarEntryStruct& sidebar_entry_out, SuperClass*& super_weapon_out, SpecialWeaponType weapon_type) +{ + sidebar_entry_out.Type = SPECIAL; + + switch (weapon_type) + { + case SPC_SONAR_PULSE: + case SPC_NUCLEAR_BOMB: + case SPC_CHRONOSPHERE: + case SPC_PARA_BOMB: + case SPC_PARA_INFANTRY: + case SPC_SPY_MISSION: + case SPC_IRON_CURTAIN: + case SPC_GPS: + case SPC_CHRONO2: + Convert_Special_Weapon_Type(weapon_type, sidebar_entry_out.SuperWeaponType, sidebar_entry_out.AssetName); + break; + default: + sidebar_entry_out.SuperWeaponType = SW_UNKNOWN; + sidebar_entry_out.Type = UNKNOWN; + super_weapon_out = nullptr; + return; + } + + super_weapon_out = &(PlayerPtr->SuperWeapon[weapon_type]); +} + +static const int _map_width_shift_bits = 7; + +void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + static const FacingType _scan_facings[] = { + FACING_E, + FACING_S, + FACING_W, + FACING_N + }; + + memset(placement_distance, 255U, MAP_CELL_TOTAL); + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++) { + CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); + BuildingClass* base = (BuildingClass*)Map[cell].Cell_Find_Object(RTTI_BUILDING); + if ((base && base->House->Class->House == PlayerPtr->Class->House && base->Class->IsBase) || + ((placement_type->IsWall || ((Map[cell].Smudge != SMUDGE_NONE) && SmudgeTypeClass::As_Reference(Map[cell].Smudge).IsBib)) && + Map[cell].Owner == PlayerPtr->Class->House)) { + placement_distance[cell] = 0U; + CELL startcell = cell; + for (unsigned char distance = 1U; distance <= (placement_type->Adjacent + 1U); distance++) { + startcell = Adjacent_Cell(startcell, FACING_NW); + CELL scancell = startcell; + for (int i = 0; i < ARRAY_SIZE(_scan_facings); i++) { + CELL nextcell = scancell; + for (unsigned char scan = 0U; scan <= (distance * 2U); scan++) { + scancell = nextcell; + if (Map.In_Radar(scancell)) { + placement_distance[scancell] = min(placement_distance[scancell], distance); + } + nextcell = Adjacent_Cell(scancell, _scan_facings[i]); + } + } + } + } + } + } +} + +void Recalculate_Placement_Distances() +{ + DLLExportClass::Recalculate_Placement_Distances(); +} + +void DLLExportClass::Recalculate_Placement_Distances() +{ + if (PlacementType[CurrentLocalPlayerIndex] != NULL) { + Calculate_Placement_Distances(PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + } +} + +/************************************************************************************************** +* DLLExportClass::Get_Placement_State -- Get a snapshot of legal validity of placing a structure on all map cells +* +* In: +* +* Out: +* +* +* +* History: 2/4/2019 3:11PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Placement_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (PlacementType[CurrentLocalPlayerIndex] == NULL) { + return false; + } + + CNCPlacementInfoStruct *placement_info = (CNCPlacementInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*placement_info); // Base amount needed. Will need more depending on how many entries there are + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + memory_needed += map_cell_width * map_cell_height * sizeof(CNCPlacementCellInfoStruct); + + if (memory_needed + 128 >= buffer_size) { + return false; + } + + placement_info->Count = map_cell_width * map_cell_height; + + int index = 0; + for (int y=0 ; y < map_cell_height ; y++) { + for (int x=0 ; x < map_cell_width ; x++) { + + CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits); + + bool pass = Passes_Proximity_Check(cell, PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]); + + CellClass * cellptr = &Map[cell]; + bool clear = cellptr->Is_Clear_To_Build(PlacementType[CurrentLocalPlayerIndex]->Speed); + + CNCPlacementCellInfoStruct &placement_cell_info = placement_info->CellInfo[index++]; + placement_cell_info.PassesProximityCheck = pass; + placement_cell_info.GenerallyClear = clear; + } + } + + Map.ZoneOffset = 0; + + return true; +} + + +bool DLLExportClass::Passes_Proximity_Check(CELL cell_in, BuildingTypeClass *placement_type, unsigned char* placement_distance) +{ + + /* + ** Scan through all cells that the building foundation would cover. If any adjacent + ** cells to these are of friendly persuasion, then consider the proximity check to + ** have been a success. + */ + short const *occupy_list = placement_type->Occupy_List(true); + + while (*occupy_list != REFRESH_EOL) { + + CELL center_cell = cell_in + *occupy_list++; + + if (!Map.In_Radar(center_cell)) { + return false; + } + + if (placement_distance[center_cell] <= (placement_type->Adjacent + 1)) { + return true; + } + } + + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Start_Construction -- Start sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Hold_Construction -- Pause sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Hold_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_HOLD_CONSTRUCTION, player_id, buildable_type, buildable_id); +} + +/************************************************************************************************** +* DLLExportClass::Cancel_Construction -- Stop sidebar construction +* +* In: +* +* Out: +* +* +* +* History: 6/12/2019 JAS +**************************************************************************************************/ +bool DLLExportClass::Cancel_Construction(uint64 player_id, int buildable_type, int buildable_id) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) + { + return false; + } + + return Cancel_Placement(player_id, buildable_type, buildable_id) && + ((GAME_TO_PLAY == GAME_NORMAL) ? + Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id) : + MP_Construction_Action(SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, player_id, buildable_type, buildable_id)); +} + + + +/************************************************************************************************** +* DLLExportClass::Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = PlayerPtr->Fetch_Factory((RTTIType)buildable_type); + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && factory != NULL) { + return(false); + } + + if (factory) { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + } + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + Queue_AI(); + + return true; + } + } + } + } + } + } + } + return false; +} + + + + +/************************************************************************************************** +* DLLExportClass::MP_Construction_Action -- Reproduce actions on the sidebar +* +* In: +* +* Out: +* +* +* +* History: 3/26/2019 1:02PM - ST +**************************************************************************************************/ +bool DLLExportClass::MP_Construction_Action(SidebarRequestEnum construction_action, uint64 player_id, int buildable_type, int buildable_id) +{ + + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** Most of this code is validating that the game is in the correct state to be able to act on a sidebar icon + ** + */ + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && genfactory != -1) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return(false); + } + + if (factory) { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + On_Speech(PlayerPtr, VOX_CANCELED); // Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + break; + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + if (factory->Is_Building()) + { + On_Speech(PlayerPtr, VOX_SUSPENDED); // Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, (RTTIType)buildable_type, buildable_id)); + } + break; + + default: + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + return false; + } + else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + // TO_DO + //Map.IsTargettingMode = true; + } + else { + + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + On_Speech(PlayerPtr, VOX_NO_FACTORY); //Speak(VOX_NO_FACTORY); // "Cannot Comply" + OutList.Add(EventClass(EventClass::ABANDON, (RTTIType)buildable_type, buildable_id)); + //Speak(VOX_NO_FACTORY); + } + else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + if (construction_action == SIDEBAR_REQUEST_START_PLACEMENT) { + if (DLLExportClass::Legacy_Render_Enabled()) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + Unselect_All(); + } + } + } + else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + //OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + + } + else { + + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (construction_action == SIDEBAR_REQUEST_START_CONSTRUCTION) { + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + return true; + } + } + } + break; + } + + } else { + + switch (construction_action) + { + case SIDEBAR_REQUEST_CANCEL_CONSTRUCTION: + case SIDEBAR_REQUEST_HOLD_CONSTRUCTION: + break; + + default: + /* + ** + */ + if ((RTTIType)buildable_type == RTTI_INFANTRYTYPE) + { + On_Speech(PlayerPtr, VOX_TRAINING); // Speak(VOX_TRAINING); + } + else + { + On_Speech(PlayerPtr, VOX_BUILDING); // Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, (RTTIType)buildable_type, buildable_id)); + + /* + ** Execute immediately so we get the sidebar feedback + */ + DLLExportClass::Glyphx_Queue_AI(); + + return true; + } + } + } + } + } + } + } + return false; +} + + + + + +/************************************************************************************************** +* DLLExportClass::Start_Placement -- Start placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 1/29/2019 11:37AM - ST +**************************************************************************************************/ +bool DLLExportClass::Start_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + Recalculate_Placement_Distances(); + + if (GAME_TO_PLAY == GAME_NORMAL) { + return Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + return MP_Construction_Action(SIDEBAR_REQUEST_START_PLACEMENT, player_id, buildable_type, buildable_id); + } + } + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Cancel_Placement -- Cancel placing a completed structure +* +* In: +* +* Out: +* +* +* +* History: 2/7/2019 10:52AM - ST +**************************************************************************************************/ +bool DLLExportClass::Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + PlacementType[CurrentLocalPlayerIndex] = NULL; + + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.IsTargettingMode = SPC_NONE; + Map.Set_Cursor_Shape(0); + + return true; +} + + + +/************************************************************************************************** +* DLLExportClass::Place -- Place a completed structure down +* +* In: +* +* Out: +* +* +* +* History: 2/6/2019 11:51AM - ST +**************************************************************************************************/ +bool DLLExportClass::Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + static const int _map_width_shift_bits = 7; + + BuildingClass *building = Get_Pending_Placement_Object(player_id, buildable_type, buildable_id); + + if (building) { + + TechnoTypeClass const * tech = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + + if (tech) { + BuildingTypeClass *building_type = (BuildingTypeClass*) tech; + //short const *occupy_list = building_type->Get_Occupy_List(true); + + PlacementType[CurrentLocalPlayerIndex] = building_type; + + /* + ** The cell coordinates passed in will be relative to the playable area that the client knows about + */ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << _map_width_shift_bits); + + /* + ** Call the place directly instead of queueing it, so we can evaluate the return code. + */ + if (PlayerPtr->Place_Object(building->What_Am_I(), cell + Map.ZoneOffset)) { + PlacementType[CurrentLocalPlayerIndex] = NULL; + } + } + } + return true; + +} + + + + +BuildingClass *DLLExportClass::Get_Pending_Placement_Object(uint64 player_id, int buildable_type, int buildable_id) +{ + /* + ** + ** Based on SidebarClass::StripClass::SelectClass::Action + ** + ** + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < Map.Column[c].BuildableCount ; b++) { + if (Map.Column[c].Buildables[b].BuildableID == buildable_id) { + if (Map.Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = Map.Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + + } else { + + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + + + SidebarGlyphxClass *context_sidebar = DLLExportClass::Get_Current_Context_Sidebar(); + + for (int c = 0 ; c < 2 ; c++) { + + /* + ** Each production slot in the column + */ + for (int b=0 ; b < context_sidebar->Column[c].BuildableCount ; b++) { + if (context_sidebar->Column[c].Buildables[b].BuildableID == buildable_id) { + if (context_sidebar->Column[c].Buildables[b].BuildableType == buildable_type) { + + + int genfactory = -1; + switch (buildable_type) { + case RTTI_INFANTRYTYPE: + genfactory = PlayerPtr->InfantryFactory; + break; + + case RTTI_UNITTYPE: + genfactory = PlayerPtr->UnitFactory; + break; + + case RTTI_AIRCRAFTTYPE: + genfactory = PlayerPtr->AircraftFactory; + break; + + case RTTI_BUILDINGTYPE: + genfactory = PlayerPtr->BuildingFactory; + break; + + default: + genfactory = -1; + break; + } + + int fnumber = context_sidebar->Column[c].Buildables[b].Factory; + int spc = 0; + ObjectTypeClass const * choice = NULL; + + if (buildable_type != RTTI_SPECIAL) { + choice = Fetch_Techno_Type((RTTIType)buildable_type, buildable_id); + } else { + spc = buildable_id; + } + + FactoryClass * factory = NULL; + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + if (spc == 0 && choice) { + if (fnumber == -1 && genfactory != -1) { + return(NULL); + } + + if (factory) { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + //Map.IsTargettingMode = true; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, buildable_type, buildable_id)); + On_Speech(PlayerPtr, VOX_NO_FACTORY); // Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + return (BuildingClass*)pending; + //PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } + } + } + } + } + } + } + } + } + } + } + } + return NULL; +} + +/************************************************************************************************** +* DLLExportClass::Place_Super_Weapon +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y) +{ + if (buildable_type != RTTI_SPECIAL) + { + return false; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + SpecialWeaponType weapon_type = (SpecialWeaponType)buildable_id; + + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, weapon_type, cell)); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Create_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Create_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 2); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Add_To_Control_Group +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Add_To_Control_Group(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 1); + + return true; +} + +/************************************************************************************************** +* DLLExportClass::Toggle_Control_Group_Selection +* +* History: +**************************************************************************************************/ +bool DLLExportClass::Toggle_Control_Group_Selection(unsigned char control_group_index) +{ + Handle_Team(control_group_index, 0); + + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Get_Shroud_State -- Get a snapshot of the shroud for the given player +* +* In: +* +* Out: +* +* +* +* History: 4/12/2019 3:44PM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCShroudStruct *shroud = (CNCShroudStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*shroud) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++) { + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCShroudEntryStruct); + if (memory_needed >= buffer_size) { + return false; + } + + int xpixel; + int ypixel; + + Map.Coord_To_Pixel(coord, xpixel, ypixel); + + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + CNCShroudEntryStruct &shroud_entry = shroud->Entries[entry_index]; + + shroud_entry.IsVisible = cellptr->Is_Visible(PlayerPtr); + shroud_entry.IsMapped = cellptr->Is_Mapped(PlayerPtr); + shroud_entry.IsJamming = cellptr->Is_Jamming(PlayerPtr); + //shroud_entry.IsVisible = cellptr->IsVisible; + //shroud_entry.IsMapped = cellptr->IsMapped; + shroud_entry.ShadowIndex = -1; + + if (shroud_entry.IsMapped) { + if (!shroud_entry.IsVisible) { + shroud_entry.ShadowIndex = (char)Map.Cell_Shadow(cell, PlayerPtr); + } + } + + entry_index++; + } + } + + shroud->Count = entry_index; + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Occupier_State -- Get the occupier state for this player +* +* In: +* +* Out: +* +* +* +* History: 10/25/2019 - SKY +**************************************************************************************************/ +bool DLLExportClass::Get_Occupier_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + UNREFERENCED_PARAMETER(player_id); + + CNCOccupierHeaderStruct* occupiers = (CNCOccupierHeaderStruct*)buffer_in; + CNCOccupierEntryHeaderStruct* entry = reinterpret_cast(occupiers + 1U); + occupiers->Count = 0; + + unsigned int memory_needed = sizeof(CNCOccupierHeaderStruct); + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + for (int y = 0; y < map_cell_height; y++) { + for (int x = 0; x < map_cell_width; x++, occupiers->Count++) { + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + int occupier_count = 0; + ObjectClass* optr = cellptr->Cell_Occupier(); + while (optr != NULL) { + occupier_count++; + optr = optr->Next; + } + + memory_needed += sizeof(CNCOccupierEntryHeaderStruct) + (sizeof(CNCOccupierObjectStruct) * occupier_count); + if (memory_needed >= buffer_size) { + return false; + } + + CNCOccupierObjectStruct* occupier = reinterpret_cast(entry + 1U); + entry->Count = 0; + + optr = cellptr->Cell_Occupier(); + for (int i = 0; i < occupier_count; i++, occupier++, entry->Count++) { + CNCObjectStruct object; + Convert_Type(optr, object); + occupier->Type = object.Type; + occupier->ID = object.ID; + optr = optr->Next; + } + + entry = reinterpret_cast(occupier + 1U); + } + } + + return true; +} + + + + +/************************************************************************************************** +* DLLExportClass::Get_Player_Info_State -- Get the multiplayer info for this player +* +* In: +* +* Out: +* +* +* +* History: 4/22/2019 10:33AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Player_Info_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return false; + } + + CNCPlayerInfoStruct *player_info = (CNCPlayerInfoStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*player_info) + 32; // A little extra for no reason + + if (memory_needed >= buffer_size) { + return false; + } + + player_info->GlyphxPlayerID = 0; + + if (PlayerPtr == NULL) { + return false;; + } + + if (Session.Players.Count() > CurrentLocalPlayerIndex) { + strncpy(&player_info->Name[0], Session.Players[CurrentLocalPlayerIndex]->Name, MPLAYER_NAME_MAX); + } + + player_info->Name[MPLAYER_NAME_MAX - 1] = 0; // Make sure it's terminated + player_info->House = PlayerPtr->Class->House; + player_info->AllyFlags = PlayerPtr->Get_Ally_Flags(); + + if (Session.Players.Count() > CurrentLocalPlayerIndex) { + player_info->ColorIndex = Session.Players[CurrentLocalPlayerIndex]->Player.Color; + } else { + player_info->ColorIndex = (PlayerPtr->Class->House == HOUSE_USSR) ? 2 : 1; // Fudge to a sensible color in campaign; 2 = red, 1 = blue + } + + player_info->GlyphxPlayerID = player_id; + player_info->HomeCellX = Cell_X(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->HomeCellY = Cell_Y(MultiplayerStartPositions[CurrentLocalPlayerIndex]); + player_info->IsDefeated = PlayerPtr->IsDefeated; + + // Can see other players' power if ally (except for the player themself) or spying on a power plant + // Can see other players' money if spying on a resource building + player_info->SpiedPowerFlags = 0U; + player_info->SpiedMoneyFlags = 0U; + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* house = Houses.Ptr(i); + if ((house != nullptr) && house->IsActive && (house != PlayerPtr) && house->Is_Ally(PlayerPtr)) { + player_info->SpiedPowerFlags |= 1U << house->Class->House; + } + } + for (int i = 0; i < Buildings.Count(); ++i) { + BuildingClass* building = Buildings.Ptr(i); + if ((building != nullptr) && building->IsActive && (building->Spied_By() & (1U << PlayerPtr->Class->House))) { + if ((*building == STRUCT_POWER) || (*building == STRUCT_ADVANCED_POWER)) { + player_info->SpiedPowerFlags |= 1U << building->House->Class->House; + } else if ((*building == STRUCT_REFINERY) || (*building == STRUCT_STORAGE)) { + player_info->SpiedMoneyFlags |= 1U << building->House->Class->House; + } + } + } + + // Populate spied data + for (char house = 0; house < MAX_HOUSES; ++house) { + HouseClass* hptr = HouseClass::As_Pointer((HousesType)house); + if ((hptr != nullptr) && hptr->IsActive) { + if (player_info->SpiedPowerFlags & (1U << house)) { + player_info->SpiedInfo[house].Power = hptr->Power; + player_info->SpiedInfo[house].Drain = hptr->Drain; + } + if (player_info->SpiedMoneyFlags & (1U << house)) { + player_info->SpiedInfo[house].Money = hptr->Available_Money(); + } + } + } + + // Populate selection info + if (CurrentObject.Count() > 0) { + CNCObjectStruct object; + Convert_Type(CurrentObject[0], object); + player_info->SelectedID = object.ID; + player_info->SelectedType = object.Type; + + const int left = Map.MapCellX; + const int right = Map.MapCellX + Map.MapCellWidth - 1; + const int top = Map.MapCellY; + const int bottom = Map.MapCellY + Map.MapCellHeight - 1; + + // Use first object with a weapon, or first object if none + ObjectClass* action_object = nullptr; + for (int i = 0; i < CurrentObject.Count(); ++i) { + ObjectClass* object = CurrentObject[i]; + if (object->Is_Techno()) { + TechnoClass* techno = (TechnoClass*)object; + if (techno->Techno_Type_Class()->PrimaryWeapon != NULL || techno->Techno_Type_Class()->SecondaryWeapon != NULL) { + action_object = object; + break; + } + } + } + if (action_object == nullptr) { + action_object = CurrentObject[0]; + } + + int index = 0; + for (int y = top; y <= bottom; ++y) { + for (int x = left; x <= right; ++x, ++index) { + 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]); + } + } + + player_info->ActionWithSelectedCount = Map.MapCellWidth * Map.MapCellHeight; + } + else { + player_info->SelectedID = -1; + player_info->SelectedType = UNKNOWN; + player_info->ActionWithSelectedCount = 0U; + } + + // Screen shake + player_info->ScreenShake = PlayerPtr->ScreenShakeTime; + + // Radar jammed + player_info->IsRadarJammed = Map.Get_Jammed(PlayerPtr); + + return true; +}; + + + + +/************************************************************************************************** +* DLLExportClass::Get_Dynamic_Map_State -- Get a snapshot of the smudges and overlays on the terrain +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 10:45AM - ST +**************************************************************************************************/ +bool DLLExportClass::Get_Dynamic_Map_State(uint64 player_id, unsigned char *buffer_in, unsigned int buffer_size) +{ + /* + ** Get the player for this... + */ + player_id; + + static int _call_count = 0; + + CNCDynamicMapStruct *dynamic_map = (CNCDynamicMapStruct*) buffer_in; + + unsigned int memory_needed = sizeof(*dynamic_map) + 256; // Base amount needed. Will need more depending on how many entries there are + + int entry_index = 0; + + /* + ** + ** Based loosely on DisplayClass::Redraw_Icons + ** + ** + */ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_WIDTH) { + map_cell_height++; + } + + int cell_index = 0; + + bool debug_output = false; + //if (_call_count == 20) { + //debug_output = true; + //} + + // Need to ignore view constraints for dynamic map updates, so the radar map + // has the latest tiberium state for cells outside the tactical view + DLLExportClass::Adjust_Internal_View(true); + + for (int y = 0 ; y < map_cell_height ; y++) { + for (int x = 0 ; x < map_cell_width ; x++) { + CELL cell = XY_Cell(map_cell_x+x, map_cell_y+y); + COORDINATE coord = Cell_Coord(cell) & 0xFF00FF00L; + + memory_needed += sizeof(CNCDynamicMapEntryStruct) * 2; + if (memory_needed >= buffer_size) { + return false; + } + + /* + ** Only cells flagged to be redraw are examined. + */ + //if (In_View(cell) && Is_Cell_Flagged(cell)) { + int xpixel; + int ypixel; + + if (Map.Coord_To_Pixel(coord, xpixel, ypixel)) { + CellClass * cellptr = &Map[Coord_Cell(coord)]; + + /* + ** If there is a portion of the underlying icon that could be visible, + ** then draw it. Also draw the cell if the shroud is off. + */ + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER || cellptr->IsMapped || Debug_Unshroud) { + Cell_Class_Draw_It(dynamic_map, entry_index, cellptr, xpixel, ypixel, debug_output); + } + + /* + ** If any cell is not fully mapped, then flag it so that the shadow drawing + ** process will occur. Only draw the shadow if Debug_Unshroud is false. + */ + //if (!cellptr->IsMapped && !Debug_Unshroud) { + // IsShadowPresent = true; + //} + } + //} + } + } + + if (entry_index) { + _call_count++; + } + + dynamic_map->Count = entry_index; + + dynamic_map->VortexActive = ChronalVortex.Is_Active(); + dynamic_map->VortexX = Coord_X(ChronalVortex.Get_Position()); + dynamic_map->VortexY = Coord_Y(ChronalVortex.Get_Position()); + dynamic_map->VortexWidth = Pixel_To_Lepton(64); + dynamic_map->VortexHeight = Pixel_To_Lepton(64); + + return true; +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Cell_Class_Draw_It -- Go through the motions of drawing a cell to get the smudge and overlay info +* +* In: +* +* Out: +* +* +* +* History: 2/8/2019 11:09AM - ST +**************************************************************************************************/ +void DLLExportClass::Cell_Class_Draw_It(CNCDynamicMapStruct *dynamic_map, int &entry_index, CellClass *cell_ptr, int xpixel, int ypixel, bool debug_output) +{ + /* + ** + ** Based on CellClass::Draw_It and SmudgeTypeClass::Draw_It + ** + ** + */ + + CELL cell = cell_ptr->Cell_Number(); + + /* + ** Redraw any smudge. + */ + if (cell_ptr->Smudge != SMUDGE_NONE) { + //SmudgeTypeClass::As_Reference(Smudge).Draw_It(x, y, SmudgeData); + + const SmudgeTypeClass &smudge_type = SmudgeTypeClass::As_Reference(cell_ptr->Smudge); + + if (smudge_type.Get_Image_Data() != NULL) { + +if (debug_output) { + IsTheaterShape = true; + Debug_Write_Shape_Type(&smudge_type, 0); + IsTheaterShape = false; +} + + CNCDynamicMapEntryStruct &smudge_entry = dynamic_map->Entries[entry_index++]; + + strncpy(smudge_entry.AssetName, smudge_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + smudge_entry.Type = (short) cell_ptr->Smudge; + smudge_entry.Owner = (char)cell_ptr->Owner; + smudge_entry.DrawFlags = SHAPE_WIN_REL; // Looks like smudges are drawn top left + smudge_entry.PositionX = xpixel; + smudge_entry.PositionY = ypixel; + smudge_entry.Width = Get_Build_Frame_Width(smudge_type.Get_Image_Data()); + smudge_entry.Height = Get_Build_Frame_Height(smudge_type.Get_Image_Data()); + smudge_entry.CellX = Cell_X(cell); + smudge_entry.CellY = Cell_Y(cell); + smudge_entry.ShapeIndex = cell_ptr->SmudgeData; + smudge_entry.IsSmudge = true; + smudge_entry.IsOverlay = false; + smudge_entry.IsResource = false; + smudge_entry.IsSellable = false; + smudge_entry.IsTheaterShape = true; // Smudges are always theater-specific + smudge_entry.IsFlag = false; + } + } + + /* + ** Draw the overlay object. + */ + if (cell_ptr->Overlay != OVERLAY_NONE) { + //OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(Overlay); + //IsTheaterShape = (bool)otype.IsTheater; + //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); + //IsTheaterShape = false; + + const OverlayTypeClass &overlay_type = OverlayTypeClass::As_Reference(cell_ptr->Overlay); + + if (overlay_type.Get_Image_Data() != NULL) { + + CNCDynamicMapEntryStruct &overlay_entry = dynamic_map->Entries[entry_index++]; + + +if (debug_output) { + IsTheaterShape = (bool)overlay_type.IsTheater; + Debug_Write_Shape_Type(&overlay_type, 0); + IsTheaterShape = false; +} + + strncpy(overlay_entry.AssetName, overlay_type.IniName, CNC_OBJECT_ASSET_NAME_LENGTH); + overlay_entry.Type = (short)cell_ptr->Overlay; + overlay_entry.Owner = (char)cell_ptr->Owner; + overlay_entry.DrawFlags = SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST; // Looks like overlays are drawn centered and translucent + overlay_entry.PositionX = xpixel + (CELL_PIXEL_W>>1); + overlay_entry.PositionY = ypixel + (CELL_PIXEL_H>>1); + overlay_entry.Width = Get_Build_Frame_Width(overlay_type.Get_Image_Data()); + overlay_entry.Height = Get_Build_Frame_Height(overlay_type.Get_Image_Data()); + overlay_entry.CellX = Cell_X(cell); + overlay_entry.CellY = Cell_Y(cell); + overlay_entry.ShapeIndex = cell_ptr->OverlayData; + overlay_entry.IsSmudge = false; + overlay_entry.IsOverlay = true; + overlay_entry.IsResource = overlay_entry.Type >= OVERLAY_GOLD1 && overlay_entry.Type <= OVERLAY_GEMS4; + overlay_entry.IsSellable = (overlay_entry.Type >= OVERLAY_SANDBAG_WALL && overlay_entry.Type <= OVERLAY_WOOD_WALL) || overlay_entry.Type == OVERLAY_FENCE; + overlay_entry.IsTheaterShape = (bool)overlay_type.IsTheater; + overlay_entry.IsFlag = false; + } + } + + + if (cell_ptr->IsFlagged) { + + const void* image_data = MFCD::Retrieve("FLAGFLY.SHP"); + if (image_data != NULL) { + + CNCDynamicMapEntryStruct &flag_entry = dynamic_map->Entries[entry_index++]; + + strncpy(flag_entry.AssetName, "FLAGFLY", CNC_OBJECT_ASSET_NAME_LENGTH); + flag_entry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + flag_entry.Type = -1; + flag_entry.Owner = cell_ptr->Owner; + flag_entry.DrawFlags = SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING; + flag_entry.PositionX = xpixel + (ICON_PIXEL_W / 2); + flag_entry.PositionY = ypixel + (ICON_PIXEL_H / 2); + flag_entry.Width = Get_Build_Frame_Width(image_data); + flag_entry.Height = Get_Build_Frame_Height(image_data); + flag_entry.CellX = Cell_X(cell); + flag_entry.CellY = Cell_Y(cell); + flag_entry.ShapeIndex = Frame % 14; + flag_entry.IsSmudge = false; + flag_entry.IsOverlay = false; + flag_entry.IsResource = false; + flag_entry.IsSellable = false; + flag_entry.IsTheaterShape = false; + flag_entry.IsFlag = true; + } + + } + +} + + + + +/************************************************************************************************** +* DLLExportClass::Glyphx_Queue_AI -- Special queue processing for Glyphx multiplayer mode +* +* In: +* +* Out: +* +* +* +* History: 3/12/2019 10:52AM - ST +**************************************************************************************************/ +void DLLExportClass::Glyphx_Queue_AI(void) +{ + + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + OutList.Next(); + } + + /* + ** Based on Execute_DoList in queue.cpp + ** + ** The events have the ID of the player encoded in them, so no special per-player processing should be needed. + ** When the event is created, the 'local player' is assumed to be the originator of the event, so PlayerPtr will need + ** to be swapped out to represent the real originating player prior to any events being created as a result of GlyphX input + ** + ** ST - 3/12/2019 10:51AM + */ + + for (int i = 0; i < MULTIPLAYER_COUNT; i++) { + + HousesType house; + HouseClass *housep; + + house = Session.Players[i]->Player.ID; + + housep= HouseClass::As_Pointer (house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!housep){ + continue; + } + + if (!housep->IsHuman){ + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (int j = 0; j < DoList.Count; j++) { + + if (!DoList[j].IsExecuted && (unsigned)Frame >= DoList[j].Frame) { + DoList[j].Execute(); + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + } + } + } + + + + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + while (DoList.Count) { + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + } + else { + break; + } + } + +} + +CarryoverClass Test_CC(CarryoverObjectStruct* cptr) +{ + CarryoverClass cc; + + cc.RTTI = (RTTIType)cptr->RTTI; + cc.Type.Building = (StructType)cptr->Type; //This works regardless of what the RTTI-type and the enum type, because they're all enums. - LLL + cc.Cell = (CELL)cptr->Cell; + cc.House = (HousesType)cptr->House; + cc.Strength = cptr->Strength; + + return cc; +} + + +/************************************************************************************************** +* DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars +* +* +* +* History: 11/8/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Store_Carryover_Objects() +{ + if (EventCallback == NULL || Scen.IsToCarryOver == false) { + return; + } + + CarryoverObjectStruct* carryover_list = 0; + CarryoverObjectStruct* carryover_list_tail = 0; + + /* + ** Record all objects, that are to be part of the carry over set, into the carry over list. + */ + for (int building_index = 0; building_index < Buildings.Count(); building_index++) { + BuildingClass * building = Buildings.Ptr(building_index); + + if (building && !building->IsInLimbo && building->Strength > 0) { + CarryoverClass carryover = CarryoverClass(building); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + UnitClass * unit = Units.Ptr(unit_index); + + if (unit && !unit->IsInLimbo && unit->Strength > 0) { + CarryoverClass carryover = CarryoverClass(unit); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Unit; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + InfantryClass * infantry = Infantry.Ptr(infantry_index); + + if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { + CarryoverClass carryover = CarryoverClass(infantry); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + VesselClass * vessel = Vessels.Ptr(vessel_index); + + if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { + CarryoverClass carryover = CarryoverClass(vessel); + CarryoverObjectStruct* cptr = new CarryoverObjectStruct(); + if (cptr) { + cptr->RTTI = carryover.RTTI; + cptr->Type = (int)carryover.Type.Building; + cptr->House = carryover.House; + cptr->Cell = carryover.Cell; + cptr->Strength = carryover.Strength; + + CarryoverClass cc = Test_CC(cptr); + cc; + + if (!carryover_list_tail) { + carryover_list = cptr; + carryover_list_tail = cptr; + } + else { + carryover_list_tail->Next = cptr; + carryover_list_tail = cptr; + } + } + } + } + + //Make & Send Event + EventCallbackStruct event; + event.EventType = CALLBACK_EVENT_STORE_CARRYOVER_OBJECTS; + event.CarryoverObjects.CarryoverList = carryover_list; + EventCallback(event); + + //Delete the list + while (carryover_list) { + CarryoverObjectStruct* cptr = (CarryoverObjectStruct*)carryover_list->Next; + delete carryover_list; + carryover_list = cptr; + } +} + + +/************************************************************************************************** +* DLLExportClass::Reset_Sidebars -- Init the multiplayer sidebars +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:10PM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Sidebars(void) +{ + for (int i=0 ; i= Session.Players.Count()) { + continue; + } + if (Session.Players[i] == NULL) { + continue; + } + HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + MultiplayerSidebars[i].Init_Clear(player_ptr); + } +} + + + + +/************************************************************************************************** +* DLLExportClass::Set_Player_Context -- Switch the C&C local player context +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +bool DLLExportClass::Set_Player_Context(uint64 glyphx_player_id, bool force) +{ + /* + ** Context never needs to change in single player + */ + if (GAME_TO_PLAY == GAME_NORMAL) { + if (PlayerPtr) { + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + } + return true; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + for (int i=0 ; iPlayer.ID); + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + CurrentLocalPlayerIndex = i; + + Refresh_Player_Control_Flags(); + + return true; + } + } + + return false; +} + + + +/************************************************************************************************** +* DLLExportClass::Reset_Player_Context -- Clear out old player context data +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 10:36AM - ST +**************************************************************************************************/ +void DLLExportClass::Reset_Player_Context(void) +{ + CurrentLocalPlayerIndex = 0; + CurrentObject.Clear_All(); +} + + + +/************************************************************************************************** +* DLLExportClass::Refresh_Player_Control_Flags -- Set the IsPlayerControl flags so that the player +* in context has IsPlayerControl +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 10:36AM - ST +**************************************************************************************************/ +void DLLExportClass::Refresh_Player_Control_Flags(void) +{ + for (int i=0 ; iPlayer.ID); + + if (player_ptr) { + + if (i == CurrentLocalPlayerIndex && player_ptr->IsHuman) { + player_ptr->IsPlayerControl = true; + } else { + player_ptr->IsPlayerControl = false; + } + } + } +} + + +/************************************************************************************************** +* Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void Logic_Switch_Player_Context(ObjectClass *object) +{ + DLLExportClass::Logic_Switch_Player_Context(object); +} + + +/************************************************************************************************** +* DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void DLLExportClass::Logic_Switch_Player_Context(ObjectClass *object) +{ + if (object == NULL) { + return; + } + + /* + ** If it's not a techno, it can't be owned. + */ + if (!object->Is_Techno()) { + return; + } + + TechnoClass *tech = static_cast(object); + + //HousesType house = tech->House->Class->House; + DLLExportClass::Logic_Switch_Player_Context(tech->House); +} + + + +/************************************************************************************************** +* Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void Logic_Switch_Player_Context(HouseClass *object) +{ + DLLExportClass::Logic_Switch_Player_Context(object); +} + + +/************************************************************************************************** +* DLLExportClass::Logic_Switch_Player_Context -- Called when the internal game locic needs to switch player context +* +* In: +* +* Out: +* +* +* +* History: 4/17/2019 9:45AM - ST +**************************************************************************************************/ +void DLLExportClass::Logic_Switch_Player_Context(HouseClass *house) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + return; + } + + if (house == NULL) { + return; + } + + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + HousesType house_type = house->Class->House; + + for (int i=0 ; iPlayer.ID) { + + if (i == CurrentLocalPlayerIndex) { + return; + } + + PlayerPtr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + CurrentObject.Set_Active_Context(PlayerPtr->Class->House); + CurrentLocalPlayerIndex = i; + + Refresh_Player_Control_Flags(); + return; + } + } +} + + +/************************************************************************************************** +* DLLExportClass::Calculate_Start_Positions -- Calculate the initial view positions for the players +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 6/12/2019 3:00PM - ST +**************************************************************************************************/ +void DLLExportClass::Calculate_Start_Positions(void) +{ + if (GAME_TO_PLAY == GAME_NORMAL) { + MultiplayerStartPositions[0] = Scen.Views[0]; + return; + } + + HouseClass *player_ptr = PlayerPtr; + + ScenarioInit++; + COORDINATE old_tac = Map.TacticalCoord; + for (int i=0 ; i< MULTIPLAYER_COUNT; i++) { + PlayerPtr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (PlayerPtr) { + long x, y; + Map.Compute_Start_Pos(x, y); + MultiplayerStartPositions[i] = XY_Cell(x, y); + } + } + Map.TacticalCoord = old_tac; + ScenarioInit--; + + PlayerPtr = player_ptr; +} + + + +/************************************************************************************************** +* DLLExportClass::Get_GlyphX_Player_ID -- Get the external GlyphX player ID from the C&C house/player pointer +* Returns 0 in single player or if player ID isn't found +* +* In: +* +* Out: +* +* +* +* History: 4/22/2019 6:23PM - ST +**************************************************************************************************/ +__int64 DLLExportClass::Get_GlyphX_Player_ID(const HouseClass *house) +{ + /* + ** C&C relies a lot on PlayerPtr, which is a pointer to the 'local' player's house. Historically, in a peer-to-peer + ** multiplayer game, each player's PlayerPtr pointed to their own local player. + ** + ** Since much of the IO logic depends on PlayerPtr being the player performing the action, we need to set PlayerPtr + ** correctly depending on which player generated input or needs output + */ + + if (GAME_TO_PLAY == GAME_NORMAL) { + return 0; + } + + if (house == NULL) { + return 0; + } + + HousesType house_type = house->Class->House; + + for (int i=0 ; iPlayer.ID) { + return GlyphxPlayerIDs[i]; + } + } + + /* + ** Failure case. + */ + return 0; +} + + + + +/************************************************************************************************** +* DLLExportClass::Adjust_Internal_View -- Set the internal tactical view to encompass the input coordinates +* +* In: +* +* Out: +* +* +* +* History: 4/16/2019 3:00PM - ST +**************************************************************************************************/ +void DLLExportClass::Adjust_Internal_View(bool force_ignore_view_constraints) +{ + /* + ** When legacy rendering is disabled (especially in multiplayer) we can get input coordinates that + ** fall outside the engine's tactical view. In this case, we need to adjust the tactical view before the + ** input will behave as expected. + */ + + if (!force_ignore_view_constraints && Legacy_Render_Enabled()) { + /* + ** Render view should already be tracking the player's local view + */ + DisplayClass::IgnoreViewConstraints = false; + return; + } + + DisplayClass::IgnoreViewConstraints = true; +} + + + + + +/************************************************************************************************** +* DLLExportClass::Get_Current_Context_Sidebar -- Get the sidebar data for the current player context +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +SidebarGlyphxClass *DLLExportClass::Get_Current_Context_Sidebar(HouseClass *player_ptr) +{ + if (player_ptr) { + + for (int i=0 ; iPlayer.ID)) { + return &MultiplayerSidebars[i]; + } + } + } + return &MultiplayerSidebars[CurrentLocalPlayerIndex]; +} + + +SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr) +{ + return DLLExportClass::Get_Current_Context_Sidebar(player_ptr); +} + + +/************************************************************************************************** +* DLLExportClass::Repair_Mode -- Starts the player's repair mode. All it does here is unselect all units. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + Unselect_All(); +} + +/************************************************************************************************** +* DLLExportClass::Repair -- Repairs a specific building +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair(uint64 player_id, int object_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + TARGET target = Build_Target(RTTI_BUILDING, object_id); + if (target != TARGET_NONE) + { + BuildingClass* building = As_Building(target); + if (building) { + if (!building->IsActive) { + GlyphX_Debug_Print("DLLExportClass::Repair -- trying to repair a non-active building"); + } else { + + if (building->Can_Repair() && building->House.Is_Valid() && building->House->Class->House == PlayerPtr->Class->House) + { + building->Repair(-1); + } + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Sell_Mode -- Starts the player's sell mode. All it does here is unselect all units. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + Unselect_All(); +} + +/************************************************************************************************** +* DLLExportClass::Sell -- Sell's a player's speceific building. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Sell(uint64 player_id, int object_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + TARGET target = Build_Target(RTTI_BUILDING, object_id); + if (target != TARGET_NONE) + { + BuildingClass* building = As_Building(target); + if (building) { + if (!building->IsActive) { + GlyphX_Debug_Print("DLLExportClass::Sell -- trying to sell a non-active building"); + } else { + if (building->Can_Demolish() && building->House.Is_Valid() && building->House->Class->House == PlayerPtr->Class->House) + { + building->Sell_Back(1); + } + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Repair_Sell_Cancel -- Ends the player's repair or sell mode. Doesn't do anything right now. +* +* In: +* +* Out: +* +* +* +* History: 5/1/2019 - LLL +**************************************************************************************************/ +void DLLExportClass::Repair_Sell_Cancel(uint64 player_id) +{ + //OutputDebugString("Repair_Sell_Cancel\n"); +} + +/************************************************************************************************** +* DLLExportClass::Scatter_Selected -- Scatter the selected units +* +* In: +* +* Out: +* +* +* +* History: 10/15/2019 - SKY +**************************************************************************************************/ +void DLLExportClass::Scatter_Selected(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move()) { + OutList.Add(EventClass(EventClass::SCATTER, TargetClass(tech))); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Next_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Next_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Next_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Select_Previous_Unit +* +* History: 03.02.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Select_Previous_Unit(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + ObjectClass* obj = Map.Prev_Object(CurrentObject.Count() ? CurrentObject[0] : NULL); + if (obj) { + Unselect_All(); + obj->Select(); + + COORDINATE center = Map.Center_Map(); + Map.Flag_To_Redraw(true); + if (center) { + On_Center_Camera(PlayerPtr, Coord_X(center), Coord_Y(center)); + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Guard_Mode +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Guard_Mode(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && tech->Can_Player_Move() && tech->Can_Player_Fire()) { + OutList.Add(EventClass(TargetClass(tech), MISSION_GUARD_AREA)); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Selected_Stop +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Selected_Stop(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // Copied from RedAlert/Conquer.cpp - Keyboard_Process() with Options.KeyStop (VK_S) + if (CurrentObject.Count()) { + for (int index = 0; index < CurrentObject.Count(); index++) { + ObjectClass const * tech = CurrentObject[index]; + + if (tech != NULL && (tech->Can_Player_Move() || (tech->Can_Player_Fire() && tech->What_Am_I() != RTTI_BUILDING))) { + OutList.Add(EventClass(EventClass::IDLE, TargetClass(tech))); + } + } + } +} + +/************************************************************************************************** +* DLLExportClass::Units_Queued_Movement_Toggle +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +void DLLExportClass::Units_Queued_Movement_Toggle(uint64 player_id, bool toggle) +{ + // Currently Red Alert only + + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + if (PlayerPtr != NULL) + { + PlayerPtr->IsQueuedMovementToggle = toggle; + } +} + +/************************************************************************************************** +* DLLExportClass::Team_Units_Formation_Toggle_On +* +* History: 03.03.2020 MBL +**************************************************************************************************/ +// extern void Toggle_Formation(void); // Code\RedAlert\Conquer.cpp +extern char FormationEvent; // Code\RedAlert\Conquer.cpp +void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id) +{ + /* + ** Get the player for this... + */ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + // + // MBL 03.23.2020: Code here copied and modified from Toggle_Formation(), since obj->IsSelected is not supported + // Replacing with ObjectClass::Is_Selected_By_Player(HouseClass *player); + // + + int team = MAX_TEAMS; + long minx = 0x7FFFFFFFL, miny = 0x7FFFFFFFL; + long maxx = 0, maxy = 0; + int index; + bool setform = 0; + + // + // Recording support + // + if (Session.Record) { + FormationEvent = 1; + } + + /* + ** Find the first selected object that is a member of a team, and + ** register his group as the team we're using. Once we find the team + ** number, update the 'setform' flag to know whether we should be setting + ** the formation's offsets, or clearing them. If they currently have + ** illegal offsets (as in 0x80000000), then we're setting. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + if (team >= MAX_TEAMS) { + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == (int)0x80000000; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + } + + if (team >= MAX_TEAMS) { + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj) { + if (obj->House == PlayerPtr) { + if (!obj->IsInLimbo) { + if (obj->Is_Selected_By_Player(PlayerPtr)) { + team = obj->Group; + if (team < MAX_TEAMS) { + setform = obj->XFormOffset == 0x80000000UL; + TeamSpeed[team] = SPEED_WHEEL; + TeamMaxSpeed[team] = MPH_LIGHT_SPEED; + break; + } + } + } + } + } + } + } + + if (team >= MAX_TEAMS) return; + /* + ** Now that we have a team, let's go set (or clear) the formation offsets. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + TeamSpeed[team] = obj->Class->Speed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = (int)0x80000000; + } + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + obj->Mark(MARK_CHANGE); + if (setform) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + if (xc < minx) minx = xc; + if (xc > maxx) maxx = xc; + if (yc < miny) miny = yc; + if (yc > maxy) maxy = yc; + if (obj->Class->MaxSpeed < TeamMaxSpeed[team]) { + TeamMaxSpeed[team] = obj->Class->MaxSpeed; + } + } else { + obj->XFormOffset = obj->YFormOffset = 0x80000000UL; + } + } + } + + /* + ** All the units have been counted to find the bounding rectangle and + ** center of the formation, or to clear their offsets. Now, if we're to + ** set them into formation, proceed to do so. Otherwise, bail. + */ + if (setform) { + int centerx = (int)((maxx - minx)/2)+minx; + int centery = (int)((maxy - miny)/2)+miny; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * obj = Units.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * obj = Vessels.Ptr(index); + if (obj && !obj->IsInLimbo && obj->House == PlayerPtr && obj->Group == team ) { + long xc = Cell_X(Coord_Cell(obj->Center_Coord())); + long yc = Cell_Y(Coord_Cell(obj->Center_Coord())); + + obj->XFormOffset = xc - centerx; + obj->YFormOffset = yc - centery; + } + } + } +} + + +/************************************************************************************************** +* CNC_Handle_Debug_Request -- Process a debug input request +* +* In: +* +* +* Out: +* +* +* History: 1/7/2019 5:20PM - ST +**************************************************************************************************/ +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) +{ + if (!DLLExportClass::Set_Player_Context(player_id)) { + return; + } + + switch (debug_request_type) { + + case DEBUG_REQUEST_SPAWN_OBJECT: + DLLExportClass::Debug_Spawn_Unit(object_name, x, y, enemy); + break; + + case DEBUG_REQUEST_FORCE_CRASH: + Debug_Force_Crash = true; + break; + + case DEBUG_REQUEST_KILL_OBJECT: + if (strcmp(object_name, "HEAL") == 0) { + DLLExportClass::Debug_Heal_Unit(x, y); + } + else { + DLLExportClass::Debug_Kill_Unit(x, y); + } + break; + + case DEBUG_REQUEST_END_GAME: + { + bool win = true; + + const char lose[] = "LOSE"; + if (strcmp(lose, object_name) == 0) { + win = false; + } + + PlayerWins = win; + PlayerLoses = !win; + } + break; + + case DEBUG_REQUEST_UNSHROUD: + Debug_Unshroud = unshroud; + Map.Flag_To_Redraw(true); + break; + + case DEBUG_REQUEST_SUPERWEAPON_RECHARGE: + for (int i = 0; i < SPC_COUNT; ++i) + { + PlayerPtr->SuperWeapon[i].Forced_Charge(true); + } + break; + + case DEBUG_REQUEST_END_PRODUCTION: + { + for (int index = 0; index < Factories.Count(); index++) { + FactoryClass* factory = Factories.Ptr(index); + if (factory->Get_House()->IsHuman) { + Factories.Ptr(index)->Force_Complete(); + } + } + } + break; + + case DEBUG_REQUEST_ADD_RESOURCES: + { + if (object_name) { + int amount = atoi(object_name); + PlayerPtr->Credits += amount; + if (PlayerPtr->Credits < 0) { + PlayerPtr->Credits = 0; + } + } + } + break; + + case DEBUG_REQUEST_UNLOCK_BUILDABLES: + PlayerPtr->DebugUnlockBuildables = !PlayerPtr->DebugUnlockBuildables; + PlayerPtr->IsRecalcNeeded = true; + break; + + case DEBUG_REQUEST_SET_GLOBAL_FLAG: + Scen.Set_Global_To(x, true); + break; + + default: + break; + } +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: Object to unlimbo , x & y cell positions +* +* +* Out: True if unlimbo succeeded +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +bool DLLExportClass::Try_Debug_Spawn_Unlimbo(TechnoClass *techno, int &cell_x, int &cell_y) +{ + if (techno) { + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_right = map_cell_x + Map.MapCellWidth; + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + map_cell_right = min(map_cell_right, cell_x + 26); // Generally try to prevent the objects from spawing off the right of the screen + + int try_x = cell_x; + int try_y = cell_y; + + while (try_y < map_cell_bottom) { + + CELL cell = XY_Cell(try_x, try_y); + + if (techno->Unlimbo(Cell_Coord(cell))) { + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + + cell_x = try_x; + cell_y = try_y; + return true; + } + + try_x++; + if (try_x > map_cell_right - 2) { + try_x = cell_x; //map_cell_x + 2; + try_y++; + } + } + + cell_x = try_x; + cell_y = try_y; + } + return false; +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures +* +* In: +* +* Out: +* +* +* +* History: 1/22/2020 2:57PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_All(int x, int y) +{ + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + + int map_cell_bottom = map_cell_y + Map.MapCellHeight; + + int origin_x = map_cell_x + 2; + int origin_y = map_cell_y + 2; + + if (x != 0 || y != 0) { + CELL screen_cell = Coord_Cell(Map.Pixel_To_Coord(x, y)); + origin_x = Cell_X(screen_cell); + origin_y = Cell_Y(screen_cell); + } + + int try_x = origin_x; + int try_y = origin_y; + + HousesType house = PlayerPtr->Class->House; + + + for (StructType sindex = STRUCT_FIRST; sindex < STRUCT_COUNT; sindex++) { + BuildingTypeClass const & building_type = BuildingTypeClass::As_Reference(sindex); + + if (building_type.Get_Ownable() && building_type.Level != -1) { + + BuildingClass * building = new BuildingClass(building_type, house); + if (building) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(building, try_x, try_y)) { + break; + } + } + } + } + } + + + for (UnitType index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const & unit_type = UnitTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (unit_type.Get_Ownable() && unit_type.Level != -1) { + + UnitClass * unit = (UnitClass*) unit_type.Create_One_Of(PlayerPtr); + if (unit) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(unit, try_x, try_y)) { + break; + } + } + } + } + } + + + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + InfantryTypeClass const &infantry_type = InfantryTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (infantry_type.Get_Ownable() && infantry_type.Level != -1) { + + InfantryClass * inf = (InfantryClass*) infantry_type.Create_One_Of(PlayerPtr); + if (inf) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(inf, try_x, try_y)) { + break; + } + } + } + } + } + + for (AircraftType index = AIRCRAFT_FIRST; index < AIRCRAFT_COUNT; index++) { + AircraftTypeClass const &aircraft_type = AircraftTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (aircraft_type.Get_Ownable() && aircraft_type.Level != -1) { + + AircraftClass * air = (AircraftClass*) aircraft_type.Create_One_Of(PlayerPtr); + if (air) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(air, try_x, try_y)) { + break; + } + } + } + } + } + + + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + VesselTypeClass const &vessel_type = VesselTypeClass::As_Reference(index); + + /* + ** Fetch the sidebar cameo image for this building. + */ + if (vessel_type.Get_Ownable() && vessel_type.Level != -1) { + + VesselClass * boat = (VesselClass*) vessel_type.Create_One_Of(PlayerPtr); + if (boat) { + + try_x = origin_x; + try_y = origin_y; + + while (try_y < map_cell_bottom) { + if (Try_Debug_Spawn_Unlimbo(boat, try_x, try_y)) { + break; + } + } + } + } + } +} + + + + + + +/************************************************************************************************** +* DLLExportClass::Debug_Spawn_Unit -- Debug spawn a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 3/14/2019 3:20PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Spawn_Unit(const char *object_name, int x, int y, bool enemy) +{ + if (object_name == NULL) { + return; + } + + if (strlen(object_name) == 0) { + return; + } + + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + HousesType house = PlayerPtr->Class->House; + + /* + ** Place all? + */ + if (stricmp(object_name, "ALLOBJECTS") == 0) { + Debug_Spawn_All(x, y); + return; + } + + /* + ** If this is for the enemy, find the enemy with the most stuff + */ + + if (enemy) { + unsigned max_count = 0; + for (int i = 0; i < Houses.Count(); ++i) { + const HouseClass* player = Houses.Ptr(i); + const unsigned count = player->CurUnits + player->CurBuildings + player->CurInfantry + player->CurVessels + player->CurAircraft; + if (!PlayerPtr->Is_Ally(player) && (count >= max_count)) { + house = player->Class->House; + max_count = count; + } + } + } + + /* + ** What is this thing? + */ + + StructType structure_type = BuildingTypeClass::From_Name(object_name); + if (structure_type != STRUCT_NONE) { + + BuildingClass * building = new BuildingClass(structure_type, house); + if (building) { + if (!building->Unlimbo(Cell_Coord(cell))) { + delete building; + } + } + +#if (0) + Map.PendingObject = &BuildingTypeClass::As_Reference(structure_type); + Map.PendingHouse = PlayerPtr->ActLike; + Map.PendingObjectPtr = Map.PendingObject->Create_One_Of(PlayerPtr); + if (Map.PendingObjectPtr) { + Map.Set_Cursor_Pos(); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List()); + + //OutList.Add(EventClass(EventClass::PLACE, RTTI_BUILDING, (CELL)(cell + Map.ZoneOffset))); + } +#endif + return; + } + + + UnitType unit_type = UnitTypeClass::From_Name(object_name); + if (unit_type != UNIT_NONE) { + + UnitClass * unit = new UnitClass(unit_type, house); + if (unit) { + unit->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + + return; + } + + + InfantryType infantry_type = InfantryTypeClass::From_Name(object_name); + if (infantry_type != INFANTRY_NONE) { + + InfantryClass * inf = new InfantryClass(infantry_type, house); + if (inf) { + inf->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + AircraftType aircraft_type = AircraftTypeClass::From_Name(object_name); + if (aircraft_type != AIRCRAFT_NONE) { + + AircraftClass * air = new AircraftClass(aircraft_type, house); + if (air) { + air->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N); + } + return; + } + + VesselType vessel_type = VesselTypeClass::From_Name(object_name); + if (vessel_type != VESSEL_NONE) { + + VesselClass *boat = new VesselClass(vessel_type, house); + if (boat != NULL) { + + if (boat->Unlimbo(Map.Pixel_To_Coord(x, y), DIR_N)) { + boat->Enter_Idle_Mode(); + } else { + delete boat; + } + } + } + + OverlayType overlay_type = OverlayTypeClass::From_Name(object_name); + if (overlay_type != OVERLAY_NONE) + { + new OverlayClass(overlay_type, cell); + + return; + } + +} + + +/************************************************************************************************** +* DLLExportClass::Debug_Kill_Unit -- Kill a unit at the specified location +* +* In: +* +* Out: +* +* +* +* History: 8/19/2019 3:09PM - ST +**************************************************************************************************/ +void DLLExportClass::Debug_Kill_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + static const int debug_damage = 100; // 100 = Incremental damage + if (obj) { + int damage = debug_damage; + obj->Take_Damage(damage, 0, WARHEAD_HE, 0, true); + } else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + cellptr->Reduce_Tiberium(1); + } + if (optr->IsWall) { + Map[cell].Reduce_Wall(debug_damage); + } + } + if (cellptr->TType == TEMPLATE_BRIDGE1 || cellptr->TType == TEMPLATE_BRIDGE2 || + cellptr->TType == TEMPLATE_BRIDGE1H || cellptr->TType == TEMPLATE_BRIDGE2H || + cellptr->TType == TEMPLATE_BRIDGE_1A || cellptr->TType == TEMPLATE_BRIDGE_1B || + cellptr->TType == TEMPLATE_BRIDGE_2A || cellptr->TType == TEMPLATE_BRIDGE_2B || + cellptr->TType == TEMPLATE_BRIDGE_3A || cellptr->TType == TEMPLATE_BRIDGE_3B) { + Map.Destroy_Bridge_At(cell); + } + } + } +} + +void DLLExportClass::Debug_Heal_Unit(int x, int y) +{ + COORDINATE coord = Map.Pixel_To_Coord(x, y); + CELL cell = Coord_Cell(coord); + + CellClass * cellptr = &Map[cell]; + + if (cellptr) { + ObjectClass *obj = cellptr->Cell_Object(); + if (obj) { + obj->Strength = obj->Class_Of().MaxStrength; + } + else { + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + if (optr->IsTiberium) { + const int cellcount = (int)FACING_COUNT + 1; + CellClass* cells[cellcount]; + cells[0] = cellptr; + for (FacingType index = FACING_N; index < FACING_COUNT; index++) { + cells[(int)index + 1] = &cellptr->Adjacent_Cell(index); + } + + for (int index = 0; index < cellcount; index++) { + CellClass * newcell = cells[index]; + + if (newcell != NULL) { + if (newcell->Can_Tiberium_Germinate()) { + switch (cellptr->Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + new OverlayClass(Random_Pick(OVERLAY_GOLD1, OVERLAY_GOLD4), newcell->Cell_Number()); + newcell->OverlayData = 0; + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + new OverlayClass(Random_Pick(OVERLAY_GEMS1, OVERLAY_GEMS4), newcell->Cell_Number()); + newcell->OverlayData = 0; + break; + + default: + break; + } + } + else if (newcell->Land_Type() == LAND_TIBERIUM) { + switch (newcell->Overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + newcell->OverlayData = MIN(newcell->OverlayData + 1, 11); + newcell->Recalc_Attributes(); + newcell->Redraw_Objects(); + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + newcell->OverlayData = MIN(newcell->OverlayData + 1, 2); + newcell->Recalc_Attributes(); + newcell->Redraw_Objects(); + break; + + default: + break; + } + } + } + } + } + } + } + } +} + + + + + +/************************************************************************************************** +* DLLExportClass::Legacy_Render_Enabled -- Is the legacy rendering enabled? +* +* In: +* +* Out: +* +* +* +* History: 4/15/2019 5:46PM - ST +**************************************************************************************************/ +bool DLLExportClass::Legacy_Render_Enabled(void) +{ + if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) { + unsigned int num_humans = 0U; + for (int i = 0; i < MULTIPLAYER_COUNT; ++i) { + HouseClass *player_ptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_ptr && player_ptr->IsHuman) { + if (++num_humans > 1) break; + } + } + return num_humans < 2; + } + + //return false; + return true; +} + + +/************************************************************************************************** +* DLLExportClass::Computer_Message -- Replacement for original Computer_Message function +* +* In: +* +* Out: +* +* +* +* History: 1/27/2020 1:42PM - ST +**************************************************************************************************/ +void DLLExportClass::Computer_Message(bool last_player_taunt) +{ + HousesType house; + HouseClass *ptr; + + HouseClass *ai_players[MAX_PLAYERS]; + int ai_player_count = 0; + + /*------------------------------------------------------------------------ + Find the computer house that the message will be from + ------------------------------------------------------------------------*/ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + MULTIPLAYER_COUNT); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + ai_players[ai_player_count++] = ptr; + } + + if (ai_player_count) { + int ai_player_index = 0; + if (ai_player_count > 1) { + ai_player_index = IRandom(0, ai_player_count - 1); + } + + int taunt_index; + if (last_player_taunt) { + taunt_index = 13; + } else { + taunt_index = IRandom(0,12); + } + + On_Message(ai_players[ai_player_index], "", 15.0f, MESSAGE_TYPE_COMPUTER_TAUNT, taunt_index); + } +} + + +/************************************************************************************************** +* DLLExportClass::Set_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Set_Special_Key_Flags(unsigned char special_key_flags) +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = special_key_flags; +} + +/************************************************************************************************** +* DLLExportClass::Clear_Special_Key_Flags -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +void DLLExportClass::Clear_Special_Key_Flags() +{ + SpecialKeyFlags[CurrentLocalPlayerIndex] = 0; +} + +/************************************************************************************************** +* DLLExportClass::Get_Input_Key_State -- +* +* In: +* +* Out: +* +* +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLLExportClass::Get_Input_Key_State(KeyNumType key) +{ + switch (key) + { + case KN_LCTRL: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_CTRL) != 0; + break; + case KN_LSHIFT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_SHIFT) != 0; + break; + case KN_LALT: + return (SpecialKeyFlags[CurrentLocalPlayerIndex] & INPUT_SPECIAL_KEY_ALT) != 0; + break; + default: + break; + }; + + return false; + +} + +/************************************************************************************************** +* Get_Input_Key_State +* +* History: 6/27/2019 - JAS +**************************************************************************************************/ +bool DLL_Export_Get_Input_Key_State(KeyNumType key) +{ + return DLLExportClass::Get_Input_Key_State(key); +} + + + + +bool DLLSave(Pipe &file) +{ + return DLLExportClass::Save(file); +} + +bool DLLLoad(Straw &file) +{ + return DLLExportClass::Load(file); +} + + +/************************************************************************************************** +* DLLExportClass::Save -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +bool DLLExportClass::Save(Pipe & pipe) +{ + /* + ** Version first + */ + unsigned int version = CNC_DLL_API_VERSION; + pipe.Put(&version, sizeof(version)); + + pipe.Put(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)); + + pipe.Put(GlyphxPlayerIDs, sizeof(GlyphxPlayerIDs)); + + pipe.Put(&GlyphXClientSidebarWidthInLeptons, sizeof(GlyphXClientSidebarWidthInLeptons)); + + pipe.Put(MPlayerIsHuman, sizeof(MPlayerIsHuman)); + + pipe.Put(MultiplayerStartPositions, sizeof(MultiplayerStartPositions)); + + pipe.Put(PlacementType, sizeof(PlacementType)); + + pipe.Put(&OverrideNewUnitsEnabled, sizeof(OverrideNewUnitsEnabled)); + + for (int i=0 ; iType; + } + } +} + + + +/************************************************************************************************** +* DLLExportClass::Decode_Pointers -- +* +* In: +* +* Out: +* +* +* +* History: 9/10/2019 10:24AM - ST +**************************************************************************************************/ +void DLLExportClass::Decode_Pointers(void) +{ + for (int i=0 ; i(PlacementType[i]); + PlacementType[i] = NULL; + if (type >= STRUCT_FIRST && type < STRUCT_COUNT) { + + TechnoTypeClass const * tech = Fetch_Techno_Type(RTTI_BUILDINGTYPE, type); + if (tech) { + BuildingTypeClass* build_type = (BuildingTypeClass*)(tech); + if (build_type) { + PlacementType[i] = build_type; + } + } + } + } + } +} + + +void DLL_Code_Pointers(void) +{ + DLLExportClass::Code_Pointers(); +} + +void DLL_Decode_Pointers(void) +{ + DLLExportClass::Decode_Pointers(); +} \ No newline at end of file diff --git a/code/redalert/dllinterface.h b/code/redalert/dllinterface.h new file mode 100644 index 0000000..04cbd9f --- /dev/null +++ b/code/redalert/dllinterface.h @@ -0,0 +1,924 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#pragma once + +#ifndef DLL_INTERFACE_H +#define DLL_INTERFACE_H + +struct CarryoverObjectStruct; + + + +/* +** DLL Interface version +** +** +** +*/ +#define CNC_DLL_API_VERSION 0x100 + + + + +#define MAX_EXPORT_CELLS (128 * 128) + +#ifdef TIBERIAN_DAWN +#define MAP_MAX_CELL_WIDTH 64 +#define MAP_MAX_CELL_HEIGHT 64 +#else +#define MAP_MAX_CELL_WIDTH 128 +#define MAP_MAX_CELL_HEIGHT 128 +#endif + + + + +/* +** Interface structs require stricter packing +** +** +*/ +#pragma pack(push) +#pragma pack(1) + + + + +/************************************************************************************** +** +** Game state request types +** +** +*/ +enum GameStateRequestEnum { + GAME_STATE_NONE, + GAME_STATE_STATIC_MAP, + GAME_STATE_DYNAMIC_MAP, + GAME_STATE_LAYERS, + GAME_STATE_SIDEBAR, + GAME_STATE_PLACEMENT, + GAME_STATE_SHROUD, + GAME_STATE_OCCUPIER, + GAME_STATE_PLAYER_INFO +}; + + + + +/************************************************************************************** +** +** Static map data (tiles) +** +** +*/ +struct CNCStaticCellStruct { + char TemplateTypeName[32]; + int IconNumber; +}; + + + +enum CnCTheaterType { + CNC_THEATER_NONE=-1, + CNC_THEATER_DESERT, + CNC_THEATER_JUNGLE, + CNC_THEATER_TEMPERATE, + CNC_THEATER_WINTER, + + CNC_THEATER_COUNT, + CNC_THEATER_FIRST=0 +}; + + +struct CNCMapDataStruct { + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + int OriginalMapCellX; + int OriginalMapCellY; + int OriginalMapCellWidth; + int OriginalMapCellHeight; + + CnCTheaterType Theater; + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + CNCStaticCellStruct StaticCells[MAX_EXPORT_CELLS]; +}; + + + + +/************************************************************************************** +** +** Object type enum +** +** +*/ +#define DLL_LAYER_COUNT 4 + +enum DllObjectTypeEnum { + UNKNOWN, + INFANTRY, + UNIT, + AIRCRAFT, + BUILDING, + TERRAIN, + ANIM, + BULLET, + OVERLAY, + SMUDGE, + OBJECT, + SPECIAL, + INFANTRY_TYPE, + UNIT_TYPE, + AIRCRAFT_TYPE, + BUILDING_TYPE, + VESSEL, + VESSEL_TYPE +}; + + + + + +/************************************************************************************** +** +** Object action types +** +** +*/ +enum DllActionTypeEnum : unsigned char { + DAT_NONE, + DAT_MOVE, + DAT_NOMOVE, + DAT_ENTER, + DAT_SELF, + DAT_ATTACK, + DAT_ATTACK_OUT_OF_RANGE, + DAT_GUARD, + DAT_SELECT, + DAT_CAPTURE, + DAT_SABOTAGE, + DAT_HEAL, + DAT_DAMAGE, + DAT_TOGGLE_PRIMARY, + DAT_CANT_DEPLOY, + DAT_REPAIR, + DAT_CANT_REPAIR +}; + + + + + +/************************************************************************************** +** +** Object state data +** +** +*/ + +#define MAX_OCCUPY_CELLS 36 +#define MAX_OBJECT_PIPS 18 +#define MAX_OBJECT_LINES 3 +#define MAX_HOUSES 32 + +struct CNCObjectLineStruct { + int X; + int Y; + int X1; + int Y1; + int Frame; + unsigned char Color; +}; + +#define CNC_OBJECT_ASSET_NAME_LENGTH 16 +struct CNCObjectStruct { + void *CNCInternalObjectPointer; + char TypeName[CNC_OBJECT_ASSET_NAME_LENGTH]; + char AssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + DllObjectTypeEnum Type; + int ID; + int BaseObjectID; + DllObjectTypeEnum BaseObjectType; + int PositionX; + int PositionY; + int Width; + int Height; + int Altitude; + int SortOrder; + int Scale; + int DrawFlags; + short MaxStrength; + short Strength; + unsigned short ShapeIndex; + unsigned short CellX; + unsigned short CellY; + unsigned short CenterCoordX; + unsigned short CenterCoordY; + short SimLeptonX; + short SimLeptonY; + unsigned char DimensionX; + unsigned char DimensionY; + unsigned char Rotation; + unsigned char MaxSpeed; + char Owner; + char RemapColor; + char SubObject; + bool IsSelectable; + unsigned int IsSelectedMask; + bool IsRepairing; + bool IsDumping; + bool IsTheaterSpecific; + unsigned int FlashingFlags; + unsigned char Cloak; + bool CanRepair; + bool CanDemolish; + bool CanDemolishUnit; + short OccupyList[MAX_OCCUPY_CELLS]; + int OccupyListLength; + int Pips[MAX_OBJECT_PIPS]; + int NumPips; + int MaxPips; + CNCObjectLineStruct Lines[MAX_OBJECT_LINES]; + int NumLines; + bool RecentlyCreated; + bool IsALoaner; + bool IsFactory; + bool IsPrimaryFactory; + bool IsDeployable; + bool IsAntiGround; + bool IsAntiAircraft; + bool IsSubSurface; + bool IsNominal; + bool IsDog; + bool IsIronCurtain; + bool IsInFormation; + bool CanMove[MAX_HOUSES]; + bool CanFire[MAX_HOUSES]; + bool CanDeploy; + bool CanHarvest; + bool CanPlaceBombs; + bool IsFixedWingedAircraft; + bool IsFake; + unsigned char ControlGroup; + unsigned int VisibleFlags; + unsigned int SpiedByFlags; + char ProductionAssetName[CNC_OBJECT_ASSET_NAME_LENGTH]; + const char* OverrideDisplayName; + DllActionTypeEnum ActionWithSelected[MAX_HOUSES]; + + static const unsigned int VISIBLE_FLAGS_ALL = 0xffffffff; +}; + +struct CNCObjectListStruct { + int Count; + CNCObjectStruct Objects[1]; // Variable length +}; + + + + +/************************************************************************************** +** +** Placement validity data +** +** Used to pass back info about tructure placement validity +*/ +struct CNCPlacementCellInfoStruct { + bool PassesProximityCheck; // If the structure was placed in this cell, does that satisfy the proximity check for the whole structure? + bool GenerallyClear; // Is this cell generally clear of obstructions that would prevent placement? +}; + +struct CNCPlacementInfoStruct { + int Count; + CNCPlacementCellInfoStruct CellInfo[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Sidebar/construction state data +** +** +*/ +enum DllSuperweaponTypeEnum { + SW_NONE, + SW_UNKNOWN, + + //TD values + SW_NUKE, + SW_AIR_STRIKE, + SW_ION_CANNON, + + //RA values + SW_SONAR_PULSE, + SW_CHRONOSPHERE, + SW_PARA_BOMB, + SW_PARA_INFANTRY, + SW_SPY_MISSION, + SW_IRON_CURTAIN, + SW_GPS, + SW_CHRONOSPHERE_DESTINATION +}; + +struct CNCSidebarEntryStruct { + char AssetName[16]; // CNC uses 8.3 filenames, so it shouldn't need to be bigger than 9 + int BuildableType; // This is the original buildable type that should be passed back if we want to start/cancel construction + int BuildableID; // This is the original buildable id that should be passed back if we want to start/cancel construction + DllObjectTypeEnum Type; // Type converted to shared enum + DllSuperweaponTypeEnum SuperWeaponType; + int Cost; // Cost to construct + int PowerProvided; // Power cost to construct + int BuildTime; // Cost to construct + float Progress; // Construction progress (0.0 - 1.0) + short PlacementList[MAX_OCCUPY_CELLS]; // Which cells this structure occupies for placement (if structure) + int PlacementListLength; // How many cells + bool Completed; // Construction has completed + bool Constructing; // Is it currently constructing + bool ConstructionOnHold; // Is the current construction on hold + bool Busy; // Is the associated factory busy + bool BuildableViaCapture; // Is this buildable due to the capture of a structure of a different faction. This will be false for captured structures of the same faction (ActLike) + bool Fake; // Is this a fake structure? +}; + + +struct CNCSidebarStruct { + int EntryCount[2]; // Counts for the left and right columns + int Credits; // Amount of currency available (excluding Tiberium) + int CreditsCounter; // Visible credits to display in the sidebar (includes count up/down logic) + int Tiberium; // Amount of Tiberium in reserve + int MaxTiberium; // Maximum amount of Tiberium storage available + int PowerProduced; + int PowerDrained; + int MissionTimer; + unsigned int UnitsKilled; // Total count of enemy units killed by this player; Includes Infantry, Vehicles, Aircraft + unsigned int BuildingsKilled; // Total count of enemy structures killed by this player + unsigned int UnitsLost; // Total count player-owned units killed/lost + unsigned int BuildingsLost; // Total count player-owned structures killed/lost + unsigned int TotalHarvestedCredits; // Complete total of gained credits over the match (does not include starting credits) + bool RepairBtnEnabled; + bool SellBtnEnabled; + bool RadarMapActive; + + CNCSidebarEntryStruct Entries[1]; // Variable length column entries +}; + + + +enum SidebarRequestEnum { + SIDEBAR_REQUEST_START_CONSTRUCTION, + SIDEBAR_REQUEST_HOLD_CONSTRUCTION, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION, + SIDEBAR_REQUEST_START_PLACEMENT, + SIDEBAR_REQUEST_PLACE, + SIDEBAR_CANCEL_PLACE, + SIDEBAR_CLICK_REPAIR, + SIDEBAR_REQUEST_ENABLE_QUEUE, + SIDEBAR_REQUEST_DISABLE_QUEUE, + SIDEBAR_REQUEST_START_CONSTRUCTION_MULTI, + SIDEBAR_REQUEST_CANCEL_CONSTRUCTION_MULTI +}; + +enum SuperWeaponRequestEnum { + SUPERWEAPON_REQUEST_PLACE_SUPER_WEAPON +}; +enum ControlGroupRequestEnum { + CONTROL_GROUP_REQUEST_CREATE, + CONTROL_GROUP_REQUEST_TOGGLE, + CONTROL_GROUP_REQUEST_ADDITIVE_SELECTION, +}; + + +/************************************************************************************** +** +** Input events sent into the DLL +** +** +*/ +enum InputRequestEnum { + INPUT_REQUEST_NONE, + INPUT_REQUEST_MOUSE_MOVE, + INPUT_REQUEST_MOUSE_LEFT_CLICK, + INPUT_REQUEST_MOUSE_RIGHT_DOWN, + INPUT_REQUEST_MOUSE_RIGHT_CLICK, + INPUT_REQUEST_MOUSE_AREA, + INPUT_REQUEST_MOUSE_AREA_ADDITIVE, + INPUT_REQUEST_SELL_AT_POSITION, + INPUT_REQUEST_SELECT_AT_POSITION, + INPUT_REQUEST_COMMAND_AT_POSITION, + INPUT_REQUEST_SPECIAL_KEYS +}; + + +/************************************************************************************** +** +** Structure Requests Repair, Sell +** +** +*/ +enum StructureRequestEnum { + INPUT_STRUCTURE_NONE, + INPUT_STRUCTURE_REPAIR_START, + INPUT_STRUCTURE_REPAIR, + INPUT_STRUCTURE_SELL_START, + INPUT_STRUCTURE_SELL, + INPUT_STRUCTURE_CANCEL, +}; + + +/************************************************************************************** +** +** Unit Requests Scatter, Select Next, Select Previous, Guard Mode, Stop +** +** +*/ +enum UnitRequestEnum { + INPUT_UNIT_NONE, + INPUT_UNIT_SCATTER, + INPUT_UNIT_SELECT_NEXT, + INPUT_UNIT_SELECT_PREVIOUS, + INPUT_UNIT_GUARD_MODE, + INPUT_UNIT_STOP, + INPUT_UNIT_FORMATION_TOGGLE, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_ON, // RA Only + INPUT_UNIT_QUEUED_MOVEMENT_OFF, // RA Only +}; + + +/************************************************************************************** +** +** Game Action Requests +** +** +*/ +enum GameRequestEnum { + INPUT_GAME_MOVIE_DONE, + INPUT_GAME_LOADING_DONE, +}; + + +/************************************************************************************** +** +** Special Keys +** +** +*/ +enum SpecialKeyRequestEnum { + INPUT_SPECIAL_KEY_CTRL = 0b00000001, + INPUT_SPECIAL_KEY_ALT = 0b00000010, + INPUT_SPECIAL_KEY_SHIFT = 0b00000100, +}; + + + +/************************************************************************************** +** +** Non-static map data. +** +** Per-cell smudges and overlays. Smudges are used for things like craters and structure bibs that draw under units. +** Overlays are things like walls and tiberium that can't move from the cell but aren't flat like smudges. +** +** +*/ +struct CNCDynamicMapEntryStruct { + char AssetName[16]; + int PositionX; + int PositionY; + int Width; + int Height; + short Type; + char Owner; + int DrawFlags; + unsigned char CellX; + unsigned char CellY; + unsigned char ShapeIndex; + bool IsSmudge; + bool IsOverlay; + bool IsResource; + bool IsSellable; + bool IsTheaterShape; + bool IsFlag; +}; + +struct CNCDynamicMapStruct { + bool VortexActive; + int VortexX; + int VortexY; + int VortexWidth; + int VortexHeight; + int Count; + CNCDynamicMapEntryStruct Entries[1]; // Variable length +}; + + + + + +/************************************************************************************** +** +** Event data +** +** Used to call back into the GlyphX engine for one-time events like sound effect triggers +** +** +*/ +enum EventCallbackType { + CALLBACK_EVENT_INVALID = -1, + CALLBACK_EVENT_SOUND_EFFECT = 0, + CALLBACK_EVENT_SPEECH, + CALLBACK_EVENT_GAME_OVER, + CALLBACK_EVENT_DEBUG_PRINT, + CALLBACK_EVENT_MOVIE, + CALLBACK_EVENT_MESSAGE, + CALLBACK_EVENT_UPDATE_MAP_CELL, + CALLBACK_EVENT_ACHIEVEMENT, + CALLBACK_EVENT_STORE_CARRYOVER_OBJECTS, + CALLBACK_EVENT_SPECIAL_WEAPON_TARGETTING, + CALLBACK_EVENT_BRIEFING_SCREEN, + CALLBACK_EVENT_CENTER_CAMERA, + CALLBACK_EVENT_PING +}; + + +struct GameOverMultiPlayerStatsStruct +{ + GameOverMultiPlayerStatsStruct() + : + GlyphXPlayerID( 0 ), + IsHuman( false ), + WasHuman( false ), + IsWinner( false ), + ResourcesGathered( 0 ), + TotalUnitsKilled( 0 ), + TotalStructuresKilled( 0 ), + Efficiency( 0 ), + Score( 0 ) + { + } + __int64 GlyphXPlayerID; + bool IsHuman; + bool WasHuman; + bool IsWinner; + int ResourcesGathered; + int TotalUnitsKilled; + int TotalStructuresKilled; + int Efficiency; // AKA Economy + int Score; +}; + +#define GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED 8 + + +enum EventCallbackMessageEnum { + MESSAGE_TYPE_DIRECT = 0, + MESSAGE_TYPE_PLAYER_DEFEATED, + MESSAGE_TYPE_COMPUTER_TAUNT, + MESSAGE_TYPE_PLAYER_DISCONNECTED +}; + +struct EventCallbackStruct { + + EventCallbackStruct::EventCallbackStruct(void) : EventType(CALLBACK_EVENT_INVALID), GlyphXPlayerID(0) { } + + EventCallbackType EventType; + + __int64 GlyphXPlayerID; + + union { + + struct SoundEffectEvent { + int SFXIndex; + int Variation; + int PixelX; + int PixelY; + int PlayerID; //TO_FIX + char SoundEffectName[ 16 ]; + int SoundEffectPriority; + int SoundEffectContext; + } SoundEffect; + + struct SpeechEvent { + int SpeechIndex; + int PlayerID; //TO_FIX + char SpeechName[ 16 ]; + } Speech; + + struct GameOverEvent { + bool Multiplayer; + // + // Single-player data + // + bool PlayerWins; //This should specify player id + const char* MovieName; + const char* MovieName2; + const char* MovieName3; + const char* MovieName4; + const char* AfterScoreMovieName; + int Score; + int Leadership; + int Efficiency; + int CategoryTotal; + int NODKilled; + int GDIKilled; + int CiviliansKilled; + int NODBuildingsDestroyed; + int GDIBuildingsDestroyed; + int CiviliansBuildingsDestroyed; + int RemainingCredits; + int SabotagedStructureType; + int TimerRemaining; + // + // Multi-player data + // + int MultiPlayerTotalPlayers; + GameOverMultiPlayerStatsStruct MultiPlayerPlayersData[ GAME_OVER_MULTIPLAYER_MAX_PLAYERS_TRACKED ]; + } GameOver; + + struct DebugPrintEvent { + const char *PrintString; + } DebugPrint; + + struct MovieEvent { + const char* MovieName; + int Theme; + bool Immediate; + } Movie; + + struct MessageEvent { + const char* Message; + float TimeoutSeconds; + EventCallbackMessageEnum MessageType; + __int64 MessageParam1; + } Message; + + struct UpdateMapCellEvent { + int CellX; + int CellY; + char TemplateTypeName[32]; + } UpdateMapCell; + + struct AchievementEvent { + const char* AchievementType; + const char* AchievementReason; + } Achievement; + + struct StoreCarryoverObjectsEvent { + const CarryoverObjectStruct* CarryoverList; + } CarryoverObjects; + + struct SpecialWeaponTargettingEvent { + int Type; + int ID; + char Name[16]; + DllSuperweaponTypeEnum WeaponType; + } SpecialWeaponTargetting; + + struct CenterCameraEvent { + int CoordX; + int CoordY; + } CenterCamera; + + struct PingEvent { + int CoordX; + int CoordY; + } Ping; + }; + +}; + + + + + + + + +/************************************************************************************** +** +** Multiplayer setup data +** +** Used to pass multiplayer setup info into the C&C code from the GlyphX engine +** +** +*/ + + + +struct CNCMultiplayerOptionsStruct { + //int MPlayerPrefColor; // preferred color index for this player + //int MPlayerColorIdx; // actual color index of this player + //CnCHousesType MPlayerHouse; // House of this player (GDI/NOD) + //unsigned char MPlayerLocalID; // ID of this player + int MPlayerCount; // # of human players in this game + int MPlayerBases; // 1 = bases are on for this scenario + int MPlayerCredits; // # credits everyone gets + int MPlayerTiberium; // 1 = tiberium enabled for this scenario + int MPlayerGoodies; // 1 = goodies enabled for this scenario + int MPlayerGhosts; // 1 = houses with no players will still play + int MPlayerSolo; // 1 = allows a single-player net game + int MPlayerUnitCount; // # units for non-base multiplayer scenarios + bool IsMCVDeploy; // MCV undeploys instead of selling + bool SpawnVisceroids; // Do visceroids spawn + bool EnableSuperweapons; // Are superweapons available + bool MPlayerShadowRegrow; + bool MPlayerAftermathUnits; + bool CaptureTheFlag; + bool DestroyStructures; // New early win condition via destroying all a player's structures +}; + + + +struct CNCSpiedInfoStruct { + int Power; + int Drain; + int Money; +}; + + +struct CNCPlayerInfoStruct { + char Name[64]; + unsigned char House; + int ColorIndex; + unsigned __int64 GlyphxPlayerID; + int Team; + int StartLocationIndex; + unsigned char HomeCellX; + unsigned char HomeCellY; + bool IsAI; + unsigned int AllyFlags; + bool IsDefeated; + unsigned int SpiedPowerFlags; + unsigned int SpiedMoneyFlags; + CNCSpiedInfoStruct SpiedInfo[MAX_HOUSES]; + int SelectedID; + DllObjectTypeEnum SelectedType; + DllActionTypeEnum ActionWithSelected[MAX_EXPORT_CELLS]; + unsigned int ActionWithSelectedCount; + unsigned int ScreenShake; + bool IsRadarJammed; +}; + + +// +enum GameRequestType { + GAME_REQUEST_MOVIE_DONE, +}; + + +/************************************************************************************** +** +** Rules configuration data +** +** +*/ +struct CNCDifficultyDataStruct +{ + float FirepowerBias; + float GroundspeedBias; + float AirspeedBias; + float ArmorBias; + float ROFBias; + float CostBias; + float BuildSpeedBias; + + float RepairDelay; + float BuildDelay; + + bool IsBuildSlowdown; + bool IsWallDestroyer; + bool IsContentScan; +}; + +struct CNCRulesDataStruct +{ + CNCDifficultyDataStruct Difficulties[3]; +}; + + +/************************************************************************************** +** +** Debug input interface +** +** +*/ + +enum DebugRequestEnum { + DEBUG_REQUEST_SPAWN_OBJECT, + DEBUG_REQUEST_END_GAME, + DEBUG_REQUEST_UNSHROUD, + DEBUG_REQUEST_SUPERWEAPON_RECHARGE, + DEBUG_REQUEST_KILL_OBJECT, + DEBUG_REQUEST_END_PRODUCTION, + DEBUG_REQUEST_ADD_RESOURCES, + DEBUG_REQUEST_UNLOCK_BUILDABLES, + DEBUG_REQUEST_FORCE_CRASH, + DEBUG_REQUEST_SET_GLOBAL_FLAG, +}; + + + + + + +/************************************************************************************** +** +** Shroud data. +** +** Per-cell shroud info +** +** +*/ +struct CNCShroudEntryStruct { + char ShadowIndex; + bool IsVisible; + bool IsMapped; + bool IsJamming; +}; + +struct CNCShroudStruct { + int Count; + CNCShroudEntryStruct Entries[1]; // Variable length +}; + + + + + + +/************************************************************************************** +** +** Occupier data. +** +** Per-cell occupier info +** +** +*/ +struct CNCOccupierObjectStruct { + DllObjectTypeEnum Type; + int ID; +}; + +struct CNCOccupierEntryHeaderStruct { + int Count; +}; + +struct CNCOccupierHeaderStruct { + int Count; +}; + + + + +/************************************************************************************** +** +** Carryover object. +** +** Used to store object data that persists between missions +** +** +*/ +struct CarryoverObjectStruct +{ + CarryoverObjectStruct() : Next(0) {} + + CarryoverObjectStruct* Next; + + int RTTI; + int Type; + int Cell; + int Strength; + int House; +}; + + + + +/* +** End of strict structure packing +** +** +*/ +#pragma pack(pop) + + + +#endif //DLL_INTERFACE_H \ No newline at end of file diff --git a/code/redalert/dllinterfaceeditor.cpp b/code/redalert/dllinterfaceeditor.cpp new file mode 100644 index 0000000..3ac370d --- /dev/null +++ b/code/redalert/dllinterfaceeditor.cpp @@ -0,0 +1,830 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//#include +#include + +#include "function.h" +#include "externs.h" +#include "DLLInterface.h" +#include "Gadget.h" +#include "defines.h" // VOC_COUNT, VOX_COUNT +#include "SidebarGlyphx.h" +#include "mixfile.h" +#include "ccini.H" + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map(char* cncdata_directory, char* house_name, int scenario_index, char* east_west, char* variant); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name(char* cncdata_directory, char* scenario_name); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map(); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory); +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points); + +int LoadScenario(); + +extern int DLL_Startup(const char * command_line); + +char EditorMapINIBuffer[SHAPE_BUFFER_SIZE]; +bool EditorMapInitialized = false; + +const static int EDITOR_COMMMAND_SUCCESS = 0; +const static int EDITOR_COMMMAND_FAILURE = 1; + +const char* CD1Path = "\\RED_ALERT\\CD1\\"; +const char* CD2Path = "\\RED_ALERT\\COUNTERSTRIKE\\"; +const char* CD3Path = "\\RED_ALERT\\AFTERMATH\\"; + +char RedAlertINI[_MAX_PATH]; + +/************************************************************************************************** +* CNC_Editor_Startup +* Initializes the system to allow map loading for the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Startup() +{ + /* + BlackPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + GamePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + OriginalPalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + WhitePalette = new(MEM_CLEAR | MEM_REAL) unsigned char[768]; + memset(WhitePalette, 63, 768); + + + Set_Palette(GamePalette); + + TheaterData = 0; + TheaterIcons = 0; + LowTheaterData = 0; + */ + + RunningFromEditor = true; + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Cleanup +* Cleans up systems initialized by the editor +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Cleanup() +{ + /* + if (BlackPalette) + { + delete[] BlackPalette; + } + + if (GamePalette) + { + delete[] GamePalette; + } + + if (OriginalPalette) + { + delete[] OriginalPalette; + } + if (WhitePalette) + { + delete[] WhitePalette; + } + */ + + return EDITOR_COMMMAND_SUCCESS; +} +/************************************************************************************************** +* CNC_Editor_Load_Mix_Files +* Loads all the Mix files for Tiberian Dawn +**************************************************************************************************/ +void CNC_Editor_Load_Mix_Files() +{ + const char* MixFileNames[] = + { + "MAIN.MIX", + "REDALERT.MIX", + "EXPAND2.MIX", + "EXPAND.MIX", + "HIRES1.MIX", + //"LORES1.MIX" + + "GENERAL.MIX", + + "LOCAL.MIX", + "HIRES.MIX", + "NCHIRES.MIX", + "CONQUER.MIX", + + "RUSSIAN.MIX", + "ALLIES.MIX", + + "SNOW.MIX", + "TEMPERAT.MIX", + "INTERIOR.MIX", + }; + + int count = sizeof(MixFileNames) / sizeof(MixFileNames[0]); + + for (int i = count - 1; i >= 0; --i) + { + MFCD::Free(MixFileNames[i]); + } + + for (int i = 0; i < count; ++i) + { + MFCD* file = new MFCD(MixFileNames[i], &FastKey); + file->Cache(); + + } + +} + +/************************************************************************************************** +* CNC_Editor_Setup_Content_Directory +* Sets up where the system should load map data from. +* +* cncdata_directory: path of the base CNC data directory +* CD1: if true, consider this disc 1, otherwise consider this disc 2. +**************************************************************************************************/ +void CNC_Editor_Setup_Content_Directory(char* cncdata_directory, int CD, char (&content_directory)[_MAX_PATH]) +{ + + switch (CD) + { + default: + case 1: + sprintf(content_directory, "%s%s", cncdata_directory, CD1Path); + break; + case 2: + sprintf(content_directory, "%s%s", cncdata_directory, CD2Path); + break; + case 3: + sprintf(content_directory, "%s%s", cncdata_directory, CD3Path); + break; + } + + //Setup red alert path + sprintf(RedAlertINI, "%sREDALERT.INI",content_directory); + + if (strlen(content_directory) != 0) { + CCFileClass::Clear_Search_Drives(); + CCFileClass::Reset_Raw_Path(); + char *dll_dir = strdup(content_directory); + CCFileClass::Set_Search_Drives(dll_dir); + free(dll_dir); + } +} + +/************************************************************************************************** +* CNC_Editor_Load_Map +* Loads the map with the given parameters. +* +* cncdata_directory: path of the base CNC data directory +* faction: the name of the faction we are loading the map for +* scenario_index: int scenario index +* east_west: +* variant: +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map( + char* cncdata_directory, + char* house_name, + int scenario_index, + char* east_west, + char* variant) +{ + + CNC_Editor_Clear_Map(); + + ScenarioPlayerType scen_player; + int CD = 1; + if (stricmp(house_name, "SPAIN") == 0) { + scen_player = SCEN_PLAYER_SPAIN; + Whom = HOUSE_GOOD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "GREECE") == 0 || stricmp(house_name, "ALLY") == 0) { + scen_player = SCEN_PLAYER_GREECE; + Whom = HOUSE_GOOD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "USSR") == 0) { + scen_player = SCEN_PLAYER_USSR; + Whom = HOUSE_BAD; + + if (scenario_index >= 40) + { + CD = 3; + } + else if (scenario_index >= 15) + { + CD = 2; + } + } + + if (stricmp(house_name, "MULTI") == 0) + { + scen_player = SCEN_PLAYER_MPLAYER; + Whom = HOUSE_MULTI1; + + if (scenario_index >= 25) + { + CD = 3; + } + + } + + if (stricmp(house_name, "JAPAN") == 0) + { + scen_player = SCEN_PLAYER_JP; + Whom = HOUSE_JP; + } + + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + + //DLL_Startup(command_line); + + Scen.Scenario = scenario_index; + ScenarioDirType scen_dir; + BuildLevel = 7; + + if (stricmp(east_west, "w") == 0) + { + scen_dir = SCEN_DIR_WEST; + } + else + { + scen_dir = SCEN_DIR_EAST; + } + + ScenarioVarType variant_enum; + + if (stricmp(variant, "b") == 0) + { + variant_enum = SCEN_VAR_B; + } + else if (stricmp(variant, "c") == 0) + { + variant_enum = SCEN_VAR_C; + } + else if (stricmp(variant, "d") == 0) + { + variant_enum = SCEN_VAR_D; + } + else + { + variant_enum = SCEN_VAR_A; + } + + Scen.Set_Scenario_Name(Scen.Scenario, scen_player, scen_dir, (ScenarioVarType)variant_enum); + + return LoadScenario(); +} + +/************************************************************************************************** +* CNC_Editor_Load_Map_By_Scenario_Name +* Loads the map with the given parameters. +* +* cncdata_directory: path of the base CNC data directory +* scenario_name: name of the scnario to load +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Load_Map_By_Scenario_Name( + char* cncdata_directory, + char* scenario_name) +{ + int CD = 3; // Always use the aftermath cd + if (strnlen(scenario_name, _MAX_PATH) >= 3 && (scenario_name[2] == 'a' || scenario_name[2] == 'A')) + { + // Ant missions are CD 2 + CD = 2; + } + + CNC_Editor_Clear_Map(); + + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory, CD, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + + //DLL_Startup(command_line); + + snprintf(Scen.ScenarioName, _MAX_FNAME + _MAX_EXT, "%s.ini", scenario_name); + + return LoadScenario(); +} + +int LoadScenario() +{ + CCFileClass file(Scen.ScenarioName); + + if (!file.Is_Available()) + { + return(EDITOR_COMMMAND_FAILURE); + } + + + CCINIClass ini; + int result = ini.Load(file,true); + if (result == 0) + { + return(EDITOR_COMMMAND_FAILURE); + } + + if (result == 2) + { + // if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { + /* + ** Make a special exception so that multiplayer maps from 1 through + ** 24 will not care if the message digest is in error. All other + ** maps will abort the scenario load. + */ + if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) + { + return(EDITOR_COMMMAND_FAILURE); + } + } + + const char * const BASIC = "Basic"; + NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); + + Map.One_Time(); + Map.Read_INI(ini); + + EditorMapInitialized = true; + + return EDITOR_COMMMAND_SUCCESS; +} + +/************************************************************************************************** +* CNC_Editor_Clear_Map +* Deletes the data for the currently loaded map. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Clear_Map() +{ + if (EditorMapInitialized) + { + Map.Init_Clear(); + EditorMapInitialized = false; + + return EDITOR_COMMMAND_SUCCESS; + } + else + { + return EDITOR_COMMMAND_FAILURE; + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Map_Stats +* Gets the stats for the currently loaded map +* +* map_width: out parameter storing the width of the map +* map_height: out parameter storing the height of the map +* theater: out paramter storing the theater of the map +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Map_Stats(int& map_width, int& map_height, int& theater) +{ + if (EditorMapInitialized) + { + map_width = Map.MapCellWidth + 1; + map_height = Map.MapCellHeight + 1; + theater = Scen.Theater; + return EDITOR_COMMMAND_SUCCESS; + } + + map_width = -1; + map_height = -1; + theater = -1; + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data_By_Index +* Get the data from the given cell. +* +* cell_index: The index of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data_By_Index(int cell_index, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + CELL index = (CELL)cell_index; + + CellClass * cellptr = &Map[index]; + + cell_name[0] = 0; + int icon = 0; + void *image_data = 0; + + char template_name[10]; + if (cellptr->Get_Template_Info(template_name, icon, image_data)) + { + snprintf(cell_name, cell_name_size, "%s-%04d", template_name, icon); + + template_type = cellptr->TType; + template_icon_index = icon; + + //TemplateTypeClass::As_Reference(ptr->TType). + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Data +* Get the data from the given cell. +* +* x,y: The corrdinates of the desired cell. +* cell_name: out buffer to be filled with the name of the given cell. +* cell_name_size: the size of the cell name buffer. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Data(int x, int y, char* cell_name, unsigned long cell_name_size, int& template_type, int& template_icon_index) +{ + if (!EditorMapInitialized) + { + return EDITOR_COMMMAND_FAILURE; + } + + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + + return CNC_Editor_Get_Cell_Data_By_Index((int)cell, cell_name, cell_name_size, template_type, template_icon_index); +} + +/************************************************************************************************** +* CNC_Editor_Get_Cell_Texture_Buffer +* +* x,y: +* out_width, out_height: dimensions of the outputed texture array +* out_texture_array: output array of unsigned chars storing the color data for the requested object, +* every 3 chars is a set of RGB values +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Cell_Texture_Buffer(int x, int y, int& out_width, int& out_height, SAFEARRAY*& out_texture_array) +{ + + int map_cell_x = Map.MapCellX; + int map_cell_y = Map.MapCellY; + int map_cell_width = Map.MapCellWidth; + int map_cell_height = Map.MapCellHeight; + + if (map_cell_x > 0) { + map_cell_x--; + map_cell_width++; + } + + if (map_cell_width < MAP_MAX_CELL_WIDTH) { + map_cell_width++; + } + + if (map_cell_y > 0) { + map_cell_y--; + map_cell_height++; + } + + if (map_cell_height < MAP_MAX_CELL_HEIGHT) { + map_cell_height++; + } + + CELL cell = XY_Cell(map_cell_x + x, map_cell_y + y); + CellClass * cellptr = &Map[cell]; + + char cell_name[_MAX_PATH]; + + int icon = 0; + void *image_data = 0; + if (cellptr->Get_Template_Info(cell_name, icon, image_data)) + { + GraphicBufferClass temp_gbuffer(24, 24); + GraphicViewPortClass temp_viewport(&temp_gbuffer, 0, 0, 24, 24); + + WindowList[WINDOW_CUSTOM][WINDOWX] = 0; + WindowList[WINDOW_CUSTOM][WINDOWY] = 0; + WindowList[WINDOW_CUSTOM][WINDOWWIDTH] = 24; + WindowList[WINDOW_CUSTOM][WINDOWHEIGHT] = 24; + + temp_viewport.Draw_Stamp(image_data, icon, 0, 0, NULL, WINDOW_CUSTOM); + + out_width = temp_viewport.Get_Width(); + out_height = temp_viewport.Get_Height(); + + const int COLOR_SIZE = 3; + + SAFEARRAYBOUND Bound; + Bound.lLbound = 0; + Bound.cElements = out_width * out_height * COLOR_SIZE; + + out_texture_array = SafeArrayCreate(VT_UI1, 1, &Bound); + + unsigned char* out_buffer; + + HRESULT hr = SafeArrayAccessData(out_texture_array, (void **)&out_buffer); + if (SUCCEEDED(hr)) + { + GraphicBufferClass* Graphic_Buffer = temp_viewport.Get_Graphic_Buffer(); + + int VP_Scan_Line = temp_viewport.Get_Width() + temp_viewport.Get_XAdd(); + + char * start_ptr; + start_ptr = (char *)Graphic_Buffer->Get_Buffer(); + start_ptr += ((temp_viewport.Get_YPos() * VP_Scan_Line) + temp_viewport.Get_XPos()); + + for (int y = 0; y < out_height; ++y) + { + unsigned char* scanline_ptr = (unsigned char*)start_ptr + y * VP_Scan_Line; + unsigned char* out_buffer_y_ptr = out_buffer + (y * out_width * COLOR_SIZE); + for (int x = 0; x < out_width; ++x) + { + unsigned char* pallete_index_ptr = scanline_ptr + x; + unsigned char* out_buffer_ptr = out_buffer_y_ptr + (x * COLOR_SIZE); + + int palette_index = (*pallete_index_ptr); + out_buffer_ptr[0] = ((unsigned char)GamePalette[palette_index].Red_Component()); + out_buffer_ptr[1] = ((unsigned char)GamePalette[palette_index].Green_Component()); + out_buffer_ptr[2] = ((unsigned char)GamePalette[palette_index].Blue_Component()); + } + } + + SafeArrayUnaccessData(out_texture_array); + + return EDITOR_COMMMAND_SUCCESS; + } + } + + return EDITOR_COMMMAND_FAILURE; +} + +/************************************************************************************************** +* CNC_Editor_Get_Template_Data +* Get the data from the given tile template type. +* +* template_type_index: The index of the template type to use. should come from the Get_Cell_Data function. +* template_positions: Out buffer to be filled with the list of positions of the tiles as offsets from the origin of the template. +* This data is store is an X, Y, X, Y, X, Y format. +* +* returns EDITOR_COMMMAND_SUCCESS on success, all other values are failure +**************************************************************************************************/ +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Template_Data(int template_type_index, SAFEARRAY*& template_points) +{ + if (template_type_index >= TEMPLATE_COUNT || template_type_index == TEMPLATE_NONE) + { + return EDITOR_COMMMAND_FAILURE; + } + + const TemplateTypeClass& template_type = TemplateTypeClass::As_Reference((TemplateType)template_type_index); + if (template_type.Get_Image_Data() == nullptr) + { + return EDITOR_COMMMAND_FAILURE; + } + + + short const * occupy_list = template_type.Occupy_List(); + + short const * counter = occupy_list; + while (counter && *counter != REFRESH_EOL) + { + counter++; + } + + int occupy_list_size = counter - occupy_list; + + + SAFEARRAYBOUND bounds; + bounds.lLbound = 0; + bounds.cElements = occupy_list_size * 2; + template_points = SafeArrayCreate(VT_I4, 1, &bounds); + + int *pData; + HRESULT hr = SafeArrayAccessData(template_points, (void **)&pData); + if (SUCCEEDED(hr)) + { + for (int i = 0; i < occupy_list_size; i++) + { + CELL cell = occupy_list[i]; + + int x = Cell_X(cell); + int y = Cell_Y(cell); + + pData[i * 2] = x; + pData[i * 2 + 1] = y; + } + + SafeArrayUnaccessData(template_points); + + return EDITOR_COMMMAND_SUCCESS; + } + + return EDITOR_COMMMAND_FAILURE; +} + +extern "C" __declspec(dllexport) int __cdecl CNC_Editor_Get_Scenario_Names(char* cncdata_directory) +{ + char content_directory[_MAX_PATH]; + + CNC_Editor_Setup_Content_Directory(cncdata_directory,false, content_directory); + + char command_line[_MAX_PATH]; + sprintf(command_line, "-CD%s", content_directory); + //DLL_Startup(command_line); + + char team_ids[] = + { + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + }; + /* + { + 'g', + 'a', + 'u', + 'm' + }; + */ + const int team_count = sizeof(team_ids) / sizeof(char); + + char direction_ids[] = + { + 'e', + 'w', + }; + const int direction_count = sizeof(direction_ids) / sizeof(char); + + char variant_ids[] = + { + 'a', + 'b', + 'c', + 'd', + 'e', + 'f', + 'g', + 'h', + 'i', + 'j', + 'k', + 'l', + 'm', + 'n', + 'o', + 'p', + 'q', + 'r', + 's', + 't', + 'u', + 'v', + 'w', + 'x', + 'y', + 'z', + }; + /* + { + 'a', + 'b', + 'c', + 'd' + }; + */ + const int variant_count = sizeof(variant_ids) / sizeof(char); + + const int min_scenario_index = 1; + const int max_scenario_index = 99; + + char scenario_name[_MAX_FNAME + _MAX_EXT]; + char file_name[_MAX_FNAME + _MAX_EXT]; + + FILE * names_file = fopen("d:\\RA_Disk2.txt", "w+"); + + for (int team_index = 0; team_index < team_count; team_index++) + { + for (int scenario_index = min_scenario_index; scenario_index <= max_scenario_index; ++scenario_index) + { + for (int direction_index = 0; direction_index < direction_count; direction_index++) + { + for (int variant_index = 0; variant_index < variant_count; variant_index++) + { + sprintf(scenario_name, "sc%c%.2d%c%c", + team_ids[team_index], + scenario_index, + direction_ids[direction_index], + variant_ids[variant_index]); + + sprintf(file_name, "%s.INI", scenario_name); + CCFileClass file(file_name); + if (file.Is_Available()) + { + fprintf(names_file, "%s\n", scenario_name); + } + } + } + } + } + + fclose(names_file); + + return EDITOR_COMMMAND_SUCCESS; +} \ No newline at end of file diff --git a/code/redalert/door.cpp b/code/redalert/door.cpp new file mode 100644 index 0000000..00f7716 --- /dev/null +++ b/code/redalert/door.cpp @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DOOR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 14, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DoorClass::AI -- Handles the door processing logic. * + * DoorClass::Close_Door -- Try to close the unit's door. * + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * DoorClass::Open_Door -- Opens the door for this unit. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * DoorClass::DoorClass -- Constructor for the DoorClass object. * + * * + * This constructor sets the door to an initial closed state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +DoorClass::DoorClass(void) +{ + State = IS_CLOSED; + IsToRedraw = false; + Stages = 0; +} + + +/*********************************************************************************************** + * DoorClass::AI -- Handles the door processing logic. * + * * + * This routine should be called every game frame. It handles the door closing and opening * + * logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/13/1995 JLB : Created. * + *=============================================================================================*/ +void DoorClass::AI(void) +{ + if (Control.Graphic_Logic()) { + if (Control.Fetch_Stage() >= Stages) { + Control.Set_Rate(0); + switch (State) { + case IS_OPENING: + State = IS_OPEN; + break; + + case IS_CLOSING: + State = IS_CLOSED; + break; + } + } + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * DoorClass::Open_Door -- Opens the door for this unit. * + * * + * This routine will perform the door open operation for this unit. It will control vehicle * + * rotation if necessary. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Was action initiated to open the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Open_Door(int rate, int stages) +{ + switch (State) { + case IS_CLOSED: + case IS_CLOSING: + State = IS_OPENING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Close_Door -- Try to close the unit's door. * + * * + * This routine will attempt to close the unit's door. If the door is already closed or * + * in the process of closing, then no action is performed. * + * * + * INPUT: rate -- The animation rate (delay) to use for the door animation logic. * + * * + * stages -- The number of animations stages that this door must pass through. * + * * + * OUTPUT: Action was initiated to close the door? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool DoorClass::Close_Door(int rate, int stages) +{ + switch (State) { + case IS_OPEN: + case IS_OPENING: + State = IS_CLOSING; + Stages = stages-1; + Control.Set_Stage(0); + Control.Set_Rate(rate); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DoorClass::Door_Stage -- Fetches the current door animation frame. * + * * + * Use this routine to fetch the current door animation frame number. Frame zero is the * + * closed frame and frame 'N' is the open frame. If the door is in the process of opening * + * or closing, the appropriate frame number is used. 'N' is defined as the number of * + * stages in the animation minus 1 (e.g., a four frame animation will return a door stage * + * number between 0 and 3, inclusive). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the door animation frame number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int DoorClass::Door_Stage(void) const +{ + switch (State) { + case IS_CLOSING: + return((Stages-1) - Control.Fetch_Stage()); + + case IS_CLOSED: + return(0); + + case IS_OPENING: + return(Control.Fetch_Stage()); + + case IS_OPEN: + return(Stages-1); + } + return(0); +} diff --git a/code/redalert/door.h b/code/redalert/door.h new file mode 100644 index 0000000..39dd110 --- /dev/null +++ b/code/redalert/door.h @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DOOR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DOOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/11/95 * + * * + * Last Update : June 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DOOR_H +#define DOOR_H + +class DoorClass +{ + private: + + /* + ** This is the animation control handler. + */ + StageClass Control; + + /* + ** This is the recorded number of stages of the current + ** door animation process. + */ + unsigned char Stages; + + /* + ** This is the door state. + */ + enum { + IS_CLOSED, // Door is closed. + IS_OPENING, // Door is in the process of opening. + IS_OPEN, // Door is fully open. + IS_CLOSING // Door is in the process of closing. + } State; + + /* + ** If the animation for this door indicates that the object it is + ** attached to should be redrawn, then this flag will be true. + */ + unsigned IsToRedraw:1; + + public: + DoorClass(void); + DoorClass(NoInitClass const & x) : Control(x) {}; + + bool Time_To_Redraw(void) {return(IsToRedraw);}; + void Clear_Redraw_Flag(void) {IsToRedraw = false;}; + void AI(void); + int Door_Stage(void) const; + bool Is_Door_Opening(void) const {return(State == IS_OPENING);}; + bool Is_Door_Closing(void) const {return(State == IS_CLOSING);}; + bool Open_Door(int rate, int stages); + bool Close_Door(int rate, int stages); + bool Is_Door_Open(void) const {return(State == IS_OPEN);}; + bool Is_Door_Closed(void) const {return(State == IS_CLOSED);}; + bool Is_Ready_To_Open(void) const; +}; + +#endif diff --git a/code/redalert/dpmi.cpp b/code/redalert/dpmi.cpp new file mode 100644 index 0000000..281f4a0 --- /dev/null +++ b/code/redalert/dpmi.cpp @@ -0,0 +1,167 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.cpv 4.41 04 Jul 1996 16:12:42 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) +#ifdef __FLAT__ +#pragma inline +#endif + +//#include "function.h" +#include "dpmi.h" + +#ifndef __FLAT__ + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + if (!size) return; + + unsigned short ssel = src.Selector; + unsigned short dsel = dest.Selector; + + asm { + push es + push ds + + mov si,soffset + mov di,doffset + mov cx,size + mov ax,ssel + mov dx,dsel + mov ds,ax + mov es,dx + } +again: + asm { + mov al,ds:[si] + mov ah,es:[di] + mov ds:[si],ah + mov es:[di],al + inc di + inc si + dec cx + jnz again + + pop ds + pop es + } +} +#endif + + +void DOSSegmentClass::Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_swap(char *src, char *dest, int size); + + #pragma aux dss_swap = \ + "again: mov al,[esi]" \ + "mov ah,[edi]" \ + "mov [esi],ah" \ + "stosb" \ + "inc esi" \ + "loop again" \ + parm [esi] [edi] [ecx] \ + modify [ax]; + + if (!size) return; + dss_swap((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} + +#ifdef OBSOLETE +void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) +{ + extern void dss_copy(char *src, char *dest, int size); + #pragma aux dss_copy = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copskip1" \ + "rep movsd" \ +"copskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copskip2" \ + "rep movsb" \ +"copskip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy((char *)(src.Selector + soffset), (char *)(dest.Selector + doffset), size); +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + extern void dss_copy_to(void *src, (void *)dest, int size); + + #pragma aux dss_copy_to = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz cop2skip1" \ + "rep movsd" \ +"cop2skip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz cop2skip2" \ + "rep movsb" \ +"cop2skip2:" \ + parm [esi edi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_to(src, (void *)(Selector + dest), size); + +} +#endif + +#ifdef OBSOLETE +void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + extern void dss_copy_from(void *dest, (void *)source, int size); + + #pragma aux dss_copy_from = \ + "mov ebx,ecx" \ + "shr ecx,2" \ + "jecxz copfskip1" \ + "rep movsd" \ +"copfskip1: mov ecx,ebx" \ + "and ecx,3" \ + "jecxz copfskip2" \ + "rep movsb" \ +"copfskip2:" \ + parm [edi esi ecx] \ + modify [ebx]; + + if (!size) return; + dss_copy_from(dest, (void *)(Selector + source), size); +} +#endif +#endif \ No newline at end of file diff --git a/code/redalert/dpmi.h b/code/redalert/dpmi.h new file mode 100644 index 0000000..2dab6c9 --- /dev/null +++ b/code/redalert/dpmi.h @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\dpmi.h_v 4.43 05 Jul 1996 17:58:40 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DPMI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef DPMI_Hx +#define DPMI_Hx +#include +#include +#include +//#include + + +extern void output(short port, short data); + + +class DOSSegmentClass { + /* + ** This is the selector/segment value. In real mode it is the segment, in protected + ** mode it is the selector (also 16 bits). This value is moved into DS or ES when + ** accessing memory. + ** Note: in Watcom flat addressing, Selector == Segment<<4 (ex: 0A0000h) + */ + unsigned int Selector; + + /* + ** These are C equivalents for pushing and popping the DS segment register. By using + ** these, it is possible to create very small code that uses a segment and + ** offset without damaging the DS register. These are especially useful in protected + ** mode, but they are legal in real mode as well. + */ + void Push_DS(void) {/*__emit__(0x1E);*/}; + void Pop_DS(void) {/*__emit__(0x1F);*/}; + + public: + DOSSegmentClass(void); + ~DOSSegmentClass(void); + DOSSegmentClass(unsigned short segment, long size=(1024L*64L)); + + unsigned int Get_Selector(void); + + /* + ** This routine is used to assign where the descriptor actually points to in + ** low DOS memory. In real mode, this is a simple segment assignment and the size + ** is always 64K regardless of what is specified. In protected mode, the segment + ** is used to update the selector and the size can be any length. + ** In Watcom flat mode, it sets Selector == segment<<4 + */ + void Assign(unsigned short segment, long size=(1024L*64L)); + + /* + ** These routines will move the data to/from regular memory and the segment/descriptor + ** memory. + */ + void Copy_To(void *source, int dest, int size); + void Copy_From(void *dest, int source, int size); + void Copy_Word_To(short data, int dest); + void Copy_Byte_To(char data, int dest); + void Copy_DWord_To(long data, int dest); + short Copy_Word_From(int source); + char Copy_Byte_From(int source); + long Copy_DWord_From(int source); + + /* + ** These routines move data around between sections of segmented (descriptor) memory. + ** Typically, this is used when accessing DOS memory in protected mode or when dealing + ** with hard memory areas such as the screen. + */ + static void Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); + static void Swap(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size); +}; + + +inline DOSSegmentClass::DOSSegmentClass(void) +{ + Selector = 0xB0000; +} + +inline DOSSegmentClass::~DOSSegmentClass(void) +{ +} + +inline void DOSSegmentClass::Copy_Word_To(short data, int dest) +{ + *(short *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_Byte_To(char data, int dest) +{ + *(char *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Copy_DWord_To(long data, int dest) +{ + *(long *)(Selector+dest) = data; +} + +inline void DOSSegmentClass::Assign(unsigned short segment, long) +{ + Selector = (long)(segment)<<4L; +} + +inline DOSSegmentClass::DOSSegmentClass(unsigned short segment, long) +{ + Assign(segment); +} + +inline void DOSSegmentClass::Copy_To(void *source, int dest, int size) +{ + memmove((void*)(Selector+dest), source, (unsigned)size); +} + +inline void DOSSegmentClass::Copy_From(void *dest, int source, int size) +{ + memmove(dest, (void*)(Selector+source), (unsigned)size); +} + +inline void DOSSegmentClass::Copy(DOSSegmentClass &src, int soffset, DOSSegmentClass &dest, int doffset, int size) { + memmove((void*)(dest.Selector+doffset), (void*)(src.Selector+soffset), (unsigned)size); +} + +inline short DOSSegmentClass::Copy_Word_From(int source) +{ + return *(short*)(Selector+source); +} + +inline char DOSSegmentClass::Copy_Byte_From(int source) +{ + return *(char*)(Selector+source); +} + +inline long DOSSegmentClass::Copy_DWord_From(int source) +{ + return *(long*)(Selector+source); +} + +inline unsigned int DOSSegmentClass::Get_Selector(void) +{ + return Selector; +} +#endif + + diff --git a/code/redalert/drive.cpp b/code/redalert/drive.cpp new file mode 100644 index 0000000..47698c1 --- /dev/null +++ b/code/redalert/drive.cpp @@ -0,0 +1,2338 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DRIVE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * DriveClass::AI -- Processes unit movement and rotation. * + * DriveClass::Approach_Target -- Handles approaching the target in order to attack it. * + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * DriveClass::Class_Of -- Fetches a reference to the class type for this object. * + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * DriveClass::DriveClass -- Constructor for drive class object. * + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * DriveClass::Teleport_To -- Teleport object to specified location. * + * DriveClass::While_Moving -- Processes unit movement. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef NEVER +void test(void) +{ + enum nums {one, two, three}; + + nums x; + nums *ptr; + + ptr = &x; +} +#endif + + +/*********************************************************************************************** + * DriveClass::Response_Select -- Voice feedback when selecting the unit. * + * * + * This is the voice to play when the unit is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Select(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_VEHIC, + VOC_REPORT, + VOC_YESSIR, + VOC_YESSIR, + VOC_YESSIR, + VOC_AWAIT + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Move -- Voice feedback when ordering the unit to move. * + * * + * This plays the audio feedback when ordering this unit to move to a new destination. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Move(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_ACKNOWL, + VOC_AFFIRM, + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Response_Attack -- Voice feedback when ordering the unit to attack a target. * + * * + * This plays the audio feedback when ordering this unit to attack. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Response_Attack(void) +{ + assert(IsActive); + + static VocType _response[] = { + VOC_AFFIRM, + VOC_ACKNOWL + }; + VocType response = _response[Sim_Random_Pick(0, ARRAY_SIZE(_response)-1)]; + if (AllowVoice) { + Sound_Effect(response, fixed(1), -(ID+1)); + } +} + + +/*********************************************************************************************** + * DriveClass::Scatter -- Causes the unit to travel to a nearby safe cell. * + * * + * This routine is called when the unit discovers that it should get out of the "hot seat" * + * and move to an adjacent cell. Since the safety of the adjacent cell is not determined * + * before the move begins, it will appear that the unit is just scattering (which it * + * should). * + * * + * INPUT: threat -- The coordinate of the source of the threat. The unit will try to move * + * roughly away from the threat. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1994 JLB : Created. * + * 09/27/1995 JLB : Revised to never scatter if already moving. * + * 07/09/1996 JLB : Moved to DriveClass so that ships will scatter too. * + * 08/02/1996 JLB : Added the "nokidding" parameter. * + *=============================================================================================*/ +void DriveClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (MissionControl[Mission].IsParalyzed) return; + + if ((What_Am_I() != RTTI_UNIT || !((UnitClass *)this)->IsDumping) && (!Target_Legal(NavCom) || (nokidding && !IsRotating))) { + if (!Target_Legal(TarCom) || forced || Random_Pick(1, 4) == 1) { + FacingType toface; + FacingType newface; + CELL newcell; + + if (threat != 0) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + toface = toface + FacingType(Random_Pick(0, 2)-1); + } + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + Assign_Destination(::As_Target(newcell)); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Limbo -- Prepares vehicle and then limbos it. * + * * + * This routine removes the occupation bits for the vehicle and also handles cleaning up * + * any vehicle reservation bits. After this, it then proceeds with limboing the unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Limbo(void) +{ + if (!IsInLimbo) { + Stop_Driver(); + TrackNumber = -1; + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * DriveClass::Stop_Driver -- Handles removing occupation bits when driving stops. * + * * + * This routine will remove the "reservation" flag (if present) when the vehicle is * + * required to stop movement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vehicle stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Stop_Driver(void) +{ + assert(IsActive); + + /* + ** We only need to do something if the vehicle is actually going + ** somewhere. + */ + if (Head_To_Coord()) { + + /* + ** Safe off whether the vehicle is down or not so we know whether + ** we have to put it back down. + */ + int temp = IsDown; + + /* + ** If the vehicle is down, pick it up so it doesn't interfere with + ** our flags. + */ + if (temp) { + Mark(MARK_UP); + } + + /* + ** Call the drive class function which will let us release the + ** reserved track. + */ + Mark_Track(Head_To_Coord(), MARK_UP); + + /* + ** If it was down it should be down when we are done. + */ + if (temp) { + Mark(MARK_DOWN); + } + } + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * DriveClass::Do_Turn -- Tries to turn the vehicle to the specified direction. * + * * + * This routine will set the vehicle to rotate to the direction specified. For tracked * + * vehicles, it is just a simple rotation. For wheeled vehicles, it performs a series * + * of short drives (three point turn) to face the desired direction. * + * * + * INPUT: dir -- The direction that this vehicle should face. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Do_Turn(DirType dir) +{ + assert(IsActive); + + if (dir != PrimaryFacing) { + +#ifdef TOFIX + /* + ** Special rotation track is needed for units that + ** cannot rotate in place. + */ + if (Special.IsThreePoint && TrackNumber == -1 && Techno_Type_Class()->Speed == SPEED_WHEEL) { + int facediff; // Signed difference between current and desired facing. + FacingType face; // Current facing (ordinal value). + + facediff = PrimaryFacing.Difference(dir) >> 5; + facediff = Bound(facediff, -2, 2); + if (facediff) { + face = Dir_Facing(PrimaryFacing); + + IsOnShortTrack = true; + Force_Track(face*FACING_COUNT + (face + facediff), Coord); + + Path[0] = FACING_NONE; + Set_Speed(0xFF); // Full speed. + } + } else { + PrimaryFacing.Set_Desired(dir); + } +#else + PrimaryFacing.Set_Desired(dir); +// IsRotating = true; +#endif + } +} + + +/*********************************************************************************************** + * DriveClass::Teleport_To -- Teleport object to specified location. * + * * + * This will teleport the object to the specified location or as close as possible to it * + * if the destination is blocked. * + * * + * INPUT: cell -- The desired destination cell to teleport to. * + * * + * OUTPUT: bool; Was the teleport successful? * + * * + * WARNINGS: All current activity of this object will be terminated by the teleport. It will * + * arrive at the destination in static guard mode. * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + * 10/31/1996 JLB : Handles flag teleport case. * + *=============================================================================================*/ +bool DriveClass::Teleport_To(CELL cell) +{ + /* + ** All cargo gets destroyed. + */ + if (Rule.IsChronoKill) { + Kill_Cargo(NULL); + } + + Stop_Driver(); + Force_Track(-1, 0); + PrimaryFacing.Set_Current(PrimaryFacing.Desired()); + Transmit_Message(RADIO_OVER_OUT); + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + Assign_Mission(MISSION_NONE); + Commence(); + Mark(MARK_UP); + + /* + ** A teleported unit will drop the flag right where it's at. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(((UnitClass *)this)->Flagged)->Flag_Attach(Coord_Cell(Coord)); + } + + if (Can_Enter_Cell(cell) != MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } + Coord = Cell_Coord(cell); + Mark(MARK_DOWN); + Look(false); + Per_Cell_Process(PCP_END); + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Force_Track -- Forces the unit to use the indicated track. * + * * + * This override (nuclear bomb) style routine is to be used when a unit needs to start * + * on a movement track but is outside the normal movement system. This occurs when a * + * harvester starts driving off of a refinery. * + * * + * INPUT: track -- The track number to start on. * + * * + * coord -- The coordinate that the unit will end up at when the movement track * + * is completed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Force_Track(int track, COORDINATE coord) +{ + assert(IsActive); + + TrackNumber = track; + TrackIndex = 0; + if (coord != 0) { + Start_Driver(coord); + } +} + + +/*********************************************************************************************** + * DriveClass::DriveClass -- Constructor for drive class object. * + * * + * This will initialize the drive class to its default state. It is called as a result * + * of creating a unit. * + * * + * INPUT: classid -- The unit's ID class. It is passed on to the foot class constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1994 JLB : Created. * + *=============================================================================================*/ +DriveClass::DriveClass(RTTIType rtti, int id, HousesType house) : + FootClass(rtti, id, house), + IsMoebius(false), + IsHarvesting(false), + IsTurretLockedDown(false), + IsOnShortTrack(false), + SpeedAccum(0), + MoebiusCountDown(0), + MoebiusCell(0), + TrackNumber(-1), + TrackIndex(0) +{ +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * DriveClass::Debug_Dump -- Displays status information to monochrome screen. * + * * + * This debug utility function will display the status of the drive class to the mono * + * screen. It is through this information that bugs can be tracked down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(66, 14, 12, 1, IsMoebius ? MonoClass::INVERSE : MonoClass::NORMAL); + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * DriveClass::Smooth_Turn -- Handles the low level coord calc for smooth turn logic. * + * * + * This routine calculates the new coordinate value needed for the * + * smooth turn logic. The adjustment and flag values must be * + * determined prior to entering this routine. * + * * + * INPUT: adj -- The adjustment coordinate as lifted from the * + * correct smooth turn table. * + * * + * dir -- Pointer to dir for possible modification * + * according to the flag bits. * + * * + * OUTPUT: Returns with the coordinate the unit should positioned to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1994 JLB : Created. * + * 07/13/1994 JLB : Converted to member function. * + *=============================================================================================*/ +COORDINATE DriveClass::Smooth_Turn(COORDINATE adj, DirType & dir) +{ + assert(IsActive); + + DirType workdir = dir; + int x,y; + int temp; + TrackControlType flags = TrackControl[TrackNumber].Flag; + + x = Coord_X(adj); + y = Coord_Y(adj); + + if (flags & F_T) { + temp = x; + x = y; + y = temp; + workdir = (DirType)(DIR_W - workdir); + } + + if (flags & F_X) { + x = -x; + workdir = (DirType)-workdir; + } + + if (flags & F_Y) { + y = -y; + workdir = (DirType)(DIR_S - workdir); + } + + dir = workdir; + + return(XY_Coord( (LEPTON)(Coord_X(Head_To_Coord()) + x), (LEPTON)(Coord_Y(Head_To_Coord()) + y))); +} + + +/*********************************************************************************************** + * DriveClass::Assign_Destination -- Set the unit's NavCom. * + * * + * This routine is used to set the unit's navigation computer to the * + * specified target. Once the navigation computer is set, the unit * + * will start planning and moving toward the destination. * + * * + * INPUT: target -- The destination target for the unit to head to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** For harvesting type vehicles, it might go into a dock and unload procedure + ** when the harvester is full and an empty refinery is selected as a target. + */ + BuildingClass * b = As_Building(target); + + /* + ** If the player clicked on refinery but it is not busy, then assign + ** it to unload at the refinery. + */ + if (b != NULL && *b == STRUCT_REFINERY && What_Am_I() == RTTI_UNIT && ((UnitTypeClass *)Techno_Type_Class())->IsToHarvest) { + if (Contact_With_Whom() != b && !b->In_Radio_Contact()) { + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + if (Mission != MISSION_ENTER && Mission != MISSION_HARVEST) { + Assign_Mission(MISSION_ENTER); + target = TARGET_NONE; + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } else { +// target = TARGET_NONE; + } + } + + /* + ** Set the unit's navigation computer. + */ + FootClass::Assign_Destination(target); + + Path[0] = FACING_NONE; // Force recalculation of path. + if (!IsDriving && Mission != MISSION_UNLOAD) { + Start_Of_Move(); + } +} + + +/*********************************************************************************************** + * DriveClass::While_Moving -- Processes unit movement. * + * * + * This routine is used to process movement for the units as they move. * + * It is called many times for each cell's worth of movement. This * + * routine only applies after the next cell HeadTo has been determined. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +bool DriveClass::While_Moving(void) +{ + assert(IsActive); + + /* + ** Perform quick legality checks. + */ + if (!IsDriving || TrackNumber == -1 || (IsRotating && !Techno_Type_Class()->IsTurretEquipped)) { + SpeedAccum = 0; // Kludge? No speed should accumulate if movement is on hold. + return(false); + } + + /* + ** If enough movement has accumulated so that the unit can + ** visibly move on the map, then process accordingly. + ** Slow the unit down if he's carrying a flag. + */ + MPHType maxspeed = MPHType(min(Techno_Type_Class()->MaxSpeed * SpeedBias * House->GroundspeedBias, (int)MPH_LIGHT_SPEED)); + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + int actual; // Working movement addition value. + if (((UnitClass *)this)->Flagged != HOUSE_NONE) { + actual = SpeedAccum + ((int)maxspeed/2) * fixed(Speed, 256); + } else { + actual = SpeedAccum + maxspeed * fixed(Speed, 256); + } + + if (actual > PIXEL_LEPTON_W) { + TurnTrackType const * track; // Track control pointer. + TrackType const * ptr; // Pointer to coord offset values. + int tracknum; // The track number being processed. + FacingType nextface; // Next facing queued in path. + bool adj; // Is a turn coming up? + + track = &TrackControl[TrackNumber]; + if (IsOnShortTrack) { + tracknum = track->StartTrack; + } else { + tracknum = track->Track; + } + ptr = RawTracks[tracknum-1].Track; + nextface = Path[0]; + + /* + ** Determine if there is a turn coming up. If there is + ** a turn, then track jumping might occur. + */ + adj = false; + if (nextface != FACING_NONE && Dir_Facing(track->Facing) != nextface) { + adj = true; + } + + /* + ** Skip ahead the number of track steps required (limited only + ** by track length). Set the unit to the new position and + ** flag the unit accordingly. + */ + Mark(MARK_UP); + while (actual > PIXEL_LEPTON_W) { + COORDINATE offset; + DirType dir; + + actual -= PIXEL_LEPTON_W; + + offset = ptr[TrackIndex].Offset; + if (offset || !TrackIndex) { + dir = ptr[TrackIndex].Facing; + Coord = Smooth_Turn(offset, dir); + + PrimaryFacing.Set(dir); + + /* + ** See if "per cell" processing is necessary. + */ + if (TrackIndex && RawTracks[tracknum-1].Cell == TrackIndex) { + Mark(MARK_DOWN); + Per_Cell_Process(PCP_DURING); + if (!IsActive) { + return(false); + } + Mark(MARK_UP); + } + + /* + ** The unit could "jump tracks". Check to see if the unit should + ** do so. + */ + if (/**this != UNIT_GUNBOAT &&*/ nextface != FACING_NONE && adj && RawTracks[tracknum-1].Jump == TrackIndex && TrackIndex) { + TurnTrackType const * newtrack; // Proposed jump-to track. + int tnum; + + tnum = (int)(Dir_Facing(track->Facing) * FACING_COUNT) + (int)nextface; + newtrack = &TrackControl[tnum]; + if (newtrack->Track && RawTracks[newtrack->Track-1].Entry) { + COORDINATE c = Head_To_Coord(); + int oldspeed = Speed; + + c = Adjacent_Cell(c, nextface); + + switch (Can_Enter_Cell(Coord_Cell(c), nextface)) { + case MOVE_OK: + IsOnShortTrack = false; // Shouldn't be necessary, but... + TrackNumber = tnum; + track = newtrack; + + tracknum = track->Track; + TrackIndex = RawTracks[tracknum-1].Entry-1; // Anticipate increment. + ptr = RawTracks[tracknum-1].Track; + adj = false; + + Stop_Driver(); + IsDriving = true; + Per_Cell_Process(PCP_END); + IsDriving = false; + if (!IsActive) return(false); + if (Start_Driver(c)) { + Set_Speed(oldspeed); + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } else { + Path[0] = FACING_NONE; + TrackNumber = -1; + actual = 0; + } + break; + + case MOVE_CLOAK: + Map[c].Shimmer(); + break; + + case MOVE_TEMP: +#ifdef TOFIX + if (*this == UNIT_HARVESTER || !House->IsHuman) { +#else + if (!House->IsHuman) { +#endif + Map[c].Incoming(0, true, true); + } + break; + } + } + } + TrackIndex++; + + } else { + actual = 0; + Coord = Head_To_Coord(); + Stop_Driver(); + TrackNumber = -1; + TrackIndex = NULL; + + /* + ** Perform "per cell" activities. + */ + Mark(MARK_DOWN); + Per_Cell_Process(PCP_END); + if (!IsActive) return(false); + Mark(MARK_UP); + + break; + } + } + if (IsActive) { + Mark(MARK_DOWN); + } + } + + /* + ** Replace any remainder back into the unit's movement + ** accumulator to be processed next pass. + */ + SpeedAccum = actual; + return(true); +} + + +/*********************************************************************************************** + * DriveClass::Per_Cell_Process -- Handles when unit finishes movement into a cell. * + * * + * This routine is called when a unit has mostly or completely * + * entered a cell. The unit might be in the middle of a movement track * + * when this routine is called. It's primary purpose is to perform * + * sighting and other "per cell" activities. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1993 JLB : Created. * + * 03/30/1994 JLB : Revamped for track system. * + * 04/15/1994 JLB : Converted to member function. * + * 06/18/1994 JLB : Converted to virtual function. * + * 06/18/1994 JLB : Distinguishes between center and near-center conditions. * + *=============================================================================================*/ +void DriveClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** Check to see if it has reached its destination. If so, then clear the NavCom + ** regardless of the remaining path list. + */ + if (As_Cell(NavCom) == cell) { + IsTurretLockedDown = false; + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + + Lay_Track(); + } + + FootClass::Per_Cell_Process(why); +} + + +/*********************************************************************************************** + * DriveClass::Start_Of_Move -- Tries to get a unit to advance toward cell. * + * * + * This will try to start a unit advancing toward the cell it is * + * facing. It will check for and handle legality and reserving of the * + * necessary cell. * + * * + * INPUT: none * + * * + * OUTPUT: true/false; Should this routine be called again because * + * initial start operation is temporarily delayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1992 JLB : Created. * + * 10/18/1993 JLB : This should be called repeatedly until HeadTo is not NULL. * + * 03/16/1994 JLB : Revamped for track logic. * + * 04/15/1994 JLB : Converted to member function. * + * 06/19/1995 JLB : Fixed so that it won't fire on ground unnecessarily. * + * 07/13/1995 JLB : Handles bumping into cloaked objects. * + * 09/22/1995 JLB : Breaks out of hopeless hunt mode. * + * 07/10/1996 JLB : Sets scan limit if necessary. * + *=============================================================================================*/ +bool DriveClass::Start_Of_Move(void) +{ + assert(IsActive); + + FacingType facing; // Direction movement will commence. + DirType dir; // Desired actual facing toward destination. + int facediff; // Difference between current and desired facing. + int speed; // Speed of unit. + CELL destcell; // Cell of destination. + LandType ground; // Ground unit is entering. + COORDINATE dest; // Destination coordinate. + + facing = Path[0]; + + if (!Target_Legal(NavCom) && facing == FACING_NONE) { + IsTurretLockedDown = false; + Stop_Driver(); + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + return(false); // Why is it calling this routine!?! + } + + /* + ** Reduce the path length if the target is a unit and the + ** range to the unit is less than the precalculated path steps. + */ + if (facing != FACING_NONE) { + int dist; + + if (Is_Target_Vessel(NavCom) || Is_Target_Unit(NavCom) || Is_Target_Infantry(NavCom)) { + dist = Lepton_To_Cell((LEPTON)Distance(NavCom)); + + if (dist < ARRAY_SIZE(Path)) { + Path[dist] = FACING_NONE; + facing = Path[0]; // Maybe needed. + } + } + } + + /* + ** If the path is invalid at this point, then generate one. If + ** generating a new path fails, then abort NavCom. + */ + if (facing == FACING_NONE) { + + /* + ** If after a path search, there is still no valid path, then set the + ** NavCom to null and let the script take care of assigning a new + ** navigation target. + */ + if (PathDelay != 0) { + return(false); + } + + if (!Basic_Path()) { + + /* + ** If the unit is close enough to the target then just stop + ** driving now. This prevents the fidgeting that would occur + ** if they mindlessly kept trying to get to the exact location + ** desired. This is quite necessary since it is typical to move + ** several units with the same mouse click. + */ + if (!Is_On_Priority_Mission() && Distance(NavCom) < Rule.CloseEnoughDistance && (Mission == MISSION_MOVE || Mission == MISSION_GUARD_AREA)) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + } else { + /* + ** If a basic path could not be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current()); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + if (TryTryAgain > 0) { + TryTryAgain--; + } else { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + } + } + + /* + ** Since the path was blocked, check to make sure that it was completely + ** blocked. If so and it has a valid TarCom and it is out of range of the + ** TarCom, then give this unit a range limit so that it might not pick + ** a "can't reach" target again. + */ + if (!Target_Legal(NavCom) && Target_Legal(TarCom) && !In_Range(TarCom)) { + IsScanLimited = true; + if (Team.Is_Valid()) Team->Scan_Limit(); + Assign_Target(TARGET_NONE); + } + + /* + ** Stop the movement, for now, and let the subsequent logic in later game + ** frames resume movement as appropriate. + */ + Stop_Driver(); + TrackNumber = -1; + IsTurretLockedDown = false; + return(false); + } + + /* + ** If a basic path could be found, but the immediate move destination is + ** blocked by a friendly temporary blockage, then cause that blockage + ** to scatter. + */ + CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0]); + if (Map.In_Radar(cell)) { + MoveType ok = Can_Enter_Cell(cell); + if (ok == MOVE_TEMP) { + CellClass * cellptr = &Map[cell]; + TechnoClass * blockage = cellptr->Cell_Techno(); + if (blockage && House->Is_Ally(blockage)) { + + /* + ** If the target can be told to get out of the way, only bother + ** to do so if we aren't very close to the target and this + ** object can just say "good enough" and stop here. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !In_Radio_Contact()) { + Assign_Destination(TARGET_NONE); + return(false); + } else { + cellptr->Incoming(0, true, false); +// cellptr->Incoming(0, true, true); + } + } + } + } + + TryTryAgain = PATH_RETRY; + facing = Path[0]; + } + + /* + ** Determine the coordinate of the next cell to move into. + */ + dest = Adjacent_Cell(Coord, facing); + dir = Facing_Dir(facing); + + /* + ** Set the facing correctly if it isn't already correct. This + ** means starting a rotation track if necessary. + */ + facediff = PrimaryFacing.Difference(dir); + if (facediff) { + + /* + ** Request a change of facing. + */ + Do_Turn(dir); + return(true); + + } else { + + /* NOTE: Beyond this point, actual track assignment can begin. + ** + ** If the cell to move into is impassable (probably for some unexpected + ** reason), then abort the path list and set the speed to zero. The + ** next time this routine is called, a new path will be generated. + */ + destcell = Coord_Cell(dest); + Mark(MARK_UP); + MoveType cando = Can_Enter_Cell(destcell, facing); + Mark(MARK_DOWN); + + if (cando != MOVE_OK) { + + if (Mission == MISSION_MOVE /*KO&& House->IsHuman */&& Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + if (!IsActive) return(false);//BG + } + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Stop_Driver(); + if (cando != MOVE_MOVING_BLOCK) { + Path[0] = FACING_NONE; // Path is blocked! + } + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (cando == MOVE_DESTROYABLE) { + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + } + IsNewNavCom = false; + TrackNumber = -1; + return(true); + } + + /* + ** Determine the speed that the unit can travel to the desired square. + */ + ground = Map[destcell].Land_Type(); + speed = Ground[ground].Cost[Techno_Type_Class()->Speed] * 255; + + /* change speed if it's related to a team move */ + if (IsFormationMove) speed = Ground[ground].Cost[FormationSpeed] * 255; + if (!speed) speed = 128; + +#ifdef NEVER + /* + ** Set the jiggle flag if the terrain would cause the unit + ** to jiggle when travelled over. + */ + BaseF &= ~BASEF_JIGGLE; + if (Ground[ground].Jiggle) { + BaseF |= BASEF_JIGGLE; + } +#endif + + /* + ** A damaged unit has a reduced speed. + */ + if (Health_Ratio() <= Rule.ConditionYellow /*(Techno_Type_Class()->MaxStrength>>1) > Strength*/) { + speed -= (speed/4); // Three quarters speed. + } + if ((speed != Speed)/* || !SpeedAdd*/) { + Set_Speed(speed); // Full speed. + } + + /* + ** Reserve the destination cell so that it won't become + ** occupied AS this unit is moving into it. + */ + if (cando != MOVE_OK) { + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + } else { + + Overrun_Square(Coord_Cell(dest), true); + + /* + ** Determine which track to use (based on recorded path). + */ + FacingType nextface = Path[1]; + if (nextface == FACING_NONE) nextface = facing; + + IsOnShortTrack = false; + TrackNumber = facing * FACING_COUNT + (int)nextface; + if (TrackControl[TrackNumber].Track == 0) { + Path[0] = FACING_NONE; + TrackNumber = -1; + return(true); + } else { + if (TrackControl[TrackNumber].Flag & F_D) { + /* + ** If the middle cell of a two cell track contains a crate, + ** the check for goodies before movement starts. + */ + if (!Map[destcell].Goodie_Check(this)) { + cando = MOVE_NO; + if (!IsActive) return(false); + } else { + if (!IsActive) return(false); + dest = Adjacent_Cell(dest, nextface); + destcell = Coord_Cell(dest); + cando = Can_Enter_Cell(destcell); + } + if (!IsActive) return(false); + + if (cando != MOVE_OK) { + + /* + ** If a temporary friendly object is blocking the path, then cause it to + ** get out of the way. + */ + if (cando == MOVE_TEMP) { + Map[destcell].Incoming(0, true, true); + } + + /* + ** If a cloaked object is blocking, then shimmer the cell. + */ + if (cando == MOVE_CLOAK) { + Map[destcell].Shimmer(); + } + + Path[0] = FACING_NONE; // Path is blocked! + TrackNumber = -1; + dest = NULL; + if (cando == MOVE_DESTROYABLE) { + + if (Map[destcell].Cell_Object()) { + if (!House->Is_Ally(Map[destcell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[destcell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[destcell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[destcell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(destcell), TARGET_NONE); + } + } + IsNewNavCom = false; + TrackIndex = 0; + return(true); + } + } else { + memcpy((char*)&Path[0], (char*)&Path[2], CONQUER_PATH_MAX-2); + Path[CONQUER_PATH_MAX-2] = FACING_NONE; + IsPlanningToLook = true; + } + } else { + memcpy((char*)&Path[0], (char*)&Path[1], CONQUER_PATH_MAX-1); + } + Path[CONQUER_PATH_MAX-1] = FACING_NONE; + } + } + + IsNewNavCom = false; + TrackIndex = 0; + if (!Start_Driver(dest)) { + TrackNumber = -1; + Path[0] = FACING_NONE; + Set_Speed(0); + } + } + return(false); +} + + +/*********************************************************************************************** + * DriveClass::AI -- Processes unit movement and rotation. * + * * + * This routine is used to process unit movement and rotation. It * + * functions autonomously from the script system. Thus, once a unit * + * is give rotation command or movement path, it will follow this * + * until specifically instructed to stop. The advantage of this * + * method is that it allows smooth movement of units, faster game * + * execution, and reduced script complexity (since actual movement * + * dynamics need not be controlled directly by the scripts). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine relies on the process control bits for the * + * specified unit (for speed reasons). Thus, only setting * + * movement, rotation, or path list will the unit perform * + * any physics. * + * * + * HISTORY: * + * 09/26/1993 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::AI(void) +{ + assert(IsActive); + + FootClass::AI(); + if (!IsActive || Height > 0) return; + + /* + ** Is this a unit that's been teleported using the chronosphere, and if so, + ** has his timer expired such that he needs to teleport back? + */ + if (IsMoebius) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() != RTTI_UNIT || ((UnitClass *)this)->Class->Type != UNIT_CHRONOTANK) { +#endif + if (MoebiusCountDown == 0) { + IsMoebius = false; + Teleport_To(MoebiusCell); + MoebiusCell = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + + /* + ** If the unit is following a track, then continue + ** to do so -- mindlessly. + */ + if (TrackNumber != -1) { + + /* + ** Perform the movement accumulation. + */ + While_Moving(); + if (!IsActive) return; + if (TrackNumber == -1 && (Target_Legal(NavCom) || Path[0] != FACING_NONE) && (What_Am_I() != RTTI_UNIT || !((UnitClass*)this)->IsDumping)) { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + + } else { + + /* + ** For tracked units that are rotating in place, perform the rotation now. + */ +#ifdef TOFIX + if ((Class->Speed == SPEED_FLOAT || Class->Speed == SPEED_HOVER || Class->Speed == SPEED_TRACK || (Class->Speed == SPEED_WHEEL && !Special.IsThreePoint)) && PrimaryFacing.Is_Rotating()) { + if (PrimaryFacing.Rotation_Adjust(Class->ROT)) { + Mark(MARK_CHANGE); + } +#else + if (PrimaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (PrimaryFacing.Rotation_Adjust(Techno_Type_Class()->ROT * House->GroundspeedBias)) { + Mark(MARK_CHANGE_REDRAW); + } +#endif + if (!IsRotating) { + Per_Cell_Process(PCP_ROTATION); + if (!IsActive) return; + } + + } else { + + /* + ** The unit has no track to follow, but if there + ** is a navigation target or a remaining path, + ** then start on a new track. + */ + if ((Mission != MISSION_GUARD || Target_Legal(NavCom)) && Mission != MISSION_UNLOAD) { + if (Target_Legal(NavCom) || Path[0] != FACING_NONE) { + + /* + ** Double check to make sure that the movement destination is + ** in a zone that this unit can travel to. If not, then abort + ** the navigation target. Exception is to allow units to leave + ** impassable cells regardless of zone checks. + */ + LandType land = LAND_NONE; + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + land = Map[Center_Coord()].Land_Type(); + } + if (IsLocked && Mission != MISSION_ENTER && Target_Legal(NavCom) && !Is_In_Same_Zone(As_Cell(NavCom)) && + land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) { + Stop_Driver(); + Assign_Destination(TARGET_NONE); + } else { + Start_Of_Move(); + if (!IsActive) return; + While_Moving(); + if (!IsActive) return; + } + } else { + Stop_Driver(); + } + } + } + } +} + + +/*********************************************************************************************** + * DriveClass::Fixup_Path -- Adds smooth start path to normal movement path. * + * * + * This routine modifies the path of the specified unit so that it * + * will not start out with a rotation. This is necessary for those * + * vehicles that have difficulty with rotating in place. Typically, * + * this includes wheeled vehicles. * + * * + * INPUT: unit -- Pointer to the unit to adjust. * + * * + * path -- Pointer to path structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only units that require a fixup get modified. The * + * modification only occurs, if there is a legal path to * + * do so. * + * * + * HISTORY: * + * 04/03/1994 JLB : Created. * + * 04/06/1994 JLB : Uses path structure. * + * 04/10/1994 JLB : Diagonal smooth turn added. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void DriveClass::Fixup_Path(PathType * path) +{ + assert(IsActive); + + FacingType stage[6]={FACING_N,FACING_N,FACING_N,FACING_N,FACING_N,FACING_N}; // Prefix path elements. + int facediff; // The facing difference value (0..4 | 0..-4). + static FacingType _path[4][6] = { + {(FacingType)2,(FacingType)0,(FacingType)2,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + static FacingType _dpath[4][6] = { + {(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0,(FacingType)0}, + {(FacingType)3,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)0,(FacingType)0}, + {(FacingType)4,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0}, + {(FacingType)5,(FacingType)0,(FacingType)2,(FacingType)2,(FacingType)1,(FacingType)0} + }; + + int index; + int counter; // Path addition + FacingType * ptr; // Path list pointer. + FacingType * ptr2; // Copy of new path list pointer. + FacingType nextpath; // Next path value. + CELL cell; // Working cell value. + bool ok; + + /* + ** Verify that the unit is valid and there is a path problem to resolve. + */ + if (!path || path->Command[0] == FACING_NONE) { + return; + } + + /* + ** Only wheeled vehicles need a path fixup -- to avoid 3 point turns. + */ +#ifdef TOFIX + if (!Special.IsThreePoint || Class->Speed != SPEED_WHEEL) { +#else + if (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) { +// if (What_Am_I() == RTTI_UNIT) { +#endif + return; + } + + /* + ** If the original path starts in the same direction as the unit, then + ** there is no problem to resolve -- abort. + */ + facediff = PrimaryFacing.Difference((DirType)(path->Command[0]<<5)) >> 5; + + if (!facediff) return; + + if (Dir_Facing(PrimaryFacing) & FACING_NE) { + ptr = &_dpath[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_dpath[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } else { + ptr = &_path[(FacingType)ABS((int)facediff)-FACING_NE][1]; // Pointer to path adjust list. + counter = (int)_path[(FacingType)ABS((int)facediff)-FACING_NE][0]; // Number of path adjusts. + } + ptr2 = ptr; + + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Adjacent_Cell(cell, nextpath); + //cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + + /* + ** If veering to the left was not successful, then try veering + ** to the right. This only makes sense if the vehicle is trying + ** to turn 180 degrees. + */ + if (!ok && ABS(facediff) == 4) { + ptr = ptr2; // Pointer to path adjust list. + facediff = -facediff; + ok = true; // Presume adjustment is all ok. + cell = Coord_Cell(Coord); // Starting cell. + nextpath = Dir_Facing(PrimaryFacing); // Starting path. + for (index = 0; index < counter; index++) { + + /* + ** Determine next path element and add it to the + ** working path list. + */ + if (facediff > 0) { + nextpath = nextpath + *ptr++; + } else { + nextpath = nextpath - *ptr++; + } + stage[index] = nextpath; + cell = Coord_Cell(Adjacent_Cell(Cell_Coord(cell), nextpath)); + + /* + ** If it can't enter this cell, then abort the path + ** building operation without adjusting the unit's + ** path. + */ + if (Can_Enter_Cell(cell, nextpath) != MOVE_OK) { + ok = false; + break; + } + } + } + + /* + ** If a legal path addition was created, then install it in place + ** of the first path value. The initial path entry is to be replaced + ** with a sequence of path entries that create smooth turning. + */ + if (ok) { + if (path->Length <= 1) { + memmove((char *)&stage[0], (char*)path->Command, max(counter, 1)); + path->Length = counter; + } else { + + /* + ** Optimize the transition path step from the smooth turn + ** first part as it joins with the rest of the normal + ** path. The normal prefix path steps are NOT to be optimized. + */ + if (counter) { + counter--; + path->Command[0] = stage[counter]; + Optimize_Moves(path, MOVE_OK); + } + + /* + ** If there is more than one prefix path element, then + ** insert the rest now. + */ + if (counter) { + memmove((char*)&path->Command[0], (char*)&path->Command[counter], 40-counter); + memmove((char*)&stage[0], (char*)&path->Command[0], counter); + path->Length += counter; + } + } + path->Command[path->Length] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * DriveClass::Lay_Track -- Handles track laying logic for the unit. * + * * + * This routine handles the track laying for the unit. This entails examining the unit's * + * current location as well as the direction and whether this unit is allowed to lay * + * tracks in the first place. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Lay_Track(void) +{ + assert(IsActive); + +#ifdef NEVER + static IconCommandType * _trackdirs[8] = { + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE, + TrackN_S, + TrackNE_SW, + TrackE_W, + TrackNW_SE + }; + + if (!(ClassF & CLASSF_TRACKS)) return; + + Icon_Install(Coord_Cell(Coord), _trackdirs[Facing_To_8(BodyFacing)]); +#endif +} + + +/*********************************************************************************************** + * DriveClass::Mark_Track -- Marks the midpoint of the track as occupied. * + * * + * This routine will ensure that the midpoint (if any) of the track that the unit is * + * following, will be marked according to the mark type specified. * + * * + * INPUT: headto -- The head to coordinate. * + * * + * type -- The type of marking to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1995 JLB : Created. * + *=============================================================================================*/ +void DriveClass::Mark_Track(COORDINATE headto, MarkType type) +{ + assert(IsActive); + + int value; + + if (type == MARK_UP) { + value = false; + } else { + value = true; + } + + if (headto) { + if (!IsOnShortTrack && TrackNumber != -1) { + + /* + ** If we have not passed the per cell process point we need + ** to deal with it. + */ + int tracknum = TrackControl[TrackNumber].Track; + if (tracknum) { + TrackType const * ptr = RawTracks[tracknum - 1].Track; + int cellidx = RawTracks[tracknum - 1].Cell; + if (cellidx > -1) { + DirType dir = ptr[cellidx].Facing; + + if (TrackIndex < cellidx && cellidx != -1) { + COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, dir); + Map[offset].Flag.Occupy.Vehicle = value; + } + } + } + } + Map[headto].Flag.Occupy.Vehicle = value; + } +} + + +/*********************************************************************************************** + * DriveClass::Ok_To_Move -- Checks to see if this object can begin moving. * + * * + * This routine is used to verify that this object is allowed to move. Some objects can * + * be temporarily occupied and thus cannot move until the situation permits. * + * * + * INPUT: direction -- The direction that movement would be desired. * + * * + * OUTPUT: Can the unit move in the direction specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool DriveClass::Ok_To_Move(DirType ) const +{ + assert(IsActive); + + return true; +} + + +/*************************************************************************** +** Smooth turn track tables. These are coordinate offsets from the center +** of the destination cell. These are the raw tracks that are modified +** by negating the X and Y portions as necessary. Also for reverse travelling +** direction, the track list can be processed backward. +** +** Track 1 = N +** Track 2 = NE +** Track 3 = N->NE 45 deg (double path consumption) +** Track 4 = N->E 90 deg (double path consumption) +** Track 5 = NE->SE 90 deg (double path consumption) +** Track 6 = NE->N 45 deg (double path consumption) +** Track 7 = N->NE (facing change only) +** Track 8 = NE->E (facing change only) +** Track 9 = N->E (facing change only) +** Track 10= NE->SE (facing change only) +** Track 11= back up into refinery +** Track 12= drive out of refinery +*/ +//#pragma warn -ias +DriveClass::TrackType const DriveClass::Track1[24] = { + {0x00F50000L,(DirType)0}, + {0x00EA0000L,(DirType)0}, + {0x00DF0000L,(DirType)0}, + {0x00D40000L,(DirType)0}, + {0x00C90000L,(DirType)0}, + {0x00BE0000L,(DirType)0}, + {0x00B30000L,(DirType)0}, + {0x00A80000L,(DirType)0}, + {0x009D0000L,(DirType)0}, + {0x00920000L,(DirType)0}, + {0x00870000L,(DirType)0}, + {0x007C0000L,(DirType)0}, // Track jump check here. + {0x00710000L,(DirType)0}, + {0x00660000L,(DirType)0}, + {0x005B0000L,(DirType)0}, + {0x00500000L,(DirType)0}, + {0x00450000L,(DirType)0}, + {0x003A0000L,(DirType)0}, + {0x002F0000L,(DirType)0}, + {0x00240000L,(DirType)0}, + {0x00190000L,(DirType)0}, + {0x000E0000L,(DirType)0}, + {0x00030000L,(DirType)0}, + {0x00000000L,(DirType)0} +}; + +DriveClass::TrackType const DriveClass::Track2[] = { + {0x00F8FF08L,(DirType)32}, + {0x00F0FF10L,(DirType)32}, + {0x00E8FF18L,(DirType)32}, + {0x00E0FF20L,(DirType)32}, + {0x00D8FF28L,(DirType)32}, + {0x00D0FF30L,(DirType)32}, + {0x00C8FF38L,(DirType)32}, + {0x00C0FF40L,(DirType)32}, + {0x00B8FF48L,(DirType)32}, + {0x00B0FF50L,(DirType)32}, + {0x00A8FF58L,(DirType)32}, + {0x00A0FF60L,(DirType)32}, + {0x0098FF68L,(DirType)32}, + {0x0090FF70L,(DirType)32}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track3[] = { + {0x01F5FF00L,(DirType)0}, + {0x01EAFF00L,(DirType)0}, + {0x01DFFF00L,(DirType)0}, + {0x01D4FF00L,(DirType)0}, + {0x01C9FF00L,(DirType)0}, + {0x01BEFF00L,(DirType)0}, + {0x01B3FF00L,(DirType)0}, + {0x01A8FF00L,(DirType)0}, + {0x019DFF00L,(DirType)0}, + {0x0192FF00L,(DirType)0}, + {0x0187FF00L,(DirType)0}, + {0x0180FF00L,(DirType)0}, + {0x0175FF00L,(DirType)0}, // Jump entry point here. + {0x016BFF00L,(DirType)0}, + {0x0160FF02L,(DirType)1}, + {0x0155FF04L,(DirType)3}, + {0x014CFF06L,(DirType)4}, + {0x0141FF08L,(DirType)5}, + {0x0137FF0BL,(DirType)7}, + {0x012EFF0FL,(DirType)8}, + {0x0124FF13L,(DirType)9}, + {0x011AFF17L,(DirType)11}, + {0x0110FF1BL,(DirType)12}, + {0x0107FF1FL,(DirType)13}, // Center cell processing here. + {0x00FCFF24L,(DirType)15}, + {0x00F3FF28L,(DirType)16}, + {0x00ECFF2CL,(DirType)17}, + {0x00E0FF32L,(DirType)19}, + {0x00D7FF36L,(DirType)20}, + {0x00CFFF3DL,(DirType)21}, + {0x00C6FF42L,(DirType)23}, + {0x00BAFF49L,(DirType)24}, + {0x00B0FF4DL,(DirType)25}, + {0x00A8FF58L,(DirType)27}, + {0x00A0FF60L,(DirType)28}, + {0x0098FF68L,(DirType)29}, + {0x0090FF70L,(DirType)31}, + {0x0088FF78L,(DirType)32}, + {0x0080FF80L,(DirType)32}, // Track jump check here. + {0x0078FF88L,(DirType)32}, + {0x0070FF90L,(DirType)32}, + {0x0068FF98L,(DirType)32}, + {0x0060FFA0L,(DirType)32}, + {0x0058FFA8L,(DirType)32}, + {0x0050FFB0L,(DirType)32}, + {0x0048FFB8L,(DirType)32}, + {0x0040FFC0L,(DirType)32}, + {0x0038FFC8L,(DirType)32}, + {0x0030FFD0L,(DirType)32}, + {0x0028FFD8L,(DirType)32}, + {0x0020FFE0L,(DirType)32}, + {0x0018FFE8L,(DirType)32}, + {0x0010FFF0L,(DirType)32}, + {0x0008FFF8L,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track4[] = { + {0x00F5FF00L,(DirType)0}, + {0x00EBFF00L,(DirType)0}, + {0x00E0FF00L,(DirType)0}, + {0x00D5FF00L,(DirType)0}, + {0x00CBFF01L,(DirType)0}, + {0x00C0FF03L,(DirType)0}, + {0x00B5FF05L,(DirType)1}, + {0x00ABFF07L,(DirType)1}, + {0x00A0FF0AL,(DirType)2}, + {0x0095FF0DL,(DirType)3}, + {0x008BFF10L,(DirType)4}, + {0x0080FF14L,(DirType)5}, // Track entry here. + {0x0075FF18L,(DirType)8}, + {0x006DFF1CL,(DirType)12}, + {0x0063FF22L,(DirType)16}, + {0x005AFF25L,(DirType)20}, + {0x0052FF2BL,(DirType)23}, + {0x0048FF32L,(DirType)27}, + {0x0040FF37L,(DirType)32}, + {0x0038FF3DL,(DirType)36}, + {0x0030FF46L,(DirType)39}, + {0x002BFF4FL,(DirType)43}, + {0x0024FF58L,(DirType)47}, + {0x0020FF60L,(DirType)51}, + {0x001BFF6DL,(DirType)54}, + {0x0017FF79L,(DirType)57}, + {0x0014FF82L,(DirType)60}, // Track jump here. + {0x0011FF8FL,(DirType)62}, + {0x000DFF98L,(DirType)63}, + {0x0009FFA2L,(DirType)64}, + {0x0006FFACL,(DirType)64}, + {0x0004FFB5L,(DirType)66}, + {0x0003FFC0L,(DirType)64}, + {0x0002FFCBL,(DirType)64}, + {0x0001FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track5[] = { + {0xFFF8FE08L,(DirType)32}, + {0xFFF0FE10L,(DirType)32}, + {0xFFE8FE18L,(DirType)32}, + {0xFFE0FE20L,(DirType)32}, + {0xFFD8FE28L,(DirType)32}, + {0xFFD0FE30L,(DirType)32}, + {0xFFC8FE38L,(DirType)32}, + {0xFFC0FE40L,(DirType)32}, + {0xFFB8FE48L,(DirType)32}, + {0xFFB0FE50L,(DirType)32}, + {0xFFA8FE58L,(DirType)32}, + {0xFFA0FE60L,(DirType)32}, + {0xFF98FE68L,(DirType)32}, + {0xFF90FE70L,(DirType)32}, + {0xFF88FE78L,(DirType)32}, + {0xFF80FE80L,(DirType)32}, // Track entry here. + {0xFF78FE88L,(DirType)32}, + {0xFF71FE90L,(DirType)32}, + {0xFF6AFE97L,(DirType)32}, + {0xFF62FE9FL,(DirType)32}, + {0xFF5AFEA8L,(DirType)32}, + {0xFF53FEB0L,(DirType)35}, + {0xFF4BFEB7L,(DirType)38}, + {0xFF44FEBEL,(DirType)41}, + {0xFF3EFEC4L,(DirType)44}, + {0xFF39FECEL,(DirType)47}, + {0xFF34FED8L,(DirType)50}, + {0xFF30FEE0L,(DirType)53}, + {0xFF2DFEEBL,(DirType)56}, + {0xFF2CFEF5L,(DirType)59}, + {0xFF2BFF00L,(DirType)62}, + {0xFF2CFF0BL,(DirType)66}, + {0xFF2DFF15L,(DirType)69}, + {0xFF30FF1FL,(DirType)72}, + {0xFF34FF28L,(DirType)75}, + {0xFF39FF30L,(DirType)78}, + {0xFF3EFF3AL,(DirType)81}, + {0xFF44FF44L,(DirType)84}, + {0xFF4BFF4BL,(DirType)87}, + {0xFF53FF50L,(DirType)90}, + {0xFF5AFF58L,(DirType)93}, + {0xFF62FF60L,(DirType)96}, + {0xFF6AFF68L,(DirType)96}, + {0xFF71FF70L,(DirType)96}, + {0xFF78FF78L,(DirType)96}, + {0xFF80FF80L,(DirType)96}, // Track jump check here. + {0xFF88FF88L,(DirType)96}, + {0xFF90FF90L,(DirType)96}, + {0xFF98FF98L,(DirType)96}, + {0xFFA0FFA0L,(DirType)96}, + {0xFFA8FFA8L,(DirType)96}, + {0xFFB0FFB0L,(DirType)96}, + {0xFFB8FFB8L,(DirType)96}, + {0xFFC0FFC0L,(DirType)96}, + {0xFFC8FFC8L,(DirType)96}, + {0xFFD0FFD0L,(DirType)96}, + {0xFFD8FFD8L,(DirType)96}, + {0xFFE0FFE0L,(DirType)96}, + {0xFFE8FFE8L,(DirType)96}, + {0xFFF0FFF0L,(DirType)96}, + {0xFFF8FFF8L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track6[] = { + {0x0100FE00L,(DirType)32}, + {0x00F8FE08L,(DirType)32}, + {0x00F0FE10L,(DirType)32}, + {0x00E8FE18L,(DirType)32}, + {0x00E0FE20L,(DirType)32}, + {0x00D8FE28L,(DirType)32}, + {0x00D0FE30L,(DirType)32}, + {0x00C8FE38L,(DirType)32}, + {0x00C0FE40L,(DirType)32}, + {0x00B8FE48L,(DirType)32}, + {0x00B0FE50L,(DirType)32}, + {0x00A8FE58L,(DirType)32}, + {0x00A0FE60L,(DirType)32}, + {0x0098FE68L,(DirType)32}, + {0x0090FE70L,(DirType)32}, + {0x0088FE78L,(DirType)32}, + {0x0080FE80L,(DirType)32}, // Jump entry point here. + {0x0078FE88L,(DirType)32}, + {0x0070FE90L,(DirType)32}, + {0x0068FE98L,(DirType)32}, + {0x0060FEA0L,(DirType)32}, + {0x0058FEA8L,(DirType)32}, + {0x0055FEAEL,(DirType)32}, + {0x004EFEB8L,(DirType)35}, + {0x0048FEC0L,(DirType)37}, + {0x0042FEC9L,(DirType)40}, + {0x003BFED2L,(DirType)43}, + {0x0037FEDAL,(DirType)45}, + {0x0032FEE3L,(DirType)48}, + {0x002BFEEBL,(DirType)51}, + {0x0026FEF5L,(DirType)53}, + {0x0022FEFEL,(DirType)56}, + {0x001CFF08L,(DirType)59}, + {0x0019FF12L,(DirType)61}, + {0x0015FF1BL,(DirType)64}, + {0x0011FF26L,(DirType)64}, + {0x000EFF30L,(DirType)64}, + {0x000BFF39L,(DirType)64}, + {0x0009FF43L,(DirType)64}, + {0x0007FF4EL,(DirType)64}, + {0x0005FF57L,(DirType)64}, + {0x0003FF62L,(DirType)64}, + {0x0001FF6DL,(DirType)64}, + {0x0000FF77L,(DirType)64}, + {0x0000FF80L,(DirType)64}, // Track jump check here. + {0x0000FF8BL,(DirType)64}, + {0x0000FF95L,(DirType)64}, + {0x0000FFA0L,(DirType)64}, + {0x0000FFABL,(DirType)64}, + {0x0000FFB5L,(DirType)64}, + {0x0000FFC0L,(DirType)64}, + {0x0000FFCBL,(DirType)64}, + {0x0000FFD5L,(DirType)64}, + {0x0000FFE0L,(DirType)64}, + {0x0000FFEBL,(DirType)64}, + {0x0000FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track7[] = { + {0x0006FFFFL,(DirType)0}, + {0x000CFFFEL,(DirType)4}, + {0x0011FFFCL,(DirType)8}, + {0x0018FFFAL,(DirType)12}, + {0x001FFFF6L,(DirType)16}, + {0x0024FFF3L,(DirType)19}, + {0x002BFFF0L,(DirType)22}, + {0x0030FFFDL,(DirType)23}, + {0x0035FFEBL,(DirType)24}, + {0x0038FFE8L,(DirType)25}, + {0x003CFFE6L,(DirType)26}, + {0x0040FFE3L,(DirType)27}, + {0x0043FFE0L,(DirType)28}, + {0x0046FFDDL,(DirType)29}, + {0x0043FFDFL,(DirType)30}, + {0x0040FFE1L,(DirType)30}, + {0x003CFFE3L,(DirType)30}, + {0x0038FFE5L,(DirType)30}, + {0x0035FFE7L,(DirType)31}, + {0x0030FFE9L,(DirType)31}, + {0x002BFFEBL,(DirType)31}, + {0x0024FFEDL,(DirType)31}, + {0x001FFFF1L,(DirType)31}, + {0x0018FFF4L,(DirType)32}, + {0x0011FFF7L,(DirType)32}, + {0x000CFFFAL,(DirType)32}, + {0x0006FFFDL,(DirType)32}, + {0x00000000L,(DirType)32} +}; + +DriveClass::TrackType const DriveClass::Track8[] = { + {0x0003FFFCL,(DirType)32}, + {0x0006FFF7L,(DirType)36}, + {0x000AFFF1L,(DirType)40}, + {0x000CFFEBL,(DirType)44}, + {0x000DFFE4L,(DirType)46}, + {0x000EFFDCL,(DirType)48}, + {0x000FFFD5L,(DirType)50}, + {0x0010FFD0L,(DirType)52}, + {0x0011FFC9L,(DirType)54}, + {0x0012FFC2L,(DirType)56}, + {0x0011FFC0L,(DirType)58}, + {0x0010FFC2L,(DirType)60}, + {0x000EFFC9L,(DirType)62}, + {0x000CFFCFL,(DirType)64}, + {0x000AFFD5L,(DirType)64}, + {0x0008FFDAL,(DirType)64}, + {0x0006FFE2L,(DirType)64}, + {0x0004FFE9L,(DirType)64}, + {0x0002FFEFL,(DirType)64}, + {0x0001FFF5L,(DirType)64}, + {0x0000FFF9L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track9[] = { + {0xFFF50002L,(DirType)0}, + {0xFFEB0004L,(DirType)2}, + {0xFFE00006L,(DirType)4}, + {0xFFD50009L,(DirType)6}, + {0xFFCE000CL,(DirType)9}, + {0xFFC8000FL,(DirType)11}, + {0xFFC00012L,(DirType)13}, + {0xFFB80015L,(DirType)16}, + {0xFFC00012L,(DirType)18}, + {0xFFC8000EL,(DirType)20}, + {0xFFCE000AL,(DirType)22}, + {0xFFD50004L,(DirType)24}, + {0xFFDE0000L,(DirType)26}, + {0xFFE9FFF8L,(DirType)28}, + {0xFFEEFFF2L,(DirType)30}, + {0xFFF5FFEBL,(DirType)32}, + {0xFFFDFFE1L,(DirType)34}, + {0x0002FFD8L,(DirType)36}, + {0x0007FFD2L,(DirType)39}, + {0x000BFFCBL,(DirType)41}, + {0x0010FFC5L,(DirType)43}, + {0x0013FFBEL,(DirType)45}, + {0x0015FFB7L,(DirType)48}, + {0x0013FFBEL,(DirType)50}, + {0x0011FFC5L,(DirType)52}, + {0x000BFFCCL,(DirType)54}, + {0x0008FFD4L,(DirType)56}, + {0x0005FFDFL,(DirType)58}, + {0x0003FFEBL,(DirType)62}, + {0x0001FFF5L,(DirType)64}, + {0x00000000L,(DirType)64} +}; + +DriveClass::TrackType const DriveClass::Track10[] = { + {0xFFF6000BL,(DirType)32}, + {0xFFF00015L,(DirType)37}, + {0xFFEB0020L,(DirType)42}, + {0xFFE9002BL,(DirType)47}, + {0xFFE50032L,(DirType)52}, + {0xFFE30038L,(DirType)57}, + {0xFFE00040L,(DirType)60}, + {0xFFE20038L,(DirType)62}, + {0xFFE40032L,(DirType)64}, + {0xFFE5002AL,(DirType)68}, + {0xFFE6001EL,(DirType)70}, + {0xFFE70015L,(DirType)72}, + {0xFFE8000BL,(DirType)74}, + {0xFFE90000L,(DirType)76}, + {0xFFE8FFF5L,(DirType)78}, + {0xFFE7FFEBL,(DirType)80}, + {0xFFE6FFE0L,(DirType)82}, + {0xFFE5FFD5L,(DirType)84}, + {0xFFE4FFCEL,(DirType)86}, + {0xFFE2FFC5L,(DirType)88}, + {0xFFE0FFC0L,(DirType)90}, + {0xFFE3FFC5L,(DirType)92}, + {0xFFE5FFCEL,(DirType)94}, + {0xFFE9FFD5L,(DirType)95}, + {0xFFEBFFE0L,(DirType)96}, + {0xFFF0FFEBL,(DirType)96}, + {0xFFF6FFF5L,(DirType)96}, + {0x00000000L,(DirType)96} +}; + +DriveClass::TrackType const DriveClass::Track11[] = { + {0x01000000L,DIR_SW}, + {0x00F30008L,DIR_SW}, + {0x00E50010L,DIR_SW_X1}, + {0x00D60018L,DIR_SW_X1}, + {0x00C80020L,DIR_SW_X1}, + {0x00B90028L,DIR_SW_X1}, + {0x00AB0030L,DIR_SW_X2}, + {0x009C0038L,DIR_SW_X2}, + {0x008D0040L,DIR_SW_X2}, + {0x007F0048L,DIR_SW_X2}, + {0x00710050L,DIR_SW_X2}, + {0x00640058L,DIR_SW_X2}, + {0x00550060L,DIR_SW_X2}, + + {0x00000000L,DIR_SW_X2} +}; + +DriveClass::TrackType const DriveClass::Track12[] = { + {0xFF550060L,DIR_SW_X2}, + {0xFF640058L,DIR_SW_X2}, + {0xFF710050L,DIR_SW_X2}, + {0xFF7F0048L,DIR_SW_X2}, + {0xFF8D0040L,DIR_SW_X2}, + {0xFF9C0038L,DIR_SW_X2}, + {0xFFAB0030L,DIR_SW_X2}, + {0xFFB90028L,DIR_SW_X1}, + {0xFFC80020L,DIR_SW_X1}, + {0xFFD60018L,DIR_SW_X1}, + {0xFFE50010L,DIR_SW_X1}, + {0xFFF30008L,DIR_SW}, + + {0x00000000L,DIR_SW} +}; + +#if(1) +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(0,-35),DIR_S}, + {XYP_COORD(0,-34),DIR_S}, + {XYP_COORD(0,-33),DIR_S}, + {XYP_COORD(0,-32),DIR_S}, + {XYP_COORD(0,-31),DIR_S}, + {XYP_COORD(0,-30),DIR_S}, + {XYP_COORD(0,-29),DIR_S}, + {XYP_COORD(0,-28),DIR_S}, + {XYP_COORD(0,-27),DIR_S}, + {XYP_COORD(0,-26),DIR_S}, + {XYP_COORD(0,-25),DIR_S}, + {XYP_COORD(0,-24),DIR_S}, + {XYP_COORD(0,-23),DIR_S}, + {XYP_COORD(0,-22),DIR_S}, + {XYP_COORD(0,-21),DIR_S}, + {XYP_COORD(0,-20),DIR_S}, + {XYP_COORD(0,-19),DIR_S}, + {XYP_COORD(0,-18),DIR_S}, + {XYP_COORD(0,-17),DIR_S}, + {XYP_COORD(0,-16),DIR_S}, + {XYP_COORD(0,-15),DIR_S}, + {XYP_COORD(0,-14),DIR_S}, + {XYP_COORD(0,-13),DIR_S}, + {XYP_COORD(0,-12),DIR_S}, + {XYP_COORD(0,-11),DIR_S}, + {XYP_COORD(0,-10),DIR_S}, + {XYP_COORD(0,-9),DIR_S}, + {XYP_COORD(0,-8),DIR_S}, + {XYP_COORD(0,-7),DIR_S}, + {XYP_COORD(0,-6),DIR_S}, + {XYP_COORD(0,-5),DIR_S}, + {XYP_COORD(0,-4),DIR_S}, + {XYP_COORD(0,-3),DIR_S}, + {XYP_COORD(0,-2),DIR_S}, + {XYP_COORD(0,-1),DIR_S}, + + {0x00000000L,DIR_S} +}; +#else +/* +** Drive out of weapon's factory. +*/ +DriveClass::TrackType const DriveClass::Track13[] = { + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-21),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(10,-20),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-18),(DirType)(DIR_SW-10)}, + {XYP_COORD(9,-17),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-16),(DirType)(DIR_SW-10)}, + {XYP_COORD(8,-15),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-14),(DirType)(DIR_SW-10)}, + {XYP_COORD(7,-13),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-12),(DirType)(DIR_SW-10)}, + {XYP_COORD(6,-11),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-10),(DirType)(DIR_SW-10)}, + {XYP_COORD(5,-9),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-8),(DirType)(DIR_SW-10)}, + {XYP_COORD(4,-7),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-6),(DirType)(DIR_SW-10)}, + {XYP_COORD(3,-5),(DirType)(DIR_SW-9)}, + {XYP_COORD(2,-4),(DirType)(DIR_SW-7)}, + {XYP_COORD(2,-3),(DirType)(DIR_SW-5)}, + {XYP_COORD(1,-2),(DirType)(DIR_SW-3)}, + {XYP_COORD(1,-1),(DirType)(DIR_SW-1)}, + + {0x00000000L,DIR_SW} +}; +#endif + +/* +** There are a limited basic number of tracks that a vehicle can follow. These +** are they. Each track can be interpreted differently but this is controlled +** by the TrackControl structure elaborated elsewhere. +*/ +DriveClass::RawTrackType const DriveClass::RawTracks[13] = { + {Track1, -1, 0, -1}, + {Track2, -1, 0, -1}, + {Track3, 37, 12, 22}, + {Track4, 26, 11, 19}, + {Track5, 45, 15, 31}, + {Track6, 44, 16, 27}, + {Track7, -1, 0, -1}, + {Track8, -1, 0, -1}, + {Track9, -1, 0, -1}, + {Track10, -1, 0, -1}, + {Track11, -1, 0, -1}, + {Track12, -1, 0, -1}, + {Track13, -1, 0, -1} +}; + + +/*************************************************************************** +** Smooth turning control table. Given two directions in a path list, this +** table determines which track to use and what modifying operations need +** be performed on the track data. +*/ +DriveClass::TurnTrackType const DriveClass::TrackControl[67] = { + {1, 0, DIR_N, F_}, // 0-0 + {3, 7, DIR_NE, F_D}, // 0-1 (raw chart) + {4, 9, DIR_E, F_D}, // 0-2 (raw chart) + {0, 0, DIR_SE, F_}, // 0-3 ! + {0, 0, DIR_S, F_}, // 0-4 ! + {0, 0, DIR_SW, F_}, // 0-5 ! + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_X|F_D)}, // 0-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-0 + {2, 0, DIR_NE, F_}, // 1-1 (raw chart) + {6, 8, DIR_E, F_D}, // 1-2 (raw chart) + {5, 10, DIR_SE, F_D}, // 1-3 (raw chart) + {0, 0, DIR_S, F_}, // 1-4 ! + {0, 0, DIR_SW, F_}, // 1-5 ! + {0, 0, DIR_W, F_}, // 1-6 ! + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 1-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-0 + {3, 7, DIR_NE, (DriveClass::TrackControlType)(F_T|F_X|F_Y|F_D)}, // 2-1 + {1, 0, DIR_E, (DriveClass::TrackControlType)(F_T|F_X)}, // 2-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-3 + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 2-4 + {0, 0, DIR_SW, F_}, // 2-5 ! + {0, 0, DIR_W, F_}, // 2-6 ! + {0, 0, DIR_NW, F_}, // 2-7 ! + {0, 0, DIR_N, F_}, // 3-0 ! + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-1 + {6, 8, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 3-2 + {2, 0, DIR_SE, F_Y}, // 3-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-4 + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_T|F_X|F_D)}, // 3-5 + {0, 0, DIR_W, F_}, // 3-6 ! + {0, 0, DIR_NW, F_}, // 3-7 ! + {0, 0, DIR_N, F_}, // 4-0 ! + {0, 0, DIR_NE, F_}, // 4-1 ! + {4, 9, DIR_E, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-2 + {3, 7, DIR_SE, (DriveClass::TrackControlType)(F_Y|F_D)}, // 4-3 + {1, 0, DIR_S, F_Y}, // 4-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-5 + {4, 9, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 4-6 + {0, 0, DIR_NW, F_}, // 4-7 ! + {0, 0, DIR_N, F_}, // 5-0 ! + {0, 0, DIR_NE, F_}, // 5-1 ! + {0, 0, DIR_E, F_}, // 5-2 ! + {5, 10, DIR_SE, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-3 + {6, 8, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 5-4 + {2, 0, DIR_SW, F_T}, // 5-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-6 + {5, 10, DIR_NW, (DriveClass::TrackControlType)(F_X|F_Y|F_D)}, // 5-7 + {4, 9, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-0 + {0, 0, DIR_NE, F_}, // 6-1 ! + {0, 0, DIR_E, F_}, // 6-2 ! + {0, 0, DIR_SE, F_}, // 6-3 ! + {4, 9, DIR_S, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-4 + {3, 7, DIR_SW, (DriveClass::TrackControlType)(F_T|F_D)}, // 6-5 + {1, 0, DIR_W, F_T}, // 6-6 + {3, 7, DIR_NW, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 6-7 + {6, 8, DIR_N, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-0 + {5, 10, DIR_NE, (DriveClass::TrackControlType)(F_T|F_Y|F_D)}, // 7-1 + {0, 0, DIR_E, F_}, // 7-2 ! + {0, 0, DIR_SE, F_}, // 7-3 ! + {0, 0, DIR_S, F_}, // 7-4 ! + {5, 10, DIR_SW, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-5 + {6, 8, DIR_W, (DriveClass::TrackControlType)(F_X|F_D)}, // 7-6 + {2, 0, DIR_NW, F_X}, // 7-7 + + {11, 11, DIR_SW, F_}, // Backup harvester into refinery. + {12, 12, DIR_SW_X2, F_}, // Drive back into refinery. + {13, 13, DIR_SW, F_} // Drive out of weapons factory. +}; + + + diff --git a/code/redalert/drive.h b/code/redalert/drive.h new file mode 100644 index 0000000..3cb332c --- /dev/null +++ b/code/redalert/drive.h @@ -0,0 +1,208 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DRIVE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DRIVE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DRIVE_H +#define DRIVE_H + +#include "foot.h" + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class DriveClass : public FootClass +{ + public: + /* + ** If this unit performing harvesting action, then this flag is true. The flag + ** is located here because the other bit flags here give it a free place to + ** reside. + */ + unsigned IsHarvesting:1; + + /* + ** This flag controls whether the unit has been moebius'd into a + ** different location, and whether the MoebiusCountDown timer should be + ** used to take him back where he belongs. + */ + unsigned IsMoebius:1; + + /* + ** This controls how long a unit can exist in its alternate location + ** before being pulled back by the chronosphere into its normal location. + */ + CDTimerClass MoebiusCountDown; + + /* + ** This is the coord the unit will be taken back to once its moebius + ** effect wears off. + */ + CELL MoebiusCell; + + /* + ** Some units must have their turret locked down to face their body direction. + ** When this flag is set, this condition is in effect. This flag is a more + ** accurate check than examining the TrackNumber since the turret may be + ** rotating into position so that a pending track may start. During this process + ** the track number does not indicate anything. + */ + unsigned IsTurretLockedDown:1; + + /* + ** This vehicle could be processing a "short track". A short track is one that + ** doesn't actually go anywhere. Kind of like turning in place. + */ + unsigned IsOnShortTrack:1; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + DriveClass(RTTIType rtti, int id, HousesType house); + DriveClass(NoInitClass const & x) : FootClass(x), MoebiusCountDown(x) {}; + virtual ~DriveClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Teleport_To(CELL cell); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + virtual bool Limbo(void); + void Do_Turn(DirType dir); + virtual void Overrun_Square(CELL , bool =true) {}; + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual bool Ok_To_Move(DirType ) const; + virtual void AI(void); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + void Force_Track(int track, COORDINATE coord); + virtual bool Stop_Driver(void); + + void Mark_Track(COORDINATE headto, MarkType type); + + /********************************************************************** + ** These enumerations are used as working constants that exist only + ** in the DriveClass namespace. + */ + enum DriveClassEnum { + BACKUP_INTO_REFINERY=64, // Track to backup into refinery. + OUT_OF_REFINERY, // Track to leave refinery. + OUT_OF_WEAPON_FACTORY // Track to leave weapons factory. + }; + + /**************************************************************************** + ** Smooth turning tracks are controlled by this structure and these + ** processing bits. + */ + typedef enum TrackControlType : unsigned char { + F_=0x00, // No translation necessary? + F_T=0x01, // Transpose X and Y components? + F_X=0x02, // Reverse X component sign? + F_Y=0x04, // Reverse Y component sign? + F_D=0x08 // Two cell consumption? + } TrackControlType; + + private: + + typedef struct { + char Track; // Which track to use. + char StartTrack; // Track when starting from stand-still. + DirType Facing; // Facing when track has been completed. + DriveClass::TrackControlType Flag; // List processing flag bits. + } TurnTrackType; + + typedef struct { + COORDINATE Offset; // Offset to origin coordinate. + DirType Facing; // Facing (primary track). + } TrackType; + + typedef struct { + TrackType const * Track; // Pointer to track list. + int Jump; // Index where track jumping is allowed. + int Entry; // Entry point if jumping to this track. + int Cell; // Per cell process should occur at this index. + } RawTrackType; + + /* + ** These speed values are used to accumulate movement and then + ** convert them into pixel "steps" that are then translated through + ** the currently running track so that the unit will move. + */ + int SpeedAccum; + + /* + ** This the track control logic (used for ground vehicles only). The 'Track' + ** variable holds the track being followed (0 == not following track). The + ** 'TrackIndex' variable holds the current index into the specified track + ** (starts at 0). + */ + int TrackNumber; + int TrackIndex; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual void Fixup_Path(PathType *path); + bool While_Moving(void); + bool Start_Of_Move(void); + void Lay_Track(void); + COORDINATE Smooth_Turn(COORDINATE adj, DirType & dir); + + static TurnTrackType const TrackControl[67]; + static RawTrackType const RawTracks[13]; + static TrackType const Track13[]; + static TrackType const Track12[]; + static TrackType const Track11[]; + static TrackType const Track10[]; + static TrackType const Track9[]; + static TrackType const Track8[]; + static TrackType const Track7[]; + static TrackType const Track6[]; + static TrackType const Track5[]; + static TrackType const Track4[]; + static TrackType const Track3[]; + static TrackType const Track2[]; + static TrackType const Track1[24]; +}; + +//PG inline DriveClass::TrackControlType operator |(DriveClass::TrackControlType, DriveClass::TrackControlType); +//PG inline DriveClass::TrackControlType operator &(DriveClass::TrackControlType, DriveClass::TrackControlType); +//PG inline DriveClass::TrackControlType operator ~(DriveClass::TrackControlType); + + +#endif diff --git a/code/redalert/drop.cpp b/code/redalert/drop.cpp new file mode 100644 index 0000000..ebd4185 --- /dev/null +++ b/code/redalert/drop.cpp @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DROP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DROP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/24/96 * + * * + * Last Update : January 24, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "drop.h" + + +DropListClass::DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9*RESFACTOR, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + Fancy_Text_Print("", 0, 0, 0, 0, flags); + EditClass::Height = FontHeight+1; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + +DropListClass & DropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((DropListClass &)EditClass::Add(object)); +} + +DropListClass & DropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((DropListClass &)EditClass::Add_Tail(object)); +} + +DropListClass & DropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((DropListClass &)EditClass::Add_Head(object)); +} + +DropListClass * DropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((DropListClass *)EditClass::Remove()); +} + +int DropListClass::Add_Item(char const * text) +{ + strncpy(String, text, MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(text)); +} + +char const * DropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + +int DropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + +void DropListClass::Set_Selected_Index(int index) +{ + if (index < List.Count()) { + List.Set_Selected_Index(index); + strcpy(String, List.Get_Item(Current_Index())); + } else { + String[0] = '\0'; + } +} + + +void DropListClass::Clear_Focus(void) +{ + Collapse(); +} + +void DropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + +void DropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9*RESFACTOR; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + +void DropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +DropListClass & DropListClass::operator = (DropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +DropListClass::DropListClass(DropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +void DropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +void DropListClass::Set_Selected_Index(char const * text) +{ + if (text) { + for (int index = 0; index < Count(); index++) { + if (stricmp(text, List.Get_Item(index)) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} + +#ifdef WOLAPI_INTEGRATION +void DropListClass::Flag_To_Redraw(void) +{ + if( IsDropped ) + List.Flag_To_Redraw(); + EditClass::Flag_To_Redraw(); +} +#endif diff --git a/code/redalert/drop.h b/code/redalert/drop.h new file mode 100644 index 0000000..7fcb258 --- /dev/null +++ b/code/redalert/drop.h @@ -0,0 +1,350 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DROP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : DROP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DROP_H +#define DROP_H + +#include "list.h" +#include "edit.h" + +class DropListClass : public EditClass { + public: + DropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + virtual ~DropListClass(void) {}; + + virtual DropListClass & Add(LinkClass & object); + virtual DropListClass & Add_Tail(LinkClass & object); + virtual DropListClass & Add_Head(LinkClass & object); + virtual DropListClass * Remove(void); + virtual void Zap(void); + + virtual int Add_Item(char const * text); + virtual char const * Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual char const * Get_Item(int index) const {return(List.Get_Item(index));}; + +#ifdef WOLAPI_INTEGRATION + virtual void Flag_To_Redraw(void); +#endif + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + DropListClass & operator = (DropListClass const & list); + DropListClass(DropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + ListClass List; +}; + + + + +template +class TDropListClass : public EditClass { + public: + TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down); + TDropListClass(TDropListClass const & list); + virtual ~TDropListClass(void) {}; + + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual TDropListClass & Add(LinkClass & object); + virtual TDropListClass & Add_Tail(LinkClass & object); + virtual TDropListClass & Add_Head(LinkClass & object); + virtual TDropListClass * Remove(void); + void Zap(void); + + virtual int Add_Item(T text); + virtual T Current_Item(void); + virtual int Current_Index(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom); + virtual void Clear_Focus(void); + virtual int Count(void) const {return(List.Count());}; + virtual T Get_Item(int index) const {return(List.Get_Item(index));}; + + + void Expand(void); + void Collapse(void); + + virtual void Set_Position(int x, int y); + + TDropListClass & operator = (TDropListClass const & list); + + /* + ** Indicates whether the list box has dropped down or not. + */ + unsigned IsDropped:1; + + /* + ** Height of list box when it is expanded. + */ + int ListHeight; + + /* + ** Drop down button. + */ + ShapeButtonClass DropButton; + + /* + ** List object when it is expanded. + */ + TListClass List; +}; + + +template +TDropListClass::TDropListClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, void const * up, void const * down) : + EditClass(id, text, max_len, flags, x, y, w, 9, ALPHANUMERIC), + IsDropped(false), + ListHeight(h), + DropButton(0, down, x+w, y), + List(0, x, y+Get_Build_Frame_Height(down), w+Get_Build_Frame_Width(down), h, flags, up, down) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Zap(void) +{ + Collapse(); + List.Zap(); + DropButton.Zap(); + EditClass::Zap(); +} + + +template +TDropListClass & TDropListClass::Add(LinkClass & object) +{ + DropButton.Add(object); + return((TDropListClass &)EditClass::Add(object)); +} + + +template +TDropListClass & TDropListClass::Add_Tail(LinkClass & object) +{ + DropButton.Add_Tail(object); + return((TDropListClass &)EditClass::Add_Tail(object)); +} + + +template +TDropListClass & TDropListClass::Add_Head(LinkClass & object) +{ + DropButton.Add_Head(object); + return((TDropListClass &)EditClass::Add_Head(object)); +} + + +template +TDropListClass * TDropListClass::Remove(void) +{ + if (IsDropped) { + Collapse(); + } + DropButton.Remove(); + return((TDropListClass *)EditClass::Remove()); +} + + +template +int TDropListClass::Add_Item(T item) +{ + strncpy(String, item->Description(), MaxLength); + Flag_To_Redraw(); + return(List.Add_Item(item)); +} + + +template +T TDropListClass::Current_Item(void) +{ + return(List.Current_Item()); +} + + +template +int TDropListClass::Current_Index(void) +{ + return(List.Current_Index()); +} + + +template +void TDropListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Set_Selected_Index(index); + strncpy(String, List.Get_Item(Current_Index())->Description(), MaxLength); + } else { + String[0] = '\0'; + } +} + + +template +void TDropListClass::Clear_Focus(void) +{ + Collapse(); +} + + +template +void TDropListClass::Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom) +{ + if (&whom == &DropButton) { + if (flags & LEFTRELEASE) { + if (IsDropped) { + Collapse(); + key = (KeyNumType)(ID | KN_BUTTON); + } else { + Expand(); + } + } + } + + if (&whom == &List) { + strncpy(String, List.Current_Item()->Description(), MaxLength); + Flag_To_Redraw(); + key = (KeyNumType)(ID | KN_BUTTON); + } +} + + +template +void TDropListClass::Expand(void) +{ + if (!IsDropped) { + List.X = X; + List.Y = Y+9; + List.Width = Width; + List.Height = ListHeight; + List.Add(Head_Of_List()); + List.Flag_To_Redraw(); + IsDropped = true; + } +} + + +template +void TDropListClass::Collapse(void) +{ + if (IsDropped) { + List.Remove(); + IsDropped = false; + } +} + + +template +TDropListClass & TDropListClass::operator = (TDropListClass const & list) +{ + if (this == &list) return(*this); + EditClass::operator =(list); + List = list.List; + IsDropped = list.IsDropped; + ListHeight = list.ListHeight; + DropButton = list.DropButton; + List.Make_Peer(*this); + DropButton.Make_Peer(*this); + return(*this); +} + + +template +TDropListClass::TDropListClass(TDropListClass const & list) : + EditClass(list), + IsDropped(list.IsDropped), + ListHeight(list.ListHeight), + DropButton(list.DropButton), + List(list.List) +{ + List.Make_Peer(*this); + DropButton.Make_Peer(*this); +} + + +template +void TDropListClass::Set_Position(int x, int y) +{ + EditClass::Set_Position(x, y); + List.Set_Position(x, y + Get_Build_Frame_Height(DropButton.Get_Shape_Data())); + DropButton.Set_Position(x + Width, y); +} + + +template +void TDropListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == List.Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/code/redalert/dtable.cpp b/code/redalert/dtable.cpp new file mode 100644 index 0000000..4f74291 --- /dev/null +++ b/code/redalert/dtable.cpp @@ -0,0 +1,1443 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +long DiffTable[] = { + 0, // Index = 0, Token = 0 + 1, // Index = 0, Token = 1 + 3, // Index = 0, Token = 2 + 4, // Index = 0, Token = 3 + 7, // Index = 0, Token = 4 + 8, // Index = 0, Token = 5 + 10, // Index = 0, Token = 6 + 11, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + -1, // Index = 0, Token = 9 + -3, // Index = 0, Token = 10 + -4, // Index = 0, Token = 11 + -7, // Index = 0, Token = 12 + -8, // Index = 0, Token = 13 + -10, // Index = 0, Token = 14 + -11, // Index = 0, Token = 15 + 1, // Index = 1, Token = 0 + 3, // Index = 1, Token = 1 + 5, // Index = 1, Token = 2 + 7, // Index = 1, Token = 3 + 9, // Index = 1, Token = 4 + 11, // Index = 1, Token = 5 + 13, // Index = 1, Token = 6 + 15, // Index = 1, Token = 7 + -1, // Index = 1, Token = 8 + -3, // Index = 1, Token = 9 + -5, // Index = 1, Token = 10 + -7, // Index = 1, Token = 11 + -9, // Index = 1, Token = 12 + -11, // Index = 1, Token = 13 + -13, // Index = 1, Token = 14 + -15, // Index = 1, Token = 15 + 1, // Index = 2, Token = 0 + 3, // Index = 2, Token = 1 + 5, // Index = 2, Token = 2 + 7, // Index = 2, Token = 3 + 10, // Index = 2, Token = 4 + 12, // Index = 2, Token = 5 + 14, // Index = 2, Token = 6 + 16, // Index = 2, Token = 7 + -1, // Index = 2, Token = 8 + -3, // Index = 2, Token = 9 + -5, // Index = 2, Token = 10 + -7, // Index = 2, Token = 11 + -10, // Index = 2, Token = 12 + -12, // Index = 2, Token = 13 + -14, // Index = 2, Token = 14 + -16, // Index = 2, Token = 15 + 1, // Index = 3, Token = 0 + 3, // Index = 3, Token = 1 + 6, // Index = 3, Token = 2 + 8, // Index = 3, Token = 3 + 11, // Index = 3, Token = 4 + 13, // Index = 3, Token = 5 + 16, // Index = 3, Token = 6 + 18, // Index = 3, Token = 7 + -1, // Index = 3, Token = 8 + -3, // Index = 3, Token = 9 + -6, // Index = 3, Token = 10 + -8, // Index = 3, Token = 11 + -11, // Index = 3, Token = 12 + -13, // Index = 3, Token = 13 + -16, // Index = 3, Token = 14 + -18, // Index = 3, Token = 15 + 1, // Index = 4, Token = 0 + 3, // Index = 4, Token = 1 + 6, // Index = 4, Token = 2 + 8, // Index = 4, Token = 3 + 12, // Index = 4, Token = 4 + 14, // Index = 4, Token = 5 + 17, // Index = 4, Token = 6 + 19, // Index = 4, Token = 7 + -1, // Index = 4, Token = 8 + -3, // Index = 4, Token = 9 + -6, // Index = 4, Token = 10 + -8, // Index = 4, Token = 11 + -12, // Index = 4, Token = 12 + -14, // Index = 4, Token = 13 + -17, // Index = 4, Token = 14 + -19, // Index = 4, Token = 15 + 1, // Index = 5, Token = 0 + 4, // Index = 5, Token = 1 + 7, // Index = 5, Token = 2 + 10, // Index = 5, Token = 3 + 13, // Index = 5, Token = 4 + 16, // Index = 5, Token = 5 + 19, // Index = 5, Token = 6 + 22, // Index = 5, Token = 7 + -1, // Index = 5, Token = 8 + -4, // Index = 5, Token = 9 + -7, // Index = 5, Token = 10 + -10, // Index = 5, Token = 11 + -13, // Index = 5, Token = 12 + -16, // Index = 5, Token = 13 + -19, // Index = 5, Token = 14 + -22, // Index = 5, Token = 15 + 1, // Index = 6, Token = 0 + 4, // Index = 6, Token = 1 + 7, // Index = 6, Token = 2 + 10, // Index = 6, Token = 3 + 14, // Index = 6, Token = 4 + 17, // Index = 6, Token = 5 + 20, // Index = 6, Token = 6 + 23, // Index = 6, Token = 7 + -1, // Index = 6, Token = 8 + -4, // Index = 6, Token = 9 + -7, // Index = 6, Token = 10 + -10, // Index = 6, Token = 11 + -14, // Index = 6, Token = 12 + -17, // Index = 6, Token = 13 + -20, // Index = 6, Token = 14 + -23, // Index = 6, Token = 15 + 1, // Index = 7, Token = 0 + 4, // Index = 7, Token = 1 + 8, // Index = 7, Token = 2 + 11, // Index = 7, Token = 3 + 15, // Index = 7, Token = 4 + 18, // Index = 7, Token = 5 + 22, // Index = 7, Token = 6 + 25, // Index = 7, Token = 7 + -1, // Index = 7, Token = 8 + -4, // Index = 7, Token = 9 + -8, // Index = 7, Token = 10 + -11, // Index = 7, Token = 11 + -15, // Index = 7, Token = 12 + -18, // Index = 7, Token = 13 + -22, // Index = 7, Token = 14 + -25, // Index = 7, Token = 15 + 2, // Index = 8, Token = 0 + 6, // Index = 8, Token = 1 + 10, // Index = 8, Token = 2 + 14, // Index = 8, Token = 3 + 18, // Index = 8, Token = 4 + 22, // Index = 8, Token = 5 + 26, // Index = 8, Token = 6 + 30, // Index = 8, Token = 7 + -2, // Index = 8, Token = 8 + -6, // Index = 8, Token = 9 + -10, // Index = 8, Token = 10 + -14, // Index = 8, Token = 11 + -18, // Index = 8, Token = 12 + -22, // Index = 8, Token = 13 + -26, // Index = 8, Token = 14 + -30, // Index = 8, Token = 15 + 2, // Index = 9, Token = 0 + 6, // Index = 9, Token = 1 + 10, // Index = 9, Token = 2 + 14, // Index = 9, Token = 3 + 19, // Index = 9, Token = 4 + 23, // Index = 9, Token = 5 + 27, // Index = 9, Token = 6 + 31, // Index = 9, Token = 7 + -2, // Index = 9, Token = 8 + -6, // Index = 9, Token = 9 + -10, // Index = 9, Token = 10 + -14, // Index = 9, Token = 11 + -19, // Index = 9, Token = 12 + -23, // Index = 9, Token = 13 + -27, // Index = 9, Token = 14 + -31, // Index = 9, Token = 15 + 2, // Index = 10, Token = 0 + 6, // Index = 10, Token = 1 + 11, // Index = 10, Token = 2 + 15, // Index = 10, Token = 3 + 21, // Index = 10, Token = 4 + 25, // Index = 10, Token = 5 + 30, // Index = 10, Token = 6 + 34, // Index = 10, Token = 7 + -2, // Index = 10, Token = 8 + -6, // Index = 10, Token = 9 + -11, // Index = 10, Token = 10 + -15, // Index = 10, Token = 11 + -21, // Index = 10, Token = 12 + -25, // Index = 10, Token = 13 + -30, // Index = 10, Token = 14 + -34, // Index = 10, Token = 15 + 2, // Index = 11, Token = 0 + 7, // Index = 11, Token = 1 + 12, // Index = 11, Token = 2 + 17, // Index = 11, Token = 3 + 23, // Index = 11, Token = 4 + 28, // Index = 11, Token = 5 + 33, // Index = 11, Token = 6 + 38, // Index = 11, Token = 7 + -2, // Index = 11, Token = 8 + -7, // Index = 11, Token = 9 + -12, // Index = 11, Token = 10 + -17, // Index = 11, Token = 11 + -23, // Index = 11, Token = 12 + -28, // Index = 11, Token = 13 + -33, // Index = 11, Token = 14 + -38, // Index = 11, Token = 15 + 2, // Index = 12, Token = 0 + 7, // Index = 12, Token = 1 + 13, // Index = 12, Token = 2 + 18, // Index = 12, Token = 3 + 25, // Index = 12, Token = 4 + 30, // Index = 12, Token = 5 + 36, // Index = 12, Token = 6 + 41, // Index = 12, Token = 7 + -2, // Index = 12, Token = 8 + -7, // Index = 12, Token = 9 + -13, // Index = 12, Token = 10 + -18, // Index = 12, Token = 11 + -25, // Index = 12, Token = 12 + -30, // Index = 12, Token = 13 + -36, // Index = 12, Token = 14 + -41, // Index = 12, Token = 15 + 3, // Index = 13, Token = 0 + 9, // Index = 13, Token = 1 + 15, // Index = 13, Token = 2 + 21, // Index = 13, Token = 3 + 28, // Index = 13, Token = 4 + 34, // Index = 13, Token = 5 + 40, // Index = 13, Token = 6 + 46, // Index = 13, Token = 7 + -3, // Index = 13, Token = 8 + -9, // Index = 13, Token = 9 + -15, // Index = 13, Token = 10 + -21, // Index = 13, Token = 11 + -28, // Index = 13, Token = 12 + -34, // Index = 13, Token = 13 + -40, // Index = 13, Token = 14 + -46, // Index = 13, Token = 15 + 3, // Index = 14, Token = 0 + 10, // Index = 14, Token = 1 + 17, // Index = 14, Token = 2 + 24, // Index = 14, Token = 3 + 31, // Index = 14, Token = 4 + 38, // Index = 14, Token = 5 + 45, // Index = 14, Token = 6 + 52, // Index = 14, Token = 7 + -3, // Index = 14, Token = 8 + -10, // Index = 14, Token = 9 + -17, // Index = 14, Token = 10 + -24, // Index = 14, Token = 11 + -31, // Index = 14, Token = 12 + -38, // Index = 14, Token = 13 + -45, // Index = 14, Token = 14 + -52, // Index = 14, Token = 15 + 3, // Index = 15, Token = 0 + 10, // Index = 15, Token = 1 + 18, // Index = 15, Token = 2 + 25, // Index = 15, Token = 3 + 34, // Index = 15, Token = 4 + 41, // Index = 15, Token = 5 + 49, // Index = 15, Token = 6 + 56, // Index = 15, Token = 7 + -3, // Index = 15, Token = 8 + -10, // Index = 15, Token = 9 + -18, // Index = 15, Token = 10 + -25, // Index = 15, Token = 11 + -34, // Index = 15, Token = 12 + -41, // Index = 15, Token = 13 + -49, // Index = 15, Token = 14 + -56, // Index = 15, Token = 15 + 4, // Index = 16, Token = 0 + 12, // Index = 16, Token = 1 + 21, // Index = 16, Token = 2 + 29, // Index = 16, Token = 3 + 38, // Index = 16, Token = 4 + 46, // Index = 16, Token = 5 + 55, // Index = 16, Token = 6 + 63, // Index = 16, Token = 7 + -4, // Index = 16, Token = 8 + -12, // Index = 16, Token = 9 + -21, // Index = 16, Token = 10 + -29, // Index = 16, Token = 11 + -38, // Index = 16, Token = 12 + -46, // Index = 16, Token = 13 + -55, // Index = 16, Token = 14 + -63, // Index = 16, Token = 15 + 4, // Index = 17, Token = 0 + 13, // Index = 17, Token = 1 + 22, // Index = 17, Token = 2 + 31, // Index = 17, Token = 3 + 41, // Index = 17, Token = 4 + 50, // Index = 17, Token = 5 + 59, // Index = 17, Token = 6 + 68, // Index = 17, Token = 7 + -4, // Index = 17, Token = 8 + -13, // Index = 17, Token = 9 + -22, // Index = 17, Token = 10 + -31, // Index = 17, Token = 11 + -41, // Index = 17, Token = 12 + -50, // Index = 17, Token = 13 + -59, // Index = 17, Token = 14 + -68, // Index = 17, Token = 15 + 5, // Index = 18, Token = 0 + 15, // Index = 18, Token = 1 + 25, // Index = 18, Token = 2 + 35, // Index = 18, Token = 3 + 46, // Index = 18, Token = 4 + 56, // Index = 18, Token = 5 + 66, // Index = 18, Token = 6 + 76, // Index = 18, Token = 7 + -5, // Index = 18, Token = 8 + -15, // Index = 18, Token = 9 + -25, // Index = 18, Token = 10 + -35, // Index = 18, Token = 11 + -46, // Index = 18, Token = 12 + -56, // Index = 18, Token = 13 + -66, // Index = 18, Token = 14 + -76, // Index = 18, Token = 15 + 5, // Index = 19, Token = 0 + 16, // Index = 19, Token = 1 + 27, // Index = 19, Token = 2 + 38, // Index = 19, Token = 3 + 50, // Index = 19, Token = 4 + 61, // Index = 19, Token = 5 + 72, // Index = 19, Token = 6 + 83, // Index = 19, Token = 7 + -5, // Index = 19, Token = 8 + -16, // Index = 19, Token = 9 + -27, // Index = 19, Token = 10 + -38, // Index = 19, Token = 11 + -50, // Index = 19, Token = 12 + -61, // Index = 19, Token = 13 + -72, // Index = 19, Token = 14 + -83, // Index = 19, Token = 15 + 6, // Index = 20, Token = 0 + 18, // Index = 20, Token = 1 + 31, // Index = 20, Token = 2 + 43, // Index = 20, Token = 3 + 56, // Index = 20, Token = 4 + 68, // Index = 20, Token = 5 + 81, // Index = 20, Token = 6 + 93, // Index = 20, Token = 7 + -6, // Index = 20, Token = 8 + -18, // Index = 20, Token = 9 + -31, // Index = 20, Token = 10 + -43, // Index = 20, Token = 11 + -56, // Index = 20, Token = 12 + -68, // Index = 20, Token = 13 + -81, // Index = 20, Token = 14 + -93, // Index = 20, Token = 15 + 6, // Index = 21, Token = 0 + 19, // Index = 21, Token = 1 + 33, // Index = 21, Token = 2 + 46, // Index = 21, Token = 3 + 61, // Index = 21, Token = 4 + 74, // Index = 21, Token = 5 + 88, // Index = 21, Token = 6 + 101, // Index = 21, Token = 7 + -6, // Index = 21, Token = 8 + -19, // Index = 21, Token = 9 + -33, // Index = 21, Token = 10 + -46, // Index = 21, Token = 11 + -61, // Index = 21, Token = 12 + -74, // Index = 21, Token = 13 + -88, // Index = 21, Token = 14 + -101, // Index = 21, Token = 15 + 7, // Index = 22, Token = 0 + 22, // Index = 22, Token = 1 + 37, // Index = 22, Token = 2 + 52, // Index = 22, Token = 3 + 67, // Index = 22, Token = 4 + 82, // Index = 22, Token = 5 + 97, // Index = 22, Token = 6 + 112, // Index = 22, Token = 7 + -7, // Index = 22, Token = 8 + -22, // Index = 22, Token = 9 + -37, // Index = 22, Token = 10 + -52, // Index = 22, Token = 11 + -67, // Index = 22, Token = 12 + -82, // Index = 22, Token = 13 + -97, // Index = 22, Token = 14 + -112, // Index = 22, Token = 15 + 8, // Index = 23, Token = 0 + 24, // Index = 23, Token = 1 + 41, // Index = 23, Token = 2 + 57, // Index = 23, Token = 3 + 74, // Index = 23, Token = 4 + 90, // Index = 23, Token = 5 + 107, // Index = 23, Token = 6 + 123, // Index = 23, Token = 7 + -8, // Index = 23, Token = 8 + -24, // Index = 23, Token = 9 + -41, // Index = 23, Token = 10 + -57, // Index = 23, Token = 11 + -74, // Index = 23, Token = 12 + -90, // Index = 23, Token = 13 + -107, // Index = 23, Token = 14 + -123, // Index = 23, Token = 15 + 9, // Index = 24, Token = 0 + 27, // Index = 24, Token = 1 + 45, // Index = 24, Token = 2 + 63, // Index = 24, Token = 3 + 82, // Index = 24, Token = 4 + 100, // Index = 24, Token = 5 + 118, // Index = 24, Token = 6 + 136, // Index = 24, Token = 7 + -9, // Index = 24, Token = 8 + -27, // Index = 24, Token = 9 + -45, // Index = 24, Token = 10 + -63, // Index = 24, Token = 11 + -82, // Index = 24, Token = 12 + -100, // Index = 24, Token = 13 + -118, // Index = 24, Token = 14 + -136, // Index = 24, Token = 15 + 10, // Index = 25, Token = 0 + 30, // Index = 25, Token = 1 + 50, // Index = 25, Token = 2 + 70, // Index = 25, Token = 3 + 90, // Index = 25, Token = 4 + 110, // Index = 25, Token = 5 + 130, // Index = 25, Token = 6 + 150, // Index = 25, Token = 7 + -10, // Index = 25, Token = 8 + -30, // Index = 25, Token = 9 + -50, // Index = 25, Token = 10 + -70, // Index = 25, Token = 11 + -90, // Index = 25, Token = 12 + -110, // Index = 25, Token = 13 + -130, // Index = 25, Token = 14 + -150, // Index = 25, Token = 15 + 11, // Index = 26, Token = 0 + 33, // Index = 26, Token = 1 + 55, // Index = 26, Token = 2 + 77, // Index = 26, Token = 3 + 99, // Index = 26, Token = 4 + 121, // Index = 26, Token = 5 + 143, // Index = 26, Token = 6 + 165, // Index = 26, Token = 7 + -11, // Index = 26, Token = 8 + -33, // Index = 26, Token = 9 + -55, // Index = 26, Token = 10 + -77, // Index = 26, Token = 11 + -99, // Index = 26, Token = 12 + -121, // Index = 26, Token = 13 + -143, // Index = 26, Token = 14 + -165, // Index = 26, Token = 15 + 12, // Index = 27, Token = 0 + 36, // Index = 27, Token = 1 + 60, // Index = 27, Token = 2 + 84, // Index = 27, Token = 3 + 109, // Index = 27, Token = 4 + 133, // Index = 27, Token = 5 + 157, // Index = 27, Token = 6 + 181, // Index = 27, Token = 7 + -12, // Index = 27, Token = 8 + -36, // Index = 27, Token = 9 + -60, // Index = 27, Token = 10 + -84, // Index = 27, Token = 11 + -109, // Index = 27, Token = 12 + -133, // Index = 27, Token = 13 + -157, // Index = 27, Token = 14 + -181, // Index = 27, Token = 15 + 13, // Index = 28, Token = 0 + 39, // Index = 28, Token = 1 + 66, // Index = 28, Token = 2 + 92, // Index = 28, Token = 3 + 120, // Index = 28, Token = 4 + 146, // Index = 28, Token = 5 + 173, // Index = 28, Token = 6 + 199, // Index = 28, Token = 7 + -13, // Index = 28, Token = 8 + -39, // Index = 28, Token = 9 + -66, // Index = 28, Token = 10 + -92, // Index = 28, Token = 11 + -120, // Index = 28, Token = 12 + -146, // Index = 28, Token = 13 + -173, // Index = 28, Token = 14 + -199, // Index = 28, Token = 15 + 14, // Index = 29, Token = 0 + 43, // Index = 29, Token = 1 + 73, // Index = 29, Token = 2 + 102, // Index = 29, Token = 3 + 132, // Index = 29, Token = 4 + 161, // Index = 29, Token = 5 + 191, // Index = 29, Token = 6 + 220, // Index = 29, Token = 7 + -14, // Index = 29, Token = 8 + -43, // Index = 29, Token = 9 + -73, // Index = 29, Token = 10 + -102, // Index = 29, Token = 11 + -132, // Index = 29, Token = 12 + -161, // Index = 29, Token = 13 + -191, // Index = 29, Token = 14 + -220, // Index = 29, Token = 15 + 16, // Index = 30, Token = 0 + 48, // Index = 30, Token = 1 + 81, // Index = 30, Token = 2 + 113, // Index = 30, Token = 3 + 146, // Index = 30, Token = 4 + 178, // Index = 30, Token = 5 + 211, // Index = 30, Token = 6 + 243, // Index = 30, Token = 7 + -16, // Index = 30, Token = 8 + -48, // Index = 30, Token = 9 + -81, // Index = 30, Token = 10 + -113, // Index = 30, Token = 11 + -146, // Index = 30, Token = 12 + -178, // Index = 30, Token = 13 + -211, // Index = 30, Token = 14 + -243, // Index = 30, Token = 15 + 17, // Index = 31, Token = 0 + 52, // Index = 31, Token = 1 + 88, // Index = 31, Token = 2 + 123, // Index = 31, Token = 3 + 160, // Index = 31, Token = 4 + 195, // Index = 31, Token = 5 + 231, // Index = 31, Token = 6 + 266, // Index = 31, Token = 7 + -17, // Index = 31, Token = 8 + -52, // Index = 31, Token = 9 + -88, // Index = 31, Token = 10 + -123, // Index = 31, Token = 11 + -160, // Index = 31, Token = 12 + -195, // Index = 31, Token = 13 + -231, // Index = 31, Token = 14 + -266, // Index = 31, Token = 15 + 19, // Index = 32, Token = 0 + 58, // Index = 32, Token = 1 + 97, // Index = 32, Token = 2 + 136, // Index = 32, Token = 3 + 176, // Index = 32, Token = 4 + 215, // Index = 32, Token = 5 + 254, // Index = 32, Token = 6 + 293, // Index = 32, Token = 7 + -19, // Index = 32, Token = 8 + -58, // Index = 32, Token = 9 + -97, // Index = 32, Token = 10 + -136, // Index = 32, Token = 11 + -176, // Index = 32, Token = 12 + -215, // Index = 32, Token = 13 + -254, // Index = 32, Token = 14 + -293, // Index = 32, Token = 15 + 21, // Index = 33, Token = 0 + 64, // Index = 33, Token = 1 + 107, // Index = 33, Token = 2 + 150, // Index = 33, Token = 3 + 194, // Index = 33, Token = 4 + 237, // Index = 33, Token = 5 + 280, // Index = 33, Token = 6 + 323, // Index = 33, Token = 7 + -21, // Index = 33, Token = 8 + -64, // Index = 33, Token = 9 + -107, // Index = 33, Token = 10 + -150, // Index = 33, Token = 11 + -194, // Index = 33, Token = 12 + -237, // Index = 33, Token = 13 + -280, // Index = 33, Token = 14 + -323, // Index = 33, Token = 15 + 23, // Index = 34, Token = 0 + 70, // Index = 34, Token = 1 + 118, // Index = 34, Token = 2 + 165, // Index = 34, Token = 3 + 213, // Index = 34, Token = 4 + 260, // Index = 34, Token = 5 + 308, // Index = 34, Token = 6 + 355, // Index = 34, Token = 7 + -23, // Index = 34, Token = 8 + -70, // Index = 34, Token = 9 + -118, // Index = 34, Token = 10 + -165, // Index = 34, Token = 11 + -213, // Index = 34, Token = 12 + -260, // Index = 34, Token = 13 + -308, // Index = 34, Token = 14 + -355, // Index = 34, Token = 15 + 26, // Index = 35, Token = 0 + 78, // Index = 35, Token = 1 + 130, // Index = 35, Token = 2 + 182, // Index = 35, Token = 3 + 235, // Index = 35, Token = 4 + 287, // Index = 35, Token = 5 + 339, // Index = 35, Token = 6 + 391, // Index = 35, Token = 7 + -26, // Index = 35, Token = 8 + -78, // Index = 35, Token = 9 + -130, // Index = 35, Token = 10 + -182, // Index = 35, Token = 11 + -235, // Index = 35, Token = 12 + -287, // Index = 35, Token = 13 + -339, // Index = 35, Token = 14 + -391, // Index = 35, Token = 15 + 28, // Index = 36, Token = 0 + 85, // Index = 36, Token = 1 + 143, // Index = 36, Token = 2 + 200, // Index = 36, Token = 3 + 258, // Index = 36, Token = 4 + 315, // Index = 36, Token = 5 + 373, // Index = 36, Token = 6 + 430, // Index = 36, Token = 7 + -28, // Index = 36, Token = 8 + -85, // Index = 36, Token = 9 + -143, // Index = 36, Token = 10 + -200, // Index = 36, Token = 11 + -258, // Index = 36, Token = 12 + -315, // Index = 36, Token = 13 + -373, // Index = 36, Token = 14 + -430, // Index = 36, Token = 15 + 31, // Index = 37, Token = 0 + 94, // Index = 37, Token = 1 + 157, // Index = 37, Token = 2 + 220, // Index = 37, Token = 3 + 284, // Index = 37, Token = 4 + 347, // Index = 37, Token = 5 + 410, // Index = 37, Token = 6 + 473, // Index = 37, Token = 7 + -31, // Index = 37, Token = 8 + -94, // Index = 37, Token = 9 + -157, // Index = 37, Token = 10 + -220, // Index = 37, Token = 11 + -284, // Index = 37, Token = 12 + -347, // Index = 37, Token = 13 + -410, // Index = 37, Token = 14 + -473, // Index = 37, Token = 15 + 34, // Index = 38, Token = 0 + 103, // Index = 38, Token = 1 + 173, // Index = 38, Token = 2 + 242, // Index = 38, Token = 3 + 313, // Index = 38, Token = 4 + 382, // Index = 38, Token = 5 + 452, // Index = 38, Token = 6 + 521, // Index = 38, Token = 7 + -34, // Index = 38, Token = 8 + -103, // Index = 38, Token = 9 + -173, // Index = 38, Token = 10 + -242, // Index = 38, Token = 11 + -313, // Index = 38, Token = 12 + -382, // Index = 38, Token = 13 + -452, // Index = 38, Token = 14 + -521, // Index = 38, Token = 15 + 38, // Index = 39, Token = 0 + 114, // Index = 39, Token = 1 + 191, // Index = 39, Token = 2 + 267, // Index = 39, Token = 3 + 345, // Index = 39, Token = 4 + 421, // Index = 39, Token = 5 + 498, // Index = 39, Token = 6 + 574, // Index = 39, Token = 7 + -38, // Index = 39, Token = 8 + -114, // Index = 39, Token = 9 + -191, // Index = 39, Token = 10 + -267, // Index = 39, Token = 11 + -345, // Index = 39, Token = 12 + -421, // Index = 39, Token = 13 + -498, // Index = 39, Token = 14 + -574, // Index = 39, Token = 15 + 42, // Index = 40, Token = 0 + 126, // Index = 40, Token = 1 + 210, // Index = 40, Token = 2 + 294, // Index = 40, Token = 3 + 379, // Index = 40, Token = 4 + 463, // Index = 40, Token = 5 + 547, // Index = 40, Token = 6 + 631, // Index = 40, Token = 7 + -42, // Index = 40, Token = 8 + -126, // Index = 40, Token = 9 + -210, // Index = 40, Token = 10 + -294, // Index = 40, Token = 11 + -379, // Index = 40, Token = 12 + -463, // Index = 40, Token = 13 + -547, // Index = 40, Token = 14 + -631, // Index = 40, Token = 15 + 46, // Index = 41, Token = 0 + 138, // Index = 41, Token = 1 + 231, // Index = 41, Token = 2 + 323, // Index = 41, Token = 3 + 417, // Index = 41, Token = 4 + 509, // Index = 41, Token = 5 + 602, // Index = 41, Token = 6 + 694, // Index = 41, Token = 7 + -46, // Index = 41, Token = 8 + -138, // Index = 41, Token = 9 + -231, // Index = 41, Token = 10 + -323, // Index = 41, Token = 11 + -417, // Index = 41, Token = 12 + -509, // Index = 41, Token = 13 + -602, // Index = 41, Token = 14 + -694, // Index = 41, Token = 15 + 51, // Index = 42, Token = 0 + 153, // Index = 42, Token = 1 + 255, // Index = 42, Token = 2 + 357, // Index = 42, Token = 3 + 459, // Index = 42, Token = 4 + 561, // Index = 42, Token = 5 + 663, // Index = 42, Token = 6 + 765, // Index = 42, Token = 7 + -51, // Index = 42, Token = 8 + -153, // Index = 42, Token = 9 + -255, // Index = 42, Token = 10 + -357, // Index = 42, Token = 11 + -459, // Index = 42, Token = 12 + -561, // Index = 42, Token = 13 + -663, // Index = 42, Token = 14 + -765, // Index = 42, Token = 15 + 56, // Index = 43, Token = 0 + 168, // Index = 43, Token = 1 + 280, // Index = 43, Token = 2 + 392, // Index = 43, Token = 3 + 505, // Index = 43, Token = 4 + 617, // Index = 43, Token = 5 + 729, // Index = 43, Token = 6 + 841, // Index = 43, Token = 7 + -56, // Index = 43, Token = 8 + -168, // Index = 43, Token = 9 + -280, // Index = 43, Token = 10 + -392, // Index = 43, Token = 11 + -505, // Index = 43, Token = 12 + -617, // Index = 43, Token = 13 + -729, // Index = 43, Token = 14 + -841, // Index = 43, Token = 15 + 61, // Index = 44, Token = 0 + 184, // Index = 44, Token = 1 + 308, // Index = 44, Token = 2 + 431, // Index = 44, Token = 3 + 555, // Index = 44, Token = 4 + 678, // Index = 44, Token = 5 + 802, // Index = 44, Token = 6 + 925, // Index = 44, Token = 7 + -61, // Index = 44, Token = 8 + -184, // Index = 44, Token = 9 + -308, // Index = 44, Token = 10 + -431, // Index = 44, Token = 11 + -555, // Index = 44, Token = 12 + -678, // Index = 44, Token = 13 + -802, // Index = 44, Token = 14 + -925, // Index = 44, Token = 15 + 68, // Index = 45, Token = 0 + 204, // Index = 45, Token = 1 + 340, // Index = 45, Token = 2 + 476, // Index = 45, Token = 3 + 612, // Index = 45, Token = 4 + 748, // Index = 45, Token = 5 + 884, // Index = 45, Token = 6 + 1020, // Index = 45, Token = 7 + -68, // Index = 45, Token = 8 + -204, // Index = 45, Token = 9 + -340, // Index = 45, Token = 10 + -476, // Index = 45, Token = 11 + -612, // Index = 45, Token = 12 + -748, // Index = 45, Token = 13 + -884, // Index = 45, Token = 14 + -1020, // Index = 45, Token = 15 + 74, // Index = 46, Token = 0 + 223, // Index = 46, Token = 1 + 373, // Index = 46, Token = 2 + 522, // Index = 46, Token = 3 + 672, // Index = 46, Token = 4 + 821, // Index = 46, Token = 5 + 971, // Index = 46, Token = 6 + 1120, // Index = 46, Token = 7 + -74, // Index = 46, Token = 8 + -223, // Index = 46, Token = 9 + -373, // Index = 46, Token = 10 + -522, // Index = 46, Token = 11 + -672, // Index = 46, Token = 12 + -821, // Index = 46, Token = 13 + -971, // Index = 46, Token = 14 + -1120, // Index = 46, Token = 15 + 82, // Index = 47, Token = 0 + 246, // Index = 47, Token = 1 + 411, // Index = 47, Token = 2 + 575, // Index = 47, Token = 3 + 740, // Index = 47, Token = 4 + 904, // Index = 47, Token = 5 + 1069, // Index = 47, Token = 6 + 1233, // Index = 47, Token = 7 + -82, // Index = 47, Token = 8 + -246, // Index = 47, Token = 9 + -411, // Index = 47, Token = 10 + -575, // Index = 47, Token = 11 + -740, // Index = 47, Token = 12 + -904, // Index = 47, Token = 13 + -1069, // Index = 47, Token = 14 + -1233, // Index = 47, Token = 15 + 90, // Index = 48, Token = 0 + 271, // Index = 48, Token = 1 + 452, // Index = 48, Token = 2 + 633, // Index = 48, Token = 3 + 814, // Index = 48, Token = 4 + 995, // Index = 48, Token = 5 + 1176, // Index = 48, Token = 6 + 1357, // Index = 48, Token = 7 + -90, // Index = 48, Token = 8 + -271, // Index = 48, Token = 9 + -452, // Index = 48, Token = 10 + -633, // Index = 48, Token = 11 + -814, // Index = 48, Token = 12 + -995, // Index = 48, Token = 13 + -1176, // Index = 48, Token = 14 + -1357, // Index = 48, Token = 15 + 99, // Index = 49, Token = 0 + 298, // Index = 49, Token = 1 + 497, // Index = 49, Token = 2 + 696, // Index = 49, Token = 3 + 895, // Index = 49, Token = 4 + 1094, // Index = 49, Token = 5 + 1293, // Index = 49, Token = 6 + 1492, // Index = 49, Token = 7 + -99, // Index = 49, Token = 8 + -298, // Index = 49, Token = 9 + -497, // Index = 49, Token = 10 + -696, // Index = 49, Token = 11 + -895, // Index = 49, Token = 12 + -1094, // Index = 49, Token = 13 + -1293, // Index = 49, Token = 14 + -1492, // Index = 49, Token = 15 + 109, // Index = 50, Token = 0 + 328, // Index = 50, Token = 1 + 547, // Index = 50, Token = 2 + 766, // Index = 50, Token = 3 + 985, // Index = 50, Token = 4 + 1204, // Index = 50, Token = 5 + 1423, // Index = 50, Token = 6 + 1642, // Index = 50, Token = 7 + -109, // Index = 50, Token = 8 + -328, // Index = 50, Token = 9 + -547, // Index = 50, Token = 10 + -766, // Index = 50, Token = 11 + -985, // Index = 50, Token = 12 + -1204, // Index = 50, Token = 13 + -1423, // Index = 50, Token = 14 + -1642, // Index = 50, Token = 15 + 120, // Index = 51, Token = 0 + 360, // Index = 51, Token = 1 + 601, // Index = 51, Token = 2 + 841, // Index = 51, Token = 3 + 1083, // Index = 51, Token = 4 + 1323, // Index = 51, Token = 5 + 1564, // Index = 51, Token = 6 + 1804, // Index = 51, Token = 7 + -120, // Index = 51, Token = 8 + -360, // Index = 51, Token = 9 + -601, // Index = 51, Token = 10 + -841, // Index = 51, Token = 11 + -1083, // Index = 51, Token = 12 + -1323, // Index = 51, Token = 13 + -1564, // Index = 51, Token = 14 + -1804, // Index = 51, Token = 15 + 132, // Index = 52, Token = 0 + 397, // Index = 52, Token = 1 + 662, // Index = 52, Token = 2 + 927, // Index = 52, Token = 3 + 1192, // Index = 52, Token = 4 + 1457, // Index = 52, Token = 5 + 1722, // Index = 52, Token = 6 + 1987, // Index = 52, Token = 7 + -132, // Index = 52, Token = 8 + -397, // Index = 52, Token = 9 + -662, // Index = 52, Token = 10 + -927, // Index = 52, Token = 11 + -1192, // Index = 52, Token = 12 + -1457, // Index = 52, Token = 13 + -1722, // Index = 52, Token = 14 + -1987, // Index = 52, Token = 15 + 145, // Index = 53, Token = 0 + 436, // Index = 53, Token = 1 + 728, // Index = 53, Token = 2 + 1019, // Index = 53, Token = 3 + 1311, // Index = 53, Token = 4 + 1602, // Index = 53, Token = 5 + 1894, // Index = 53, Token = 6 + 2185, // Index = 53, Token = 7 + -145, // Index = 53, Token = 8 + -436, // Index = 53, Token = 9 + -728, // Index = 53, Token = 10 + -1019, // Index = 53, Token = 11 + -1311, // Index = 53, Token = 12 + -1602, // Index = 53, Token = 13 + -1894, // Index = 53, Token = 14 + -2185, // Index = 53, Token = 15 + 160, // Index = 54, Token = 0 + 480, // Index = 54, Token = 1 + 801, // Index = 54, Token = 2 + 1121, // Index = 54, Token = 3 + 1442, // Index = 54, Token = 4 + 1762, // Index = 54, Token = 5 + 2083, // Index = 54, Token = 6 + 2403, // Index = 54, Token = 7 + -160, // Index = 54, Token = 8 + -480, // Index = 54, Token = 9 + -801, // Index = 54, Token = 10 + -1121, // Index = 54, Token = 11 + -1442, // Index = 54, Token = 12 + -1762, // Index = 54, Token = 13 + -2083, // Index = 54, Token = 14 + -2403, // Index = 54, Token = 15 + 176, // Index = 55, Token = 0 + 528, // Index = 55, Token = 1 + 881, // Index = 55, Token = 2 + 1233, // Index = 55, Token = 3 + 1587, // Index = 55, Token = 4 + 1939, // Index = 55, Token = 5 + 2292, // Index = 55, Token = 6 + 2644, // Index = 55, Token = 7 + -176, // Index = 55, Token = 8 + -528, // Index = 55, Token = 9 + -881, // Index = 55, Token = 10 + -1233, // Index = 55, Token = 11 + -1587, // Index = 55, Token = 12 + -1939, // Index = 55, Token = 13 + -2292, // Index = 55, Token = 14 + -2644, // Index = 55, Token = 15 + 194, // Index = 56, Token = 0 + 582, // Index = 56, Token = 1 + 970, // Index = 56, Token = 2 + 1358, // Index = 56, Token = 3 + 1746, // Index = 56, Token = 4 + 2134, // Index = 56, Token = 5 + 2522, // Index = 56, Token = 6 + 2910, // Index = 56, Token = 7 + -194, // Index = 56, Token = 8 + -582, // Index = 56, Token = 9 + -970, // Index = 56, Token = 10 + -1358, // Index = 56, Token = 11 + -1746, // Index = 56, Token = 12 + -2134, // Index = 56, Token = 13 + -2522, // Index = 56, Token = 14 + -2910, // Index = 56, Token = 15 + 213, // Index = 57, Token = 0 + 639, // Index = 57, Token = 1 + 1066, // Index = 57, Token = 2 + 1492, // Index = 57, Token = 3 + 1920, // Index = 57, Token = 4 + 2346, // Index = 57, Token = 5 + 2773, // Index = 57, Token = 6 + 3199, // Index = 57, Token = 7 + -213, // Index = 57, Token = 8 + -639, // Index = 57, Token = 9 + -1066, // Index = 57, Token = 10 + -1492, // Index = 57, Token = 11 + -1920, // Index = 57, Token = 12 + -2346, // Index = 57, Token = 13 + -2773, // Index = 57, Token = 14 + -3199, // Index = 57, Token = 15 + 234, // Index = 58, Token = 0 + 703, // Index = 58, Token = 1 + 1173, // Index = 58, Token = 2 + 1642, // Index = 58, Token = 3 + 2112, // Index = 58, Token = 4 + 2581, // Index = 58, Token = 5 + 3051, // Index = 58, Token = 6 + 3520, // Index = 58, Token = 7 + -234, // Index = 58, Token = 8 + -703, // Index = 58, Token = 9 + -1173, // Index = 58, Token = 10 + -1642, // Index = 58, Token = 11 + -2112, // Index = 58, Token = 12 + -2581, // Index = 58, Token = 13 + -3051, // Index = 58, Token = 14 + -3520, // Index = 58, Token = 15 + 258, // Index = 59, Token = 0 + 774, // Index = 59, Token = 1 + 1291, // Index = 59, Token = 2 + 1807, // Index = 59, Token = 3 + 2324, // Index = 59, Token = 4 + 2840, // Index = 59, Token = 5 + 3357, // Index = 59, Token = 6 + 3873, // Index = 59, Token = 7 + -258, // Index = 59, Token = 8 + -774, // Index = 59, Token = 9 + -1291, // Index = 59, Token = 10 + -1807, // Index = 59, Token = 11 + -2324, // Index = 59, Token = 12 + -2840, // Index = 59, Token = 13 + -3357, // Index = 59, Token = 14 + -3873, // Index = 59, Token = 15 + 284, // Index = 60, Token = 0 + 852, // Index = 60, Token = 1 + 1420, // Index = 60, Token = 2 + 1988, // Index = 60, Token = 3 + 2556, // Index = 60, Token = 4 + 3124, // Index = 60, Token = 5 + 3692, // Index = 60, Token = 6 + 4260, // Index = 60, Token = 7 + -284, // Index = 60, Token = 8 + -852, // Index = 60, Token = 9 + -1420, // Index = 60, Token = 10 + -1988, // Index = 60, Token = 11 + -2556, // Index = 60, Token = 12 + -3124, // Index = 60, Token = 13 + -3692, // Index = 60, Token = 14 + -4260, // Index = 60, Token = 15 + 312, // Index = 61, Token = 0 + 936, // Index = 61, Token = 1 + 1561, // Index = 61, Token = 2 + 2185, // Index = 61, Token = 3 + 2811, // Index = 61, Token = 4 + 3435, // Index = 61, Token = 5 + 4060, // Index = 61, Token = 6 + 4684, // Index = 61, Token = 7 + -312, // Index = 61, Token = 8 + -936, // Index = 61, Token = 9 + -1561, // Index = 61, Token = 10 + -2185, // Index = 61, Token = 11 + -2811, // Index = 61, Token = 12 + -3435, // Index = 61, Token = 13 + -4060, // Index = 61, Token = 14 + -4684, // Index = 61, Token = 15 + 343, // Index = 62, Token = 0 + 1030, // Index = 62, Token = 1 + 1717, // Index = 62, Token = 2 + 2404, // Index = 62, Token = 3 + 3092, // Index = 62, Token = 4 + 3779, // Index = 62, Token = 5 + 4466, // Index = 62, Token = 6 + 5153, // Index = 62, Token = 7 + -343, // Index = 62, Token = 8 + -1030, // Index = 62, Token = 9 + -1717, // Index = 62, Token = 10 + -2404, // Index = 62, Token = 11 + -3092, // Index = 62, Token = 12 + -3779, // Index = 62, Token = 13 + -4466, // Index = 62, Token = 14 + -5153, // Index = 62, Token = 15 + 378, // Index = 63, Token = 0 + 1134, // Index = 63, Token = 1 + 1890, // Index = 63, Token = 2 + 2646, // Index = 63, Token = 3 + 3402, // Index = 63, Token = 4 + 4158, // Index = 63, Token = 5 + 4914, // Index = 63, Token = 6 + 5670, // Index = 63, Token = 7 + -378, // Index = 63, Token = 8 + -1134, // Index = 63, Token = 9 + -1890, // Index = 63, Token = 10 + -2646, // Index = 63, Token = 11 + -3402, // Index = 63, Token = 12 + -4158, // Index = 63, Token = 13 + -4914, // Index = 63, Token = 14 + -5670, // Index = 63, Token = 15 + 415, // Index = 64, Token = 0 + 1246, // Index = 64, Token = 1 + 2078, // Index = 64, Token = 2 + 2909, // Index = 64, Token = 3 + 3742, // Index = 64, Token = 4 + 4573, // Index = 64, Token = 5 + 5405, // Index = 64, Token = 6 + 6236, // Index = 64, Token = 7 + -415, // Index = 64, Token = 8 + -1246, // Index = 64, Token = 9 + -2078, // Index = 64, Token = 10 + -2909, // Index = 64, Token = 11 + -3742, // Index = 64, Token = 12 + -4573, // Index = 64, Token = 13 + -5405, // Index = 64, Token = 14 + -6236, // Index = 64, Token = 15 + 457, // Index = 65, Token = 0 + 1372, // Index = 65, Token = 1 + 2287, // Index = 65, Token = 2 + 3202, // Index = 65, Token = 3 + 4117, // Index = 65, Token = 4 + 5032, // Index = 65, Token = 5 + 5947, // Index = 65, Token = 6 + 6862, // Index = 65, Token = 7 + -457, // Index = 65, Token = 8 + -1372, // Index = 65, Token = 9 + -2287, // Index = 65, Token = 10 + -3202, // Index = 65, Token = 11 + -4117, // Index = 65, Token = 12 + -5032, // Index = 65, Token = 13 + -5947, // Index = 65, Token = 14 + -6862, // Index = 65, Token = 15 + 503, // Index = 66, Token = 0 + 1509, // Index = 66, Token = 1 + 2516, // Index = 66, Token = 2 + 3522, // Index = 66, Token = 3 + 4529, // Index = 66, Token = 4 + 5535, // Index = 66, Token = 5 + 6542, // Index = 66, Token = 6 + 7548, // Index = 66, Token = 7 + -503, // Index = 66, Token = 8 + -1509, // Index = 66, Token = 9 + -2516, // Index = 66, Token = 10 + -3522, // Index = 66, Token = 11 + -4529, // Index = 66, Token = 12 + -5535, // Index = 66, Token = 13 + -6542, // Index = 66, Token = 14 + -7548, // Index = 66, Token = 15 + 553, // Index = 67, Token = 0 + 1660, // Index = 67, Token = 1 + 2767, // Index = 67, Token = 2 + 3874, // Index = 67, Token = 3 + 4981, // Index = 67, Token = 4 + 6088, // Index = 67, Token = 5 + 7195, // Index = 67, Token = 6 + 8302, // Index = 67, Token = 7 + -553, // Index = 67, Token = 8 + -1660, // Index = 67, Token = 9 + -2767, // Index = 67, Token = 10 + -3874, // Index = 67, Token = 11 + -4981, // Index = 67, Token = 12 + -6088, // Index = 67, Token = 13 + -7195, // Index = 67, Token = 14 + -8302, // Index = 67, Token = 15 + 608, // Index = 68, Token = 0 + 1825, // Index = 68, Token = 1 + 3043, // Index = 68, Token = 2 + 4260, // Index = 68, Token = 3 + 5479, // Index = 68, Token = 4 + 6696, // Index = 68, Token = 5 + 7914, // Index = 68, Token = 6 + 9131, // Index = 68, Token = 7 + -608, // Index = 68, Token = 8 + -1825, // Index = 68, Token = 9 + -3043, // Index = 68, Token = 10 + -4260, // Index = 68, Token = 11 + -5479, // Index = 68, Token = 12 + -6696, // Index = 68, Token = 13 + -7914, // Index = 68, Token = 14 + -9131, // Index = 68, Token = 15 + 669, // Index = 69, Token = 0 + 2008, // Index = 69, Token = 1 + 3348, // Index = 69, Token = 2 + 4687, // Index = 69, Token = 3 + 6027, // Index = 69, Token = 4 + 7366, // Index = 69, Token = 5 + 8706, // Index = 69, Token = 6 + 10045, // Index = 69, Token = 7 + -669, // Index = 69, Token = 8 + -2008, // Index = 69, Token = 9 + -3348, // Index = 69, Token = 10 + -4687, // Index = 69, Token = 11 + -6027, // Index = 69, Token = 12 + -7366, // Index = 69, Token = 13 + -8706, // Index = 69, Token = 14 + -10045, // Index = 69, Token = 15 + 736, // Index = 70, Token = 0 + 2209, // Index = 70, Token = 1 + 3683, // Index = 70, Token = 2 + 5156, // Index = 70, Token = 3 + 6630, // Index = 70, Token = 4 + 8103, // Index = 70, Token = 5 + 9577, // Index = 70, Token = 6 + 11050, // Index = 70, Token = 7 + -736, // Index = 70, Token = 8 + -2209, // Index = 70, Token = 9 + -3683, // Index = 70, Token = 10 + -5156, // Index = 70, Token = 11 + -6630, // Index = 70, Token = 12 + -8103, // Index = 70, Token = 13 + -9577, // Index = 70, Token = 14 + -11050, // Index = 70, Token = 15 + 810, // Index = 71, Token = 0 + 2431, // Index = 71, Token = 1 + 4052, // Index = 71, Token = 2 + 5673, // Index = 71, Token = 3 + 7294, // Index = 71, Token = 4 + 8915, // Index = 71, Token = 5 + 10536, // Index = 71, Token = 6 + 12157, // Index = 71, Token = 7 + -810, // Index = 71, Token = 8 + -2431, // Index = 71, Token = 9 + -4052, // Index = 71, Token = 10 + -5673, // Index = 71, Token = 11 + -7294, // Index = 71, Token = 12 + -8915, // Index = 71, Token = 13 + -10536, // Index = 71, Token = 14 + -12157, // Index = 71, Token = 15 + 891, // Index = 72, Token = 0 + 2674, // Index = 72, Token = 1 + 4457, // Index = 72, Token = 2 + 6240, // Index = 72, Token = 3 + 8023, // Index = 72, Token = 4 + 9806, // Index = 72, Token = 5 + 11589, // Index = 72, Token = 6 + 13372, // Index = 72, Token = 7 + -891, // Index = 72, Token = 8 + -2674, // Index = 72, Token = 9 + -4457, // Index = 72, Token = 10 + -6240, // Index = 72, Token = 11 + -8023, // Index = 72, Token = 12 + -9806, // Index = 72, Token = 13 + -11589, // Index = 72, Token = 14 + -13372, // Index = 72, Token = 15 + 980, // Index = 73, Token = 0 + 2941, // Index = 73, Token = 1 + 4902, // Index = 73, Token = 2 + 6863, // Index = 73, Token = 3 + 8825, // Index = 73, Token = 4 + 10786, // Index = 73, Token = 5 + 12747, // Index = 73, Token = 6 + 14708, // Index = 73, Token = 7 + -980, // Index = 73, Token = 8 + -2941, // Index = 73, Token = 9 + -4902, // Index = 73, Token = 10 + -6863, // Index = 73, Token = 11 + -8825, // Index = 73, Token = 12 + -10786, // Index = 73, Token = 13 + -12747, // Index = 73, Token = 14 + -14708, // Index = 73, Token = 15 + 1078, // Index = 74, Token = 0 + 3235, // Index = 74, Token = 1 + 5393, // Index = 74, Token = 2 + 7550, // Index = 74, Token = 3 + 9708, // Index = 74, Token = 4 + 11865, // Index = 74, Token = 5 + 14023, // Index = 74, Token = 6 + 16180, // Index = 74, Token = 7 + -1078, // Index = 74, Token = 8 + -3235, // Index = 74, Token = 9 + -5393, // Index = 74, Token = 10 + -7550, // Index = 74, Token = 11 + -9708, // Index = 74, Token = 12 + -11865, // Index = 74, Token = 13 + -14023, // Index = 74, Token = 14 + -16180, // Index = 74, Token = 15 + 1186, // Index = 75, Token = 0 + 3559, // Index = 75, Token = 1 + 5932, // Index = 75, Token = 2 + 8305, // Index = 75, Token = 3 + 10679, // Index = 75, Token = 4 + 13052, // Index = 75, Token = 5 + 15425, // Index = 75, Token = 6 + 17798, // Index = 75, Token = 7 + -1186, // Index = 75, Token = 8 + -3559, // Index = 75, Token = 9 + -5932, // Index = 75, Token = 10 + -8305, // Index = 75, Token = 11 + -10679, // Index = 75, Token = 12 + -13052, // Index = 75, Token = 13 + -15425, // Index = 75, Token = 14 + -17798, // Index = 75, Token = 15 + 1305, // Index = 76, Token = 0 + 3915, // Index = 76, Token = 1 + 6526, // Index = 76, Token = 2 + 9136, // Index = 76, Token = 3 + 11747, // Index = 76, Token = 4 + 14357, // Index = 76, Token = 5 + 16968, // Index = 76, Token = 6 + 19578, // Index = 76, Token = 7 + -1305, // Index = 76, Token = 8 + -3915, // Index = 76, Token = 9 + -6526, // Index = 76, Token = 10 + -9136, // Index = 76, Token = 11 + -11747, // Index = 76, Token = 12 + -14357, // Index = 76, Token = 13 + -16968, // Index = 76, Token = 14 + -19578, // Index = 76, Token = 15 + 1435, // Index = 77, Token = 0 + 4306, // Index = 77, Token = 1 + 7178, // Index = 77, Token = 2 + 10049, // Index = 77, Token = 3 + 12922, // Index = 77, Token = 4 + 15793, // Index = 77, Token = 5 + 18665, // Index = 77, Token = 6 + 21536, // Index = 77, Token = 7 + -1435, // Index = 77, Token = 8 + -4306, // Index = 77, Token = 9 + -7178, // Index = 77, Token = 10 + -10049, // Index = 77, Token = 11 + -12922, // Index = 77, Token = 12 + -15793, // Index = 77, Token = 13 + -18665, // Index = 77, Token = 14 + -21536, // Index = 77, Token = 15 + 1579, // Index = 78, Token = 0 + 4737, // Index = 78, Token = 1 + 7896, // Index = 78, Token = 2 + 11054, // Index = 78, Token = 3 + 14214, // Index = 78, Token = 4 + 17372, // Index = 78, Token = 5 + 20531, // Index = 78, Token = 6 + 23689, // Index = 78, Token = 7 + -1579, // Index = 78, Token = 8 + -4737, // Index = 78, Token = 9 + -7896, // Index = 78, Token = 10 + -11054, // Index = 78, Token = 11 + -14214, // Index = 78, Token = 12 + -17372, // Index = 78, Token = 13 + -20531, // Index = 78, Token = 14 + -23689, // Index = 78, Token = 15 + 1737, // Index = 79, Token = 0 + 5211, // Index = 79, Token = 1 + 8686, // Index = 79, Token = 2 + 12160, // Index = 79, Token = 3 + 15636, // Index = 79, Token = 4 + 19110, // Index = 79, Token = 5 + 22585, // Index = 79, Token = 6 + 26059, // Index = 79, Token = 7 + -1737, // Index = 79, Token = 8 + -5211, // Index = 79, Token = 9 + -8686, // Index = 79, Token = 10 + -12160, // Index = 79, Token = 11 + -15636, // Index = 79, Token = 12 + -19110, // Index = 79, Token = 13 + -22585, // Index = 79, Token = 14 + -26059, // Index = 79, Token = 15 + 1911, // Index = 80, Token = 0 + 5733, // Index = 80, Token = 1 + 9555, // Index = 80, Token = 2 + 13377, // Index = 80, Token = 3 + 17200, // Index = 80, Token = 4 + 21022, // Index = 80, Token = 5 + 24844, // Index = 80, Token = 6 + 28666, // Index = 80, Token = 7 + -1911, // Index = 80, Token = 8 + -5733, // Index = 80, Token = 9 + -9555, // Index = 80, Token = 10 + -13377, // Index = 80, Token = 11 + -17200, // Index = 80, Token = 12 + -21022, // Index = 80, Token = 13 + -24844, // Index = 80, Token = 14 + -28666, // Index = 80, Token = 15 + 2102, // Index = 81, Token = 0 + 6306, // Index = 81, Token = 1 + 10511, // Index = 81, Token = 2 + 14715, // Index = 81, Token = 3 + 18920, // Index = 81, Token = 4 + 23124, // Index = 81, Token = 5 + 27329, // Index = 81, Token = 6 + 31533, // Index = 81, Token = 7 + -2102, // Index = 81, Token = 8 + -6306, // Index = 81, Token = 9 + -10511, // Index = 81, Token = 10 + -14715, // Index = 81, Token = 11 + -18920, // Index = 81, Token = 12 + -23124, // Index = 81, Token = 13 + -27329, // Index = 81, Token = 14 + -31533, // Index = 81, Token = 15 + 2312, // Index = 82, Token = 0 + 6937, // Index = 82, Token = 1 + 11562, // Index = 82, Token = 2 + 16187, // Index = 82, Token = 3 + 20812, // Index = 82, Token = 4 + 25437, // Index = 82, Token = 5 + 30062, // Index = 82, Token = 6 + 34687, // Index = 82, Token = 7 + -2312, // Index = 82, Token = 8 + -6937, // Index = 82, Token = 9 + -11562, // Index = 82, Token = 10 + -16187, // Index = 82, Token = 11 + -20812, // Index = 82, Token = 12 + -25437, // Index = 82, Token = 13 + -30062, // Index = 82, Token = 14 + -34687, // Index = 82, Token = 15 + 2543, // Index = 83, Token = 0 + 7630, // Index = 83, Token = 1 + 12718, // Index = 83, Token = 2 + 17805, // Index = 83, Token = 3 + 22893, // Index = 83, Token = 4 + 27980, // Index = 83, Token = 5 + 33068, // Index = 83, Token = 6 + 38155, // Index = 83, Token = 7 + -2543, // Index = 83, Token = 8 + -7630, // Index = 83, Token = 9 + -12718, // Index = 83, Token = 10 + -17805, // Index = 83, Token = 11 + -22893, // Index = 83, Token = 12 + -27980, // Index = 83, Token = 13 + -33068, // Index = 83, Token = 14 + -38155, // Index = 83, Token = 15 + 2798, // Index = 84, Token = 0 + 8394, // Index = 84, Token = 1 + 13990, // Index = 84, Token = 2 + 19586, // Index = 84, Token = 3 + 25183, // Index = 84, Token = 4 + 30779, // Index = 84, Token = 5 + 36375, // Index = 84, Token = 6 + 41971, // Index = 84, Token = 7 + -2798, // Index = 84, Token = 8 + -8394, // Index = 84, Token = 9 + -13990, // Index = 84, Token = 10 + -19586, // Index = 84, Token = 11 + -25183, // Index = 84, Token = 12 + -30779, // Index = 84, Token = 13 + -36375, // Index = 84, Token = 14 + -41971, // Index = 84, Token = 15 + 3077, // Index = 85, Token = 0 + 9232, // Index = 85, Token = 1 + 15388, // Index = 85, Token = 2 + 21543, // Index = 85, Token = 3 + 27700, // Index = 85, Token = 4 + 33855, // Index = 85, Token = 5 + 40011, // Index = 85, Token = 6 + 46166, // Index = 85, Token = 7 + -3077, // Index = 85, Token = 8 + -9232, // Index = 85, Token = 9 + -15388, // Index = 85, Token = 10 + -21543, // Index = 85, Token = 11 + -27700, // Index = 85, Token = 12 + -33855, // Index = 85, Token = 13 + -40011, // Index = 85, Token = 14 + -46166, // Index = 85, Token = 15 + 3385, // Index = 86, Token = 0 + 10156, // Index = 86, Token = 1 + 16928, // Index = 86, Token = 2 + 23699, // Index = 86, Token = 3 + 30471, // Index = 86, Token = 4 + 37242, // Index = 86, Token = 5 + 44014, // Index = 86, Token = 6 + 50785, // Index = 86, Token = 7 + -3385, // Index = 86, Token = 8 + -10156, // Index = 86, Token = 9 + -16928, // Index = 86, Token = 10 + -23699, // Index = 86, Token = 11 + -30471, // Index = 86, Token = 12 + -37242, // Index = 86, Token = 13 + -44014, // Index = 86, Token = 14 + -50785, // Index = 86, Token = 15 + 3724, // Index = 87, Token = 0 + 11172, // Index = 87, Token = 1 + 18621, // Index = 87, Token = 2 + 26069, // Index = 87, Token = 3 + 33518, // Index = 87, Token = 4 + 40966, // Index = 87, Token = 5 + 48415, // Index = 87, Token = 6 + 55863, // Index = 87, Token = 7 + -3724, // Index = 87, Token = 8 + -11172, // Index = 87, Token = 9 + -18621, // Index = 87, Token = 10 + -26069, // Index = 87, Token = 11 + -33518, // Index = 87, Token = 12 + -40966, // Index = 87, Token = 13 + -48415, // Index = 87, Token = 14 + -55863, // Index = 87, Token = 15 + 4095, // Index = 88, Token = 0 + 12286, // Index = 88, Token = 1 + 20478, // Index = 88, Token = 2 + 28669, // Index = 88, Token = 3 + 36862, // Index = 88, Token = 4 + 45053, // Index = 88, Token = 5 + 53245, // Index = 88, Token = 6 + 61436, // Index = 88, Token = 7 + -4095, // Index = 88, Token = 8 + -12286, // Index = 88, Token = 9 + -20478, // Index = 88, Token = 10 + -28669, // Index = 88, Token = 11 + -36862, // Index = 88, Token = 12 + -45053, // Index = 88, Token = 13 + -53245, // Index = 88, Token = 14 + -61436 // Index = 88, Token = 15 +}; + diff --git a/code/redalert/dynavec.cpp b/code/redalert/dynavec.cpp new file mode 100644 index 0000000..6f8d677 --- /dev/null +++ b/code/redalert/dynavec.cpp @@ -0,0 +1,300 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/DYNAVEC.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + * * + * Project Name : Red Alert * + * * + * File Name : DYNAVEC.CPP * + * * + * Programmer : Bill R Randolph * + * * + * Start Date : 09/18/96 * + * * + * Last Update : September 18, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * DynamicVectorClass::Delete -- Deletes specified index from vector. * + * DynamicVectorClass::Delete -- Remove specified object from vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor * + * DynamicVectorClass::ID -- Find matching value in dynamic vector. * + * DynamicVectorClass::Resize -- Changes size of a dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) +#include "function.h" +#include "vector.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +//#include +#include + + + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previously allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previously and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * * + * This routine will add the specified element to the head of the vector. If necessary, * + * the vector will be expanded accordingly. * + * * + * INPUT: object -- Reference to the object to add to the head of this vector. * + * * + * OUTPUT: bool; Was the object added without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; +// (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + return(Delete(ID(object))); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if ((unsigned)index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i+1]; + } + return(true); + } + return(false); +} + +/************************** end of dynavec.cpp *****************************/ +#endif \ No newline at end of file diff --git a/code/redalert/edit.cpp b/code/redalert/edit.cpp new file mode 100644 index 0000000..1684c36 --- /dev/null +++ b/code/redalert/edit.cpp @@ -0,0 +1,445 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EDIT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EditClass::Action -- Handles input events. * + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * EditClass::Draw_Text -- Draws the edit gadget text. * + * EditClass::EditClass -- Normal constructor for edit class object. * + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * EditClass::EditClass -- Normal constructor for edit class object. * + * * + * This is the normal constructor used to create an edit object. * + * * + * INPUT: id -- The ID number for this edit object. This is the ID number that will be * + * returned by the Input() function when the key is pressed if this * + * gadget has the keyboard input focus. * + * * + * text -- Reference to the text buffer that the edit gadget will modify as keyboard * + * input is processed. The value that this buffer contains is the default * + * text displayed. * + * * + * maxlen-- The maximum size of the text buffer specified. This length INCLUDES the * + * trailing null character so a simple sizeof() function call can be used. * + * * + * flags -- These are the text print control flags. It is used to control how the * + * text looks in the edit box. Use the normal TPF_??? flags. * + * * + * x,y -- The pixel coordinates of the upper left corner of the edit gadget area. * + * * + * w,h -- The pixel dimensions of the edit box. If either of these are no provided, * + * or set to -1, then the dimension is determined from the string itself. * + * * + * sytle -- This style flag parameter control what kind of characters are allowed in * + * the edit box. The initial string in the text buffer may contain illegal * + * characters, but they are NOT removed regardless of this parameter. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/05/1995 MML : Created. * + * 01/21/1995 JLB : Modified. * + *=============================================================================================*/ +EditClass::EditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + ControlClass (id, x, y, w, h, LEFTPRESS), String(text) +{ + TextFlags = flags & ~(TPF_CENTER); + EditFlags = style; + Set_Text(text, max_len); + Color = GadgetClass::Get_Color_Scheme(); + + if (w == -1 || h == -1) { + // PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + + if (h == -1) { + Height = FontHeight+1; + } + if (w == -1) { + if (strlen(String) > 0) { + Width = String_Pixel_Width(String) + 6; + } else { + Width = ((Char_Pixel_Width('X')+FontXSpacing) * (MaxLength+1)) + 2; + } + } + } + + IsReadOnly = 0; +} + + +/*********************************************************************************************** + * EditClass::~EditClass -- Default destructor for the edit gadget. * + * * + * This default destructor removes the focus setting if it currently has it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/24/1995 JLB : Created. * + *=============================================================================================*/ +EditClass::~EditClass(void) +{ + if (Has_Focus()) { + Clear_Focus(); + } +} + + +/*********************************************************************************************** + * EditClass::Set_Text -- Sets the text to the edit gadget. * + * * + * Use this routine to change the text that this edit gadget refers to. * + * * + * INPUT: text -- Reference to the character array that this edit gadget will be * + * modifying. * + * max_len -- The maximum size of the buffer that will be modified. * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Set_Text(char * text, int max_len) +{ + String = text; + MaxLength = max_len-1; + Length = strlen(String); + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * EditClass::Draw_Me -- Draws the edit box and embedded text. * + * * + * This routine will render the edit box. This will show the box outline as well as any * + * text it may contain. * + * * + * INPUT: forced -- Should the edit box be drawn even if it thinks it doesn't have to? * + * * + * OUTPUT: Was the edit box drawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Background(); + + /* + ** Display the text. + */ + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * EditClass::Action -- Handles input events. * + * * + * This routine will handle all mouse and keyboard events directed at this edit box * + * gadget. For keyboard events, this will insert the characters into the edit box. * + * * + * INPUT: flags -- The event flag that triggered this function call. * + * * + * key -- Reference to the keyboard/mouse event that triggered this function call. * + * * + * OUTPUT: Should the list be processed further? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int EditClass::Action(unsigned flags, KeyNumType & key) +{ + /* + ** If this is a read-only edit box, it's a display-only device + */ + if (IsReadOnly) { + return(false); + } + + /* + ** If the left mouse button is pressed over this gadget, then set the focus to + ** this gadget. The event flag is cleared so that no button ID number is returned. + */ + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + /* + ** Handle keyboard events here. Normally, the key is added to the string, but if the + ** RETURN key is pressed, then the button ID number is returned from the Input() + ** function. + */ + if ((flags & KEYBOARD) && Has_Focus()) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + //KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + Handle_Key(key, flags); + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * EditClass::Draw_Background -- Draw the background to the edit gadget. * + * * + * This routine will redraw the edit gadget background. The overlaying text is handled by * + * a different routine. The mouse is guaranteed to be hidden when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Background(void) +{ + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); +} + + +/*********************************************************************************************** + * EditClass::Draw_Text -- Draws the edit gadget text. * + * * + * This routine is called when the edit gadget text needs to be drawn. The background has * + * already been drawn by the time this function is called. The mouse is guaranteed to be * + * hidden as well. * + * * + * INPUT: text -- The text to draw in the edit gadget. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +void EditClass::Draw_Text(char const * text) +{ + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && (int)strlen(text) < MaxLength && + ((int)String_Pixel_Width(text) + (int)String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + + +/*********************************************************************************************** + * EditClass::Handle_Key -- Handles keyboard input to edit gadget. * + * * + * This is the gruntwork routine that processes keyboard input to the edit gadget. This * + * routine will be called when keyboard input has been detected and this gadget has the * + * current focus. * + * * + * INPUT: ascii -- The ASCII key code that was fetched from the keyboard buffer. * + * * + * OUTPUT: bool; Should this keyboard input NOT cause the gadget ID number to be returned * + * from the controlling Input() routine? Typically, the return value would be * + * true unless the focus is lost due to the key being pressed. * + * * + * WARNINGS: none * + * HISTORY: * + * 01/21/1995 JLB : Created. * + *=============================================================================================*/ +bool EditClass::Handle_Key(KeyNumType key, unsigned flags) +{ + unsigned char testAscii = UserInput.KeyB.ASCII; + //if (key & KN_SHIFT_BIT || key & KN_CAPSLOCK_BIT) testAscii = toupper(testAscii); + + switch (key & 0xff) { + /* + ** Handle the special case of a non-keyboard event. It is possible that this + ** key code might be passed to this routine if this routine has been overridden + ** and the key event was consumed. + */ + case 0: + break; + + /* + ** If the return key is pressed, then remove the focus from this edit + ** gadget but otherwise let the normal gadget processing proceed. This + ** causes the gadget ID number to be returned from the Input() function + ** so that the controlling program will know that the text can be + ** processed. + */ + case KN_RETURN: + Clear_Focus(); + return(false); + + /* + ** When the BACKSPACE key is pressed, remove the last character in the edit string. + */ + case KN_BACKSPACE: + if (Length) { + Length--; + String[Length] = '\0'; + Flag_To_Redraw(); + } + break; + + /* + ** If the keyboard event was not a recognized special key, then examine to see + ** if it can legally be added to the edit string and do so if possible. + */ + default: + + /* + ** Don't add a character if the length is greater than edit width. + */ + if (((int)String_Pixel_Width(String) + (int)Char_Pixel_Width(testAscii) ) >= (Width-2)) { + break; + } + + /* + ** Don't add a character if the length is already at maximum. + */ + if (Length >= MaxLength) break; + + /* + ** Invisible characters are never added to the string. This is + ** especially true for spaces at the beginning of the string. + */ + if (!isgraph(testAscii) && testAscii != ' ') break; + if (testAscii == ' ' && Length == 0) break; + + /* + ** If this is an upper case only edit gadget, then force the alphabetic + ** character to upper case. + */ + if ((EditFlags & UPPERCASE) && isalpha(testAscii)) { + testAscii = (KeyASCIIType)toupper(testAscii); + } + + if ((!(EditFlags & NUMERIC) || !isdigit(testAscii)) && + (!(EditFlags & ALPHA) || !isalpha(testAscii)) && + (!(EditFlags & MISC) || isalnum(testAscii)) && + testAscii != ' ') { + break; + } + + /* + ** The character passed all legality checks, so add it to the edit string + ** and flag this gadget to be redrawn. The manual flag to redraw is needed + ** because the event flag has been cleared. This prevents the gadget's ID + ** number from being returned just because the gadget has been edited. + */ + String[Length++] = testAscii; + String[Length] = '\0'; + + Flag_To_Redraw(); + break; + } + return(true); +} + + +void EditClass::Set_Focus(void) +{ + Length = 0; + if (String) { + Length = strlen(String); + } + ControlClass::Set_Focus(); +} diff --git a/code/redalert/edit.h b/code/redalert/edit.h new file mode 100644 index 0000000..5d861ad --- /dev/null +++ b/code/redalert/edit.h @@ -0,0 +1,120 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EDIT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EDIT_H +#define EDIT_H + +class EditClass : public ControlClass +{ + public: + typedef enum EditStyle { + ALPHA =0x0001, // Edit accepts alphabetic characters. + NUMERIC =0x0002, // Edit accepts numbers. + MISC =0x0004, // Edit accepts misc graphic characters. + UPPERCASE =0x0008, // Force to upper case. + ALPHANUMERIC=(int)ALPHA|(int)NUMERIC|(int)MISC + } EditStyle; + + EditClass (int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1, EditStyle style=ALPHANUMERIC); + virtual ~EditClass(void); + + virtual void Set_Focus(void); + virtual int Draw_Me(int forced); + virtual void Set_Text(char * text, int max_len); + virtual char * Get_Text(void) {return(String);}; + void Set_Color (RemapControlType * color) { Color = color; } + + void Set_Read_Only(int rdonly) {IsReadOnly = rdonly;} + + protected: + + /* + ** These are the text size and style flags to be used when displaying the text + ** of the edit gadget. + */ + TextPrintType TextFlags; + + /* + ** Input flags that control what characters are allowed in the string. + */ + EditStyle EditFlags; + + /* + ** Pointer to text staging buffer and the maximum length of the string it + ** can contain. + */ + char *String; + int MaxLength; + + /* + ** This is the current length of the string. This length will never exceed the + ** MaxLength allowed. + */ + int Length; + + /* + ** This is the desired color of the edit control. + */ + RemapControlType * Color; + + virtual int Action (unsigned flags, KeyNumType &key); + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + virtual bool Handle_Key(KeyNumType ascii, unsigned flags); + + private: + int IsReadOnly; +}; + +//PG inline EditClass::EditStyle operator |(EditClass::EditStyle, EditClass::EditStyle); +//PG inline EditClass::EditStyle operator &(EditClass::EditStyle, EditClass::EditStyle); +//PG inline EditClass::EditStyle operator ~(EditClass::EditStyle); + +inline EditClass::EditStyle operator|(EditClass::EditStyle a, EditClass::EditStyle b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline EditClass::EditStyle operator&(EditClass::EditStyle a, EditClass::EditStyle b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline EditClass::EditStyle operator~(EditClass::EditStyle a) +{ + return static_cast(~static_cast(a)); +} + + +#endif diff --git a/code/redalert/egos.cpp b/code/redalert/egos.cpp new file mode 100644 index 0000000..9d30e2c --- /dev/null +++ b/code/redalert/egos.cpp @@ -0,0 +1,921 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/EGOS.CPP 2 3/10/97 3:19p Steve_tall $ */ +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : EGOS.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//PG_TO_FIX +#include "function.h" +#include "memflag.h" + +/* +** List of Ego Class instances +** There will be one instance for each line of text. +*/ +DynamicVectorClass EgoList; + +/* +** Number of slideshow pictures +*/ + +#define NUM_SLIDES 17 + +/* +** Length of time frame is displayed for +*/ +#define FRAME_DELAY 150 + +/* +** Number of frames that palete fade occurs over +*/ +#define FADE_DELAY 37 + +/* +** Names of slideshow pictures to play behind the text +*/ +char SlideNames[NUM_SLIDES][13]={ + "aftr_hi.pcx", + "aly1.pcx", + "apc_hi.pcx", + "aphi0049.pcx", + "bnhi0020.pcx", + "dchi0040.pcx", + "frhi0166.pcx", + "lab.pcx", + "landsbrg.pcx", + "mahi0107.pcx", + "mig_hi.pcx", + "mtfacthi.pcx", + "needle.pcx", + "sov2.pcx", + "spy.pcx", + "stalin.pcx", + "tent.pcx" +}; + +/* +** Array of all the palettes required for the slides +*/ +char SlidePals[NUM_SLIDES][256*3]; + +/* +** Array of graphic buffers containing the slides +*/ +GraphicBufferClass *SlideBuffers[NUM_SLIDES]; + +/* +** Original copy of slide (pref in video mem) that we use to undraw the text +*/ +GraphicBufferClass *BackgroundPage; + +/* +** This palette contains both the font palette entries and the slide +** palette. +*/ +PaletteClass ComboPalette; + +/* +** Ptr to the combo palette. +*/ +unsigned char *ComboPalPtr; + +/* +** Lookup table. If an entry is non-zero then it should be faded in/out when the slide changes. +*/ +char PaletteLUT[256]; + +/* +** Height of the strips that are blitted from the slides to the backgound and hid pages. +** We blit in several strips over several frames so as not to impact on the frame rate. +*/ +#define CHUNK_HEIGHT RESFACTOR * 50 + + + +#ifndef WIN32 +extern void Vsync(void); +#pragma aux Vsync modify [edx ebx eax] = \ + "mov edx,03DAh" \ + "mov ebx,[VertBlank]" \ + "and bl,001h" \ + "shl bl,3" \ + "in_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "je in_vbi" \ + "out_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "jne out_vbi" +#endif //WIN32 + + + +/*********************************************************************************************** + * EC::EgoClass -- EgoClass constructor * + * * + * * + * * + * INPUT: x position of text * + * y position of text * + * ptr to text string * + * flags to print text with * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:53PM ST : Created * + *=============================================================================================*/ +EgoClass::EgoClass (int x, int y, char *text, TextPrintType flags) +{ + XPos = x; + YPos = y; + Flags= flags; + Text = new char [strlen (text)+1]; + strcpy (Text, text); +} + + +/*********************************************************************************************** + * EC::~EgoClass -- EgoClass destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:54PM ST : Created * + *=============================================================================================*/ +EgoClass::~EgoClass(void) +{ + delete [] Text; +} + + +/*********************************************************************************************** + * EC::Scroll -- Apply the given distance to the y position of the text. * + * A positive distance scrolls up. * + * * + * * + * INPUT: distance in pixels to scroll up * + * * + * OUTPUT: true if text scrolled beyond the top of the screen * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:55PM ST : Created * + *=============================================================================================*/ +bool EgoClass::Scroll(int distance) +{ + YPos -= distance; + if (YPos < -20) { + return (true); + }else{ + return (false); + } +} + + +/*********************************************************************************************** + * EC::Render -- Draws the text to the logic page * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:57PM ST : Created * + *=============================================================================================*/ +void EgoClass::Render (void) +{ + if (YPos < LogicPage->Get_Height() && YPos > -16) { + Fancy_Text_Print(Text, XPos, YPos, GadgetClass::Get_Color_Scheme(), TBLACK, Flags); + } +} + + + +/*********************************************************************************************** + * EC::Wipe -- Wipes the previously rendered text by blitting a rectangle from the given * + * background screen. * + * * + * * + * INPUT: ptr to screen containing original background * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:58PM ST : Created * + *=============================================================================================*/ +void EgoClass::Wipe (GraphicBufferClass *background) +{ + int width = String_Pixel_Width (Text); + int x = XPos; + + if (Flags & TPF_RIGHT) { + x -= width; + }else{ + if (Flags & TPF_CENTER){ + x -= width/2; + } + } + + GL_FillRect( TBLACK, x - 1, YPos, width + 2, 7 * RESFACTOR + 1 ); + + //background->Blit(*LogicPage, x-1, YPos, x-1, YPos, width+2, 7 * RESFACTOR +1, false); +} + + + +/*********************************************************************************************** + * Set_Pal -- Low level palette set * + * * + * * + * * + * INPUT: ptr to palette * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/9/96 11:59PM ST : Created * + *=============================================================================================*/ +void Set_Pal(char *palette) +{ +//#ifndef WIN32 + //Vsync(); + //unsigned char *rgbptr = (unsigned char *) palette; + //outportb(0x03C8, 0); //Start from color 0 + + //for (int index = 0; index < 256; index++) { + // outrgb(rgbptr[index*3], rgbptr[index*3+1], rgbptr[index*3+2]); + //} +//#else //WIN32 + + Set_Palette((void*)palette); +//#endif +} + + +/*********************************************************************************************** + * Slide_Show -- Handles the blitting and fading of the background pictures. * + * * + * The picture frame number is used to trigger blitting and fading events * + * * + * * + * INPUT: picture number * + * frame * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:16AM ST : Created * + *=============================================================================================*/ +void Copy_Palette(PaletteClass const & source, PaletteClass & destination) +{ + for (int index = 0; index < source.COLOR_COUNT; index++) { + destination[index] = source[index]; + } +} + +void Slide_Show(int slide, int frame) +{ + + /* + ** Temprary storage to save CCPalette to + */ + //char save_palette[256*3]; + PaletteClass save_palette; + + if (frame >= 1 && frame <=4){ + /* + ** Blit in a quarter of the new frame to the background page. + */ + SlideBuffers[slide]->Blit (*BackgroundPage, 0, (frame-1) * CHUNK_HEIGHT, + 0, (frame-1) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame >= 5 && frame <=8 ){ + /* + ** Blit in a quarter of the new frame to the hid page. + */ + BackgroundPage->Blit (HidPage, 0, (frame-5) * CHUNK_HEIGHT, + 0, (frame-5) * CHUNK_HEIGHT, + SeenBuff.Get_Width(), CHUNK_HEIGHT, false); + return; + } + + if (frame ==9){ + /* + ** Create the combo palette from the font entries and the picture entries. + */ + for (int index = 0 ; index < 256 ; index++ ){ + if (PaletteLUT[index]) { + ComboPalPtr[index * 3] = SlidePals[slide][index * 3]; + ComboPalPtr[index * 3 + 1] = SlidePals[slide][index*3 + 1]; + ComboPalPtr[index * 3 + 2] = SlidePals[slide][index*3 + 2]; + } + } + return; + } + + + if (frame >10 && frame < FADE_DELAY+10){ + /* + ** Fade up the picture in the background. The text colors never fade. + */ + Copy_Palette( CCPalette, save_palette ); + + //CCPalette.Partial_Adjust (MIN (6*(frame-5), 255), ComboPalette, PaletteLUT); + int ratio = MIN( ( 255 / FADE_DELAY - 1 ) * (frame - 10), 255 ); + CCPalette.Partial_Adjust ( ratio, ComboPalette, PaletteLUT); + + //CCPalette.Partial_Adjust( 255, ComboPalette, PaletteLUT ); + + Set_Pal ( (char *) CCPalette.Get_Data() ); + + if (frame != 9+FADE_DELAY) + { + Copy_Palette( save_palette, CCPalette ); + } + + return; + } + + + if (frame >FRAME_DELAY && frame < FRAME_DELAY+FADE_DELAY){ + /* + ** Fade down the picture in the background. The text colors never fade. + */ + Copy_Palette( CCPalette, save_palette ); + + CCPalette.Partial_Adjust ( 0, PaletteLUT ); + + int ratio = MIN( 255 / ( (FRAME_DELAY + FADE_DELAY) - FRAME_DELAY ) * (frame - FRAME_DELAY), 255); + CCPalette.Partial_Adjust( 255 - ratio, ComboPalette, PaletteLUT); + //CCPalette.Partial_Adjust( 0, ComboPalette, PaletteLUT ); + + if (frame != FRAME_DELAY+FADE_DELAY-1) + { + Set_Pal ( (char *) CCPalette.Get_Data() ); + Copy_Palette( save_palette, CCPalette ); + }else{ + /* + ** If this is the last fade down frame then zero the picture palette entries. + */ + unsigned char *ccpalptr = (unsigned char*)&CCPalette; + for (int index = 0 ; index < 256 ; index++){ + if (PaletteLUT[index]){ + ccpalptr[index*3] = 0; + ccpalptr[index*3+1] = 0; + ccpalptr[index*3+2] = 0; + } + } + + Set_Pal ( (char *) CCPalette.Get_Data() ); + } + + } + +} + +/*********************************************************************************************** + * Show_Who_Was_Responsible -- Main function to print the credits. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 9/10/96 0:20AM ST : Created * + *=============================================================================================*/ +void Show_Who_Was_Responsible (void) +{ + + int i; + int key; + + /* + ** Deault speed of credits scolling. This is the frame delay between pixel scrolls. + */ + static int speed = 3; + + /* + ** In DOS we need to scroll slower so we have a bool that lets us do it every other time + */ +#ifndef WIN32 + bool scroll_now = false; +#endif //WIN32 + + /* + ** Read in the credits file to be displayed + ** + ** Lines of text in CREDITS.TXT are treated as follows.... + ** + ** If the text starts and ends to the left of column 40 then text will be right justified. + ** If the text starts before column 40 and ends after it then it will be centered. + ** If the text starts after column 40 it will be right justified. + */ + CCFileClass creditsfile ("credits.txt"); + if ( !creditsfile.Is_Available()) return; + char *credits = new char [creditsfile.Size()+1]; + creditsfile.Read (credits, creditsfile.Size()); + + /* + ** Initialise the text printing system. + */ + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_GREEN]); + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + /* + ** Miscellaneous stuff for parsing the credits text file. + */ + int length = creditsfile.Size(); + int line = 0; + int column = 0; + char *cptr = credits; + char ch, lastchar, oldchar; + char *strstart, *strparse; + bool gotendstr; + int startcolumn, endcolumn, x; + int y=SeenBuff.Get_Height()+2; + EgoClass *ego; + TextPrintType flags; + + + /* + ** Search through the text file and extract the strings, using each string to create + ** a new EgoClass + */ + do { + /* + ** Search for text + */ + ch = *cptr++; + length --; + + /* + ** Look for a non whitespace character. + */ + switch ( ch ){ + + case 13: + /* + ** Char was carriage return. Go on to the next line starting at column 0. + */ + line++; + column = 0; + break; + + + case 10: + /* + ** Ignore line feed. CR does both. + */ + break; + + /* + ** Space character. Just advance the cursor and move on. + */ + case 32: + column++; + break; + + /* + ** Tab char. Advance to the next tab column. Tabs are every 8 columns. + */ + case 9: + column += 8; + column &= 0xfffffff8; + break; + + default: + /* + ** Found new string. Work out where it ends so we know how to treat it. + */ + lastchar = ch; + strstart = cptr-1; + strparse = cptr-1; + endcolumn = startcolumn = column; + gotendstr = false; + + do { + ch = *strparse++; + switch ( ch ){ + case 9: + case 10: + case 13: + gotendstr = true; + break; + + case 32: + if (lastchar == 32) gotendstr = true; + endcolumn++; + break; + + default: + endcolumn++; + } + if (strparse >= cptr+length) gotendstr = true; + + lastchar = ch; + }while (!gotendstr); + + + if (strparse >= cptr+length) break; + + /* + ** Strip off any trailing space. + */ + if (*(strparse-2) == 32){ + strparse--; + endcolumn -= 2; + } + + + /* + ** If string straddles the center column then center it. + ** + ** If string is on the left hand side then right justify it. + ** + ** If string is on the right hand side then left justify it. + */ + flags = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_DROPSHADOW; //TPF_NOSHADOW; + + if (startcolumn <40 && endcolumn >40){ + flags = flags | TPF_CENTER; + x = SeenBuff.Get_Width() / 2; + }else{ + if (startcolumn <40){ + flags = flags | TPF_RIGHT; + x = endcolumn *SeenBuff.Get_Width() /80; + }else{ + x = startcolumn * SeenBuff.Get_Width() / 80; + } + } + + /* + ** Temporarily terminate the string. + */ + oldchar = *(strparse-1); + *(strparse-1) = 0; + + /* + ** Create the new class and add it to our list. + */ + ego = new EgoClass (x, y+ line *8 *RESFACTOR, strstart, flags); + + EgoList.Add (ego); + + /* + ** Restore the character that was lost when we added the terminator. + */ + *(strparse-1) = oldchar; + + /* + ** Fix up our outer loop parsing variables. + */ + cptr = strparse; + column += strparse - strstart; + length -= strparse - strstart-1; + + if (ch == 13) { + line++; + column = 0; + }else{ + if (ch == 9){ + column += 7; + column &= 0xfffffff8; + } + } + break; + } + + } while ( length>0 ); + + + /* + ** Work out which palette entries the font needs so we dont fade those colors. + */ + memset (PaletteLUT, 1, sizeof (PaletteLUT)); + int pcolor = PCOLOR_GREEN; + + for (int index = 0; index < 6; index++) { + PaletteLUT[ColorRemaps[pcolor].FontRemap[10+index]] =0; + } + //PaletteLUT[ColorRemaps[pcolor].BrightColor] = 0; + PaletteLUT[ColorRemaps[pcolor].Color] = 0; + PaletteLUT[ColorRemaps[pcolor].Shadow] = 0; + PaletteLUT[ColorRemaps[pcolor].Background] = 0; + PaletteLUT[ColorRemaps[pcolor].Corners] = 0; + PaletteLUT[ColorRemaps[pcolor].Highlight] = 0; + PaletteLUT[ColorRemaps[pcolor].Bright] = 0; + PaletteLUT[ColorRemaps[pcolor].Underline] = 0; + PaletteLUT[ColorRemaps[pcolor].Bar] = 0; + PaletteLUT[ColorRemaps[pcolor].Box] = 0; + + + /* + ** Stop the music. + */ + Theme.Stop(); + + /* + ** Fade to black. + */ + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + + /* + ** Load the reference palette for the font. + */ + CCFileClass("EGOPAL.PAL").Read(&CCPalette, sizeof(CCPalette)); + + /* + ** Copy the font palette entries into the combo palette. + */ + Copy_Palette( CCPalette, ComboPalette ); + ComboPalPtr = (unsigned char*)&ComboPalette; + + for (index = 0 ; index < 256 ; index++ ) + { + if (PaletteLUT[index]) { + ComboPalPtr[index*3] = 0; + ComboPalPtr[index*3+1] = 0; + ComboPalPtr[index*3+2] = 0; + } + } + + /* + ** Clear the Seen Page since we will not be blitting to all of it. + */ + SeenBuff.Clear(); + HidPage.Clear(); + + /* + ** Set the font palette. + */ + Copy_Palette( ComboPalette, CCPalette ); + CCPalette.Set(); + + /* + ** Loop through and load up all the slideshow pictures + */ + for (index = 0 ; index < NUM_SLIDES ; index++) + { + //SlideBuffers[index] = new GraphicBufferClass; + //SlideBuffers[index]->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); + //Load_Title_Screen( &SlideNames[index][0], SlideBuffers[index], (unsigned char*) &SlidePals[index][0] ); + } + + /* + ** Create a new graphic buffer to restore the background from. Initialise it to black so + ** we can start scrolling before the first slideshow picture is blitted. + */ + BackgroundPage = new GraphicBufferClass; + BackgroundPage->Init (SeenBuff.Get_Width(), SeenBuff.Get_Height(), NULL , 0 , (GBC_Enum)0); + + SeenBuff.Blit(*BackgroundPage); + + /* + ** Go away nasty keyboard. + */ + Keyboard->Clear(); + + Set_Logic_Page(HidPage); + + /* + ** Start any old song. + */ + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Queue_Song(THEME_CREDITS); + + /* + ** Init misc timing variables. + */ + int time = TickCount; + int frame = 0; + int picture_frame = 0; + int slide_number = 0; + + Hide_Mouse(); + + key = KN_NONE; + + /* + ** Main scrolling loop. + ** Keeps going until all the EgoClass objects are deleted or esc is pressed. + */ + while ( EgoList.Count() ) + { + frame++; + + /* + ** Update the slideshow frame and switch to the next picture if its time. + */ + picture_frame++; + + if (picture_frame > FRAME_DELAY+50){ + if (slide_number =0 ; i--){ + EgoList[i]->Wipe(BackgroundPage); + if ( EgoList[i]->Scroll(1) ){ + EgoList.Delete(i); + break; + } + } + + /* + ** Render all the text strings in their new positions. + */ + if (LogicPage->Lock()){ + for (i=EgoList.Count()-1 ; i>=0 ; i--){ + EgoList[i]->Render(); + } + LogicPage->Unlock(); + } + + if (frame > 1000 && !Theme.Still_Playing()){ + Theme.Queue_Song(THEME_CREDITS); //NONE); + } + + /* + ** Stop calling Theme.AI after a while so a different song doesnt start playing + */ + Call_Back(); + + /* + ** Kill any spare time before blitting the hid page forward. + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + /* + ** Blit all but the top and bottom of the hid page. This is beacuse the text print doesn't + ** clip vertically and looks ugly when it suddenly appears and disappears. + */ + HidPage.Blit(SeenBuff, 0, 8 * RESFACTOR, 0, 8*RESFACTOR, SeenBuff.Get_Width(), SeenBuff.Get_Height() - 16* RESFACTOR, false); + + + /* + ** If user hits escape then break. + */ + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + key = Keyboard->Check(); + + if (key != 0) + { + key = Keyboard->Get(); + + if (key == KN_ESC){ + break; + } + } + } + + if (key == KN_ESC) + { + Theme.Fade_Out(); + BlackPalette.Set(TIMER_SECOND*2, Call_Back); + } + else + { + /* + ** Wait for the picture to fade down + */ + while (picture_frame <= FADE_DELAY+FRAME_DELAY){ + if (picture_frame < FRAME_DELAY && picture_frame > 10+FADE_DELAY){ + picture_frame = FRAME_DELAY; + } + frame++; + picture_frame++; + + Slide_Show(slide_number, picture_frame); + + Call_Back(); +// Sound_Callback(); //Theme.AI(); + + /* + ** Kill any spare time + */ + while (TickCount - time < frame *speed && !Keyboard->Check()) {} + + } + } + + /* + ** Tidy up. + */ + //SetPriorityClass (GetCurrentProcess() , process_priority); + SeenBuff.Clear(); + + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + Theme.Stop(); + Options.Set_Score_Volume(oldvolume, false); + + for (index = 0 ; index < NUM_SLIDES ; index++){ + delete SlideBuffers[index]; + } + + delete BackgroundPage; + + delete [] credits; + + EgoList.Clear(); +} + + + + + + + + + + + + diff --git a/code/redalert/egos.h b/code/redalert/egos.h new file mode 100644 index 0000000..a0b330e --- /dev/null +++ b/code/redalert/egos.h @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : EGOS.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : September 4th, 1996 * + * * + * Last Update : September 4th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Scrolling movie style credits. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class EgoClass { + public: + EgoClass (int x, int y, char *text, TextPrintType flags); + ~EgoClass (); + + bool Scroll (int distance); + void Render (void); + void Wipe (GraphicBufferClass *background); + + char *Text; + int XPos; + int YPos; + TextPrintType Flags; +}; + + diff --git a/code/redalert/ending.cpp b/code/redalert/ending.cpp new file mode 100644 index 0000000..f7d0ff9 --- /dev/null +++ b/code/redalert/ending.cpp @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ENDING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void GDI_Ending(void) +{ +#ifdef NEVER + if (TempleIoned) { + Play_Movie("GDIFINB"); + } else { + Play_Movie("GDIFINA"); + } + + Score.Presentation(); + + if (TempleIoned) { + Play_Movie("GDIEND2"); + } else { + Play_Movie("GDIEND1"); + } + Play_Movie("CC2TEASE"); +#endif +} + + +/*********************************************************************************************** + * Nod_Ending -- play ending movies for Nod players * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 7/10/1995 BWG : Created. * + *=============================================================================================*/ +void Nod_Ending(void) +{ +#ifdef NEVER + static char const _tanpal[]={0x0,0x0,0xED,0x0,0x2C,0x0,0xFB,0x0,0xFD,0x0,0x0,0x0,0x0,0x0,0x52,0x0}; + + char fname[12]; + char * satpic = new char[64000]; + int oldfontxspacing = FontXSpacing; + void const * oldfont; + + Score.Presentation(); + + oldfont = Set_Font(ScoreFontPtr); + + void * localpal = Load_Alloc_Data(CCFileClass("SATSEL.PAL")); + Load_Uncompress(CCFileClass("SATSEL.CPS"), HidPage, HidPage); + memcpy(satpic, HidPage.Get_Buffer(), 64000); + + void * kanefinl = Load_Sample("KANEFINL.AUD"); + void * loopie6m = Load_Sample("LOOPIE6M.AUD"); + + Play_Movie("NODFINAL", THEME_NONE, false); + + Hide_Mouse(); + Wait_Vert_Blank(VertBlank); + Set_Palette(localpal); + memcpy(SeenBuff.Get_Buffer(), satpic, 64000); + Show_Mouse(); + + Keyboard->Clear(); + Play_Sample(kanefinl, 255, 128); + Play_Sample(loopie6m, 255, 128); + + bool mouseshown = false; + bool done = false; + int selection = 1; + bool printedtext = false; + while (!done) { + if (!printedtext && !Is_Sample_Playing(kanefinl)) { + printedtext++; + Alloc_Object(new ScorePrintClass(Text_String(TXT_SEL_TARGET), 0, 180, _tanpal)); + mouseshown = true; + Show_Mouse(); + } + Call_Back_Delay(1); + if (!Keyboard->Check()) { + if (!Is_Sample_Playing(loopie6m)) Play_Sample(loopie6m, 255, 128); + } else { + if (Is_Sample_Playing(kanefinl)) { + Clear_KeyBuffer(); + } else { + int key = Keyboard->Get(); + if ((key & 0xFF) == KN_LMOUSE && !(key & KN_RLSE_BIT)) { + int mousex = MouseQX; + int mousey = MouseQY; + if (mousex >= 22 && mousey <= 177) { + done++; + if (mousex < 160 && mousey < 100) selection = 2; + if (mousex < 160 && mousey >= 100) selection = 3; + if (mousex >= 160 && mousey >= 100) selection = 4; + } + } + } + } + } + if (mouseshown) Hide_Mouse(); + delete satpic; + +/* get rid of all the animating objects */ + for (int i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + // erase the "choose a target" text + SeenBuff.Fill_Rect(0, 180, 319, 199, 0); + + Hide_Mouse(); + Keyboard->Clear(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + Free_Sample(kanefinl); + Free_Sample(loopie6m); + + sprintf(fname, "NODEND%d", selection); + PreserveVQAScreen = 1; + Play_Movie(fname); + + Play_Movie("CC2TEASE"); +#endif +} diff --git a/code/redalert/ending.h b/code/redalert/ending.h new file mode 100644 index 0000000..173abe1 --- /dev/null +++ b/code/redalert/ending.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ENDING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ENDING.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : July 10, 1995 * + * * + * Last Update : July 10, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef ENDING_H +#define ENDING_H + +void Nod_Ending(void); + +#endif diff --git a/code/redalert/event.cpp b/code/redalert/event.cpp new file mode 100644 index 0000000..176dd9a --- /dev/null +++ b/code/redalert/event.cpp @@ -0,0 +1,1067 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EVENT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : November 10, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EventClass::EventClass -- Construct an id and cell based event. * + * EventClass::EventClass -- Construct simple target type event. * + * EventClass::EventClass -- Constructor for mission change events. * + * EventClass::EventClass -- Constructor for navigation computer events. * + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * EventClass::EventClass -- Constructor for sidebar build events. * + * EventClass::EventClass -- Constructs event to transfer special flags. * + * EventClass::EventClass -- Default constructor for event objects. * + * EventClass::EventClass -- Event for sequencing animations. * + * EventClass::EventClass -- Megamission assigned to unit. * + * EventClass::Execute -- Execute a queued command. * + * EventClass::EventClass -- construct a variable-sized event * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#endif //WIN32 + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +/*************************************************************************** +** Table of what data is really used in the EventClass struct for different +** events. This table must be kept current with the EventType enum. +*/ +unsigned char EventClass::EventLength[EventClass::LAST_EVENT] = { + 0, // EMPTY + size_of(EventClass, Data.General ), // ALLY + size_of(EventClass, Data.MegaMission ), // MEGAMISSION + size_of(EventClass, Data.MegaMission_F ), // MEGAMISSION_F + size_of(EventClass, Data.Target ), // IDLE + size_of(EventClass, Data.Target ), // SCATTER + 0, // DESTRUCT + 0, // DEPLOY + size_of(EventClass, Data.Place ), // PLACE + 0, // OPTIONS + size_of(EventClass, Data.General ), // GAMESPEED + size_of(EventClass, Data.Specific ), // PRODUCE + size_of(EventClass, Data.Specific.Type ), // SUSPEND + size_of(EventClass, Data.Specific.Type ), // ABANDON + size_of(EventClass, Data.Target ), // PRIMARY + size_of(EventClass, Data.Special ), // SPECIAL_PLACE + 0, // EXIT + size_of(EventClass, Data.Anim ), // ANIMATION + size_of(EventClass, Data.Target ), // REPAIR + size_of(EventClass, Data.Target ), // SELL + size_of(EventClass, Data.SellCell), // SELLCELL + size_of(EventClass, Data.Options ), // SPECIAL + 0, // FRAMESYNC + 0, // MESSAGE + size_of(EventClass, Data.FrameInfo.Delay ), // RESPONSE_TIME + size_of(EventClass, Data.FrameInfo ), // FRAMEINFO + 0, // SAVEGAME + size_of(EventClass, Data.NavCom ), // ARCHIVE + size_of(EventClass, Data.Variable.Size), // ADDPLAYER + size_of(EventClass, Data.Timing ), // TIMING + size_of(EventClass, Data.ProcessTime ), // PROCESS_TIME +#ifdef FIXIT_VERSION_3 // Stalemate games. + 0, // PROPOSE_DRAW + 0, // RETRACT_DRAW +#endif +}; + +char * EventClass::EventNames[EventClass::LAST_EVENT] = { + "EMPTY", + "ALLY", + "MEGAMISSION", + "MEGAMISSION_F", + "IDLE", + "SCATTER", + "DESTRUCT", + "DEPLOY", + "PLACE", + "OPTIONS", + "GAMESPEED", + "PRODUCE", + "SUSPEND", + "ABANDON", + "PRIMARY", + "SPECIAL_PLACE", + "EXIT", + "ANIMATION", + "REPAIR", + "SELL", + "SELLCELL", + "SPECIAL", + "FRAMESYNC", + "MESSAGE", + "RESPONSE_TIME", + "FRAMEINFO", + "SAVEGAME", + "ARCHIVE", + "ADDPLAYER", + "TIMING", + "PROCESS_TIME", +#ifdef FIXIT_VERSION_3 // Stalemate games. + "PROPOSE_DRAW", + "RETRACT_DRAW", +#endif +}; + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructs event to transfer special flags. * + * * + * This constructs an event that will transfer the special flags. * + * * + * INPUT: data -- The special flags to be transported to all linked computers. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(SpecialClass data) +{ + ID = PlayerPtr->ID; + Type = SPECIAL; + Frame = ::Frame; + Data.Options.Data = data; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct simple target type event. * + * * + * This will construct a generic event that needs only a target parameter. The actual * + * event and target values are specified as parameters. * + * * + * INPUT: type -- The event type to construct. * + * * + * target-- The target value that this event is to apply to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass target) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Target.Whom = target; +} + + +EventClass::EventClass(EventType type, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.SellCell.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Default constructor for event objects. * + * * + * This constructs a simple event object that requires no parameters other than the * + * type of event it is. * + * * + * INPUT: type -- The type of event to construct. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for general-purpose-data events. * + * * + * INPUT: type -- The type of event to construct. * + * val -- data value * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int val) +{ + ID = PlayerPtr->ID; + Type = type; + Data.General.Value = val; + Frame = ::Frame; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for navigation computer events. * + * * + * Constructor for events that are used to assign the navigation computer. * + * * + * INPUT: type -- The type of event (this constructor can be used by other navigation * + * type events). * + * * + * src -- The object that the event should apply to. * + * * + * dest -- The destination (or target) that the event needs to complete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, TargetClass src, TargetClass dest) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.NavCom.Whom = src; + Data.NavCom.Where = TargetClass(dest); +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Event for sequencing animations. * + * * + * This constructor is used for animations that must be created through the event system. * + * * + * INPUT: anim -- The animation that will be created. * + * * + * coord -- The location where the animation is to be created. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord) +{ + ID = PlayerPtr->ID; + Type = ANIMATION; + Frame = ::Frame; + Data.Anim.What = anim; + Data.Anim.Owner = owner; + Data.Anim.Where = coord; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION; + Frame = ::Frame; + Data.MegaMission.Whom = src; + Data.MegaMission.Mission = mission; + Data.MegaMission.Target = target; + Data.MegaMission.Destination = destination; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Megamission assigned to unit. * + * * + * This is the event that is used to assign most missions to units. It combines both the * + * mission and the target (navcom and tarcom). This variation is used for formation moves. * + * * + * INPUT: src -- The object that this mission is to apply to. * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target to assign to this object's TarCom. * + * * + * destination -- The destination to assign to this object's NavCom. * + * * + * speed -- The formation override speed for this move. * + * * + * maxspeed -- The formation override maximum speed for this move. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed) +{ + ID = PlayerPtr->ID; + Type = MEGAMISSION_F; + Frame = ::Frame; + Data.MegaMission_F.Whom = src; + Data.MegaMission_F.Mission = mission; + Data.MegaMission_F.Target = TargetClass(target); + Data.MegaMission_F.Destination = TargetClass(destination); + Data.MegaMission_F.Speed = speed; + Data.MegaMission_F.MaxSpeed = maxspeed; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for sidebar build events. * + * * + * This constructor is used for events that deal with an object type and an object ID. * + * Typically, this is used exclusively by the sidebar. * + * * + * INPUT: type -- The event type of this object. * + * * + * object -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, int id) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Specific.Type = object; + Data.Specific.ID = id; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Constructor for object types affecting cells event. * + * * + * This constructor is used for those events that have an object type and associated cell. * + * Typically, this is for building placement after construction has completed. * + * * + * INPUT: type -- The event type for this object. * + * * + * object -- The object type number (actual object is probably inferred from the * + * sidebar data). * + * * + * cell -- The cell location where this event is to occur. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, RTTIType object, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Place.Type = object; + Data.Place.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- Construct an id and cell based event. * + * * + * This constructor is used for those events that require an ID number and a cell location. * + * * + * INPUT: type -- The event type this will be. * + * * + * id -- The arbitrary id number to assign. * + * * + * cell -- The location for this event. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, int id, CELL cell) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Special.ID = id; + Data.Special.Cell = cell; +} + + +/*********************************************************************************************** + * EventClass::EventClass -- construct a variable-sized event * + * * + * INPUT: * + * ptr ptr to data associated with this event * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/10/1995 BRR : Created. * + *=============================================================================================*/ +EventClass::EventClass(EventType type, void * ptr, unsigned long size) +{ + ID = PlayerPtr->ID; + Type = type; + Frame = ::Frame; + Data.Variable.Pointer = ptr; + Data.Variable.Size = size; +} + + +/*********************************************************************************************** + * EventClass::Execute -- Execute a queued command. * + * * + * This routine executes an event. The even must already have been confirmed by any * + * remote machine before calling this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void EventClass::Execute(void) +{ + TechnoClass * techno; + AnimClass * anim = 0; + HouseClass * house = 0; +// CELL cell = 0; + char txt[80]; + bool formation = false; +// RTTIType rt; + + if (Debug_Print_Events) { + printf("(%d) Executing %s ID:%d Frame:%d ", + ::Frame, EventNames[Type], ID, Frame); + } + + switch (Type) { + + /* + ** Update the archive target for this building. + */ + case ARCHIVE: + techno = Data.NavCom.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->ArchiveTarget = Data.NavCom.Where.As_TARGET(); + } + break; + + /* + ** Make or break alliance. + */ + case ALLY: + house = Houses.Raw_Ptr(Data.General.Value); + if (Houses.Raw_Ptr(ID)->Is_Ally(house)) { + Houses.Raw_Ptr(ID)->Make_Enemy((HousesType)Data.General.Value); + } else { + Houses.Raw_Ptr(ID)->Make_Ally((HousesType)Data.General.Value); + } + break; + + /* + ** Special self destruct action requested. This is active in the multiplayer mode. + */ + case DESTRUCT: + Houses.Raw_Ptr(ID)->Flag_To_Die(); + break; + + /* + ** Update the special control flags. This is necessary so that in a multiplay + ** game, all machines will agree on the rules. If these options change during + ** game play, then all players are informed that options have changed. + */ + case SPECIAL: + { + Special = Data.Options.Data; + HouseClass * house = Houses.Raw_Ptr(ID); + + //sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->Name); // Should be IniName? ST - 5/8/2019 + sprintf(txt, Text_String(TXT_SPECIAL_WARNING), house->IniName); + Session.Messages.Add_Message(NULL, 0, txt, + house->RemapColor, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1200); + Map.Flag_To_Redraw(false); + } + break; + + /* + ** Starts or stops repair on the specified object. This event is triggered by the + ** player clicking the repair wrench on a building. + */ + case REPAIR: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive) { + techno->Repair(-1); + } + break; + + /* + ** Tells a building/unit to sell. This event is triggered by the player clicking the + ** sell animating cursor over the building or unit. + */ + case SELL: + techno = Data.Target.Whom.As_Techno(); + if (techno && techno->IsActive && techno->House == Houses.Raw_Ptr(ID)) { + if (techno->What_Am_I() == RTTI_BUILDING || (techno->What_Am_I() == RTTI_UNIT && Map[techno->Center_Coord()].Cell_Building() != 0)) { + techno->Sell_Back(-1); + } + } else { +// if (Is_Target_Cell(Data.Target.Whom)) { +// Houses.Raw_Ptr(ID)->Sell_Wall(As_Cell(Data.Target.Whom)); +// } + } + break; + + /* + ** Tells the wall at the specified location to sell off. + */ + case SELLCELL: +// cell = Data.SellCell.Cell; + Houses.Raw_Ptr(ID)->Sell_Wall(Data.SellCell.Cell); + break; + + /* + ** This even is used to trigger an animation that is generated as a direct + ** result of player intervention. + */ + case ANIMATION: + anim = new AnimClass(Data.Anim.What, Data.Anim.Where); + if (anim) { + //2019/09/19 JAS - Visibility needs to be determined per player + if (Data.Anim.Owner == HOUSE_NONE || Data.Anim.What != ANIM_MOVE_FLASH) + { + anim->Set_Visible_Flags(0xffff); + } + else + { + anim->Set_Visible_Flags(1 << Data.Anim.Owner); + } + } + break; + + /* + ** This event will place the specified object at the specified location. + ** The event is used to place newly constructed buildings down on the map. The + ** object type is specified. From this object type, the house can determine the + ** exact factory and real object pointer to use. + */ + case PLACE: + Houses.Raw_Ptr(ID)->Place_Object(Data.Place.Type, Data.Place.Cell); + break; + + /* + ** This event starts production of the specified object type. The house can + ** determine from the type and ID value, what object to begin production on and + ** what factory to use. + */ + case PRODUCE: + Houses.Raw_Ptr(ID)->Begin_Production(Data.Specific.Type, Data.Specific.ID); + break; + + /* + ** This event is generated when the player puts production on hold. From the + ** object type, the factory can be inferred. + */ + case SUSPEND: + Houses.Raw_Ptr(ID)->Suspend_Production(Data.Specific.Type); + break; + + /* + ** This event is generated when the player cancels production of the specified + ** object type. From the object type, the exact factory can be inferred. + */ + case ABANDON: + Houses.Raw_Ptr(ID)->Abandon_Production(Data.Specific.Type); + break; + + /* + ** Toggles the primary factory state of the specified building. + */ + case PRIMARY: + { + BuildingClass * building = Data.Target.Whom.As_Building(); + if (building && building->IsActive) { + building->Toggle_Primary(); + } + } + break; + + /* + ** This is the general purpose mission control event. Most player + ** action routes through this event. It sets a unit's mission, TarCom, + ** and NavCom to the values specified. + */ + case MEGAMISSION_F: + techno = Data.MegaMission_F.Whom.As_Techno(); + if (techno && techno->IsActive && techno->Is_Foot()) { + ((FootClass *)techno)->IsFormationMove = true; + ((FootClass *)techno)->FormationSpeed = Data.MegaMission_F.Speed; + ((FootClass *)techno)->FormationMaxSpeed = Data.MegaMission_F.MaxSpeed; + FormMove = true; + FormSpeed = Data.MegaMission_F.Speed; + FormMaxSpeed = Data.MegaMission_F.MaxSpeed; + formation = true; + } + // Fall thru to next case... + + case MEGAMISSION: + if (Debug_Print_Events) { + printf("Whom:%x Tgt:%x Dest:%x ", + Data.MegaMission.Whom.As_TARGET(), + Data.MegaMission.Target.As_TARGET(), + Data.MegaMission.Destination.As_TARGET()); + } + techno = Data.MegaMission.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && techno->Strength > 0 && !techno->IsInLimbo) { + + /* + ** Fetch a pointer to the object of the mission. If there is an error with + ** this object, such as it is dead, then bail. + */ + ObjectClass * object = NULL; + if (Data.MegaMission.Target.Is_Valid()) { + object = Data.MegaMission.Target.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Target.Invalidate(); + } + } + + /* + ** If the destination target is invalid because the object is dead, then + ** bail from processing this mega mission. + */ + if (Data.MegaMission.Destination.Is_Valid()) { + object = Data.MegaMission.Destination.As_Object(); + if (object != NULL && (!object->IsActive || object->Strength == 0 || object->IsInLimbo)) { + break; +// object = NULL; +// Data.MegaMission.Destination.Invalidate(); + } + } + + /* + ** Break any existing tether or team contact, since it is now invalid. + */ + if (!techno->IsTethered) { + techno->Transmit_Message(RADIO_OVER_OUT); + } + if (techno->Is_Foot()) { + if (!formation) ((FootClass *)techno)->IsFormationMove = false; + if (((FootClass *)techno)->Team) { + ((FootClass *)techno)->Team->Remove((FootClass *)techno); + } + } + + if (object != NULL) { + + // 2019/09/20 JAS - Added record of who clicked on the object + HouseClass* house = Houses.Raw_Ptr(ID); + bool is_allied = house != nullptr && house->Is_Ally(techno); + if (is_allied) { + object->Clicked_As_Target((HousesType)ID); + } + } + + /* + ** Test to see if the navigation target should really be queued rather + ** than assigned to the object. This would be the case if this is a + ** special queued move mission and there is already a valid navigation + ** target for this unit. + */ + bool q = (Data.MegaMission.Mission == MISSION_QMOVE); + + techno->Assign_Mission(Data.MegaMission.Mission); + + if (techno->Is_Foot()) { + ((FootClass*)techno)->SuspendedNavCom = TARGET_NONE; + } + techno->SuspendedTarCom = TARGET_NONE; + + /* + ** Guard area mode is handled with care. The specified target is actually + ** assigned as the location that should be guarded. In addition, the + ** movement destination is immediately set to this new location. + */ + if (Data.MegaMission.Mission == MISSION_GUARD_AREA && techno->Is_Foot()) { + techno->Assign_Target(TARGET_NONE); + techno->Assign_Destination(Data.MegaMission.Target.As_TARGET()); + techno->ArchiveTarget = Data.MegaMission.Target.As_TARGET(); + } else { + if (q && techno->Is_Foot()) { + ((FootClass *)techno)->Queue_Navigation_List(Data.MegaMission.Destination.As_TARGET()); + } else { + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + techno->Assign_Target(Data.MegaMission.Target.As_TARGET()); + techno->Assign_Destination(Data.MegaMission.Destination.As_TARGET()); + } + } + + // + // Special case for ship repairing: If the assigned mission is to + // move, and 'techno' is a Vessel: + // If the destination is a shipyard or sub pen, set the IsToSelfRepair flag + // Otherwise, clear both IsToSelfRepair and IsSelfRepairing + // + RTTIType rt = techno->What_Am_I(); +// rt = Data.MegaMission.Whom; + if (rt == RTTI_VESSEL && techno != NULL && techno->What_Am_I() == RTTI_VESSEL) { + VesselClass *ship = (VesselClass *)techno; + if (Data.MegaMission.Mission == MISSION_MOVE) { + if (object != NULL) { + if (object->What_Am_I() == RTTI_BUILDING && + ((BuildingClass *)object)->House->Is_Ally(techno) && +// if ((RTTIType)Data.MegaMission.Destination == RTTI_BUILDING && + (((BuildingClass *)object)->Class->Type == STRUCT_SHIP_YARD || + ((BuildingClass *)object)->Class->Type == STRUCT_SUB_PEN)) { + ship->IsToSelfRepair = true; + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } else { + ship->IsToSelfRepair = false; + ship->IsSelfRepairing = false; + } + } + +#ifdef NEVER + if ((techno->What_Am_I() == RTTI_UNIT || techno->What_Am_I() == RTTI_INFANTRY) && + Data.MegaMission.Mission == MISSION_GUARD_AREA) { + + ((FootClass *)techno)->ArchiveTarget = Data.MegaMission.Destination; + } +#endif + } + break; + + /* + ** Request that the unit/infantry/aircraft go into idle mode. + */ + case IDLE: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered && techno->What_Am_I() != RTTI_BUILDING) { + techno->Transmit_Message(RADIO_OVER_OUT); + techno->Assign_Destination(TARGET_NONE); + techno->Assign_Target(TARGET_NONE); + techno->Enter_Idle_Mode(); + if (techno->Is_Foot()) { + ((FootClass *)techno)->Clear_Navigation_List(); + } + } + break; + + /* + ** Request that the unit/infantry/aircraft scatter from its current location. + */ + case SCATTER: + techno = Data.Target.Whom.As_Techno(); + if (techno != NULL && techno->Is_Foot() && techno->IsActive && !techno->IsInLimbo && !techno->IsTethered) { + ((FootClass *)techno)->IsScattering = true; + techno->Scatter(0, true, false); + } + break; + + /* + ** If we are placing down the ion cannon blast then lets take + ** care of it. + */ + case SPECIAL_PLACE: + Houses.Raw_Ptr(ID)->Place_Special_Blast((SpecialWeaponType)Data.Special.ID, Data.Special.Cell); + break; + + /* + ** Exit the game. + ** Give parting message while palette is fading to black. + */ + case EXIT: + Theme.Queue_Song(THEME_NONE); + Stop_Speaking(); + Speak(VOX_CONTROL_EXIT); + while (Is_Speaking()) { + Call_Back(); + } + GameActive = false; +// jmarshall - hack for skirmish games, lets the game flow go back to the mainmenu. + Session.Type = GAME_NORMAL; +// jmarshall end + break; + + /* + ** Process the options menu, unless we're playing back a recording. + */ + case OPTIONS: + if (!Session.Play) { + SpecialDialog = SDLG_OPTIONS; + } + break; + + /* + ** Process the options Game Speed + */ + case GAMESPEED: + Options.GameSpeed = Data.General.Value; + break; + + /* + ** Adjust connection timing for multiplayer games + */ + case RESPONSE_TIME: + Session.MaxAhead = Data.FrameInfo.Delay; + break; + + /* + ** Save a multiplayer game (this event is only generated in multiplayer mode) + */ + case SAVEGAME: + /* + ** Show the user what's going on with a message box (but only if + ** we're not already inside a dialog box routine!) + */ + if (SpecialDialog == SDLG_NONE) { + CDTimerClass timer; + //timer.Start(); + timer = TICKS_PER_SECOND * 4; + + WWMessageBox().Process(TXT_SAVING_GAME, TXT_NONE); + + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + + while (timer > 0) { + Call_Back(); + } + + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + else { + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + } + break; + + /* + ** Add a new player to the game: + ** - Form a network connection to him + ** - Add his name, ID, House etc to our list of players + ** - Re-sort the ID array + ** - Place his units on the map + */ + case ADDPLAYER: + int i; + printf("ADDPLAYER EVENT!\n"); + for (i=0;i<(int)Data.Variable.Size;i++) { + printf("%d\n", ((char *)Data.Variable.Pointer)[i]); + } + if (ID != PlayerPtr->ID) { + delete [] Data.Variable.Pointer; + } + break; + + // + // This event tells all systems to use new timing values. It's like + // RESPONSE_TIME, only it works. It's only used with the + // COMM_MULTI_E_COMP protocol. + // + case TIMING: +#if(TIMING_FIX) + // + // If MaxAhead is about to increase, we're vulnerable to a Packet- + // Received-Too-Late error, if any system generates an event after + // this TIMING event, but before it executes. So, record the + // period of vulnerability's frame start & end values, so we + // can reschedule these events to execute after it's over. + // + if (Data.Timing.MaxAhead > Session.MaxAhead) { + NewMaxAheadFrame1 = Frame; + NewMaxAheadFrame2 = Frame + Data.Timing.MaxAhead; + } +#endif + Session.DesiredFrameRate = Data.Timing.DesiredFrameRate; + Session.MaxAhead = Data.Timing.MaxAhead; + +#ifndef WOLAPI_INTEGRATION + /* + ** If spawned from WChat then we should be getting poked every minute. If not then + ** deliberately break the max ahead value + */ +#ifdef WIN32 + if (Special.IsFromWChat) { + Session.MaxAhead += DDEServer.Time_Since_Heartbeat()/(70*60); + } +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + if (Debug_Print_Events) { + printf("DesiredFrameRate:%d MaxAhead:%d ", + Session.DesiredFrameRate, + Session.MaxAhead); + } + + break; + + // + // This event tells all systems what the other systems' process + // timing requirements are; it's used to compute a desired frame rate + // for the game. + // + case PROCESS_TIME: + for (i = 0; i < Session.Players.Count(); i++) { + if (ID == Session.Players[i]->Player.ID) { + Session.Players[i]->Player.ProcessTime = Data.ProcessTime.AverageTicks; + break; + } + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case PROPOSE_DRAW: + if( ID == PlayerPtr->ID ) + { + if( Scen.bOtherProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bLocalProposesDraw = true; + break; + } + Scen.bLocalProposesDraw = true; + //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_PROPOSED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + if( Scen.bLocalProposesDraw ) + { + // Both sides agree to draw. Game will end in a tie. + Scen.bOtherProposesDraw = true; + break; + } + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + //PG sprintf( szMessage, TXT_WOL_DRAW_PROPOSED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = true; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; + + case RETRACT_DRAW: + if( ID == PlayerPtr->ID ) + { + Scen.bLocalProposesDraw = false; + //PG Session.Messages.Add_Message( NULL, 0, TXT_WOL_DRAW_RETRACTED_LOCAL, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + else + { + char szMessage[ 100 ]; + for (i = 0; i < Session.Players.Count(); i++) + { + if (ID == Session.Players[i]->Player.ID) + { + //PG sprintf( szMessage, TXT_WOL_DRAW_RETRACTED_OTHER, Session.Players[i]->Name ); + break; + } + } + Scen.bOtherProposesDraw = false; + Session.Messages.Add_Message( NULL, 0, szMessage, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + } + Sound_Effect( VOC_INCOMING_MESSAGE ); + break; +#endif + + /* + ** Default: do nothing. + */ + default: + break; + } + + if (Debug_Print_Events) { + printf("\n"); + } + +} diff --git a/code/redalert/event.h b/code/redalert/event.h new file mode 100644 index 0000000..647bff6 --- /dev/null +++ b/code/redalert/event.h @@ -0,0 +1,257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EVENT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/09/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EVENT_H +#define EVENT_H + +/* +** This event class is used to contain all external game events (things that the player can +** do at any time) so that these events can be transported between linked computers. This +** encapsulation is required in order to ensure that each event affects all computers at the +** same time (same game frame). +** NOTE: If you add or remove an event type, you must also update the globals +** EventLength[] and EventNames[]. +*/ +class EventClass +{ + public: + + /* + ** All external events are identified by these labels. + */ + typedef enum EventType : unsigned char { + EMPTY, + + ALLY, // Make allie of specified house. + MEGAMISSION, // Full change of mission with target and destination. + MEGAMISSION_F, // Full change of mission with target and destination, and formation overrides. + IDLE, // Request to enter idle mode. + SCATTER, // Request to scatter from current location. + DESTRUCT, // Self destruct request (surrender action). + DEPLOY, // MCV is to deploy at current location. + PLACE, // Place building at location specified. + OPTIONS, // Bring up options screen. + GAMESPEED, // Set game speed + PRODUCE, // Start or Resume production. + SUSPEND, // Suspend production. + ABANDON, // Abandon production. + PRIMARY, // Primary factory selected. + SPECIAL_PLACE, // Special target location selected + EXIT, // Exit game. + ANIMATION, // Flash ground as movement feedback. + REPAIR, // Repair specified object. + SELL, // Sell specified object. + SELLCELL, // Sell wall at specified cell. + SPECIAL, // Special options control. + + // Private events. + FRAMESYNC, // Game-connection packet; includes Scenario CRC & sender's frame # + // Used to initiate game connection phase & to reconnect; + // When one of these is received, the receiver knows there are + // no associated commands in this packet. + MESSAGE, // Message to another player (The message is the 40 bytes + // after the event class). + RESPONSE_TIME, // use a new propagation delay value + FRAMEINFO, // Game-heartbeat packet; includes Game CRC & command count + // All packets sent for a frame are prefixed with one of these + SAVEGAME, // allows multiplayer games to save + ARCHIVE, // Updates archive target on specified object. + ADDPLAYER, // Add a new player + + TIMING, // new timing values for all systems to use + PROCESS_TIME, // a system's average processing time, in ticks per frame + +#ifdef FIXIT_VERSION_3 // Stalemate games. + PROPOSE_DRAW, // Players proposes that 2-player game be called a stalemate. + RETRACT_DRAW, // Player retracts proposed draw offer. +#endif + + LAST_EVENT, // one past the last event + } EventType; + + EventType Type; // Type of queue command object. + + /* + ** 'Frame' is the frame that the command should execute on. + ** 27 bits gives over 25 days of playing time without wrapping, + ** at 30 frames per second, so it should be plenty! + */ + unsigned Frame : 26; + + /* + ** House index of the player originating this event + */ + unsigned ID : 5; + + /* + ** This bit tells us if we've already executed this event. + */ + unsigned IsExecuted: 1; + + /* + ** This union contains the specific data that the event requires. + */ + union { + struct { + SpecialClass Data; // The special option flags. + } Options; + struct { + CELL Cell; // The cell to sell wall at. + } SellCell; + struct { + xTargetClass Whom; // The object to apply the event to. + } Target; + struct { + AnimType What; // The animation to create. + HousesType Owner; // The owner of the animation (when it matters). + COORDINATE Where; // The location to place the animation. + } Anim; + struct { + int Value; // general-purpose data + } General; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + } MegaMission; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + xTargetClass Target; // Target to assign. + xTargetClass Destination;// Destination to assign. + SpeedType Speed; // Formation override speed. + MPHType MaxSpeed; // Formation override maximum speed. + } MegaMission_F; + struct { + xTargetClass Whom; // Whom to apply mission to. + MissionType Mission; // What mission to apply. + } Mission; + struct { + xTargetClass Whom; // Whom to apply movement change to. + xTargetClass Where; // Where to set NavCom to. + } NavCom; + struct { + xTargetClass Whom; // Whom to apply attack change to. + xTargetClass Target; // What to set TarCom to. + } TarCom; + struct { + RTTIType Type; + int ID; + } Specific; + struct { + RTTIType Type; + CELL Cell; + } Place; + struct { + int ID; + CELL Cell; + } Special; + + /* + ** This structure is used for FRAMEINFO, FRAMESYNC, and RESPONSE_TIME + ** events; exactly one of these will be sent each frame, whether there's + ** data that frame or not. + ** CRC: the game CRC when this packet was generated; used to detect sync errors + ** CommandCount: # of commands the sender has sent; used to detect missed packets + ** Delay: sender's propagation delay value for this frame + */ + struct { + unsigned long CRC; + unsigned short CommandCount; // # commands sent so far + unsigned char Delay; // propagation delay used this frame + // (Frame - Delay = sender's current frame #) + } FrameInfo; + /* + ** This structure is used for the special variable-length event. This event + ** can be anything the application wants it to be. Its purpose is to allow + ** relatively large amounts of data to be associated with the event, without + ** bloating the size of this union (and thus all other event types). + */ + struct { + void * Pointer; + unsigned long Size; + } Variable; + + // + // This structure sets new timing values for all systems in a multiplayer + // game. This structure replaces the RESPONSE_TIME event for + // the COMM_MULTI_E_COMP protocol. + // + struct { + unsigned short DesiredFrameRate; + unsigned short MaxAhead; + } Timing; + + // + // This structure is transmitted by all systems, and is used to compute + // the "desired" frame rate for the game. + // + struct { + unsigned short AverageTicks; + } ProcessTime; + + } Data; + + //-------------- Constructors --------------------- + EventClass(void) {Type = EMPTY;}; + EventClass(SpecialClass data); + EventClass(EventType type, TargetClass target); + EventClass(EventType type); + EventClass(EventType type, int val); + EventClass(EventType type, CELL cell); + EventClass(EventType type, TargetClass src, TargetClass dest); + EventClass(TargetClass src, MissionType mission, TargetClass target=TARGET_NONE, TargetClass destination=TARGET_NONE); + + EventClass(TargetClass src, MissionType mission, TargetClass target, TargetClass destination, SpeedType speed, MPHType maxspeed); + + EventClass(EventType type, RTTIType object, int id); + EventClass(EventType type, RTTIType object, CELL cell); + EventClass(EventType type, int id, CELL cell); + EventClass(AnimType anim, HousesType owner, COORDINATE coord); + EventClass(void *ptr, unsigned long size); + EventClass(EventType type, void *ptr, unsigned long size); + + // Process the event. + void Execute(void); + + int operator == (EventClass & q) { + return memcmp(this, &q, sizeof(q)) == 0; + }; + + static unsigned char EventLength[LAST_EVENT]; + static char * EventNames[LAST_EVENT]; +}; + +#endif diff --git a/code/redalert/expand.cpp b/code/redalert/expand.cpp new file mode 100644 index 0000000..ae1cad2 --- /dev/null +++ b/code/redalert/expand.cpp @@ -0,0 +1,542 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/EXPAND.CPP 7 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXPAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/03/95 * + * * + * Last Update : Mar 01, 1997 [V.Grippi] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +//#define CS_DEBUG + +#define ARRAYOFFSET 20 + + +/*********************************************************************************************** + * Expansion_CS_Present -- Is the Counterstrike expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if counterstrike installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/5/97 1:59PM ST : Fixed to check for EXPAND.MIX * + *=============================================================================================*/ +bool Expansion_CS_Present(void) +{ + // ajw 9/29/98 + return Is_Counterstrike_Installed(); +// RawFileClass file("EXPAND.MIX"); +// return(file.Is_Available()); +} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +/*********************************************************************************************** + * Expansion_AM_Present -- Is the Aftermath expansion available? * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if AfterMath is installed * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 7/9/97 1:59PM BG : Fixed to check for EXPAND2.MIX * + *=============================================================================================*/ +bool Expansion_AM_Present(void) +{ + // ajw 9/29/98 + return Is_Aftermath_Installed(); +// RawFileClass file("EXPAND2.MIX"); +// return(file.Is_Available()); +} +#endif + + +const char* ExpandNames[] = { + "SCG20EA", + "SCG21EA", + "SCG22EA", + "SCG23EA", + "SCG24EA", + "SCG26EA", + "SCG27EA", + "SCG28EA", + "SCU31EA", + "SCU32EA", + "SCU33EA", + "SCU34EA", + "SCU35EA", + "SCU36EA", + "SCU37EA", + "SCU38EA", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "SCG43EA", // Harbor Reclamation + "SCG41EA", // In the nick of time + "SCG40EA", // Caught in the act + "SCG42EA", // Production Disruption + "SCG47EA", // Negotiations + "SCG45EA", // Monster Tank Madness + "SCG44EA", // Time Flies + "SCG48EA", // Absolut MADness + "SCG46EA", // Pawn + + "SCU43EA", // Testing Grounds + "SCU40EA", // Shock Therapy + "SCU42EA", // Let's Make a Steal + "SCU41EA", // Test Drive + "SCU45EA", // Don't Drink The Water + "SCU44EA", // Situation Critical + "SCU46EA", // Brothers in Arms + "SCU47EA", // Deus Ex Machina + "SCU48EA", // Grunyev Revolution +#endif + NULL + }; + +const char* TestNames2[] = { + "SCG01EA", + "SCG02EA", + "SCG03EA", + "SCG04EA", + "SCG05EA", + "SCG06EA", + "SCG07EA", + "SCG08EA", + "SCU01EA", + "SCU02EA", + "SCU03EA", + "SCU04EA", + "SCU05EA", + "SCU06EA", + "SCU07EA", + "SCU08EA", + "SCU09EA", + NULL +}; + +#ifdef GERMAN +const char* XlatNames[] = { + "Zusammenstoss", + "Unter Tage", + "Kontrollierte Verbrennung", + "Griechenland 1 - Stavros", + "Griechenland 2 - Evakuierung", + "Sibirien 1 - Frische Spuren", + "Sibirien 2 - In der Falle", + "Sibirien 3 - Wildnis", + "Das Feld der Ehre", + "Belagerung", + "Mausefalle", + "Teslas Erbe", + "Soldat Volkov", + "Die Spitze der Welt", + "Paradoxe Gleichung", + "Nukleare Eskalation", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + "Ein sicherer Hafen", // "SCG43EA", // Harbor Reclamation + "Zeitkritische Routine", // "SCG41EA", // In the nick of time + "Auf frischer Tat ertappt", // "SCG40EA", // Caught in the act + "Drastischer Baustopp", // "SCG42EA", // Production Disruption + "Harte Verhandlungen", // "SCG47EA", // Negotiations + "Ferngelenktes Kriegsspielzeug",// "SCG45EA", // Monster Tank Madness + "Licht aus", // "SCG44EA", // Time Flies + "Molekulare Kriegsfhrung", // "SCG48EA", // Absolut MADness + "Bauernopfer", // "SCG46EA", // Pawn + + "Testgel„nde", // "SCU43EA", // Testing Grounds + "Schocktherapie", // "SCU40EA", // Shock Therapy + "Der Letzte seiner Art", // "SCU42EA", // Let's Make a Steal + "Probefahrt", // "SCU41EA", // Test Drive + "Schlaftrunk", // "SCU45EA", // Don't Drink The Water + "Der jngste Tag", // "SCU44EA", // Situation Critical + "Waffenbrder", // "SCU46EA", // Brothers in Arms + "Deus Ex Machina", // "SCU47EA", // Deus Ex Machina + "Die Replikanten von Grunyev", // "SCU48EA", // Grunyev Revolution + +#endif + NULL +}; + +#endif + +#ifdef FRENCH +const char* XlatNames[] = { + "Gaz Sarin 1: Ravitaillement Fatal", + "Gaz Sarin 2: En Sous-sol", + "Gaz Sarin 3: Attaque Chirurgicale", + "GrŠce Occup‚e 1: Guerre Priv‚e", + "GrŠce Occup‚e 2: Evacuation", + "Conflit Sib‚rien 1: Traces FraŒches", + "Conflit Sib‚rien 2: Pris au PiŠge", + "Conflit Sib‚rien 3: Terres de Glace", + "Mise … l'Epreuve", + "Assi‚g‚s", + "La SouriciŠre", + "L'H‚ritage de Tesla", + "Tandem de Choc", + "Jusqu'au Sommet du Monde", + "Effets Secondaires", + "Intensification nucl‚aire", +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +"Le vieux port", // "SCG43EA", // Harbor Reclamation +"Juste … temps", // "SCG41EA", // In the nick of time +"La main dans le sac", // "SCG40EA", // Caught in the act +"Production interrompue", // "SCG42EA", // Production Disruption +"N‚gociations", // "SCG47EA", // Negotiations +"Tanks en folie!", // "SCG45EA", // Monster Tank Madness +"Le temps passe", // "SCG44EA", // Time Flies +"D‚mence absolue", // "SCG48EA", // Absolut MADness +"Le pion", // "SCG46EA", // Pawn + +"Terrains d'essais", // "SCU43EA", // Testing Grounds +"Th‚rapie de choc", // "SCU40EA", // Shock Therapy +"Au voleur!", // "SCU42EA", // Let's Make a Steal +"Essai de conduite", // "SCU41EA", // Test Drive +"Ne buvez pas la tasse", // "SCU45EA", // Don't Drink The Water +"Situation critique", // "SCU44EA", // Situation Critical +"FrŠres d'armes", // "SCU46EA", // Brothers in Arms +"Deus Ex Machina", // "SCU47EA", // Deus Ex Machina +"La R‚volution de Grunyev",// "SCU48EA", // Grunyev Revolution + +#endif + + NULL, +}; + +#endif + +#define OPTION_WIDTH 560 +#define OPTION_HEIGHT 332 + +struct EObjectClass +{ + HousesType House; + int Scenario; + char Name[128]; + char FullName[128]; +}; + + +/* +** Derived from list class to handle expansion scenario listings. The listings +** are recorded as EObjectClass objects. The data contained specifies the scenario +** number, side, and text description. +*/ +class EListClass : public ListClass +{ + public: + EListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ListClass(id, x, y, w, h, flags, up, down) {}; + + virtual int Add_Object(EObjectClass * obj) { + return(ListClass::Add_Item((char const *)obj)); + } + virtual EObjectClass * Get_Object(int index) const { + return((EObjectClass *)ListClass::Get_Item(index)); + } + virtual EObjectClass * Current_Object(void) { + return((EObjectClass *)ListClass::Current_Item()); + } + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + private: + virtual int Add_Item(char const * text) {return(ListClass::Add_Item(text));}; + virtual int Add_Item(int text) {return(ListClass::Add_Item(text));}; + virtual char const * Current_Item(void) const {return(ListClass::Current_Item());}; + virtual char const * Get_Item(int index) const {return(ListClass::Get_Item(index));}; +}; + + +/*********************************************************************************************** + * EListClass::Draw_Entry -- Draws entry for expansion scenario. * + * * + * This overrides the normal list class draw action so that the scenario name will be * + * displayed along with the house name. * + * * + * INPUT: index -- The index of the entry that should be drawn. * + * * + * x,y -- Coordinate of upper left region to draw the entry into. * + * * + * width -- Width of region (pixels) to draw the entry. * + * * + * selected -- Is this entry considered selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1995 JLB : Created. * + *=============================================================================================*/ +void EListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + char buffer[128]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int text = TXT_NONE; + if (Get_Object(index)->House == HOUSE_GOOD) { + text = TXT_ALLIES; + } else { + text = TXT_SOVIET; + } + sprintf(buffer, "%s: %s", Text_String(text), Get_Object(index)->Name); + + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, 1); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(buffer, x + 100, y, scheme, TBLACK, flags & ~(TPF_CENTER), width, Tabs); +} + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ) // If not bCounterstrike, then this was called for Aftermath. +#else +bool Expansion_Dialog(void) +#endif +{ + GadgetClass * buttons = NULL; + int OPTION_X = (SeenBuff.Get_Width() - OPTION_WIDTH) / 2; + int OPTION_Y = (SeenBuff.Get_Height() - OPTION_HEIGHT) / 2; + +#ifndef WIN32 + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y+OPTION_HEIGHT-15); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y+OPTION_HEIGHT-15); +#else + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+40, OPTION_Y + OPTION_HEIGHT - 50 ); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-85, OPTION_Y + OPTION_HEIGHT - 50 ); +#endif + +#ifndef WIN32 + EListClass list(202, OPTION_X + 20, OPTION_Y+20, OPTION_WIDTH-40, OPTION_HEIGHT-40, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#else + EListClass list(202, OPTION_X+35, OPTION_Y + 30, OPTION_WIDTH-70, OPTION_HEIGHT - 85, TPF_BUTTON, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + +#endif + buttons = &ok; + cancel.Add(*buttons); + list.Add(*buttons); + + /* + ** Add in all the expansion scenarios. + */ + CCFileClass file; + char buffer[128], buffer2[128]; + char * sbuffer = (char*)_ShapeBuffer; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Though disgusted. + for (int index = 20; index < (36+18); index++) { +#else + for (int index = 20; index < 36; index++) { +#endif + +#ifndef CS_DEBUG + strcpy(buffer,ExpandNames[index - 20]); + strcpy(buffer2, ExpandNames[index - 20]); +#else + strcpy(buffer, TestNames2[index]); + strcpy(buffer2, TestNames2[index]); +#endif + if(buffer[0] == NULL) + break; + + strcat(buffer, ".INI"); + strcat(buffer2, ".INI"); + Scen.Set_Scenario_Name(buffer); + Scen.Scenario = index; + file.Set_Name(buffer); +#ifdef FIXIT_VERSION_3 + bool bOk; + if( index < 36 ) + bOk = bCounterstrike; + else + bOk = !bCounterstrike; + + if( bOk && file.Is_Available() ) + { +#else // FIXIT_VERSION_3 + if (file.Is_Available()) { +#endif // FIXIT_VERSION_3 + EObjectClass * obj = new EObjectClass; + switch(buffer[2]){ + + case 'G': + case 'g': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_GOOD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + case 'U': + case 'u': + file.Read(sbuffer, 2000); + sbuffer[2000] = '\r'; + sbuffer[2000+1] = '\n'; + sbuffer[2000+2] = '\0'; + WWGetPrivateProfileString("Basic", "Name", "x", buffer, sizeof(buffer), sbuffer); +#if defined(GERMAN) || defined(FRENCH) + strcpy(obj->Name, XlatNames[index - ARRAYOFFSET]); +#else + strcpy(obj->Name, buffer); +#endif +// strcpy(obj->Name, buffer); + strcpy(obj->FullName, buffer2); + obj->House = HOUSE_BAD; + obj->Scenario = index; + list.Add_Object(obj); + break; + + + default: + break; + } + } + } + + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + bool okval = true; + + + while (process) { + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } +#endif + + Call_Back(); + + { + display = false; + + Hide_Mouse(); + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); +#ifdef FIXIT_VERSION_3 + if (bCounterstrike) + { + //PG Draw_Caption( TXT_WOL_CS_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + } + else + { + //PG Draw_Caption( TXT_WOL_AM_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + } +#else + Draw_Caption(TXT_NEW_MISSIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); +#endif + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case 200|KN_BUTTON: + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + case KN_ESC: + case 201|KN_BUTTON: + process = false; + okval = false; + break; + + case (KN_RETURN): + Whom = list.Current_Object()->House; + Scen.Scenario = list.Current_Object()->Scenario; + strcpy(Scen.ScenarioName, list.Current_Object()->FullName); + process = false; + okval = true; + break; + + default: + break; + } + } + + /* + ** Free up the allocations for the text lines in the list box. + */ + for (int index = 0; index < list.Count(); index++) { + delete list.Get_Object(index); + } + + return(okval); + +} + diff --git a/code/redalert/externs.h b/code/redalert/externs.h new file mode 100644 index 0000000..2a26b6b --- /dev/null +++ b/code/redalert/externs.h @@ -0,0 +1,514 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/EXTERNS.H 2 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef EXTERNS_H +#define EXTERNS_H + +#include "cell.h" + +#ifdef SCENARIO_EDITOR +#include "mapedit.h" +#endif +#include "techno.h" +#include "type.h" +#include "building.h" +#include "unit.h" +#include "credits.h" +#include "goptions.h" +#include "options.h" +#include "infantry.H" +#include "DSOUND.H" + +extern char _staging_buffer[32000]; +extern "C" { +void _PRO(void); +} + +/* +** Convenient alias for MixFileClass object. This allows +** easier entry into the code and less clutter. +*/ +typedef MixFileClass MFCD; + +extern bool IsVQ640; +extern unsigned long GameVersion; +extern bool Debug_MotionCapture; +extern bool Debug_Rotate; +extern bool Debug_Quiet; +extern bool Debug_Cheat; +extern bool Debug_Remap; +extern bool Debug_Flag; +extern bool Debug_Lose; +extern bool Debug_Map; +extern bool Debug_Win; +extern bool Debug_Icon; +extern bool Debug_Passable; +extern bool Debug_Unshroud; +extern bool Debug_Threat; +extern bool Debug_Find_Path; +extern bool Debug_Check_Map; +extern bool Debug_Playtest; + +extern bool Debug_Heap_Dump; +extern bool Debug_Smart_Print; +extern bool Debug_Trap_Check_Heap; +extern bool Debug_Modem_Dump; +extern bool Debug_Print_Events; +extern bool Debug_Force_Crash; + +extern void const *LightningShapes; + +extern int NewINIFormat; + +extern bool renderConsole; + + +#ifdef FIXIT_ANTS +extern bool AntsEnabled; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool NewUnitsEnabled; +extern bool SecretUnitsEnabled; +extern int MTankDistance; +extern bool OverrideNewUnitsEnabled; // ST - 12/13/2019 12:19PM +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +extern int CarrierLaunchDelay; +#endif +#endif + +#ifdef FIXIT_NAME_OVERRIDE +extern char const * NameOverride[25]; +extern int NameIDOverride[25]; +#endif + +#ifdef WIN32 +extern bool GameInFocus; +extern unsigned char *InterpolatedPalettes[100]; +extern BOOL PalettesRead; +extern unsigned PaletteCounter; +extern int AllDone; +extern HANDLE hInstance; +extern bool InMovie; +extern WinTimerClass * WindowsTimer; +extern WWMouseClass * WWMouse; +extern GraphicBufferClass HiddenPage; +#define SeenPage SeenBuff +extern GraphicBufferClass VisiblePage; +extern GraphicViewPortClass SeenBuff; +extern GraphicBufferClass SysMemPage; +extern LPDIRECTSOUND SoundObject; +extern LPDIRECTSOUNDBUFFER PrimaryBufferPtr; +extern int ScreenWidth; +extern int ScreenHeight; +extern GraphicBufferClass ModeXBuff; + +#else + +extern VideoBufferClass SeenPage; +extern GraphicBufferClass SeenBuff; +extern GraphicBufferClass & VisiblePage; +#endif + + +/* +** Dynamic global variables (these change or are initialized at run time). +*/ +extern MissionControlClass MissionControl[MISSION_COUNT]; +extern char const * TutorialText[225]; +extern Buffer * TheaterBuffer; +extern GetCDClass CDList; +extern CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern CCINIClass AftermathINI; +#endif +//extern Benchmark * Benches; +extern int MapTriggerID; +extern int LogicTriggerID; +extern PKey FastKey; +extern PKey SlowKey; +extern RulesClass Rule; +extern KeyboardClass * Keyboard; +extern UserInputClass UserInput; +extern EventQueueClass EventQueue; +extern RandomStraw CryptRandom; +extern RandomClass NonCriticalRandomNumber; +extern CarryoverClass * Carryover; +extern ScenarioClass Scen; +extern RemapControlType ColorRemaps[PCOLOR_COUNT]; +extern RemapControlType MetalScheme; +extern RemapControlType GreyScheme; +extern VersionClass VerNum; +extern bool SlowPalette; +extern bool ScoresPresent; +extern bool AllowVoice; +extern NewConfigType NewConfig; +extern VoxType SpeakQueue; +extern bool PlayerWins; +extern bool PlayerLoses; +extern bool PlayerRestarts; +extern long Frame; +extern VoxType SpeechRecord[2]; +extern void * SpeechBuffer[2]; +extern int PreserveVQAScreen; +extern bool BreakoutAllowed; +extern bool Brokeout; + +extern GameOptionsClass Options; + +extern LogicClass Logic; +#ifdef SCENARIO_EDITOR +extern MapEditClass Map; +#else +extern MouseClass Map; +#endif +extern ScoreClass Score; +extern MonoClass MonoArray[DMONO_COUNT]; +extern MFCD * TheaterData; +extern MFCD * MoviesMix; +extern MFCD * GeneralMix; +extern MFCD * ScoreMix; +extern MFCD * MainMix; +extern MFCD * ConquerMix; +extern ThemeClass Theme; +extern SpecialClass Special; + +/* +** Game object allocation and tracking classes. +*/ +extern TFixedIHeapClass Aircraft; +extern TFixedIHeapClass Anims; +extern TFixedIHeapClass Buildings; +extern TFixedIHeapClass Bullets; +extern TFixedIHeapClass Factories; +extern TFixedIHeapClass Houses; +extern TFixedIHeapClass Infantry; +extern TFixedIHeapClass Overlays; +extern TFixedIHeapClass Smudges; +extern TFixedIHeapClass Teams; +extern TFixedIHeapClass TeamTypes; +extern TFixedIHeapClass Templates; +extern TFixedIHeapClass Terrains; +extern TFixedIHeapClass Triggers; +extern TFixedIHeapClass Units; +extern TFixedIHeapClass Vessels; +extern TFixedIHeapClass TriggerTypes; + +extern TFixedIHeapClass HouseTypes; +extern TFixedIHeapClass BuildingTypes; +extern TFixedIHeapClass AircraftTypes; +extern TFixedIHeapClass InfantryTypes; +extern TFixedIHeapClass BulletTypes; +extern TFixedIHeapClass AnimTypes; +extern TFixedIHeapClass UnitTypes; +extern TFixedIHeapClass VesselTypes; +extern TFixedIHeapClass TemplateTypes; +extern TFixedIHeapClass TerrainTypes; +extern TFixedIHeapClass OverlayTypes; +extern TFixedIHeapClass SmudgeTypes; + +extern FixedIHeapClass * HeapPointers[RTTI_COUNT]; + +extern TFixedIHeapClass Weapons; +extern TFixedIHeapClass Warheads; + +extern QueueClass OutList; +extern QueueClass DoList; + +#ifdef MIRROR_QUEUE +extern QueueClass MirrorList; +#endif + +typedef DynamicVectorArrayClass SelectedObjectsType; +extern SelectedObjectsType CurrentObject; +extern DynamicVectorClass LogicTriggers; +extern DynamicVectorClass MapTriggers; +extern DynamicVectorClass HouseTriggers[HOUSE_COUNT]; + +extern BaseClass Base; + +/* These variables are used to keep track of the slowest speed of a team */ +extern MPHType TeamMaxSpeed[MAX_TEAMS]; +extern SpeedType TeamSpeed[MAX_TEAMS]; +extern bool FormMove; +extern SpeedType FormSpeed; +extern MPHType FormMaxSpeed; + +extern bool IsTanyaDead; +extern bool SaveTanya; + +extern bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool PendingTimeQuake; +extern TARGET TimeQuakeCenter; +extern fixed QuakeUnitDamage; +extern fixed QuakeBuildingDamage; +extern int QuakeInfantryDamage; +extern int QuakeDelay; +extern fixed ChronoTankDuration; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +extern fixed EngineerDamage; // Amount of damage an engineer does +extern fixed EngineerCaptureLevel; // Building damage level before engineer can capture +#endif +#endif + +/* +** Loaded data file pointers. +*/ +extern void const * Metal12FontPtr; +extern void const * MapFontPtr; +extern void const * VCRFontPtr; +extern void const * TypeFontPtr; +extern void const * Font3Ptr; +extern void const * Font6Ptr; +extern void const * EditorFont; +extern void const * Font8Ptr; +extern void const * FontLEDPtr; +extern void const * ScoreFontPtr; +extern void const * GradFont6Ptr; +extern char const * SystemStrings; +extern char const * DebugStrings; + +/* +** Miscellaneous globals. +*/ +extern ChronalVortexClass ChronalVortex; +extern TTimerClass TickCount; +extern bool PassedProximity; // used in display.cpp +extern HousesType Whom; +//extern _VQAConfig AnimControl; +extern long SpareTicks; +extern long PathCount; +extern long CellCount; +extern long TargetScan; +extern long SidebarRedraws; +extern DMonoType MonoPage; +extern bool GameActive; +extern bool SpecialFlag; +extern int ScenarioInit; +extern HouseClass * PlayerPtr; +extern PaletteClass CCPalette; +extern PaletteClass BlackPalette; +extern PaletteClass WhitePalette; +extern PaletteClass GamePalette; +//extern PaletteClass InGamePalette; +#define InGamePalette GamePalette +extern PaletteClass OriginalPalette; +extern PaletteClass ScorePalette; +extern int BuildLevel; +extern unsigned long ScenarioCRC; + +#ifdef FIXIT_VERSION_3 +extern bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +extern unsigned long PlayingAgainstVersion; // Negotiated version number +extern bool Version107InMix; // Is there a v1.07 in the game +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +extern bool bAutoSonarPulse; +#endif + +#ifdef SCENARIO_EDITOR +extern CELL CurrentCell; +#endif + +extern SessionClass Session; +//extern NullModemClass NullModem; +extern IPXManagerClass Ipx; + +#if(TEN) +extern TenConnManClass *Ten; +#endif + +#if(MPATH) +extern MPlayerManClass *MPath; +#endif + +#if(TIMING_FIX) +extern int NewMaxAheadFrame1; +extern int NewMaxAheadFrame2; +#endif + +extern int Seed; +extern int CustomSeed; +extern GroundType Ground[LAND_COUNT]; + +/* +** Constant externs (data is not modified during game play). +*/ +extern std::map const sdl_keyMapping; + +extern char const * Missions[MISSION_COUNT]; +extern char const Keys[]; +extern char const * const VQName[VQ_COUNT]; +extern int CrateData[CRATE_COUNT]; +extern char const * const CrateNames[CRATE_COUNT]; +extern int CrateShares[CRATE_COUNT]; +extern AnimType CrateAnims[CRATE_COUNT]; +extern char const * const SpecialWeaponName[SPC_COUNT]; +extern int const SpecialWeaponHelp[SPC_COUNT]; +extern char const * const SpecialWeaponFile[SPC_COUNT]; +extern char const * const ArmorName[ARMOR_COUNT]; +extern char const * const QuarryName[QUARRY_COUNT]; +extern char const * const FormationName[FORMATION_COUNT]; +extern unsigned long const PlayCodes[]; +extern unsigned long const CheatCodes[]; +//extern char const * const ProjectileNames[]; +extern unsigned long const EditorCodes[]; +extern char const * const SourceName[SOURCE_COUNT]; +extern int const GroundColor[LAND_COUNT]; +extern int const SnowColor[LAND_COUNT]; +extern TheaterDataType const Theaters[THEATER_COUNT]; +extern unsigned char const Facing32[256]; +extern unsigned char const Facing16[256]; +extern signed char const Rotation16[256]; +extern unsigned char const Facing8[256]; +extern unsigned char const Pixel2Lepton[24]; +extern COORDINATE const StoppingCoordAbs[5]; +extern CELL const AdjacentCell[FACING_COUNT]; +extern COORDINATE const AdjacentCoord[FACING_COUNT]; +extern unsigned char const RemapCiv2[]; +extern unsigned char const RemapCiv4[]; +extern unsigned char const RemapCiv5[]; +extern unsigned char const RemapCiv6[]; +extern unsigned char const RemapCiv7[]; +extern unsigned char const RemapCiv8[]; +extern unsigned char const RemapCiv9[]; +extern unsigned char const RemapCiv10[]; +extern unsigned char const RemapEmber[]; + +extern int SoundOn; + +#ifdef WIN32 +extern GraphicViewPortClass HidPage; +#else +extern GraphicBufferClass HidPage; +#endif +extern int MenuList[][8]; +extern CDTimerClass FrameTimer; +extern CDTimerClass CountDownTimer; + +extern SpecialDialogType SpecialDialog; + +extern int RequiredCD; +extern int CurrentCD; +extern int MouseInstalled; + + +extern int LogLevel; +extern unsigned long LogLevelTime[ MAX_LOG_LEVEL ]; +extern unsigned long LogLastTime; + +extern class DynamicVectorClass test2; +extern class DynamicVectorClass test3; + +extern bool LogDump_Print; + +extern "C"{ + extern bool IsTheaterShape; +} + +extern void Reset_Theater_Shapes(void); +extern TheaterType LastTheater; +void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); +void Do_Vortex (int x, int y, int frame); + + +/************************************************************ +** Win32 specific externs +*/ +#ifdef WIN32 +extern int ReadyToQuit; //Are we about to exit cleanly +extern bool InDebugger; //Are we being run from a debugger +void Memory_Error_Handler(void); //Memory error handler function +void WWDebugString (char *string); +#else +extern bool IsTheaterShape; +#endif //WIN32 + + +/************************************************************* +** Internet specific externs +*/ +#ifdef WIN32 + +extern char PlanetWestwoodHandle[]; //Planet WW user name +extern char PlanetWestwoodPassword[]; //Planet WW password +extern char PlanetWestwoodIPAddress[]; //IP of server or other player +extern long PlanetWestwoodPortNumber; //Port number to send to +extern bool PlanetWestwoodIsHost; //Flag true if player has control of game options +extern unsigned long PlanetWestwoodGameID; //Game ID +extern HWND WChatHWND; //Handle to Wchat window. +extern bool GameStatisticsPacketSent; +extern bool ConnectionLost; +extern void *PacketLater; +extern bool SpawnedFromWChat; +extern int ShowCommand; +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +extern int UnitBuildPenalty; + + +#endif //WIN32 + +/* +** From SENDFILE.CPP - externs for scenario file transfers +*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, unsigned int crc, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); +bool Get_Scenario_File_From_Host(char *return_name, int gametype); +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official); + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +extern MCIMovie* MciMovie; +#endif + +#include "mpgset.h" +extern MPGSettings* MpgSettings; +#endif + +extern "C" bool MMXAvailable; + +extern bool ShareAllyVisibility; + +#endif diff --git a/code/redalert/face.cpp b/code/redalert/face.cpp new file mode 100644 index 0000000..9603364 --- /dev/null +++ b/code/redalert/face.cpp @@ -0,0 +1,224 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * Desired_Facing8 -- Determines facing from one coordinate to another. * + * * + * This routine will find the facing (compass direction) from one location to another. * + * Typical use of this is in find path and other 'monster' movement logic. * + * * + * INPUT: x1,y1 -- X and Y coordinates for the source location. The coordinate 0,0 is * + * presumed to be the northwest corner of the map. * + * * + * x2,y2 -- X and Y coordinates for the destination (target) location. * + * * + * OUTPUT: Returns with the facing from the first coordinate to the second coordinate. The * + * value returned will range from 0 being North, increasing clockwise until reaching * + * 255 which is just shy of North in a Westerly direction. * + * * + * WARNINGS: This routine is only accurate to the 8 primary compass directions. It is much * + * faster than the Desired_Facing256() function so it should be used where speed * + * is more important than accuracy. * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +/*********************************************************************************************** + * Desired_Facing256 -- Determines facing from one coordinate to another. * + * * + * This routine will figure the facing from the source coordinate toward the destination * + * coordinate. Typically, this routine is used for movement and other 'monster' logic. It * + * is more accurate than the corresponding Desired_Facing8() function, but is slower. * + * * + * INPUT: srcx, srcy -- The source coordinate to determine the facing from. * + * * + * dstx, dsty -- The destination (or target) coordinate to determine the facing * + * toward. * + * * + * OUTPUT: Returns with the facing from the source coordinate toward the destination * + * coordinate with 0 being North increasing in a clockwise direction. 64 is East, * + * 128 is South, etc. * + * * + * WARNINGS: The coordinate 0,0 is presumed to be in the Northwest corner of the map. * + * Although this routine is fast, it is not as fast as Desired_Facing8(). * + * * + * HISTORY: * + * 03/08/1996 JLB : Created. * + *=============================================================================================*/ +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + //lint -e414 Division by zero cannot occur here. + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} diff --git a/code/redalert/face.h b/code/redalert/face.h new file mode 100644 index 0000000..dee5f9e --- /dev/null +++ b/code/redalert/face.h @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/08/96 * + * * + * Last Update : March 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACE_H +#define FACE_H + +// Enumerations of the facing values returned from Desired_Facing(). +typedef enum DirType : unsigned char { + DIR_MIN=0, + DIR_N=0, + DIR_NE=1<<5, + DIR_E=2<<5, + DIR_SE=3<<5, + DIR_S=4<<5, + DIR_SW=5<<5, + DIR_W=6<<5, + DIR_NW=7<<5, + DIR_MAX=255 +} DirType; + +// Operators that allow simple math with DirType. +inline DirType operator + (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator + (DirType f1, int f2) +{ + return (DirType)(((int)f1 + (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, DirType f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} +inline DirType operator - (DirType f1, int f2) +{ + return (DirType)(((int)f1 - (int)f2) & 0x00FF); +} + + +// Function prototypes. +DirType Desired_Facing8(int x1, int y1, int x2, int y2); +DirType Desired_Facing256(int srcx, int srcy, int dstx, int dsty); + +#endif + + + diff --git a/code/redalert/facing.cpp b/code/redalert/facing.cpp new file mode 100644 index 0000000..751be72 --- /dev/null +++ b/code/redalert/facing.cpp @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACING.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FacingClass::FacingClass -- Default constructor for the facing class. * + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * FacingClass::Set_Current -- Sets the current rotation value. * + * FacingClass::Set_Desired -- Sets the desired facing value. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "facing.h" + + +/*********************************************************************************************** + * FacingClass::FacingClass -- Default constructor for the facing class. * + * * + * This default constructor merely sets the desired and current facing values to be the * + * same (North). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +FacingClass::FacingClass(void) +{ + CurrentFacing = DIR_N; + DesiredFacing = DIR_N; +} + + +/*********************************************************************************************** + * FacingClass::Set_Desired -- Sets the desired facing value. * + * * + * This routine is used to set the desired facing value without altering the current * + * facing setting. Typical use of this routine is when a vehicle needs to face a * + * direction, but currently isn't facing the correct direction. After this routine is * + * called, it is presumed that subsequent calls to Rotation_Adjust() will result in the * + * eventual alignment of the current facing. * + * * + * INPUT: facing -- The new facing to assign to the desired value. * + * * + * OUTPUT: bool; Did the desired facing value actually change by this routine call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Desired(DirType facing) +{ + if (DesiredFacing != facing) { + DesiredFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Set_Current -- Sets the current rotation value. * + * * + * This routine will set the current rotation value. It is used to override the facing * + * value without adjusting the desired setting. * + * * + * INPUT: facing -- The new facing to assign to the current facing value. * + * * + * OUTPUT: bool; Was the current setting changed by this routine. Failure means that the * + * current setting was already at the value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Set_Current(DirType facing) +{ + if (CurrentFacing != facing) { + CurrentFacing = facing; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FacingClass::Rotation_Adjust -- Perform a rotation adjustment to current facing. * + * * + * This routine is used when the current and desired facings differ but the current * + * facing should be adjusted toward the desired facing. The amount of rotation to adjust * + * is provided as a rotation rate parameter. Typical use of this routine is for turrets * + * and other vehicle related rotating. * + * * + * INPUT: rate -- The rotation rate to use when adjusting the current facing toward the * + * desired facing. A value of 127 means instantaneous rotation. * + * * + * OUTPUT: bool; Did the rotation result in the current facing transitioning from one * + * 1/32 zone to another? If true, then the owning object most likely will * + * need to be redrawn to reflect the change. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/21/1995 JLB : Created. * + *=============================================================================================*/ +int FacingClass::Rotation_Adjust(int rate) +{ + /* + ** Only perform the rotation adjustment if the desired facing is not the + ** same as the current facing. + */ + if (Is_Rotating()) { + rate = min(rate, 127); + + DirType oldfacing = CurrentFacing; + int diff = Difference(); + + /* + ** If the allowed facing change is greater than the difference between + ** the current facing and the desired facing, then just snap the + ** facing to the new value. + */ + if (ABS(diff) < rate) { + CurrentFacing = DesiredFacing; + } else { + + /* + ** Adjust the current facing clockwise or counterclockwise depending + ** on the shortest distance to the desired facing from the current + ** facing. + */ + if (diff < 0) { + CurrentFacing = (DirType)(CurrentFacing - (DirType)rate); + } else { + CurrentFacing = (DirType)(CurrentFacing + (DirType)rate); + } + } + + /* + ** If this facing adjustment caused the current facing to rotate into a + ** new 1/32 rotation zone (likely to cause a redraw), then return + ** this fact with a true value. + */ + return(Dir_To_32(CurrentFacing) != Dir_To_32(oldfacing)); + } + return(false); +} diff --git a/code/redalert/facing.h b/code/redalert/facing.h new file mode 100644 index 0000000..d453268 --- /dev/null +++ b/code/redalert/facing.h @@ -0,0 +1,77 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACING.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACING.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/21/95 * + * * + * Last Update : March 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACING_H +#define FACING_H + +/* +** This is a general facing handler class. It is used in those cases where facing needs to be +** kept track of, but there could also be an associated desired facing. The current facing +** is supposed to transition to the desired state over time. Using this class facilitates this +** processing as well as isolating the rest of the code from the internals. +*/ +class FacingClass +{ + public: + FacingClass(void); + FacingClass(DirType dir) : CurrentFacing(dir), DesiredFacing(dir) {}; + FacingClass(NoInitClass const & ) {}; + + operator DirType (void) const {return(CurrentFacing);}; + + DirType Current(void) const {return(CurrentFacing);}; + DirType Desired(void) const {return(DesiredFacing);}; + + int Set_Desired(DirType facing); + int Set_Current(DirType facing); + + void Set(DirType facing) { + Set_Current(facing); + Set_Desired(facing); + }; + + DirType Get(void) const { return CurrentFacing; } + + int Is_Rotating(void) const {return (DesiredFacing != CurrentFacing);}; + int Difference(void) const {return (int)(signed char)((int)DesiredFacing - (int)CurrentFacing);} + int Difference(DirType facing) const {return (int)(signed char)((int)facing - (int)CurrentFacing);} + int Rotation_Adjust(int rate); + + private: + DirType CurrentFacing; + DirType DesiredFacing; +}; + + +#endif diff --git a/code/redalert/factory.cpp b/code/redalert/factory.cpp new file mode 100644 index 0000000..8ba74e1 --- /dev/null +++ b/code/redalert/factory.cpp @@ -0,0 +1,697 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACTORY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : May 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FactoryClass::AI -- Process factory production logic. * + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * FactoryClass::Get_Special_Item -- gets factory's spc prod item * + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * FactoryClass::Set -- Assigns a factory to produce an object. * + * FactoryClass::Set -- Force factory to "produce" special object. * + * FactoryClass::Start -- Resumes production after suspension or creation. * + * FactoryClass::Suspend -- Temporarily stop production. * + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FactoryClass::FactoryClass -- Default constructor for factory objects. * + * * + * This brings the factory into a null state. It is called when a factory object is * + * created. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::FactoryClass(void) : + RTTI(RTTI_FACTORY), + ID(Factories.ID(this)), + IsSuspended(false), + IsDifferent(false), + IsBlocked(false), + Balance(0), + OriginalBalance(0), + Object(0), + SpecialItem(SPC_NONE), + House(0) +{ + Set_Rate(0); + Set_Stage(0); +} + + +/*********************************************************************************************** + * FactoryClass::~FactoryClass -- Default destructor for factory objects. * + * * + * This cleans up a factory object in preparation for deletion. If there is currently * + * an object in production, it is abandoned and money is refunded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +FactoryClass::~FactoryClass(void) +{ + if (GameActive) { + Abandon(); + } +} + + +/*********************************************************************************************** + * FactoryClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the factory list and objects. This routine is typically * + * used in preparation for a new scenario load. All factory are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Init(void) +{ + Factories.Free_All(); +} + + +/*********************************************************************************************** + * FactoryClass::operator new -- Allocates a factory object from the free factory pool. * + * * + * This routine allocates a factory from the free factory pool. If there is no more room * + * to allocate a factory, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to the newly allocated factory object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void * FactoryClass::operator new(size_t) +{ + void * ptr = Factories.Allocate(); + if (ptr) { + ((FactoryClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * FactoryClass::operator delete -- Returns a factory to the free factory pool. * + * * + * This returns the factory object back to the factory allocation pool. The factory is then * + * available to be allocated. * + * * + * INPUT: ptr -- Pointer to the factory object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::operator delete(void * ptr) +{ + if (ptr) { + ((FactoryClass *)ptr)->IsActive = false; + } + Factories.Free((FactoryClass *)ptr); +} + + +/*********************************************************************************************** + * FactoryClass::AI -- Process factory production logic. * + * * + * This routine should be called once per game tick. It handles the production process. * + * As production proceeds, money is deducted from the owner object's house. When production * + * completes, the factory stop processing. A call to Abandon, Delete, or Completed is * + * required after that point. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 01/04/1995 JLB : Uses exact installment payment method. * + *=============================================================================================*/ +void FactoryClass::AI(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended && (Object != NULL || SpecialItem)) { + for (int index = 0; index < 1; index++) { + if (!Has_Completed() && Graphic_Logic() ) { + IsDifferent = true; + + int cost = Cost_Per_Tick(); + + cost = min(cost, Balance); + + /* + ** Enough time has expired so that another production step can occur. + ** If there is insufficient funds, then go back one production step and + ** continue the countdown. The idea being that by the time the next + ** production step occurs, there may be sufficient funds available. + */ + if (cost > House->Available_Money()) { + Set_Stage(Fetch_Stage()-1); + } else { + House->Spend_Money(cost); + Balance -= cost; + } + + /* + ** If the production has completed, then suspend further production. + */ + if (Fetch_Stage() == STEP_COUNT) { + IsSuspended = true; + Set_Rate(0); + House->Spend_Money(Balance); + Balance = 0; + } + } + } + } +} + + + +/*********************************************************************************************** + * FactoryClass::Force_Complete -- Force the factory to finish what it's building * + * * + * For debugging/testing support * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 8/23/2019 3:54PM ST : Created. * + *=============================================================================================*/ +void FactoryClass::Force_Complete(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended && (Object != NULL || SpecialItem)) { + Set_Stage(STEP_COUNT); + IsSuspended = true; + Set_Rate(0); + Balance = 0; + IsDifferent = true; + } +} + + +/*********************************************************************************************** + * FactoryClass::Has_Changed -- Checks to see if a production step has occurred? * + * * + * Use this routine to determine if production has advanced at least one step. By using * + * this function, intelligent rendering may be performed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the production process advanced one step since the last time this * + * function was called? * + * * + * WARNINGS: This function clears the changed status flag as a side effect. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Changed(void) +{ + assert(Factories.ID(this) == ID); + + bool changed = IsDifferent; + IsDifferent = false; + return(changed); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Assigns a factory to produce an object. * + * * + * This routine initializes a factory to produce the object specified. The desired object * + * type is created and placed in suspended animation (limbo) until such time as production * + * completes. Production is not actually started by this routine. An explicit call to * + * Start() is required to begin production. * + * * + * INPUT: object -- Reference to the object type class that is to be produced. * + * * + * house -- Reference to the owner of the object to be produced. * + * * + * OUTPUT: bool; Was production successfully prepared for this factory object. Failure means * + * that the object could not be created. This is catastrophic and in such * + * cases, the factory object should be deleted. * + * * + * WARNINGS: Be sure to examine the return value from this function. Failure to initialize * + * the factory means that the factory is useless and should be deleted. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Set(TechnoTypeClass const & object, HouseClass & house) +{ + assert(Factories.ID(this) == ID); + + /* + ** If there is any production currently in progress, abandon it. + */ + Abandon(); + + /* + ** Set up the factory for the new production process. + */ + IsDifferent = true; + IsSuspended = true; + Set_Rate(0); + Set_Stage(0); + + /* + ** Create an object of the type requested. + */ + Object = (TechnoClass *)object.Create_One_Of(&house); + + /* + ** Buildings that are constructed, will default to rebuilding on so that + ** repair can commence and base rebuilding can occur. + */ + if (!house.IsHuman && Object != NULL && Object->What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)Object)->IsToRebuild = true; + } + + if (Object) { + House = Object->House; + Balance = object.Cost_Of() * house.CostBias; + Object->PurchasePrice = Balance; + } + + /* + ** If all was set up successfully, then return true. + */ + return(Object != NULL); +} + + +/*********************************************************************************************** + * FactoryClass::Set -- Fills a factory with an already completed object. * + * * + * This routine is called when a produced object is in placement mode but then placement * + * is suspended. The object must then return to the factory as if it were newly completed * + * and awaiting removal. * + * * + * INPUT: object -- The object to return to the factory. * + * * + * OUTPUT: none * + * * + * WARNINGS: This will abandon any current object being produced at the factory in order * + * to set the new object into it. * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void FactoryClass::Set(TechnoClass & object) +{ + assert(Factories.ID(this) == ID); + + Abandon(); + Object = &object; + House = Object->House; + Balance = 0; + Set_Rate(0); + Set_Stage(STEP_COUNT); + IsDifferent = true; + IsSuspended = true; +} + + +/*********************************************************************************************** + * FactoryClass::Suspend -- Temporarily stop production. * + * * + * This routine will suspend production until a subsequent call to Start() or Abandon(). * + * Typical use of this function is when the player puts production on hold or when there * + * is insufficient funds. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production actually stopped? A false return value indicates that the * + * factory was empty or production was already stopped (or never started). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Suspend(void) +{ + assert(Factories.ID(this) == ID); + + if (!IsSuspended) { + IsSuspended = true; + Set_Rate(0); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Start -- Resumes production after suspension or creation. * + * * + * This function will start the production process. It works for newly created factory * + * objects, as well as if production had been suspended previously. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was production started? A false return value means that the factory is * + * empty or there is insufficient credits to begin production. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Start(void) +{ + assert(Factories.ID(this) == ID); + + if ((Object || SpecialItem) && IsSuspended && !Has_Completed()) { + if (House->IsHuman || House->Available_Money() >= Cost_Per_Tick()) { + int time; + + if (Object) { + time = Object->Time_To_Build(); +// } else { +// time = TICKS_PER_MINUTE * 5; + } + + /* + ** Adjust time according to IQ setting of computer controlled house. The + ** build time will range from double normal time at the slowest to + ** just normal time at the fastest. + */ + if (!House->IsHuman && Rule.Diff[House->Difficulty].IsBuildSlowdown) { + time = time * Inverse(fixed(House->IQ+Rule.MaxIQ, Rule.MaxIQ*2)); + } + + int rate = time / Bound(House->Power_Fraction(), fixed(1, 16), fixed(1)); +// int frac = House->Power_Fraction(); +// frac = Bound(frac, 0x0010, 0x0100); +// int rate = (time*256) / frac; + + rate /= STEP_COUNT; + rate = Bound(rate, 1, 255); + + Set_Rate(rate); + IsSuspended = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Abandon -- Abandons current construction with money refunded. * + * * + * This routine is used when construction is to be abandoned and current money spend is * + * to be refunded. This function effectively clears out this factory of all record of the * + * producing object so that it may either be deleted or started anew with the Set() * + * function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was an object actually abandoned? A false return value indicates that the * + * factory was not producing any object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Abandon(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + + if (Object) { + /* + ** Refund all money expended so far, back to the owner of the object under construction. + */ + int money = Object->Class_Of().Cost_Of() * Object->House->CostBias; + House->Refund_Money(money - Balance); + Balance = 0; + + /* + ** Delete the object under construction. + */ + ScenarioInit++; + delete Object; + Object = NULL; + ScenarioInit--; + } + if (SpecialItem) { + SpecialItem = SPC_NONE; + } + + /* + ** Set the factory back to the idle and empty state. + */ + Set_Rate(0); + Set_Stage(0); + IsSuspended = true; + IsDifferent = true; + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Completion -- Fetches the completion step for this factory. * + * * + * Use this routine to determine what animation (or completion step) the factory is * + * currently on. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a completion step number between 0 (uncompleted), to STEP_COUNT (completed)* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Completion(void) +{ + assert(Factories.ID(this) == ID); + + return(Fetch_Stage()); +} + + +/*********************************************************************************************** + * FactoryClass::Has_Completed -- Checks to see if object has completed production. * + * * + * Use this routine to examine the factory object in order to determine if the associated * + * object has completed production and is awaiting placement. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the associated object to the factory completed and ready for placement? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Has_Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + return(true); + } + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FactoryClass::Get_Object -- Fetches pointer to object being constructed. * + * * + * This routine gets the pointer to the currently constructing object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object undergoing construction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * FactoryClass::Get_Object(void) const +{ + assert(Factories.ID(this) == ID); + + return(Object); +} + + +/*************************************************************************** + * FactoryClass::Get_Special_Item -- gets factory spc prod item * + * * + * INPUT: none * + * * + * OUTPUT: int the item the factory is currently working on * + * * + * HISTORY: * + * 05/05/1995 PWG : Created. * + *=========================================================================*/ +int FactoryClass::Get_Special_Item(void) const +{ + assert(Factories.ID(this) == ID); + + return(SpecialItem); +} + + +/*********************************************************************************************** + * FactoryClass::Cost_Per_Tick -- Breaks entire production cost into manageable chunks. * + * * + * Use this routine to determine the cost per game "tick" to produce the object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of credits necessary to advance production one game tick. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int FactoryClass::Cost_Per_Tick(void) +{ + assert(Factories.ID(this) == ID); + + if (Object) { + int steps = STEP_COUNT - Fetch_Stage(); + if (steps) { + return(Balance / steps); + } + return(Balance); + } + return(0); +} + + +/*********************************************************************************************** + * FactoryClass::Completed -- Clears factory object after a completed production process. * + * * + * This routine is called after production completes, and the object produced has been * + * placed into the game. It resets the factory for deletion or starting of new production. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Did any resetting occur? Failure is the result of the factory not having * + * any completed object. An immediate second call to this routine will also * + * yield false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool FactoryClass::Completed(void) +{ + assert(Factories.ID(this) == ID); + + if (Object && Fetch_Stage() == STEP_COUNT) { + Object = NULL; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + + if (SpecialItem && Fetch_Stage() == STEP_COUNT) { + SpecialItem = SPC_NONE; + IsSuspended = true; + IsDifferent = true; + Set_Stage(0); + Set_Rate(0); + return(true); + } + return(false); +} diff --git a/code/redalert/factory.h b/code/redalert/factory.h new file mode 100644 index 0000000..c251700 --- /dev/null +++ b/code/redalert/factory.h @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FACTORY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FACTORY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/26/94 * + * * + * Last Update : December 26, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FACTORY_H +#define FACTORY_H + +#include "stage.h" + +class FactoryClass : private StageClass +{ + public: + RTTIType RTTI; + int ID; + + + FactoryClass(void); + FactoryClass(NoInitClass const & x) : StageClass(x) {}; + ~FactoryClass(void); + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + static void Init(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Abandon(void); + bool Completed(void); + bool Has_Changed(void); + bool Has_Completed(void); + bool Is_Building(void) const {return(Fetch_Rate() != 0);}; + bool Set(TechnoTypeClass const & object, HouseClass & house); + bool Set(int const & type, HouseClass & house); + bool Start(void); + bool Suspend(void); + int Completion(void); + TechnoClass * Get_Object(void) const; + int Get_Special_Item(void) const; + void AI(void); + void Set(TechnoClass & object); + HouseClass * Get_House(void) {return(House);}; + char const * Name(void) {return("Factory");} + bool Is_Blocked(void) {return IsBlocked;} + void Set_Is_Blocked(bool set) {IsBlocked = set;} + + /* + ** Added for debugging / testing. ST - 8/23/2019 3:52PM + */ + void Force_Complete(void); + + /* + ** This flag is used to maintain the pool of factory class objects. If the object has + ** been allocated, then this flag is true. Otherwise, the object is free to be + ** allocated. + */ + unsigned IsActive:1; + + enum StepCountEnum { + STEP_COUNT=54 // Number of steps to break production down into. + }; + protected: + + int Cost_Per_Tick(void); + + private: + + /* + ** If production is temporarily suspended, then this flag will be true. A factory + ** is suspended when it is first created, when production has completed, and when + ** explicitly instructed to Suspend() production. Suspended production is not + ** abandoned. It may be resumed with a call to Start(). + */ + unsigned IsSuspended:1; + + /* + ** If the AI process detected that the production process has advanced far enough + ** that a change in the building animation would occur, this flag will be true. + ** Examination of this flag (through the Has_Changed function) allows intelligent + ** updating of any production graphic. + */ + unsigned IsDifferent:1; + + /* + ** The exit from the factory is blocked by something, which means a unit is prevented from exiting after construction + ** has completed. ST - 2/25/2020 11:29AM + */ + unsigned IsBlocked:1; + + /* + ** This records the balance due on the current production item. This value will + ** be reduced as production proceeds. It will reach zero the moment production has + ** finished. Using this method ensures that the total production cost will be EXACT + ** regardless of the number of installment payments that are made. + */ + int Balance; + int OriginalBalance; + + /* + ** This is the object that is being produced. It is held in a state of limbo while + ** undergoing production. Since the object is created at the time production is + ** started, it is always available when production completes. + */ + TechnoClass * Object; + + /* + ** If the factory is not producing an object and is instead producing + ** a special item, then special item will be set. + */ + int SpecialItem; + + /* + ** The factory has to be doing production for one house or another. + ** The house pointer will point to whichever house it is being done + ** for. + */ + HouseClass * House; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/fakesock.h b/code/redalert/fakesock.h new file mode 100644 index 0000000..f36a7e3 --- /dev/null +++ b/code/redalert/fakesock.h @@ -0,0 +1,50 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : FAKESOCK.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 6th, 1996 * + * * + * Last Update : August 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Stub replacement for the Winsock interface in DOS * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WIN32 + +class TcpipManagerClass { + + public: + inline BOOL Get_Connected(void) {return (FALSE);} + +}; + +extern TcpipManagerClass Winsock; +#endif diff --git a/code/redalert/field.cpp b/code/redalert/field.cpp new file mode 100644 index 0000000..8421ec8 --- /dev/null +++ b/code/redalert/field.cpp @@ -0,0 +1,211 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * Actual member function for the field class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include "field.h" + +FieldClass::FieldClass(char *id, char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned char data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_CHAR; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned short data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_SHORT; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, unsigned long data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_UNSIGNED_LONG; + Size = sizeof(data); + Data = new char[Size]; + memcpy(Data, &data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, char *data) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_STRING; + Size = (unsigned short)(strlen(data)+1); + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + +FieldClass::FieldClass(char *id, void *data, int length) +{ + strncpy(ID, id, sizeof(ID)); + DataType = TYPE_CHUNK; + Size = (unsigned short)length; + Data = new char[Size]; + memcpy(Data, data, Size); + Next = NULL; +} + + +/************************************************************************** + * PACKETCLASS::HOST_TO_NET_FIELD -- Converts host field to net format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Host_To_Net(void) +{ + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = htons(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = htonl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } + // + // Finally convert over the data type and the size of the packet. + // + DataType = htons(DataType); + Size = htons(Size); +} +/************************************************************************** + * PACKETCLASS::NET_TO_HOST_FIELD -- Converts net field to host format * + * * + * INPUT: FIELD * to the data field we need to convert * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +void FieldClass::Net_To_Host(void) +{ + // + // Convert the variables to host order. This needs to be converted so + // the switch statement does compares on the data that follows. + // + Size = ntohs(Size); + + DataType = ntohs(DataType); + + // + // Before we convert the data type, we should convert the actual data + // sent. + // + switch (DataType) { + case TYPE_CHAR: + case TYPE_UNSIGNED_CHAR: + case TYPE_STRING: + case TYPE_CHUNK: + break; + + case TYPE_SHORT: + case TYPE_UNSIGNED_SHORT: + *((unsigned short *)Data) = ntohs(*((unsigned short *)Data)); + break; + + case TYPE_LONG: + case TYPE_UNSIGNED_LONG: + *((unsigned long *)Data) = ntohl(*((unsigned long *)Data)); + break; + + // + // Might be good to insert some type of error message here for unknown + // datatypes -- but will leave that for later. + // + default: + break; + } +} + diff --git a/code/redalert/field.h b/code/redalert/field.h new file mode 100644 index 0000000..d35b0c5 --- /dev/null +++ b/code/redalert/field.h @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : FIELD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 22, 1996 [PWG] * + * * + * This module takes care of maintaining the field list used to process * + * packets. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __FIELD_H +#define __FIELD_H + +#include +#include + +#define FIELD_HEADER_SIZE (sizeof(FieldClass) - (sizeof(void *) * 2)) + +#define TYPE_CHAR 1 +#define TYPE_UNSIGNED_CHAR 2 +#define TYPE_SHORT 3 +#define TYPE_UNSIGNED_SHORT 4 +#define TYPE_LONG 5 +#define TYPE_UNSIGNED_LONG 6 +#define TYPE_STRING 7 +#define TYPE_CHUNK 20 + +class PacketClass; + +class FieldClass { + + public: + friend class PacketClass; + // + // Define constructors to be able to create all the different kinds + // of fields. + // + FieldClass(void) {}; + FieldClass(char *id, char data); + FieldClass(char *id, unsigned char data); + FieldClass(char *id, short data); + FieldClass(char *id, unsigned short data); + FieldClass(char *id, long data); + FieldClass(char *id, unsigned long data); + FieldClass(char *id, char *data); + FieldClass(char *id, void *data, int length); + + void Host_To_Net(void); + void Net_To_Host(void); + + private: + char ID[4]; // id value of this field + unsigned short DataType; // id of the data type we are using + unsigned short Size; // size of the data portion of this field + void *Data; // pointer to the data portion of this field + FieldClass *Next; // pointer to the next field in the field list +}; + +#endif diff --git a/code/redalert/filepcx.h b/code/redalert/filepcx.h new file mode 100644 index 0000000..0fa4695 --- /dev/null +++ b/code/redalert/filepcx.h @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/code/redalert/findpath.cpp b/code/redalert/findpath.cpp new file mode 100644 index 0000000..2a3107d --- /dev/null +++ b/code/redalert/findpath.cpp @@ -0,0 +1,1307 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FINDPATH.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FINDPATH.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : May 25, 1995 [PWG] * + * * + * The path algorithm works by following a LOS path to the target. If it * + * collides with an impassable spot, it uses an Edge following routine to * + * get around it. The edge follower moves along the edge in a clockwise or * + * counter clockwise fashion until finding the destination spot. The * + * destination is determined by Find_Path. It is the first passable that * + * can be reached (so it will handle the doughnut case, where there is * + * a passable in the center of an unreachable area). * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Clear_Path_Overlap -- clears the path overlap list * + * Find_Path -- Find a path from point a to point b. * + * Find_Path_Cell -- Finds a given cell on a specified path * + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * Get_New_XY -- Get the new x,y based on current position and direction. * + * Optimize_Moves -- Optimize the move list. * + * Set_Path_Overlap -- Sets the overlap bit for given cell * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +//#include + +/* +** When an edge search is started, it can be performed CLOCKwise or +** COUNTERCLOCKwise direction. +*/ +#define CLOCK (FacingType)1 // Clockwise. +#define COUNTERCLOCK (FacingType)-1 // Counterclockwise. + +/* +** If defined, diagonal moves are allowed, else no diagonals. +*/ +#define DIAGONAL + +/* +** This is the marker to signify the end of the path list. +*/ +#define END FACING_NONE + +/* +** "- 1" test for bit manipulation. +*/ +#define TEST + +/* +** If memory is more important than speed, set this define to +** true. It will then perform intermediate optimizations to get the most +** milage out of a limited movement list staging area. If this value +** is true then it figures paths a bit more intelligently. +*/ +#define SAVEMEM true + +/* +** Modify this macro so that given two cell values, it will return +** a value between 0 and 7, with 0 being North and moving +** clockwise (just like map degrees). +*/ +#define CELL_FACING(a, b) Dir_Facing(::Direction((a), (b))) + + +/*-------------------------------------------------------------------------*/ +/* +** Cells values are really indexes into the 'map'. The following value is +** the X width of the map. +*/ +#define MODULO MAP_CELL_W + +/* +** Maximum lookahead cells. Twice this value in bytes will be +** reserved on the stack. The smaller this number, the faster the processing. +*/ +#define MAX_MLIST_SIZE 300 +#define THREAT_THRESHOLD 5 + + +#define MAX_PATH_EDGE_FOLLOW 400 + +#ifdef NEVER +typedef enum { + FACING_N, // North + FACING_NE, // North-East + FACING_E, // East + FACING_SE, // South-East + FACING_S, // South + FACING_SW, // South-West + FACING_W, // West + FACING_NW, // North-West + + FACING_COUNT // Total of 8 directions (0..7). +} FacingType; +#endif + + +/*-------------------------------------------------------------------------*/ +//static bool DrawPath; + +inline FacingType Opposite(FacingType face) +{ + return( (FacingType) (face ^ 4)); +} + + +inline static FacingType Next_Direction(FacingType facing, FacingType dir) +{ + facing = facing + dir; + #ifndef DIAGONAL + facing = (FacingType)(facing & 0x06); + #endif + return(facing); +} + +/*=========================================================================*/ +/* Define a couple of variables which are private to the module they are */ +/* declared in. */ +/*=========================================================================*/ +static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path +static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path +static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path + + +//static CELL MoveMask = 0; +static CELL DestLocation; +static CELL StartLocation; + +/*************************************************************************** + * Point_Relative_To_Line -- Relation between a point and a line * + * * + * If a point is on a line then the following function holds true: * + * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * + * line (x1,z1),(x2,z2). * + * If the right side is > then the left side then the point is on one * + * side of the line and if the right side is < the the left side, then* + * the point is on the other side of the line. By subtracting one side* + * from the other we can determine on what side (if any) the point is on* + * by testing the side of the resulting subtraction. * + * * + * INPUT: * + * int x - x pos of point. * + * int z - z pos of point. * + * int x1 - x pos of first end of line segment. * + * int z1 - z pos of first end of line segment. * + * int x1 - x pos of second end of line segment. * + * int z1 - z pos of second end of line segment. * + * * + * OUTPUT: * + * Assuming (x1,z1) is north, (x2,z2) is south: * + * 0 : point is on line. * + * > 0 : point is east of line. * + * < 0 : point is west of line. * + * * + * WARNINGS: * + * Remember that int means that assumes 16 bits of precision. * + * * + * HISTORY: * + * 10/28/1994 SKB : Created. * + *=========================================================================*/ +int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) +{ + return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); +} + + +/*************************************************************************** + * FootClass::Unravel_Loop -- Unravels a loop in the movement path * + * * + * While in the midst of the Follow Edge logic, it is possible (due to the * + * fact that we support diagonal movement) to begin looping around a * + * column of some type. The Unravel loop function will scan backward * + * through the list and fixup the path to try to prevent the loop. * + * * + * INPUT: path - pointer to the generated path so we can pull the * + * commands out of it. * + * cell - the cell we tried to enter that generated the * + * double overlap condition. * + * dir - the direction we tried to enter from when we * + * generated the double overlap condition * + * startx - the start x position of this path segment * + * starty - the start y position of this path segment * + * destx - the dest x position for this path segment * + * desty - the dest y position for this path segment * + * * + * OUTPUT: TRUE - loop has been successfully unravelled * + * FALSE - loop can not be unravelled so abort follow edge * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/25/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Unravel_Loop(PathType * path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) +{ + /* + ** Walk back to the actual cell before we advanced our position + */ + FacingType curr_dir = dir; + CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); + int idx = path->Length; // start at the last position + FacingType * list = &path->Command[idx-1]; // point to the last command + int checkx; + int checky; + int last_was_line = false; + + /* + ** loop backward through the list searching for a point that is + ** on the line. If the point was a diagonal move then adjust + ** it. + */ + while (idx) { + checkx = Cell_X(curr_pos); + checky = Cell_Y(curr_pos); + + if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { + + /* + ** We have now found a point on the line. Now we must check to see + ** if we left the line on a diagonal. If we did then we need to fix + ** it up. + */ + if (curr_dir & 1 && curr_pos != path->LastFixup) { + cell = curr_pos; + dir = *(list-1); + path->Length = idx; + path->LastFixup = curr_pos; + return(true); + } + + last_was_line = !last_was_line; + } + + /* + ** Since this cell will not be in the list, then pull out its cost + */ + path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); + + /* + ** Remove this cells flag from the overlap list for the path + */ +#ifdef TEST + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31))); +#else + path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); +#endif + + /* + ** Adjust to the next list position and direction. + */ + curr_dir = *list--; + curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); + idx--; + } + + /* + ** If we can't modify the list to eliminate the problem, then we have + ** a larger problem in that we have deleted all of the cells in the + ** list. + */ + return(false); +} + + +/*************************************************************************** + * Register_Cell -- registers a cell on our path and check for backtrack * + * * + * This function adds a new cell to our path. If the cell has already * + * been recorded as part of our path, then this function moves back down * + * the list truncating it at the point we registered that cell. This * + * function will eliminate all backtracking from the list. * + * * + * INPUT: long * list - the list to set the overlap bit for * + * CELL cell - the cell to mark on the overlap list * + * * + * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * + * * + * HISTORY: * + * 05/23/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Register_Cell(PathType * path, CELL cell, FacingType dir, int cost, MoveType threshhold) +{ + FacingType * list; + int pos = cell >> 5; +#ifdef TEST + int bit = (cell & 31); +#else + int bit = (cell & 31) - 1; +#endif + + /* + ** See if this point has already been registered as on the list. If so + ** we need to truncate the list back to this point and register the + ** new direction. + */ + if (path->Overlap[pos] & (1 << bit)) { + /* + ** If this is not a case of immediate back tracking then handle + ** by searching the list to see what we find. However is this is + ** an immediate back track, then pop of the last direction + ** and unflag the cell we are in (not the cell we are moving to). + ** Note: That we do not check for a zero length cell because we + ** could not have a duplicate unless there are cells in the list. + */ + + if (path->Command[path->Length - 1] == Opposite(dir)) { + CELL pos = Adjacent_Cell(cell, Opposite(dir)); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + path->Length--; + } else { + /* + ** If this overlap is in the same place as we had our last overlap + ** then we are in a loop condition. We need to signify that we + ** cannot register this cell. + */ + if (path->LastOverlap == cell) { + return(false); + } else { + path->LastOverlap = cell; + } + + CELL pos = path->Start; + int newlen = 0; + int idx = 0; + list = path->Command; + + /* + ** Note that the cell has to be in this list, so theres no sense + ** in checking whether we found it (famous last words). + ** + ** PWG 8/16/95 - However there is no sense searching the list if + ** the cell we have overlapped on is the cell we + ** started in. + */ + + if (pos != cell) { + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + if (pos == cell) { + idx++; + list++; + break; + } + idx++; + list++; + } + newlen = idx; + } + + /* + ** Now we are pointing at the next command in the list. From here on + ** out we need to unmark the fact that we have entered these cells and + ** adjust the cost of our path to reflect that we have not entered + ** then. + */ + while (idx < path->Length) { + pos = Adjacent_Cell(pos, *list); + path->Cost -= Passable_Cell(pos, *list, -1, threshhold); +#ifdef TEST + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); +#else + path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); +#endif + idx++; + list++; + } + path->Length = newlen; + } + } else { + /* + ** Now we need to register the new direction, updating the cell structure + ** and the cost. + */ + int cpos = path->Length++; + path->Command[cpos] = dir; // save of the direction we moved + path->Cost += cost; // figure new cost for cell + path->Overlap[pos] |= (1 << bit); // mark the we have entered point + } + return(true); +} + + +/*********************************************************************************************** + * Find_Path -- Find a path from point a to point b. * + * * + * INPUT: int source x,y, int destination x,y, char *final moves * + * array to store moves, int maximum moves we may attempt * + * * + * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * + * not reach the destination * + * * + * WARNINGS: This algorithm assumes that the target is NOT situated * + * inside an impassable. If this case may arise, the do-while * + * statement inside the inner while (true) must be changed * + * to include a check to se if the next_x,y is equal to the * + * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + *=============================================================================================*/ +PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, MoveType threshhold) +{ + CELL source = Coord_Cell(Coord); // Source expressed as cell + static PathType path; // Main path control. + CELL next; // Next cell to enter + CELL startcell; // Cell we started in + FacingType direction; // Working direction of look ahead. + FacingType newdir; // Tentative facing value. + + bool left=false, // Was leftward path legal? + right=false; // Was rightward path legal? + + int len; // Length of detour command list. + int unit_threat; // Calculated unit threat rating + int cost; // Cost to enter the square + FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. + moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. + PathType pleft,pright; // Path control structures. + PathType * which; // Which path to actually use. + int threat = 0; // + int threat_stage = 0; //These weren't initialized. ST - 1/8/2019 12:03PM + + + /* + ** If we have been provided an illegal place to store our final moves + ** then forget it. + */ + if (!final_moves) return(NULL); + + BStart(BENCH_FINDPATH); + + PathCount++; + + if (Team && Team->Class->IsRoundAbout) { + unit_threat = (Team) ? Team->Risk : Risk(); + threat_stage = 0; + threat = 0; + } else { + unit_threat = threat = -1; + } + + StartLocation = source; + DestLocation = dest; + + /* + ** Initialize the path structure so that we can keep track of the + ** path. + */ + path.Start = source; + path.Cost = 0; + path.Length = 0; + path.Command = final_moves; + path.Command[0] = END; + path.Overlap = MainOverlap; + path.LastOverlap = -1; + path.LastFixup = -1; + + memset(path.Overlap, 0, sizeof(MainOverlap)); + + /* + ** Clear the over lap list and then make sure that our starting position is marked + ** on the overlap list. (Otherwise the harvesters will drive in circles... ) + */ +#ifdef TEST + path.Overlap[source >> 5] |= (1 << ((source & 31))); +#else + path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); +#endif + + startcell = source; + + /* + ** Account for trailing end of list command, so reduce the maximum + ** allowed legal commands to reflect this. + */ + maxlen--; + + /* + ** As long as there is room to put commands in the movement command list, + ** then put commands in it. We build the path using the following + ** methodology. + ** + ** 1. Scan through the desired straight line path until we either hit an + ** impassable or have created a valid path. + ** + ** 2. If we have hit an impassable, walk through the impassable to make + ** sure that there is a passable on the other side. If there is not + ** and we can not change the impassable, then this list is dead. + ** + ** 3. Walk around the impassable on both the left and right edges and + ** take the shorter of the two paths. + ** + ** 4. Taking the new location as our start location start again with + ** step #1. + */ + while (path.Length < maxlen) { + +top_of_list: + /* + ** Have we reached the destination already? If so abort any further + ** command building. + */ + if (startcell == dest) { + break; + } + + /* + ** Find the absolute correct direction to reach the next straight + ** line cell and what cell it is. + */ + direction = CELL_FACING(startcell, dest); + next = Adjacent_Cell(startcell, direction); + + /* + ** If we can move here, then make this our next move. + */ + cost = Passable_Cell(next, direction, threat, threshhold); + if (cost) { + Register_Cell(&path, next, direction, cost, threshhold); + } else { + /* + ** If the impassable location is actually the destination, + ** then stop here and consider this "good enough". + */ + if (next == dest) break; + + /* + ** We could not move to the next cell, so follow through the + ** impassable until we find a passable spot that can be reached. + ** Once we find a passable, figure out the shortest path to it. + ** Since we have variable passable conditions this is not as + ** simple as it used to be. The limiter loop below allows us to + ** step through ten doughnuts before we give up. + */ + for (int limiter = 0; limiter < 5; limiter++) { + + /* + ** Get the next passable position by zipping through the + ** impassable positions until a passable position is found + ** or the destination is reached. + */ + for (;;) { + + /* + ** Move one step closer toward destination. + */ + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + + /* + ** If the cell is passable then we have been completely + ** successful. If the cell is not passable then continue. + */ + if (Passable_Cell(next, FACING_NONE, threat, threshhold)) { +// if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { + break; + } + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + } + + /* + ** Try to find a path to the passable position by following + ** the edge of the blocking object in both CLOCKwise and + ** COUNTERCLOCKwise fashions. + */ + int follow_len = maxlen + (maxlen >> 1); + + Mem_Copy(&path, &pleft, sizeof(PathType)); + pleft.Command = &moves_left[0]; + pleft.Overlap = LeftOverlap; + Mem_Copy(path.Command, pleft.Command, path.Length); + Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); + + // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; + // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; + // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn. + #if 0 + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left)/sizeof(moves_left[0]), threshhold); +// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); + #endif + left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); + + + if (left) { + follow_len = min(maxlen, pleft.Length + (pleft.Length >> 1)); + } + + Mem_Copy(&path, &pright, sizeof(PathType)); + pright.Command = &moves_right[0]; + pright.Overlap = RightOverlap; + Mem_Copy(path.Command, pright.Command, path.Length); + Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); + + // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; + // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; + // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; This code does not exist in Tiberian Dawn. + #if 0 + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right)/sizeof(moves_right[0]), threshhold); +// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); + #endif + right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); + + /* + ** If we could find a path, break from this loop. Otherwise this + ** means that we have found a "hole" of passable terrain that + ** cannot be reached by normal means. Scan forward looking for + ** the other side of the "doughnut". + */ + if (left || right) break; + + /* + ** If no path can be found to the intermediate cell, then + ** presume we have found a doughnut of some sort. Scan + ** forward until the next impassable is found and then + ** process this loop again. + */ + do { + + /* + ** If we reached destination while in this loop, we + ** know that either the destination is impassible (if + ** we are ignoring) or that we need to up our threat + ** tolerance and try again. + */ + if (next == dest) { + if (threat != -1) { + switch (threat_stage++) { + case 0: + threat = unit_threat >> 1; + break; + + case 1: + threat += unit_threat; + break; + + case 2: + threat = -1; + break; + } + goto top_of_list; + } + goto end_of_list; + } + + newdir = CELL_FACING(next, dest); + next = Adjacent_Cell(next, newdir); + } while (Passable_Cell(next, newdir, threat, threshhold)); + } + + if (!left && !right) break; + + /* + ** We found a path around the impassable locations, so figure out + ** which one was the smallest and copy those moves into the + ** path.Command array. + */ + which = &pleft; + if (right) { + which = &pright; + if (left) { + if (pleft.Length < pright.Length) { + which = &pleft; + } else { + which = &pright; + } + } + } + + /* + ** Record as much as possible of the shorter of the two + ** paths. The trailing EOL command is not copied because + ** this may not be the end of the find path logic. + */ + len = which->Length; + len = min(len, maxlen); + if (len > 0) { + memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); + memcpy(&path.Command[0], &which->Command[0], len * sizeof(FacingType)); + path.Length = len; + path.Cost = which->Cost; + path.LastOverlap = -1; + path.LastFixup = -1; + } else { + break; + } + } + startcell = next; + } + +end_of_list: + /* + ** Poke in the stop command. + */ + if (path.Length < maxlen) { + path.Command[path.Length++] = END; + } + + /* + ** Optimize the move list but only necessary if + ** diagonal moves are allowed. + */ + #ifdef DIAGONAL + Optimize_Moves(&path, threshhold); + #endif + + BEnd(BENCH_FINDPATH); + + return(&path); +} + + +/*********************************************************************************************** + * Follow_Edge -- Follow an edge to get around an impassable spot. * + * * + * INPUT: start -- cell to head from * + * * + * target -- Target cell to head to. * + * * + * path -- Pointer to path list structure. * + * * + * search -- Direction of search (1=clock, -1=counterclock). * + * * + * olddir -- Facing impassible direction from start. * + * * + * callback -- Function pointer for determining if a cell is * + * passable or not. * + * * + * OUTPUT: bool: Could a path be found to the desired cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized & commented. * + *=============================================================================================*/ +bool FootClass::Follow_Edge(CELL start, CELL target, PathType * path, FacingType search, FacingType olddir, int threat, int , int max_cells, MoveType threshhold) +{ + FacingType newdir; // Direction of facing before surrounding cell check. + CELL oldcell, // Current cell. + newcell; // Tentative new cell. + int cost; // Working cost value. + int startx; + int starty; + int online=true; + int targetx; + int targety; + int oldval = 0; + int cellcount=0; + int forceout = false; + FacingType firstdir = (FacingType)-1; + CELL firstcell = -1; + bool stepped_off_line = false; + startx = Cell_X(start); + starty = Cell_Y(start); + targetx = Cell_X(target); + targety = Cell_Y(target); + + if (!path) return(false); + path->LastOverlap = -1; + path->LastFixup = -1; + + #ifndef DIAGONAL + /* + ** The edge following algorithm doesn't "do" diagonals. Force initial facing + ** to be an even 90 degree value. Adjust it in the direction it should be + ** rotating. + */ + if (olddir & 0x01) { + olddir = Next_Direction(olddir, search); + } + #endif + + newdir = Next_Direction(olddir, search); + oldcell = start; + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** Continue until we find our target, find our original starting spot, + ** or run out of moves. + */ + while (path->Length < max_cells) { + + /* + ** Look in all the adjacent cells to determine a passable one that + ** most closely matches the desired direction (working in the specified + ** direction). + */ + newdir = olddir; + for (;;) { + bool forcefail; // Is failure forced? + + forcefail = false; + + #ifdef DIAGONAL + /* + ** Rotate 45/90 degrees in desired direction. + */ + newdir = Next_Direction(newdir, search); + + /* + ** If facing a diagonal we must check the next 90 degree location + ** to make sure that we don't walk right by the destination. This + ** will happen if the destination it is at the corner edge of an + ** impassable that we are moving around. + */ + if (newdir & FACING_NE) { + CELL checkcell; // Non-diagonal check cell. + //int x,y; + + checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); + + if (checkcell == target) { + + /* + ** This only works if in fact, it is possible to move to the + ** cell from the current location. + */ + cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); + if (cost) { + /* + ** YES! The destination is at the corner of an impassable, so + ** set the direction to point directly at it and then the + ** scanning will terminate later. + */ + newdir = Next_Direction(newdir, search); + newcell = Adjacent_Cell(oldcell, newdir); + break; + } + } + + /* + ** Perform special diagonal check. If the edge follower would cross the + ** diagonal or fall on the diagonal line from the source, then consider + ** that cell impassible. Otherwise, the find path algorithm will fail + ** when there are two impassible locations located on a diagonal + ** that is lined up between the source and destination location. + ** + ** P.S. It might help if you check the right cell rather than using + ** the value that just happened to be in checkcell. + */ + + checkcell = Adjacent_Cell(oldcell, newdir); + + int checkx = Cell_X(checkcell); + int checky = Cell_Y(checkcell); + int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); + if (checkval && !online) { + forcefail = ((checkval ^ oldval) < 0); + } else { + forcefail = false; + } + /* + ** The only exception to the above is when we are directly backtracking + ** because we could be trying to escape from a culdesack! + */ + if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { + forcefail = false; + } + } + + #else + newdir = Next_Direction(newdir, search*2); + #endif + + /* + ** If we have just checked the same heading we started with, + ** we are surrounded by impassable characters and we exit. + */ + if (newdir == olddir) { + return(false); + } + + /* + ** Get the new cell. + */ + newcell = Adjacent_Cell(oldcell, newdir); + + /* + ** If we found a passable position, this is where we should move. + */ + if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { + break; + } else { + if (newcell == target) { + forceout = true; + break; + } + } + + } + + /* + ** Record the direction. + */ + if (!forceout) { + /* + ** Mark the cell because this is where we need to be. If register + ** cell fails then the list has been shortened and we need to adjust + ** the new direction. + */ + if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { + /* + ** The only reason we could not register a cell is that we are in + ** a looping situation. So we need to try and unravel the loop if + ** we can. + */ + if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { + return(false); + } + /* + ** Since we need to eliminate a diagonal we must pretend the upon + ** attaining this square, we were moving turned further in the + ** search direction then we really were. + */ + newdir = Next_Direction(newdir, (FacingType)(search*2)); + } + /* + ** Find out which side of the line this cell is on. If it is on + ** a side, then store off that side. + */ + int newx = Cell_X(newcell); + int newy = Cell_Y(newcell); + int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); + if (val) { + oldval = val; + online = false; + } else { + online = true; + } + cellcount++; + if (cellcount == MAX_PATH_EDGE_FOLLOW) { + return(false); + } + } + + /* + ** If we have found the target spot, we are done. + */ + if (newcell == target) { + path->Command[path->Length] = END; + return(true); + } + + /* + ** If we make a full circle back to our original spot, get out. + */ + if (newcell == firstcell && newdir == firstdir) { + return(false); + } + + if (firstcell == -1) { + firstcell = newcell; + firstdir = newdir; + } + + /* + ** Because we moved, our facing is now incorrect. We want to face toward + ** the impassable edge we are following (well, not actually toward, but + ** a little past so that we can turn corners). We have to turn 45/90 degrees + ** more than expected in anticipation of the pending 45/90 degree turn at + ** the start of this loop. + */ + #ifdef DIAGONAL + olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); + #else + olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); + #endif + oldcell = newcell; + } + + /* + ** The maximum search path is exhausted... abort with a failure. + */ + return(false); +} + + +/*********************************************************************************************** + * Optimize_Moves -- Optimize the move list. * + * * + * INPUT: char *moves to optimize * + * * + * OUTPUT: none (list is optimized) * + * * + * WARNINGS: EMPTY moves are used to hold the place of eliminated * + * commands. Also, NEVER call this routine with a list that * + * contains illegal commands. The list MUST be terminated * + * with a EOL command * + * * + * HISTORY: * + * 07/08/1991 CY : Created. * + * 06/01/1992 JLB : Optimized and commented. * + *=============================================================================================*/ +#define EMPTY (FacingType)-2 +int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) +//int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) +{ + /* + ** Facing command pair adjustment table. Compare the facing difference between + ** the two commands. 0 means no optimization is possible. 3 means backtracking + ** so eliminate both commands. Any other value adjusts the first command facing. + */ +#ifdef DIAGONAL + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. +#else + static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; +#endif + FacingType * cmd1, // Floating first command pointer. + * cmd2, // Floating second command pointer. + newcmd; // Calculated new optimized command. + FacingType newdir; // Tentative new direction for smoothing. + CELL cell; // Working cell (as it moves along path). + + /* + ** Abort if there is any illegal parameter. + */ + if (!path || !path->Command) return(0); + + /* + ** Optimization loop -- start scanning with the + ** first pair of commands (if there are at least two + ** in the command list). + */ + path->Command[path->Length] = END; // Force end of list. + + if (path->Length == 0) return(0); + + cell = path->Start; + if (path->Length > 1) { + cmd2 = path->Command + 1; + while (*cmd2 != END) { + + /* + ** Set the cmd1 pointer to point to the valid command closest, but + ** previous to cmd2. Be sure not to go previous to the head of the + ** command list. + */ + cmd1 = cmd2-1; + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + + /* + ** If there isn't any valid previous command, then bump the + ** cmd pointers to the next command pair and continue... + */ + if (*cmd1 == EMPTY) { + cmd2++; + continue; + } + + /* + ** Fetch precalculated command change value. 0 means leave + ** command set alone, 3 means backtrack and eliminate two + ** commands. Any other value is new direction and eliminate + ** one command. + */ + newcmd = (FacingType)(*cmd2 - *cmd1); + if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); + newcmd = _trans[newcmd]; + + /* + ** Check for backtracking. If this occurs, then eliminate the + ** two commands. This is the easiest optimization. + */ + if (newcmd == FACING_SE) { + *cmd1 = EMPTY; + *cmd2++ = EMPTY; + continue; + } + + /* + ** If an optimization code was found the process it. The command is a facing + ** offset to more directly travel toward the immediate destination cell. + */ + if (newcmd) { + + /* + ** Optimizations differ when dealing with diagonals. Especially when dealing + ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can + ** only be optimized if the intervening cell is passable. The distance travelled + ** is the same, but the path is less circuitous. + */ + if (*cmd1 & FACING_NE) { + + /* + ** Diagonal optimizations are always only 45 + ** degree adjustments. + */ + newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); + + /* + ** Diagonal 90 degree changes can be smoothed, although + ** the path isn't any shorter. + */ + if (ABS((int)newcmd) == 1) { + if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { + *cmd2 = newdir; + *cmd1 = newdir; + } + // BOB 16.12.92 + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + continue; + } + } else { + newdir = Next_Direction(*cmd1, newcmd); + } + + /* + ** Allow shortening turn only on right angle moves that are based on + ** 90 degrees. Always allow 135 degree optimizations. + */ + *cmd2 = newdir; + *cmd1 = EMPTY; + + /* + ** Backup what it thinks is the current cell. + */ + while (*cmd1 == EMPTY && cmd1 != path->Command) { + cmd1--; + } + if (*cmd1 != EMPTY) { + cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); + } else { + cell = path->Start; + } + continue; + } + + /* + ** Since we could not make an optimization, we move our + ** head pointer forward. + */ + cell = Adjacent_Cell(cell, *cmd1); + cmd2++; + } + } + + /* + ** Pack the command list to remove any EMPTY command entries. + */ + cmd1 = path->Command; + cmd2 = path->Command; + cell = path->Start; + path->Cost = 0; + path->Length = 0; + while (*cmd2 != END) { + if (*cmd2 != EMPTY) { + cell = Adjacent_Cell(cell, *cmd2); + path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); + path->Length++; + *cmd1++ = *cmd2; + } + cmd2++; + } + path->Length++; + *cmd1 = END; + return(path->Length); +} + + +CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) +{ + FacingType dir; + CELL next; + int lp; + + dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; + + /* + ** Loop through the different acceptable distances. + */ + for (int dist = start; dist < max; dist ++) { + + /* + ** Move to the starting location. + */ + next = dst; + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir); + } + + if (dir & 1) { + /* + ** If our direction is diagonal than we need to check + ** only one side which is as long as both of the old sides + ** together. + */ + for (lp = 0; lp < dist << 1; lp ++) { + next = Adjacent_Cell(next, dir + 3); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } else { + /* + ** If our direction is not diagonal than we need to check two + ** sides so that we are checking a corner like location. + */ + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 2); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + + for (lp = 0; lp < dist; lp ++) { + next = Adjacent_Cell(next, dir + 4); + if (!Can_Enter_Cell(next)) { + return(next); + } + } + } + } + return(-1); +} + + + + +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +{ + MoveType move = Can_Enter_Cell(cell, face); + + if (move < MOVE_MOVING_BLOCK && Distance(Cell_Coord(cell)) > 0x0100) threshhold = MOVE_MOVING_BLOCK; + + if (move > threshhold) return(0); + + if (Session.Type == GAME_NORMAL) { + if (threat != -1) { + if (::Distance(Cell_Coord(cell), Cell_Coord(DestLocation)) > (THREAT_THRESHOLD * CELL_LEPTON_W)) { +// if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { + if (Map.Cell_Threat(cell, Owner()) > threat) + return(0); + } + } + } + + static int _value[MOVE_COUNT] = { + 1, // MOVE_OK + 1, // MOVE_CLOAK + 3, // MOVE_MOVING_BLOCK + 8, // MOVE_DESTROYABLE + 10, // MOVE_TEMP + 0 // MOVE_NO + }; + return(_value[move]); +} + diff --git a/code/redalert/fixed.cpp b/code/redalert/fixed.cpp new file mode 100644 index 0000000..e8add29 --- /dev/null +++ b/code/redalert/fixed.cpp @@ -0,0 +1,245 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FIXED.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FIXED.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/20/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "fixed.h" +#include +#include +#include +#include + + +/* +** These are some handy fixed point constants. Using these constants instead of manually +** constructing them is not only faster, but more readable. +*/ +const fixed fixed::_1_2(1, 2); // 1/2 +const fixed fixed::_1_3(1, 3); // 1/3 +const fixed fixed::_1_4(1, 4); // 1/4 +const fixed fixed::_3_4(3, 4); // 3/4 +const fixed fixed::_2_3(2, 3); // 2/3 + + +fixed::fixed(int numerator, int denominator) +{ + if (denominator == 0) { + Data.Raw = 0U; + } else { + Data.Raw = (unsigned int)(((unsigned __int64)numerator * PRECISION) / denominator); + } +} + + +/*********************************************************************************************** + * fixed::fixed -- Constructor for fixed integral from ASCII initializer. * + * * + * This will parse the ASCII initialization string into a fixed point number. * + * The source string can be a conventional fixed point representation (e.g., "1.0", ".25") * + * or a percent value (e.g. "100%", "25%", "150%"). For percent values, the trailing "%" * + * is required. * + * * + * INPUT: ascii -- Pointer to the ascii source to translate into a fixed point number. * + * * + * OUTPUT: none * + * * + * WARNINGS: It is possible to specify an ASCII string that has more precision and * + * magnitude than can be represented by the fixed point number. In such a case, * + * the resulting value is undefined. * + * * + * HISTORY: * + * 06/20/1996 JLB : Created. * + *=============================================================================================*/ +fixed::fixed(char const * ascii) +{ + /* + ** If there is no valid pointer, then default to zero value. This takes care of any + ** compiler confusion that would call this routine when the programmer wanted the + ** integer parameter constructor to be called. + */ + if (ascii == NULL) { + Data.Raw = 0U; + return; + } + + /* + ** The whole part (if any) always starts with the first legal characters. + */ + char const * wholepart = ascii; + + /* + ** Skip any leading white space. + */ + while (isspace(*ascii)) { + ascii++; + } + + /* + ** Determine if the number is expressed as a percentage. Detect this by + ** seeing if there is a trailing "%" character. + */ + char const * tptr = ascii; + while (isdigit(*tptr)) { + tptr++; + } + + /* + ** Percentage value is specified as a whole number but is presumed to be + ** divided by 100 to get mathematical fixed point percentage value. + */ + if (*tptr == '%') { // Removed '/' preceding '%'. ST - 5/8/2019 + Data.Raw = (unsigned int)(((unsigned __int64)atoi(ascii) * PRECISION) / 100ULL); + } else { + + Data.Composite.Whole = Data.Composite.Fraction = 0U; + if (wholepart && *wholepart != '.') { + Data.Composite.Whole = (unsigned short)atoi(wholepart); + } + + const char * fracpart = strchr(ascii, '.'); + if (fracpart) fracpart++; + if (fracpart) { + unsigned int frac = (unsigned int)atoi(fracpart); + + int len = 0; + unsigned int base = 1; + char const * fptr = fracpart; + while (isdigit(*fptr)) { + fptr++; + len++; + base *= 10U; + } + + Data.Composite.Fraction = (unsigned short)(((unsigned __int64)frac * PRECISION) / base); + } + } +} + + +/*********************************************************************************************** + * fixed::To_ASCII -- Convert a fixed point number into an ASCII string. * + * * + * Use this routine to convert this fixed point number into an ASCII null terminated * + * string. This is the counterpart to the fixed point constructor that takes an ASCII * + * string. * + * * + * INPUT: buffer -- Pointer to the buffer to hold the fixed point ASCII string. * + * * + * maxlen -- The length of the buffer. * + * * + * OUTPUT: Returns with the number of characters placed in the buffer. The trailing null is * + * not counted in this total. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int fixed::To_ASCII(char * buffer, int maxlen) const +{ + if (buffer == NULL) return(0); + + /* + ** Determine the whole and fractional parts of the number. The fractional + ** part number is the value in 1000ths. + */ + unsigned int whole = Data.Composite.Whole; + unsigned int frac = ((unsigned int)Data.Composite.Fraction * 1000U) / PRECISION; + char tbuffer[32]; + + /* + ** If there number consists only of a whole part, then the number is simply + ** printed into the buffer. If there is a fractional part, then there + ** will be a decimal place followed by up to three digits of accuracy for the + ** fractional component. + */ + if (frac == 0) { + sprintf(tbuffer, "%u", whole); + } else { + sprintf(tbuffer, "%u.%02u", whole, frac); + + char * ptr = &tbuffer[strlen(tbuffer)-1]; + while (*ptr == '0') { + *ptr = '\0'; + ptr--; + } + } + + /* + ** If no maximum length to the output buffer was specified, then presume the + ** output buffer is just long enough to store the number and the trailing + ** zero. + */ + if (maxlen == -1) { + maxlen = strlen(tbuffer)+1; + } + + /* + ** Fill the output buffer with the ASCII number. + */ + strncpy(buffer, tbuffer, maxlen); + + /* + ** Return with the number of ASCII characters placed into the output buffer. + */ + int len = strlen(tbuffer); + if (len < maxlen-1) return(len); + return(maxlen-1); +} + + +/*********************************************************************************************** + * fixed::As_ASCII -- Returns a pointer (static) of this number as an ASCII string. * + * * + * This number will be converted into an ASCII string (using a static buffer) and the * + * string pointer will be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the ASCII representation of this fixed point number. * + * * + * WARNINGS: As with all static return pointers, the pointer is valid only until such time * + * as this routine is called again. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +char const * fixed::As_ASCII(void) const +{ + static char buffer[32]; + + To_ASCII(buffer, sizeof(buffer)); + return(buffer); +} diff --git a/code/redalert/fixed.h b/code/redalert/fixed.h new file mode 100644 index 0000000..b141508 --- /dev/null +++ b/code/redalert/fixed.h @@ -0,0 +1,234 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FIXED.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FIXED.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/19/96 * + * * + * Last Update : June 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef FIXED_H +#define FIXED_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +//#pragma warning 604 9 +//#pragma warning 595 9 + +/* +** This is a very simple fixed point class that functions like a regular integral type. However +** it is under certain restrictions. The whole part must not exceed 65535. The fractional part is +** limited to an accuracy of 1/65536. It cannot represent or properly handle negative values. It +** really isn't all that fast (if an FPU is guaranteed to be present than using "float" might be +** more efficient). It doesn't detect overflow or underflow in mathematical or bit-shift operations. +** +** Take careful note that the normal mathematical operators return integers and not fixed point +** values if either of the components is an integer. This is the normal C auto-upcasting rule +** as it would apply presuming that integers are considered to be of higher precision than +** fixed point numbers. This allows the result of these operators to generate values with greater +** magnitude than is normally possible if the result were coerced into a fixed point number. +** If the result should be fixed point, then ensure that both parameters are fixed point. +** +** Note that although integers are used as the parameters in the mathematical operators, this +** does not imply that negative parameters are supported. The use of integers is as a convenience +** to the programmer -- constant integers are presumed signed. If unsigned parameters were +** specified, then the compiler would have ambiguous conversion situation in the case of constant +** integers (e.g. 1, 10, 32, etc). This is most important for the constructor when dealing with the +** "0" parameter case. In that situation the compiler might interpret the "0" as a null pointer rather +** than an unsigned integer. There should be no adverse consequences of using signed integer parameters +** since the precision/magnitude of these integers far exceeds the fixed point component counterparts. +** +** Note that when integer values are returns from the arithmetic operators, the value is rounded +** to the nearest whole integer value. This differs from normal integer math that always rounds down. +*/ +class fixed +{ + static constexpr unsigned int PRECISION = 1 << 16; + + public: + // The default constructor must not touch the data members in any way. + fixed(void) {} + + // Copy constructor + fixed(fixed const & rvalue) {Data.Raw = rvalue.Data.Raw;} + + // Convenient constructor if numerator and denominator components are known. + fixed(int numerator, int denominator); + + // Conversion constructor to get fixed point from integer. + fixed(int value) {Data.Composite.Fraction = 0U;Data.Composite.Whole = (unsigned short)value;} + fixed(unsigned int value) {Data.Composite.Fraction = 0U;Data.Composite.Whole = (unsigned short)value;} + + // Conversion constructor to get fixed point from floating-point. + fixed(float value) {value += 1.0f/(PRECISION<<1);Data.Composite.Fraction = (unsigned short)((value - (unsigned short)value) * PRECISION);Data.Composite.Whole = (unsigned short)value;} + + // Constructor if ASCII image of number is known. + fixed(char const * ascii); + + // Convert to integer when implicitly required. + operator unsigned (void) const {return(unsigned)(((unsigned __int64)Data.Raw+(PRECISION>>1)) / PRECISION);} + + /* + ** The standard operators as they apply to in-place operation. + */ + fixed & operator *= (fixed const & rvalue) {Data.Raw = (unsigned int)(((unsigned __int64)Data.Raw * rvalue.Data.Raw) / PRECISION);return(*this);} + fixed & operator *= (int rvalue) {Data.Raw *= (unsigned int)rvalue;return(*this);} + fixed & operator /= (fixed const & rvalue) {if (rvalue.Data.Raw != 0U && rvalue.Data.Raw != PRECISION) Data.Raw = (unsigned int)((((unsigned __int64)Data.Raw * PRECISION)+(PRECISION>>1)) / rvalue.Data.Raw);return(*this);} + fixed & operator /= (int rvalue) {if (rvalue) Data.Raw /= (unsigned int)rvalue;return(*this);} + fixed & operator += (fixed const & rvalue) {Data.Raw += rvalue.Data.Raw;return(*this);} + fixed & operator -= (fixed const & rvalue) {Data.Raw -= rvalue.Data.Raw;return(*this);} + + /* + ** The standard "My Dear Aunt Sally" operators. The integer versions of multiply + ** and divide are more efficient than using the fixed point counterparts. + */ + const fixed operator * (fixed const & rvalue) const { return fixed(*this) *= rvalue; } + const int operator * (int rvalue) const { return fixed(*this) *= rvalue; } + const fixed operator / (fixed const & rvalue) const { return fixed(*this) /= rvalue; } + const int operator / (int rvalue) const { return fixed(*this) /= rvalue; } + const fixed operator + (fixed const & rvalue) const { return fixed(*this) += rvalue; } + const int operator + (int rvalue) const { return fixed(*this) += rvalue; } + const fixed operator - (fixed const & rvalue) const { return fixed(*this) -= rvalue; } + const int operator - (int rvalue) const { return fixed(*this) -= rvalue; } + + /* + ** The Shift operators are more efficient than using multiplies or divides by power-of-2 numbers. + */ + fixed & operator >>= (unsigned rvalue) {Data.Raw >>= rvalue;return(*this);} + fixed & operator <<= (unsigned rvalue) {Data.Raw <<= rvalue;return(*this);} + const fixed operator >> (unsigned rvalue) const {return fixed(*this) >>= rvalue;} + const fixed operator << (unsigned rvalue) const {return fixed(*this) <<= rvalue;} + + /* + ** The full set of comparison operators. + */ + bool operator == (fixed const & rvalue) const {return(Data.Raw == rvalue.Data.Raw);} + bool operator != (fixed const & rvalue) const {return(Data.Raw != rvalue.Data.Raw);} + bool operator < (fixed const & rvalue) const {return(Data.Raw < rvalue.Data.Raw);} + bool operator > (fixed const & rvalue) const {return(Data.Raw > rvalue.Data.Raw);} + bool operator <= (fixed const & rvalue) const {return(Data.Raw <= rvalue.Data.Raw);} + bool operator >= (fixed const & rvalue) const {return(Data.Raw >= rvalue.Data.Raw);} + bool operator ! (void) const {return(Data.Raw == 0U);} + + /* + ** Comparison to integers requires consideration of fractional component. + */ + bool operator < (int rvalue) const {return(Data.Raw < ((unsigned int)rvalue*PRECISION));} + bool operator > (int rvalue) const {return(Data.Raw > ((unsigned int)rvalue*PRECISION));} + bool operator <= (int rvalue) const {return(Data.Raw <= ((unsigned int)rvalue*PRECISION));} + bool operator >= (int rvalue) const {return(Data.Raw >= ((unsigned int)rvalue*PRECISION));} + bool operator == (int rvalue) const {return(Data.Raw == ((unsigned int)rvalue*PRECISION));} + bool operator != (int rvalue) const {return(Data.Raw != ((unsigned int)rvalue*PRECISION));} + + /* + ** Friend functions to handle the alternate positioning of fixed and integer parameters. + */ + friend const int operator * (int lvalue, fixed const & rvalue) { return fixed(lvalue) * rvalue; } + friend const int operator / (int lvalue, fixed const & rvalue) { return fixed(lvalue) / rvalue; } + friend const int operator + (int lvalue, fixed const & rvalue) { return fixed(lvalue) + rvalue; } + friend const int operator - (int lvalue, fixed const & rvalue) { return fixed(lvalue) - rvalue; } + friend bool operator < (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) < rvalue; } + friend bool operator > (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) > rvalue; } + friend bool operator <= (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) <= rvalue; } + friend bool operator >= (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) >= rvalue; } + friend bool operator == (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) == rvalue; } + friend bool operator != (unsigned lvalue, fixed const & rvalue) { return fixed(lvalue) != rvalue; } + friend int operator *= (int & lvalue, fixed const & rvalue) { lvalue = lvalue * rvalue; return(lvalue); } + friend int operator /= (int & lvalue, fixed const & rvalue) { lvalue = lvalue / rvalue; return(lvalue); } + friend int operator += (int & lvalue, fixed const & rvalue) { lvalue = lvalue + rvalue; return(lvalue); } + friend int operator -= (int & lvalue, fixed const & rvalue) { lvalue = lvalue - rvalue; return(lvalue); } + + /* + ** Helper functions to handle simple and common operations on fixed point numbers. + */ + void Round_Up(void) {Data.Raw += (PRECISION-1U);Data.Composite.Fraction = 0U;} + void Round_Down(void) {Data.Composite.Fraction = 0U;} + void Round(void) {if (Data.Composite.Fraction >= PRECISION>>1) Round_Up();Round_Down();} + void Saturate(unsigned capvalue) {if (Data.Raw > (capvalue*PRECISION)) Data.Raw = capvalue*PRECISION;} + void Saturate(fixed const & capvalue) {if (*this > capvalue) *this = capvalue;} + void Sub_Saturate(unsigned capvalue) {if (Data.Raw >= (capvalue*PRECISION)) Data.Raw = (capvalue*PRECISION)-1U;} + void Sub_Saturate(fixed const & capvalue) {if (*this >= capvalue) Data.Raw = capvalue.Data.Raw-1U;} + void Inverse(void) {*this = fixed(1) / *this;} + + /* + ** Friend helper functions that work in the typical C fashion of passing the object to + ** be processed as a parameter to the function. + */ + friend const fixed Round_Up(fixed const & value) {fixed temp = value; temp.Round_Up();return(temp);} + friend const fixed Round_Down(fixed const & value) {fixed temp = value; temp.Round_Down();return(temp);} + friend const fixed Round(fixed const & value) {fixed temp = value; temp.Round();return(temp);} + friend const fixed Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, unsigned capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Sub_Saturate(fixed const & value, fixed const & capvalue) {fixed temp = value;temp.Sub_Saturate(capvalue);return(temp);} + friend const fixed Inverse(fixed const & value) {fixed temp = value;temp.Inverse();return(temp);} + + /* + ** Conversion of the fixed point number into an ASCII string. + */ + int To_ASCII(char * buffer, int maxlen=-1) const; + char const * As_ASCII(void) const; + + /* + ** Helper constants that provide some convenient fixed point values. + */ + static const fixed _1_2; + static const fixed _1_3; + static const fixed _1_4; + static const fixed _3_4; + static const fixed _2_3; + + private: + union { + struct { +#ifdef BIG_ENDIAN + unsigned short Whole; + unsigned short Fraction; +#else + unsigned short Fraction; + unsigned short Whole; +#endif + } Composite; + unsigned int Raw; + } Data; +}; + + +#endif diff --git a/code/redalert/flasher.cpp b/code/redalert/flasher.cpp new file mode 100644 index 0000000..f0fee92 --- /dev/null +++ b/code/redalert/flasher.cpp @@ -0,0 +1,130 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLASHER.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FlasherClass::Debug_Dump -- Displays current status to the monochrome screen. * + * * + * This utility function will output the current status of the FlasherClass to the mono * + * screen. It is through this display that bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void FlasherClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(50, 7); + mono->Printf("%2d", FlashCount); +} +#endif + + +/*********************************************************************************************** + * FlasherClass::Process -- Performs the logic processing for the flashing ability. * + * * + * The ability for an object to flash is controlled by this logic processing routine. It * + * should be called once per game tick per unit. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should the associated object be redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 06/20/1994 JLB : Is now independent of object it represents. * + *=============================================================================================*/ +bool FlasherClass::Process(void) +{ + // 2019/09/20 JAS - Flashing info needs to exist per player + for (int i = 0; i < HOUSE_COUNT; i++) + { + if (FlashCountPerPlayer[i]) + { + FlashCountPerPlayer[i]--; + } + } + + if (FlashCount) { + FlashCount--; + IsBlushing = false; + + if (FlashCount & 0x01) { + IsBlushing = true; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FlasherClass::Get_Flashing_Flags -- * + * * + * Gets the flags tell which players this object should flash for. * + * * + * INPUT: none * + * * + * OUTPUT: unsigned int; Flag representing the players to flash for * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 2019/09/20 JAS : Created. * + *=============================================================================================*/ +unsigned int FlasherClass::Get_Flashing_Flags() const +{ + unsigned flags = 0; + for (int i = 0; i < HOUSE_COUNT; ++i) + { + if (FlashCountPerPlayer[i] > 0) + { + flags |= (1 << i); + } + } + + return flags; +} \ No newline at end of file diff --git a/code/redalert/flasher.h b/code/redalert/flasher.h new file mode 100644 index 0000000..eeafe8d --- /dev/null +++ b/code/redalert/flasher.h @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLASHER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLASHER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 28, 1994 * + * * + * Last Update : May 28, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLASHER_H +#define FLASHER_H + +class FlasherClass { + public: + /* + ** When this object is targeted, it will flash a number of times. This is the + ** flash control number. It counts down to zero and then stops. Odd values + ** cause the object to be rendered in a lighter color. + */ + unsigned FlashCount:7; + + /* + ** When an object is targeted, it flashes several times to give visual feedback + ** to the player. Every other game "frame", this flag is true until the flashing + ** is determined to be completed. + */ + unsigned IsBlushing:1; + + // 2019/09/20 JAS - Flashing info needs to exist per player + unsigned int Get_Flashing_Flags() const; + unsigned int FlashCountPerPlayer[HOUSE_COUNT]; + + // 2019/09/20 JAS - Flashing info needs to exist per player + FlasherClass(void) { + FlashCount = 0; + IsBlushing = false; + + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = 0; + } + } + + FlasherClass(NoInitClass const & ) {}; + ~FlasherClass(void) {}; + + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + bool Process(void); +}; + +#endif diff --git a/code/redalert/fly.cpp b/code/redalert/fly.cpp new file mode 100644 index 0000000..584c781 --- /dev/null +++ b/code/redalert/fly.cpp @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLY.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : June 5, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * FlyClass::Physics -- Performs vector physics (movement). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FlyClass::Physics -- Performs vector physics (movement). * + * * + * This routine performs movement (vector) physics. It takes the * + * specified location and moves it according to the facing and speed * + * of the vector. It returns the status of the move. * + * * + * INPUT: coord -- Reference to the coordinate that the vector will * + * be applied to. * + * * + * OUTPUT: Returns with the status of the vector physics. This could * + * range from no effect, to exiting the edge of the world. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + * 06/05/1995 JLB : Simplified to just do movement. * + *=============================================================================================*/ +ImpactType FlyClass::Physics(COORDINATE & coord, DirType facing) +{ + if (SpeedAdd != MPH_IMMOBILE) { + int actual = (int)SpeedAdd + SpeedAccum; + div_t result = div(actual, PIXEL_LEPTON_W); + SpeedAccum = result.rem; + actual -= result.rem; + COORDINATE old = coord; + + /* + ** If movement occurred that is at least one + ** pixel, then check update the coordinate and + ** check for edge of world collision. + */ + if (result.quot) { + COORDINATE newcoord; // New working coordinate. + newcoord = Coord_Move(coord, facing, actual); + /* + ** If no movement occurred, then presume it hasn't moved at all + ** and return immediately with this indication. + */ + if (newcoord == coord) { + return(IMPACT_NONE); + } + + /* + ** Remember the new position. + */ + coord = newcoord; + + /* + ** If the new coordinate is off the edge of the world, then report + ** this. + */ + if (newcoord & HIGH_COORD_MASK /*|| !Map.In_Radar(Coord_Cell(newcoord))*/) { +// if (!Map.In_Radar(Coord_Cell(newcoord))) { + coord = old; + return(IMPACT_EDGE); + } + + return(IMPACT_NORMAL); + } + } + return(IMPACT_NONE); +} + + +/*********************************************************************************************** + * FlyClass::Fly_Speed -- Sets the flying object to the speed specified. * + * * + * This sets the speed of the projectile. It basically functions like a throttle value * + * where 0 equals stop and 255 equals maximum speed (whatever that is for the particular * + * object). * + * * + * INPUT: speed -- Speed setting from 0 to 255. * + * * + * maximum -- The maximum speed of the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1994 JLB : Created. * + * 07/26/1994 JLB : Added maximum speed as guiding value. * + *=============================================================================================*/ +void FlyClass::Fly_Speed(int speed, MPHType maximum) +{ + SpeedAdd = (MPHType)( maximum * fixed(speed, 256)); +// SpeedAdd = (MPHType)Fixed_To_Cardinal((int)maximum, speed); +} + + diff --git a/code/redalert/fly.h b/code/redalert/fly.h new file mode 100644 index 0000000..b02163e --- /dev/null +++ b/code/redalert/fly.h @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FLY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FLY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FLY_H +#define FLY_H + +typedef enum ImpactType : unsigned char { + IMPACT_NONE, // No movement (of significance) occurred. + IMPACT_NORMAL, // Some (non eventful) movement occurred. + IMPACT_EDGE // The edge of the world was reached. +} ImpactType; + + +/**************************************************************************** +** Flying objects are handled by this class definition. +*/ +class FlyClass { + public: + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FlyClass(void) : SpeedAccum(0), SpeedAdd(MPH_IMMOBILE) {}; + FlyClass(NoInitClass const &) {}; + ~FlyClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + void Fly_Speed(int speed, MPHType maximum); + ImpactType Physics(COORDINATE &coord, DirType facing); + MPHType Get_Speed(void) const {return(SpeedAdd);}; + + private: + /* + ** Object movement consists of incrementing the accumulator until enough "distance" + ** has accumulated so that moving the object becomes reasonable. + */ + unsigned SpeedAccum; // Lepton accumulator. + MPHType SpeedAdd; // Lepton add (per frame). +}; + +#endif diff --git a/code/redalert/foot.cpp b/code/redalert/foot.cpp new file mode 100644 index 0000000..3ef0bdb --- /dev/null +++ b/code/redalert/foot.cpp @@ -0,0 +1,2597 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FOOT.CPP 2 3/06/97 1:46p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 22, 1994 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FootClass::AI -- Handle general movement AI. * + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * FootClass::Body_Facing -- Set the body rotation/facing. * + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * FootClass::Death_Announcement -- Announces the death of a unit. * + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * FootClass::Detach -- Detaches a target from tracking systems. * + * FootClass::Detach_All -- Removes this object from the game system. * + * FootClass::Enters_Building -- When unit enters a building for some reason. * + * FootClass::FootClass -- Normal constructor for the foot class object. * + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority* + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * FootClass::Mark -- Unit interface to map rendering system. * + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * FootClass::Mission_Capture -- Handles the capture mission. * + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * FootClass::Override_Mission -- temporarily overrides a units mission * + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * FootClass::Restore_Mission -- Restores an overridden mission * + * FootClass::Sell_Back -- Causes this object to be sold back. * + * FootClass::Set_Speed -- Initiate unit movement physics. * + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * FootClass::Take_Damage -- Handles taking damage to this object. * + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FootClass::FootClass -- Default constructor for foot class objects. * + * * + * This is the default constructor for FootClass objects. It sets the foot class values to * + * their default starting settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/23/1994 JLB : Created. * + *=============================================================================================*/ +FootClass::FootClass(RTTIType rtti, int id, HousesType house) : + TechnoClass(rtti, id, house), + IsScanLimited(false), + IsInitiated(false), + IsNewNavCom(false), + IsPlanningToLook(false), + IsDeploying(false), + IsFiring(false), + IsRotating(false), + IsDriving(false), + IsUnloading(false), + IsFormationMove(false), + IsNavQueueLoop(false), + IsScattering(false), + IsMovingOntoBridge(false), + Speed(0), + SpeedBias(1), + XFormOffset(0x80000000), + YFormOffset(0x80000000), + NavCom(TARGET_NONE), + SuspendedNavCom(TARGET_NONE), + Team(0), + Group(255), + Member(0), + PathThreshhold(MOVE_CLOAK), + PathDelay(0), + TryTryAgain(PATH_RETRY), + BaseAttackTimer(0), + FormationSpeed(SPEED_FOOT), + FormationMaxSpeed(MPH_IMMOBILE), + HeadToCoord(0) +{ + Path[0] = FACING_NONE; + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * FootClass::Debug_Dump -- Displays the status of the FootClass to the mono monitor. * + * * + * This routine is used to output the current status of the foot class to the mono * + * monitor. Through this display bugs may be tracked down or eliminated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 07/04/1995 JLB : Handles aircraft special case. * + *=============================================================================================*/ +void FootClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Fill_Attrib(53, 13, 12, 1, IsInitiated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsPlanningToLook ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 14, 12, 1, IsDeploying ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 15, 12, 1, IsFiring ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 16, 12, 1, IsRotating ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 17, 12, 1, IsDriving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 18, 12, 1, IsUnloading ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsFormationMove ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Set_Cursor(45, 1);mono->Printf("%02X", Speed); + if (NavCom) { + mono->Set_Cursor(29, 5); + mono->Printf("%08X", NavCom); + } + if (SuspendedNavCom) { + mono->Set_Cursor(38, 5); + mono->Printf("%08X", SuspendedNavCom); + } + + if (Team) Team->Debug_Dump(mono); + if (Group != 255) { + mono->Set_Cursor(59, 1);mono->Printf("%d", Group); + } + + static char const * _p2c[9] = {"-","0","1","2","3","4","5","6","7"}; + for (int index = 0; index < min(12, ARRAY_SIZE(Path)); index++) { + mono->Set_Cursor(54+index, 3); + mono->Printf("%s", _p2c[((ABS((int)Path[index]+1)) % ARRAY_SIZE(_p2c))]); + } + mono->Set_Cursor(54, 5);mono->Printf("%2d", PathThreshhold); + mono->Set_Cursor(72, 3);mono->Printf("%4d", (long)PathDelay); + mono->Set_Cursor(67, 3);mono->Printf("%3d", TryTryAgain); + if (HeadToCoord) { + mono->Set_Cursor(60, 5);mono->Printf("%08X", HeadToCoord); + } + + TechnoClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * FootClass::Set_Speed -- Initiate unit movement physics. * + * * + * This routine is used to set a unit's velocity control structure. * + * The game will then process the unit's movement during the momentum * + * physics calculation. * + * * + * INPUT: unit -- Pointer to the unit to alter. * + * * + * speed -- Throttle setting (0=stop, 255=full throttle). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 09/24/1993 JLB : Revised for faster speed. * + * 04/02/1994 JLB : Revised for new system. * + * 04/15/1994 JLB : Converted to member function. * + * 07/21/1994 JLB : Simplified. * + *=============================================================================================*/ +void FootClass::Set_Speed(int speed) +{ + assert(IsActive); + + speed &= 0xFF; + ((unsigned char &)Speed) = speed; +} + + +/*********************************************************************************************** + * FootClass::Mark -- Unit interface to map rendering system. * + * * + * This routine is the interface function for units as they relate to * + * the map rendering system. Whenever a unit's imagery changes, this * + * function is called. * + * * + * INPUT: mark -- Type of image change (MARK_UP, _DOWN, _CHANGE) * + * MARK_UP -- Unit is removed. * + * MARK_CHANGE -- Unit alters image but doesn't move. * + * MARK_DOWN -- Unit is overlaid onto existing icons. * + * * + * OUTPUT: bool; Did the marking operation succeed? Failure could be the result of marking * + * down when it is already down, or visa versa. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + * 12/23/1994 JLB : Performs low level check before processing. * + *=============================================================================================*/ +bool FootClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (TechnoClass::Mark(mark)) { +// short list[32]; + CELL cell = Coord_Cell(Coord); + +#ifndef PARTIAL + if (In_Which_Layer() != LAYER_GROUND && (mark == MARK_UP || mark == MARK_DOWN)) mark = MARK_CHANGE; +#endif + + /* + ** Inform the map of the refresh, occupation, and overlap + ** request. + */ + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + case MARK_CHANGE_REDRAW: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List()); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Basic_Path -- Finds the basic path for a ground object. * + * * + * This is a common routine used by both infantry and other ground travelling units. It * + * will fill in the unit's basic path to the NavCom destination. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a path found? A failure to find a path means either the target cannot * + * be found or the terrain prohibits the unit's movement. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Basic_Path(void) +{ + assert(IsActive); + + PathType * path; // Pointer to path control structure. + CELL cell; + int skip_path = false; + + Path[0] = FACING_NONE; + + if (Target_Legal(NavCom)) { + cell = As_Cell(NavCom); + + /* + ** When the navigation computer is set to a location that is impassible, then + ** find a nearby cell that can be entered and try to head toward that instead. + ** EXCEPT when that cell is very close -- then just bail. + */ + int dist = Distance(NavCom); + int checkdist = Team.Is_Valid() ? Rule.StrayDistance : Rule.CloseEnoughDistance; + if (Can_Enter_Cell(cell) > MOVE_CLOAK && dist > checkdist) { + CELL cell2 = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + if (cell2 != 0 && ::Distance(Cell_Coord(cell), Cell_Coord(cell2)) < dist) cell = cell2; + } + + if (What_Am_I() == RTTI_INFANTRY) { + CELL mycell = Coord_Cell(Center_Coord()); + ObjectClass * obj = Map[mycell].Cell_Occupier(); + while (obj) { + if (obj != this && obj->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)obj; + if (inf->NavCom == NavCom && inf->Path[0] != FACING_NONE) { + if (Coord_Cell(inf->Head_To_Coord()) == Coord_Cell(inf->Coord)) { + Mem_Copy(&inf->Path[1], Path, sizeof(Path)-sizeof(Path[0])); + } else { + Mem_Copy(inf->Path, Path, sizeof(Path)); + } + if (Path[0] != FACING_NONE) { + skip_path = true; + } + break; + } + } + obj = obj->Next; + } + } + + if (!skip_path) { + Mark(MARK_UP); + Path[0] = FACING_NONE; // Probably not necessary, but... + + /* + ** Try to find a path to the destination. If a failure occurs, then keep trying + ** with greater determination until either a complete failure occurs, or a decent + ** path was found. + */ + bool found1=false; // Found a best path yet? + PathType path1; + FacingType workpath1[200]; // Staging area for path list. +// FacingType workpath2[200]; // Staging area for path list. + MoveType maxtype = MOVE_TEMP; + if (!House->IsHuman) { + maxtype = MOVE_TEMP; +// maxtype = MOVE_DESTROYABLE; + } else { + + /* + ** For simple movement missions by the human player, then don't + ** consider friendly units as passable if close to the destination. + ** This will prevent a human controlled unit from just sitting next + ** to a destination just because there is another friendly unit + ** occupying the destination location. + */ + if (Mission == MISSION_MOVE && Distance(NavCom) < Rule.CloseEnoughDistance) { + maxtype = MOVE_DESTROYABLE; + } + } + + /* + ** Try to find a path to the destination. If there is a path + ** failure, then try a more severe path method until the + ** maximum severity is reached. + */ + for (;;) { + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), PathThreshhold); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + break; + } + + /* + ** A valid path was not found. Try the next greater path severity + ** level if the severity can be increased. If not, then consider this + ** a total failure. + */ + PathThreshhold++; + if (PathThreshhold > maxtype) break; + } + +#ifdef NEVER + /* + ** Determine if ANY path could be calculated by first examining the most + ** aggressive case. If this fails, then no path will succeed. Further + ** scanning is unnecessary. + */ + path = Find_Path(cell, &workpath1[0], sizeof(workpath1), maxtype); + if (path && path->Cost) { + memcpy(&path1, path, sizeof(path1)); + found1 = true; + + /* + ** Scan for the best path possible. If this succeeds, then do a simple + ** comparison with the most aggressive path. If they are very close, then + ** go with the best (easiest) path method. + */ + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), MOVE_CLOAK); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } else { + + /* + ** The easiest path method didn't result in a satisfactory path. Scan through + ** the rest of the path options, looking for the best one. + */ + for (MoveType move = (MoveType)(MOVE_CLOAK+1); move < (MoveType)(maxtype-1); move++) { +// for (MoveType move = MOVE_MOVING_BLOCK; move < maxtype-1; move++) { + path = Find_Path(cell, &workpath2[0], sizeof(workpath2), move); + if (path && path->Cost && path->Cost < max((path1.Cost + (path1.Cost/2)), 3)) { + memcpy(&path1, path, sizeof(path1)); + memcpy(workpath1, workpath2, sizeof(workpath1)); + } + } + } + } +#endif + + /* + ** If a good path was found, then record it in the object's path + ** list. + */ + if (found1) { + Fixup_Path(&path1); + memcpy(&Path[0], &workpath1[0], min(path->Length, (int)sizeof(Path))); + } + + Mark(MARK_DOWN); + } + + PathDelay = Rule.PathDelay * TICKS_PER_MINUTE; + if (Path[0] != FACING_NONE) return(true); + + /* + ** If a basic path couldn't be determined, then abort the navigation process. + */ + Stop_Driver(); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Move -- AI process for moving a vehicle to its destination. * + * * + * This simple AI script handles moving the vehicle to its desired destination. Since * + * simple movement is handled directly by the engine, this routine merely waits until * + * the unit has reached its destination, and then causes the unit to enter idle mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 10/02/1996 JLB : Player controlled or human owned units don't scan for targets. * + *=============================================================================================*/ +int FootClass::Mission_Move(void) +{ + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsDriving && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + return(1); + } +// if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman) { + if (!Target_Legal(TarCom) && !House->IsPlayerControl && !House->IsHuman && (!Team.Is_Valid() || !Team->Class->IsSuicide)) { + Target_Something_Nearby(THREAT_RANGE); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Capture -- Handles the capture mission. * + * * + * Capture missions are nearly the same as normal movement missions. The only difference * + * is that the final destination is handled in a special way so that it is not marked as * + * impassable. This allows the object (usually infantry) the ability to walk onto the * + * object and thus capture it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game ticks to delay before calling this routine. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Capture(void) +{ + assert(IsActive); + + /* + ** If there is a valid TarCom but the NavCom isn't set, then set the NavCom accordingly. + */ + if (Is_Target_Building(TarCom) && !Target_Legal(NavCom) && What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber) { + Assign_Destination(TarCom); + } + + if (!Target_Legal(NavCom) /*&& !In_Radio_Contact()*/) { + Enter_Idle_Mode(); + if (Map[Center_Coord()].Cell_Building()) { + Scatter(0, true); + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Attack -- AI for heading towards and firing upon target. * + * * + * This AI routine handles heading to within range of the target and then firing upon * + * it until it is destroyed. If the target is destroyed, then the unit will change * + * missions to match its "idle mode" of operation (usually guarding). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Attack(void) +{ + assert(IsActive); + if (Target_Legal(TarCom)) { + Approach_Target(); + } else { + Enter_Idle_Mode(); + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard -- Handles the AI for guarding in place. * + * * + * Units that are performing stationary guard duty use this AI process. They will sit * + * still and target any enemies that get within range. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Guard(void) +{ + assert(IsActive); + + /* + ** If this unit is on an impassable cell for any reason, it needs to scatter immediately + */ + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + LandType land = Map[Coord].Land_Type(); + if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + Scatter(0, true, true); + Shorten_Mission_Timer(); + } + } + + if (!Target_Something_Nearby(THREAT_RANGE)) { + Random_Animate(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_VESSEL) { + switch (((VesselClass *)this)->Class->Type) { + case VESSEL_DD: + case VESSEL_PT: + dtime = MissionControl[Mission].AA_Delay(); + break; + + case VESSEL_CA: + dtime *= 2; + break; + + default: + break; + } + } + if (What_Am_I() == RTTI_INFANTRY) { + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + } + + switch (((InfantryClass *)this)->Class->Type) { + case INFANTRY_E1: + case INFANTRY_E3: + dtime = MissionControl[Mission].AA_Delay(); + break; + + default: + break; + } + } + + return((Arm != 0) ? (int)Arm : (dtime+Random_Pick(0, 2))); +} + + +/*********************************************************************************************** + * FootClass::Mission_Hunt -- Handles the default hunt order. * + * * + * This routine is the default hunt order for game objects. It handles searching for a * + * nearby object and heading toward it. The act of targeting will cause it to attack * + * the target it selects. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the game tick delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Hunt(void) +{ + assert(IsActive); + if (!Target_Something_Nearby(THREAT_NORMAL)) { +#if(0) +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this == INFANTRY_GENERAL && House->Class->House==HOUSE_UKRAINE && Scen.Scenario==47) { + for(int index=0; index < Buildings.Count(); index++) { + if(Buildings.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Buildings.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Units.Count(); index++) { + if(Units.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Units.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Infantry.Count(); index++) { + if(Infantry.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Infantry.Ptr(index)->As_Target()); + break; + } + } + for(index=0; index < Aircraft.Count(); index++) { + if(Aircraft.Ptr(index)->IsOwnedByPlayer) { + Assign_Target(Aircraft.Ptr(index)->As_Target()); + break; + } + } + } +#endif +#endif + Random_Animate(); + } else { + if (What_Am_I() == RTTI_INFANTRY && ( ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_RENOVATOR || ((InfantryTypeClass const &)Class_Of()).Type == INFANTRY_THIEF) ) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + } else { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsBomber && Is_Target_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + } else { + Approach_Target(); + } + } + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Stop_Driver -- This routine clears the driving state of the object. * + * * + * This is the counterpart routine to the Start_Driver function. It clears the driving * + * status flags and destination coordinate record. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Greatly simplified. * + *=============================================================================================*/ +bool FootClass::Stop_Driver(void) +{ + assert(IsActive); + + if (HeadToCoord) { + HeadToCoord = NULL; + Set_Speed(0); + IsDriving = false; + IsMovingOntoBridge = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Start_Driver -- This starts the driver heading to the destination desired. * + * * + * Before a unit can move it must be started by this routine. This routine handles * + * reserving the cell and setting the driving flag. * + * * + * INPUT: headto -- The coordinate of the immediate drive destination. This is one cell * + * away from the unit's current location. * + * * + * OUTPUT: bool; Was driving initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 12/12/1994 JLB : Uses simple spot index finder. * + *=============================================================================================*/ +bool FootClass::Start_Driver(COORDINATE &headto) +{ + assert(IsActive); + + Stop_Driver(); + if (headto) { + HeadToCoord = headto; + IsDriving = true; + + CellClass * cellptr = &Map[headto]; + TemplateType ttype = cellptr->TType; + IsMovingOntoBridge = (ttype >= TEMPLATE_BRIDGE1 && ttype <= TEMPLATE_BRIDGE2D) || (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3F); + + /* + ** Check for crate goodie finder here. + */ + if (Map[headto].Goodie_Check(this)) { + return(true); + } + if (!IsActive) return(false); + + HeadToCoord = NULL; + IsDriving = false; + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Sort_Y -- Determine the sort coordinate for foot class objects. * + * * + * This routine will determine the sort coordinate for foot class object. This coordinate * + * is usually the coordinate of the object. The exception is if the object is tethered. * + * In this case (presumes offloading to the north), the sorting coordinate is adjusted * + * so that the object will be drawn on top of the transport unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 11/04/1994 JLB : Sort value is different when unloading from aircraft. * + *=============================================================================================*/ +COORDINATE FootClass::Sort_Y(void) const +{ + assert(IsActive); + + if (IsUnloading) { + return(Coord_Add(Coord, 0x01000000L)); + } + if (In_Radio_Contact() && IsTethered && Contact_With_Whom()->What_Am_I() == RTTI_UNIT) { + return(Coord_Add(Coord, 0x01000000L)); + } + return(Coord_Add(Coord, 0x00300000L)); +} + + +/*********************************************************************************************** + * FootClass::Stun -- Prepares a ground travelling object for removal. * + * * + * This routine clears the units' navigation computer in preparation for removal from the * + * game. This is probably called as a result of unit destruction in combat. Clearing the * + * navigation computer ensures that the normal AI process won't start it moving again while * + * the object is undergoing any death animations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void FootClass::Stun(void) +{ + assert(IsActive); + + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + Stop_Driver(); + TechnoClass::Stun(); +} + + +/*********************************************************************************************** + * FootClass::Approach_Target -- Sets the navigation computer to approach target object. * + * * + * This routine will set the navigation computer to approach the target indicated by the * + * targeting computer. It is through this function that the unit nears the target so * + * that weapon firing may occur. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/13/1994 JLB : Made part of TechnoClass. * + * 12/22/1994 JLB : Enhanced search algorithm. * + * 05/20/1995 JLB : Always approaches if the object is off the map. * + *=============================================================================================*/ +void FootClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + /* + ** If the target is too far away then head toward it. + */ + int maxrange = Weapon_Range(primary); +// int maxrange = max(Weapon_Range(0), Weapon_Range(1)); + + if (!Target_Legal(NavCom) && (!In_Range(TarCom, primary) || !IsLocked)) { +// if (!Target_Legal(NavCom) && (Distance(TarCom) > maxrange || !IsLocked)) { + + /* + ** If the object that we are attacking is a building adjust the unit's + ** max range so that people can stand far away from the buildings and + ** hit them. + */ + BuildingClass * obj = As_Building(TarCom); + if (obj) { + maxrange += ((obj->Class->Width() + obj->Class->Height()) * (0x100 / 4)); + } + + /* + ** Adjust the max range of an infantry unit for where he is standing + ** in the room. + */ + maxrange -= 0x00B7; +#ifdef OBSOLETE + if (What_Am_I() == RTTI_INFANTRY) { + maxrange -= 0x0111; + } else { + maxrange -= 0x00B7; + } +#endif + maxrange = max(maxrange, 0); + + COORDINATE tcoord = ::As_Coord(TarCom); + COORDINATE trycoord = 0; + CELL tcell = Coord_Cell(tcoord); + CELL trycell = tcell; + DirType dir = Direction256(tcoord, Center_Coord()); + bool found = false; + + /* + ** Sweep through the cells between the target and the unit, looking for + ** a cell that the unit can enter but which is also within weapon range + ** of the target. If after a reasonable search, no appropriate cell could + ** be found, then the target will be assigned as the movement destination + ** and let "the chips fall where they may." + */ + for (int range = maxrange; range > 0x0080; range -= 0x0100) { + static int _angles[] = {0, 8, -8, 16, -16, 24, -24, 32, -32, 48, -48, 64, -64}; + + for (int index = 0; index < (sizeof(_angles)/sizeof(_angles[0])); index++) { + trycoord = Coord_Move(tcoord, (DirType)(dir + _angles[index]), range); + + if (::Distance(trycoord, tcoord) < range) { + trycell = Coord_Cell(trycoord); + if (Map.In_Radar(trycell) && Map[trycell].Is_Clear_To_Move(Techno_Type_Class()->Speed, false, false, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)) { +// if (Can_Enter_Cell(trycell) <= MOVE_CLOAK && Map.In_Radar(trycell)) { + found = true; + break; + } + } + } + if (found) break; + } + + /* + ** If a suitable intermediate location was found, then head toward it. + ** Otherwise, head toward the enemy unit directly. + */ + if (found) { + Assign_Destination(::As_Target(trycell)); + } else { + + trycell = Map.Nearby_Location(trycell, Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + Assign_Destination(::As_Target(trycell)); +// Assign_Destination(TarCom); + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Mission_Guard_Area -- Causes unit to guard an area about twice weapon range. * + * * + * This mission routine causes the unit to scan for targets out to twice its weapon range * + * from the home point. If a target was found, then it will be attacked. The unit will * + * chase the target until it gets up to to its weapon range from the home position. * + * In that case, it will return to home position and start scanning for another target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with time delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 07/27/1995 JLB : Greatly simplified. * + *=============================================================================================*/ +int FootClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + /* + ** If this unit is on an impassable cell for any reason, it needs to scatter immediately + */ + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT) { + LandType land = Map[Coord].Land_Type(); + if (!Target_Legal(NavCom) && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + Scatter(0, true, true); + Shorten_Mission_Timer(); + } + } + + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Class->IsToHarvest) { + Assign_Mission(MISSION_HARVEST); + return(1+Random_Pick(1, 10)); + } + + /* + ** Ensure that the archive target is valid. + */ + if (!Target_Legal(ArchiveTarget)) { + ArchiveTarget = ::As_Target(Coord); + } + + /* + ** If this is a bomber type infantry and the current target is a building, then go into + ** sabotage mode if not already. + */ + if (!House->IsHuman && What_Am_I() == RTTI_INFANTRY && Is_Target_Building(TarCom) && ((InfantryClass *)this)->Class->IsBomber && Mission != MISSION_SABOTAGE) { + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + /* + ** Make sure that the unit has not strayed too far from the home position. + ** If it has, then race back to it. + */ + int maxrange = Threat_Range(1)/2; + + if (!IsFiring && !Target_Legal(NavCom) && Distance(ArchiveTarget) > maxrange) { + Assign_Target(TARGET_NONE); + Assign_Destination(ArchiveTarget); + } + + if (!Target_Legal(TarCom)) { + COORDINATE old = Coord; + Coord = As_Coord(ArchiveTarget); + Target_Something_Nearby(THREAT_AREA); + Coord = old; + if (Target_Legal(TarCom)) { + return(1); + } + Random_Animate(); + } else { + Approach_Target(); + } + + int dtime = MissionControl[Mission].Normal_Delay(); + if (What_Am_I() == RTTI_AIRCRAFT) { + dtime *= 2; + } + return(dtime + Random_Pick(1, 5)); +} + + +/*********************************************************************************************** + * FootClass::Unlimbo -- Unlimbos object and performs special fixups. * + * * + * This routine will make sure that the home position for the foot class object gets * + * reset. This is necessary since the home position may change depending on the unit's * + * transition between limbo and non-limbo condition. * + * * + * INPUT: coord -- The coordinate to unlimbo the unit at. * + * * + * dir -- The initial direction to give the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + /* + ** Try to unlimbo the unit. + */ + if (TechnoClass::Unlimbo(coord, dir)) { + + /* + ** Mobile units are always revealed to the house that owns them. + */ + Revealed(House); + + /* + ** Start in a still (non-moving) state. + */ + Path[0] = FACING_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Take_Damage -- Handles taking damage to this object. * + * * + * This routine intercepts the damage assigned to this object and if this object is * + * a member of a team, it informs the team that the damage has occurred. The team may * + * change it's priority or action based on this event. * + * * + * INPUT: damage -- The damage points inflicted on the unit. * + * * + * distance -- The distance from the point of damage to the unit itself. * + * * + * warhead -- The type of damage that is inflicted. * + * * + * source -- The perpetrator of the damage. By knowing who caused the damage, * + * the team know's who to "get even with". * + * * + * OUTPUT: Returns with the result type of the damage. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/30/1994 JLB : Created. * + *=============================================================================================*/ +ResultType FootClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(IsActive); + + ResultType result = TechnoClass::Take_Damage(damage, distance, warhead, source, forced); + + if (result != RESULT_NONE && Team) { + + Team->Took_Damage(this, result, source); + + } else { + + if (result != RESULT_DESTROYED && result != RESULT_NONE) { + + /* + ** Determine if the target that is currently being attacked has a weapon that can + ** do harm to a ground based unit. This information is needed so that an appropriate + ** response will occur when damage is taken. + */ +// bool tweap = false; +// if (As_Techno(TarCom)) { +// tweap = (As_Techno(TarCom)->Techno_Type_Class()->PrimaryWeapon != NULL); +// } + + /* + ** This ensures that if a unit is in sticky mode, then it will snap out of + ** it when it takes damage. + */ + if (source != NULL && MissionControl[Mission].IsNoThreat && !MissionControl[Mission].IsZombie) { + Enter_Idle_Mode(); + } + + /* + ** If this object is not part of a team and it can retaliate for the damage, then have + ** it try to do so. This prevents it from just sitting there and taking damage. + */ + if (Is_Allowed_To_Retaliate(source)) { + + int primary = What_Weapon_Should_I_Use(source->As_Target()); + if (In_Range(source, primary) || !House->IsHuman) { + Assign_Target(source->As_Target()); + } + + if (Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + /* + ** Simple retaliation cannot occur because the source of the damage + ** is too far away. If scatter logic is enabled, then scatter now. + */ + if (!Target_Legal(TarCom) && !Target_Legal(NavCom) && Rule.IsScatter) { + Scatter(0, true); + } + + } else { + + /* + ** If this object isn't doing anything important, then scatter. + */ + if (MissionControl[Mission].IsScatter && !IsTethered && !IsDriving && !Target_Legal(TarCom) && !Target_Legal(NavCom) && What_Am_I() != RTTI_AIRCRAFT && What_Am_I() != RTTI_VESSEL) { + if (!House->IsHuman || Rule.IsScatter) { + Scatter(0, true); + } + } + } + } + } + return(result); +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Initiates attack or move according to target clicked on. * + * * + * At this level, the object is known to have the ability to attack or move to the * + * target specified (in theory). Perform the attack or move as indicated. * + * * + * INPUT: target -- The target clicked upon that will precipitate action. * + * * + * OUTPUT: Returns with the type of action performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/06/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(IsActive); + assert(object != NULL); + + switch (action) { + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + if (What_Am_I() == RTTI_INFANTRY && + ((InfantryClass *)this)->Class->IsBomber && + object->What_Am_I() == RTTI_BUILDING && + !House->Is_Ally(object)) { + + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } else { + Player_Assign_Mission(MISSION_GUARD_AREA, object->As_Target()); + } + } + break; + + case ACTION_SELF: + Player_Assign_Mission(MISSION_UNLOAD); + break; + + case ACTION_ATTACK: + if (Can_Player_Fire()) { + Player_Assign_Mission(MISSION_ATTACK, object->As_Target()); + } + break; + + case ACTION_ENTER: + if (Can_Player_Move() && object && object->Is_Techno() /*&& !((RadioClass *)object)->In_Radio_Contact()*/) { + Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_SABOTAGE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, object->As_Target()); + } + break; + + case ACTION_NOMOVE: + case ACTION_MOVE: + if (Can_Player_Move()) { + + TARGET targ = object->As_Target(); + + /* + ** If the destination object is not the same zone, then pick a nearby location. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT && Techno_Type_Class()->Speed != SPEED_WINGED && Map[Coord].Zones[Techno_Type_Class()->MZone] != Map[object->Center_Coord()].Zones[Techno_Type_Class()->MZone]) { + +#ifdef FIXIT_MINE_PASSABLE + // Fixes units not driving onto mines. + if (Can_Enter_Cell(Coord_Cell(object->Center_Coord())) > MOVE_OK) { + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); + } +#else + targ = ::As_Target(Map.Nearby_Location(Coord_Cell(object->Center_Coord()), Techno_Type_Class()->Speed, Map[Coord].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, targ); + } + break; + + case ACTION_NO_DEPLOY: + Speak(VOX_DEPLOY); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * FootClass::Active_Click_With -- Performs action as a result of left mouse click. * + * * + * This routine performs the action requested when the left mouse button was clicked over * + * a cell. Typically, this is just a move command. * + * * + * INPUT: action -- The predetermined action that should occur. * + * * + * cell -- The cell number that the action should occur at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(IsActive); + + action = What_Action(cell); + switch (action) { + case ACTION_HARVEST: + Player_Assign_Mission(MISSION_HARVEST, TARGET_NONE, ::As_Target(cell)); + break; + + case ACTION_MOVE: + if (AllowVoice) { + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + // Fall into next case. + + case ACTION_NOMOVE: + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (What_Am_I() != RTTI_AIRCRAFT || Map[cell].Is_Visible(PlayerPtr)) { + + /* + ** Find the closest same-zoned cell to where the unit currently is. + ** This will allow the unit to come as close to the destination cell + ** as is reasonably possible, when clicking on an impassable cell + ** (as is likely when clicking in the shroud.) It looks for the + ** nearest cell using an expanding-radius box, and ignores cells + ** off the edge of the map. + */ + CellClass const * cellptr = &Map[::As_Cell(::As_Target(Center_Coord()))]; + if (What_Am_I() != RTTI_AIRCRAFT) { + + if (Can_Enter_Cell(Coord_Cell(Center_Coord())) == MOVE_OK) { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); + } else { + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed); + } +#ifdef OBSOLETE + cell = Map.Nearby_Location(cell, Techno_Type_Class()->Speed, cellptr->Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone); +#endif + } + + Player_Assign_Mission(MISSION_MOVE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_ATTACK: + Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); + break; + + /* + ** Engineer attempting to capture bridge to repair it + */ + case ACTION_CAPTURE: + if (Can_Player_Move()) { + Player_Assign_Mission(MISSION_CAPTURE, TARGET_NONE, ::As_Target(cell)); + } + break; + + case ACTION_SABOTAGE: + Player_Assign_Mission(MISSION_SABOTAGE, TARGET_NONE, ::As_Target(cell) ); + break; + + // MBL 05.15.2020 - Adding support for CTRL+ALT clicking the ground to have units move to an area and guard it + case ACTION_GUARD_AREA: + if (Can_Player_Fire() && Can_Player_Move()) { + Player_Assign_Mission(MISSION_GUARD_AREA, ::As_Target(cell)); + } + break; + // END MBL 05.15.2020 + } +} + + +/*********************************************************************************************** + * FootClass::Per_Cell_Process -- Perform action based on once-per-cell condition. * + * * + * This routine is called as this object moves from cell to cell. When the center of the * + * cell is reached, check to see if any trigger should be sprung. For moving units, reduce * + * the path to the distance to the target. This forces path recalculation in an effort to * + * avoid units passing each other. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/08/1995 JLB : Handles generic enter trigger event. * + * 07/16/1995 JLB : If next to a scanner and cloaked, then shimmer. * + *=============================================================================================*/ +void FootClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + + IsScattering = false; + + /* + ** Clear any unloading flag if necessary. + */ + IsUnloading = false; + + /* + ** If adjacent to an enemy techno that has the ability to reveal a sub, + ** then shimmer the cloaked object. + */ + if (Cloak == CLOAKED) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Adjacent_Cell(Coord_Cell(Coord), face); + + if (Map.In_Radar(cell)) { + TechnoClass const * techno = Map[cell].Cell_Techno(); + + if (techno && !techno->House->Is_Ally(this) && techno->Techno_Type_Class()->IsScanner) { + Do_Shimmer(); + break; + } + } + } + } + + /* + ** Shorten the path if the target is now within weapon range of this + ** unit and this unit is on an attack type mission. But only if the target + ** is slow enough for leading to make sense. + */ + if (Target_Legal(TarCom) && (What_Am_I() != RTTI_INFANTRY || !((InfantryClass *)this)->Class->IsDog)) { + int primary = What_Weapon_Should_I_Use(TarCom); + bool inrange = In_Range(TarCom, primary); + TechnoClass const * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Foot()) { + FootClass const * foot = (FootClass const *)techno; + MPHType speed = ((TechnoTypeClass const &)techno->Class_Of()).MaxSpeed; + COORDINATE rangecoord = (speed > MPH_SLOW) ? foot->Likely_Coord() : foot->Target_Coord(); + inrange = In_Range(rangecoord, primary); + } + + if ((Mission == MISSION_RESCUE || Mission == MISSION_GUARD_AREA || Mission == MISSION_ATTACK || Mission == MISSION_HUNT) && inrange) { + Assign_Destination(TARGET_NONE); + Path[0] = FACING_NONE; + } + } + + /* + ** Trigger event associated with the player entering the cell. + */ + if (Cloak != CLOAKED) { + TriggerClass * trigger = Map[Coord].Trigger; + if (trigger != NULL) { + trigger->Spring(TEVENT_PLAYER_ENTERED, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + + /* + ** Check for horizontal trigger crossing. + */ + int x = Cell_X(Coord_Cell(Coord)); + int y = Cell_Y(Coord_Cell(Coord)); + int index; + for (index = 0; index < Map.MapCellWidth; index++) { + trigger = Map[XY_Cell(index+Map.MapCellX, y)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_HORIZONTAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_HORIZONTAL)) { + trigger->Spring(TEVENT_CROSS_HORIZONTAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for vertical trigger crossing. + */ + for (index = 0; index < Map.MapCellHeight; index++) { + trigger = Map[XY_Cell(x, index+Map.MapCellY)].Trigger; + if (trigger != NULL) { + if (trigger->Class->Event1.Event == TEVENT_CROSS_VERTICAL || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_CROSS_VERTICAL)) { + trigger->Spring(TEVENT_CROSS_VERTICAL, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** Check for zone entry trigger events. + */ + for (MapTriggerID = 0; MapTriggerID < MapTriggers.Count(); MapTriggerID++) { + trigger = MapTriggers[MapTriggerID]; + if (trigger->Class->Event1.Event == TEVENT_ENTERS_ZONE || (trigger->Class->EventControl != MULTI_ONLY && trigger->Class->Event2.Event == TEVENT_ENTERS_ZONE)) { + if (Map[trigger->Cell].Zones[Techno_Type_Class()->MZone] == Map[Coord].Zones[Techno_Type_Class()->MZone]) { + trigger->Spring(TEVENT_ENTERS_ZONE, this, Coord_Cell(Coord)); + if (!IsActive) return; + } + } + } + + /* + ** If any of these triggers cause this unit to be destroyed, then + ** stop all further processing for this unit. + */ + if (!IsActive) return; + } + +#ifdef OBSOLETE + /* + ** Flag any gap generators to re-draw + */ + for (int index = 0; index IsInLimbo && (HouseClass *)obj->House != PlayerPtr) { + int dist = Distance(obj) / CELL_LEPTON_W; + if (dist < (6 + Rule.GapShroudRadius) ) { + // if (dist < (6 + obj->Class->SightRange) ) { + obj->IsJamming = false; // lie so it'll re-jam now + } + } + } +#endif + + } + + TechnoClass::Per_Cell_Process(why); +} + + +/*************************************************************************** + * FootClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void FootClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedNavCom = NavCom; + TechnoClass::Override_Mission(mission, tarcom, navcom); + + Assign_Destination(navcom); +} + + +/*************************************************************************** + * FootClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool FootClass::Restore_Mission(void) +{ + assert(IsActive); + + if (TechnoClass::Restore_Mission()) { + Assign_Destination(SuspendedNavCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FootClass::Receive_Message -- Movement related radio messages are handled here. * + * * + * This routine handles radio message that are related to movement. These are used for * + * complex coordinated maneuvers. * + * * + * INPUT: from -- Pointer to the originator of this radio message. * + * * + * message -- The radio message that is being received. * + * * + * param -- The optional parameter (could be a movement destination). * + * * + * OUTPUT: Returns with the radio response appropriate to the message received. Usually the * + * response is RADIO_ROGER. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Answers if this object is located on top of a service depot. + */ + case RADIO_ON_DEPOT: + if (Map[Center_Coord()].Cell_Building() != NULL) { + BuildingClass const * building = Map[Center_Coord()].Cell_Building(); + if (*building == STRUCT_REPAIR) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** Intercept the repair request and if this object is moving, then no repair + ** is possible. + */ + case RADIO_REPAIR: + if (Target_Legal(NavCom)) return(RADIO_NEGATIVE); + break; + + /* + ** Something bad has happened to the object in contact with. Abort any coordinated + ** activity with this object. Basically, ... run away! Run away! + */ + case RADIO_RUN_AWAY: + if (In_Radio_Contact()) { + if (NavCom == Contact_With_Whom()->As_Target()) { + Assign_Destination(TARGET_NONE); + } + } + if (Mission == MISSION_SLEEP) { + Assign_Mission(MISSION_GUARD); + Commence(); + } + if (Mission == MISSION_ENTER) { + Assign_Mission(MISSION_GUARD); + } + if (!IsRotating && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } + break; + + /* + ** Checks to see if this unit needs to move somewhere. If it is already in motion, + ** then it doesn't need further movement instructions. + */ + case RADIO_NEED_TO_MOVE: + param = (long)NavCom; + if (!Target_Legal(NavCom)) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** Radio request to move to location specified. Typically this is used + ** for complex loading and unloading missions. + */ + case RADIO_MOVE_HERE: + if (NavCom != (TARGET)param) { + if (::As_Target(Coord_Cell(Coord)) == (TARGET)param) { + return(RADIO_YEA_NOW_WHAT); + } else { + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE) { + Assign_Mission(MISSION_MOVE); + } + Assign_Destination((TARGET)param); + Shorten_Mission_Timer(); + } + } + return(RADIO_ROGER); + + /* + ** Requests if this unit is trying to cooperatively load up. Typically, this occurs + ** for passengers and when vehicles need to be repaired. + */ + case RADIO_TRYING_TO_LOAD: + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + TechnoClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + } + break; + } + return(TechnoClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * FootClass::Mission_Enter -- Enter (cooperatively) mission handler. * + * * + * This mission handler will cooperatively coordinate the object to maneuver into the * + * object it is in radio contact with. This is used by infantry when they wish to load * + * into an APC as well as by vehicles when they wish to enter a repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 JLB : Created. * + * 09/22/1995 JLB : Modified to handle the "on hold" condition. * + *=============================================================================================*/ +int FootClass::Mission_Enter(void) +{ + assert(IsActive); + + /* + ** Find out who to coordinate with. If in radio contact, then this the transporter is + ** defined. If not in radio contact, then try the archive target value to see if that + ** is suitable. + */ + TechnoClass * contact = Contact_With_Whom(); + if (contact == NULL) { + contact = As_Techno(ArchiveTarget); + } + + /* + ** If in contact, then let the transporter handle the movement coordination. + */ + if (contact != NULL) { + + /* + ** If the transport says to "bug off", then abort the enter mission. The transport may + ** likely say all is 'ok' with the "RADIO ROGER", then try again later. + */ + if (Transmit_Message(RADIO_DOCKING, contact) != RADIO_ROGER && !IsTethered) { + Transmit_Message(RADIO_OVER_OUT); + Enter_Idle_Mode(); + } + + } else { + + /* + ** Since there is no potential object to enter, then abort this + ** mission with some default standby mission. + */ + Enter_Idle_Mode(); + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * FootClass::Assign_Destination -- Assigns specified destination to NavCom. * + * * + * This routine will assign the specified target to the navigation computer. No legality * + * checks are performed. * + * * + * INPUT: target -- The target value to assign to the navigation computer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + NavCom = target; + + /* + ** Presume that the easiest path is tried first. As the findpath proceeds, when + ** a failure occurs, this threshhold will be increased until path failure + ** cannot be prevent. At this point, all movement should cease. + */ + PathThreshhold = MOVE_CLOAK; +} + + +/*********************************************************************************************** + * FootClass::Detach_All -- Removes this object from the game system. * + * * + * This routine will remove this object from the game system. This routine is called when * + * this object is about to be deleted. All other objects should no longer reference this * + * object in that case. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Detach_All(bool all) +{ + assert(IsActive); + + if (Team && !ScenarioInit) { + Team->Remove(this); + Team = NULL; + } + + TechnoClass::Detach_All(all); +} + + +/*********************************************************************************************** + * FootClass::Rescue_Mission -- Calls this unit to the rescue. * + * * + * This routine is called when the house determines that it should attack the specified * + * target. This routine will determine if it can attack the target specified and if so, * + * the amount of power it can throw at it. This returned power value is used to allow * + * intelligent distribution of retaliation. * + * * + * INPUT: target -- The target that this object just might be assigned to attack and thus * + * how much power it can bring to bear should be returned. * + * * + * OUTPUT: Returns with the amount of power that this object can bring to bear against the * + * potential target specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Rescue_Mission(TARGET tarcom) +{ + assert(IsActive); + + /* + ** If the target specified is not legal, then it cannot be attacked. Always return + ** zero in this case. + */ + if (!Target_Legal(tarcom)) return(0); + + /* + ** If the unit is already assigned to destroy the tarcom then we need + ** to return a negative value which tells the computer to lower the + ** desired threat rating. + */ + if (TarCom == tarcom) { + return(-Risk()); + } + + /* + ** If the unit is currently attacking a target that has a weapon then we + ** cannot abandon it as it will destroy us if we return to base. + */ + if (Target_Legal(TarCom)) { + TechnoClass * techno = As_Techno(TarCom); + if (techno != NULL && techno->Is_Weapon_Equipped()) { + return(0); + } + } + + /* + ** If the unit is in a harvest mission or is currently attacking + ** something, or is not very effective, then it will be of no help + ** at all. + */ + if (Team.Is_Valid() || Mission == MISSION_HARVEST || !Risk()) { + return(0); + } + + /* + ** Find the distance to the target modified by the range. If the + ** the distance is 0, then things are ok. + */ + int dist = Distance(tarcom) - Weapon_Range(0); + int threat = Risk() * 1024; + int speed = -1; + if (dist > 0) { + + /* + ** Next we need to figure out how fast the unit moves because this + ** decreases the distance penalty. + */ + speed = max((unsigned)Techno_Type_Class()->MaxSpeed, (unsigned)1); + + int ratio = (speed > 0) ? Max(dist / speed, 1) : 1; + + /* + ** Finally modify the threat by the distance the unit is away. + */ + threat = max(threat/ratio, 1); + } + return(threat); +} + + +/*********************************************************************************************** + * FootClass::Death_Announcement -- Announces the death of a unit. * + * * + * This routine is called when a unit (infantry, vehicle, or aircraft) is destroyed. * + * * + * INPUT: source -- The perpetrator of this death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Death_Announcement(TechnoClass const * ) const +{ + assert(IsActive); + + //if (IsOwnedByPlayer) { + if ((Session.Type == GAME_GLYPHX_MULTIPLAYER && House->IsHuman) || (Session.Type != GAME_GLYPHX_MULTIPLAYER && IsOwnedByPlayer)) { + if (What_Am_I() == RTTI_VESSEL) { + // Speak(VOX_SHIP_LOST); // MBL 02.06.2020 + Speak(VOX_SHIP_LOST, House, Center_Coord()); + } else { + // Speak(VOX_UNIT_LOST); // MBL 02.06.2020 + Speak(VOX_UNIT_LOST, House, Center_Coord()); + } + } +} + + +/*********************************************************************************************** + * FootClass::Greatest_Threat -- Fetches the greatest threat to this object. * + * * + * This routine will return with the greatest threat (best target) for this object. For * + * movable ground object, they won't automatically return ANY target if this object is * + * cloaked. Otherwise, cloaking is relatively useless. * + * * + * INPUT: method -- The request method (bit flags) to use when scanning for a target. * + * * + * OUTPUT: Returns with the best target to attack. If there is no target that qualifies, then * + * TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 07/10/1996 JLB : Handles scan range limitation. * + *=============================================================================================*/ +TARGET FootClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + /* + ** If the scan is forced to be limited, then limit the scan now. + */ + if (IsScanLimited) { + method = method & ~THREAT_AREA; + method = method | THREAT_RANGE; + } + + /* + ** If this object can cloak, then it won't select a target automatically. + */ + if (House->IsHuman && IsCloakable && Mission == MISSION_GUARD) { + return(TARGET_NONE); + } + + if (!(method & (THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BUILDINGS|THREAT_TIBERIUM|THREAT_BOATS|THREAT_CIVILIANS|THREAT_POWER|THREAT_FAKES|THREAT_FACTORIES|THREAT_BASE_DEFENSE))) { + if (What_Am_I() != RTTI_VESSEL) { + method = method | THREAT_GROUND; + } else { + method = method | THREAT_BOATS|THREAT_GROUND; + } + } + + /* + ** Perform the search for the target. + */ + TARGET target = TechnoClass::Greatest_Threat(method); + + /* + ** If no target could be located and this object is under scan range + ** restrictions, then this restriction must be lifted now. + */ + if (IsScanLimited && target == TARGET_NONE) { + const_cast(this)->IsScanLimited = false; // const_cast ST - 5/8/2019 + } + + /* + ** Return with final target found. + */ + return(target); +} + + +/*********************************************************************************************** + * FootClass::Detach -- Detaches a target from tracking systems. * + * * + * This routine will detach the specified target from the tracking systems of this object. * + * It will be removed from the navigation computer and any queued mission record. * + * * + * INPUT: target -- The target to be removed from this object. * + * * + * all -- Is the unit really about to be eliminated? If this is true then even * + * friendly contact (i.e., radio) must be eliminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 07/24/1996 JLB : Removes target from NavQueue list. * + *=============================================================================================*/ +void FootClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + + TechnoClass::Detach(target, all); + + if (!SpecialFlag) { + if (ArchiveTarget == target) { + ArchiveTarget = TARGET_NONE; + } + } + + if (SuspendedNavCom == target) { + SuspendedNavCom = TARGET_NONE; + SuspendedMission = MISSION_NONE; + } + + /* + ** If the navigation computer is assigned to the target, then the navigation + ** computer must be cleared. + */ + if (NavCom == target) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + Restore_Mission(); + } + + /* + ** Remove the target from the NavQueue list as well. + */ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == target) { + NavQueue[index] = TARGET_NONE; + if (index < ARRAY_SIZE(NavQueue)-1) { + memmove(&NavQueue[index], &NavQueue[index+1], ((ARRAY_SIZE(NavQueue)-index)-1) * sizeof(NavQueue[0])); + index--; + } + } + } + + /* + ** If targeting the specified object and this unit is obviously heading + ** toward the target to get within range, then abort the path. + */ + if (TarCom == target && House->IsHuman) { + Path[0] = FACING_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Offload_Tiberium_Bail -- Fetches the Tiberium to offload per step. * + * * + * This routine is called when a packet/package/bail of Tiberium needs to be offloaded * + * from the object. This function is overridden for those objects that can contain * + * Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded from the object. * + * * + * WARNINGS: This routine must be called multiple times in order to completely offload the * + * Tiberium. When this routine return 0, all Tiberium has been offloaded. * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int FootClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * FootClass::Can_Enter_Cell -- Checks to see if the object can enter cell specified. * + * * + * This routine examines the specified cell to see if the object can enter it. This * + * function is to be overridden for objects that could have the possibility of not being * + * allowed to enter the cell. Typical objects at the FootClass level always return * + * MOVE_OK. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The direction that this cell might be entered from. * + * * + * OUTPUT: Returns with the move check result type. This will be MOVE_OK if there is not * + * blockage. There are various other values that represent other blockage types. * + * The value returned will indicated the most severe reason why entry into the cell * + * is blocked. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +MoveType FootClass::Can_Enter_Cell(CELL , FacingType) const +{ + assert(IsActive); + + return MOVE_OK; +} + + +/*********************************************************************************************** + * FootClass::Can_Demolish -- Checks to see if this object can be sold back. * + * * + * This routine determines if it is legal to sell the object back. A foot class object can * + * only be sold back if it is sitting on a repair bay. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully sold back? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Can_Demolish(void) const +{ + assert(IsActive); + + StructType sell_struct = STRUCT_NONE; + switch (What_Am_I()) { + case RTTI_UNIT: + sell_struct = STRUCT_REPAIR; + break; + case RTTI_AIRCRAFT: + sell_struct = STRUCT_AIRSTRIP; + break; + default: + break; + } + if (sell_struct != STRUCT_NONE) { + if (In_Radio_Contact() && + Contact_With_Whom()->What_Am_I() == RTTI_BUILDING && + *((BuildingClass *)Contact_With_Whom()) == sell_struct && + Distance(Contact_With_Whom()) < 0x0080) { + return(true); + } + } + return(TechnoClass::Can_Demolish()); +} + + +/*********************************************************************************************** + * FootClass::Sell_Back -- Causes this object to be sold back. * + * * + * When an object is sold back, a certain amount of money is refunded to the owner and then * + * the object is removed from the game system. * + * * + * INPUT: control -- The action to perform. The only supported action is "1", which means * + * to sell back. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +void FootClass::Sell_Back(int control) +{ + assert(IsActive); + + if (control != 0) { + if (House == PlayerPtr) { + Speak(VOX_UNIT_SOLD); + Sound_Effect(VOC_CASHTURN); + } + House->Refund_Money(Refund_Amount()); + Stun(); + Limbo(); + delete this; + } +} + + +/*********************************************************************************************** + * FootClass::Likely_Coord -- Fetches the coordinate the object will be at shortly. * + * * + * This routine comes in handy when determining where a travelling object will be at * + * when considering the amount of time it would take for a normal unit to travel one cell. * + * Using this information, an intelligent "approach target" logic can be employed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate the object is at or soon will be. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE FootClass::Likely_Coord(void) const +{ + assert(IsActive); + + if (Head_To_Coord()) { + return(Head_To_Coord()); + } + return(Target_Coord()); +} + + +/*********************************************************************************************** + * FootClass::Adjust_Dest -- Adjust candidate movement cell to account for formation. * + * * + * This routine modify the specified cell if the unit is part of a formation. The * + * adjustment will take into consideration the formation relative offset from the * + * (presumed) center cell specified. * + * * + * INPUT: cell -- The cell to presume as the desired center point of the formation. * + * * + * OUTPUT: Returns with the cell that should be used as the actual destination. If this * + * object is part of a formation, then the cell location will be appropriately * + * adjusted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +CELL FootClass::Adjust_Dest(CELL cell) const +{ + assert(IsActive); + + if (IsFormationMove) { + int xdest = Cell_X(cell); + int ydest = Cell_Y(cell); + + int newx = Bound(XFormOffset + xdest, Map.MapCellX, Map.MapCellX + Map.MapCellWidth -1); + int newy = Bound(YFormOffset + ydest, Map.MapCellY, Map.MapCellY + Map.MapCellHeight -1); + + cell = XY_Cell(newx, newy); + } + return(cell); +} + + +/*********************************************************************************************** + * FootClass::Handle_Navigation_List -- Processes the navigation queue. * + * * + * This routine will process the navigation queue. If the queue is present and valid and * + * there is currently no navigation target assigned to this object, then the first entry * + * of the queue will be assigned. The remaining entries will move down. If the queue is * + * to be processed as a circular list, then the first entry is appended to the end. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine might end up assigning a movement destination. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Handle_Navigation_List(void) +{ + /* + ** The navigation queue only needs to be processed if there is + ** currently no navigation target for this object. + */ + if (!Target_Legal(NavCom)) { + TARGET target = NavQueue[0]; + + /* + ** Check to see if the navigation queue even exists and + ** has at least one valid entry. If it does, then process it by + ** assigning the object's NavCom to the first entry on the list. + */ + if (Target_Legal(target)) { + Assign_Destination(target); + memmove(&NavQueue[0], &NavQueue[1], sizeof(NavQueue)-sizeof(NavQueue[0])); + NavQueue[ARRAY_SIZE(NavQueue)-1] = TARGET_NONE; + + /* + ** If the navigation queue is to loop (indefinately), then append the + ** target value from the first part to the end of the queue. + */ + if (IsNavQueueLoop) { + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + if (NavQueue[index] == TARGET_NONE) { + NavQueue[index] = target; + break; + } + } + } + } + } +} + + +/*********************************************************************************************** + * FootClass::Queue_Navigation_List -- Add a target to the objects navigation list. * + * * + * This routine will append the destination target to the object's NavQueue list. After * + * doing so, if the object is not doing anything important, then it will be started on * + * that destination. This is functionally the same as Assign_Destination, but it stores * + * the target to the NavQueue first. * + * * + * INPUT: target -- The movement target destination to append the queue. * + * * + * OUTPUT: none * + * * + * WARNINGS: The queue is of finite size and any queue requests that would exceed that size * + * are ignored. If there are no queue entries pending and the unit is not * + * otherwise occupied, then the queue target might be carried directly into the * + * NavCom. * + * * + * HISTORY: * + * 07/18/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Queue_Navigation_List(TARGET target) +{ + if (Target_Legal(target)) { + int count; + for (count = 0; count < ARRAY_SIZE(NavQueue); count++) { + if (!Target_Legal(NavQueue[count])) break; + } + + /* + ** If the target is this object itself, then this indicates that the + ** queue list is to be processed as a loop. Otherwise, just tack the + ** navigation target to the end of the list. + */ + if (target == As_Target() && count > 0) { + IsNavQueueLoop = true; + } else { + if (count == 0) { + IsNavQueueLoop = false; + } + if (count < ARRAY_SIZE(NavQueue)) { + NavQueue[count] = target; + } + } + + /* + ** If this object isn't doing anything, then start acting on the + ** navigation queue now. + */ + if (!Target_Legal(NavCom) && Mission == MISSION_GUARD) { + Enter_Idle_Mode(); + } + } +} + + +/*********************************************************************************************** + * FootClass::Clear_Navigation_List -- Clears out the navigation queue. * + * * + * This routine will clear out any values in the navigation queue. This is the preferred * + * way of aborting a navigation queue for a unit. If the unit is already travelling, it * + * won't be interrupted by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This will clear the navigation list but not the navigation computer. Thus a * + * unit will still travel to its current immediate destination. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::Clear_Navigation_List(void) +{ + for (int index = 0; index < ARRAY_SIZE(NavQueue); index++) { + NavQueue[index] = TARGET_NONE; + } +} + + +/*********************************************************************************************** + * FootClass::Is_Allowed_To_Leave_Map -- Checks to see if it can leave the map and the game. * + * * + * This routine will determine if this object has permission to leave the map and thus * + * leave the game. Typical objects with this permission are transports used to drop of * + * reinforcements. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this object have permission to travel off the map edge and leave the * + * game? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Allowed_To_Leave_Map(void) const +{ + /* + ** If the unit hasn't entered the map yet, then don't allow leave the game. + */ + if (!IsLocked) return(false); + + /* + ** A unit that isn't marked as a loaner is a gift to the player. Such objects can never + ** leave the map unless they are part of a team that gives it special permision. + */ + if (!IsALoaner && Mission != MISSION_RETREAT && (!Team.Is_Valid() || !Team->Is_Leaving_Map())) return(false); + + return(true); +} + + +/*********************************************************************************************** + * FootClass::Is_Recruitable -- Determine if this object is recruitable as a team members. * + * * + * This will examine this object to determine if it is suitable as a team recruit. Some * + * objects are disqualified if they are otherwise premptively occupied. * + * * + * INPUT: house -- Pointer to the house that is trying to recruit this object. * + * * + * OUTPUT: bool; Is this object suitable for recruitment by a team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_Recruitable(HouseClass const * house) const +{ + /* + ** If not of the correct house presuasion, then recruitment is not allowed. + */ + if (house != NULL && house != House) { + return(false); + } + + /* + ** If the object is not a playing member of the game, then don't consider it available. + */ + if (IsInLimbo) { + return(false); + } + + /* + ** If it is already part of another team, then it is not available for + ** general recruitment. + */ + if (Team.Is_Valid()) { + return(false); + } + + /* + ** If it is currently in a mission the precludes recruitment into a team, then + ** return with this information. + */ + if (!Is_Recruitable_Mission(Mission)) { + return(false); + } + + /* + ** It was not disqualified for general team recruitment, so return that + ** it is available. + */ + return(true); +} + + +/*********************************************************************************************** + * FootClass::AI -- Handle general movement AI. * + * * + * This basically just sees if this object is within weapon range of the target and if * + * so, it will stop movement so that firing may commence. This prevents the occasional * + * case of an attacker driving right up to the defender before firing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void FootClass::AI(void) +{ + TechnoClass::AI(); + +// FootClass::Per_Cell_Process does this function already. +#ifdef OBSOLETE + if (IsActive) { + if (!IsScattering && !IsTethered && !IsInLimbo && What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && In_Range(TarCom)) { + Assign_Destination(TARGET_NONE); + } + } +#endif +} + + +/*********************************************************************************************** + * FootClass::Is_On_Priority_Mission -- Checks to see if this object should be given priority. * + * * + * Some objects are on an important mission that must succeed. If the object is on such * + * a mission, then it will be more aggressive in its movement action. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object on a priority mission? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +bool FootClass::Is_On_Priority_Mission(void) const +{ + if (Mission == MISSION_ENTER) return(true); + return(false); +} + + +/*********************************************************************************************** + * FootClass::Mission_Retreat -- Handle reatreat from map mission for mobile objects. * + * * + * This will try to make this mobile object leave the map. It does this by assigning a * + * movement destination that is located off the edge of the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1996 JLB : Created. * + *=============================================================================================*/ +int FootClass::Mission_Retreat(void) +{ + assert(IsActive); + + enum { + FIND_EDGE, + TRAVELLING + }; + + switch (Status) { + + /* + ** Find a suitable edge to travel to and then assign destination there. + */ + case FIND_EDGE: + if (Target_Legal(NavCom)) { + Status = TRAVELLING; + } else { + + CELL cell = 0; + + /* + ** If this is part of a team, then pick the edge where the team as likely + ** entered from. + */ + if (Team.Is_Valid() && Team->Class->Origin != -1) { + cell = Map.Calculated_Cell(House->Control.Edge, Team->Class->Origin, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + /* + ** If an edge hasn't been found, then try to find one that is not based on any + ** team information. + */ + if (cell == 0) { + cell = Map.Calculated_Cell(House->Control.Edge, -1, Coord_Cell(Center_Coord()), Techno_Type_Class()->Speed); + } + + assert(cell == 0); // An edge cell must be found! + + Assign_Destination(::As_Target(cell)); + Status = TRAVELLING; + } + break; + + /* + ** While travelling, monitor that all is proceeding according to plan. + */ + case TRAVELLING: + if (!Target_Legal(NavCom)) { + Status = FIND_EDGE; + } + break; + } + + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} diff --git a/code/redalert/foot.h b/code/redalert/foot.h new file mode 100644 index 0000000..244833b --- /dev/null +++ b/code/redalert/foot.h @@ -0,0 +1,389 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FOOT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FOOT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FOOT_H +#define FOOT_H + +#include "target.h" +#include "type.h" +#include "techno.h" +#include "ftimer.h" + +class UnitClass; +class BuildingClass; + + +/**************************************************************************** +** Movable objects are handled by this class definition. Moveable objects +** cover everything except buildings. +*/ +class FootClass : public TechnoClass +{ + public: + /* + ** This flag controls whether a range limiting effect should be in place. If + ** true, then target scanning will be limited to the range of the object + ** regardless of what was requested from the target scanning logic. This value + ** is used for ships so that they won't permanently stick on a an attack mission + ** for a target they can never get within range of. This value will toggle when + ** a path cannot be generated and the target is not within range. It will also + ** toggle when path limiting is true, but there is not target found within + ** the limited range. + */ + unsigned IsScanLimited:1; + + /* + ** If this unit has officially joined the team's group, then this flag is + ** true. A newly assigned unit to a team is not considered part of the + ** team until it actually reaches the location where the team is. By + ** using this flag, it allows a team to continue to intelligently attack + ** a target without falling back to regroup the moment a distant member + ** joins. + */ + unsigned IsInitiated:1; + + /* + ** When the player gives this object a navigation target AND that target + ** does not result in any movement of the unit, then a beep should be + ** sounded. This typically occurs when selecting an invalid location for + ** movement. This flag is cleared if any movement was able to be performed. + ** It never gets set for computer controlled units. + */ + unsigned IsNewNavCom:1; + + /* + ** There are certain cases where a unit should perform a full scan rather than + ** the more efficient "ring scan". This situation occurs when a unit first + ** appears on the map or when it finishes a multiple cell movement track. + */ + unsigned IsPlanningToLook:1; + + /* + ** Certain units have the ability to metamorphize into a building. When this + ** operation begins, certain processes must occur. During these operations, this + ** flag will be true. This ensures that any necessary special case code gets + ** properly executed for this unit. + */ + unsigned IsDeploying:1; + + /* + ** This flag tells the system that the unit is doing a firing animation. This is + ** critical to the firing logic. + */ + unsigned IsFiring:1; + + /* + ** This unit could be either rotating its body or rotating its turret. During the + ** process of rotation, this flag is set. By examining this flag, unnecessary logic + ** can be avoided. + */ + unsigned IsRotating:1; + + /* + ** If this object is current driving to a short range destination, this flag is + ** true. A short range destination is either the next cell or the end of the + ** current "curvy" track. An object that is driving is not allowed to do anything + ** else until it reaches its destination. The exception is when infantry wish to + ** head to a different destination, they are allowed to start immediately. + */ + unsigned IsDriving:1; + + /* + ** If this object is unloading from a hover transport, then this flag will be + ** set to true. This handles the unusual case of an object disembarking from the + ** hover lander yet not necessarily tethered but still located in an overlapping + ** position. This flag will be cleared automatically when the object moves to the + ** center of a cell. + */ + unsigned IsUnloading:1; + + /* + ** If this object is part of a formation, this bit will be set. The + ** formation only occurs when every member of a team is selected, and + ** only those members of the team are the ones selected. + */ + unsigned IsFormationMove:1; + + /* + ** If the navigation movement queue is to be looped rather than consumed, then + ** this flag will be true. By looping, the unit will travel through the locations + ** in the queue indefinately. + */ + unsigned IsNavQueueLoop:1; + + /* + ** If this object is scattering, then this flag will be true. While true, the + ** NavCom should not be arbitrarily changed. This flag will automatcially be + ** cleared when the object moves one cell. + */ + unsigned IsScattering:1; + + /* + ** If this object is moving onto a bridge, then this flag will be true. + */ + unsigned IsMovingOntoBridge:1; + + /* + ** This is the "throttle setting" of the unit. It is a fractional value with 0 = stop + ** and 255 = full speed. + */ + int Speed; + + /* + ** This is the override speed adjuster. Normally, this is a fixed point + ** value of 0x0100, but it can be modified by crate powerups. + */ + fixed SpeedBias; + + /* + ** For units in a formation, these values represent the distance from + ** the target destination where the unit should move to. For example, + ** in a horizontal line formation, XFormOffset would be set to some + ** value, and YFormOffset would be zero. + */ + int XFormOffset; + int YFormOffset; + + /* + ** + ** This is the desired destination of the unit. The unit will attempt to head + ** toward this target (avoiding intervening obstacles). + */ + TARGET NavCom; + TARGET SuspendedNavCom; + + /* + ** A sequence of move destinations can be given to a unit. The sequence is + ** stores as an array of movement targets. The list is terminated with a + ** TARGET_NONE. + */ + TARGET NavQueue[10]; + + /* + ** This points to the team that "owns" this object. This pointer is used to + ** quickly process the team when this object is the source of the change. An + ** example would be if this object were to be destroyed, it would inform the + ** team of this fact by using this pointer. + */ + CCPtr Team; + + /* + ** If this object is part of a pseudo-team that the player is managing, then + ** this will be set to the team number (0 - 9). If it is not part of any + ** pseudo-team, then the number will be -1. + */ + unsigned char Group; + + /* + ** This points to the next member in the team that this object is part of. This + ** is used to quickly process each team member when the team class is the source + ** of the change. An example would be if the team decided that everyone is going + ** to move to a new location, it would inform each of the objects by chaining + ** through this pointer. + */ + FootClass * Member; + + /* + ** Since all objects derived from this class move according to a path list. + ** This is the path list. It specifies, as a simple list of facings, the + ** path that the object should follow in order to reach its destination. + ** This path list is limited in size, so it might require several generations + ** of path lists before the ultimate destination is reached. The game logic + ** handles regenerating the path list as necessary. + */ + FacingType Path[CONQUER_PATH_MAX]; + + /* + ** This value keeps track of how serious the unit is in trying to reach the + ** destination specified. As blockages arise, this threshold will rise + ** unit it reaches the point of complete failure. When that event occurs, the + ** unit will realize that it cannot get to its specified destination and will + ** try to perform some other action instead. + */ + MoveType PathThreshhold; + + /* + ** When there is a complete findpath failure, this timer is initialized so + ** that a findpath won't be calculated until this timer expires. + */ + CDTimerClass PathDelay; + enum {PATH_RETRY=10}; + int TryTryAgain; // Number of retry attempts remaining. + + /* + ** If the object has recently attacked a base, then this timer will not + ** have expired yet. It is used so a building does not keep calling + ** for help from the same attacker. + */ + CDTimerClass BaseAttackTimer; + + /* + ** For formation moves, this will be the override speed. + */ + SpeedType FormationSpeed; + + /* + ** For formation moves, this will be the override maximum speed. + */ + MPHType FormationMaxSpeed; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + FootClass(NoInitClass const & x) : TechnoClass(x), Team(x), PathDelay(x), BaseAttackTimer(x) {}; + FootClass(RTTIType rtti, int id, HousesType house); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool Basic_Path(void); + + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Can_Demolish(void) const; + bool Is_Recruitable(HouseClass const * house=NULL) const; + bool Is_On_Priority_Mission(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Likely_Coord(void) const; + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + COORDINATE Head_To_Coord(void) const {return (HeadToCoord);}; + virtual bool Start_Driver(COORDINATE &headto); + virtual bool Stop_Driver(void); + virtual void Assign_Destination(TARGET target); + bool Is_Allowed_To_Leave_Map(void) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual bool Unlimbo(COORDINATE , DirType dir = DIR_N); + virtual bool Mark(MarkType mark=MARK_CHANGE); + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual void Stun(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Death_Announcement(TechnoClass const * source=0) const; + + /* + ** AI. + */ + virtual void AI(void); + virtual void Sell_Back(int control); + virtual int Offload_Tiberium_Bail(void); + virtual TARGET Greatest_Threat(ThreatType method) const; + virtual void Detach(TARGET target, bool all); + virtual void Detach_All(bool all=true); + virtual int Mission_Retreat(void); + virtual int Mission_Enter(void); + virtual int Mission_Move(void); + virtual int Mission_Capture(void); + virtual int Mission_Attack(void); + virtual int Mission_Guard(void); + virtual int Mission_Hunt(void); + virtual int Mission_Guard_Area(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + void Handle_Navigation_List(void); + void Queue_Navigation_List(TARGET target); + void Clear_Navigation_List(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Approach_Target(void); + virtual void Fixup_Path(PathType *) {}; + virtual void Set_Speed(int speed); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + int Optimize_Moves(PathType *path, MoveType threshhold); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + CELL Adjust_Dest(CELL cell) const; + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + CELL Safety_Point(CELL src, CELL dst, int start, int max); + int Rescue_Mission(TARGET tarcom); + + private: + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + void Debug_Draw_Map(char const * txt, CELL start, CELL dest, bool pause); + void Debug_Draw_Path(PathType *path); + bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); + bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); + bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); + + /* + ** This is the coordinate that the unit is heading to + ** as an immediate destination. This coordinate is never further + ** than once cell (or track) from the unit's location. When this coordinate + ** is reached, then the next location in the path list becomes the + ** next HeadTo coordinate. + */ + COORDINATE HeadToCoord; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; +}; + +#endif + diff --git a/code/redalert/ftimer.h b/code/redalert/ftimer.h new file mode 100644 index 0000000..e24d4cf --- /dev/null +++ b/code/redalert/ftimer.h @@ -0,0 +1,716 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FTIMER.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FTIMER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/16/95 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * BasicTimerClass::operator () -- Function operator for timer object. * + * BasicTimerClass::operator long -- Conversion to long operator. * + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * TTimerClass::operator () -- Function operator for timer object. * + * TTimerClass::operator long -- Conversion operator for timer object. * + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * CDTimerClass::operator () -- Function operator for the count down timer. * + * CDTimerClass::operator long -- Conversion to long operator function. * + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * TTimerClass::Value -- Returns with the current value of the timer. * + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FTIMER_H +#define FTIMER_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +/* +** This is a timer class that watches a constant rate timer (specified by the parameter +** type class) and provides basic timer functionality. It is possible to set the start value +** WITHOUT damaging or otherwise affecting any other timer that may be built upon the same +** specified timer class object. Treat an object of this type as if it were a "magic" integral +** long that automatically advances at the speed of the timer class object controlling it. +*/ +// Let lint know that non-virtual destructor is OK for this class. +//lint -esym(1509,BasicTimerClass) +template +class BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + BasicTimerClass(unsigned long set=0); + BasicTimerClass(NoInitClass const & ); + + ~BasicTimerClass(void); + + // Fetch current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + protected: + T Timer; // Timer regulator (ticks at constant rate). + unsigned long Started; // Time started. +}; + + +template +inline BasicTimerClass::BasicTimerClass(NoInitClass const & ) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::BasicTimerClass -- Constructor for basic timer class. * + * * + * This is the constructor for the basic timer class object. It sets the timer counting * + * up from zero at the rate of the controlling timer class object. * + * * + * INPUT: set -- Alternate initial start value for the counter. If not specified, then * + * the timer is assumed to start at zero and count upwards. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +//lint -esym(1403,BasicTimerClass::Timer) +//lint -esym(1403,BasicTimerClass::Timer) +template +inline BasicTimerClass::BasicTimerClass(unsigned long set) : + Started(Timer()-set) +{ +} + + +/*********************************************************************************************** + * BasicTimerClass::~BasicTimerClass -- Destructor for basic timer object. * + * * + * The destructor for the basic timer object doesn't have to do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::~BasicTimerClass(void) +{ +} + + +template +inline unsigned long BasicTimerClass::Value(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator long -- Conversion to long operator. * + * * + * This conversion operator allows the basic timer object to function in much the same * + * manner as the integral "long" type. One can assign a long with a timer object and the * + * actual value of the timer is extracted from the object and used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline BasicTimerClass::operator unsigned long(void) const +{ + return(Timer()-Started); +} + + +/*********************************************************************************************** + * BasicTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator allows the timer to also serve as the parameter type class for * + * additional timer objects. This allows one to instantiate a controlling timer class that * + * can control (e.g., turn on or off) all timers that are based upon it. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current timer value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long BasicTimerClass::operator () (void) const +{ + return(Timer()-Started); +} + + +/* +** This timer class functions similarly to the basic timer class. In addition to the +** normal timer operation, this class has the ability to be stopped and started at +** will. If you have no need to start or stop the timer, then use the basic timer +** class instead. +*/ +template +class TTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + TTimerClass(unsigned long set=0); + TTimerClass(NoInitClass const & x); + + ~TTimerClass(void) {}; + + // Fetches current value of timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + private: + unsigned long Accumulated; // Total accumulated ticks. +}; + + +template +inline TTimerClass::TTimerClass(NoInitClass const & x) : + BasicTimerClass(x) +{ +} + + +/*********************************************************************************************** + * TTimerClass::TTimerClass -- Constructor for timer class object. * + * * + * This is the constructor for the advanced timer class object. This object class can start * + * or stop the timer under user control. * + * * + * INPUT: set -- The initial value to set the timer to. If no value is specified, then * + * the timer is assumed to start from zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::TTimerClass(unsigned long set) : + BasicTimerClass(set), + Accumulated(0) +{ +} + + +/*********************************************************************************************** + * TTimerClass::Value -- Returns with the current value of the timer. * + * * + * This routine will return with the current value of the timer. It takes into account * + * whether the timer has stopped or not so as to always return the correct value regardless * + * of that condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current value of the timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::Value(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator long -- Conversion operator for timer object. * + * * + * This conversion operator allows this timer object to function as an "rvalue" of a "long" * + * type. This is consistent with the integral "long" value. It is possible to assign a * + * timer object to a long and have the long initialized with the current value of the * + * timer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time value expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline TTimerClass::operator unsigned long(void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::operator () -- Function operator for timer object. * + * * + * This function operator for the timer class allows this timer class to be used as the * + * template parameter for other timer class objects. With this ability, one can control * + * several timers (e.g., start or stop them) by using a single controlling timer class * + * that other timers are instantiated from. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current time expressed as a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long TTimerClass::operator () (void) const +{ + unsigned long value = Accumulated; + if (Started != 0xFFFFFFFFU) { + value += BasicTimerClass::Value(); + } + return(value); +} + + +/*********************************************************************************************** + * TTimerClass::Stop -- Stops the current timer from incrementing. * + * * + * This routine will stop (pause) the timer from further increments. To cause the timer * + * to begin anew, call the Start() function. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/05/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + Accumulated += BasicTimerClass::operator unsigned long(); + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * TTimerClass::Start -- Starts (resumes) a stopped timer. * + * * + * This routine will resume a timer that was previously stopped with the Stop() function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void TTimerClass::Start(void) +{ + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * TTimerClass::Is_Active -- Checks to see if the timer is counting. * + * * + * Since this timer can be paused, this routine is used to examine the timer to see if it * + * is currently paused or active. If the timer is active, then the return value will be * + * true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this timer currently active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool TTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + + +/* +** This timer counts down from the specified (or constructed) value down towards zero. +** The countdown rate is controlled by the timer object specified. This timer object can +** be started or stopped. It can also be tested to see if it has expired or not. An expired +** count down timer is one that has value of zero. You can treat this class object as if it +** were an integral "magic" long that automatically counts down toward zero. +*/ +template +class CDTimerClass : public BasicTimerClass { + public: + // Constructor allows assignment as if class was integral 'long' type. + CDTimerClass(unsigned long set=0); + CDTimerClass(NoInitClass const & x); + + ~CDTimerClass(void); + + // Fetches current value of count down timer. + unsigned long Value(void) const; + + // Conversion operator to allow consistent treatment with integral types. + operator unsigned long(void) const; + + // Function operator to allow timer object definition to be cascaded. + unsigned long operator () (void) const; + + // Stops (pauses) the timer. + void Stop(void); + + // Starts (resumes) the timer. + void Start(void); + + // Queries whether the timer is currently active. + bool Is_Active(void) const; + + bool Was_Started(void) const { return WasStarted; } + + private: + unsigned long DelayTime; // Ticks remaining before countdown timer expires. + bool WasStarted; +}; + + +template +inline CDTimerClass::CDTimerClass(NoInitClass const & x) : + BasicTimerClass(x), WasStarted(false) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::CDTimerClass -- Constructor for count down timer. * + * * + * This is the constructor for the count down timer object. The optional starting value * + * can be used to initiate the timer. Because of this constructor it is possible to assign * + * a long to a count down timer object in order to begin the countdown process. * + * * + * INPUT: set -- The initial starting value for the countdown timer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::CDTimerClass(unsigned long set) : + BasicTimerClass(0), + DelayTime(set), + WasStarted(false) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::~CDTimerClass -- Destructor for the count down timer object. * + * * + * The destructor for the count down timer object does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::~CDTimerClass(void) +{ +} + + +/*********************************************************************************************** + * CDTimerClass::Value -- Fetches the current value of the countdown timer. * + * * + * Use this routine to fetch the current value of the timer. It takes into consideration * + * whether the timer has been stopped or not. It returns the correct value regardless of * + * this condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the correct value of this count down timer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::Value(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator long -- Conversion to long operator function. * + * * + * This conversion operator allows the count down timer object to be used as if it were * + * a "magic" long that automatically counted downward at the controller class tick rate. * + * The count down object can be used in any place that an rvalue long could be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline CDTimerClass::operator unsigned long(void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::operator () -- Function operator for the count down timer. * + * * + * This is the function operator for the count down timer object. By supporting this * + * function operator, this class (or one derived from this class) could be used as the * + * controlling timer to the timer templates. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current count down time expressed in the form of a long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline unsigned long CDTimerClass::operator () (void) const +{ + unsigned long remain = DelayTime; + if (Started != 0xFFFFFFFFU) { + unsigned long value = BasicTimerClass::Value(); + if (value < remain) { + return(remain - value); + } else { + return(0); + } + } + return(remain); +} + + +/*********************************************************************************************** + * CDTimerClass::Stop -- Stops (pauses) the count down timer. * + * * + * This routine is used to stop (pause) the count down timer object. A timer object paused * + * in this fashion will be resumed by a call to Start() or by assigning a new count down * + * value to the timer. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Stop(void) +{ + if (Started != 0xFFFFFFFFU) { + DelayTime = *this; + Started = 0xFFFFFFFFU; + } +} + + +/*********************************************************************************************** + * CDTimerClass::Start -- Starts (resumes) the count down timer. * + * * + * This routine is used to start (resume) the count down timer that was previously stopped * + * with the Stop() function. The timer will also resume when a new timer value is assigned. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +void CDTimerClass::Start(void) +{ + WasStarted = true; + + if (Started == 0xFFFFFFFFU) { + Started = Timer(); + } +} + + +/*********************************************************************************************** + * CDTimerClass::Is_Active -- Checks to see if the timer object is active. * + * * + * Because the timer object counting can be stopped, this routine is used to determine * + * if the timer is currently paused or active. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the timer currently active? * + * * + * WARNINGS: Note that if the timer has counted down to zero, then it may be active, but * + * the value will, naturally, not change. * + * * + * HISTORY: * + * 02/06/1996 JLB : Created. * + *=============================================================================================*/ +template +inline bool CDTimerClass::Is_Active(void) const +{ + return(Started != 0xFFFFFFFFU); +} + +#endif diff --git a/code/redalert/function.h b/code/redalert/function.h new file mode 100644 index 0000000..e00d0d6 --- /dev/null +++ b/code/redalert/function.h @@ -0,0 +1,1199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUNCTION.H 2 3/13/97 2:05p Steve_tall $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUNCTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 27, 1994 * + * * + * Last Update : May 27, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUNCTION_H +#define FUNCTION_H + +/* +** !!!DEFINE!!! "NDEBUG" if the assertion code is to be !!!REMOVED!!! from the project. +*/ +//#define NDEBUG // ST - 5/13/2019 + +//#pragma warn -hid + +#ifdef NEVER +Map (screen) class heirarchy. + + MapeditClass (most derived class) -- scenario editor + ³ + MouseClass -- handles mouse animation and display control + ³ + ScrollClass -- map scroll handler + ³ + HelpClass -- pop-up help text handler + ³ + TabClass -- file folder tab screen mode control dispatcher + ³ + SidebarClass -- displays and controls construction list sidebar + ³ + PowerClass -- display power production/consumption bargraph + ³ + RadarClass -- displays and controls radar map + ³ + DisplayClass -- general tactical map display handler + ³ + MapClass -- general tactical map data handler + ³ + GScreenClass (pure virtual base class) -- generic screen control + + AbstractClass + ³ + ³ + ³ + ³ + ObjectClass + ³ + ÚÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄ¿ + AnimClass ³ TemplateClass ³ ÃÄ FuseClass ³ TerrainClass + ³ ³ ÃÄ FlyClass ³ + ³ ³ BulletClass ³ + OverlayClass MissionClass SmudgeClass + ³ + RadioClass + ³ + ÃÄ CrewClass + ÃÄ FlasherClass + ÃÄ StageClass + ÃÄ CargoClass + TechnoClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + FootClass BuildingClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÁÂÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + DriveClass InfantryClass ÃÄ FlyClass + ³ AircraftClass + ÚÄÁÄÄÄÄÄÄÄÄÄ¿ + ³ ³ + ³ VesselClass + ³ + UnitClass + + + AbstractTypeClass + ³ + ObjectTypeClass + ³ + ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ + ³ ³ ³ ³ + TechnoTypeClass ³ ³ ³ + ³ BulletTypeClass ³ ³ + ³ TemplateTypeClass ³ + ÚÄÄÄÄÄÄÄÄÁÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄ¿ TerrainTypeClass + ³ ³ ³ ³ ³ +UnitTypeClass ³ BuildingTypeClass ³ VesselTypeClass + ³ ³ + AircraftTypeClass InfantryTypeClass +#endif + + +#include "watcom.h" +#include "lint.h" + + +#ifdef WIN32 +//#define WIN32_LEAN_AND_MEAN +#include +#define WWFILE_H +#define RAWFILE_H +#define MONOC_H + +#define _MAX_NAME _MAX_FNAME + +#endif + + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#if (0) +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +#endif + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +/********************************************************************** +** This class is solely used as a parameter to a constructor that does +** absolutely no initialization to the object being constructed. By using +** this method, it is possible to load and save data directly from a +** class that has virtual functions. The construction process automatically +** takes care of initializing the virtual function table pointer and the +** rest of the constructor doesn't initialize any data members. After loading +** into a class object, simply perform an in-place new operation. +*/ +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + + +#define FILE_H +#define WWMEM_H + +#ifndef WIN32 +#define TIMER_H +#endif + +#include "key.h" +#include "io/UserInput.h" +#include "io/EventQueue.h" + +#include +#include "mpu.h" +#include "bench.h" +#include "rect.h" +#include "jshell.h" +#include "buff.h" +#include "face.h" +#include "random.h" +#include "crc.h" +#include "compat.h" +#include "fixed.h" +#include "base64.h" +#include "pipe.h" +#include "xpipe.h" +#include "ramfile.h" +#include "lcw.h" +#include "lzw.h" +#include "lcwpipe.h" +#include "lzwpipe.h" +#include "lzopipe.h" +#include "crcpipe.h" +#include "shapipe.h" +#include "b64pipe.h" +#include "straw.h" +#include "xstraw.h" +#include "b64straw.h" +#include "lcwstraw.h" +#include "lzwstraw.h" +#include "lzostraw.h" +#include "crcstraw.h" +#include "shastraw.h" +#include "rndstraw.h" + +// Should be part of WWLIB.H. This is used in JSHELL.CPP. +typedef struct { + unsigned char SourceColor; + unsigned char DestColor; + unsigned char Fading; + unsigned char reserved; +} TLucentType; + + +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include +//#include + + + +#ifdef WIN32 +#define int386x(a,b,c,d) 0 +#define int386(a,b,c) 0 +#endif + + +/* +** VQ player specific includes. +*/ +//#include +//#include + +extern bool GameActive; +extern long LParam; + +#include +#include "vector.h" +#include "heap.h" +#include "ccfile.h" +#include "monoc.h" +#include "conquer.h" +//#include "debug.h" +#include "special.h" +#include "defines.h" +#include "ccini.h" +#include "ccptr.h" +#include "bar.h" + +/* +** Greenleaf specific includes. +*/ +//#include +//#include + + +extern long Frame; +CELL Coord_Cell(COORDINATE coord); + +#include "utracker.h" +#include "crate.h" +#include "rules.h" +#include "ini.h" +#include "int.h" +#include "pk.h" +#include "pkpipe.h" +#include "pkstraw.h" +#include "sha.h" +#include "blowfish.h" +#include "blowpipe.h" +#include "blwstraw.h" +#include "language.h" +#include "hsv.h" +#include "rgb.h" +#include "palette.h" +#include "palettec.h" //ST 5/13/2019 +#include "version.h" +#include "facing.h" +#include "ftimer.h" +#include "theme.h" +#include "link.h" +#include "gadget.h" +#include "control.h" +#include "toggle.h" +#include "checkbox.h" +#include "shapebtn.h" +#include "textbtn.h" +#include "statbtn.h" +#include "slider.h" +#include "list.h" +#include "drop.h" +#include "cheklist.h" +#include "colrlist.h" +#include "edit.h" +#include "gauge.h" +#include "msgbox.h" +#include "dial8.h" +#include "txtlabel.h" +#include "loaddlg.h" +#include "super.h" +#include "house.h" +#include "gscreen.h" +#include "map.h" +#include "display.h" +#include "radar.h" +#include "power.h" +#include "sidebar.h" +#include "tab.h" +#include "help.h" +#include "mouse.h" +#include "help.h" +#include "target.h" +#include "theme.h" +#include "team.h" // Team objects. +#include "warhead.h" +#include "weapon.h" +#include "trigtype.h" +#include "teamtype.h" // Team type objects. +#include "taction.h" +#include "tevent.h" +#include "trigger.h" // Trigger event objects. +#include "mapedit.h" // map editor class +#include "abstract.h" +#include "object.h" +#include "mission.h" +#include "door.h" +#include "bullet.h" // Bullet objects. +#include "terrain.h" // Terrain objects. +#include "anim.h" // Animation objects. +#include "template.h" // Icon template objects. +#include "overlay.h" // Overlay objects. +#include "smudge.h" // Stains on the terrain objects. +#include "aircraft.h" // Aircraft objects. +#include "unit.h" // Ground unit objects. +#include "vessel.h" // Sea unit objects. +#include "infantry.h" // Infantry objects. +#include "credits.h" // Credit counter class. +#include "score.h" // Scoring system class. +#include "factory.h" // Production manager class. +#include "intro.h" +#include "ending.h" +#include "logic.h" +#include "queue.h" +#include "event.h" +#include "base.h" // defines the AI's pre-built base +#include "carry.h" +#include "scenario.h" +#include "msglist.h" // Multiplayer chat message system +#include "session.h" // Multiplayer session class +//#include "phone.h" // Phone list manager +#include "ipxmgr.h" // IPX connection manager +//#include "nullmgr.h" // Modem connection manager +#include "readline.h" +#include "vortex.h" +#include "egos.h" + +#ifdef WIN32 +//#include "pcx.h" +#endif + +#if(TEN) +#include "tenmgr.h" +#endif + +#if(MPATH) +#ifdef WIN32 +#include "mpmgrw.h" +#else +#include "mpmgrd.h" +#endif +#endif + +// Denzil 5/18/98 - Mpeg movie playback +#ifdef MPEGMOVIE +bool InitDDraw(void); +bool PlayMpegMovie(const char* name); +#endif + +#include "externs.h" + + +extern int Get_CD_Drive(void); +extern void Fatal(char const *message, ...); + +#ifdef WIN32 + +/* +** For WIN32, replace the assert macro so we get an error on the debugger screen +** where we can see it. +** +*/ +#ifndef __BORLANDC__ +#ifdef assert +#undef assert +#endif //assert +void Assert_Failure (char *expression, int line, char *file); + +#ifdef NDEBUG + #define assert(__ignore) ((void)0) +#else + #define assert(expr) ((expr)?(void)0:Assert_Failure(#expr,__LINE__,__FILE__)) +#endif //NDEBUG + +#endif //__BORLANDC__ + + +extern void Free_Interpolated_Palettes(void); +extern int Load_Interpolated_Palettes(char const *filename, BOOL add=FALSE); +extern void Rebuild_Interpolated_Palette(unsigned char *interpal); +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Increase_Palette_Luminance (unsigned char *palette , int red_percentage , int green_percentage , int blue_percentage ,int cap); +#endif + +/* +** ADATA.CPP +*/ +char const * Anim_Name(AnimType anim); + +/* +** AIRCRAFT.CPP +*/ +bool Building_Check(void); + +/* +** ANIM.CPP +*/ +void Shorten_Attached_Anims(ObjectClass * obj); +AnimType Anim_From_Name(char const * name); + +/* +** AUDIO.CPP +*/ +VocType Voc_From_Name(char const * name); +char const * Speech_Name(VoxType speech); +char const * Voc_Name(VocType voc); +int Sound_Effect(VocType voc, fixed volume=1, int variation=1, signed short panvalue=0, HousesType house=HOUSE_NONE); +// void Speak(VoxType voice); // MBL 02.06.2020 +void Speak(VoxType voice, HouseClass *house=NULL, COORDINATE coord=0); +void Speak_AI(void); +void Stop_Speaking(void); +void Sound_Effect(VocType voc, COORDINATE coord, int variation=1, HousesType house=HOUSE_NONE); +bool Is_Speaking(void); + +/* +** CDFILE.CPP +*/ +#ifdef WIN32 +int harderr_handler(unsigned, unsigned, unsigned *); +#else +int harderr_handler(unsigned, unsigned, unsigned __far *); +#endif + +/* +** COMBAT.CPP +*/ +int Modify_Damage(int damage, WarheadType warhead, ArmorType armor, int distance); +void Explosion_Damage(COORDINATE coord, int strength, TechnoClass * source, WarheadType warhead); +AnimType Combat_Anim(int damage, WarheadType warhead, LandType land); +void Wide_Area_Damage(COORDINATE coord, LEPTON radius, int damage, TechnoClass * source, WarheadType warhead); + +/* +** CONQUER.CPP +*/ +void List_Copy(short const * source, int len, short * dest); +int Get_CD_Index (int cd_drive, int timeout); +int Owner_From_Name(char const * text); +CrateType Crate_From_Name(char const * name); +Rect const Shape_Dimensions(void const * shapedata, int shapenum); +void IPX_Call_Back(void); +bool Is_Counterstrike_Installed (void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Aftermath_Installed (void); +#endif + + +#if(TEN) +void Ten_Call_Back(void); +#endif // TEN + +#if(MPATH) +void MPATH_Call_Back(void); +#endif // MPATH + +#define ALWAYS_RELOAD_CD 1000 + +void Center_About_Objects(void); +bool Force_CD_Available(int cd); +void Handle_View(int view, int action=0); +void Handle_Team(int team, int action=0); +TechnoTypeClass const * Fetch_Techno_Type(RTTIType type, int id); +char const * Fading_Table_Name(char const * base, TheaterType theater); +void Unselect_All(void); +void Unselect_All_Except(ObjectClass* object); +void Play_Movie(char const * name, ThemeType theme=THEME_NONE, bool clrscrn=true, bool immediate = false); +void Play_Movie(VQType name, ThemeType theme=THEME_NONE, bool clrscrn=true, bool immediate=false); +bool Main_Loop(void); +TheaterType Theater_From_Name(char const * name); +void Main_Game(int argc, char * argv[]); +long VQ_Call_Back(unsigned char * buffer=NULL, long frame=0); +void Call_Back(void); +char const *Language_Name(char const * basename); +SourceType Source_From_Name(char const * name); +char const *Name_From_Source(SourceType source); +FacingType KN_To_Facing(int input); +void const *Get_Radar_Icon(void const * shapefile, int shapenum, int frames, int zoomfactor); +//void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, DirType rotation=DIR_N, long scale=0x0100); +void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N); + +// Added for draw intercept. ST - 1/17/2019 12:31PM +void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100); +void CC_Draw_Shape(Image_t *image, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const* fadingdata, void const* ghostdata, DirType rotation); +void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100, char override_owner = HOUSE_NONE); + +// Added for pip draw intercept - SKY +void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N); + +void Go_Editor(bool flag); +//long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes); +char *CC_Get_Shape_Filename(void const * shapeptr ); +void CC_Add_Shape_To_Global(void const * shapeptr, char * filename, char code ); +void Bubba_Print(char * format,...); +void Heap_Dump_Check(char * string ); +void Dump_Heap_Pointers(void); +unsigned long Disk_Space_Available(void); +void * Hires_Load(char * name); +void Shake_The_Screen(int shakes, HousesType house = HOUSE_NONE); + +/* +** COORD.CPP +*/ +short const * Coord_Spillage_List(COORDINATE coord, Rect const & rect, bool nocenter=true); +void Normal_Move_Point(short& x, short& y, register DirType dir, unsigned short distance); +void Move_Point(short &x, short &y, register DirType dir, unsigned short distance); +COORDINATE Coord_Move(COORDINATE start, DirType facing, unsigned short distance); +COORDINATE Coord_Scatter(COORDINATE coord, unsigned distance, bool lock=false); +DirType Direction(CELL cell1, CELL cell2); +DirType Direction(COORDINATE coord1, COORDINATE coord2); +DirType Direction256(COORDINATE coord1, COORDINATE coord2); +DirType Direction8(COORDINATE coord1, COORDINATE coord2); +int Distance(COORDINATE coord1, COORDINATE coord2); +int Distance(TARGET target1, TARGET target2); +short const * Coord_Spillage_List(COORDINATE coord, int maxsize); + +/* +** DEBUG.CPP +*/ +void Log_Event(char const *text, ...); +void Debug_Key(unsigned input); +void Self_Regulate(void); + +/* +** DIALOG.CPP +*/ +void Draw_Caption(int text, int x, int y, int w); +void Draw_Caption(char const * text, int x, int y, int w); +int Format_Window_String(char * string, int maxlinelen, int & width, int & height); +extern void Dialog_Box(int x, int y, int w, int h); +void Conquer_Clip_Text_Print(char const *, unsigned x, unsigned y, RemapControlType * fore, unsigned back=(unsigned)TBLACK, TextPrintType flag=TPF_8POINT|TPF_DROPSHADOW, int width=-1, int const * tabs=0); +void Draw_Box(int x, int y, int w, int h, BoxStyleEnum up, bool filled); +int cdecl Dialog_Message(char *errormsg, ...); +void Window_Box(WindowNumberType window, BoxStyleEnum style); +void Fancy_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Fancy_Text_Print(int text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag, ...); +void Simple_Text_Print(char const *text, unsigned x, unsigned y, RemapControlType * fore, unsigned back, TextPrintType flag); +void Plain_Text_Print(int text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); +void Plain_Text_Print(char const *text, unsigned x, unsigned y, unsigned fore, unsigned back, TextPrintType flag, ...); + +/* +** DISPLAY.CPP +*/ +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, const ObjectClass* object); +ObjectClass* Best_Object_With_Action(DynamicVectorClass& objects, CELL cell); + +ActionType Best_Object_Action(DynamicVectorClass& objects, const ObjectClass* object); +ActionType Best_Object_Action(DynamicVectorClass& objects, CELL cell); + +ObjectClass* Best_Object_With_Action(const ObjectClass* object); +ObjectClass* Best_Object_With_Action(CELL cell); + +ActionType Best_Object_Action(const ObjectClass* object); +ActionType Best_Object_Action(CELL cell); + +/* +** ENDING.CPP +*/ +void GDI_Ending(void); +void Nod_Ending(void); + +/* +** EXPAND.CPP +*/ +bool Expansion_Present(void); +bool Expansion_Dialog(void); +bool Expansion_CS_Present(void); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Expansion_AM_Present(void); +#endif +/* +** FINDPATH.CPP +*/ +int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshhold); + +/* +** GOPTIONS.CPP +*/ + +/* +** INI.CPP +*/ +void Write_Scenario_INI(char *root); +bool Read_Scenario_INI(char *root, bool fresh=true); +int Scan_Place_Object(ObjectClass *obj, CELL cell); +void Assign_Houses(void); + +/* +** INIBIN.CPP +*/ +unsigned long Ini_Binary_Version( void ); +bool Read_Scenario_INB( CCFileClass *file, char *root, bool fresh ); +bool Valid_Scenario_INB( CCFileClass *file ); + +/* +** INICODE.CPP +*/ +bool Read_Scenario_INI_Write_INB( char *root, bool fresh ); + +/* +** INIT.CPP +*/ +void Load_Title_Page(bool visible=false); +long Obfuscate(char const * string); +void Anim_Init(void); +bool Init_Game(int argc, char *argv[]); +bool Select_Game(bool fade = false); +bool Parse_Command_Line(int argc, char *argv[]); +void Parse_INI_File(void); + +/* +** INTERPAL.CPP +*/ +#define SIZE_OF_PALETTE 256 +extern "C" unsigned char *InterpolationPalette; +extern BOOL InterpolationPaletteChanged; +extern void Interpolate_2X_Scale( GraphicBufferClass *source, GraphicViewPortClass *dest ,char const *palette_file_name); +void Read_Interpolation_Palette (char const *palette_file_name); +void Write_Interpolation_Palette (char const *palette_file_name); +void Increase_Palette_Luminance(unsigned char *InterpolationPalette , int RedPercentage ,int GreenPercentage ,int BluePercentage ,int cap); +extern "C"{ + extern unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + extern unsigned char *InterpolationPalette; + void __cdecl Asm_Create_Palette_Interpolation_Table(void); +} + +/* +** JSHELL.CPP +*/ +int Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette, PicturePlaneType format); +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void *dest, int color, int frac); +void * Small_Icon(void const * iconptr, int iconnum); +void Set_Window(int window, int x, int y, int w, int h); +void * Load_Alloc_Data(FileClass &file); +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void *reserved_data); +long Translucent_Table_Size(int count); +void *Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void *Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const *control, int count, void *buffer); +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac); + +void Buffer_Enable_HD_Texture(bool hdTextureEnabled); + +/* +** KEYFBUFF.ASM +*/ +long __cdecl Buffer_Frame_To_Page(int shapeNum, int x, int y, int w, int h, Image_t* image, unsigned int Window, int flags, ...); + +/* +** KEYFRAME.CPP +*/ +unsigned long Build_Frame(void const *dataptr, unsigned short framenumber, void *buffptr); +unsigned short Get_Build_Frame_Count(void const *dataptr); +unsigned short Get_Build_Frame_X(void const *dataptr); +unsigned short Get_Build_Frame_Y(void const *dataptr); +unsigned short Get_Build_Frame_Width(void const *dataptr); +unsigned short Get_Build_Frame_Height(void const *dataptr); +bool Get_Build_Frame_Palette(void const *dataptr, void *palette); +int Get_Last_Frame_Length(void); + + +/* +** MAP.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); +int Coord_Spillage_Number(COORDINATE coord, int maxsize); + +/* +** MENUS.CPP +*/ +void Setup_Menu(int menu, char const *text[], unsigned long field, int index, int skip); +int Check_Menu(int menu, char const *text[], char *selection, long field, int index, KeyNumType _key = KN_NONE); +int Do_Menu(char const **strings, bool blue); +extern int UnknownKey; +int Main_Menu(unsigned long timeout); + +/* +** MPLAYER.CPP +*/ +GameType Select_MPlayer_Game (void); +void Clear_Listbox(ListClass *list); +void Clear_Vector(DynamicVectorClass *vector); +void Computer_Message(void); +int Surrender_Dialog(int text); +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text); +bool Determine_If_Using_DVD(); +bool Using_DVD(); +#endif +int Abort_Dialog(void); + +#if(TEN) +int Read_TEN_Game_Options(void); +#endif // TEN + +#if(MPATH) +int Read_MPATH_Game_Options(void); +#endif // MPATH + +#if(TEN) +/* +** CCTEN.CPP +*/ +int Init_TEN(void); +void Shutdown_TEN(void); +void Connect_TEN(void); +void Destroy_TEN_Connection(int id, int error); +void Send_TEN_Win_Packet(void); +void Send_TEN_Alliance(char *whom, int ally); +void Send_TEN_Out_Of_Sync(void); +void Send_TEN_Packet_Too_Late(void); +#endif // TEN + +#if(MPATH) +/* +** CCMPATH.CPP +*/ +int Init_MPATH(void); +void Shutdown_MPATH(void); +void Connect_MPATH(void); +void Destroy_MPATH_Connection(int id, int error); +#endif // MPATH + +/* +** NETDLG.CPP +*/ +bool Init_Network (void); +void Shutdown_Network (void); +bool Remote_Connect (void); +void Destroy_Connection(int id, int error); +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address); +unsigned long Compute_Name_CRC(char *name); +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, unsigned long timeval); + +/* +** NULLDLG.CPP +*/ +int Init_Null_Modem( SerialSettingsType *settings ); +void Shutdown_Modem( void ); +void Modem_Signoff( void ); +int Test_Null_Modem( void ); +int Reconnect_Modem( void ); +void Destroy_Null_Connection(int id, int error); +GameType Select_Serial_Dialog( void ); +int Com_Scenario_Dialog(bool skirmish=false); +int Com_Show_Scenario_Dialog(void); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); +void Log_Start_Time( char *string ); +void Log_End_Time( char *string ); +void Log_Time( char *string ); +void Log_Start_Nest_Time( char *string ); +void Log_End_Nest_Time( char *string ); + +/* +** OBJECT.CPP +*/ + +/* +** PROFILE.CPP +*/ +int WWGetPrivateProfileInt(char const *section, char const *entry, int def, char *profile); +bool WWWritePrivateProfileInt(char const *section, char const *entry, int value, char *profile); +bool WWWritePrivateProfileString(char const *section, char const *entry, char const *string, char *profile); +char * WWGetPrivateProfileString(char const *section, char const *entry, char const *def, char *retbuffer, int retlen, char const * profile); +unsigned WWGetPrivateProfileHex (char const *section, char const *entry, char * profile); + +char *Read_Bin_Buffer( void ); +bool Read_Bin_Init( char *buffer, int length ); +int Read_Bin_Length( char *buffer ); +bool Read_Bin_Num( void *num, int length, char *buffer ); +int Read_Bin_Pos( char *buffer ); +int Read_Bin_PosSet( unsigned int pos, char *buffer ); +bool Read_Bin_String( char *string, char *buffer ); + +char *Write_Bin_Buffer( void ); +bool Write_Bin_Init( char *buffer, int length ); +int Write_Bin_Length( char *buffer ); +bool Write_Bin_Num( void *num, int length, char *buffer ); +int Write_Bin_Pos( char *buffer ); +int Write_Bin_PosSet( unsigned int pos, char *buffer ); +bool Write_Bin_String( char *string, int length, char *buffer ); + +/* +** QUEUE.CPP +*/ +bool Queue_Target(TargetClass whom, TARGET target); +bool Queue_Destination(TargetClass whom, TARGET target); +bool Queue_Mission(TargetClass whom, MissionType mission); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination); +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed); +bool Queue_Options(void); +bool Queue_Exit(void); +void Queue_AI(void); +void Add_CRC(unsigned long *crc, unsigned long val); + +/* +** RANDOM.CPP +*/ + +/* +** REINF.CPP +*/ +bool Do_Reinforcements(TeamTypeClass const * team); +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission = TMISSION_NONE, int argument =0); +int Create_Air_Reinforcement(HouseClass *house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger=INFANTRY_NONE); + +/* +** ROTBMP.CPP +*/ +int Rotate_Bitmap(GraphicViewPortClass *srcvp,GraphicViewPortClass *destvp,int angle); + +/* +** RULES.CPP +*/ +bool Is_MCV_Deploy(); + +/* +** SAVELOAD.CPP +*/ +bool Load_Misc_Values(Straw & file); +bool Save_Misc_Values(Pipe & file); +bool Load_MPlayer_Values(Straw & file); +bool Save_MPlayer_Values(Pipe & file); +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep); +bool Load_Game(int id); +bool Load_Game(const char *file_name); +//bool Read_Object (void * ptr, int base_size, int class_size, FileClass & file, void * vtable); // Original Read_Object prototype. ST - 9/17/2019 12:50PM +bool Read_Object(void *ptr, int class_size, FileClass & file, bool has_vtable); +bool Save_Game(int id, char const * descr, bool bargraph=false); +bool Save_Game(const char *file_name, const char *descr); +bool Write_Object (void * ptr, int class_size, FileClass & file); +void Code_All_Pointers(void); +void Decode_All_Pointers(void); +void Dump(void); + +/* +** SCENARIO.CPP +*/ +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var); +void Post_Load_Game(int load_net); +bool End_Game(void); +bool Read_Scenario(char *root); +bool Start_Scenario(char *root, bool briefing=true); +void Scenario_MapScriptFrame(void); +void Set_Scenario_Difficulty(int difficulty); +HousesType Select_House(void); +void Clear_Scenario(void); +void Do_Briefing(char const * text); +void Do_Lose(void); +void Do_Win(void); +void Do_Restart(void); +void Fill_In_Data(void); +bool Restate_Mission(char const * name, int button1, int button2); +int BGMessageBox(char const *text, int button1, int button2); + +/* +** SCORE.CPP +*/ +char const * Map_Selection(void); +void Bit_It_In(int x, int y, int w, int h, GraphicBufferClass *src, GraphicBufferClass *dest, int delay=0, int dagger=0); +void Call_Back_Delay(int time); +int Alloc_Object(ScoreAnimClass *obj); + +/* +** SPECIAL.CPP +*/ +void Special_Dialog(bool simple=false); +char const * Fetch_Password(int caption, int message, int btext=TXT_OK); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath=false); +#else +int Fetch_Difficulty(void); +#endif + +/* +** STARTUP.CPP +*/ +void Print_Error_End_Exit(char *string); +void Emergency_Exit ( int code ); + +/* +** SUPPORT.ASM +*/ + +/* +** TARGET.CPP +*/ +TechnoTypeClass const * As_TechnoType(TARGET target); +COORDINATE As_Movement_Coord(TARGET target); +AircraftClass * As_Aircraft(TARGET target, bool check_active = true); +AnimClass * As_Animation(TARGET target, bool check_active = true); +BuildingClass * As_Building(TARGET target, bool check_active = true); +BulletClass * As_Bullet(TARGET target, bool check_active = true); +CELL As_Cell(TARGET target); +COORDINATE As_Coord(TARGET target); +InfantryClass * As_Infantry(TARGET target, bool check_active = true); +TeamClass * As_Team(TARGET target, bool check_active = true); +TeamTypeClass * As_TeamType(TARGET target); +TechnoClass * As_Techno(TARGET target, bool check_active = true); +TriggerClass * As_Trigger(TARGET target, bool check_active = true); +TriggerTypeClass * As_TriggerType(TARGET target); +UnitClass * As_Unit(TARGET target, bool check_active = true); +VesselClass * As_Vessel(TARGET target, bool check_active = true); +bool Target_Legal(TARGET target); +ObjectClass * As_Object(TARGET target, bool check_active = true); + +/* +** TEAMTYPE.CPP +*/ +NeedType TeamMission_Needs(TeamMissionType tmtype); + +/* +** TRACKER.CPP +*/ +void Detach_This_From_All(TARGET target, bool all=true); + +/* +** TRIGGER.CPP +*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype); + +/* +** ULOGIC.CPP +*/ +int Terrain_Cost(CELL cell, FacingType facing); + +/* +** VERSION.CPP +*/ +char const * Version_Name(void); + +/* +** WEAPON.CPP +*/ +WeaponType Weapon_From_Name(char const * name); +ArmorType Armor_From_Name(char const * name); + +/* +** Winstub.cpp +*/ +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette); +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +/* +** Egos.CPP +*/ +void Show_Who_Was_Responsible (void); + + +// +// We need to know when the visible page changes +// ST - 1/4/2019 10:31AM +// +void Blit_Hid_Page_To_Seen_Buff(void); +extern bool RunningAsDLL; +extern bool RunningFromEditor; + + +#include "inline.h" + +/* +** These declarations ensure that the templates will be expanded for these specified +** types. Doing this is required because some of the body functions for this template class +** are located in a .CPP module. Doing so results in faster compilation but requires declarations +** such as this for all types that will be required. There are no actual objects of with these +** names ever created, however there are other objects of this type (with different names) that +** are created. +*/ +extern CCPtr _wefwefy1; // Previus definition was function??? +extern CCPtr y2; +extern CCPtr y3; +extern CCPtr y4; +extern CCPtr y5; +extern CCPtr y6; +extern CCPtr y7; +extern CCPtr y8; +extern CCPtr y9; +extern CCPtr y10; +extern CCPtr y11; +extern CCPtr y12; +extern CCPtr y13; +extern CCPtr y14; +extern CCPtr y15; +extern CCPtr y16; +extern CCPtr y17; +extern CCPtr y18; +extern CCPtr y19; +extern CCPtr y20; +extern CCPtr y21; +extern CCPtr y22; +extern CCPtr y23; +extern CCPtr y24; +extern CCPtr y25; +extern CCPtr y26; +extern CCPtr y27; +template <> class DynamicVectorClass >; +extern DynamicVectorClass > y002; +template <>class DynamicVectorClass >; +extern DynamicVectorClass > y001; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx1; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx2; +template <>class DynamicVectorClass; +extern DynamicVectorClass xxx3; + +void WWSDL_ProcessEvents(KeyNumType& key, int& flags, bool presentBuffer = true); +void Device_Present(void); + +/* +** Debug output. ST - 6/27/2019 10:00PM +*/ +void GlyphX_Debug_Print(const char *debug_text); + +void Disable_Uncompressed_Shapes (void); +void Enable_Uncompressed_Shapes (void); + +int Cmd_Argc(); +char* Cmd_Argv(int index); + +void Console_Init(void); +void Console_Render(void); +void Console_Printf(const char* fmt, ...); +void Console_Tick(void); + +typedef void (*xcommand_t) (void); +void Cmd_AddCommand(char* cmd_name, xcommand_t function); + + +#include +extern jmp_buf select_game_jump; + +/* +** Achievement event. ST - 11/11/2019 11:39AM +*/ +void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); + +// jmarshall - editor +#define TXT_CLEAR_MAP 0x3e8 // Clear the map +#define TXT_INHERIT_MAP 0x3e9 // Inherit previous map +#define TXT_SPECIAL_OPTIONS 0x3ea // Select Special Options +#define TXT_VISIBLE_TARGET 0x3eb // Targeting flash visible to +#define TXT_TREE_TARGET 0x3ec // Allow targeting of trees. +#define TXT_MCV_DEPLOY 0x3ed // Allow undeploy of +#define TXT_SMART_DEFENCE 0x3ee // Employ smarter self defense +#define TXT_SLOW_BUILD 0x3ef // Moderate production speed. +#define TXT_THREE_POINT 0x3f0 // Use three point turn logic. +#define TXT_TIBERIUM_GROWTH 0x3f1 // Ore will grow. +#define TXT_TIBERIUM_SPREAD 0x3f2 // Ore will spread. +#define TXT_ROAD_PIECES 0x3f3 // Disable building "bib" +#define TXT_SCATTER 0x3f4 // Allow running from +#define TXT_SHOW_NAMES 0x3f5 // Show true object names. +#define TXT_DEFENDER_ADVANTAGE 0x3f6 // Defender has the advantage. +#define TXT_SEPARATE_HELIPAD 0x3f7 // Allow separate helipad +#define TXT_PASSWORD_CAPTION 0x3f8 // Password Request +#define TXT_PASSWORD_MESSAGE 0x3f9 // Enter Red Alert access code +#define TXT_PASSWORD_ERROR 0x3fa // Access code error detected. +#define TXT_TRY_AGAIN 0x3fb // Try Again +#define TXT_MINE_AWARE 0x3fc // Friendly units avoid +#define TXT_TRIGGER_EDITOR 0x3fd // Trigger Editor +#define TXT_TRIGGER_JUST_EVENT 0x3fe // Just This Event +#define TXT_TRIGGER_AND 0x3ff // ... and ... +#define TXT_TRIGGER_OR 0x400 // ... or ... +#define TXT_TRIGGER_LINKED 0x401 // ... linked ... +#define TXT_TRIGGER_JUST_ACTION 0x402 // Just This Action +#define TXT_TEAM_EDIT 0x403 // Team Editor +#define TXT_SELLABLE 0x404 // Sellable +#define TXT_REBUILD 0x405 // Rebuild +#define TXT_SPEED_BUILD 0x406 // Building constructin time +#define TXT_SCENARIO_ERRORx 0x407 // Scenario authentication +#define TXT_DEBUG_STRESS 0x408 // ÚFrames:ÄÂF/R:ÂCPU:ÄÄÂF/R:ÄÄ +#define TXT_DEBUG_VEHICLE 0x409 // ÚFull +#define TXT_DEBUG_INFANTRY 0x40a // ÚFull +#define TXT_DEBUG_SHIP 0x40b // ÚFull +#define TXT_DEBUG_BUILDING 0x40c // ÚFull +#define TXT_DEBUG_PERFORMANCE 0x40d // Game Objects³ Drawing +#define TXT_DEBUG_AIRCRAFT 0x40e // ÚFull +#define TXT_DEBUG_HOUSE 0x40f // ÚFull Name:ÄÄÄÄÄÄÄÄÂAct +#define TXT_NO_PENTIUM 0x410 // **************************** +#define TXT_SIZE_MAP 0x411 // Size Map +#define TXT_TRUCK_CRATE 0x412 // Trucks drop crate when + +extern const int NUM_EDIT_MISSIONS; + +extern void (*Imgui_Dialog_Function)(void); + +/* +================== +COM_SetExtension +================== +*/ +__forceinline void COM_SetExtension(char* path, int maxSize, const char* extension) { + char oldPath[2048]; + char* src; + + // + // if path doesn't have a .EXT, append extension + // (extension should include the .) + // + src = path + strlen(path) - 1; + + while (*src != '/' && src != path) { + if (*src == '.') { + *src = 0; + break; + } + src--; + } + + strcpy(oldPath, path); + sprintf(path, "%s%s", oldPath, extension); +} + +void Tileset_LoadRules(void); +void Buildings_LoadRules(void); +void Units_LoadRules(void); + +const char* Tileset_FindHDTexture(int theaterType, const char* shapeFileName, int shapeNum, int frameNum); +int Tileset_GetNumFramesForTile(int theaterType, const char* shapeFileName, int shapeNum); +int Buildings_GetNumFramesForTile(const char* shapeFileName, int shapeNum); +const char* Buildings_FindHDTexture(const char* shapeFileName, int shapeNum, int frameNum); +int Units_GetNumFramesForTile(const char* shapeFileName, int shapeNum); +const char* Units_FindHDTexture(const char* shapeFileName, int shapeNum, int frameNum); + +extern bool g_inMainMenu; +extern KeyNumType g_globalKeyNumType; +extern int g_globalKeyFlags; +extern float animFrameNum; +#endif \ No newline at end of file diff --git a/code/redalert/fuse.cpp b/code/redalert/fuse.cpp new file mode 100644 index 0000000..2536251 --- /dev/null +++ b/code/redalert/fuse.cpp @@ -0,0 +1,194 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUSE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : October 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * FuseClass::FuseClass -- Constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1995 BRR : Created. Gosh, what a lotta work. * + *=============================================================================================*/ +FuseClass::FuseClass(void) +{ + Timer = 0; + Arming = 0; + HeadTo = 0; + Proximity = 0; +} + + +/*********************************************************************************************** + * FuseClass::Arm_Fuse -- Sets up fuse for detonation check. * + * * + * This starts a fuse. Fuses are proximity detonation variety but * + * can be modified to have a minimum time to elapse before detonation * + * and a maximum time to exist before detonation. Typically, the * + * timing values are used for missiles that have a minimum arming * + * distance and a limited amount of fuel. * + * * + * INPUT: location -- The coordinate where the projectile start. This * + * is needed for proper proximity tracking. * + * * + * target -- The actual impact point. Fuses are based on real * + * word coordinates. * + * * + * time -- The maximum time that the fuse may work before * + * explosion is forced. * + * * + * arming -- The minimum time that must elapse before the * + * fuse may explode. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Arm_Fuse(COORDINATE location, COORDINATE target, int timeto, int arming) +{ + timeto = max(timeto, arming); + Timer = min(timeto, 0xFF); + Arming = min(arming, 0xFF); + HeadTo = target; + Proximity = Distance(location, target); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Checkup -- Determines if the fuse triggers. * + * * + * This will process the fuse and update the internal clocks as well * + * as check to see if the fuse should trigger (explode) or not. * + * * + * INPUT: newlocation -- The new location of the fuse. This is needed * + * to determine proximity explosions. * + * * + * OUTPUT: bool; Was the fuse triggered to explode now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1994 JLB : Created. * + *=============================================================================================*/ +bool FuseClass::Fuse_Checkup(COORDINATE newlocation) +{ + int proximity; + + /* + ** Always decrement the fuse timer. + */ + if (Timer) Timer--; + + /* + ** If the arming countdown has not expired, then do nothing. + */ + if (Arming) { + Arming--; + } else { + + /* + ** If the timer has run out, then the warhead explodes. + */ + if (!Timer) return(true); + + proximity = Distance(newlocation, HeadTo); + if (proximity < 0x0010) return(true); + if (proximity < ICON_LEPTON_W && proximity > Proximity) { + return(true); + } + Proximity = proximity; + } + return(false); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Write -- Writes the fuse data to the save game file. * + * * + * Use this routine to output the fuse class data to the save game file specified. * + * * + * INPUT: file -- The file to output the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Write(FileClass & file) +{ + file.Write(&Timer, sizeof(Timer)); + file.Write(&Arming, sizeof(Arming)); + file.Write(&HeadTo, sizeof(HeadTo)); + file.Write(&Proximity, sizeof(Proximity)); +} + + +/*********************************************************************************************** + * FuseClass::Fuse_Read -- Reads the fuse class data from the save game file. * + * * + * Use this routine to input the fuse class data from the save game file specified. * + * * + * INPUT: file -- The file to input the data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void FuseClass::Fuse_Read(FileClass & file) +{ + file.Read(&Timer, sizeof(Timer)); + file.Read(&Arming, sizeof(Arming)); + file.Read(&HeadTo, sizeof(HeadTo)); + file.Read(&Proximity, sizeof(Proximity)); +} + + diff --git a/code/redalert/fuse.h b/code/redalert/fuse.h new file mode 100644 index 0000000..2a222e4 --- /dev/null +++ b/code/redalert/fuse.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/FUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : FUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 24, 1994 * + * * + * Last Update : April 24, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FUSE_H +#define FUSE_H + +/**************************************************************************** +** The fuse is used by projectiles to determine whether detonation should +** occur. This is usually determined by tracking the distance to the +** designated target reaches zero or when the timer expires. +*/ +class FuseClass { + public: + FuseClass(void); + FuseClass(NoInitClass const &) {}; + ~FuseClass(void) {}; + + void Arm_Fuse(COORDINATE location, COORDINATE target, int time=0xFF, int arming=0); + bool Fuse_Checkup(COORDINATE newlocation); + void Fuse_Write(FileClass & file); + void Fuse_Read(FileClass & file); + COORDINATE Fuse_Target(void); + + /* + ** Fuses can detonate if enough time has elapsed. This value counts + ** down. When it reaches zero, detonation occurs. + */ + unsigned char Timer; + + private: + + /* + ** Some fuses need a certain amount of time before detonation can + ** occur. This counts down and when it reaches zero, normal fuse + ** detonation checking can occur. + */ + unsigned char Arming; + + /* + ** This is the designated impact point of the projectile. The fuse + ** will trip when the closest point to this location has been reached. + */ + COORDINATE HeadTo; + + /* + ** This is the running proximity value to the impact point. This value + ** will progressively get smaller. Detonation occurs when it reaches + ** zero or when it starts to grow larger. + */ + short Proximity; +}; + +inline COORDINATE FuseClass::Fuse_Target(void) +{ + return(HeadTo); +} + +#endif diff --git a/code/redalert/gadget.h b/code/redalert/gadget.h new file mode 100644 index 0000000..1641b75 --- /dev/null +++ b/code/redalert/gadget.h @@ -0,0 +1,273 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GADGET.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GADGET.H * + * * + * Programmer : Maria del Mar McCready Legg * + * * + * Start Date : January 3, 1995 * + * * + * Last Update : January 3, 1995 [MML] * + * * + * * + * LinkClass [This is the linked list manager class. It keeps a record * + * ³ of the next and previous gadget in the list. It is possible * + * ³ delete a gadget out of the middle of the list with this * + * ³ class.] * + * ³ * + * GadgetClass [The is the basic gadget class. It handles processing of * + * ³ input events and dispatching the appropriate functions. * + * ³ All gadgets must be derived from this class.] * + * ÃÄÄÄÄ¿ * + * ³ ³ * + * ³ ListClass [This list class functions like a list box does in Windows. It * + * ³ keeps track of a list of text strings. This list can be * + * ³ scrolled and an item selected. If the list becomes larger than * + * ³ can be completely displayed, it will automatically create a * + * ³ slider (at the right edge) to manage the scrolling.] * + * ³ * + * ControlClass [This class adds the concept of giving an ID number to the * + * ³ gadget. This ID can then be returned from the Input() * + * ³ function as if it were a pseudo-keystroke. Additionally, * + * ³ the ability to inform another button that this button has * + * ³ been actioned is allowed. This ability allows one button * + * ³ to watch what happens to another button. Example: a list * + * ³ box gadget can tell when an attached slider has been * + * ³ touched.] * + * ÚÄÄÄÄÄÄÄÅÄÄÄÄ¿ * + * ³ ³ ³ * + * ³ ³ GaugeClass [This class looks similar to Windows slider, but has * + * ³ ³ ³ a different controlling logic. There is no thumb and * + * ³ ³ ³ it serves as a simple variable control setting. This * + * ³ ³ ³ is analogous to a volume slider.] * + * ³ ³ ³ * + * ³ ³ SliderClass [The slider class is similar to the typical Windows slider. It * + * ³ ³ has a current setting, a thumb, and a controllable scale. This * + * ³ ³ is the object created to handle a scrolling list box.] * + * ³ ³ * + * ³ EditClass * + * ³ * + * ³ * + * ToggleClass [The toggle class is used for buttons that have an image and behave just * + * ³ like the buttons in Windows do. That is, they have a separate visual for * + * ³ when they are pressed and raised. They are officially triggered (return * + * ³ their ID number) when the mouse button is released while over the button. * + * ³ This class doesn't perform any rendering itself. It merely provides the * + * ³ logic so that the derived classes will function correctly.] * + * ÚÄÁÄÄÄÄ¿ * + * ³ ³ * + * ³ TextButtonClass [The text button functions like a normal Windows style button, but * + * ³ the imagery is based on text that is displayed on the button. A * + * ³ typical example would be the "OK" or "Cancel" buttons.] * + * ³ * + * ShapeButtonClass [The shape buttons is similar to the TextButton but instead of text * + * being used to give the button its imagery, an actual shape is used * + * instead. This allows graphic buttons. These are similar to the up/down * + * arrows seen in a Windows slider.] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GADGET_H +#define GADGET_H + +#include "link.h" + +class ControlClass; + +class GadgetClass : public LinkClass +{ + public: + friend class DLLExportClass; // ST - 5/13/2019 + + typedef enum FlagEnum { + LEFTPRESS = 0x0001, // Left mouse button press. + LEFTHELD = 0x0002, // Left mouse button is being held down. + LEFTRELEASE = 0x0004, // Left mouse button released. + LEFTUP = 0x0008, // Left mouse button is being held up. + RIGHTPRESS = 0x0010, // Right mouse button press. + RIGHTHELD = 0x0020, // Right mouse button is being held down. + RIGHTRELEASE = 0x0040, // Right mouse button released. + RIGHTUP = 0x0080, // Right mouse button is being held up. + KEYBOARD = 0x0100 // Keyboard input processing (maybe). + } FlagEnum; + + GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky=false); + GadgetClass(NoInitClass const & x) : LinkClass(x) {}; + GadgetClass(void) {}; + GadgetClass(GadgetClass const & gadget); + virtual ~GadgetClass(void); + + /* + ** Gadget list management functions. + */ + virtual KeyNumType Input(void); + virtual void Draw_All(bool forced=true); + virtual void Delete_List(void); + virtual ControlClass * Extract_Gadget(unsigned id); + virtual void Flag_List_To_Redraw(void) {LastList = 0;}; + virtual GadgetClass * Remove(void); + virtual GadgetClass * Get_Next(void) const; + virtual GadgetClass * Get_Prev(void) const; + + /* + ** Manages individual gadget states and actions. + */ + virtual void Disable(void); + virtual void Enable(void); + virtual unsigned Get_ID(void) const {return 0;}; + virtual void Flag_To_Redraw(void); + virtual void Peer_To_Peer(unsigned , KeyNumType & , ControlClass & ) {}; + virtual void Set_Focus(void); + virtual void Clear_Focus(void); + virtual bool Has_Focus(void); + virtual int Is_List_To_Redraw(void); + virtual bool Is_To_Redraw(void) {return (IsToRepaint);} + virtual void Set_Position(int x, int y); + + /* + ** General render function. + */ + virtual int Draw_Me(int forced=false); + + /* + ** Sets the current color scheme + */ + static void Set_Color_Scheme(RemapControlType *scheme) + { ColorScheme = scheme; } + + static RemapControlType * Get_Color_Scheme(void) + { return (ColorScheme); } + + /* + ** This is the coordinates and dimensions of the gadget region. These are in + ** absolute screen pixel coordinates. + */ + int X; + int Y; + int Width; + int Height; + + protected: + + /* + ** Processes the event flags so that if this gadget needs to "stick" or + ** "unstick", it will be properly flagged. Call this function if you are + ** going to clear the button press flags before calling the base class + ** Action() function. Otherwise, calling this function manually, is + ** unnecessary since the base class Action() function already does so. + */ + virtual void Sticky_Process(unsigned flags); + + /* + ** This is the action function that will be called whenever the flags and mouse + ** input indicates. This is the main method by which this button performs a useful + ** function. + */ + virtual int Action(unsigned flags, KeyNumType & key); + + /* + ** This is a record of the last list passed to the Input() function. If a list + ** different than the last recorded one is detected, then the draw function is + ** called for every gadget in the list. This causes all buttons to be redrawn the + ** fire time Input() is called without forced a manual call to Draw_All(). + */ + static GadgetClass * LastList; + + /* + ** This points to the gadget that has the keyboard focus. All keyboard only + ** events are fed to this gadget to the exclusion of all others. + */ + static GadgetClass * Focused; + + /* + ** This button should call the Draw_Me function because some graphic element needs + ** to be redrawn. This flag is set by default if the Action function is called. + */ + unsigned IsToRepaint:1; + + public: // HACK HACK HACK.. this is here because the sidebar buttons are static. + /* + ** A sticky button is one that is processed to the exclusion of all other buttons + ** IF the mouse was pressed down while over this button and the mouse continues + ** to remain pressed. This is the standard behavior for all normal Windows style + ** buttons. + */ + unsigned IsSticky:1; + + // ajw - Publicized StuckOn 7/30/98 (was protected) + /* + ** If there is a sticky button being processed, then this will point to it. A sticky + ** button is one that will ONLY be processed while the mouse button is being + ** held down. + */ + static GadgetClass * StuckOn; + + protected: + + /* + ** If the button is disabled, then it won't be processed by the input function. It will + ** have its Draw_Me function called as necessary. In order to not display the button + ** at all, the appropriate draw function should perform no action -- just return. Or, + ** just remove the button from the list. + */ + unsigned IsDisabled:1; + + /* + ** These are the action flags that are used to determine when the action function + ** should be called. Example: If this gadget only wants the action button called when + ** the left mouse button is pressed over the its region, then the flag will be set + ** to LEFTPRESS. + */ + unsigned Flags; + + /* + ** This is the current color scheme; it must be initialized by the app. + */ + static RemapControlType *ColorScheme; + + private: +public: //ST - 5/14/2019 + virtual int Clicked_On(KeyNumType & key, unsigned flags, int x, int y); +}; + +//PG +//inline GadgetClass::FlagEnum operator |(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator &(GadgetClass::FlagEnum, GadgetClass::FlagEnum); +//inline GadgetClass::FlagEnum operator ~(GadgetClass::FlagEnum); + +inline GadgetClass::FlagEnum operator|(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{ + return static_cast(static_cast(a) | static_cast(b)); +} + +inline GadgetClass::FlagEnum operator&(GadgetClass::FlagEnum a, GadgetClass::FlagEnum b) +{ + return static_cast(static_cast(a) & static_cast(b)); +} + +inline GadgetClass::FlagEnum operator~(GadgetClass::FlagEnum a) +{ + return static_cast(~static_cast(a)); +} + + +#endif diff --git a/code/redalert/gamedlg.cpp b/code/redalert/gamedlg.cpp new file mode 100644 index 0000000..e45ec58 --- /dev/null +++ b/code/redalert/gamedlg.cpp @@ -0,0 +1,517 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAMEDLG.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "gamedlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#define GERMAN_OFFSET_Y 4 //VG + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); +#endif + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 12/31/1994 MML : Created. * + *=============================================================================================*/ +void GameControlsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 232 * RESFACTOR; // dialog width + int d_dialog_h = 141 * RESFACTOR; // dialog height + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + int d_top_margin = 25 * RESFACTOR; + + int d_txt6_h = (6 * RESFACTOR) + 1; // ht of 6-pt text + int d_margin1 = (5 * RESFACTOR); // large margin + int d_margin2 = (2 * RESFACTOR); // small margin + + int d_speed_w = d_dialog_w - (34 * RESFACTOR); + int d_speed_h = 6 * RESFACTOR; + int d_speed_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_speed_y = d_dialog_y + d_top_margin + d_margin1 + d_txt6_h; +#endif + + int d_scroll_w = d_dialog_w - (34 * RESFACTOR); + int d_scroll_h = 6 * RESFACTOR; + int d_scroll_x = d_dialog_x + (17 * RESFACTOR); +#ifdef GERMAN + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h - GERMAN_OFFSET_Y; +#else + int d_scroll_y = d_speed_y + d_speed_h + d_txt6_h + (d_margin1 * 2) + d_txt6_h; +#endif + + int d_visual_w = d_dialog_w - (40 * RESFACTOR); + int d_visual_h = 9 * RESFACTOR; + int d_visual_x = d_dialog_x + (20 * RESFACTOR); + int d_visual_y = d_scroll_y + d_scroll_h + d_txt6_h + (d_margin1 * 2); + + int d_sound_w = d_dialog_w - (40 * RESFACTOR); + int d_sound_h = (9 * RESFACTOR); + int d_sound_x = d_dialog_x + (20 * RESFACTOR); + int d_sound_y = d_visual_y + d_visual_h + d_margin1; + + int d_ok_w = 20 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_cx - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + +#ifdef WOLAPI_INTEGRATION + int d_wol_x = d_sound_x; + int d_wol_y = d_sound_y + d_sound_h + d_margin1; + int d_wol_w = d_sound_w; + int d_wol_h = d_sound_h; + + bool bShowWolapi = ( pWolapi && !pWolapi->bConnectionDown ); + if( bShowWolapi ) + { + // Enlarge dialog and shift ok button down. + d_dialog_h += d_wol_h + d_margin1; + d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); // centered y-coord + //d_ok_y += d_wol_h + d_margin1; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - (4 * RESFACTOR); + } +#endif + + /* + ** Button Enumerations + */ +#ifdef WOLAPI_INTEGRATION + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_WOLAPI, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#else + enum { + BUTTON_SPEED = 100, + BUTTON_SCROLLRATE, + BUTTON_VISUAL, + BUTTON_SOUND, + BUTTON_OK, + BUTTON_COUNT, + BUTTON_FIRST = BUTTON_SPEED, + }; +#endif + + /* + ** Dialog variables + */ + KeyNumType input; + + int gamespeed = Options.GameSpeed; + int scrollrate = Options.ScrollRate; + int selection; + bool pressed = false; + int curbutton = 0; + TextButtonClass *buttons[BUTTON_COUNT - BUTTON_FIRST]; + TextPrintType style; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands; // button list + + SliderClass gspeed_btn(BUTTON_SPEED, d_speed_x, d_speed_y, d_speed_w, d_speed_h, true); + SliderClass scrate_btn(BUTTON_SCROLLRATE, d_scroll_x, d_scroll_y, d_scroll_w, d_scroll_h, true); + TextButtonClass visual_btn(BUTTON_VISUAL, TXT_VISUAL_CONTROLS, TPF_BUTTON, d_visual_x, d_visual_y, d_visual_w, d_visual_h); + TextButtonClass sound_btn(BUTTON_SOUND, TXT_SOUND_CONTROLS, TPF_BUTTON, d_sound_x, d_sound_y, d_sound_w, d_sound_h); + TextButtonClass okbtn(BUTTON_OK, TXT_OPTIONS_MENU, TPF_BUTTON, d_ok_x, d_ok_y); + okbtn.X = (SeenBuff.Get_Width()-okbtn.Width)/2; + +#ifdef WOLAPI_INTEGRATION + TextButtonClass wol_btn( BUTTON_WOLAPI, TXT_WOL_OPTTITLE, TPF_BUTTON, d_wol_x, d_wol_y, d_wol_w, d_wol_h ); +#endif + + /* + ** Various Inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build button list + */ + commands = &okbtn; + gspeed_btn.Add_Tail(*commands); + scrate_btn.Add_Tail(*commands); + visual_btn.Add_Tail(*commands); + sound_btn.Add_Tail(*commands); +#ifdef WOLAPI_INTEGRATION + if( bShowWolapi ) + wol_btn.Add_Tail(*commands); +#endif + /* + ** Init button states + ** For sliders, the thumb ranges from 0 - (maxval-1), so to convert the + ** thumb value to a real-world value: + ** val = (MAX - slider.Get_Value()) - 1; + ** and, + ** slider.Set_Value(-(val + 1 - MAX)); + */ + gspeed_btn.Set_Maximum(OptionsClass::MAX_SPEED_SETTING); // varies from 0 - 7 + gspeed_btn.Set_Thumb_Size(1); + gspeed_btn.Set_Value((OptionsClass::MAX_SPEED_SETTING-1) - gamespeed); + + scrate_btn.Set_Maximum(OptionsClass::MAX_SCROLL_SETTING); // varies from 0 - 7 + scrate_btn.Set_Thumb_Size(1); + scrate_btn.Set_Value((OptionsClass::MAX_SCROLL_SETTING-1) - scrollrate); + + /* + ** Fill array of button ptrs. + */ + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = &visual_btn; + buttons[3] = &sound_btn; +#ifdef WOLAPI_INTEGRATION + buttons[4] = &wol_btn; + buttons[5] = &okbtn; +#else + buttons[4] = &okbtn; +#endif + /* + ** Processing loop. + */ + bool process = true; + bool display = true; + bool refresh = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + { + Hide_Mouse(); + + Map.Flag_To_Redraw(true); + Map.Render(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_GAME_CONTROLS, d_dialog_x, d_dialog_y, d_dialog_w); + Show_Mouse(); + display = false; + refresh = true; + } + + { + Hide_Mouse(); + + /* + ** Label the game speed slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SPEED, d_speed_x, d_speed_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print(TXT_SLOWER, d_speed_x, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_FASTER, d_speed_x + d_speed_w, d_speed_y + d_speed_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + /* + ** Label the scroll rate slider + */ + style = TPF_TEXT; + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST)) { + style = (TextPrintType)(style | TPF_BRIGHT_COLOR); + } + Fancy_Text_Print(TXT_SCROLLRATE, d_scroll_x, d_scroll_y - d_txt6_h, scheme, TBLACK, style); + + Fancy_Text_Print (TXT_SLOWER, d_scroll_x, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print (TXT_FASTER, d_scroll_x + d_scroll_w, d_scroll_y + d_scroll_h + (1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + commands->Draw_All(); + + Show_Mouse(); + refresh = false; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + case (BUTTON_SPEED | KN_BUTTON): + curbutton = (BUTTON_SPEED - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_SCROLLRATE | KN_BUTTON): + curbutton = (BUTTON_SCROLLRATE - BUTTON_FIRST); + refresh = true; + break; + + case (BUTTON_VISUAL | KN_BUTTON): + selection = BUTTON_VISUAL; + pressed = true; + break; + + case (BUTTON_SOUND | KN_BUTTON): + selection = BUTTON_SOUND; + pressed = true; + break; + + case (BUTTON_OK | KN_BUTTON): + selection = BUTTON_OK; + pressed = true; + break; + +#ifdef WOLAPI_INTEGRATION + case (BUTTON_WOLAPI | KN_BUTTON): + selection = BUTTON_WOLAPI; + pressed = true; + break; +#endif + + case (KN_ESC): + process = false; + break; + + case (KN_LEFT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(1); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(1); + } + break; + + case (KN_RIGHT): + if (curbutton == (BUTTON_SPEED - BUTTON_FIRST) ) { + gspeed_btn.Bump(0); + } else + if (curbutton == (BUTTON_SCROLLRATE - BUTTON_FIRST) ) { + scrate_btn.Bump(0); + } + break; + + case (KN_UP): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton--; // Skip over missing button. + } +#endif + if (curbutton < 0) { + curbutton = (BUTTON_COUNT - BUTTON_FIRST - 1); + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_DOWN): + if (buttons[curbutton]) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; +#ifdef WOLAPI_INTEGRATION + if( !bShowWolapi ) + { + if( curbutton == BUTTON_WOLAPI - BUTTON_FIRST ) + curbutton++; // Skip over missing button. + } +#endif + if (curbutton > (BUTTON_COUNT - BUTTON_FIRST - 1) ) { + curbutton = 0; + } + + if (buttons[curbutton]) { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + refresh = true; + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_FIRST; + pressed = true; + break; + + default: + break; + } + + /* + ** Perform some action. Either to exit the dialog or bring up another. + */ + if (pressed) { + + /* + ** Record the new options slider settings. + ** The GameSpeed data member MUST NOT BE SET HERE! It will cause multiplayer + ** games to go out of sync. It's set by virtue of the event being executed. + */ + if (gamespeed != ((OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value()) ) { + gamespeed = (OptionsClass::MAX_SPEED_SETTING-1) - gspeed_btn.Get_Value(); + OutList.Add(EventClass(EventClass::GAMESPEED, gamespeed)); + } + + if (scrollrate != ((OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value()) ) { + scrollrate = (OptionsClass::MAX_SCROLL_SETTING-1) - scrate_btn.Get_Value(); + Options.ScrollRate = scrollrate; + } + process = false; + + /* + ** Save the settings in such a way that the GameSpeed is only set during + ** the save process; restore it when we're done, so multiplayer games don't + ** go out of sync. + */ + if (Session.Type == GAME_NORMAL) { + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + } else { + int old = Options.GameSpeed; // save orig value + Options.GameSpeed = gamespeed; + Options.Save_Settings(); // save new value + Options.GameSpeed = old; // restore old value + } + + /* + ** Possibly launch into another dialog if so directed. + */ + switch (selection) { + case (BUTTON_VISUAL): + VisualControlsClass().Process(); + process = true; + display = true; + refresh = true; + break; + + case (BUTTON_SOUND): + if (!SoundType) { + WWMessageBox().Process(Text_String(TXT_NO_SOUND_CARD)); + process = true; + display = true; + refresh = true; + } else { + SoundControlsClass().Process(); + process = true; + display = true; + refresh = true; + } + break; + +#ifdef WOLAPI_INTEGRATION + case BUTTON_WOLAPI: + if( WOL_Options_Dialog( pWolapi, true ) ) + { + // The game ended while in this dialog. + process = false; + } + else + { + process = true; + display = true; + refresh = true; + } + break; +#endif + + case (BUTTON_OK): + break; + } + + pressed = false; + } + } +} + + diff --git a/code/redalert/gamedlg.h b/code/redalert/gamedlg.h new file mode 100644 index 0000000..0629fe4 --- /dev/null +++ b/code/redalert/gamedlg.h @@ -0,0 +1,49 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAMEDLG.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAMEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAMEDLG_H +#define GAMEDLG_H + +#include "gadget.h" + +class GameControlsClass +{ + public: + GameControlsClass(void) {}; + void Process(void); +}; + + +#endif + diff --git a/code/redalert/gauge.cpp b/code/redalert/gauge.cpp new file mode 100644 index 0000000..fe536e9 --- /dev/null +++ b/code/redalert/gauge.cpp @@ -0,0 +1,536 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAUGE.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.CPP * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 16, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GaugeClass::Action -- Handles input events for the gauge. * + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * GaugeClass::Set_Value -- Set the value of the gauge. * + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * GaugeClass::GaugeClass -- class constructor * + * * + * INPUT: id -- button ID * + * * + * x,y -- upper-left corner, in pixels * + * * + * w,h -- width, height, in pixels * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +GaugeClass::GaugeClass(unsigned id, int x, int y, int w, int h) + : ControlClass(id, x, y, w, h, LEFTHELD|LEFTPRESS|LEFTRELEASE, true) +{ + Set_Maximum(255); + Set_Value(0); + + HasThumb = true; + IsHorizontal = (w > h); + IsColorized = true; + + ClickDiff = 0; +} + + +/*********************************************************************************************** + * GaugeClass::Set_Maximum -- Sets the maximum value for the gauge. * + * * + * This routine will set the maximum value for the gauge. This is the largest value that * + * the current setting may reach. The ability to change this allows the gauge to use and * + * return values that are convenient for the programmer's current needs. * + * * + * INPUT: value -- The value to use as the gauge maximum. * + * * + * OUTPUT: bool; Was the gauge maximum changed? A false indicates that the specified value * + * already matches the current maximum. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Maximum(int value) +{ + if (value != MaxValue) { + MaxValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Set_Value -- Set the value of the gauge. * + * * + * This routine will set the current value for the gauge. This value is clipped to the * + * limits of the gauge maximum. * + * * + * INPUT: value -- The value to set at the new current value. * + * * + * OUTPUT: bool; Was the current setting changed? A false indicates that the setting * + * specified is the same as what was already there. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Set_Value(int value) +{ + value = Bound(value, 0, MaxValue); + if (value != CurValue) { + CurValue = value; + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Pixel_To_Value -- Convert a pixel offset into a gauge value. * + * * + * Use this routine to convert the specified pixel offset into a gauge value. This is used * + * in translating mouse clicks into a corresponding setting for the gauge. * + * * + * INPUT: pixel -- The pixel offset form the start of the gauge. * + * * + * OUTPUT: Returns with the setting value in gauge coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Pixel_To_Value(int pixel) +{ + int maximum; + + if (IsHorizontal) { + pixel -= X+1; + maximum = Width; + } else { + pixel -= Y+1; + maximum = Height; + } + maximum -= 2; + pixel = Bound(pixel, 0, maximum); + return(MaxValue * fixed(pixel, maximum)); +// return(Fixed_To_Cardinal(MaxValue, Cardinal_To_Fixed(maximum, pixel))); +} + + +/*********************************************************************************************** + * GaugeClass::Value_To_Pixel -- Convert gauge value to pixel offset. * + * * + * Use this routine to convert the specified gauge value into a pixel offset from the * + * star of the gauge. This is used for thumb positioning. * + * * + * INPUT: value -- The value to convert to a pixel offset. * + * * + * OUTPUT: Returns with the pixel offset of the specified value from the start of the * + * gauge. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Value_To_Pixel(int value) +{ + int maximum; + int start; + if (IsHorizontal) { + maximum = Width; + start = X; + } else { + maximum = Height; + start = Y; + } + maximum -= 2; + return(start + maximum * fixed(value, MaxValue)); +// return(start + Fixed_To_Cardinal(maximum, Cardinal_To_Fixed(MaxValue, value))); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * * + * OUTPUT: bool; Was the gauge redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + if (IsColorized) { + int middle = Value_To_Pixel(CurValue); + int color = GadgetClass::Get_Color_Scheme()->Bright; + if (IsHorizontal) { + if (middle >= (X + 1)) + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, color); + } else { + if (middle >= (Y + 1)) + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, color); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * GaugeClass::Action -- Handles input events for the gauge. * + * * + * This routine will handle input event processing for the gauge. It will adjust the * + * current setting of the gauge according to the mouse position. * + * * + * INPUT: flags -- The input event that is the reason for this function call. * + * key -- The key code that caused the event. * + * * + * OUTPUT: bool; Was the even recognized, processed, and no further gadget scanning is * + * desired (for this pass). * + * * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int GaugeClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there's no thumb on this gauge, it's a display-only device. + */ + if (!HasThumb) { + return(false); + } + + /* + ** We might end up clearing the event bits. Make sure that the sticky + ** process is properly updated anyway. + */ + Sticky_Process(flags); + + /* + ** If the thumb is currently being "dragged around", then update the slider + ** position according to the mouse position. In all other cases, ignore the + ** button being held down. + */ + if ((flags & LEFTPRESS) || ((flags & LEFTHELD) && StuckOn == this)) { + + /* + ** Compute the difference between where we clicked, and the edge of + ** the thumb (only if we clicked on the thumb.) + */ + if (flags & LEFTPRESS) { + int curpix = Value_To_Pixel(CurValue); + int clickpix = (IsHorizontal ? Get_Mouse_X() : Get_Mouse_Y()); + + if ( (clickpix > curpix) && (clickpix - curpix) < Thumb_Pixels()) { + ClickDiff = (clickpix - curpix); + } else { + ClickDiff = 0; + } + + int testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + + /* + ** Correct for round-down errors in Pixel_To_Value() and + ** Value_To_Pixel(); make ClickDiff exactly right so that + ** at this point, Get_Mouse_n() - ClickDiff converts to + ** CurValue. + */ + while (testval < CurValue && ClickDiff > 0) { + ClickDiff--; + testval = Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff); + } + } + + /* + ** If no change occurred in the gauge, just call Control's Action routine, + ** but turn off the flags so it won't fill in 'key' with the button ID. + ** Thus, no button ID will be returned by Input. + */ + if (!Set_Value(Pixel_To_Value(IsHorizontal ? + Get_Mouse_X() - ClickDiff : Get_Mouse_Y() - ClickDiff))) { + + flags &= ~(LEFTHELD|LEFTRELEASE|LEFTPRESS); + ControlClass::Action(0, key); + key = KN_NONE; + return(true); + } + + } else { + + /* + ** Ignore the left mouse button being held down if this gauge is not + ** currently in "sticky" mode. This allows processing of the LEFTPRESS + ** by any derived classes such that this gauge can be more closely + ** controlled. + */ + flags &= ~LEFTHELD; + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * GaugeClass::Draw_Thumb -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: none. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +void GaugeClass::Draw_Thumb(void) +{ + int x = Value_To_Pixel(CurValue); + + if ((x + 4) > Value_To_Pixel(MaxValue)) { + x = Value_To_Pixel(MaxValue) - 2; + } + + if (x < X) { + x = X; + } + + if (IsHorizontal) { + Draw_Box(x, Y, 4, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, x, Width, 4, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * TriColorGaugeClass::TriColorGaugeClass -- Constructor for 3 color (red\yellow\green) gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: See below. * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +TriColorGaugeClass::TriColorGaugeClass(unsigned id, int x, int y, int w, int h) + : GaugeClass(id, x, y, w, h) +{ + RedLimit = 0; // maximum value for red + YellowLimit = 0; // maximum value for yellow +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Red_Limit -- Set the value for the red area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Red_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value > YellowLimit) { +// RedLimit = YellowLimit; +// YellowLimit = value; +// } else { + RedLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Set_Yellow_Limit -- Set the value for the yellow area of gauge. * + * * + * INPUT: int value. * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Set_Yellow_Limit(int value) +{ + if (value >= 0 && value < MaxValue) { + +// if (value < RedLimit) { +// YellowLimit = RedLimit; +// RedLimit = value; +// } else { + YellowLimit = value; +// } + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TriColorGaugeClass::Draw_Me -- Draw the tri color gauge. * + * * + * INPUT: int forced -- draw or not? * + * * + * OUTPUT: bool true of false. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/16/1995 MML : Created. * + *=============================================================================================*/ +int TriColorGaugeClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color + */ + Draw_Box (X, Y, Width, Height, (IsDisabled ? BOXSTYLE_RAISED : BOXSTYLE_DOWN), true); + + /* + ** Colourize the inside of the gauge if indicated. + */ + int red = Value_To_Pixel(RedLimit); + int yellow = Value_To_Pixel(YellowLimit); + int middle = Value_To_Pixel(CurValue); + + if (CurValue <= RedLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, middle, Y+Height-2, PINK); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, middle, PINK); + } + } else if (CurValue > RedLimit && CurValue <= YellowLimit) { + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, middle, Y+Height-2, YELLOW); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, middle, YELLOW); + } + } else if (CurValue > YellowLimit && CurValue <= MaxValue) { + + if (IsHorizontal) { + LogicPage->Fill_Rect(X+1, Y+1, red, Y+Height-2, PINK); + LogicPage->Fill_Rect(red, Y+1, yellow, Y+Height-2, YELLOW); + LogicPage->Fill_Rect(yellow, Y+1, middle, Y+Height-2, GREEN); + } else { + LogicPage->Fill_Rect(X+1, Y+1, X+Width-2, red, PINK); + LogicPage->Fill_Rect(X+1, red, X+Width-2, yellow, YELLOW); + LogicPage->Fill_Rect(X+1, yellow, X+Width-2, middle, GREEN); + } + } + + if (HasThumb) { + Draw_Thumb(); + } + + /* + ** Display the mouse + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + diff --git a/code/redalert/gauge.h b/code/redalert/gauge.h new file mode 100644 index 0000000..67763e4 --- /dev/null +++ b/code/redalert/gauge.h @@ -0,0 +1,106 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GAUGE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GAUGE.H * + * * + * Programmer : Joe L. Bostic, Maria del Mar McCready Legg * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GAUGE_H +#define GAUGE_H + +class GaugeClass : public ControlClass +{ + public: + + GaugeClass(unsigned id, int x, int y, int w, int h); + + virtual int Draw_Me(int forced=false); + virtual int Set_Maximum(int value); + virtual int Set_Value(int value); + virtual int Get_Value(void) const {return (CurValue);}; + virtual void Use_Thumb(int value) { HasThumb = value ? true : false; }; + + virtual int Thumb_Pixels(void) { return (4);} + + /* + ** If this gauge has a color to the left of the current setting, then this + ** flag will be true. + */ + unsigned IsColorized:1; + + protected: + + /* + ** If a thumb is desired, set to true. + */ + unsigned HasThumb:1; + + /* + ** Is this a horizontal slider? + */ + unsigned IsHorizontal:1; + + int MaxValue; // maximum value (in application units) + int CurValue; // index of 1st displayed string in box + // (in application units) + + /* + ** This value records the difference between where the user clicked + ** and the edge of the thumb, so that the thumb follows the mouse + ** with the proper offset. + */ + int ClickDiff; + + protected: + virtual void Draw_Thumb(void); + virtual int Action(unsigned flags, KeyNumType &key); + virtual int Pixel_To_Value(int pixel); + virtual int Value_To_Pixel(int value); +}; + + + +class TriColorGaugeClass : public GaugeClass +{ + public: + TriColorGaugeClass(unsigned id, int x, int y, int w, int h); + virtual int Draw_Me(int forced); + virtual int Set_Red_Limit(int value); + virtual int Set_Yellow_Limit(int value); + + protected: + int RedLimit; // maximum value for red + int YellowLimit; // maximum value for yellow +}; + + + + +#endif diff --git a/code/redalert/getcpu.cpp b/code/redalert/getcpu.cpp new file mode 100644 index 0000000..d2571a8 --- /dev/null +++ b/code/redalert/getcpu.cpp @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GETCPU.CPP 1 3/03/97 10:24a Joe_bostic $*/ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : GETCPU * + * * + * File Name : GETCPU.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 6/26/96 * + * * + * Last Update : June 26th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Example of interface to assembly language code to find CPU type * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Get_CPU_Type -- interface to ASM detection code * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +//#define WIN32 +//#include +#include +#include + + +#define bool int + +/* +** Prototypes for linkage to assembly module +*/ + +//extern "C" { +// bool __cdecl Detect_MMX_Availability(void); +// void __cdecl Init_MMX(void); +// +// extern char CPUType; +// extern char VendorID; +//} + + +/*********************************************************************************************** + * Get_CPU_Type -- Find out what kind of CPU we are running on * + * * + * * + * * + * INPUT: int - reference to cpu type * + * bool - reference to mmx availability flag * + * char* - ptr to buffer to receive chip vendor info * + * int - length of above buffer * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/26/96 10:15AM ST : Created * + *=============================================================================================*/ +void Get_CPU_Type(int & cpu_type, bool & mmx, char * vendor_id, int vendor_id_length) +{ + /* + ** Call the asm CPU detection code + */ + mmx = 1; //Detect_MMX_Availability(); + + /* + ** Return the promised results + */ + cpu_type = 1; //(int)CPUType; + char* vendor_ptr = "Fake CPU"; //&VendorID; + strncpy(vendor_id, vendor_ptr, vendor_id_length); +} + + + + + + + + + + diff --git a/code/redalert/globals.cpp b/code/redalert/globals.cpp new file mode 100644 index 0000000..83acebf --- /dev/null +++ b/code/redalert/globals.cpp @@ -0,0 +1,826 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/GLOBALS.CPP 2 3/10/97 6:22p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GLOBALS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +bool IsVQ640 = false; +unsigned long GameVersion = 0; +bool Debug_MotionCapture = false; +bool Debug_Rotate = false; // Rotation algorithm control. +bool Debug_Quiet = false; +bool Debug_Cheat = false; +bool Debug_Remap = false; +bool Debug_Icon = false; +bool Debug_Flag = true; +bool Debug_Lose = false; +bool Debug_Win = false; +bool Debug_Map = false; // true = map editor mode +bool Debug_Passable = false; // true = show passable/impassable terrain +bool Debug_Unshroud = false; // true = hide the shroud +bool Debug_Threat = false; +bool Debug_Find_Path = false; +bool Debug_Check_Map = false; // true = validate the map each frame +bool Debug_Playtest = false; + +bool Debug_Heap_Dump = false; // true = print the Heap Dump +bool Debug_Smart_Print = false; // true = print everything that calls Smart_Printf +bool Debug_Trap_Check_Heap = false; // true = check the Heap +bool Debug_Modem_Dump = false; // true = print the Modem Stuff +bool Debug_Print_Events = false; // true = print event & packet processing +bool Debug_Force_Crash = false; + +TFixedIHeapClass Aircraft; +TFixedIHeapClass Anims; +TFixedIHeapClass Buildings; +TFixedIHeapClass Bullets; +TFixedIHeapClass Factories; +TFixedIHeapClass Houses; +TFixedIHeapClass Infantry; +TFixedIHeapClass Overlays; +TFixedIHeapClass Smudges; +TFixedIHeapClass Teams; +TFixedIHeapClass TeamTypes; +TFixedIHeapClass Templates; +TFixedIHeapClass Terrains; +TFixedIHeapClass Triggers; +TFixedIHeapClass Units; +TFixedIHeapClass Vessels; +TFixedIHeapClass TriggerTypes; + +TFixedIHeapClass HouseTypes; +TFixedIHeapClass BuildingTypes; +TFixedIHeapClass AircraftTypes; +TFixedIHeapClass InfantryTypes; +TFixedIHeapClass BulletTypes; +TFixedIHeapClass AnimTypes; +TFixedIHeapClass UnitTypes; +TFixedIHeapClass VesselTypes; +TFixedIHeapClass TemplateTypes; +TFixedIHeapClass TerrainTypes; +TFixedIHeapClass OverlayTypes; +TFixedIHeapClass SmudgeTypes; + + +/* +** These are the instantiate static heap pointers for the various +** CCPtr class objects that are allowed to exist. If the linker generates +** an error about a missing heap pointer, then this indicates that CCPtr objects +** for that type are not allowed. For every case of a TFixedIHeap manager of +** game objects, then a CCPtr can be instantiated for it. +*/ +#if (0) //Moved to runtime initialization. ST - 5/20/2019 +template<> FixedIHeapClass * CCPtr::Heap = &Aircraft; +template<> FixedIHeapClass * CCPtr::Heap = &Anims; +template<> FixedIHeapClass * CCPtr::Heap = &Buildings; +template<> FixedIHeapClass * CCPtr::Heap = &Bullets; +template<> FixedIHeapClass * CCPtr::Heap = &Factories; +template<> FixedIHeapClass * CCPtr::Heap = &Houses; +template<> FixedIHeapClass * CCPtr::Heap = &Infantry; +template<> FixedIHeapClass * CCPtr::Heap = &Overlays; +template<> FixedIHeapClass * CCPtr::Heap = &Smudges; +template<> FixedIHeapClass * CCPtr::Heap = &Teams; +template<> FixedIHeapClass * CCPtr::Heap = &TeamTypes; +template<> FixedIHeapClass * CCPtr::Heap = &Templates; +template<> FixedIHeapClass * CCPtr::Heap = &Terrains; +template<> FixedIHeapClass * CCPtr::Heap = &Triggers; +template<> FixedIHeapClass * CCPtr::Heap = &TriggerTypes; + +template<> FixedIHeapClass * CCPtr::Heap = &HouseTypes; +template<> FixedIHeapClass * CCPtr::Heap = &BuildingTypes; +template<> FixedIHeapClass * CCPtr::Heap = &AircraftTypes; +template<> FixedIHeapClass * CCPtr::Heap = &InfantryTypes; +template<> FixedIHeapClass * CCPtr::Heap = &BulletTypes; +template<> FixedIHeapClass * CCPtr::Heap = &AnimTypes; +template<> FixedIHeapClass * CCPtr::Heap = &UnitTypes; +template<> FixedIHeapClass * CCPtr::Heap = &VesselTypes; +template<> FixedIHeapClass * CCPtr::Heap = &TemplateTypes; +template<> FixedIHeapClass * CCPtr::Heap = &TerrainTypes; +template<> FixedIHeapClass * CCPtr::Heap = &OverlayTypes; +template<> FixedIHeapClass * CCPtr::Heap = &SmudgeTypes; +#endif + +/* These variables are used to keep track of the slowest speed of a team */ +MPHType TeamMaxSpeed[MAX_TEAMS]; +SpeedType TeamSpeed[MAX_TEAMS]; +bool FormMove; +SpeedType FormSpeed; +MPHType FormMaxSpeed; + + +char _staging_buffer[32000]; + +/* +** Global flag for the life of Tanya. If this flag is set, she is +** no longer available. +*/ +bool IsTanyaDead; +bool SaveTanya; + +#ifdef FIXIT_ANTS +bool AntsEnabled = false; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool NewUnitsEnabled = false; +bool SecretUnitsEnabled = false; +int MTankDistance = 15; +bool OverrideNewUnitsEnabled = false; // ST - 12/13/2019 12:21PM +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +int CarrierLaunchDelay = 60; +#endif +#endif + +int NewINIFormat = 0; + +bool TimeQuake; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool PendingTimeQuake; +TARGET TimeQuakeCenter; +fixed QuakeUnitDamage=0x300; +fixed QuakeBuildingDamage=0x300; +int QuakeInfantryDamage=25; +int QuakeDelay; +fixed ChronoTankDuration=0x300; // chrono override for chrono tanks +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 +fixed EngineerDamage=0x55; // Amount of damage an engineer does +fixed EngineerCaptureLevel=0x40; // Building damage level before engineer can capture +#endif +#endif + +#ifdef WIN32 +unsigned short Hard_Error_Occured = 0; +WWMouseClass * WWMouse = NULL; +GraphicBufferClass SysMemPage(DEFAULT_SCREEN_WIDTH, 200, (void*)NULL); +WinTimerClass * WindowsTimer=NULL; +int ScreenWidth=3072; +int ScreenHeight=3072; +GraphicBufferClass ModeXBuff; +bool InMovie = FALSE; //Are we currently playing a VQ movie? +HANDLE hInstance; +int AllDone; + + +/*************************************************************************** +** This is true if the game is the currently in focus windows app +** +*/ +bool GameInFocus = false; + +#endif + + +/*************************************************************************** +** Encryption keys. +*/ +PKey FastKey; +#ifdef CHEAT_KEYS +PKey SlowKey; +#endif + +#ifdef FIXIT_NAME_OVERRIDE +/*************************************************************************** +** This is where the name overrides for the units will reside. +*/ +char const * NameOverride[25]; +int NameIDOverride[25]; +#endif + +/*************************************************************************** +** These are the mission control structures. They hold the information about +** how the missions should behave in the system. +*/ +MissionControlClass MissionControl[MISSION_COUNT]; + + +/*************************************************************************** +** There are various tutorial messages that can appear in the game. These +** are called upon by number and pointed to by this array. +*/ +char const * TutorialText[225]; + + +/*************************************************************************** +** This holds the rules database. The rules database won't change during the +** program's run, but may need to be referenced intermitently. +*/ +CCINIClass RuleINI; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +CCINIClass AftermathINI; +#endif + +/*************************************************************************** +** This points to the benchmark objects that are allocated only if the +** machine is running on a Pentium and this is a debug version. +*/ +//Benchmark * Benches; + + +/*************************************************************************** +** General rules that control the game. +*/ +RulesClass Rule; + + +/*************************************************************************** +** All user input, mouse and keyboard, is routed through the object pointed to by this +** user input class pointer. +*/ +UserInputClass UserInput; +EventQueueClass EventQueue; + +/*************************************************************************** +** All keyboard input is routed through the object pointed to by this +** keyboard class pointer. +*/ +KeyboardClass * Keyboard; + + +/*************************************************************************** +** Remap control array. This is used to hold the remap +** tables for the various possible player colors, and the color schemes +** for dialogs. +*/ +RemapControlType ColorRemaps[PCOLOR_COUNT]; + +/* +** Special remap scheme for font that hs to print over metallic tabs +*/ +RemapControlType MetalScheme; + +/* +** This remap table is for special purposes. It consists of dark grey shades, +** and is used for dimming things out. +*/ +RemapControlType GreyScheme; + + +/*************************************************************************** +** This is the source of the random numbers used in the game. This controls +** the game logic and thus must be in sync with any networked machines. +*/ +RandomClass NonCriticalRandomNumber; +RandomStraw CryptRandom; + + +/*************************************************************************** +** This tracks all selected objects per house (for this map). +*/ +SelectedObjectsType CurrentObject; + + +/*************************************************************************** +** This is the game version. +*/ +VersionClass VerNum; + + +/*************************************************************************** +** This is the VQ animation controller structure. It is filled in by reading +** the PLAYER.INI and overridden through program control. +*/ +//VQAConfig AnimControl; + +int PreserveVQAScreen; // Used for screen mode transition control. +bool BreakoutAllowed = true; // "true" if aborting of movies is allowed. +bool Brokeout; // Was the movie broken out of? +bool SlowPalette = false; // Slow palette flag set? + + +/*************************************************************************** +** These are the movie names to use for mission briefing, winning, and losing +** sequences. They are read from the INI file. +*/ +ScenarioClass Scen; + + +/*************************************************************************** +** This is the pending speech sample to play. This sample will be played +** at the first opportunity. +*/ +VoxType SpeakQueue = VOX_NONE; + + +/*************************************************************************** +** This records if the score (music) file is present. If not, then much of +** the streaming score system can be disabled. +*/ +bool ScoresPresent; + + +/*************************************************************************** +** This flag will control whether there is a response from game units. +** By carefully controlling this global, multiple responses are suppressed +** when a large group of infantry is given the movement order. +*/ +bool AllowVoice = true; + + +/*************************************************************************** +** This is the current frame number. This number is guaranteed to count +** upward at the rate of one per game logic process. The target rate is 15 +** per second. This value is saved and restored with the saved game. +*/ +long Frame = 0; + + +/*************************************************************************** +** These globals are constantly monitored to determine if the player +** has won or lost. They get set according to the trigger events associated +** with the scenario. +*/ +bool PlayerWins; +bool PlayerLoses; +bool PlayerRestarts; + +/* +** This flag is set if the player neither wins nor loses; it's mostly for +** multiplayer mode. +*/ +bool PlayerAborts; + + +/*************************************************************************** +** This is the pointer for the speech staging buffer. This buffer is used +** to hold the currently speaking voice data. Since only one speech sample +** is played at a time, this buffer is only as big as the largest speech +** sample that can be played. +*/ +void * SpeechBuffer[2]; +VoxType SpeechRecord[2]; + + +/*************************************************************************** +** The theater specific mixfiles are cached into the buffer pointed to by +** this global. +*/ +Buffer * TheaterBuffer; + + +/*************************************************************************** +** This is a running accumulation of the number of ticks that were unused. +** This accumulates into a useful value that contributes to a +** histogram of game performance. +*/ +long SpareTicks; +long PathCount; // Number of findpaths called. +long CellCount; // Number of cells redrawn. +long TargetScan; // Number of target scans. +long SidebarRedraws; // Number of sidebar redraws. + + +/*************************************************************************** +** This is the monochrome debug page array. The various monochrome data +** screens are located here. +*/ +MonoClass MonoArray[DMONO_COUNT]; +DMonoType MonoPage = DMONO_STRESS; // The current page. + + +/*************************************************************************** +** This holds the theater specific mixfiles. +*/ +MFCD * TheaterData = 0; +MFCD * MoviesMix = 0; +MFCD * GeneralMix = 0; +MFCD * ScoreMix = 0; +MFCD * MainMix = 0; +MFCD * ConquerMix = 0; + + +/*************************************************************************** +** This is the options control class. The options control such things as +** game speed, visual controls, and other user settings. +*/ +GameOptionsClass Options; + + +/*************************************************************************** +** Logic processing is controlled by this element. It handles both graphic +** and AI logic. +*/ +LogicClass Logic; + + +/*************************************************************************** +** This handles the background music. +*/ +ThemeClass Theme; + + +/*************************************************************************** +** This is the main control class for the map. +*/ +#ifdef SCENARIO_EDITOR +MapEditClass Map; +#else +MouseClass Map; +#endif + + +/************************************************************************** +** The running game score is handled by this class (and member functions). +*/ +ScoreClass Score; + + +/*************************************************************************** +** The running credit display is controlled by this class (and member +** functions. +*/ +CreditClass CreditDisplay; + + +/************************************************************************** +** This class records the special command override options that C&C +** supports. +*/ +SpecialClass Special; + + +bool PassedProximity; // used in display.cpp + + +/*************************************************************************** +** This is the scenario data for the currently loaded scenario. +** These variables should all be set together. +*/ +HousesType Whom; // Initial command line house choice. +int ScenarioInit; +bool SpecialFlag = false; + + +/*************************************************************************** +** This value tells the sidebar what items it's allowed to add. The +** lower the value, the simpler the sidebar will be. This value is the +** displayed value for tech level in the multiplay dialogs. It remaps to +** the in-game rules.ini tech levels. +*/ +int BuildLevel = 10; // Buildable level (1 = simplest) + + +/*************************************************************************** +** The various tutor and dialog messages are located in the data block +** referenced by this pointer. +*/ +char const * SystemStrings; +char const * DebugStrings; + + +/*************************************************************************** +** The game plays as long as this var is true. +*/ +bool GameActive; + + +/*************************************************************************** +** This is a scratch variable that is used to when a reference is needed to +** a long, but the value wasn't supplied to a function. This is used +** specifically for the default reference value. As such, it is not stable. +*/ +long LParam; + + +#ifdef SCENARIO_EDITOR +/*************************************************************************** +** The currently-selected cell for the Scenario Editor +*/ +CELL CurrentCell = 0; +#endif + + +/*************************************************************************** +** Most of the text in the game will use the six point font. These are the +** pointers to the fonts. If it is NULL, then the font hasn't been loaded +** yet. +*/ +void const * Metal12FontPtr; //Font for use on in-game tabs in hires +void const * MapFontPtr; // Standard very small font. +void const * TypeFontPtr; // Teletype font for mission briefings. +void const * Font3Ptr; // Standard very small font. +void const * Font6Ptr; // Standard small font. +void const * EditorFont; // Font used for scenario editor. +void const * Font8Ptr; // 8 point proportional. +void const * FontLEDPtr; // LED fixed point font. +void const * VCRFontPtr; // VCR font pointer. +void const * ScoreFontPtr; // font for score & map selection screens +void const * GradFont6Ptr; // gradient 6 point font pointer. + + +/*************************************************************************** +** This is the house that the human player is currently playing. +*/ +HouseClass * PlayerPtr; + + +/*************************************************************************** +** Special palettes for MCGA mode goes here. These palette buffers are used +** for pictures that do not use the game palette or are used for fading to +** black. +*/ +PaletteClass CCPalette; +PaletteClass GamePalette; +//PaletteClass InGamePalette; +PaletteClass BlackPalette(RGBClass(0, 0, 0)); +PaletteClass WhitePalette(RGBClass(RGBClass::MAX_VALUE, RGBClass::MAX_VALUE, RGBClass::MAX_VALUE)); +PaletteClass OriginalPalette; +PaletteClass ScorePalette; + + +/*************************************************************************** +** These are the event queues. One is for holding events until they are ready to be +** sent to the remote computer for processing. The other list is for incoming events +** that need to be executed when the correct frame has been reached. +*/ +QueueClass OutList; +QueueClass DoList; + +#ifdef MIRROR_QUEUE +QueueClass MirrorList; +#endif + + +/*************************************************************************** +** These are arrays/lists of trigger pointers for each cell & the houses. +*/ +DynamicVectorClass HouseTriggers[HOUSE_COUNT]; +DynamicVectorClass MapTriggers; +int MapTriggerID; +DynamicVectorClass LogicTriggers; +int LogicTriggerID; + + +/*************************************************************************** +** This is the list of BuildingTypes that define the AI's base. +*/ +BaseClass Base; + + +/*************************************************************************** +** This is the list of carry over objects. These objects are part of the +** pseudo saved game that might be carried along with the current saved +** game. +*/ +CarryoverClass * Carryover; + + +/*************************************************************************** +** This value is computed every time a new scenario is loaded; it's a +** CRC of the INI and binary map files. +*/ +unsigned long ScenarioCRC; + + +/*************************************************************************** +** This class manages data specific to multiplayer games. +*/ +SessionClass Session; +#if(TIMING_FIX) +// +// These values store the min & max frame #'s for when MaxAhead >>increases<<. +// If MaxAhead increases, and the other systems free-run to the new MaxAhead +// value, they may miss an event generated after the MaxAhead event was sent, +// but before it executed, since it will have been scheduled with the older, +// shorter MaxAhead value. This will cause a Packet_Received_Too_Late error. +// The frames from the point where the new MaxAhead takes effect, up to that +// frame Plus the new MaxAhead, represent a "period of vulnerability"; any +// events received that are scheduled to execute during this period should +// be re-scheduled for after that period. +// +int NewMaxAheadFrame1; +int NewMaxAheadFrame2; +#endif + +#ifdef FIXIT_VERSION_3 +bool bAftermathMultiplayer; // Is multiplayer game being played with Aftermath rules? +#else +unsigned long PlayingAgainstVersion; // Negotiated version number +bool Version107InMix; // Is there a v1.07 in the game +#endif + +/*************************************************************************** +** This is the null modem manager class. Declaring this class doesn't +** perform any allocations; +*/ +#if (0) +NullModemClass NullModem ( + 16, // number of send entries + 16, // number of receive entries + (MAX_SERIAL_PACKET_SIZE / sizeof(EventClass) ) * sizeof(EventClass) + sizeof( CommHeaderType ), + 0x1234); // Magic number must have each digit unique + // and different from the queue magic number +#endif + + +/*************************************************************************** +** This is the network IPX manager class. It handles multiple remote +** connections. Declaring this class doesn't perform any allocations; +** the class itself is 140 bytes. +*/ +//IPXManagerClass Ipx ( +// MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets +// ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), +// 10, // # entries in Global Queue +// 8, // # entries in Private Queues +// VIRGIN_SOCKET, // Socket ID # +// IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + +IPXManagerClass Ipx ( + MAX (sizeof (GlobalPacketType), sizeof(RemoteFileTransferType)), // size of Global Channel packets + ((546 - sizeof(CommHeaderType)) / sizeof(EventClass) ) * sizeof(EventClass), + 160, // # entries in Global Queue + 32, // # entries in Private Queues + VIRGIN_SOCKET, // Socket ID # + IPXGlobalConnClass::COMMAND_AND_CONQUER0);// Product ID # + + +#if(TEN) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - TEN connection ID's are equal to the HousesType for that player. +** - The TEN internal player ID is used to determine the player's color. +** - Ten's broadcast destination address -1 +*/ +TenConnManClass *Ten = NULL; + +#endif + + +#if(MPATH) +/*************************************************************************** +** This is the connection manager for Ten. Special Ten notes: +** - MPATH connection ID's are equal to the HousesType for that player. +** - The player's color is read from the OPTIONS.INI file +** - MPath's broadcast destination address is 0 +*/ +MPlayerManClass *MPath = NULL; + +#endif + + +/*************************************************************************** +** This is the random-number seed; it's synchronized between systems for +** multiplayer games. +*/ +int Seed = 0; + + +/*************************************************************************** +** If this value is non-zero, use it as the random # seed instead; this should +** help reproduce some bugs. +*/ +int CustomSeed = 0; + +int WindowList[][9] = { +/* xbyte, ypixel, bytewid, pixelht, cursor color, bkgd color, cursor x, cursor y */ + + /* do not change the first 2 entries!! they are necc. to the system */ + + {0,0,40*8*RESFACTOR,200*RESFACTOR,WHITE,BLACK,0,0}, /* screen window */ + {1*8,75,38*8,100,WHITE,BLACK,0,0}, /* DOS Error window */ + + // Tactical map. + {0, 0, 40*8*RESFACTOR, 200*RESFACTOR, WHITE,LTGREY,0,0}, + + // Initial menu window. + {12*8, 199-42, 16*8, 42, LTGREY, DKGREY, 0, 0}, + + // Sidebar clipping window. + {0,0,0,0,0,0,0,0}, + + // Scenario editor window. + {5*8, 30, 30*8, 140, 0, 0, 0, 0}, + + // Partial object draw sub-window. + {0,0,0,0,WHITE,BLACK,0,0}, + + // Custom window. + {0, 0, 0, 0, 0, 0, 0, 0}, + + // Virtual window for external rendering. ST - 1/15/2019 3:02PM + {0, 0, 0, 0, 0, 0, 0, 0} + +}; + + +/* X,Y,Item Width,Items High,Selected,Norm Color,Sel Color,zero */ +int MenuList[][8]={ + {1, 3, 12, 3, 0, WHITE, PINK, 0}, +}; + + +#ifdef WIN32 +GraphicBufferClass VisiblePage; +GraphicBufferClass HiddenPage; +GraphicViewPortClass SeenBuff(&VisiblePage, 0, 0, 3072, 3072); +GraphicViewPortClass HidPage(&HiddenPage, 0, 0, 3072, 3072); +#else +GraphicBufferClass HidPage(DEFAULT_SCREEN_WIDTH, 201, (void*)NULL); +GraphicBufferClass SeenBuff(320, 200, (void *)0xA0000L); +VideoBufferClass SeenPage; +GraphicBufferClass & VisiblePage = SeenBuff; +#endif + + +#ifdef WIN32 +#else +#endif + +int SoundOn; +CDTimerClass FrameTimer; +CDTimerClass CountDownTimer; + +NewConfigType NewConfig; +TheaterType LastTheater = THEATER_NONE; //Lets us know when theater type changes. + + +/*************************************************************************** +** This flag is for popping up dialogs that call the main loop. +*/ +SpecialDialogType SpecialDialog = SDLG_NONE; + + +int RequiredCD = -1; +int CurrentCD = -1; +int MouseInstalled; + +// +// Variables for helping track how much time goes bye in routines +// +int LogLevel = 0; +unsigned long LogLevelTime[ MAX_LOG_LEVEL ] = { 0 }; +unsigned long LogLastTime = 0; +bool LogDump_Print = false; // true = print the Log time Stuff + + +/*************************************************************************** +** Tick Count global timer object. +*/ +TTimerClass TickCount = 0; + + +/*************************************************************************** +** Win32 specific globals +*/ +#ifdef WIN32 + +bool InDebugger = false; +int ReadyToQuit = 0; + +#else +bool IsTheaterShape = false; // must be defined only if not Win32 +#endif //WIN32 + +//PG GetCDClass CDList; +int UnitBuildPenalty = 100; + +#ifdef MPEGMOVIE // Denzil 6/15/98 +#ifdef MCIMPEG +#include "mcimovie.h" +MCIMovie* MciMovie = NULL; +#endif + +#include "mpgset.h" +MPGSettings* MpgSettings = NULL; +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +bool bAutoSonarPulse = false; +#endif + +bool MMXAvailable = false; + + +// ST - 5/14/2019 +bool RunningAsDLL = false; +bool RunningFromEditor = false; diff --git a/code/redalert/goptions.cpp b/code/redalert/goptions.cpp new file mode 100644 index 0000000..c538891 --- /dev/null +++ b/code/redalert/goptions.cpp @@ -0,0 +1,610 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/GOPTIONS.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#include "goptions.h" +#include "loaddlg.h" +#include "sounddlg.h" +#include "visudlg.h" +#include "gamedlg.h" +#include "textbtn.h" +#include "descdlg.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +bool RedrawOptionsMenu; + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + * 06/23/1995 JLB : Handles restating the mission objective. * + * 07/27/1995 JLB : Adjusts menu for multiplay mode. * + *=============================================================================================*/ +void GameOptionsClass::Process(void) +{ + static struct { + int ID; // Button ID to use. + int Text; // Text number to use for this button. + bool Multiplay; // Allowed in multiplayer version? + } _constants[] = { + {BUTTON_LOAD, TXT_LOAD_MISSION, false}, +#ifdef FIXIT_MULTI_SAVE + {BUTTON_SAVE, TXT_SAVE_MISSION, true}, +#else + {BUTTON_SAVE, TXT_SAVE_MISSION, false}, +#endif + {BUTTON_DELETE, TXT_DELETE_MISSION, true}, + {BUTTON_GAME, TXT_GAME_CONTROLS, true}, + {BUTTON_QUIT, TXT_QUIT_MISSION, true}, +#ifdef FIXIT_VERSION_3 // Stalemate games. + {BUTTON_DRAW, TXT_OK, true}, +#endif + {BUTTON_RESUME, TXT_RESUME_MISSION, true}, + {BUTTON_RESTATE, TXT_RESTATE_MISSION, false}, + }; + + /* + ** Variables. + */ + TextButtonClass * buttons = 0; + int selection; + bool pressed; +#ifdef FIXIT_VERSION_3 // Stalemate games. + int curbutton = 7; +#else + int curbutton = 6; +#endif + int y; + TextButtonClass * buttonsel[ARRAY_SIZE(_constants)]; + static int num_buttons = sizeof(_constants)/sizeof(_constants[0]); + + + int num_players = 0; + int i; + + // + // Compute the number of real players in the game; only allow saves + // if there are more than 1. + // + for (i = 0; i < Session.Players.Count(); i++) { + if (!(HouseClass::As_Pointer(Session.Players[i]->Player.ID)->IsDefeated)) { + num_players++; + } + } + + + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list for all of the buttons for this dialog. + */ + int maxwidth = 0; + + for (int index = 0; index < num_buttons ; index++ ) { + int text = _constants[index].Text; + buttonsel[index] = NULL; + + if (Session.Type != GAME_NORMAL && !_constants[index].Multiplay) { + continue; + } + + if ( (Session.Type == GAME_SKIRMISH || + Session.Type == GAME_INTERNET) && text == TXT_SAVE_MISSION) { + continue; + } + +#ifdef FIXIT_VERSION_3 + if (Session.Type != GAME_NORMAL && ( num_players < 2 ) && + text == TXT_SAVE_MISSION) { + continue; + } +#else +#ifdef FIXIT_MULTI_SAVE + if (Session.Type != GAME_NORMAL && (num_players < 2 || PlayingAgainstVersion == VERSION_RED_ALERT_104) && + text == TXT_SAVE_MISSION) { + continue; + } +#endif //FIXIT_MULTI_SAVE +#endif + + if (Session.Type == GAME_SKIRMISH && text == TXT_DELETE_MISSION) { + continue; + } + + if (Session.Type != GAME_NORMAL && text == TXT_DELETE_MISSION) { + text = TXT_RESIGN; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + if (index < 6) { +#else + if (index < 5) { +#endif + y = (SeenBuff.Get_Height() - OptionHeight)/2 + ButtonY + ((OButtonHeight+2) * index); + } else { + y = OptionY + ButtonResumeY; + } + +#ifdef FIXIT_VERSION_3 // Stalemate games. + TextButtonClass* g; + if( _constants[index].ID == BUTTON_DRAW ) + { + if( Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && Session.Players.Count() == 2 ) + { + if( Scen.bLocalProposesDraw ) + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_RETRACT_DRAW, TPF_BUTTON, 0, y ); + else + continue; // Game will end now anyway. + } + else + { + if( !Scen.bOtherProposesDraw ) + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_PROPOSE_DRAW, TPF_BUTTON, 0, y ); + else + g = new TextButtonClass( BUTTON_DRAW, TXT_WOL_ACCEPT_DRAW, TPF_BUTTON, 0, y ); + } + } + else + continue; + } + else + g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#else + TextButtonClass * g = new TextButtonClass(_constants[index].ID, text, TPF_BUTTON, 0, y); +#endif + + if (g->Width > maxwidth) { + maxwidth = g->Width; + } + if (buttons == NULL) { + buttons = g; + } else { + g->Add_Tail(*buttons); + } + + buttonsel[index] = g; + } + + /* + ** BG: In skirmish mode, there is no 'restate' button, so we have to + ** backtrack through the list to find the last valid button. + */ + while(!buttonsel[curbutton-1]) curbutton--; + + buttonsel[curbutton-1]->Turn_On(); + + /* + ** Force all button lengths to match the maximum length of the widest button. + */ + GadgetClass * g = buttons; + while (g != NULL) { + g->Width = max(maxwidth, 90 * RESFACTOR); + g->X = OptionX+(OptionWidth-g->Width)/2; + g = g->Get_Next(); + } +//#ifdef FRENCH +// buttonsel[BUTTON_RESUME-1]->Width = 110 * RESFACTOR; +// buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR) - 5; +//#else + buttonsel[BUTTON_RESUME-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESUME-1]->X = OptionX + (17 * RESFACTOR); +//#endif + + if (Session.Type == GAME_NORMAL) { + buttonsel[BUTTON_RESTATE-1]->Width = 90 * RESFACTOR; + buttonsel[BUTTON_RESTATE-1]->X = OptionX+OptionWidth-(buttonsel[BUTTON_RESTATE-1]->Width+(17 * RESFACTOR)); + } + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + (new GadgetClass(OptionX, OptionY, OptionWidth, OptionHeight, GadgetClass::LEFTPRESS))->Add_Tail(*buttons); + + /* + ** This cause a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to game button. + */ + (new ControlClass(BUTTON_RESUME, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS))->Add_Tail(*buttons); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_TEXT); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + pressed = false; + while (process) { + //Device_Present(); + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + { + + /* + ** Redraw the map. + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + /* + ** Reset up the window. Window x-coords are in bytes not pixels. + */ + Set_Window(WINDOW_EDITOR, OptionX, OptionY, OptionWidth, OptionHeight); + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(OptionX, OptionY, OptionWidth, OptionHeight); + + /* + ** Draw the arrows border if requested. + */ + Draw_Caption(TXT_OPTIONS, OptionX, OptionY, OptionWidth); + + /* + ** Display the version number at the bottom of the dialog box. + */ + Fancy_Text_Print("%s\rV%s", + (OptionX+OptionWidth)-(25 * RESFACTOR), + OptionY+OptionHeight-((Session.Type == GAME_NORMAL) ? (32 * RESFACTOR) : (24 * RESFACTOR)), + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Scen.ScenarioName, + Version_Name()); + + buttons->Draw_All(); + TabClass::Hilite_Tab(0); + Show_Mouse(); + display = false; + RedrawOptionsMenu = false; + } + + /* + ** Get user input. + */ + KeyNumType input = buttons->Input(); + + /* + ** Process Input. + */ + switch (input) { + case (BUTTON_RESTATE | KN_BUTTON): + selection = BUTTON_RESTATE; + pressed = true; + break; + + case (BUTTON_LOAD | KN_BUTTON): + selection = BUTTON_LOAD; + pressed = true; + break; + + case (BUTTON_SAVE | KN_BUTTON): + selection = BUTTON_SAVE; + pressed = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + selection = BUTTON_DELETE; + pressed = true; + break; + + case (BUTTON_QUIT | KN_BUTTON): + selection = BUTTON_QUIT; + pressed = true; + break; + + case (BUTTON_GAME | KN_BUTTON): + selection = BUTTON_GAME; + pressed = true; + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case (BUTTON_DRAW | KN_BUTTON): + selection = BUTTON_DRAW; + pressed = true; + break; +#endif + + case (KN_ESC): + case (BUTTON_RESUME | KN_BUTTON): + selection = BUTTON_RESUME; + pressed = true; + break; + + case (KN_UP): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton--; + if (curbutton < 1) curbutton = num_buttons; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + do { + curbutton++; + if ( curbutton > num_buttons ) curbutton = 1; + } while (!buttonsel[curbutton-1]); + + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + buttonsel[curbutton-1]->IsPressed = true; + buttonsel[curbutton-1]->Draw_Me(true); + selection = curbutton; + pressed = true; + Keyboard->Clear(); + break; + + default: + break; + } + + if (pressed) { + + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + curbutton = selection; + buttonsel[curbutton-1]->Turn_On(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + + switch (selection) { + case BUTTON_RESTATE: + display = true; + if (!Restate_Mission(Scen.ScenarioName, TXT_VIDEO, TXT_RESUME_MISSION/*KOTXT_OPTIONS*/)) { + BreakoutAllowed = true; + Play_Movie(Scen.BriefMovie); + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + + Map.Flag_To_Redraw(true); + Theme.Queue_Song(THEME_PICK_ANOTHER); + process = false; + } else { + BlackPalette.Adjust(0x08, WhitePalette); + BlackPalette.Set(); + BlackPalette.Adjust(0xFF); + BlackPalette.Set(); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + process = false; + } + break; + + case (BUTTON_LOAD): + display = true; + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + process = false; + } + break; + + case (BUTTON_SAVE): + display = true; + if (Session.Type == GAME_NORMAL) { + LoadOptionsClass(LoadOptionsClass::SAVE).Process(); + + } else { + OutList.Add(EventClass(EventClass::SAVEGAME)); + process = false; + } + break; + + case (BUTTON_DELETE): + display = true; + if (Session.Type != GAME_NORMAL) { + if (Surrender_Dialog(TXT_SURRENDER)) { + OutList.Add(EventClass(EventClass::DESTRUCT)); + } + process = false; + } else { + LoadOptionsClass(LoadOptionsClass::WWDELETE).Process(); + } + break; + + case (BUTTON_QUIT): + if (Session.Type == GAME_NORMAL) { + switch (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_ABORT, TXT_CANCEL, TXT_RESTART)) { + case 1: + display = true; + break; + + case 0: + process = false; + Queue_Exit(); + break; + + case 2: + PlayerRestarts = true; + process = false; + break; + } + } else { + if (Surrender_Dialog(TXT_CONFIRM_EXIT)) { + process = false; + Queue_Exit(); + } else { + display = true; + } + //if (WWMessageBox().Process(TXT_CONFIRM_EXIT, TXT_YES, TXT_NO) == 0) { + //process = false; + //Queue_Exit(); + //} else { + //display = true; + //} + } + break; + +#ifdef FIXIT_VERSION_3 // Stalemate games. + case BUTTON_DRAW: + if( Scen.bLocalProposesDraw ) + { + // Retract draw offer. + OutList.Add(EventClass(EventClass::RETRACT_DRAW)); + process = false; + } + else + { + if( !Scen.bOtherProposesDraw ) + { + // Propose a draw? + if( Surrender_Dialog( TXT_WOL_PROPOSE_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + else + { + // Accept a draw? + if( Surrender_Dialog( TXT_WOL_ACCEPT_DRAW_CONFIRM ) ) + { + OutList.Add(EventClass(EventClass::PROPOSE_DRAW)); + process = false; + } + else + display = true; + } + } + break; +#endif + + case (BUTTON_GAME): + display = true; + GameControlsClass().Process(); + break; + + case (BUTTON_RESUME): + Save_Settings(); + process = false; + display = true; + break; + } + + pressed = false; + buttonsel[curbutton-1]->IsPressed = false; + buttonsel[curbutton-1]->Turn_Off(); + buttonsel[curbutton-1]->Flag_To_Redraw(); + } + } + + /* + ** Clean up and re-enter the game. + */ + buttons->Delete_List(); + + /* + ** Redraw the map. + */ + Keyboard->Clear(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); +} + + +void GameOptionsClass::Adjust_Variables_For_Resolution(void) +{ + OptionWidth = (216+8) * RESFACTOR; +#ifdef FIXIT_VERSION_3 // Stalemate games. + OptionHeight = 111 * RESFACTOR; +#else + OptionHeight = 100 * RESFACTOR; +#endif + OptionX = ((SeenBuff.Get_Width() - OptionWidth) / 2); + OptionY = ((SeenBuff.Get_Height() - OptionHeight) / 2); + ButtonWidth = 130 * RESFACTOR; + OButtonHeight = 9 * RESFACTOR; + CaptionYPos = 5 * RESFACTOR; + ButtonY = 21 * RESFACTOR; + Border1Len = 72 * RESFACTOR; + Border2Len = 16 * RESFACTOR; + ButtonResumeY = (OptionHeight - (19 * RESFACTOR)); +} diff --git a/code/redalert/goptions.h b/code/redalert/goptions.h new file mode 100644 index 0000000..06e72ac --- /dev/null +++ b/code/redalert/goptions.h @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GOPTIONS.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GOPTIONS_H +#define GOPTIONS_H + +#include "options.h" +#include "gadget.h" + + +class GameOptionsClass : public OptionsClass { + enum GameOptionsButtonEnum { + BUTTON_LOAD=1, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_GAME, + BUTTON_QUIT, +#ifdef FIXIT_VERSION_3 // Stalemate games. + BUTTON_DRAW, +#endif + BUTTON_RESUME, + BUTTON_RESTATE, + + BUTTON_COUNT + }; + + enum GameOptionsEnum { + OPTION_WIDTH=(216+8), + OPTION_HEIGHT=100, + OPTION_X=((320 - (216+8)) / 2), + OPTION_Y=((200 - 100) / 2), +#ifdef FRENCH + BUTTON_WIDTH=142, +#else + BUTTON_WIDTH=130, +#endif + NUMBER_OF_BUTTONS=6, // ajw Not used. + CAPTION_Y_POS=5, + BUTTON_Y=21, + BORDER1_LEN=72, + BORDER2_LEN=16, + BUTTON_RESUME_Y=(100-15) + }; + + public: + GameOptionsClass(void): OptionsClass () { }; + void Adjust_Variables_For_Resolution(void); + void Process(void); + + private: + int OptionWidth; + int OptionHeight; + int OptionX; + int OptionY; + int ButtonWidth; + int OButtonHeight; + int CaptionYPos; + int ButtonY; + int Border1Len; + int Border2Len; + int ButtonResumeY; + +}; + +#endif diff --git a/code/redalert/gscreen.cpp b/code/redalert/gscreen.cpp new file mode 100644 index 0000000..e315b54 --- /dev/null +++ b/code/redalert/gscreen.cpp @@ -0,0 +1,487 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GSCREEN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * GScreenClass::Input -- Fetches input and processes gadgets. * + * GScreenClass::One_Time -- Handles one time class setups. * + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +GadgetClass * GScreenClass::Buttons = 0; + +GraphicBufferClass * GScreenClass::ShadowPage = 0; + + +/*********************************************************************************************** + * GScreenClass::GScreenClass -- Default constructor for GScreenClass. * + * * + * This constructor merely sets the display system, so that it will redraw the first time * + * the render function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +GScreenClass::GScreenClass(void) +{ + IsToUpdate = true; + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::One_Time -- Handles one time class setups. * + * * + * This routine (and all those that overload it) must perform truly one-time initialization. * + * Such init's would normally be done in the constructor, but other aspects of the game may * + * not have been initialized at the time the constructors are called (such as the file system, * + * the display, or other WWLIB subsystems), so many initializations should be deferred to the * + * One_Time init's. * + * * + * Any variables set in this routine should be declared as static, so they won't be modified * + * by the load/save process. Non-static variables will be over-written by a loaded game. * + * * + * This function allocates the shadow buffer that is used for quick screen updates. If * + * there were any data files to load, they would be loaded at this time as well. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::One_Time(void) +{ + /* + ** Allocate the screen shadow page. This page is used to reduce access to the + ** actual screen memory. It contains a duplicate of what the SEENPAGE is. + */ + Buttons = 0; + ShadowPage = new GraphicBufferClass(320, 200); + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } +} + + +/*********************************************************************************************** + * GScreenClass::Init -- Init's the entire display hierarchy by calling all Init routines. * + * * + * This routine shouldn't be overloaded. It's the main map initialization routine, and will * + * perform a complete map initialization, from mixfiles to clearing the buffers. Calling this * + * routine results in calling every initialization routine in the entire map hierarchy. * + * * + * INPUT: * + * theater theater to initialize to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init(TheaterType theater) +{ + Init_Clear(); + Init_IO(); + Init_Theater(theater); +} + + +/*********************************************************************************************** + * GScreenClass::Init_Clear -- Sets the map to a known state. * + * * + * This routine (and those that overload it) clears any buffers and variables to a known * + * state. It assumes that all buffers are allocated & valid. The map should be displayable * + * after calling this function, and should draw basically an empty display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Clear(void) +{ + /* + ** Clear the ShadowPage & HidPage to force a complete shadow blit. + */ + if (ShadowPage) { + ShadowPage->Clear(); + HidPage.Clear(); + } + + IsToRedraw = true; +} + + +/*********************************************************************************************** + * GScreenClass::Init_Theater -- Performs theater-specific initializations. * + * * + * This routine (and those that overload it) performs any theater-specific initializations * + * needed. This will include setting the palette, setting up remap tables, etc. This routine * + * only needs to be called when the theater has changed. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_Theater(TheaterType ) +{ +} + + +/*********************************************************************************************** + * GScreenClass::Init_IO -- Initializes the Button list ('Buttons'). * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + *=============================================================================================*/ +void GScreenClass::Init_IO(void) +{ + /* + ** Reset the button list. This means that any other elements of the map that need + ** buttons must attach them after this routine is called! + */ + Buttons = 0; +} + + +/*********************************************************************************************** + * GScreenClass::Flag_To_Redraw -- Flags the display to be redrawn. * + * * + * This function is used to flag the display system whether any rendering is needed. The * + * parameter tells the system either to redraw EVERYTHING, or just that something somewhere * + * has changed and the individual Draw_It functions must be called. When a sub system * + * determines that it needs to render something local to itself, it would call this routine * + * with a false parameter. If the entire screen gets trashed or needs to be rebuilt, then * + * this routine will be called with a true parameter. * + * * + * INPUT: complete -- bool; Should the ENTIRE screen be redrawn? * + * * + * OUTPUT: none * + * * + * WARNINGS: This doesn't actually draw the screen, it merely sets flags so that when the * + * Render() function is called, the appropriate drawing steps will be performed. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Flag_To_Redraw(bool complete) +{ + IsToUpdate = true; + if (complete) { + IsToRedraw = true; + } +} + + +/*********************************************************************************************** + * GScreenClass::Input -- Fetches input and processes gadgets. * + * * + * This routine will fetch the keyboard/mouse input and dispatch this through the gadget * + * system. * + * * + * INPUT: key -- Reference to the key code (for future examination). * + * * + * x,y -- Reference to mouse coordinates (for future examination). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Input(KeyNumType & key, int & x, int & y) +{ + if (this == NULL) return; + + key = Keyboard->Check(); + + x = Keyboard->Mouse_X(); + y = Keyboard->Mouse_Y(); + + Show_Mouse(false); + + if (Buttons != NULL) { + + /* + ** If any buttons need redrawing, they will do so in the Input routine, and + ** they should draw themselves to the HidPage. So, flag ourselves for a Blit + ** to show the newly drawn buttons. + */ + if (Buttons->Is_List_To_Redraw()) { + Flag_To_Redraw(false); + } + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); +#endif + + key = Buttons->Input(); + + Set_Logic_Page(oldpage); + + } else { + + if (key != 0) { + key = Keyboard->Get(); + } + } + + AI(key, x, y); +} + + +/*********************************************************************************************** + * GScreenClass::Add_A_Button -- Add a gadget to the game input system. * + * * + * This will add a gadget to the game input system. The gadget will be processed in * + * subsequent calls to the GScreenClass::Input() function. * + * * + * INPUT: gadget -- Reference to the gadget that will be added to the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Add_A_Button(GadgetClass & gadget) +{ + /* + ** If this gadget is already in the list, remove it before adding it in: + ** - If 1st gadget in list, use Remove_A_Button to remove it, to reset the + ** value of 'Buttons' appropriately + ** - Otherwise, just call the Remove function for that gadget to remove it + ** from any list it may be in + */ + if (Buttons == &gadget) { + Remove_A_Button(gadget); + } else { + gadget.Remove(); + } + + /* + ** Now add the gadget to our list: + ** - If there are not buttons, start the list with this one + ** - Otherwise, add it to the tail of the existing list + */ + if (Buttons) { + gadget.Add_Tail(*Buttons); + } else { + Buttons = &gadget; + } +} + + +/*********************************************************************************************** + * GScreenClass::Remove_A_Button -- Removes a gadget from the game input system. * + * * + * INPUT: gadget -- Reference to the gadget that will be removed from the input system. * + * * + * OUTPUT: none * + * * + * WARNINGS: 'gadget' MUST be already a part of 'Buttons', or the new value of 'Buttons' * + * will be invalid! * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Remove_A_Button(GadgetClass & gadget) +{ + Buttons = gadget.Remove(); +} + + +/*********************************************************************************************** + * GScreenClass::Render -- General drawing dispatcher an display update function. * + * * + * This routine should be called in the main game loop (once every game frame). It will * + * call the Draw_It() function if necessary. All rendering is performed to the LogicPage * + * which is set to the HIDPAGE. After rendering has been performed, the HIDPAGE is * + * copied to the visible page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This actually updates the graphic display. As a result it can take quite a * + * while to perform. * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void GScreenClass::Render(void) +{ + //This is unnessasary surely? ST - 10/16/96 2:30PM + //if (Buttons && Buttons->Is_List_To_Redraw()) { + // IsToRedraw = true; + //} + IsToRedraw = true; + if (IsToUpdate || IsToRedraw) { + BStart(BENCH_GSCREEN_RENDER); + +#ifdef WIN32 + GraphicViewPortClass * oldpage= Set_Logic_Page(HidPage); +#else + GraphicBufferClass * oldpage= Set_Logic_Page(HidPage); + + if (IsToRedraw) { + Hide_Mouse(); + SeenPage.To_Buffer(0, 0, 320, 200, ShadowPage); + Show_Mouse(); + } +#endif + + Draw_It(IsToRedraw); + + if (Buttons) Buttons->Draw_All(false); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the Editor's buttons + */ + if (Debug_Map) { + if (Buttons) { + Buttons->Draw_All(); + } + } +#endif + /* + ** Draw the multiplayer message system to the Hidpage at this point. + ** This way, they'll Blit along with the rest of the map. + */ + if (Session.Messages.Num_Messages() > 0) { + Session.Messages.Set_Width( + Lepton_To_Cell(Map.TacLeptonWidth) * ICON_PIXEL_W); + } + Session.Messages.Draw(); + + Blit_Display(); + IsToUpdate = false; + IsToRedraw = false; + + BEnd(BENCH_GSCREEN_RENDER); + Set_Logic_Page(oldpage); + } +} + + +/*********************************************************************************************** + * GScreenClass::Blit_Display -- Redraw the display from the hidpage to the seenpage. * + * * + * This routine is used to copy the correct display from the HIDPAGE * + * to the SEENPAGE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/14/1994 JLB : Created. * + * 05/01/1994 JLB : Converted to member function. * + *=============================================================================================*/ +extern "C" { + void ModeX_Blit (GraphicBufferClass * source); +} + +void GScreenClass::Blit_Display(void) +{ + BEnd(BENCH_BLIT_DISPLAY); +#ifdef WIN32 + if (SeenBuff.Get_Width()!=320) { + WWMouse->Draw_Mouse(&HidPage); + //HidPage.Blit(SeenBuff , 0 , 0 , 0 , 0 , HidPage.Get_Width() , HidPage.Get_Height() , (BOOL) FALSE ); + memcpy(SeenBuff.Get_Graphic_Buffer()->GetMemoryBuffer(), HidPage.Get_Graphic_Buffer()->GetMemoryBuffer(), HidPage.Get_Width() * HidPage.Get_Height() * 4); + WWMouse->Erase_Mouse(&HidPage, FALSE); + } else { + //PG ModeX_Blit(&HiddenPage); + } + #else + Shadow_Blit(0, 0, 320, 200, HidPage, SeenPage, ShadowPage->Get_Buffer()); + #endif +} + + diff --git a/code/redalert/gscreen.h b/code/redalert/gscreen.h new file mode 100644 index 0000000..7a15ec1 --- /dev/null +++ b/code/redalert/gscreen.h @@ -0,0 +1,136 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/GSCREEN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : GSCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GSCREEN_H +#define GSCREEN_H + +#include "function.h" +#include "cell.h" + +class GScreenClass +{ + public: + + GScreenClass(void); + GScreenClass(NoInitClass const &) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time initializations + virtual void Init(TheaterType = THEATER_NONE); // Inits everything + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + + /* + ** Player I/O is routed through here. It is called every game tick. + */ + virtual void Input(KeyNumType & key, int & x, int & y); + virtual void AI(KeyNumType &, int, int) {}; + virtual void Add_A_Button(GadgetClass & gadget); + virtual void Remove_A_Button(GadgetClass & gadget); + + /* + ** Called when map needs complete updating. + */ + virtual void Flag_To_Redraw(bool complete=false); + + /* + ** Render maintenance routine (call every game tick). Probably no need + ** to override this in derived classes. + */ + virtual void Render(void); + + /* + ** Is called when actual drawing is required. This is the function to + ** override in derived classes. + */ + virtual void Draw_It(bool =false) {}; + + /* + ** This moves the hidpage up to the seenpage. + */ + virtual void Blit_Display(void); + + /* + ** Changes the mouse shape as indicated. + */ + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall) = 0; + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall) = 0; + virtual void Revert_Mouse_Shape(void) = 0; + virtual void Mouse_Small(bool wsmall) = 0; + + /* + ** Misc routines. + */ + virtual void * Shadow_Address(void) {return(ShadowPage);}; + + /* + ** This points to the buttons that are used for input. All of the derived classes will + ** attached their specific buttons to this list. + */ + static GadgetClass * Buttons; + + private: + + /* + ** If the entire map is required to redraw, then this flag is true. This flag + ** is set by the Flag_To_Redraw function. Typically, this occurs when the screen + ** has been trashed or is first created. + */ + unsigned IsToRedraw:1; + + /* + ** If only a sub-system of the map must be redrawn, then this flag will be set. + ** An example of something that would set this flag would be an animating icon + ** in the sidebar. In such a case, complete redrawing of the entire display is not + ** necessary, but the Draw_It function should still be called so that the appropriate + ** class can perform it's rendering. + */ + unsigned IsToUpdate:1; + + /* + ** Pointer to an exact copy of the visible graphic page. This copy is used to speed + ** display rendering by using an only-update-changed-pixels algorithm. + */ + static GraphicBufferClass * ShadowPage; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/hdata.cpp b/code/redalert/hdata.cpp new file mode 100644 index 0000000..2230f56 --- /dev/null +++ b/code/redalert/hdata.cpp @@ -0,0 +1,523 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HDATA.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 22, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * HouseTypeClass::One_Time -- One-time initialization * + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +static HouseTypeClass const HouseEngland( + HOUSE_ENGLAND, + "England", // NAME: House name. + TXT_ENGLAND, // FULLNAME: Translated house name. + "ENG", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'E' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGermany( + HOUSE_GERMANY, + "Germany", // NAME: House name. + TXT_GERMANY, // FULLNAME: Translated house name. + "GER", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseFrance( + HOUSE_FRANCE, + "France", // NAME: House name. + TXT_FRANCE, // FULLNAME: Translated house name. + "FRA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'F' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUkraine( + HOUSE_UKRAINE, + "Ukraine", // NAME: House name. + TXT_UKRAINE, // FULLNAME: Translated house name. + "UKA", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'K' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseUSSR( + HOUSE_USSR, + "USSR", // NAME: House name. + TXT_USSR, // FULLNAME: Translated house name. + "RED", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'U' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGreece( + HOUSE_GREECE, + "Greece", // NAME: House name. + TXT_GREECE, // FULLNAME: Translated house name. + "GRE", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseTurkey( + HOUSE_TURKEY, + "Turkey", // NAME: House name. + TXT_TURKEY, // FULLNAME: Translated house name. + "TRK", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'T' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseSpain( + HOUSE_SPAIN, + "Spain", // NAME: House name. + TXT_SPAIN, // FULLNAME: Translated house name. + "SPN", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'S' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseGood( + HOUSE_GOOD, + "GoodGuy", // NAME: House name. + TXT_GOODGUY, // FULLNAME: Translated house name. + "GDI", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'G' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseBad( + HOUSE_BAD, + "BadGuy", // NAME: House name. + TXT_BADGUY, // FULLNAME: Translated house name. + "NOD", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'B' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseCivilian( + HOUSE_NEUTRAL, + "Neutral", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "CIV", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'C' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseJP( + HOUSE_JP, + "Special", // NAME: House name. + TXT_JP, // FULLNAME: Translated house name. + "JP", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'J' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti1( + HOUSE_MULTI1, + "Multi1", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP1", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GOLD, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti2( + HOUSE_MULTI2, + "Multi2", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP2", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_LTBLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti3( + HOUSE_MULTI3, + "Multi3", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP3", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_RED, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti4( + HOUSE_MULTI4, + "Multi4", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP4", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREEN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti5( + HOUSE_MULTI5, + "Multi5", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP5", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_ORANGE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti6( + HOUSE_MULTI6, + "Multi6", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP6", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_GREY, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti7( + HOUSE_MULTI7, + "Multi7", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP7", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BLUE, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +static HouseTypeClass const HouseMulti8( + HOUSE_MULTI8, + "Multi8", // NAME: House name. + TXT_CIVILIAN, // FULLNAME: Translated house name. + "MP8", // SUFFIX: House file suffix. + 0, // LEMON: Lemon vehicle frequency. + PCOLOR_BROWN, // Remap color ID number. + 'M' // VOICE: Voice prefix character. +); + +#ifdef OBSOLETE +HouseTypeClass const * const HouseTypeClass::Pointers[HOUSE_COUNT] = { + &HouseSpain, + &HouseGreece, + &HouseUSSR, + &HouseEngland, + &HouseUkraine, + &HouseGermany, + &HouseFrance, + &HouseTurkey, + &HouseGood, + &HouseBad, + &HouseCivilian, + &HouseJP, + &HouseMulti1, + &HouseMulti2, + &HouseMulti3, + &HouseMulti4, + &HouseMulti5, + &HouseMulti6, + &HouseMulti7, + &HouseMulti8, +}; +#endif + + +/*********************************************************************************************** + * HouseTypeClass::HouseTypeClass -- Constructor for house type objects. * + * * + * This is the constructor for house type objects. This object holds the constant data * + * for the house type. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1994 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass::HouseTypeClass( + HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix) : + AbstractTypeClass(RTTI_HOUSETYPE, house, fullname, ini), +// RTTI(RTTI_HOUSETYPE), +// ID(house), + House(house), +// IniName(ini), +// FullName(fullname), + Lemon(lemon), + RemapColor(remapcolor), + Prefix(prefix), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1) +{ + strncpy(Suffix, ext, 3); + Suffix[3] = '\0'; +} + + +/*********************************************************************************************** + * HouseTypeClass::operator new -- Allocates a house type class object from special heap. * + * * + * This will allocate a house type object from the special heap that is used to maintain * + * objects of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the newly allocated house type object. * + * * + * WARNINGS: If there is insufficient room, this routine may return NULL. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void * HouseTypeClass::operator new(size_t) +{ + return(HouseTypes.Alloc()); +} + + +/*********************************************************************************************** + * HouseTypeClass::operator delete -- Returns a house type object back to the heap. * + * * + * This will return the house type object specified back into the special heap that * + * is used to maintain house type objects. * + * * + * INPUT: ptr -- Pointer to the house type object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::operator delete(void * ptr) +{ + HouseTypes.Free((HouseTypeClass *)ptr); +} + + +/*********************************************************************************************** + * HouseTypeClass::Init_Heap -- Allocate all heap objects for the house types. * + * * + * This will preallocate all the house types. They must be allocated in a particular order. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine should be called only once at the beginning of the game. * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +void HouseTypeClass::Init_Heap(void) +{ + /* + ** These house type class objects must be allocated in the exact order that they + ** are specified in the HousesType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new HouseTypeClass(HouseSpain); + new HouseTypeClass(HouseGreece); + new HouseTypeClass(HouseUSSR); + new HouseTypeClass(HouseEngland); + new HouseTypeClass(HouseUkraine); + new HouseTypeClass(HouseGermany); + new HouseTypeClass(HouseFrance); + new HouseTypeClass(HouseTurkey); + new HouseTypeClass(HouseGood); + new HouseTypeClass(HouseBad); + new HouseTypeClass(HouseCivilian); + new HouseTypeClass(HouseJP); + new HouseTypeClass(HouseMulti1); + new HouseTypeClass(HouseMulti2); + new HouseTypeClass(HouseMulti3); + new HouseTypeClass(HouseMulti4); + new HouseTypeClass(HouseMulti5); + new HouseTypeClass(HouseMulti6); + new HouseTypeClass(HouseMulti7); + new HouseTypeClass(HouseMulti8); +} + + +/*********************************************************************************************** + * HouseTypeClass::From_Name -- Fetch house pointer from its name. * + * * + * This routine will convert the ASCII house name specified into a * + * real house number. Typically, this is used when processing a * + * scenario INI file. * + * * + * INPUT: name -- ASCII name of house to process. * + * * + * OUTPUT: Returns with actual house number represented by the ASCII * + * name specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/21/1994 JLB : Converted to member function. * + *=============================================================================================*/ +HousesType HouseTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + if (stricmp(As_Reference(house).IniName, name) == 0) { +// if (stricmp(Pointers[house]->IniName, name) == 0) { + return(house); + } + } + } + return(HOUSE_NONE); +} + + +/*********************************************************************************************** + * HouseTypeClass::One_Time -- One-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/21/1994 JLB : Converted to member function. * + * 06/19/1996 JLB : Converted to regular heap class management. * + *=============================================================================================*/ +void HouseTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * HouseTypeClass::As_Reference -- Fetches a reference to the house specified. * + * * + * Use this routine to fetch a reference to the house number specified. * + * * + * INPUT: house -- The house number (HousesType) to look up. * + * * + * OUTPUT: Returns with a reference to the HouseTypeClass object that matches the house * + * number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseTypeClass & HouseTypeClass::As_Reference(HousesType house) +{ + return(*HouseTypes.Ptr(house)); +} + + +/*********************************************************************************************** + * HouseTypeClass::Remap_Table -- Fetches the remap table for this house. * + * * + * Use this routine to fetch the remap table assigned to this house. The remap table is * + * what gives the house's units/buildings their distinctive color. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +unsigned char const * HouseTypeClass::Remap_Table(void) const +{ + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseTypeClass::Read_INI -- Fetch the house control values from ini database. * + * * + * This routine will fetch the rules controllable values for the house type from the * + * INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to fetch the house control values from. * + * * + * OUTPUT: bool; Was the house section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/04/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + FirepowerBias = ini.Get_Fixed(Name(), "Firepower", FirepowerBias); + GroundspeedBias = ini.Get_Fixed(Name(), "Groundspeed", GroundspeedBias); + AirspeedBias = ini.Get_Fixed(Name(), "Airspeed", AirspeedBias); + ArmorBias = ini.Get_Fixed(Name(), "Armor", ArmorBias); + ROFBias = ini.Get_Fixed(Name(), "ROF", ROFBias); + CostBias = ini.Get_Fixed(Name(), "Cost", CostBias); + BuildSpeedBias = ini.Get_Fixed(Name(), "BuildTime", BuildSpeedBias); + return(true); + } + return(false); +} diff --git a/code/redalert/heap.cpp b/code/redalert/heap.cpp new file mode 100644 index 0000000..e6fc5d9 --- /dev/null +++ b/code/redalert/heap.cpp @@ -0,0 +1,663 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HEAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : May 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * FixedIHeapClass::Free -- Frees an object in the heap. * + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * TFixedIHeapClass::Load -- Loads all active objects * + * TFixedIHeapClass::Save -- Saves all active objects * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "heap.h" +//#include +#include +#include +#include +#include + + +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; +template class TFixedIHeapClass; + + +/*********************************************************************************************** + * FixedHeapClass::FixedHeapClass -- Normal constructor for heap management class. * + * * + * This is the normal constructor used for the heap manager class. This initializes * + * the class but doesn't yet assign actual heap memory to this manager. That is handled * + * by the Set_Heap() function. * + * * + * INPUT: size -- The size of the individual sub-blocks in this heap. This value is * + * typically the size of some class or structure. * + * * + * OUTPUT: none * + * * + * WARNINGS: The heap must first be assigned a block of memory to manage before it can * + * be used. * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::FixedHeapClass(int size) : + IsAllocated(false), + Size(size), + TotalCount(0), + ActiveCount(0), + Buffer(0) +{ +} + + +/*********************************************************************************************** + * FixedHeapClass::~FixedHeapClass -- Destructor for the heap manager class. * + * * + * This is the default constructor for the heap manager class. It handles freeing the * + * memory assigned to this heap. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +FixedHeapClass::~FixedHeapClass(void) +{ + FixedHeapClass::Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Set_Heap -- Assigns a memory block for this heap manager. * + * * + * This routine is used to assign a memory heap to this object. A memory heap so assigned * + * will start with all sub-blocks unallocated. After this routine is called, normal * + * allocation and freeing may occur. This routine will allocate necessary memory if the * + * buffer parameter is NULL. * + * * + * INPUT: count -- The number of objects that this heap should manage. * + * * + * buffer -- Pointer to pre-allocated buffer that this manager will use. If this * + * parameter is NULL, then memory will be automatically allocated. * + * * + * OUTPUT: bool; Was the heap successfully initialized? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Set_Heap(int count, void * buffer) +{ + /* + ** Clear out the old heap data. + */ + Clear(); + + /* + ** If there is no size to the objects in the heap, then this block memory + ** handler can NEVER function. Return with a failure condition. + */ + if (!Size) return(false); + + /* + ** If there is no count specified, then this indicates that the heap should + ** be disabled. + */ + if (!count) return(true); + + /* + ** Initialize the free boolean vector and the buffer for the actual + ** allocation objects. + */ + if (FreeFlag.Resize(count)) { + if (!buffer) { + buffer = new char[count * Size]; + if (!buffer) { + FreeFlag.Clear(); + return(false); + } + IsAllocated = true; + } + Buffer = buffer; + TotalCount = count; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::Allocate -- Allocate a sub-block from the heap. * + * * + * Finds the first available sub-block in the heap and returns a pointer to it. The sub- * + * block is marked as allocated by this routine. If there are no more sub-blocks * + * available, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated sub-block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedHeapClass::Allocate(void) +{ + if (ActiveCount < TotalCount) { + int index = FreeFlag.First_False(); + + if (index != -1) { + ActiveCount++; + FreeFlag[index] = true; + return((*this)[index]); + } + } + return(0); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free -- Frees a sub-block in the heap. * + * * + * Use this routine to free a previously allocated sub-block in the heap. * + * * + * INPUT: pointer -- A pointer to the sub-block to free. This is the same pointer that * + * was returned from the Allocate() function. * + * * + * OUTPUT: bool; Was the deallocation successful? Failure could indicate a pointer that * + * doesn't refer to this heap or a null pointer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free(void * pointer) +{ + if (pointer && ActiveCount) { + int index = ID(pointer); + + if (index < TotalCount) { + if (FreeFlag[index]) { + ActiveCount--; + FreeFlag[index] = false; + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * FixedHeapClass::ID -- Converts a pointer to a sub-block index number. * + * * + * Use this routine to convert a pointer (returned by Allocate) into the sub-block * + * index number. This index number can be used as a form of identifier for the block. * + * * + * INPUT: pointer -- A pointer to the sub-block to convert into an ID number. * + * * + * OUTPUT: Returns with the index (ID) number for the sub-block specified. This number will * + * range between 0 and the sub-block max -1. If -1 is returned, then the pointer * + * was invalid. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::ID(void const * pointer) const +{ + if (pointer && Size) { + return((int)(((char *)pointer - (char *)Buffer) / Size)); + } + return(-1); +} + + +/*********************************************************************************************** + * FixedHeapClass::Clear -- Clears (and frees) the heap manager memory. * + * * + * This routine is used to bring the heap manager back into a non-functioning state. All * + * memory allocated by this manager is freed. Any previous pointers to allocated blocks * + * from this heap are now invalid. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedHeapClass::Clear(void) +{ + /* + ** Free the old buffer (if present). + */ + if (Buffer && IsAllocated) { + delete[] Buffer; + } + Buffer = 0; + IsAllocated = false; + ActiveCount = 0; + TotalCount = 0; + FreeFlag.Clear(); +} + + +/*********************************************************************************************** + * FixedHeapClass::Free_All -- Frees all objects in the fixed heap. * + * * + * This routine will free all previously allocated objects out of the heap. Use this * + * routine to ensure that the heap is empty. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of all objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedHeapClass::Free_All(void) +{ + ActiveCount = 0; + FreeFlag.Reset(); + return(true); +} + + +///////////////////////////////////////////////////////////////////// + + +/*********************************************************************************************** + * FixedIHeapClass::Free_All -- Frees all objects out of the indexed heap. * + * * + * Use this routine to free all previously allocated objects in the heap. This routine will * + * also clear out the allocated object vector as well. * + * * + * INPUT: none * + * * + * OUTPUT: Was the heap successfully cleared of objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free_All(void) +{ + ActivePointers.Delete_All(); + return(FixedHeapClass::Free_All()); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Clear -- Clears the fixed heap of all entries. * + * * + * This routine will clear the entire heap. All memory that was allocation, will be freed * + * by this routine. After calling this routine, the heap must either be resized or * + * a new heap memory block specifically attached, before it can be used again. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void FixedIHeapClass::Clear(void) +{ + FixedHeapClass::Clear(); + ActivePointers.Clear(); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Set_Heap -- Set the heap to the buffer provided. * + * * + * This routine will set the heap to use the buffer specified. Use this routine when a * + * pre-allocated buffer is to be used for the heap. A heap that is assigned in this * + * manner cannot be resized. * + * * + * INPUT: count -- The number of objects that the buffer pointer can be used to track. * + * * + * buffer -- Pointer to the buffer to use when keeping track of the objects. * + * * + * OUTPUT: Was the heap assigned successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Set_Heap(int count, void * buffer) +{ + Clear(); + if (FixedHeapClass::Set_Heap(count, buffer)) { + ActivePointers.Resize(count); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Allocate -- Allocate an object from the heap. * + * * + * This routine will allocate an object located in the heap. If no free object space * + * could be found, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated object memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * FixedIHeapClass::Allocate(void) +{ + void * ptr = FixedHeapClass::Allocate(); + if (ptr) { + ActivePointers.Add(ptr); + memset (ptr, 0, Size); + } + return(ptr); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Free -- Frees an object in the heap. * + * * + * This routine is used to free an object in the heap. Freeing is accomplished by marking * + * the object's memory as free to be reallocated. The object is also removed from the * + * allocated object pointer vector. * + * * + * INPUT: pointer -- Pointer to the object that is to be removed from the heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/21/1995 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Free(void * pointer) +{ + if (FixedHeapClass::Free(pointer)) { + ActivePointers.Delete(pointer); + } + return(false); +} + + +/*********************************************************************************************** + * FixedIHeapClass::Logical_ID -- Fetches the logical ID number. * + * * + * Ths logical ID number of a memory block is the index number of the block as if the * + * heap consisted only of valid allocated blocks. This knowledge comes in handy when * + * the real index number must be anticipated before a memory block packing process. * + * * + * INPUT: pointer -- Pointer to an allocated block in the heap. * + * * + * OUTPUT: Returns with the logical index number of this block. The number returned must not * + * be used as a regular index into the heap until such time as the heap has been * + * compacted (by some means or another) without modifying the block order. * + * * + * WARNINGS: Runs in linear time. * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +int FixedIHeapClass::Logical_ID(void const * pointer) const +{ + if (pointer != NULL) { + for (int index = 0; index < Count(); index++) { + if (Active_Ptr(index) == pointer) { + return(index); + } + } + } + return(-1); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Save -- Saves all active objects * + * * + * INPUT: file file to write to * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + * 03/12/1996 JLB : Uses in-place new operator for virtual table control. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Save(Pipe & file) const +{ + /* + ** Save the number of instances of this class + */ + file.Put(&ActiveCount, sizeof(ActiveCount)); + + /* + ** Save each instance of this class + */ + for (int i = 0; i < ActiveCount; i++) { + + /* + ** Save the array index of the object, so it can be loaded back into the + ** same array location (so TARGET translations will work) + */ + int idx = ID(Ptr(i)); + file.Put(&idx, sizeof(idx)); + + /* + ** Save the object itself + */ + file.Put(Ptr(i), sizeof(T)); + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Load -- Loads all active objects * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +int TFixedIHeapClass::Load(Straw & file) +{ + int i; // loop counter + int idx; // object index + T * ptr; // object pointer + int a_count; + + /* + ** Read the number of instances of this class + */ + if (file.Get(&a_count, sizeof(a_count)) != sizeof(a_count)) { + return(false); + } + + /* + ** Error if more objects than we can hold + */ + if (a_count > TotalCount) { + return(false); + } + + /* + ** Read each class instance + */ + for (i = 0; i < a_count; i++) { + /* + ** Read the object's array index + */ + if (file.Get(&idx, sizeof(idx)) != sizeof(idx)) { + return(false); + } + + /* + ** Get a pointer to the object, activate that object + */ + ptr = (T *)(*this)[idx]; + FreeFlag[idx] = true; + ActiveCount++; + ActivePointers.Add(ptr); + + /* + ** Load the object + */ + file.Get(ptr, sizeof(T)); + new(ptr) T(NoInitClass()); +// if (!ptr->Load(file)) { +// return(false); +// } + } + + return(true); +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Code_Pointers -- codes pointers for every object, to prepare for save * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Code_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Code_Pointers(); + } +} + + +/*********************************************************************************************** + * TFixedIHeapClass::Decode_Pointers -- Decodes all object pointers, for after loading * + * * + * INPUT: file file to read from * + * * + * OUTPUT: true = OK, false = error * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 BRR : Created. * + *=============================================================================================*/ +template +void TFixedIHeapClass::Decode_Pointers(void) +{ + int i; + + for (i = 0; i < ActiveCount; i++) { + Ptr(i)->Decode_Pointers(); + } +} diff --git a/code/redalert/heap.h b/code/redalert/heap.h new file mode 100644 index 0000000..ac07df5 --- /dev/null +++ b/code/redalert/heap.h @@ -0,0 +1,195 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HEAP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HEAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/18/95 * + * * + * Last Update : February 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HEAP_H +#define HEAP_H + +#include "vector.h" + +/************************************************************************** +** This is a block memory management handler. It is used when memory is to +** be treated as a series of blocks of fixed size. This is similar to an +** array of integral types, but unlike such an array, the memory blocks +** are anonymously. This facilitates the use of this class when overloading +** the new and delete operators for a normal class object. +*/ +class FixedHeapClass +{ + public: + FixedHeapClass(int size); + virtual ~FixedHeapClass(void); + + int Count(void) const {return ActiveCount;}; + int Length(void) const {return TotalCount;}; + int Avail(void) const {return TotalCount-ActiveCount;}; + + virtual int ID(void const * pointer) const; + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + + void * operator[](int index) {return ((char *)Buffer) + (index * Size);}; + void const * operator[](int index) const {return ((char *)Buffer) + (index * Size);}; + + protected: + /* + ** If the memory block buffer was allocated by this class, then this flag + ** will be true. The block must be deallocated by this class if true. + */ + unsigned IsAllocated:1; + + /* + ** This is the size of each sub-block within the buffer. + */ + int Size; + + /* + ** This records the absolute number of sub-blocks in the buffer. + */ + int TotalCount; + + /* + ** This is the total blocks allocated out of the heap. This number + ** will never exceed Count. + */ + int ActiveCount; + + /* + ** Pointer to the heap's memory buffer. + */ + void * Buffer; + + /* + ** This is a boolean vector array of allocation flag bits. + */ + BooleanVectorClass FreeFlag; + + private: + // The assignment operator is not supported. + FixedHeapClass & operator = (FixedHeapClass const &); + + // The copy constructor is not supported. + FixedHeapClass(FixedHeapClass const &); +}; + + +/************************************************************************** +** This template serves only as an interface to the heap manager class. By +** using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +template +class TFixedHeapClass : public FixedHeapClass +{ + public: + TFixedHeapClass(void) : FixedHeapClass(sizeof(T)) {}; + virtual ~TFixedHeapClass(void) {}; + + + virtual int ID(T const * pointer) const {return FixedHeapClass::ID(pointer);}; + virtual T * Alloc(void) {return (T*)FixedHeapClass::Allocate();}; + virtual int Free(T * pointer) {return(FixedHeapClass::Free(pointer));}; + + T & operator[](int index) {return *(T *)(((char *)Buffer) + (index * Size));}; + T const & operator[](int index) const {return *(T*)(((char *)Buffer) + (index * Size));}; +}; + + +/************************************************************************** +** This is a derivative of the fixed heap class. This class adds the +** ability to quickly iterate through the active (allocated) objects. Since the +** active array is a sequence of pointers, the overhead of this class +** is 4 bytes per potential allocated object (be warned). +*/ +class FixedIHeapClass : public FixedHeapClass +{ + public: + FixedIHeapClass(int size) : FixedHeapClass(size) {}; + virtual ~FixedIHeapClass(void) {}; + + virtual int Set_Heap(int count, void * buffer=0); + virtual void * Allocate(void); + virtual void Clear(void); + virtual int Free(void * pointer); + virtual int Free_All(void); + virtual int Logical_ID(void const * pointer) const; + virtual int Logical_ID(int id) const {return(Logical_ID((*this)[id]));} + + virtual void * Active_Ptr(int index) {return ActivePointers[index];}; + virtual void const * Active_Ptr(int index) const {return ActivePointers[index];}; + + /* + ** This is an array of pointers to allocated objects. Using this array + ** to control iteration through the objects ensures a minimum of processing. + ** It also allows access to this array so that custom sorting can be + ** performed. + */ + DynamicVectorClass ActivePointers; +}; + + +/************************************************************************** +** This template serves only as an interface to the iteratable heap manager +** class. By using this template, the object pointers are automatically converted +** to the correct type without any code overhead. +*/ +class FileClass; +template +class TFixedIHeapClass : public FixedIHeapClass +{ + public: + TFixedIHeapClass(void) : FixedIHeapClass(sizeof(T)) {}; + virtual ~TFixedIHeapClass(void) {}; + + virtual int ID(T const * pointer) const {return FixedIHeapClass::ID(pointer);}; + virtual int Logical_ID(T const * pointer) const {return(FixedIHeapClass::Logical_ID(pointer));} + virtual int Logical_ID(int id) const {return(FixedIHeapClass::Logical_ID(id));} + virtual T * Alloc(void) {return (T*)FixedIHeapClass::Allocate();}; + virtual int Free(T * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Free(void * pointer) {return FixedIHeapClass::Free(pointer);}; + virtual int Save(Pipe & file) const; + virtual int Load(Straw & file); + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + virtual T * Ptr(int index) const {return (T*)FixedIHeapClass::ActivePointers[index];}; + virtual T * Raw_Ptr(int index) {return (index >= 0 && index < Length()) ? (T*)((*this)[index]) : NULL;}; +}; + + +#endif + + diff --git a/code/redalert/help.cpp b/code/redalert/help.cpp new file mode 100644 index 0000000..08e4f20 --- /dev/null +++ b/code/redalert/help.cpp @@ -0,0 +1,414 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HELP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * HelpClass::HelpClass -- Default constructor for the help processor. * + * HelpClass::Help_AI -- Handles the help text logic. * + * HelpClass::Help_Text -- Assigns text as the current help text. * + * HelpClass::Init_Clear -- Sets help system to a known state. * + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Tactical_Position -- Sets the tactical map position. * + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** This is the holding buffer for the text overlap list. This buffer must be in the near +** data segment. It will be filled in by the Set_Text() function. +*/ +//short const HelpClass::OverlapList[60] = { // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM +short HelpClass::OverlapList[60] = { + REFRESH_EOL +}; + +char const * HelpClass::HelpText; + + +/*********************************************************************************************** + * HelpClass::HelpClass -- Default constructor for the help processor. * + * * + * The help processor is initialized by this routine. It merely sets up the help engine * + * to the default state. The default state will not display any help text. Call the * + * Help_Text() function to enable help processing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +HelpClass::HelpClass(void) : + HelpX(0), + HelpY(0), + HelpWidth(0), + IsRight(false), + Cost(0), + X(0), + Y(0), + DrawX(0), + DrawY(0), + Width(0), + Text(TXT_NONE), + Color(LTGREY), + CountDownTimer(0) +{ +} + + +/*********************************************************************************************** + * HelpClass::Init_Clear -- Sets help system to a known state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Init_Clear(void) +{ + TabClass::Init_Clear(); + + Set_Text(TXT_NONE); +} + + +/*********************************************************************************************** + * HelpClass::Overlap_List -- Returns with offset list for cells under help text. * + * * + * Use this routine to fetch an offset list for the cells under the text displayed. If * + * there is no text displayed, then the list will consist of just the terminator code. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the offset list for the help text overlap. The offset * + * list is based on the tactical map upper left corner cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +short const * HelpClass::Overlap_List(void) const +{ + if (Text == TXT_NONE || CountDownTimer) { + ((short &)(OverlapList[0])) = REFRESH_EOL; + } + return(OverlapList); +} + + +/*********************************************************************************************** + * HelpClass::Help_AI -- Handles the help text logic. * + * * + * This routine handles tracking the mouse position to see if the mouse remains stationary * + * for the required amount of time. If the time requirement has been met, then it flags * + * the help system to display the help text the next time the Draw_Help() function is * + * called. * + * * + * INPUT: key -- Keyboard input code. * + * * + * x,y -- Mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine must be called once and only once per game frame (15 times per * + * second). * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinates as passed in. * + *=============================================================================================*/ +void HelpClass::AI(KeyNumType &key, int x, int y) +{ + if (!CountDownTimer && !IsRight && (x != X || y != Y)) { + Help_Text(TXT_NONE); + } + + /* + ** Process the countdown timer only if it hasn't already expired and there is + ** a real help text message to display. + */ + if (CountDownTimer && !HelpText && Text != TXT_NONE) { + + /* + ** If the mouse has moved, then reset the timer since a moving mouse is not + ** supposed to bring up the help text. + */ + if (!IsRight && (X != x || Y != y)) { + X = x; + Y = y; + CountDownTimer = HELP_DELAY; + Help_Text(TXT_NONE); + } else { + + /* + ** If the delay has expired, then the text must be drawn. Build the help text + ** overlay list at this time. Better to do it now, when we KNOW it is needed, then + ** to do it earlier when it might not be needed. + */ + Set_Text(Text); + } + } + + TabClass::AI(key, x, y); +} + + +/*********************************************************************************************** + * HelpClass::Help_Text -- Assigns text as the current help text. * + * * + * Use this routine to change the help text that will pop up if the cursor isn't moved * + * for the help delay duration. Call this routine as often as desired. * + * * + * INPUT: text -- The text number for the help text to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Help_Text(int text, int x, int y, int color, bool quick) +{ + if (text != Text) { + + /* + ** If there is an existing text message, then flag the map to redraw the underlying + ** icons so that the text message is erased. + */ + if (Text != TXT_NONE) { + Refresh_Cells(Coord_Cell(TacticalCoord), &OverlapList[0]); + } + + /* + ** Record the position of the mouse. This recorded position will be used to determine + ** if the mouse has moved. A moving mouse prevents the help text from popping up. + */ + X = x; + if (x == -1) X = Get_Mouse_X(); + Y = y; + if (y == -1) Y = Get_Mouse_Y(); + IsRight = (y != -1) || (x != -1); + + if (quick) { + CountDownTimer = 1; + } else { + CountDownTimer = HELP_DELAY; + } + + /* + ** All help text prints in the same color for E3 + */ + //Color = color; + color = color; + Color = HELP_TEXT_COLOR; + Text = text; + Cost = 0; + } +} + + +/*********************************************************************************************** + * HelpClass::Draw_Help -- Display the help message (if necessary). * + * * + * This function will print the help text if it thinks it should. The timer and text * + * message can control whether this occurs. If there is no help text or the countdown timer * + * has not expired, then no text will be printed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Draw_It(bool forced) +{ + TabClass::Draw_It(forced); + + forced = false; // TCTCTCTC + if (Text != TXT_NONE && (forced || !CountDownTimer)) { + + if (LogicPage->Lock()) { + Plain_Text_Print(Text, DrawX, DrawY, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY-1, DrawX+Width+1, DrawY+FontHeight, Color); + + if (Cost) { + char buffer[15]; + sprintf(buffer, "$%d", Cost); + int width = String_Pixel_Width(buffer); + + Plain_Text_Print(buffer, DrawX, DrawY+FontHeight, Color, BLACK, TPF_MAP|TPF_NOSHADOW); + LogicPage->Draw_Rect(DrawX-1, DrawY+FontHeight, DrawX+width+1, DrawY+FontHeight+FontHeight-1, Color); + LogicPage->Draw_Line(DrawX, DrawY+FontHeight, DrawX+min(width+1, Width)-1, DrawY+FontHeight, BLACK); + } + LogicPage->Unlock(); + } + } +} + + +/*********************************************************************************************** + * HelpClass::Set_Text -- Determines the overlap list and draw coordinates. * + * * + * This routine is used to build the overlap list -- used for icon refreshing. It also * + * determines if the text can fit on the screen and makes adjustments so that it will. * + * * + * INPUT: text -- The text number to set the help system to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/18/1994 JLB : Created. * + * 12/11/1994 JLB : Won't draw past tactical map edges. * + *=============================================================================================*/ +void HelpClass::Set_Text(int text) +{ + if (text != TXT_NONE) { + Text = text; + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_MAP|TPF_NOSHADOW); + Width = String_Pixel_Width(Text_String(Text)); + if (IsRight) { + DrawX = X - Width; + DrawY = Y; + } else { + int right = TacPixelX + Lepton_To_Pixel(TacLeptonWidth) - 3*RESFACTOR; + int bottom = TacPixelY + Lepton_To_Pixel(TacLeptonHeight) - 1*RESFACTOR; + + DrawX = X+X_OFFSET; + DrawY = Y+Y_OFFSET; + if (DrawX + Width > right) { + DrawX -= (DrawX+Width) - right; + } + if (DrawY + 10*RESFACTOR > bottom) { + DrawY -= (DrawY+10*RESFACTOR) - bottom; + } + if (DrawX < TacPixelX+1) DrawX = TacPixelX+1; + if (DrawY < TacPixelY+1) DrawY = TacPixelY+1; + } + memcpy((void*)OverlapList, Text_Overlap_List(Text_String(Text), DrawX-1, DrawY), sizeof(OverlapList)); + *(short *)&OverlapList[ARRAY_SIZE(OverlapList)-1] = REFRESH_EOL; + } +} + + +/*********************************************************************************************** + * HelpClass::Scroll_Map -- Makes sure scrolling doesn't leave text shards. * + * * + * This routine intercepts the map scrolling request and then makes sure that if, in fact, * + * the map is going to scroll, then reset and erase the help text so that it doesn't * + * mess up the display. * + * * + * INPUT: facing -- The direction to scroll (unused by this routine). * + * * + * really -- If the scroll is actually going to occur, rather than just be examined * + * for legality, then this parameter will be true. If this parameter is * + * true, then the help text is reset. * + * * + * OUTPUT: Returns if it can, or did, scroll in the requested direction. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +bool HelpClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (really) { + Help_Text(TXT_NONE); + } + return(TabClass::Scroll_Map(facing, distance, really)); +} + + +/*********************************************************************************************** + * HelpClass::Set_Cost -- Initiates the second line of help text showing item cost. * + * * + * Use this routine after the Help_Text() function to activate the second line. The second * + * line displays a cost. Typically, this is used by the sidebar to display the cost of the * + * specified item. * + * * + * INPUT: cost -- The cost to associate with this help text. If this value is zero, then * + * no second line is displayed, so don't pass in zero. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/09/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Cost(int cost) +{ + Cost = cost; +} + + +/*********************************************************************************************** + * HelpClass::Set_Tactical_Position -- Sets the position of the tactical map. * + * * + * This routine will set the position of the tactical map. At this class level, it merely * + * makes sure that the help text disappears when this happens. The lower level classes * + * actually change the map's position. * + * * + * INPUT: coord -- The new coordinate to make the upper left corner of the visible display. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void HelpClass::Set_Tactical_Position(COORDINATE coord) +{ + if (TacticalCoord != coord) { + Help_Text(TXT_NONE); + } + TabClass::Set_Tactical_Position(coord); +} diff --git a/code/redalert/help.h b/code/redalert/help.h new file mode 100644 index 0000000..8358799 --- /dev/null +++ b/code/redalert/help.h @@ -0,0 +1,140 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HELP.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HELP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HELP_H +#define HELP_H + +#include "tab.h" + +#define HELP_TEXT_COLOR 80 //158 //Goldy/orange + +class HelpClass: public TabClass +{ + public: + HelpClass(void); + HelpClass(NoInitClass const & x) : TabClass(x) {}; + + /* + ** Initialization + */ + virtual void Init_Clear(void); // Clears all to known state + + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Scroll_Map(DirType facing, int &distance, bool really); + virtual void Set_Tactical_Position(COORDINATE coord); + + void Help_Text(int text, int x=-1, int y=-1, int color=LTGREY, bool quick=false); + void Set_Cost(int cost); + short const * Overlap_List(void) const; + + private: + + static char const *HelpText; + int HelpX; + int HelpY; + int HelpWidth; + + + void Set_Text(int text); + + /* + ** If the help text is right justified (as with the help text that pops up over the + ** sidebar icons), then this flag is set to true. + */ + unsigned IsRight:1; + + /* + ** If the optional second line of text that displays cost is desired, then this + ** value will be non-zero. Typically, this is true when the help text is associated + ** with one of the sidebar construction icons. + */ + int Cost; + + /* + ** This is the recorded position of the cursor at the time the help text + ** pops up. The help text is rendered as an offset from this pixel position. + */ + int X; + int Y; + + /* + ** This is the draw X and Y coordinate. This position is relative to the X and + ** Y coordinates but adjusted for screen edges as necessary. + */ + int DrawX; + int DrawY; + + /* + ** The width of the help text (in pixels) is stored here. This is a convenience + ** since calculating the width takes a bit of time. + */ + int Width; + + /* + ** The text number of the help text to display is held here. If no text is to be + ** displayed, then this value will be TXT_NONE. + */ + int Text; + + /* + ** This is the background color to use for the help text. It can change according + ** to the message displayed. + */ + int Color; + + /* + ** This countdown timer controls when the help text will pop up. If the mouse + ** remains stationary while this countdown timer expires, then the help text + ** will pop up. + */ + CDTimerClass CountDownTimer; + + /* + ** This is a calculated cell offset list (from the Map.TacticalCell) that indicates + ** which cells are under the help text and thus which cells need to be redrawn if + ** the help text is to be erased. + */ + //static short const OverlapList[60]; // Can't be const - it's expected to be written to. ST - 2/7/2019 5:16PM + static short OverlapList[60]; + + enum HelpClassEnum { + HELP_DELAY=TIMER_SECOND*1, // The countdown timer delay before help text pops up. + Y_OFFSET=0, // The Y pixel offset from cursor for help text print. + X_OFFSET=12 // The X pixel offset from cursor for help text print. + }; +}; + + +#endif diff --git a/code/redalert/house.cpp b/code/redalert/house.cpp new file mode 100644 index 0000000..5bbce62 --- /dev/null +++ b/code/redalert/house.cpp @@ -0,0 +1,8175 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/HOUSE.CPP 4 3/13/97 7:11p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : November 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HouseClass::AI -- Process house logic. * + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * HouseClass::AI_Attack -- Handles offensive attack logic. * + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * HouseClass::AI_Building -- Determines what building to build. * + * HouseClass::AI_Fire_Sale -- Check for and perform a fire sale. * + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * HouseClass::AI_Money_Check -- Handles money production logic. * + * HouseClass::AI_Power_Check -- Handle the power situation. * + * HouseClass::AI_Unit -- Determines what unit to build next. * + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * HouseClass::Attacked -- Lets player know if base is under attack. * + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * HouseClass::Blowup_All -- blows up everything * + * HouseClass::Can_Build -- General purpose build legality checker. * + * HouseClass::Clobber_All -- removes all objects for this house * + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * HouseClass::Expert_AI -- Handles expert AI processing. * + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * HouseClass::Find_Building -- Finds a building of specified type. * + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * HouseClass::HouseClass -- Constructor for a house object. * + * HouseClass::Init -- init's in preparation for new scenario * + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * HouseClass::Make_Ally -- Make the specified house an ally. * + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * HouseClass::Production_Begun -- Records that production has begun. * + * HouseClass::Read_INI -- Reads house specific data from INI. * + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * HouseClass::Spend_Money -- Removes money from the house. * + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * HouseClass::Tally_Score -- Fills in the score system for this round * + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * HouseClass::delete -- Deallocator function for a house object. * + * HouseClass::new -- Allocator for a house class. * + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * HouseClass::~HouseClass -- Default destructor for a house object. * + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + +//#include "WolDebug.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + +TFixedIHeapClass HouseClass::BuildChoice; + +int TFixedIHeapClass::Save(Pipe &) const +{ + return(true); +} + +int TFixedIHeapClass::Load(Straw &) +{ + return(0); +} + +void TFixedIHeapClass::Code_Pointers(void) +{ +} + +void TFixedIHeapClass::Decode_Pointers(void) +{ +} + +extern bool RedrawOptionsMenu; + +/*********************************************************************************************** + * HouseClass::operator HousesType -- Conversion to HousesType operator. * + * * + * This operator will automatically convert from a houses class object into the HousesType * + * enumerated value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the object's HousesType value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass::operator HousesType(void) const +{ + assert(Houses.ID(this) == ID); + + return(Class->House); +} + + +/*********************************************************************************************** + * HouseClass::Tiberium_Fraction -- Calculates the tiberium fraction of capacity. * + * * + * This will calculate the current tiberium (gold) load as a ratio of the maximum storage * + * capacity. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current tiberium storage situation as a ratio of load over capacity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Tiberium_Fraction(void) const +{ + if (Tiberium == 0) { + return(0); + } + return(fixed(Tiberium, Capacity)); +} + + +/*********************************************************************************************** + * HouseClass::As_Pointer -- Converts a house number into a house object pointer. * + * * + * Use this routine to convert a house number into the house pointer that it represents. * + * A simple index into the Houses template array is not sufficient, since the array order * + * is arbitrary. An actual scan through the house object is required in order to find the * + * house object desired. * + * * + * INPUT: house -- The house type number to look up. * + * * + * OUTPUT: Returns with a pointer to the house object that the house number represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +HouseClass * HouseClass::As_Pointer(HousesType house) +{ + if (house != HOUSE_NONE) { + for (int index = 0; index < Houses.Count(); index++) { + HouseClass* houseClass = Houses.Ptr(index); + HouseTypeClass* houseTypeClass = houseClass->Class; + if (houseTypeClass->House == house) { + return(Houses.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::One_Time -- Handles one time initialization of the house array. * + * * + * This basically calls the constructor for each of the houses in the game. All other * + * data specific to the house is initialized when the scenario is loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE at the beginning of the game. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::One_Time(void) +{ + BuildChoice.Set_Heap(STRUCT_COUNT); +} + + +/*********************************************************************************************** + * HouseClass::Assign_Handicap -- Assigns the specified handicap rating to the house. * + * * + * The handicap rating will affect combat, movement, and production for the house. It can * + * either make it more or less difficult for the house (controlled by the handicap value). * + * * + * INPUT: handicap -- The handicap value to assign to this house. The default value for * + * a house is DIFF_NORMAL. * + * * + * OUTPUT: Returns with the old handicap value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + * 10/22/1996 JLB : Uses act like value for multiplay only. * + *=============================================================================================*/ +DiffType HouseClass::Assign_Handicap(DiffType handicap) +{ + DiffType old = Difficulty; + Difficulty = handicap; + + if (Session.Type != GAME_NORMAL) { + HouseTypeClass const * hptr = &HouseTypeClass::As_Reference(ActLike); + FirepowerBias = hptr->FirepowerBias * Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = hptr->GroundspeedBias * Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = hptr->AirspeedBias * Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = hptr->ArmorBias * Rule.Diff[handicap].ArmorBias; + ROFBias = hptr->ROFBias * Rule.Diff[handicap].ROFBias; + CostBias = hptr->CostBias * Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = hptr->BuildSpeedBias * Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } else { + FirepowerBias = Rule.Diff[handicap].FirepowerBias; + GroundspeedBias = Rule.Diff[handicap].GroundspeedBias * Rule.GameSpeedBias; + AirspeedBias = Rule.Diff[handicap].AirspeedBias * Rule.GameSpeedBias; + ArmorBias = Rule.Diff[handicap].ArmorBias; + ROFBias = Rule.Diff[handicap].ROFBias; + CostBias = Rule.Diff[handicap].CostBias; + RepairDelay = Rule.Diff[handicap].RepairDelay; + BuildDelay = Rule.Diff[handicap].BuildDelay; + BuildSpeedBias = Rule.Diff[handicap].BuildSpeedBias * Rule.GameSpeedBias; + } + + return(old); +} + + + +#ifdef CHEAT_KEYS + +void HouseClass::Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const +{ + mono->Set_Cursor(x, y); + mono->Printf("A:%-5d I:%-5d V:%-5d", ZoneInfo[zone].AirDefense, ZoneInfo[zone].InfantryDefense, ZoneInfo[zone].ArmorDefense); +} + + +/*********************************************************************************************** + * HouseClass::Debug_Dump -- Dumps the house status data to the mono screen. * + * * + * This utility function will output the current status of the house class to the mono * + * screen. Through this information bugs may be fixed or detected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_HOUSE)); + + mono->Set_Cursor(1, 1);mono->Printf("[%d]%14.14s", Class->House, Name()); + mono->Set_Cursor(20, 1);mono->Printf("[%d]%13.13s", ActLike, HouseTypeClass::As_Reference(ActLike).Name()); + mono->Set_Cursor(39, 1);mono->Printf("%2d", Control.TechLevel); + mono->Set_Cursor(45, 1);mono->Printf("%2d", Difficulty); + mono->Set_Cursor(52, 1);mono->Printf("%2d", State); + mono->Set_Cursor(58, 1);mono->Printf("%2d", Blockage); + mono->Set_Cursor(65, 1);mono->Printf("%2d", IQ); + mono->Set_Cursor(72, 1);mono->Printf("%5d", (long)RepairTimer); + + mono->Set_Cursor(1, 3);mono->Printf("%08X", AScan); + mono->Set_Cursor(10, 3);mono->Printf("%8.8s", (BuildAircraft == AIRCRAFT_NONE) ? " " : AircraftTypeClass::As_Reference(BuildAircraft).Graphic_Name()); + mono->Set_Cursor(21, 3);mono->Printf("%3d", CurAircraft); + mono->Set_Cursor(27, 3);mono->Printf("%8d", Credits); + mono->Set_Cursor(37, 3);mono->Printf("%5d", Power); + mono->Set_Cursor(45, 3);mono->Printf("%04X", RadarSpied); + mono->Set_Cursor(52, 3);mono->Printf("%5d", PointTotal); + mono->Set_Cursor(62, 3);mono->Printf("%5d", (long)TeamTime); + mono->Set_Cursor(71, 3);mono->Printf("%5d", (long)AlertTime); + + mono->Set_Cursor(1, 5);mono->Printf("%08X", BScan); + mono->Set_Cursor(10, 5);mono->Printf("%8.8s", (BuildStructure == STRUCT_NONE) ? " " : BuildingTypeClass::As_Reference(BuildStructure).Graphic_Name()); + mono->Set_Cursor(21, 5);mono->Printf("%3d", CurBuildings); + mono->Set_Cursor(27, 5);mono->Printf("%8d", Tiberium); + mono->Set_Cursor(37, 5);mono->Printf("%5d", Drain); + mono->Set_Cursor(44, 5);mono->Printf("%16.16s", QuarryName[PreferredTarget]); + mono->Set_Cursor(62, 5);mono->Printf("%5d", (long)TriggerTime); + mono->Set_Cursor(71, 5);mono->Printf("%5d", (long)BorrowedTime); + + mono->Set_Cursor(1, 7);mono->Printf("%08X", UScan); + mono->Set_Cursor(10, 7);mono->Printf("%8.8s", (BuildUnit == UNIT_NONE) ? " " : UnitTypeClass::As_Reference(BuildUnit).Graphic_Name()); + mono->Set_Cursor(21, 7);mono->Printf("%3d", CurUnits); + mono->Set_Cursor(27, 7);mono->Printf("%8d", Control.InitialCredits); + mono->Set_Cursor(38, 7);mono->Printf("%5d", UnitsLost); + mono->Set_Cursor(44, 7);mono->Printf("%08X", Allies); + mono->Set_Cursor(71, 7);mono->Printf("%5d", (long)Attack); + + mono->Set_Cursor(1, 9);mono->Printf("%08X", IScan); + mono->Set_Cursor(10, 9);mono->Printf("%8.8s", (BuildInfantry == INFANTRY_NONE) ? " " : InfantryTypeClass::As_Reference(BuildInfantry).Graphic_Name()); + mono->Set_Cursor(21, 9);mono->Printf("%3d", CurInfantry); + mono->Set_Cursor(27, 9);mono->Printf("%8d", Capacity); + mono->Set_Cursor(38, 9);mono->Printf("%5d", BuildingsLost); + mono->Set_Cursor(45, 9);mono->Printf("%4d", Radius / CELL_LEPTON_W); + mono->Set_Cursor(71, 9);mono->Printf("%5d", (long)AITimer); + + mono->Set_Cursor(1, 11);mono->Printf("%08X", VScan); + mono->Set_Cursor(10, 11);mono->Printf("%8.8s", (BuildVessel == VESSEL_NONE) ? " " : VesselTypeClass::As_Reference(BuildVessel).Graphic_Name()); + mono->Set_Cursor(21, 11);mono->Printf("%3d", CurVessels); + mono->Set_Cursor(54, 11);mono->Printf("%04X", Coord_Cell(Center)); + mono->Set_Cursor(71, 11);mono->Printf("%5d", (long)DamageTime); + + + for (int index = 0; index < ARRAY_SIZE(Scen.GlobalFlags); index++) { + mono->Set_Cursor(1+index, 15); + if (Scen.GlobalFlags[index] != 0) { + mono->Print("1"); + } else { + mono->Print("0"); + } + if (index >= 24) break; + } + if (Enemy != HOUSE_NONE) { + char const * name = ""; + name = HouseClass::As_Pointer(Enemy)->Name(); + mono->Set_Cursor(53, 15);mono->Printf("[%d]%21.21s", Enemy, HouseTypeClass::As_Reference(Enemy).Name()); + } + + Print_Zone_Stats(27, 11, ZONE_NORTH, mono); + Print_Zone_Stats(27, 13, ZONE_CORE, mono); + Print_Zone_Stats(27, 15, ZONE_SOUTH, mono); + Print_Zone_Stats(1, 13, ZONE_WEST, mono); + Print_Zone_Stats(53, 13, ZONE_EAST, mono); + + mono->Fill_Attrib(1, 17, 12, 1, IsActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 18, 12, 1, IsHuman ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 19, 12, 1, IsPlayerControl ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 20, 12, 1, IsAlerted ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 21, 12, 1, IsDiscovered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 22, 12, 1, IsMaxedOut ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(14, 17, 12, 1, IsDefeated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 18, 12, 1, IsToDie ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 19, 12, 1, IsToWin ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 20, 12, 1, IsToLose ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 21, 12, 1, IsCivEvacuated ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 22, 12, 1, IsRecalcNeeded ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(27, 17, 12, 1, IsVisionary ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 18, 12, 1, IsTiberiumShort ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 19, 12, 1, IsSpied ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 20, 12, 1, IsThieved ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 21, 12, 1, IsGPSActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 22, 12, 1, IsStarted ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(40, 17, 12, 1, IsResigner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 18, 12, 1, IsGiverUpper ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 19, 12, 1, IsBuiltSomething ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 20, 12, 1, IsBaseBuilding ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * HouseClass::new -- Allocator for a house class. * + * * + * This is the allocator for a house class. Since there can be only * + * one of each type of house, this is allocator has restricted * + * functionality. Any attempt to allocate a house structure for a * + * house that already exists, just returns a pointer to the previously * + * allocated house. * + * * + * INPUT: house -- The house to allocate a class object for. * + * * + * OUTPUT: Returns with a pointer to the allocated class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void * HouseClass::operator new(size_t) +{ + void * ptr = Houses.Allocate(); + if (ptr) { + ((HouseClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * HouseClass::delete -- Deallocator function for a house object. * + * * + * This function marks the house object as "deallocated". Such a * + * house object is available for reallocation later. * + * * + * INPUT: ptr -- Pointer to the house object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::operator delete(void * ptr) +{ + if (ptr) { + ((HouseClass *)ptr)->IsActive = false; + } + Houses.Free((HouseClass *)ptr); +} + + +/*********************************************************************************************** + * HouseClass::HouseClass -- Constructor for a house object. * + * * + * This function is the constructor and it marks the house object * + * as being allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +#define VOX_NOT_READY VOX_NONE +HouseClass::HouseClass(HousesType house) : + RTTI(RTTI_HOUSE), + ID(Houses.ID(this)), + Class(HouseTypes.Ptr(house)), + Difficulty(Scen.CDifficulty), + FirepowerBias(1), + GroundspeedBias(1), + AirspeedBias(1), + ArmorBias(1), + ROFBias(1), + CostBias(1), + BuildSpeedBias(1), + RepairDelay(0), + BuildDelay(0), + ActLike(Class->House), + IsHuman(false), + WasHuman(false), + IsPlayerControl(false), + IsStarted(false), + IsAlerted(false), + IsBaseBuilding(false), + IsDiscovered(false), + IsMaxedOut(false), + IsDefeated(false), + IsToDie(false), + IsToLose(false), + IsToWin(false), + IsCivEvacuated(false), + IsRecalcNeeded(true), + IsVisionary(false), + IsTiberiumShort(false), + IsSpied(false), + IsThieved(false), + IsGPSActive(false), + IsBuiltSomething(false), + IsResigner(false), + IsGiverUpper(false), + IsParanoid(false), + IsToLook(true), + IsQueuedMovementToggle(false), + DidRepair(false), + IQ(Control.IQ), + State(STATE_BUILDUP), + JustBuiltStructure(STRUCT_NONE), + JustBuiltInfantry(INFANTRY_NONE), + JustBuiltUnit(UNIT_NONE), + JustBuiltAircraft(AIRCRAFT_NONE), + JustBuiltVessel(VESSEL_NONE), + Blockage(0), + RepairTimer(0), + AlertTime(0), + BorrowedTime(0), + BScan(0), + ActiveBScan(0), + OldBScan(0), + UScan(0), + ActiveUScan(0), + OldUScan(0), + IScan(0), + ActiveIScan(0), + OldIScan(0), + AScan(0), + ActiveAScan(0), + OldAScan(0), + VScan(0), + ActiveVScan(0), + OldVScan(0), + CreditsSpent(0), + HarvestedCredits(0), + StolenBuildingsCredits(0), + CurUnits(0), + CurBuildings(0), + CurInfantry(0), + CurVessels(0), + CurAircraft(0), + Tiberium(0), + Credits(0), + Capacity(0), + AircraftTotals(NULL), + InfantryTotals(NULL), + UnitTotals(NULL), + BuildingTotals(NULL), + VesselTotals(NULL), + DestroyedAircraft(NULL), + DestroyedInfantry(NULL), + DestroyedUnits(NULL), + DestroyedBuildings(NULL), + DestroyedVessels(NULL), + CapturedBuildings(NULL), + TotalCrates(NULL), + AircraftFactories(0), + InfantryFactories(0), + UnitFactories(0), + BuildingFactories(0), + VesselFactories(0), + Power(0), + Drain(0), + AircraftFactory(-1), + InfantryFactory(-1), + UnitFactory(-1), + BuildingFactory(-1), + VesselFactory(-1), + Radar(RADAR_NONE), + FlagLocation(TARGET_NONE), + FlagHome(0), + UnitsLost(0), + BuildingsLost(0), + WhoLastHurtMe(house), + StartLocationOverride(-1), + Center(0), + Radius(0), + LATime(0), + LAType(RTTI_NONE), + LAZone(ZONE_NONE), + LAEnemy(HOUSE_NONE), + ToCapture(TARGET_NONE), + RadarSpied(0), + PointTotal(0), + PreferredTarget(QUARRY_ANYTHING), + ScreenShakeTime(0), + Attack(0), + Enemy(HOUSE_NONE), + AITimer(0), + UnitToTeleport(0), + BuildStructure(STRUCT_NONE), + BuildUnit(UNIT_NONE), + BuildInfantry(INFANTRY_NONE), + BuildAircraft(AIRCRAFT_NONE), + BuildVessel(VESSEL_NONE), + NukeDest(0), + Allies(0), + DamageTime(TICKS_PER_MINUTE * Rule.DamageDelay), + TeamTime(TICKS_PER_MINUTE * Rule.TeamDelay), + TriggerTime(0), + SpeakAttackDelay(1), + SpeakPowerDelay(1), + SpeakMoneyDelay(1), + SpeakMaxedDelay(1), + RemapColor(Class->RemapColor), + DebugUnlockBuildables(false) +{ + /* + ** Explicit in-place construction of the super weapons is + ** required here because the default constructor for super + ** weapons must serve as a no-initialization constructor (save/load reasons). + */ + new (&SuperWeapon[SPC_NUCLEAR_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.NukeTime, true, VOX_ABOMB_PREPPING, VOX_ABOMB_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_SONAR_PULSE]) SuperClass(TICKS_PER_MINUTE * Rule.SonarTime, false, VOX_NONE, VOX_SONAR_AVAILABLE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_CHRONOSPHERE]) SuperClass(TICKS_PER_MINUTE * Rule.ChronoTime, true, VOX_CHRONO_CHARGING, VOX_CHRONO_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_PARA_BOMB]) SuperClass(TICKS_PER_MINUTE * Rule.ParaBombTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_PARA_INFANTRY]) SuperClass(TICKS_PER_MINUTE * Rule.ParaInfantryTime, false, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_SPY_MISSION]) SuperClass(TICKS_PER_MINUTE * Rule.SpyTime, false, VOX_NONE, VOX_SPY_PLANE, VOX_NOT_READY, VOX_NOT_READY); + new (&SuperWeapon[SPC_IRON_CURTAIN]) SuperClass(TICKS_PER_MINUTE * Rule.IronCurtainTime, true, VOX_IRON_CHARGING, VOX_IRON_READY, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + new (&SuperWeapon[SPC_GPS]) SuperClass(TICKS_PER_MINUTE * Rule.GPSTime, true, VOX_NONE, VOX_NONE, VOX_NOT_READY, VOX_INSUFFICIENT_POWER); + + memset(UnitsKilled, '\0', sizeof(UnitsKilled)); + memset(BuildingsKilled, '\0', sizeof(BuildingsKilled)); + memset(BQuantity, '\0', sizeof(BQuantity)); + memset(UQuantity, '\0', sizeof(UQuantity)); + memset(IQuantity, '\0', sizeof(IQuantity)); + memset(AQuantity, '\0', sizeof(AQuantity)); + memset(VQuantity, '\0', sizeof(VQuantity)); + strcpy(IniName, Text_String(TXT_COMPUTER)); // Default computer name. + HouseTriggers[house].Clear(); + memset((void *)&Regions[0], 0x00, sizeof(Regions)); + Make_Ally(house); + Assign_Handicap(Scen.CDifficulty); + + /* + ** Set the time of the first AI attack. + */ + Attack = Rule.AttackDelay * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + + Init_Unit_Trackers(); +} + + +/*********************************************************************************************** + * HouseClass::~HouseClass -- House class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/6/96 4:48PM ST : Created * + *=============================================================================================*/ +HouseClass::~HouseClass (void) +{ + Class = 0; + + Free_Unit_Trackers(); +} + + +/*********************************************************************************************** + * HouseStaticClass::HouseStaticClass -- Default constructor for house static class. * + * * + * This is the default constructor that initializes all the values to their default * + * settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +HouseStaticClass::HouseStaticClass(void) : + IQ(0), + TechLevel(1), + Allies(0), + MaxUnit(Rule.UnitMax/6), + MaxBuilding(Rule.BuildingMax/6), + MaxInfantry(Rule.InfantryMax/6), + MaxVessel(Rule.VesselMax/6), + MaxAircraft(Rule.UnitMax/6), + InitialCredits(0), + Edge(SOURCE_NORTH) +{ +} + + +/*********************************************************************************************** + * HouseClass::Can_Build -- General purpose build legality checker. * + * * + * This routine is called when it needs to be determined if the specified object type can * + * be built by this house. Production and sidebar maintenance use this routine heavily. * + * * + * INPUT: type -- Pointer to the type of object that legality is to be checked for. * + * * + * house -- This is the house to check for legality against. Note that this might * + * not be 'this' house since the check could be from a captured factory. * + * Captured factories build what the original owner of them could build. * + * * + * OUTPUT: Can the specified object be built? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + * 08/12/1995 JLB : Updated for GDI building sandbag walls in #9. * + * 10/23/1996 JLB : Hack to allow Tanya to both sides in multiplay. * + * 11/04/1996 JLB : Computer uses prerequisite record. * + *=============================================================================================*/ +bool HouseClass::Can_Build(ObjectTypeClass const * type, HousesType house) const +{ + assert(Houses.ID(this) == ID); + assert(type != NULL); + + /* + ** An object with a prohibited tech level availability will never be allowed, regardless + ** of who requests it. + */ + if (((TechnoTypeClass const *)type)->Level == -1) return(false); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** If this is a CounterStrike II-only unit, and we're playing a multiplayer + ** game in 'downshifted' mode against CounterStrike or Red Alert, then + ** don't allow building this unit. + */ + if (!NewUnitsEnabled) { + switch(type->What_Am_I()) { + case RTTI_INFANTRYTYPE: + if ( ((InfantryTypeClass *)type)->ID >= INFANTRY_RA_COUNT) + return(false); + break; + case RTTI_UNITTYPE: + if ( ((UnitTypeClass *)type)->ID >= UNIT_RA_COUNT) + return(false); + break; + case RTTI_VESSELTYPE: + if ( ((VesselTypeClass *)type)->ID >= VESSEL_RA_COUNT) + return(false); + break; + default: + break; + } + } +#endif + + /* + ** The computer can always build everything. + */ + if (!IsHuman && Session.Type == GAME_NORMAL) return(true); + + /* + ** Special hack to get certain objects to exist for both sides in the game. + */ + int own = type->Get_Ownable(); + + /* + ** Check to see if this owner can build the object type specified. + */ + if (((1L << house) & own) == 0) { + return(false); + } + + /* + ** Perform some equivalency fixups for the building existence flags. + */ + long flags = ActiveBScan; + + /* + ** The computer records prerequisite buildings because it can't relay on the + ** sidebar to keep track of this information. + */ + if (!IsHuman) { + flags = OldBScan; + } + + int pre = ((TechnoTypeClass const *)type)->Prerequisite; + + /* + ** Advanced power also serves as a prerequisite for normal power. + */ + if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER; + + /* + ** Either tech center counts as a prerequisite. + */ + if (Session.Type != GAME_NORMAL) { + if ((flags & (STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH)) != 0) flags |= STRUCTF_SOVIET_TECH|STRUCTF_ADVANCED_TECH; + } + + int level = Control.TechLevel; +#ifdef CHEAT_KEYS + if (Debug_Cheat) { + level = 98; + pre = 0; + } +#endif + + // ST - 8/23/2019 4:53PM + if (DebugUnlockBuildables) { + level = 98; + pre = 0; + } + + /* + ** See if the prerequisite requirements have been met. + */ + return((pre & flags) == pre && ((TechnoTypeClass const *)type)->Level <= (unsigned)level); +} + + +/*************************************************************************** + * HouseClass::Init -- init's in preparation for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 12/17/1994 JLB : Resets tracker bits. * + *=========================================================================*/ +void HouseClass::Init(void) +{ + Houses.Free_All(); + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseTriggers[index].Clear(); + } +} + +// Object selection list is switched with player context for GlyphX. ST - 8/7/2019 10:11AM +extern void Logic_Switch_Player_Context(HouseClass *house); +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * HouseClass::AI -- Process house logic. * + * * + * This handles the AI for the house object. It should be called once per house per game * + * tick. It processes all house global tasks such as low power damage accumulation and * + * house specific trigger events. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + * 07/17/1995 JLB : Limits EVA speaking unless the player can do something. * + *=============================================================================================*/ +extern void Recalculate_Placement_Distances(); + +void HouseClass::AI(void) +{ + assert(Houses.ID(this) == ID); + + // Set PlayerPtr to be this house. ST - 8/7/2019 10:12AM + Logic_Switch_Player_Context(this); + + /* + ** If base building has been turned on by a trigger, then force the house to begin + ** production and team creation as well. This is also true if the IQ is high enough to + ** being base building. + */ + if (IsBaseBuilding || IQ >= Rule.IQProduction) { + IsBaseBuilding = true; + IsStarted = true; + IsAlerted = true; + } + + /* + ** Check to see if the house wins. + */ + if (Session.Type == GAME_NORMAL && IsToWin && BorrowedTime == 0 && Blockage <= 0) { + IsToWin = false; + if (this == PlayerPtr) { + PlayerWins = true; + } else { + PlayerLoses = true; + } + } + + /* + ** Check to see if the house loses. + */ + if (Session.Type == GAME_NORMAL && IsToLose && BorrowedTime == 0) { + IsToLose = false; + if (this == PlayerPtr) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + } + + /* + ** Check to see if all objects of this house should be blown up. + */ + if (IsToDie && BorrowedTime == 0) { + IsToDie = false; + Blowup_All(); + } + + /* + ** Double check power values to correct illegal conditions. It is possible to + ** get a power output of negative (one usually) as a result of damage sustained + ** and the fixed point fractional math involved with power adjustments. If the + ** power rating drops below zero, then make it zero. + */ + Power = max(Power, 0); + Drain = max(Drain, 0); + + /* + ** If the base has been alerted to the enemy and should be attacking, then + ** see if the attack timer has expired. If it has, then create the attack + ** teams. + */ + if (IsAlerted && AlertTime == 0) { + + /* + ** Adjusted to reduce maximum number of teams created. + */ + int maxteams = Random_Pick(2, (int)(((Control.TechLevel-1)/3)+1)); + for (int index = 0; index < maxteams; index++) { + TeamTypeClass const * ttype = Suggested_New_Team(true); + if (ttype != NULL) { + ScenarioInit++; + ttype->Create_One_Of(); + ScenarioInit--; + } + } + AlertTime = Rule.AutocreateTime * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); +// int mintime = Rule.AutocreateTime * (TICKS_PER_MINUTE/2); +// int maxtime = Rule.AutocreateTime * (TICKS_PER_MINUTE*2); +// AlertTime = Random_Pick(mintime, maxtime); + } + + /* + ** If this house's flag waypoint is a valid cell, see if there's + ** someone sitting on it. If so, make the scatter. + */ + if (FlagHome != 0 && (Frame % TICKS_PER_SECOND) == 0) { + + TechnoClass * techno = Map[FlagHome].Cell_Techno(); + if (techno != NULL) { + bool moving = false; + if (techno->Is_Foot()) { + if (Target_Legal(((FootClass *)techno)->NavCom)) { + moving = true; + } + } + + if (!moving) { + techno->Scatter(0, true, true); + } + } + } + + /* + ** Create teams for this house if necessary. + ** (Use the same timer for some extra capture-the-flag logic.) + */ + if (!IsAlerted && !TeamTime) { + + TeamTypeClass const * ttype = Suggested_New_Team(false); + if (ttype) { + ttype->Create_One_Of(); + } + + TeamTime = Rule.TeamDelay * TICKS_PER_MINUTE; + } + + /* + ** If there is insufficient power, then all buildings that are above + ** half strength take a little bit of damage. + */ + if (DamageTime == 0) { + + /* + ** When the power is below required, then the buildings will take damage over + ** time. + */ + if (Power_Fraction() < 1) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass & b = *Buildings.Ptr(index); + + if (b.House == this && b.Health_Ratio() > Rule.ConditionYellow) { + // BG: Only damage buildings that require power, to keep the + // land mines from blowing up under low-power conditions + if (b.Class->Drain) { + int damage = 1; + b.Take_Damage(damage, 0, WARHEAD_AP, 0); + } + } + } + } + DamageTime = TICKS_PER_MINUTE * Rule.DamageDelay; + } + + /* + ** If there are no more buildings to sell, then automatically cancel the + ** sell mode. + */ + if (PlayerPtr == this && !ActiveBScan && Map.IsSellMode) { + Map.Sell_Mode_Control(0); + } + + /* + ** Various base conditions may be announced to the player. Typically, this would be + ** low tiberium capacity or low power. + */ + if (PlayerPtr == this) { + + if (SpeakMaxedDelay == 0 && Available_Money() < 100 && UnitFactories+BuildingFactories+InfantryFactories > 0) { + Speak(VOX_NEED_MO_MONEY); + Map.Flash_Money(); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + + int text_id = TXT_INSUFFICIENT_FUNDS; + char const * text = Text_String(TXT_INSUFFICIENT_FUNDS); + if (text != NULL) { + Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + + if (SpeakMaxedDelay == 0 && IsMaxedOut) { + IsMaxedOut = false; + if ((Capacity - Tiberium) < 300 && Capacity > 500 && (ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + SpeakMaxedDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + } + } + if (SpeakPowerDelay == 0 && Power_Fraction() < 1) { + if (ActiveBScan & STRUCTF_CONST) { + Speak(VOX_LOW_POWER); + SpeakPowerDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + Map.Flash_Power(); + + int text_id = -1; + char const * text = NULL; + if (BQuantity[STRUCT_AAGUN] > 0) { + text = Text_String(TXT_POWER_AAGUN); + text_id = TXT_POWER_AAGUN; + } + if (BQuantity[STRUCT_TESLA] > 0) { + text = Text_String(TXT_POWER_TESLA); + text_id = TXT_POWER_TESLA; + } + if (text == NULL) { + text = Text_String(TXT_LOW_POWER); + text_id = TXT_LOW_POWER; + } + if (text != NULL) { + Session.Messages.Add_Message(NULL, text_id, text, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + } + } + + /* + ** If there is a flag associated with this house, then mark it to be + ** redrawn. + */ + if (Target_Legal(FlagLocation)) { + UnitClass * unit = As_Unit(FlagLocation); + if (unit) { + unit->Mark(MARK_CHANGE); + } else { + CELL cell = As_Cell(FlagLocation); + Map[cell].Redraw_Objects(); + } + } + + bool is_time = false; + + /* + ** Triggers are only checked every so often. If the trigger timer has expired, + ** then set the trigger processing flag. + */ + if (TriggerTime == 0 || IsBuiltSomething) { + is_time = true; + TriggerTime = TICKS_PER_MINUTE/10; + IsBuiltSomething = false; + } + + /* + ** Process any super weapon logic required. + */ + + if (Session.Type != GAME_GLYPHX_MULTIPLAYER || !MPSuperWeaponDisable) { + Super_Weapon_Handler(); + } + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Scen.AutoSonarTimer == 0 ) + { + // If house has nothing but subs left, do an automatic sonar pulse to reveal them. + if( VQuantity[ VESSEL_SS ] > 0 ) // Includes count of VESSEL_MISSILESUBs. ajw + { + int iCount = 0; + int i; + for( i = 0; i != STRUCT_COUNT-3; ++i ) + { + iCount += BQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != UNIT_RA_COUNT-3; ++i ) + { + iCount += UQuantity[ i ]; + } + if( !iCount ) + { + // ajw - Found bug - house's civilians are not removed from IQuantity when they die. + // Workaround... + for( i = 0; i <= INFANTRY_DOG; ++i ) + { + iCount += IQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != AIRCRAFT_COUNT; ++i ) + { + iCount += AQuantity[ i ]; + } + if( !iCount ) + { + for( i = 0; i != VESSEL_RA_COUNT; ++i ) + { + if( i != VESSEL_SS ) + iCount += VQuantity[ i ]; + } + if( !iCount ) + { + // Do the ping. + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + bAutoSonarPulse = true; + } + } + } + } + } + } + } +#endif + + if (Session.Type != GAME_NORMAL) { + Check_Pertinent_Structures(); + } + + /* + ** Special win/lose check for multiplayer games; by-passes the + ** trigger system. We must wait for non-zero frame, because init + ** may not properly set IScan etc for each house; you have to go + ** through each object's AI before it will be properly set. + */ + if (Session.Type != GAME_NORMAL && !IsDefeated && + !ActiveBScan && !ActiveAScan && !UScan && !ActiveIScan && !ActiveVScan && Frame > 0) { + MPlayer_Defeated(); + } + + /* + ** Try to spring all events attached to this house. The triggers will check + ** for themselves if they actually need to be sprung or not. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + if (HouseTriggers[Class->House][index]->Spring() && index > 0) { + index--; + continue; + } + } + + /* + ** If a radar facility is not present, but the radar is active, then turn the radar off. + ** The radar also is turned off when the power gets below 100% capacity. + */ + if (PlayerPtr == this) { + bool jammed = true; + + /* + ** Find if there are any radar facilities, and if they're jammed or not + */ + + if (IsGPSActive) { + jammed = false; + } else { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); +#ifdef FIXIT_RADAR_JAMMED + if (building != NULL && !building->IsInLimbo && building->House == PlayerPtr) { +#else + if (building && building->House == PlayerPtr) { +#endif + if (*building == STRUCT_RADAR /* || *building == STRUCT_EYE */) { + if (!building->IsJammed) { + jammed = false; + break; + } + } + } + } + } + + Map.Set_Jammed(this, jammed); +// Need to add in here where we activate it when only GPS is active. + if (Map.Is_Radar_Active()) { + if (ActiveBScan & STRUCTF_RADAR) { + if (Power_Fraction() < 1 && !IsGPSActive) { + Map.Radar_Activate(0); + } + } else { + if (!IsGPSActive) { + Map.Radar_Activate(0); + } + } + + } else { + if (IsGPSActive || (ActiveBScan & STRUCTF_RADAR)) { + if (Power_Fraction() >= 1 || IsGPSActive) { + Map.Radar_Activate(1); + } + } else { + if (Map.Is_Radar_Existing()) { + Map.Radar_Activate(4); + } + } + } + if (!IsGPSActive && !(ActiveBScan & STRUCTF_RADAR)) { + Radar = RADAR_NONE; + } else { + Radar = (Map.Is_Radar_Active() || Map.Is_Radar_Activating()) ? RADAR_ON : RADAR_OFF; + } + } + + VisibleCredits.AI(false, this, true); + + /* + ** Perform any expert system AI processing. + */ + if (IsBaseBuilding && AITimer == 0) { + AITimer = Expert_AI(); + } + + if (!IsBaseBuilding && State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } + + AI_Building(); + AI_Unit(); + AI_Vessel(); + AI_Infantry(); + AI_Aircraft(); + + + /* + ** If the production possibilities need to be recalculated, then do so now. This must + ** occur after the scan bits have been properly updated. + */ + if (PlayerPtr == this && IsRecalcNeeded) { + IsRecalcNeeded = false; + Map.Recalc(); + + /* + ** This placement might affect any prerequisite requirements for construction + ** lists. Update the buildable options accordingly. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + if (building && building->Strength > 0 && building->Owner() == Class->House && building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION) { + + if (PlayerPtr == building->House) { + building->Update_Buildables(); + } + } + } + + Recalculate_Placement_Distances(); + Check_Pertinent_Structures(); + } + + /* + ** See if it's time to re-set the can-repair flag + */ + if (DidRepair && RepairTimer == 0) { + DidRepair = false; + } + + if (this == PlayerPtr && IsToLook) { + IsToLook = false; + Map.All_To_Look(PlayerPtr); + } +} + +/*********************************************************************************************** + * HouseClass::Super_Weapon_Handler -- Handles the super weapon charge and discharge logic. * + * * + * This handles any super weapons assigned to this house. It also performs any necessary * + * maintenance that the super weapons require. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/17/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Super_Weapon_Handler(void) +{ + /* + ** Perform all super weapon AI processing. This just checks to see if + ** the graphic needs changing for the special weapon and updates the + ** sidebar as necessary. + */ + for (SpecialWeaponType special = SPC_FIRST; special < SPC_COUNT; special++) { + SuperClass * super = &SuperWeapon[special]; + + if (super->Is_Present()) { + + /* + ** Perform any charge-up logic for the super weapon. If the super + ** weapon is owned by the player and a graphic change is detected, then + ** flag the sidebar to be redrawn so the player will see the change. + */ + if (super->AI(this == PlayerPtr)) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + + /* + ** Repeating super weapons that require power will be suspended if there + ** is insufficient power available. + */ + if (!super->Is_Ready() && super->Is_Powered() && !super->Is_One_Time()) { + super->Suspend(Power_Fraction() < 1); + } + } + } + + /* + ** Check to see if they have launched the GPS, but subsequently lost their + ** tech center. If so, remove the GPS, and shroud the map. + */ + if (IsGPSActive && !(ActiveBScan & STRUCTF_ADVANCED_TECH) ) { + IsGPSActive = false; + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:32AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (IsPlayerControl) { + Map.Shroud_The_Map(PlayerPtr); + } + + } else { + + if (IsHuman) { + Map.Shroud_The_Map(this); + } + } + } + + /* + ** Check to see if the GPS Satellite should be removed from the sidebar + ** because of outside circumstances. The advanced technology facility + ** being destroyed is a good example of this. Having fired the satellite + ** is another good example, because it's a one-shot item. + */ + if (SuperWeapon[SPC_GPS].Is_Present()) { + if (!(ActiveBScan & STRUCTF_ADVANCED_TECH) || IsGPSActive || IsDefeated) { + /* + ** Remove the missile capability when there is no advanced tech facility. + */ + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + /* + ** Auto-fire the GPS satellite if it's charged up. + */ + if (SuperWeapon[SPC_GPS].Is_Ready()) { + SuperWeapon[SPC_GPS].Discharged(this == PlayerPtr); + if (SuperWeapon[SPC_GPS].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this) { + bldg->HasFired = true; + bldg->Assign_Mission(MISSION_MISSILE); + break; + } + } + } + } + } else { + /* + ** If there is no GPS satellite present, but there is a GPS satellite + ** facility available, then make the GPS satellite available as well. + */ + if ((ActiveBScan & STRUCTF_ADVANCED_TECH) != 0 && + !IsGPSActive && + Control.TechLevel >= Rule.GPSTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + bool canfire = false; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_ADVANCED_TECH && bldg->House == this && !bldg->IsInLimbo) { + if (!bldg->HasFired) { + canfire = true; + break; + } + } + } + + if (canfire) { + SuperWeapon[SPC_GPS].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_GPS, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_GPS); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + } + + /* + ** Check to see if the chronosphere should be removed from the sidebar + ** because of outside circumstances. The chronosphere facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_CHRONOSPHERE) && !SuperWeapon[SPC_CHRONOSPHERE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the chronosphere when there is no chronosphere facility. + ** Note that this will not remove the one time created chronosphere. + */ + if (SuperWeapon[SPC_CHRONOSPHERE].Remove()) { + if (this == PlayerPtr) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || Map.IsTargettingMode == SPC_CHRONO2) { + if (Map.IsTargettingMode == SPC_CHRONO2) { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + if (tech && tech->IsActive && tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + } else { + Map.IsTargettingMode = SPC_NONE; + } + } else { + Map.IsTargettingMode = SPC_NONE; + } + } +#else + if (Map.IsTargettingMode == SPC_CHRONOSPHERE || + Map.IsTargettingMode == SPC_CHRONO2) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no chronosphere present, but there is a chronosphere + ** facility available, then make the chronosphere available as well. + */ + if ((ActiveBScan & STRUCTF_CHRONOSPHERE) && +// (ActLike == HOUSE_GOOD || Session.Type != GAME_NORMAL) && + (unsigned)Control.TechLevel >= BuildingTypeClass::As_Reference(STRUCT_CHRONOSPHERE).Level && +// Control.TechLevel >= Rule.ChronoTechLevel && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_CHRONOSPHERE].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_CHRONOSPHERE, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_CHRONOSPHERE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the iron curtain should be removed from the sidebar + ** because of outside circumstances. The iron curtain facility + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_IRON_CURTAIN) && !SuperWeapon[SPC_IRON_CURTAIN].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the iron curtain when there is no iron curtain facility. + ** Note that this will not remove the one time created iron curtain. + */ + if (SuperWeapon[SPC_IRON_CURTAIN].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_IRON_CURTAIN) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } + } else { + /* + ** If there is no iron curtain present, but there is an iron curtain + ** facility available, then make the iron curtain available as well. + */ + if ((ActiveBScan & STRUCTF_IRON_CURTAIN) && + (ActLike == HOUSE_USSR || ActLike == HOUSE_UKRAINE || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_IRON_CURTAIN].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_IRON_CURTAIN, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_IRON_CURTAIN); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + /* + ** Check to see if the sonar pulse should be removed from the sidebar + ** because of outside circumstances. The spied-upon enemy sub pen + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Is_Present()) { + int usspy = 1 << (Class->House); + bool present = false; + bool powered = false; + for (int q = 0; q < Buildings.Count() && !powered; q++) { + BuildingClass * bldg = Buildings.Ptr(q); + if ((*bldg == STRUCT_SUB_PEN) && (bldg->House->Class->House != Class->House) && (bldg->Spied_By() & usspy) ) { + present = true; + powered = !(bldg->House->Power_Fraction() < 1); + } + } + if ( (!present && !SuperWeapon[SPC_SONAR_PULSE].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the sonar pulse when there is no spied-upon enemy sub pen. + ** Note that this will not remove the one time created sonar pulse. + */ + if (SuperWeapon[SPC_SONAR_PULSE].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } + } + + /* + ** Check to see if the nuclear weapon should be removed from the sidebar + ** because of outside circumstances. The missile silos + ** being destroyed is a good example of this. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Present()) { + if ( (!(ActiveBScan & STRUCTF_MSLO) && !SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) || IsDefeated) { + + /* + ** Remove the nuke when there is no missile silo. + ** Note that this will not remove the one time created nuke. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Remove()) { + if (this == PlayerPtr) { + if (Map.IsTargettingMode == SPC_NUCLEAR_BOMB) { + Map.IsTargettingMode = SPC_NONE; + } + Map.Column[1].Flag_To_Redraw(); + } + IsRecalcNeeded = true; + } + } else { + /* + ** Allow the computer to fire the nuclear weapon when the weapon is + ** ready and the owner is the computer. + */ + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_NUCLEAR_BOMB); + } + } + + } else { + /* + ** If there is no nuclear missile present, but there is a missile + ** silo available, then make the missile available as well. + */ + if ((ActiveBScan & STRUCTF_MSLO) && + ((ActLike != HOUSE_USSR && ActLike != HOUSE_UKRAINE) || Session.Type != GAME_NORMAL) && + (IsHuman || IQ >= Rule.IQSuperWeapons)) { + + SuperWeapon[SPC_NUCLEAR_BOMB].Enable(false, this == PlayerPtr, Power_Fraction() < 1); + + /* + ** Flag the sidebar to be redrawn if necessary. + */ + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_NUCLEAR_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + + if (SuperWeapon[SPC_SPY_MISSION].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_SPY_MISSION].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (this == PlayerPtr && !SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Map.Column[1].Flag_To_Redraw(); + } + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_SPY_MISSION); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && !Scen.IsNoSpyPlane && Control.TechLevel >= Rule.SpyPlaneTechLevel) { + SuperWeapon[SPC_SPY_MISSION].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SPY_MISSION, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_SPY_MISSION); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + if (SuperWeapon[SPC_PARA_BOMB].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_BOMB].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_BOMB); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaBombTechLevel && Session.Type == GAME_NORMAL) { + SuperWeapon[SPC_PARA_BOMB].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Present()) { + if ((ActiveBScan & STRUCTF_AIRSTRIP) == 0) { + if (SuperWeapon[SPC_PARA_INFANTRY].Remove()) { + if (this == PlayerPtr) Map.Column[1].Flag_To_Redraw(); + IsRecalcNeeded = true; + } + } else { + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready() && !IsHuman) { + Special_Weapon_AI(SPC_PARA_INFANTRY); + } + } + } else { + if ((ActiveBScan & STRUCTF_AIRSTRIP) != 0 && Control.TechLevel >= Rule.ParaInfantryTechLevel) { + SuperWeapon[SPC_PARA_INFANTRY].Enable(false, this == PlayerPtr, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_INFANTRY, this); + } + } else { + if (this == PlayerPtr) { + Map.Add(RTTI_SPECIAL, SPC_PARA_INFANTRY); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Attacked -- Lets player know if base is under attack. * + * * + * Call this function whenever a building is attacked (with malice). This function will * + * then announce to the player that his base is under attack. It checks to make sure that * + * this is referring to the player's house rather than the enemy's. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/27/1994 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Attacked(BuildingClass* source) +{ + assert(Houses.ID(this) == ID); + +#ifdef FIXIT_BASE_ANNOUNCE + if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) { +#else + if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) { +#endif + Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0); + SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); + + /* + ** If there is a trigger event associated with being attacked, process it + ** now. + */ + for (int index = 0; index < HouseTriggers[Class->House].Count(); index++) { + HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Harvested -- Adds Tiberium to the harvest storage. * + * * + * Use this routine whenever Tiberium is harvested. The Tiberium is stored equally between * + * all storage capable buildings for the house. Harvested Tiberium adds to the credit * + * value of the house, but only up to the maximum storage capacity that the house can * + * currently maintain. * + * * + * INPUT: tiberium -- The number of Tiberium credits to add to the House's total. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Harvested(unsigned tiberium) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + + Tiberium += tiberium; + if (Tiberium > Capacity) { + Tiberium = Capacity; + IsMaxedOut = true; + } + HarvestedCredits += tiberium; + Silo_Redraw_Check(oldtib, Capacity); +} + + +/*********************************************************************************************** + * HouseClass::Stole -- Accounts for the value of a captured building. * + * * + * Use this routine whenever a building is captured. It keeps track of the cost of the * + * building for use in the scoring routine, because you get an 'economy' boost for the * + * value of the stolen building (but you don't get the credit value for it.) * + * * + * INPUT: worth -- The worth of the building we captured (stole). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/05/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Stole(unsigned worth) +{ + assert(Houses.ID(this) == ID); + + StolenBuildingsCredits += worth; +} + + +/*********************************************************************************************** + * HouseClass::Available_Money -- Fetches the total credit worth of the house. * + * * + * Use this routine to determine the total credit value of the house. This is the sum of * + * the harvested Tiberium in storage and the initial unspent cash reserves. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total credit value of the house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +long HouseClass::Available_Money(void) const +{ + assert(Houses.ID(this) == ID); + + return(Tiberium + Credits); +} + + +/*********************************************************************************************** + * HouseClass::Spend_Money -- Removes money from the house. * + * * + * Use this routine to extract money from the house. Typically, this is a result of * + * production spending. The money is extracted from available cash reserves first. When * + * cash reserves are exhausted, then Tiberium is consumed. * + * * + * INPUT: money -- The amount of money to spend. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/20/1995 JLB : Spends Tiberium before spending cash. * + *=============================================================================================*/ +void HouseClass::Spend_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + long oldtib = Tiberium; + if (money > (unsigned)Tiberium) { + money -= (unsigned)Tiberium; + Tiberium = 0; + Credits -= money; + } else { + Tiberium -= money; + } + Silo_Redraw_Check(oldtib, Capacity); + CreditsSpent += money; +} + + +/*********************************************************************************************** + * HouseClass::Refund_Money -- Refunds money to back to the house. * + * * + * Use this routine when money needs to be refunded back to the house. This can occur when * + * construction is aborted. At this point, the exact breakdown of Tiberium or initial * + * credits used for the orignal purchase is lost. Presume as much of the money is in the * + * form of Tiberium as storage capacity will allow. * + * * + * INPUT: money -- The number of credits to refund back to the house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + * 06/01/1995 JLB : Refunded money is never lost * + *=============================================================================================*/ +void HouseClass::Refund_Money(unsigned money) +{ + assert(Houses.ID(this) == ID); + + Credits += money; +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Capacity -- Adjusts the house Tiberium storage capacity. * + * * + * Use this routine to adjust the maximum storage capacity for the house. This storage * + * capacity will limit the number of Tiberium credits that can be stored at any one time. * + * * + * INPUT: adjust -- The adjustment to the Tiberium storage capacity. * + * * + * inanger -- Is this a forced adjustment to capacity due to some hostile event? * + * * + * OUTPUT: Returns with the number of Tiberium credits lost. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/25/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Adjust_Capacity(int adjust, bool inanger) +{ + assert(Houses.ID(this) == ID); + + long oldcap = Capacity; + int retval = 0; + + Capacity += adjust; + Capacity = max(Capacity, 0L); + if (Tiberium > Capacity) { + retval = Tiberium - Capacity; + Tiberium = Capacity; + if (!inanger) { + Refund_Money(retval); + retval = 0; + } else { + IsMaxedOut = true; + } + } + Silo_Redraw_Check(Tiberium, oldcap); + return(retval); +} + + +/*********************************************************************************************** + * HouseClass::Silo_Redraw_Check -- Flags silos to be redrawn if necessary. * + * * + * Call this routine when either the capacity or tiberium levels change for a house. This * + * routine will determine if the aggregate tiberium storage level will result in the * + * silos changing their imagery. If this is detected, then all the silos for this house * + * are flagged to be redrawn. * + * * + * INPUT: oldtib -- Pre-change tiberium level. * + * * + * oldcap -- Pre-change tiberium storage capacity. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Silo_Redraw_Check(long oldtib, long oldcap) +{ + assert(Houses.ID(this) == ID); + + int oldratio = 0; + if (oldcap) oldratio = (oldtib * 5) / oldcap; + int newratio = 0; + if (Capacity) newratio = (Tiberium * 5) / Capacity; + + if (oldratio != newratio) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == STRUCT_STORAGE) { + b->Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Determines if the specified house is an ally. * + * * + * This routine will determine if the house number specified is a ally to this house. * + * * + * INPUT: house -- The house number to check to see if it is an ally. * + * * + * OUTPUT: Is the house an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(HousesType house) const +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE) { + return(((1<Class->House)); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Ally -- Checks to see if the object is an ally. * + * * + * This routine will examine the specified object and return whether it is an ally or not. * + * * + * INPUT: object -- The object to examine to see if it is an ally. * + * * + * OUTPUT: Is the specified object an ally? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Ally(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (object) { + return(Is_Ally(object->Owner())); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Make_Ally -- Make the specified house an ally. * + * * + * This routine will make the specified house an ally to this house. An allied house will * + * not be considered a threat or potential target. * + * * + * INPUT: house -- The house to make an ally of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 08/08/1995 JLB : Breaks off combat when ally commences. * + * 10/17/1995 JLB : Added reveal base when allied. * + *=============================================================================================*/ +void HouseClass::Make_Ally(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (Is_Allowed_To_Ally(house)) { + + Allies |= (1L << house); + + /* + ** Don't consider the newfound ally to be an enemy -- of course. + */ + if (Enemy == house) { + Enemy = HOUSE_NONE; + } + + if (ScenarioInit) { + Control.Allies |= (1L << house); + } + + if (Session.Type != GAME_NORMAL && !ScenarioInit) { + HouseClass * hptr = HouseClass::As_Pointer(house); + + /* + ** An alliance with another human player will cause the computer + ** players (if present) to become paranoid. + */ + if (hptr != NULL && IsHuman && Rule.IsComputerParanoid) { +// if (hptr != NULL && hptr->IsHuman) { +// if (!hptr->IsHuman) { +// hptr->Make_Ally(Class->House); +// } + Computer_Paranoid(); + } + + char buffer[80]; + + /* + ** Sweep through all techno objects and perform a cheeseball tarcom clear to ensure + ** that fighting will most likely stop when the cease fire begins. + */ + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + + if (object != NULL && object->Is_Techno() && !object->IsInLimbo && object->Owner() == Class->House) { + TARGET target = ((TechnoClass *)object)->TarCom; + if (Target_Legal(target) && As_Techno(target) != NULL) { + if (Is_Ally(As_Techno(target))) { + ((TechnoClass *)object)->Assign_Target(TARGET_NONE); + } + } + } + } + + /* + ** Cause all structures to be revealed to the house that has been + ** allied with. + */ + if (Rule.IsAllyReveal && house == PlayerPtr->Class->House) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b && !b->IsInLimbo && (HouseClass *)b->House == this) { + Map.Sight_From(Coord_Cell(b->Center_Coord()), b->Class->SightRange, PlayerPtr, false); + } + } + } + + if (IsHuman) { + sprintf(buffer, Text_String(TXT_HAS_ALLIED), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_HAS_ALLIED), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[((HouseClass::As_Pointer(house))->Class->House) - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + } + +#if(TEN) + // + // Notify the TEN server of the new alliance + // + if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(hptr->IniName, 1); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the new alliance + // + //if (this == PlayerPtr && hptr != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(hptr->IniName, 1); + //} +#endif // MPATH + + Map.Flag_To_Redraw(false); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Make_Enemy -- Make an enemy of the house specified. * + * * + * This routine will flag the house specified so that it will be an enemy to this house. * + * Enemy houses are legal targets for attack. * + * * + * INPUT: house -- The house to make an enemy of this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 07/27/1995 JLB : Making war is a bilateral action. * + *=============================================================================================*/ +void HouseClass::Make_Enemy(HousesType house) +{ + assert(Houses.ID(this) == ID); + + if (house != HOUSE_NONE && Is_Ally(house)) { + HouseClass * enemy = HouseClass::As_Pointer(house); + Allies &= ~(1L << house); + + if (ScenarioInit) { + Control.Allies &= !(1L << house); + } + + /* + ** Breaking an alliance is a bilateral event. + */ + if (enemy != NULL && enemy->Is_Ally(this)) { + enemy->Allies &= ~(1L << Class->House); + + if (ScenarioInit) { + Control.Allies &= ~(1L << Class->House); + } + } + + if ((Debug_Flag || Session.Type != GAME_NORMAL) && !ScenarioInit && IsHuman) { + char buffer[80]; + + sprintf(buffer, Text_String(TXT_AT_WAR), IniName, HouseClass::As_Pointer(house)->IniName); +// sprintf(buffer, Text_String(TXT_AT_WAR), Session.Players[Class->House - HOUSE_MULTI1]->Name, Session.Players[enemy->Class->House - HOUSE_MULTI1]->Name); + Session.Messages.Add_Message(NULL, 0, buffer, RemapColor, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, TICKS_PER_MINUTE * Rule.MessageDelay); + Map.Flag_To_Redraw(false); + +#if(TEN) + // + // Notify the TEN server of the broken alliance + // + if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_TEN) { + Send_TEN_Alliance(enemy->IniName, 0); + } +#endif // TEN + +#if(MPATH) + // + // Notify the MPATH server of the broken alliance + // + //if (this == PlayerPtr && enemy != NULL && Session.Type == GAME_MPATH) { + //Send_MPATH_Alliance(enemy->IniName, 0); + //} +#endif // MPATH + + } + } +} + + +/*********************************************************************************************** + * HouseClass::Remap_Table -- Fetches the remap table for this house object. * + * * + * This routine will return with the remap table to use when displaying an object owned * + * by this house. If the object is blushing (flashing), then the lightening remap table is * + * always used. The "unit" parameter allows proper remap selection for those houses that * + * have a different remap table for buildings or units. * + * * + * INPUT: blushing -- Is the object blushing (flashing)? * + * * + * remap -- The remap control value to use. * + * REMAP_NONE No remap pointer returned at all. * + * REMAP_NORMAL Return the remap pointer for this house. * + * REMAP_ALTERNATE (Nod solo play only -- forces red remap). * + * Multiplay returns same as REMAP_NORMAL * + * * + * OUTPUT: Returns with a pointer to the remap table to use when drawing this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/25/1995 JLB : Uses remap control value. * + *=============================================================================================*/ +unsigned char const * HouseClass::Remap_Table(bool blushing, RemapType remap) const +{ + assert(Houses.ID(this) == ID); + + if (blushing) return(&Map.FadingLight[0]); + + if (remap == REMAP_NONE) return(0); + + return(ColorRemaps[RemapColor].RemapTable); +} + + +/*********************************************************************************************** + * HouseClass::Suggested_New_Team -- Determine what team should be created. * + * * + * This routine examines the house condition and returns with the team that it thinks * + * should be created. The units that are not currently a member of a team are examined * + * to determine the team needed. * + * * + * INPUT: alertcheck -- Select from the auto-create team list. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass const * HouseClass::Suggested_New_Team(bool alertcheck) +{ + assert(Houses.ID(this) == ID); + + return(TeamTypeClass::Suggested_New_Team(this, AScan, UScan, IScan, VScan, alertcheck)); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Threat -- Adjust threat for the region specified. * + * * + * This routine is called when the threat rating for a region needs to change. The region * + * and threat adjustment are provided. * + * * + * INPUT: region -- The region that adjustment is to occur on. * + * * + * threat -- The threat adjustment to perform. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Threat(int region, int threat) +{ + assert(Houses.ID(this) == ID); + + static int _val[] = { + -MAP_REGION_WIDTH - 1, -MAP_REGION_WIDTH, -MAP_REGION_WIDTH + 1, + -1, 0, 1, + MAP_REGION_WIDTH -1, MAP_REGION_WIDTH, MAP_REGION_WIDTH +1 + }; + static int _thr[] = { + 2, 1, 2, + 1, 0, 1, + 2, 1, 2 + }; + int neg; + int * val = &_val[0]; + int * thr = &_thr[0]; + + if (threat < 0) { + threat = -threat; + neg = true; + } else { + neg = false; + } + + for (int lp = 0; lp < 9; lp ++) { + Regions[region + *val].Adjust_Threat(threat >> *thr, neg); + val++; + thr++; + } +} + + +/*********************************************************************************************** + * HouseClass::Begin_Production -- Starts production of the specified object type. * + * * + * This routine is called from the event system. It will start production for the object * + * type specified. This will be reflected in the sidebar as well as the house factory * + * tracking variables. * + * * + * INPUT: type -- The type of object to begin production on. * + * * + * id -- The subtype of object. * + * * + * OUTPUT: Returns with the reason why, or why not, production was started. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 10/21/1996 JLB : Handles max object case. * + *=============================================================================================*/ +ProdFailType HouseClass::Begin_Production(RTTIType type, int id) +{ + assert(Houses.ID(this) == ID); + int result = true; + bool initial_start = false; + FactoryClass * fptr; + TechnoTypeClass const * tech = Fetch_Techno_Type(type, id); + + fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code, unless we are restarting production. + */ + if (fptr != NULL) { + if (fptr->Is_Building()) { + return(PROD_CANT); + } + } else { + fptr = new FactoryClass(); + if (!fptr) return(PROD_CANT); + Set_Factory(type, fptr); + result = fptr->Set(*tech, *this); + initial_start = true; + + /* + ** If set failed, we probably reached the production cap. Don't let the factory linger, preventing further production attempts. + ** ST - 3/17/2020 2:03PM + */ + if (!result) { + Set_Factory(type, NULL); + delete fptr; + fptr = NULL; + } + } + + if (result) { + fptr->Start(); + + /* + ** Link this factory to the sidebar so that proper graphic feedback + ** can take place. + */ + // Handle Glyphx multiplayer sidebar. ST - 8/14/2019 1:26PM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Factory_Link(fptr->ID, type, id, this); + } + } else { + if (PlayerPtr == this) { + Map.Factory_Link(fptr->ID, type, id); + } + } + + return(PROD_OK); + } + + delete fptr; + return(PROD_CANT); +} + + +/*********************************************************************************************** + * HouseClass::Suspend_Production -- Temporarily puts production on hold. * + * * + * This routine is called from the event system whenever the production of the specified * + * type needs to be suspended. The suspended production will be reflected in the sidebar * + * as well as in the house control structure. * + * * + * INPUT: type -- The type of object that production is being suspended for. * + * * + * OUTPUT: Returns why, or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Suspend_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If the house is already busy producing the requested object, then + ** return with this failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Actually suspend the production. + */ + fptr->Suspend(); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + if (PlayerPtr == this) { + Map.SidebarClass::IsToRedraw = true; + if (!RunningAsDLL) { // Don't force a redraw when running under GlyphX. PlayerPtr==this will always be true in this case, and we don't want to force a redraw even for AI players + Map.Flag_To_Redraw(false); + } + } + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Abandon_Production -- Abandons production of item type specified. * + * * + * This routine is called from the event system whenever production must be abandoned for * + * the type specified. This will remove the factory and pending object from the sidebar as * + * well as from the house factory record. * + * * + * INPUT: type -- The object type that production is being suspended for. * + * * + * OUTPUT: Returns the reason why or why not, production was suspended. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +ProdFailType HouseClass::Abandon_Production(RTTIType type) +{ + assert(Houses.ID(this) == ID); + + FactoryClass * fptr = Fetch_Factory(type); + + /* + ** If there is no factory to abandon, then return with a failure code. + */ + if (fptr == NULL) return(PROD_CANT); + + /* + ** Tell the sidebar that it needs to be redrawn because of this. + */ + // Handle Glyphx multiplayer sidebar. ST - 8/7/2019 10:18AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (IsHuman) { + Sidebar_Glyphx_Abandon_Production(type, fptr->ID, this); + + // Need to clear pending object here if legacy renderer enabled + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + } else { + if (PlayerPtr == this) { + Map.Abandon_Production(type, fptr->ID); + + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + } + } + + /* + ** Abandon production of the object. + */ + fptr->Abandon(); + Set_Factory(type, NULL); + delete fptr; + + return(PROD_OK); +} + + +/*********************************************************************************************** + * HouseClass::Special_Weapon_AI -- Fires special weapon. * + * * + * This routine will pick a good target to fire the special weapon specified. * + * * + * INPUT: id -- The special weapon id to fire. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 PWG : Created. * + *=============================================================================================*/ +void HouseClass::Special_Weapon_AI(SpecialWeaponType id) +{ + assert(Houses.ID(this) == ID); + + /* + ** Loop through all of the building objects on the map + ** and see which ones are available. + */ + BuildingClass * bestptr = NULL; + int best = -1; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + /* + ** If the building is valid, not in limbo, not in the process of + ** being destroyed and not our ally, then we can consider it. + */ + if (b != NULL && !b->IsInLimbo && b->Strength && !Is_Ally(b)) { + if (Percent_Chance(90) && (b->Value() > best || best == -1)) { + best = b->Value(); + bestptr = b; + } + } + } + + if (bestptr) { + CELL cell = Coord_Cell(bestptr->Center_Coord()); + Place_Special_Blast(id, cell); + } +} + + +/*********************************************************************************************** + * HouseClass::Place_Special_Blast -- Place a special blast effect at location specified. * + * * + * This routine will create a blast effect at the cell specified. This is the result of * + * the special weapons. * + * * + * INPUT: id -- The special weapon id number. * + * * + * cell -- The location where the special weapon attack is to occur. * + * * + * OUTPUT: Was the special weapon successfully fired at the location specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented. * + * 07/25/1995 JLB : Added scatter effect for nuclear bomb. * + * 07/28/1995 JLB : Revamped to use super weapon class controller. * + *=============================================================================================*/ +extern void Logic_Switch_Player_Context(ObjectClass *object); +extern void Logic_Switch_Player_Context(HouseClass *object); +extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + +bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell) +{ + assert(Houses.ID(this) == ID); + + // Added. ST - 12/2/2019 11:26AM + bool fired = false; + const char *what = NULL; + + BuildingClass * launchsite = 0; + AnimClass * anim = 0; + switch (id) { + case SPC_SONAR_PULSE: + // Automatically discharge the sonar pulse and uncloak all subs. + if (SuperWeapon[SPC_SONAR_PULSE].Is_Ready()) { + SuperWeapon[SPC_SONAR_PULSE].Discharged(this == PlayerPtr); + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.Activate_Pulse(); + } + Sound_Effect(VOC_SONAR); + IsRecalcNeeded = true; + fired = true; + what = "SONAR"; + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * sub = Vessels.Ptr(index); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*sub == VESSEL_SS || *sub == VESSEL_MISSILESUB) { +#else + if (*sub == VESSEL_SS) { +#endif + sub->PulseCountDown = 15 * TICKS_PER_SECOND; + sub->Do_Uncloak(); + } + } + } + break; + + case SPC_NUCLEAR_BOMB: + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_Ready()) { + if (SuperWeapon[SPC_NUCLEAR_BOMB].Is_One_Time()) { + BulletClass * bullet = new BulletClass(BULLET_NUKE_DOWN, ::As_Target(cell), 0, 200, WARHEAD_NUKE, MPH_VERY_FAST); + if (bullet) { + int celly = Cell_Y(cell); + celly -= 15; + if (celly < 1) celly = 1; + COORDINATE start = Cell_Coord(XY_Cell(Cell_X(cell), celly)); + if (!bullet->Unlimbo(start, DIR_S)) { + delete bullet; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "NUKE"; + if (this == PlayerPtr) { + Map.Column[1].Flag_To_Redraw(); + Map.IsTargettingMode = SPC_NONE; + } + } + } else { + + /* + ** Search for a suitable launch site for this missile. + */ + launchsite = Find_Building(STRUCT_MSLO); + + /* + ** If a launch site was found, then proceed with the normal launch + ** sequence. + */ + if (launchsite) { + launchsite->Assign_Mission(MISSION_MISSILE); + launchsite->Commence(); + NukeDest = cell; + } + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_NUCLEAR_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "NUKE"; + } + } + break; + + case SPC_PARA_INFANTRY: + if (SuperWeapon[SPC_PARA_INFANTRY].Is_Ready()) { + + TeamTypeClass * ttype = TeamTypeClass::As_Pointer("@PINF"); + if (ttype == NULL) { + ttype = new TeamTypeClass; + if (ttype != NULL) { + strcpy(ttype->IniName, "@PINF"); + ttype->IsTransient = true; + ttype->IsPrebuilt = false; + ttype->IsReinforcable = false; + ttype->Origin = WAYPT_SPECIAL; + ttype->MissionCount = 1; + ttype->MissionList[0].Mission = TMISSION_ATT_WAYPT; + ttype->MissionList[0].Data.Value = WAYPT_SPECIAL; + ttype->ClassCount = 2; + ttype->Members[0].Quantity = AircraftTypeClass::As_Reference(AIRCRAFT_BADGER).Max_Passengers(); + ttype->Members[0].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + ttype->Members[1].Quantity = 1; + ttype->Members[1].Class = &AircraftTypeClass::As_Reference(AIRCRAFT_BADGER); + } + } + + if (ttype != NULL) { + ttype->House = Class->House; + Scen.Waypoint[WAYPT_SPECIAL] = Map.Nearby_Location(cell, SPEED_FOOT); + Do_Reinforcements(ttype); + } + +// Create_Air_Reinforcement(this, AIRCRAFT_BADGER, 1, MISSION_HUNT, ::As_Target(cell), TARGET_NONE, INFANTRY_E1); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_INFANTRY].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "PARA"; + } + break; + + case SPC_SPY_MISSION: + if (SuperWeapon[SPC_SPY_MISSION].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_U2, 1, MISSION_HUNT, ::As_Target(cell), ::As_Target(cell)); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_SPY_MISSION].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "SPY"; + } + break; + + case SPC_PARA_BOMB: + if (SuperWeapon[SPC_PARA_BOMB].Is_Ready()) { + Create_Air_Reinforcement(this, AIRCRAFT_BADGER, Rule.BadgerBombCount, MISSION_HUNT, ::As_Target(cell), TARGET_NONE); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_PARA_BOMB].Discharged(this == PlayerPtr); + IsRecalcNeeded = true; + fired = true; + what = "PARABOMB"; + } + break; + + case SPC_IRON_CURTAIN: + if (SuperWeapon[SPC_IRON_CURTAIN].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech) { + switch (tech->What_Am_I()) { + case RTTI_UNIT: + case RTTI_BUILDING: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_SECOND; + } +#endif + tech->Mark(MARK_CHANGE); + Sound_Effect(VOC_IRON1, tech->Center_Coord()); + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } + SuperWeapon[SPC_IRON_CURTAIN].Discharged(this == PlayerPtr); + break; + default: + break; + } + } + + IsRecalcNeeded = true; + fired = true; + what = "IRON"; + } + break; + + case SPC_CHRONOSPHERE: + if (SuperWeapon[SPC_CHRONOSPHERE].Is_Ready()) { + int x = Keyboard->MouseQX - Map.TacPixelX; + int y = Keyboard->MouseQY - Map.TacPixelY; + TechnoClass * tech = Map[cell].Cell_Techno(x, y); + if (tech && Is_Ally(tech)) { + if (tech->What_Am_I() == RTTI_UNIT || + tech->What_Am_I() == RTTI_INFANTRY || +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + (tech->What_Am_I() == RTTI_VESSEL && (*((VesselClass *)tech) != VESSEL_TRANSPORT && *((VesselClass *)tech) != VESSEL_CARRIER) )) { +#else + (tech->What_Am_I() == RTTI_VESSEL && *((VesselClass *)tech) != VESSEL_TRANSPORT)) { +#endif + + if (tech->What_Am_I() != RTTI_UNIT || !((UnitClass *)tech)->IsDeploying) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + bool porthim = true; + if(tech->What_Am_I() == RTTI_UNIT && ((UnitClass *)tech)->Class->Type == UNIT_CHRONOTANK) { + porthim = false; + } + if (porthim) { +#endif + HouseClass* old_player_ptr = PlayerPtr; + Logic_Switch_Player_Context(this); + Map.IsTargettingMode = SPC_CHRONO2; + On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode); + Logic_Switch_Player_Context(old_player_ptr); + UnitToTeleport = tech->As_Target(); + fired = true; + what = "CHRONO"; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + } + break; + + case SPC_CHRONO2: + { + TechnoClass * tech = (TechnoClass *)::As_Object(UnitToTeleport); + CELL oldcell = cell; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (tech != NULL && tech->IsActive && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#else + if (tech != NULL && tech->Is_Foot() && tech->What_Am_I() != RTTI_AIRCRAFT) { +#endif + /* + ** Destroy any infantryman that gets teleported + */ + if (tech->What_Am_I() == RTTI_INFANTRY) { + InfantryClass * inf = (InfantryClass *)tech; + inf->Mark(MARK_UP); + inf->Coord = Cell_Coord(cell); + inf->Mark(MARK_DOWN); + int damage = inf->Strength; + inf->Take_Damage(damage, 0, WARHEAD_FIRE, 0, true); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } else if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_DEMOTRUCK) { + tech->Assign_Target(tech->As_Target()); +#endif + } else { + /* + ** Warp the unit to the new location. + */ + DriveClass * drive = (DriveClass *)tech; + drive->MoebiusCell = Coord_Cell(drive->Coord); + oldcell = drive->MoebiusCell; + drive->Teleport_To(cell); + drive->IsMoebius = true; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->IsMoebius = false; + } + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; + if (tech->What_Am_I() == RTTI_UNIT && *(UnitClass *)tech == UNIT_CHRONOTANK) { + drive->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + } +#else + drive->MoebiusCountDown = Rule.ChronoDuration * TICKS_PER_MINUTE; +#endif + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONO, drive->Coord); + + /* + ** Set active animation on Chronospheres. + */ + for (int index = 0; index < Buildings.Count(); ++index) { + BuildingClass* building = Buildings.Ptr(index); + if (building != nullptr && building->IsActive && building->Owner() == Class->House && *building == STRUCT_CHRONOSPHERE) { + building->Begin_Mode(BSTATE_ACTIVE); + } + } + } + } + UnitToTeleport = TARGET_NONE; + if (this == PlayerPtr) { + Map.IsTargettingMode = SPC_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) { +#endif + SuperWeapon[SPC_CHRONOSPHERE].Discharged(this == PlayerPtr); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + IsRecalcNeeded = true; + fired = true; + what = "CHRONO2"; + + /* + ** Now set a percentage chance that a time quake will occur. + */ + if (!TimeQuake) { + TimeQuake = Percent_Chance(Rule.QuakeChance * 100); + } + + /* + ** Now set a percentage chance that a chronal vortex will appear. It + ** might appear where the object teleported to or it might appear + ** where it teleported from -- random chance. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Don't allow a vortex if the teleportation was due to a chrono tank. + if(tech && tech->IsActive && (tech->What_Am_I() != RTTI_UNIT || *(UnitClass *)tech != UNIT_CHRONOTANK)) +#endif + if (!ChronalVortex.Is_Active() && Percent_Chance(Rule.VortexChance * 100)) { + int x = Random_Pick(0, Map.MapCellWidth-1); + int y = Random_Pick(0, Map.MapCellHeight-1); + ChronalVortex.Appear(Cell_Coord(XY_Cell(Map.MapCellX + x, Map.MapCellY + y))); + +// if (Percent_Chance(50)) { +// ChronalVortex.Appear(Cell_Coord(oldcell)); +// } else { +// ChronalVortex.Appear(Cell_Coord(cell)); +// } + } + + break; + } + } + + /* + ** Maybe trigger an achivement. ST - 12/2/2019 11:25AM + */ + if (IsHuman && fired && what) { + On_Achievement_Event(this, "SUPERWEAPON_FIRED", what); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Place_Object -- Places the object (building) at location specified. * + * * + * This routine is called when a building has been produced and now must be placed on * + * the map. When the player clicks on the map, this routine is ultimately called when the * + * event passes through the event queue system. * + * * + * INPUT: type -- The object type to place. The actual object is lifted from the sidebar. * + * * + * * + * cell -- The location to place the object on the map. * + * * + * OUTPUT: Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +extern void On_Ping(const HouseClass* player_ptr, COORDINATE coord); + +bool HouseClass::Place_Object(RTTIType type, CELL cell) +{ + assert(Houses.ID(this) == ID); + + TechnoClass * tech = 0; + FactoryClass * factory = Fetch_Factory(type); + + /* + ** Only if there is a factory active for this type, can it be "placed". + ** In the case of a missing factory, then this request is completely bogus -- + ** ignore it. This might occur if, between two events to exit the same + ** object, the mouse was clicked on the sidebar to start building again. + ** The second placement event should NOT try to place the object that is + ** just starting construction. + */ + if (factory && factory->Has_Completed()) { + tech = factory->Get_Object(); + + if (cell == -1) { + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + +#ifdef FIXIT_HELI_LANDING + /* + ** Try to find a place for the object to appear from. For helicopters, it has the + ** option of finding a nearby helipad if no helipads are free. + */ + TechnoClass * builder = pending->Who_Can_Build_Me(false, false); + if (builder == NULL && pending->What_Am_I() == RTTI_AIRCRAFT && !((AircraftClass *)pending)->Class->IsFixedWing) { + builder = pending->Who_Can_Build_Me(true, false); + + } +#else + bool intheory = false; + if (pending->What_Am_I() == RTTI_AIRCRAFT) { + + /* + ** BG hack - helicopters don't need a specific building to + ** emerge from, in fact, they'll land next to a building if + ** need be. + */ + if( !((AircraftClass *)pending)->Class->IsFixedWing) { + intheory = true; + } + } + TechnoClass * builder = pending->Who_Can_Build_Me(intheory, false); +#endif + TechnoTypeClass const *object_type = pending->Techno_Type_Class(); + if (builder != NULL && builder->Exit_Object(pending)) { + + /* + ** Since the object has left the factory under its own power, delete + ** the production manager tied to this slot in the sidebar. Its job + ** has been completed. + */ + factory->Set_Is_Blocked(false); + factory->Completed(); + Abandon_Production(type); + + /* + ** Could be tied to an achievement. ST - 11/11/2019 11:56AM + */ + if (IsHuman) { + if (object_type) { + On_Achievement_Event(this, "UNIT_CONSTRUCTED", object_type->IniName); + } + if (pending->IsActive) { + On_Ping(this, pending->Center_Coord()); + } + } + + switch (pending->What_Am_I()) { + case RTTI_UNIT: + JustBuiltUnit = ((UnitClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_VESSEL: + JustBuiltVessel = ((VesselClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_INFANTRY: + JustBuiltInfantry = ((InfantryClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_BUILDING: + JustBuiltStructure = ((BuildingClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + + case RTTI_AIRCRAFT: + JustBuiltAircraft = ((AircraftClass*)pending)->Class->Type; + IsBuiltSomething = true; + break; + } + } else { + /* + ** The object could not leave under it's own power. Just wait + ** until the player tries to place the object again. + */ + + /* + ** Flag that it's blocked so we can re-try the exit later. + ** This would have been a bad idea under the old peer-peer code since it would have pumped events into + ** the queue too often. ST - 2/25/2020 11:56AM + */ + factory->Set_Is_Blocked(true); + return(false); + } + } + + } else { + if (tech) { + TechnoClass * builder = tech->Who_Can_Build_Me(false, false); + if (builder) { + + builder->Transmit_Message(RADIO_HELLO, tech); + if (tech->Unlimbo(Cell_Coord(cell))) { + factory->Completed(); + Abandon_Production(type); + + if (PlayerPtr == this) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN); + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + } + return(true); + } else { + if (this == PlayerPtr) { + Speak(VOX_DEPLOY); + } + } + builder->Transmit_Message(RADIO_OVER_OUT); + } + return(false); + + } else { + + // Play a bad sound here? + return(false); + } + } + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Manual_Place -- Inform display system of building placement mode. * + * * + * This routine will inform the display system that building placement mode has begun. * + * The cursor will be created that matches the layout of the building shape. * + * * + * INPUT: builder -- The factory that is building this object. * + * * + * object -- The building that is going to be placed down on the map. * + * * + * OUTPUT: Was the building placement mode successfully initiated? * + * * + * WARNINGS: This merely adjusts the cursor shape. Nothing that affects networked games * + * is affected. * + * * + * HISTORY: * + * 05/04/1995 JLB : Created. * + * 05/30/1995 JLB : Uses the Bib_And_Offset() function to determine bib size. * + *=============================================================================================*/ +bool HouseClass::Manual_Place(BuildingClass * builder, BuildingClass * object) +{ + assert(Houses.ID(this) == ID); + + if (this == PlayerPtr && !Map.PendingObject && builder && object) { + /* + ** Ensures that object selection doesn't remain when + ** building placement takes place. + */ + Unselect_All(); + + Map.Repair_Mode_Control(0); + Map.Sell_Mode_Control(0); + + Map.PendingObject = object->Class; + Map.PendingObjectPtr = object; + Map.PendingHouse = Class->House; + + Map.Set_Cursor_Shape(object->Occupy_List(true)); + Map.Set_Cursor_Pos(Coord_Cell(builder->Coord)); + builder->Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*************************************************************************** + * HouseClass::Clobber_All -- removes all objects for this house * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine removes the house itself, so the multiplayer code * + * must not rely on there being "empty" houses lying around. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + *=========================================================================*/ +void HouseClass::Clobber_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this) { + delete ::Aircraft.Ptr(i); + i--; + } + } + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this) { + delete ::Units.Ptr(i); + i--; + } + } + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this) { + delete ::Vessels.Ptr(i); + i--; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this) { + delete Infantry.Ptr(i); + i--; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this) { + delete Buildings.Ptr(i); + i--; + } + } + for (i = 0; i < TeamTypes.Count(); i++) { + if (TeamTypes.Ptr(i)->House == Class->House) { + delete TeamTypes.Ptr(i); + i--; + } + } + for (i = 0; i < Triggers.Count(); i++) { + if (Triggers.Ptr(i)->Class->House == Class->House) { + delete Triggers.Ptr(i); + i--; + } + } + for (i = 0; i < TriggerTypes.Count(); i++) { + if (TriggerTypes.Ptr(i)->House == Class->House) { + delete TriggerTypes.Ptr(i); + i--; + } + } + + delete this; +} + + +/*********************************************************************************************** + * HouseClass::Detach -- Removes specified object from house tracking systems. * + * * + * This routine is called when an object is to be removed from the game system. If the * + * specified object is part of the house tracking system, then it will be removed. * + * * + * INPUT: target -- The target value of the object that is to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : commented * + *=============================================================================================*/ +void HouseClass::Detach(TARGET target, bool ) +{ + assert(Houses.ID(this) == ID); + + if (ToCapture == target) { + ToCapture = TARGET_NONE; + } + + if (Is_Target_Trigger(target)) { + HouseTriggers[ID].Delete(As_Trigger(target)); + } +} + + +/*********************************************************************************************** + * HouseClass::Does_Enemy_Building_Exist -- Checks for enemy building of specified type. * + * * + * This routine will examine the enemy houses and if there is a building owned by one * + * of those house, true will be returned. * + * * + * INPUT: btype -- The building type to check for. * + * * + * OUTPUT: Does a building of the specified type exist for one of the enemy houses? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Does_Enemy_Building_Exist(StructType btype) const +{ + assert(Houses.ID(this) == ID); + + int bflag = 1L << btype; + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + HouseClass * house = HouseClass::As_Pointer(index); + + if (house && !Is_Ally(house) && (house->ActiveBScan & bflag) != 0) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Object -- Determine what would the next buildable object be. * + * * + * This routine will examine the house status and return with a techno type pointer to * + * the object type that it thinks should be created. The type is restricted to match the * + * type specified. Typical use of this routine is by computer controlled factories. * + * * + * INPUT: objecttype -- The type of object to restrict the scan for. * + * * + * kennel -- Is this from a kennel? There are special hacks to ensure that only * + * dogs can be produced from a kennel. * + * * + * OUTPUT: Returns with a pointer to a techno type for the object type that should be * + * created. If no object should be created, then NULL is returned. * + * * + * WARNINGS: This is a time consuming routine. Only call when necessary. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * HouseClass::Suggest_New_Object(RTTIType objecttype, bool kennel) const +{ + assert(Houses.ID(this) == ID); + + TechnoTypeClass const * techno = NULL; + + switch (objecttype) { + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + if (BuildAircraft != AIRCRAFT_NONE) { + return(&AircraftTypeClass::As_Reference(BuildAircraft)); + } + return(NULL); + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + if (BuildVessel != VESSEL_NONE) { + return(&VesselTypeClass::As_Reference(BuildVessel)); + } + return(NULL); + + /* + ** Unit construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_UNIT: + case RTTI_UNITTYPE: + if (BuildUnit != UNIT_NONE) { + return(&UnitTypeClass::As_Reference(BuildUnit)); + } + return(NULL); + + /* + ** Infantry construction is based on the rule that up to twice the number required + ** to fill all teams will be created. + */ + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + if (BuildInfantry != INFANTRY_NONE) { + if (kennel && BuildInfantry != INFANTRY_DOG) return(NULL); + if (!kennel && BuildInfantry == INFANTRY_DOG) return(NULL); + return(&InfantryTypeClass::As_Reference(BuildInfantry)); + } + return(NULL); + + /* + ** Building construction is based upon the preconstruction list. + */ + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); + } + return(techno); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Remove -- Removes the flag from the specified target. * + * * + * This routine will remove the flag attached to the specified target object or cell. * + * Call this routine before placing the object down. This is called inherently by the * + * the Flag_Attach() functions. * + * * + * INPUT: target -- The target that the flag was attached to but will be removed from. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully removed from the specified target? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Remove(TARGET target, bool set_home) +{ + assert(Houses.ID(this) == ID); + + bool rc = false; + + if (Target_Legal(target)) { + + /* + ** Remove the flag from a unit + */ + UnitClass * object = As_Unit(target); + if (object) { + rc = object->Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + + } else { + + /* + ** Remove the flag from a cell + */ + CELL cell = As_Cell(target); + if (Map.In_Radar(cell)) { + rc = Map[cell].Flag_Remove(); + if (rc && FlagLocation == target) { + FlagLocation = TARGET_NONE; + } + } + } + + /* + ** Handle the flag home cell: + ** If 'set_home' is set, clear the home value & the cell's overlay + */ + if (set_home) { + if (FlagHome != 0) { + Map[FlagHome].Overlay = OVERLAY_NONE; + Map.Flag_Cell(FlagHome); + FlagHome = 0; + } + } + } + return(rc); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attach flag to specified cell (or thereabouts). * + * * + * This routine will attach the house flag to the location specified. If the location * + * cannot contain the flag, then a suitable nearby location will be selected. * + * * + * INPUT: cell -- The desired cell location to place the flag. * + * * + * set_home -- if true, resets the flag's waypoint designation * + * * + * OUTPUT: Was the flag successfully placed? * + * * + * WARNINGS: The cell picked for the flag might very likely not be the cell requested. * + * Check the FlagLocation value to determine the final cell resting spot. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + * 10/08/1996 JLB : Uses map nearby cell scanning handler. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(CELL cell, bool set_home) +{ + assert(Houses.ID(this) == ID); + + bool rc; + bool clockwise; + + /* + ** Randomly decide if we're going to search cells clockwise or counter- + ** clockwise + */ + clockwise = Percent_Chance(50); + + /* + ** Only continue if this cell is a legal placement cell. + */ + if (Map.In_Radar(cell)) { + + /* + ** If the flag already exists, then it must be removed from the object + ** it is attached to. + */ + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the cell specified. If it can't be placed, then pick + ** a nearby cell where it can be placed. + */ + CELL newcell = cell; + rc = Map[newcell].Flag_Place(Class->House); + if (!rc) { + newcell = Map.Nearby_Location(cell, SPEED_TRACK, -1, MZONE_NORMAL, true); + if (newcell != 0) { + rc = Map[newcell].Flag_Place(Class->House); + } + +#ifdef OBSOLETE + /* + ** Loop for increasing distance from the desired cell. + ** For each distance, randomly pick a starting direction. Between + ** this and the clockwise/counterclockwise random value, the flag + ** should appear to be placed fairly randomly. + */ + for (int dist = 1; dist < 32; dist++) { + FacingType fcounter; + FacingType rot; + + /* + ** Clockwise search. + */ + if (clockwise) { + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot++; + if (rot > FACING_NW) rot = FACING_N; + } + } else { + + /* + ** Counter-clockwise search + */ + rot = Random_Pick(FACING_N, FACING_NW); + for (fcounter = FACING_NW; fcounter >= FACING_N; fcounter--) { + newcell = Coord_Cell(Coord_Move(Cell_Coord(cell), Facing_Dir(rot), dist*256)); + if (Map.In_Radar(newcell) && Map[newcell].Flag_Place(Class->House)) { + dist = 32; + rc = true; + break; + } + rot--; + if (rot < FACING_N) + rot = FACING_NW; + } + } + } +#endif + } + + /* + ** If we've found a spot for the flag, place the flag at the new cell. + ** if 'set_home' is set, OR this house has no current flag home cell, + ** mark that cell as this house's flag home cell. Otherwise fall back + ** on returning the flag to its home. + */ + if (rc) { + FlagLocation = As_Target(newcell); + + if (set_home || FlagHome == 0) { + Map[newcell].Overlay = OVERLAY_FLAG_SPOT; + Map[newcell].OverlayData = 0; + Map[newcell].Recalc_Attributes(); + FlagHome = newcell; + } + } + else if (FlagHome != 0) { + rc = Map[FlagHome].Flag_Place(Class->House); + } + + return(rc); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Flag_Attach -- Attaches the house flag the specified unit. * + * * + * This routine will attach the house flag to the specified unit. This routine is called * + * when a unit drives over a cell containing a flag. * + * * + * INPUT: object -- Pointer to the object that the house flag is to be attached to. * + * * + * set_home -- if true, clears the flag's waypoint designation * + * * + * OUTPUT: Was the flag attached successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_Attach(UnitClass * object, bool set_home) +{ + assert(Houses.ID(this) == ID); + + if (object && !object->IsInLimbo) { + Flag_Remove(FlagLocation, set_home); + + /* + ** Attach the flag to the object. + */ + object->Flag_Attach(Class->House); + FlagLocation = object->As_Target(); + return(true); + } + return(false); +} + +extern void On_Defeated_Message(const char* message, float timeout_seconds); + +/*************************************************************************** + * HouseClass::MPlayer_Defeated -- multiplayer; house is defeated * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/25/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::MPlayer_Defeated(void) +{ + assert(Houses.ID(this) == ID); + + char txt[80]; + int i,j; + unsigned char id; + HouseClass * hptr; + HouseClass * hptr2; + int num_alive; + int num_humans; + int all_allies; + + /* + ** Set the defeat flag for this house + */ + IsDefeated = true; + + /* + ** If this is a computer controlled house, then all computer controlled + ** houses become paranoid. + */ + if (IQ == Rule.MaxIQ && !IsHuman && Rule.IsComputerParanoid) { + Computer_Paranoid(); + } + + /* + ** Remove this house's flag & flag home cell + */ + if (Special.IsCaptureTheFlag) { + if (FlagLocation) { + Flag_Remove(FlagLocation, true); + } else { + if (FlagHome != 0) { + Flag_Remove(FlagHome, true); + } + } + } + + /* + ** If this is me: + ** - Set MPlayerObiWan, so I can only send messages to all players, and + ** not just one (so I can't be obnoxiously omnipotent) + ** - Reveal the map + ** - Add my defeat message + */ + if (PlayerPtr == this) { + Session.ObiWan = 1; + Debug_Unshroud = true; + HidPage.Clear(); + Map.Flag_To_Redraw(true); + + /* + ** Pop up a message showing that I was defeated + */ + sprintf(txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + if (Session.Type == GAME_NORMAL) { + Session.Messages.Add_Message(NULL, 0, txt, Session.ColorIdx, + TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + Map.Flag_To_Redraw(false); + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; + On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + } else { + + /* + ** If it wasn't me, find out who was defeated + */ + if (IsHuman) { + sprintf (txt, Text_String(TXT_PLAYER_DEFEATED), IniName); + + //Session.Messages.Add_Message(NULL, 0, txt, RemapColor, + // TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + RedrawOptionsMenu = true; + + int timeout = Rule.MessageDelay * TICKS_PER_MINUTE; + On_Defeated_Message(txt, timeout * 60.0f / TICKS_PER_MINUTE); + Sound_Effect(VOC_INCOMING_MESSAGE); + } + } + + /* + ** Find out how many players are left alive. + */ + num_alive = 0; + num_humans = 0; + for (i = 0; i < Session.MaxPlayers; i++) { + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (hptr && !hptr->IsDefeated) { + if (hptr->IsHuman) { + num_humans++; + } + num_alive++; + } + } + + /* + ** If all the houses left alive are allied with each other, then in reality + ** there's only one player left: + */ + all_allies = 1; + for (i = 0; i < Session.MaxPlayers; i++) { + + /* + ** Get a pointer to this house + */ + hptr = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + i)); + if (!hptr || hptr->IsDefeated) + continue; + + /* + ** Loop through all houses; if there's one left alive that this house + ** isn't allied with, then all_allies will be false + */ + for (j = 0; j < Session.MaxPlayers; j++) { + hptr2 = HouseClass::As_Pointer((HousesType)(HOUSE_MULTI1 + j)); + if (!hptr2) { + continue; + } + + if (!hptr2->IsDefeated && !hptr->Is_Ally(hptr2)) { + all_allies = 0; + break; + } + } + if (!all_allies) { + break; + } + } + + /* + ** If all houses left are allies, set 'num_alive' to 1; game over. + */ + if (all_allies) { + num_alive = 1; + } + + /* + ** If there's only one human player left or no humans left, the game is over: + ** - Determine whether this player wins or loses, based on the state of the + ** player's IsDefeated flag + ** - Find all players' indices in the Session.Score array + ** - Tally up scores for this game + */ + if (num_alive == 1 || num_humans == 0) { + if (PlayerPtr->IsDefeated) { + PlayerLoses = true; + } else { + PlayerWins = true; + } + + /* + ** Add up the scores + */ + Tally_Score(); + + /* + ** Destroy all the IPX connections, since we have to go through the rest + ** of the Main_Loop() before we detect that the game is over, and we'll + ** end up waiting for frame sync packets from the other machines. + */ + if (Session.Type==GAME_IPX || Session.Type == GAME_INTERNET) { + i = 0; + while (Ipx.Num_Connections() && (i++ < 1000) ) { + id = Ipx.Connection_ID(0); + Ipx.Delete_Connection(id); + } + Session.NumPlayers = 0; + } + +#if(TEN) + // + // Tell the TEN server who won + // + if (Session.Type == GAME_TEN) { + Send_TEN_Win_Packet(); + } +#endif // TEN + +#if(MPATH) + // + // Tell the MPATH server who won + // + if (Session.Type == GAME_MPATH) { + FILE *fp; + + fp = fopen("winner.txt","wt"); + if (fp) { + for (i = 0; i < Session.Players.Count(); i++) { + hptr = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (!hptr->IsDefeated) { + fprintf(fp,"%s\n",hptr->IniName); + } + } + fclose(fp); + } + } +#endif // MPATH + + } + + /* + ** Be sure our messages get displayed, even if we're about to exit. + */ + Map.Render(); +} + + +/*************************************************************************** + * HouseClass::Tally_Score -- Fills in the score system for this round * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void HouseClass::Tally_Score(void) +{ + HousesType house; + HousesType house2; + HouseClass * hptr; + int score_index; + int i,j,k; + int max_index; + int max_count; + int count; + + /* + ** Loop through all houses, tallying up each player's score + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + hptr = HouseClass::As_Pointer(house); + /* + ** Skip this house if it's not human. + */ + if (!hptr || !hptr->IsHuman) { + continue; + } + /* + ** Now find out where this player is in the score array + */ + score_index = -1; + for (i = 0; i < Session.NumScores; i++) { + if (!stricmp(hptr->IniName, Session.Score[i].Name)) { + score_index = i; + break; + } + } + + /* + ** If the index is still -1, the name wasn't found; add a new entry. + */ + if (score_index == -1) { + /* + ** Just add this player to the end of the array, if there's room + */ + if (Session.NumScores < MAX_MULTI_NAMES) { + score_index = Session.NumScores; + Session.NumScores++; + } + /* + ** If there's not room, we have to remove somebody. + ** For each player in the scores array, count the # of '-1' entries + ** from this game backwards; the one with the most is the one that + ** hasn't played the longest; replace him with this new guy. + */ + else { + max_index = 0; + max_count = 0; + for (j = 0; j < Session.NumScores; j++) { + count = 0; + for (k = Session.NumScores - 1; k >= 0; k--) { + if (Session.Score[j].Kills[k]==-1) { + count++; + } + else { + break; + } + } + if (count > max_count) { + max_count = count; + max_index = j; + } + } + score_index = max_index; + } + + /* + ** Initialize this new score entry + */ + Session.Score[score_index].Wins = 0; + strcpy (Session.Score[score_index].Name, hptr->IniName); + for (j = 0; j < MAX_MULTI_GAMES; j++) + Session.Score[score_index].Kills[j] = -1; + } + + /* + ** Init this player's Kills to 0 (-1 means he didn't play this round; + ** 0 means he played but got no kills). + */ + Session.Score[score_index].Kills[Session.CurGame] = 0; + + /* + ** Init this player's color to his last-used color index + */ + Session.Score[score_index].Color = hptr->RemapColor; + + /* + ** If this house was undefeated, it must have been the winner. + ** (If no human houses are undefeated, the computer won.) + */ + if (!hptr->IsDefeated) { + Session.Score[score_index].Wins++; + Session.Winner = score_index; + } + + /* + ** Tally up all kills for this player + */ + for (house2 = HOUSE_FIRST; house2 < HOUSE_COUNT; house2++) { + Session.Score[score_index].Kills[Session.CurGame] += hptr->UnitsKilled[house2]; + Session.Score[score_index].Kills[Session.CurGame] += hptr->BuildingsKilled[house2]; + } + } +} + + +/*************************************************************************** + * HouseClass::Blowup_All -- blows up everything * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/16/1995 BRR : Created. * + * 06/09/1995 JLB : Handles aircraft. * + * 05/07/1996 JLB : Handles ships. * + *=========================================================================*/ +void HouseClass::Blowup_All(void) +{ + assert(Houses.ID(this) == ID); + + int i; + int damage; + UnitClass * uptr; + InfantryClass * iptr; + BuildingClass * bptr; + int count; + WarheadType warhead; + + /* + ** Find everything owned by this house & blast it with a huge amount of damage + ** at zero range. Do units before infantry, so the units' drivers are killed + ** too. Using Explosion_Damage is like dropping a big bomb right on the + ** object; it will also damage anything around it. + */ + for (i = 0; i < ::Units.Count(); i++) { + if (::Units.Ptr(i)->House == this && !::Units.Ptr(i)->IsInLimbo) { + uptr = ::Units.Ptr(i); + + /* + ** Some units can't be killed with one shot, so keep damaging them until + ** they're gone. The unit will destroy itself, and put an infantry in + ** its place. When the unit destroys itself, decrement 'i' since + ** its pointer will be removed from the active pointer list. + */ + count = 0; + while (::Units.Ptr(i)==uptr && uptr->Strength) { + damage = uptr->Strength; + uptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5 && uptr->IsActive) { + delete uptr; + break; + } + } + i--; + } + } + + /* + ** Destroy all aircraft owned by this house. + */ + for (i = 0; i < ::Aircraft.Count(); i++) { + if (::Aircraft.Ptr(i)->House == this && !::Aircraft.Ptr(i)->IsInLimbo) { + AircraftClass * aptr = ::Aircraft.Ptr(i); + + damage = aptr->Strength; + aptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!aptr->IsActive) { + i--; + } + } + } + + /* + ** Destroy all vessels owned by this house. + */ + for (i = 0; i < ::Vessels.Count(); i++) { + if (::Vessels.Ptr(i)->House == this && !::Vessels.Ptr(i)->IsInLimbo) { + VesselClass * vptr = ::Vessels.Ptr(i); + + damage = vptr->Strength; + vptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + if (!vptr->IsActive) { + i--; + } + } + } + + /* + ** Buildings don't delete themselves when they die; they shake the screen + ** and begin a countdown, so don't decrement 'i' when it's destroyed. + */ + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->House == this && !Buildings.Ptr(i)->IsInLimbo) { + bptr = Buildings.Ptr(i); + + count = 0; + while (Buildings.Ptr(i)==bptr && bptr->Strength) { + damage = bptr->Strength; + bptr->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + count++; + if (count > 5) { + delete bptr; + break; + } + } + } + } + + /* + ** Infantry don't delete themselves when they die; they go into a death- + ** animation sequence, so there's no need to decrement 'i' when they die. + ** Infantry should die by different types of warheads, so their death + ** anims aren't all synchronized. + */ + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->House == this && !Infantry.Ptr(i)->IsInLimbo) { + iptr = Infantry.Ptr(i); + + count = 0; + while (Infantry.Ptr(i)==iptr && iptr->Strength) { + damage = iptr->Strength; + warhead = Random_Pick(WARHEAD_SA, WARHEAD_FIRE); + iptr->Take_Damage(damage, 0, warhead, NULL, true); + + count++; + if (count > 5) { + delete iptr; + break; + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Die -- Flags the house to blow up soon. * + * * + * When this routine is called, the house will blow up after a period of time. Typically * + * this is called when the flag is captured or the HQ destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to blow up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Die(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToDie = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToDie); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Win -- Flags the house to win soon. * + * * + * When this routine is called, the house will be declared the winner after a period of * + * time. * + * * + * INPUT: none * + * * + * OUTPUT: Was the house flagged to win? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Win(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsToWin && !IsToDie && !IsToLose) { + IsToWin = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToWin); +} + + +/*********************************************************************************************** + * HouseClass::Flag_To_Lose -- Flags the house to die soon. * + * * + * When this routine is called, it will spell the doom of this house. In a short while * + * all of the object owned by this house will explode. Typical use of this routine is when * + * the flag has been captured or the command vehicle has been destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Has the doom been initiated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/12/1995 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Flag_To_Lose(void) +{ + assert(Houses.ID(this) == ID); + + IsToWin = false; + if (!IsToDie && !IsToLose) { + IsToLose = true; + BorrowedTime = TICKS_PER_MINUTE * Rule.SavourDelay; + } + return(IsToLose); +} + + +/*********************************************************************************************** + * HouseClass::Init_Data -- Initializes the multiplayer color data. * + * * + * This routine is called when initializing the color and remap data for this house. The * + * primary user of this routine is the multiplayer version of the game, especially for * + * saving & loading multiplayer games. * + * * + * INPUT: color -- The color of this house. * + * * + * house -- The house that this should act like. * + * * + * credits -- The initial credits to assign to this house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +extern bool NowSavingGame; // TEMP MBL: Need to discuss better solution with Steve +void HouseClass::Init_Data(PlayerColorType color, HousesType house, int credits) +{ + assert(Houses.ID(this) == ID); + + Credits = Control.InitialCredits = credits; + VisibleCredits.Current = Credits; + RemapColor = color; + ActLike = house; + + // MBL 03.20.2020 + // Attempt to fix Red Alert credit tick-up bug after saving a game that has had harvesting underway + // Note that this code gets called with both game loads and saves + // When this function is called, sometimes credits value has Tiberium (or HarvestedCredits?) variables applied, and sometimes now + // + if (NowSavingGame == true) + { + // At this point VisibleCredits.Current (set above) does not have harvested ore/tiberium applied, but VisibleCredits.Credits does + VisibleCredits.Current = VisibleCredits.Credits; + } +} + + +/*********************************************************************************************** + * HouseClass::Power_Fraction -- Fetches the current power output rating. * + * * + * Use this routine to fetch the current power output as a fixed point fraction. The * + * value 0x0100 is 100% power. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with power rating as a fixed pointer number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1995 JLB : Created. * + *=============================================================================================*/ +fixed HouseClass::Power_Fraction(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power >= Drain || Drain == 0) return(1); + + if (Power) { + return(fixed(Power, Drain)); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Sell_Wall -- Tries to sell the wall at the specified location. * + * * + * This routine will try to sell the wall at the specified location. If there is a wall * + * present and it is owned by this house, then it can be sold. * + * * + * INPUT: cell -- The cell that wall selling is desired. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1995 JLB : Created. * + * 11/02/1996 JLB : Checks unsellable bit for wall type. * + *=============================================================================================*/ +void HouseClass::Sell_Wall(CELL cell) +{ + assert(Houses.ID(this) == ID); + + if ((unsigned)cell > 0) { + OverlayType overlay = Map[cell].Overlay; + + if (overlay != OVERLAY_NONE && Map[cell].Owner == Class->House) { + OverlayTypeClass const & optr = OverlayTypeClass::As_Reference(overlay); + + if (optr.IsWall) { + BuildingTypeClass const * btype = NULL; + switch (overlay) { + case OVERLAY_SANDBAG_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_SANDBAG_WALL); + break; + + case OVERLAY_CYCLONE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_CYCLONE_WALL); + break; + + case OVERLAY_BRICK_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BRICK_WALL); + break; + + case OVERLAY_BARBWIRE_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_BARBWIRE_WALL); + break; + + case OVERLAY_WOOD_WALL: + btype = &BuildingTypeClass::As_Reference(STRUCT_WOOD_WALL); + break; + + case OVERLAY_FENCE: + btype = &BuildingTypeClass::As_Reference(STRUCT_FENCE); + break; + + default: + break; + } + if (btype != NULL && !btype->IsUnsellable) { + + if (PlayerPtr == this) { + Sound_Effect(VOC_CASHTURN); + } + + Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); + Map[cell].Overlay = OVERLAY_NONE; + Map[cell].OverlayData = 0; + Map[cell].Owner = HOUSE_NONE; + Map[cell].Wall_Update(); + Map[cell].Adjacent_Cell(FACING_N).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_W).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_S).Wall_Update(); + Map[cell].Adjacent_Cell(FACING_E).Wall_Update(); + Map[cell].Recalc_Attributes(); + Map[cell].Redraw_Objects(); + Map.Radar_Pixel(cell); + Detach_This_From_All(::As_Target(cell), true); + + if (optr.IsCrushable) { + Map.Zone_Reset(MZONEF_NORMAL); + } else { + Map.Zone_Reset(MZONEF_CRUSHER|MZONEF_NORMAL); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Suggest_New_Building -- Examines the situation and suggests a building. * + * * + * This routine is called when a construction yard needs to know what to build next. It * + * will either examine the prebuilt base list or try to figure out what to build next * + * based on the current game situation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the building type class to build. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +BuildingTypeClass const * HouseClass::Suggest_New_Building(void) const +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) { + return(&BuildingTypeClass::As_Reference(BuildStructure)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Building -- Finds a building of specified type. * + * * + * This routine is used to find a building of the specified type. This is particularly * + * useful for when some event requires a specific building instance. The nuclear missile * + * launch is a good example. * + * * + * INPUT: type -- The building type to scan for. * + * * + * zone -- The zone that the building must be located in. If no zone specific search * + * is desired, then pass ZONE_NONE. * + * * + * OUTPUT: Returns with a pointer to the building type requested. If there is no building * + * of the type requested, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + * 10/02/1995 JLB : Allows for zone specifics. * + *=============================================================================================*/ +BuildingClass * HouseClass::Find_Building(StructType type, ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Only scan if we KNOW there is at least one building of the type + ** requested. + */ + if (BQuantity[type] > 0) { + + /* + ** Search for a suitable launch site for this missile. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + if (b && !b->IsInLimbo && b->House == this && *b == type) { + if (zone == ZONE_NONE || Which_Zone(b) == zone) { + return(b); + } + } + } + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Find_Build_Location -- Finds a suitable building location. * + * * + * This routine is used to find a suitable building location for the building specified. * + * The auto base building logic uses this when building the base for the computer. * + * * + * INPUT: building -- Pointer to the building that needs to be placed down. * + * * + * OUTPUT: Returns with the coordinate to place the building at. If there are no suitable * + * locations, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE HouseClass::Find_Build_Location(BuildingClass * building) const +{ + assert(Houses.ID(this) == ID); + + int zonerating[ZONE_COUNT]; + struct { + int AntiAir; // Average air defense for the base. + int AntiArmor; // Average armor defense for the base. + int AntiInfantry; // Average infantry defense for the base. + } zoneinfo = {0,0,0}; + int antiair = building->Anti_Air(); + int antiarmor = building->Anti_Armor(); + int antiinfantry = building->Anti_Infantry(); + bool adj = true; + + /* + ** Never place combat buildings adjacent to each other. This is partly + ** because combat buildings don't have a bib and jamming will occur as well + ** as because spacing defensive buildings out will yield a better + ** defense. + */ + if (antiair || antiarmor || antiinfantry) { + adj = false; + } + + /* + ** Determine the average zone strengths for the base. This value is + ** used to determine what zones are considered under or over strength. + */ + ZoneType z; + for (z = ZONE_NORTH; z < ZONE_COUNT; z++) { + zoneinfo.AntiAir += ZoneInfo[z].AirDefense; + zoneinfo.AntiArmor += ZoneInfo[z].ArmorDefense; + zoneinfo.AntiInfantry += ZoneInfo[z].InfantryDefense; + } + zoneinfo.AntiAir /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiArmor /= ZONE_COUNT-ZONE_NORTH; + zoneinfo.AntiInfantry /= ZONE_COUNT-ZONE_NORTH; + + /* + ** Give each zone a rating for value. The higher the value the more desirable + ** to place the specified building in that zone. Factor the average value of + ** zone defense such that more weight is given to zones that are very under + ** defended. + */ + memset(&zonerating[0], '\0', sizeof(zonerating)); + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + int diff; + + diff = zoneinfo.AntiAir-ZoneInfo[z].AirDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiair, diff); + } + + diff = zoneinfo.AntiArmor-ZoneInfo[z].ArmorDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiarmor, diff); + } + + diff = zoneinfo.AntiInfantry-ZoneInfo[z].InfantryDefense; + if (z == ZONE_CORE) diff /= 2; + if (diff > 0) { + zonerating[z] += min(antiinfantry, diff); + } + } + + /* + ** Now that each zone has been given a desirability rating, find the zone + ** with the greatest value and try to place the building in that zone. + */ + ZoneType zone = Random_Pick(ZONE_FIRST, ZONE_WEST); + int largest = 0; + for (z = ZONE_FIRST; z < ZONE_COUNT; z++) { + if (zonerating[z] > largest) { + zone = z; + largest = zonerating[z]; + } + } + + CELL zcell = Find_Cell_In_Zone(building, zone); + if (zcell) { + return(Cell_Coord(zcell)); + } + + /* + ** Could not build in preferred zone, so try building in any zone. + */ + static ZoneType _zones[] = {ZONE_CORE, ZONE_NORTH, ZONE_SOUTH, ZONE_EAST, ZONE_WEST}; + int start = Random_Pick(0, ARRAY_SIZE(_zones)-1); + for (int zz = 0; zz < ARRAY_SIZE(_zones); zz++) { + ZoneType tryzone = _zones[(zz + start) % ARRAY_SIZE(_zones)]; + zcell = Find_Cell_In_Zone(building, tryzone); + if (zcell) return(zcell); + } + + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Center -- Recalculates the center point of the base. * + * * + * This routine will average the location of the base and record the center point. The * + * recorded center point is used to determine such things as how far the base is spread * + * out and where to protect the most. This routine should be called whenever a building * + * is created or destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/28/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Center(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** First presume that there is no base. If there is a base, then these values will be + ** properly filled in below. + */ + Center = 0; + Radius = 0; + for (ZoneType zone = ZONE_FIRST; zone < ZONE_COUNT; zone++) { + ZoneInfo[zone].AirDefense = 0; + ZoneInfo[zone].ArmorDefense = 0; + ZoneInfo[zone].InfantryDefense = 0; + } + + /* + ** Only process the center base size/position calculation if there are buildings to + ** consider. When no buildings for this house are present, then no processing need + ** occur. + */ + if (CurBuildings > 0) { + int x = 0; + int y = 0; + int count = 0; + int index; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + + /* + ** Give more "weight" to buildings that cost more. The presumption is that cheap + ** buildings don't affect the base disposition as much as the more expensive + ** buildings do. + */ + int weight = (b->Class->Cost_Of() / 1000)+1; + for (int i = 0; i < weight; i++) { + x += Coord_X(b->Center_Coord()); + y += Coord_Y(b->Center_Coord()); + count++; + } + } + } + + /* + ** This second check for quantity of buildings is necessary because the first + ** check against CurBuildings doesn't take into account if the building is in + ** limbo, but for base calculation, the limbo state disqualifies a building + ** from being processed. Thus, CurBuildings may indicate a base, but count may + ** not match. + */ + if (count > 0) { + x /= count; + y /= count; + +#ifdef NEVER + /* + ** Bias the center of the base away from the edges of the map. + */ + LEPTON left = Cell_To_Lepton(Map.MapCellX + 10); + LEPTON top = Cell_To_Lepton(Map.MapCellY + 10); + LEPTON right = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth - 10); + LEPTON bottom = Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight - 10); + if (x < left) x = left; + if (x > right) x = right; + if (y < top) y = top; + if (y > bottom) y = bottom; +#endif + + Center = XY_Coord(x, y); + } + + /* + ** If there were any buildings discovered as legal to consider as part of the base, + ** then figure out the general average radius of the building disposition as it + ** relates to the center of the base. + */ + if (count > 1) { + int radius = 0; + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + radius += Distance(Center, b->Center_Coord()); + } + } + Radius = max(radius / count, 2 * CELL_LEPTON_W); + + /* + ** Determine the relative strength of each base defense zone. + */ + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && (HouseClass *)b->House == this && b->Strength > 0) { + ZoneType z = Which_Zone(b); + + if (z != ZONE_NONE) { + ZoneInfo[z].ArmorDefense += b->Anti_Armor(); + ZoneInfo[z].AirDefense += b->Anti_Air(); + ZoneInfo[z].InfantryDefense += b->Anti_Infantry(); + } + } + } + + } else { + Radius = 0x0200; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Expert_AI -- Handles expert AI processing. * + * * + * This routine is called when the computer should perform expert AI processing. This * + * method of AI is categorized as an "Expert System" process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: This is relatively time consuming -- call periodically. * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Expert_AI(void) +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = 0; + bool stop = false; + int time = TICKS_PER_SECOND * 10; + + /* + ** If the current enemy no longer has a base or is defeated, then don't consider + ** that house a threat anymore. Clear out the enemy record and then try + ** to find a new enemy. + */ + if (Enemy != HOUSE_NONE) { + HouseClass * h = HouseClass::As_Pointer(Enemy); + + if (h == NULL || !h->IsActive || h->IsDefeated || Is_Ally(h) || h->BScan == 0) { + Enemy = HOUSE_NONE; + } + } + + /* + ** If there is no enemy assigned to this house, then assign one now. The + ** enemy that is closest is picked. However, don't pick an enemy if the + ** base has not been established yet. + */ + if (ActiveBScan && Center && Attack == 0) { + int close = 0; + HousesType enemy = HOUSE_NONE; + int maxunit = 0; + int maxinfantry = 0; + int maxvessel = 0; + int maxaircraft = 0; + int maxbuilding = 0; + int enemycount = 0; + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && h->IsActive && !h->IsDefeated && !Is_Ally(h)) { + + /* + ** Perform a special restriction check to ensure that no enemy is chosen if + ** there is even one enemy that has not established a base yet. This will + ** ensure an accurate first pick for enemy since the distance to base + ** value can be determined. + */ + if (!h->IsStarted) { + enemy = HOUSE_NONE; + break; + } + + /* + ** Keep track of the number of buildings and units owned by the + ** enemy. This is used to bring up the maximum allowed to match. + */ + maxunit += h->CurUnits; + maxbuilding += h->CurBuildings; + maxinfantry += h->CurInfantry; + maxvessel += h->CurVessels; + maxaircraft += h->CurAircraft; + enemycount++; + + /* + ** Determine a priority value based on distance to the center of the + ** candidate base. The higher the value, the better the candidate house + ** is to becoming the preferred enemy for this house. + */ + int value = ((MAP_CELL_W*2)-Distance(Center, h->Center)); + value *= 2; + + /* + ** In addition to distance, record the number of kills directed + ** against this house. The enemy that does more damage might be + ** considered a greater threat. + */ + value += h->BuildingsKilled[Class->House]*5; + value += h->UnitsKilled[Class->House]; + + /* + ** Factor in the relative sizes of the bases. An enemy that has a + ** larger base will be considered a bigger threat. Conversely, a + ** smaller base is considered a lesser threat. + */ + value += h->CurUnits - CurUnits; + value += h->CurBuildings - CurBuildings; + value += (h->CurInfantry - CurInfantry)/4; + + /* + ** Whoever last attacked is given a little more priority as + ** a potential designated enemy. + */ + if (house == LAEnemy) { + value += 100; + } + +#ifdef OBSOLETE + /* + ** Human players are a given preference as the target. + */ + if (h->IsHuman) { + value *= 2; + } +#endif + + /* + ** Compare the calculated value for this candidate house and if it is + ** greater than the previously recorded maximum, record this house as + ** the prime candidate for enemy. + */ + if (value > close) { + enemy = house; + close = value; + } + } + } + + /* + ** Record this closest enemy base as the first enemy to attack. + */ + Enemy = enemy; + + /* + ** Up the maximum allowed units and buildings to match a rough average + ** of what the enemies are allowed. + */ + if (enemycount) { + maxunit /= enemycount; + maxbuilding /= enemycount; + maxinfantry /= enemycount; + maxvessel /= enemycount; + maxaircraft /= enemycount; + } + + if (Control.MaxBuilding < (unsigned)maxbuilding + 10) { + Control.MaxBuilding = maxbuilding + 10; + } + if (Control.MaxUnit < (unsigned)maxunit + 10) { + Control.MaxUnit = maxunit + 10; + } + if (Control.MaxInfantry < (unsigned)maxinfantry + 10) { + Control.MaxInfantry = maxinfantry + 10; + } + if (Control.MaxVessel < (unsigned)maxvessel + 10) { + Control.MaxVessel = maxvessel + 10; + } + if (Control.MaxAircraft < (unsigned)maxaircraft + 10) { + Control.MaxAircraft = maxaircraft + 10; + } + } + + /* + ** House state transition check occurs here. Transitions that occur here are ones + ** that relate to general base condition rather than specific combat events. + ** Typically, this is limited to transitions between normal buildup mode and + ** broke mode. + */ + if (State == STATE_ENDGAME) { + Fire_Sale(); + Do_All_To_Hunt(); + } else { + if (State == STATE_BUILDUP) { + if (Available_Money() < 25) { + State = STATE_BROKE; + } + } + if (State == STATE_BROKE) { + if (Available_Money() >= 25) { + State = STATE_BUILDUP; + } + } + if (State == STATE_ATTACKED && LATime + TICKS_PER_MINUTE < Frame) { + State = STATE_BUILDUP; + } + if (State != STATE_ATTACKED && LATime + TICKS_PER_MINUTE > Frame) { + State = STATE_ATTACKED; + } + } + + /* + ** Records the urgency of all actions possible. + */ + UrgencyType urgency[STRATEGY_COUNT]; + StrategyType strat; + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + urgency[strat] = URGENCY_NONE; + + switch (strat) { + case STRATEGY_BUILD_POWER: + urgency[strat] = Check_Build_Power(); + break; + + case STRATEGY_BUILD_DEFENSE: + urgency[strat] = Check_Build_Defense(); + break; + + case STRATEGY_BUILD_INCOME: + urgency[strat] = Check_Build_Income(); + break; + + case STRATEGY_FIRE_SALE: + urgency[strat] = Check_Fire_Sale(); + break; + + case STRATEGY_BUILD_ENGINEER: + urgency[strat] = Check_Build_Engineer(); + break; + + case STRATEGY_BUILD_OFFENSE: + urgency[strat] = Check_Build_Offense(); + break; + + case STRATEGY_RAISE_MONEY: + urgency[strat] = Check_Raise_Money(); + break; + + case STRATEGY_RAISE_POWER: + urgency[strat] = Check_Raise_Power(); + break; + + case STRATEGY_LOWER_POWER: + urgency[strat] = Check_Lower_Power(); + break; + + case STRATEGY_ATTACK: + urgency[strat] = Check_Attack(); + break; + + default: + urgency[strat] = URGENCY_NONE; + break; + } + } + + /* + ** Performs the action required for each of the strategies that share + ** the most urgent category. Stop processing if any strategy at the + ** highest urgency performed any action. This is because higher urgency + ** actions tend to greatly affect the lower urgency actions. + */ + for (UrgencyType u = URGENCY_CRITICAL; u >= URGENCY_LOW; u--) { + bool acted = false; + + for (strat = STRATEGY_FIRST; strat < STRATEGY_COUNT; strat++) { + if (urgency[strat] == u) { + switch (strat) { + case STRATEGY_BUILD_POWER: + acted |= AI_Build_Power(u); + break; + + case STRATEGY_BUILD_DEFENSE: + acted |= AI_Build_Defense(u); + break; + + case STRATEGY_BUILD_INCOME: + acted |= AI_Build_Income(u); + break; + + case STRATEGY_FIRE_SALE: + acted |= AI_Fire_Sale(u); + break; + + case STRATEGY_BUILD_ENGINEER: + acted |= AI_Build_Engineer(u); + break; + + case STRATEGY_BUILD_OFFENSE: + acted |= AI_Build_Offense(u); + break; + + case STRATEGY_RAISE_MONEY: + acted |= AI_Raise_Money(u); + break; + + case STRATEGY_RAISE_POWER: + acted |= AI_Raise_Power(u); + break; + + case STRATEGY_LOWER_POWER: + acted |= AI_Lower_Power(u); + break; + + case STRATEGY_ATTACK: + acted |= AI_Attack(u); + break; + + default: + break; + } + } + } + } + + return(TICKS_PER_SECOND*5 + Random_Pick(1, TICKS_PER_SECOND/2)); +} + + +UrgencyType HouseClass::Check_Build_Power(void) const +{ + assert(Houses.ID(this) == ID); + + fixed frac = Power_Fraction(); + UrgencyType urgency = URGENCY_NONE; + + if (frac < 1 && Can_Make_Money()) { + urgency = URGENCY_LOW; + + /* + ** Very low power condition is considered a higher priority. + */ + if (frac < fixed::_3_4) urgency = URGENCY_MEDIUM; + + /* + ** When under attack and there is a need for power in defense, + ** then consider power building a higher priority. + */ + if (State == STATE_THREATENED || State == STATE_ATTACKED) { + if (BScan | (STRUCTF_CHRONOSPHERE)) { + urgency = URGENCY_HIGH; + } + } + + } + return(urgency); +} + + +UrgencyType HouseClass::Check_Build_Defense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that base defense + ** should be given. The more vulnerable the base is, the higher + ** the urgency this routine should return. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Offense(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine determines what urgency level that offensive + ** weaponry should be given. Surplus money or a very strong + ** defense will cause the offensive urgency to increase. + */ + return(URGENCY_NONE); +} + +/* +** Determines what the attack state of the base is. The higher the state, +** the greater the immediate threat to base defense is. +*/ +UrgencyType HouseClass::Check_Attack(void) const +{ + assert(Houses.ID(this) == ID); + + if (Frame > TICKS_PER_MINUTE && Attack == 0) { + if (State == STATE_ATTACKED) { + return(URGENCY_LOW); + } + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Income(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should determine if income processing buildings + ** should be constructed and at what urgency. The lower the money, + ** the lower the refineries, or recent harvester losses should + ** cause a greater urgency to be returned. + */ + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Fire_Sale(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** If there are no more factories at all, then sell everything off because the game + ** is basically over at this point. + */ + if (State != STATE_ATTACKED && CurBuildings && !(ActiveBScan & (STRUCTF_TENT|STRUCTF_BARRACKS|STRUCTF_CONST|STRUCTF_AIRSTRIP|STRUCTF_WEAP|STRUCTF_HELIPAD))) { + return(URGENCY_CRITICAL); + } + return(URGENCY_NONE); +} + + +UrgencyType HouseClass::Check_Build_Engineer(void) const +{ + assert(Houses.ID(this) == ID); + + /* + ** This routine should check to see what urgency that the production of + ** engineers should be. If a friendly building has been captured or the + ** enemy has weak defenses, then building an engineer would be a priority. + */ + return(URGENCY_NONE); +} + + +/* +** Checks to see if money is critically low and something must be done +** to immediately raise cash. +*/ +UrgencyType HouseClass::Check_Raise_Money(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + if (Available_Money() < 100) { + urgency = URGENCY_LOW; + } + if (Available_Money() < 2000 && !Can_Make_Money()) { + urgency++; + } + + return(urgency); +} + +/* +** Checks to see if power is very low and if so, a greater urgency to +** build more power is returned. +*/ +UrgencyType HouseClass::Check_Lower_Power(void) const +{ + assert(Houses.ID(this) == ID); + + if (Power > Drain+300) { + return(URGENCY_LOW); + } + return(URGENCY_NONE); +} + +/* +** This routine determines if there is a power emergency. Such an +** emergency might require selling of structures in order to free +** up power. This might occur if the base is being attacked and there +** are defenses that require power, but are just short of having +** enough. +*/ +UrgencyType HouseClass::Check_Raise_Power(void) const +{ + assert(Houses.ID(this) == ID); + + UrgencyType urgency = URGENCY_NONE; + + if (Power_Fraction() < Rule.PowerEmergencyFraction && Power < Drain - 400) { +// if (Power_Fraction() < Rule.PowerEmergencyFraction && (BQuantity[STRUCT_CONST] == 0 || Available_Money() < 200 || Power < Drain-400)) { + urgency = URGENCY_MEDIUM; + if (State == STATE_ATTACKED) { + urgency++; + } + } + return(urgency); +} + + +bool HouseClass::AI_Attack(UrgencyType ) +{ + assert(Houses.ID(this) == ID); + + bool shuffle = !((Frame > TICKS_PER_MINUTE && !CurBuildings) || Percent_Chance(33)); + bool forced = (CurBuildings == 0); + int index; + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * a = Aircraft.Ptr(index); + + if (a != NULL && !a->IsInLimbo && a->House == this && a->Strength > 0) { + if (!shuffle && a->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + a->Assign_Mission(MISSION_HUNT); + } + } + } + for (index = 0; index < Units.Count(); index++) { + UnitClass * u = Units.Ptr(index); + + if (u != NULL && !u->IsInLimbo && u->House == this && u->Strength > 0) { + if (!shuffle && u->Is_Weapon_Equipped() && (forced || Percent_Chance(75))) { + u->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this unit is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && u->Mission == MISSION_GUARD_AREA && Which_Zone(u) != ZONE_NONE) { + u->ArchiveTarget = ::As_Target(Where_To_Go(u)); + } + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * i = Infantry.Ptr(index); + + if (i != NULL && !i->IsInLimbo && i->House == this && i->Strength > 0) { + if (!shuffle && (i->Is_Weapon_Equipped() || *i == INFANTRY_RENOVATOR) && (forced || Percent_Chance(75))) { + i->Assign_Mission(MISSION_HUNT); + } else { + + /* + ** If this soldier is guarding the base, then cause it to shuffle + ** location instead. + */ + if (Percent_Chance(20) && i->Mission == MISSION_GUARD_AREA && Which_Zone(i) != ZONE_NONE) { + i->ArchiveTarget = ::As_Target(Where_To_Go(i)); + } + } + } + } + Attack = Rule.AttackInterval * Random_Pick(TICKS_PER_MINUTE/2, TICKS_PER_MINUTE*2); + return(true); +} + + +/* +** Given the specified urgency, build a power structure to meet +** this need. +*/ +bool HouseClass::AI_Build_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +/* +** Given the specified urgency, build base defensive structures +** according to need and according to existing base disposition. +*/ +bool HouseClass::AI_Build_Defense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build offensive units according +** to need and according to the opponents base defenses. +*/ +bool HouseClass::AI_Build_Offense(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, build income producing +** structures according to need. +*/ +bool HouseClass::AI_Build_Income(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + + +bool HouseClass::AI_Fire_Sale(UrgencyType urgency) +{ + assert(Houses.ID(this) == ID); + + if (CurBuildings && urgency == URGENCY_CRITICAL) { + Fire_Sale(); + Do_All_To_Hunt(); + return(true); + } + return(false); +} + +/* +** Given the specified urgency, build an engineer. +*/ +bool HouseClass::AI_Build_Engineer(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + return(false); +} + +/* +** Given the specified urgency, sell of some power since +** there appears to be excess. +*/ +bool HouseClass::AI_Lower_Power(UrgencyType ) const +{ + assert(Houses.ID(this) == ID); + + BuildingClass * b = Find_Building(STRUCT_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + + b = Find_Building(STRUCT_ADVANCED_POWER); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Power -- Try to raise power levels by selling off buildings. * + * * + * This routine is called when the computer needs to raise power by selling off buildings. * + * Usually this occurs because of some catastrophe that has lowered power levels to * + * the danger zone. * + * * + * INPUT: urgency -- The urgency that the power needs to be raised. This controls what * + * buildings will be sold. * + * * + * OUTPUT: bool; Was a building sold to raise power? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Power(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_IRON_CURTAIN, URGENCY_MEDIUM}, + {STRUCT_RADAR, URGENCY_MEDIUM}, + {STRUCT_REPAIR, URGENCY_MEDIUM}, + {STRUCT_TESLA, URGENCY_HIGH} + }; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + BuildingClass * b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::AI_Raise_Money -- Raise emergency cash by selling buildings. * + * * + * This routine handles the situation where the computer desperately needs cash but cannot * + * wait for normal harvesting to raise it. Buildings must be sold. * + * * + * INPUT: urgency -- The urgency level that cash must be raised. The greater the urgency, * + * the more important the buildings that can be sold become. * + * * + * OUTPUT: bool; Was a building sold to raise cash? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::AI_Raise_Money(UrgencyType urgency) const +{ + assert(Houses.ID(this) == ID); + + /* + ** Sell off structures in this order. + */ + static struct { + StructType Structure; + UrgencyType Urgency; + } _types[] = { + {STRUCT_CHRONOSPHERE, URGENCY_LOW}, + {STRUCT_SHIP_YARD, URGENCY_LOW}, + {STRUCT_SUB_PEN, URGENCY_LOW}, + {STRUCT_ADVANCED_TECH, URGENCY_LOW}, + {STRUCT_FORWARD_COM, URGENCY_LOW}, + {STRUCT_SOVIET_TECH, URGENCY_LOW}, + {STRUCT_STORAGE,URGENCY_LOW}, + {STRUCT_REPAIR,URGENCY_LOW}, + {STRUCT_TESLA,URGENCY_MEDIUM}, + {STRUCT_HELIPAD,URGENCY_MEDIUM}, + {STRUCT_POWER,URGENCY_HIGH}, + {STRUCT_AIRSTRIP,URGENCY_HIGH}, +// {STRUCT_WEAP,URGENCY_HIGH}, +// {STRUCT_BARRACKS,URGENCY_HIGH}, +// {STRUCT_TENT,URGENCY_HIGH}, + {STRUCT_CONST,URGENCY_CRITICAL} + }; + BuildingClass * b = 0; + + /* + ** Find a structure to sell and then sell it. Bail from further scanning until + ** the next time. + */ + for (int i = 0; i < ARRAY_SIZE(_types); i++) { + if (urgency >= _types[i].Urgency) { + b = Find_Building(_types[i].Structure); + if (b != NULL) { + b->Sell_Back(1); + return(true); + } + } + } + return(false); +} + + +#ifdef NEVER + +/*********************************************************************************************** + * HouseClass::AI_Base_Defense -- Handles maintaining a strong base defense. * + * * + * This logic is used to maintain a base defense. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Base_Defense(void) +{ + assert(Houses.ID(this) == ID); + + /* + ** Check to find if any zone of the base is over defended. Such zones should have + ** some of their defenses sold off to make better use of the money. + */ + + /* + ** Make sure that the core defense is only about 1/2 of the perimeter defense average. + */ + int average = 0; + for (ZoneType z = ZONE_NORTH; z < ZONE_COUNT; z++) { + average += ZoneInfo[z].AirDefense; + average += ZoneInfo[z].ArmorDefense; + average += ZoneInfo[z].InfantryDefense; + } + average /= (ZONE_COUNT-ZONE_NORTH); + + /* + ** If the core value is greater than the average, then sell off some of the + ** inner defensive structures. + */ + int core = ZoneInfo[ZONE_CORE].AirDefense + ZoneInfo[ZONE_CORE].ArmorDefense + ZoneInfo[ZONE_CORE].InfantryDefense; + if (core >= average) { + static StructType _stype[] = { + STRUCT_GTOWER, + STRUCT_TURRET, + STRUCT_ATOWER, + STRUCT_OBELISK, + STRUCT_TESLA, + STRUCT_SAM + }; + BuildingClass * b; + + for (int index = 0; index < sizeof(_stype)/sizeof(_stype[0]); index++) { + b = Find_Building(_stype[index], ZONE_CORE); + if (b) { + b->Sell_Back(1); + break; + } + } + } + + /* + ** If the enemy doesn't have any offensive air capability, then sell off any + ** SAM sites. Only do this when money is moderately low. + */ + if (Available_Money() < 1000 && (ActiveBScan & STRUCTF_SAM)) { + + /* + ** Scan to find if ANY human opponents have aircraft or a helipad. If one + ** is found then consider that opponent to have a valid air threat potential. + ** Don't sell off SAM sites in that case. + */ + bool nothreat = true; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * house = HouseClass::As_Pointer(h); + + if (house && house->IsActive && house->IsHuman && !Is_Ally(house)) { + if ((house->ActiveAScan & (AIRCRAFTF_ORCA|AIRCRAFTF_TRANSPORT|AIRCRAFTF_HELICOPTER)) || (house->ActiveBScan & STRUCTF_HELIPAD)) { + nothreat = false; + break; + } + } + } + } + + return(TICKS_PER_SECOND*5); +} +#endif + + +/*********************************************************************************************** + * HouseClass::AI_Building -- Determines what building to build. * + * * + * This routine handles the general case of determining what building to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + * 11/03/1996 JLB : Tries to match aircraft of enemy * + *=============================================================================================*/ +int HouseClass::AI_Building(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildStructure != STRUCT_NONE) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL && Base.House == Class->House) { + BaseNodeClass * node = Base.Next_Buildable(); + if (node) { + BuildStructure = node->Type; + } + } + + if (IsBaseBuilding) { + /* + ** Don't suggest anything to build if the base is already big enough. + */ + unsigned int quant = 0; + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass const * hptr = HouseClass::As_Pointer(h); + + if (hptr != NULL && hptr->IsActive && hptr->IsHuman && quant < hptr->CurBuildings) { + quant = hptr->CurBuildings; + } + } + quant += Rule.BaseSizeAdd; + +// TCTC -- Should multiply largest player base by some rational number. +// if (CurBuildings >= quant) return(TICKS_PER_SECOND); + + BuildChoice.Free_All(); + BuildChoiceClass * choiceptr; + StructType stype = STRUCT_NONE; + int money = Available_Money(); + int level = Control.TechLevel; + bool hasincome = (BQuantity[STRUCT_REFINERY] > 0 && !IsTiberiumShort && UQuantity[UNIT_HARVESTER] > 0); + BuildingTypeClass const * b = NULL; + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + level = Control.TechLevel; + + /* + ** Try to build a power plant if there is insufficient power and there is enough + ** money available. + */ + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_POWER); + if (Can_Build(b, ActLike) && Power <= Drain+Rule.PowerSurplus && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a refinery if there isn't one already available. + */ + unsigned int current = BQuantity[STRUCT_REFINERY]; + if (!IsTiberiumShort && current < Round_Up(Rule.RefineryRatio*fixed(CurBuildings)) && current < (unsigned)Rule.RefineryLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_REFINERY); + if (Can_Build(b, ActLike) && (money > b->Cost_Of() || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(BQuantity[STRUCT_REFINERY] == 0 ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always make sure there is a barracks available, but only if there + ** will be sufficient money to train troopers. + */ + current = BQuantity[STRUCT_BARRACKS] + BQuantity[STRUCT_TENT]; + if (current < Round_Up(Rule.BarracksRatio*fixed(CurBuildings)) && current < (unsigned)Rule.BarracksLimit && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_BARRACKS); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TENT); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** Try to build one dog house. + */ + current = BQuantity[STRUCT_KENNEL]; + if (current < 1 && (money > 300 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_KENNEL); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Try to build one gap generator. + */ + current = BQuantity[STRUCT_GAP]; + if (current < 1 && Power_Fraction() >= 1 && hasincome) { + b = &BuildingTypeClass::As_Reference(STRUCT_GAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** A source of combat vehicles is always needed, but only if there will + ** be sufficient money to build vehicles. + */ + current = BQuantity[STRUCT_WEAP]; + if (current < Round_Up(Rule.WarRatio*fixed(CurBuildings)) && current < (unsigned)Rule.WarLimit && (money > 2000 || hasincome)) { + b = &BuildingTypeClass::As_Reference(STRUCT_WEAP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(current > 0 ? URGENCY_LOW : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Always build up some base defense. + */ + current = BQuantity[STRUCT_PILLBOX] + BQuantity[STRUCT_CAMOPILLBOX] + BQuantity[STRUCT_TURRET] + BQuantity[STRUCT_FLAME_TURRET]; + if (current < Round_Up(Rule.DefenseRatio*fixed(CurBuildings)) && current < (unsigned)Rule.DefenseLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_FLAME_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + if (Percent_Chance(50)) { + b = &BuildingTypeClass::As_Reference(STRUCT_PILLBOX); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_TURRET); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Build some air defense. + */ + current = BQuantity[STRUCT_SAM] + BQuantity[STRUCT_AAGUN]; + if (current < Round_Up(Rule.AARatio*fixed(CurBuildings)) && current < (unsigned)Rule.AALimit) { + + /* + ** Building air defense only makes sense if the opponent has aircraft + ** of some kind. + */ + bool airthreat = false; + int threat_quantity = 0; + if (enemy != NULL && enemy->AScan != 0) { + airthreat = true; + threat_quantity = enemy->CurAircraft; + } + if (!airthreat) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + if (h != NULL && !Is_Ally(house) && h->AScan != 0) { + airthreat = true; + break; + } + } + } + + if (airthreat) { + + if (BQuantity[STRUCT_RADAR] == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_RADAR); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_HIGH, b->Type); + } + } + } + + b = &BuildingTypeClass::As_Reference(STRUCT_SAM); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_AAGUN); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass((current < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + } + } + + /* + ** Advanced base defense would be good. + */ + current = BQuantity[STRUCT_TESLA]; + if (current < Round_Up(Rule.TeslaRatio*fixed(CurBuildings)) && current < (unsigned)Rule.TeslaLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_TESLA); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** Build a tech center as soon as possible. + */ + current = BQuantity[STRUCT_ADVANCED_TECH] + BQuantity[STRUCT_SOVIET_TECH]; + if (current < 1) { + b = &BuildingTypeClass::As_Reference(STRUCT_ADVANCED_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } else { + b = &BuildingTypeClass::As_Reference(STRUCT_SOVIET_TECH); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome) && Power_Fraction() >= 1) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } + } + + /* + ** A helipad would be good. + */ + current = BQuantity[STRUCT_HELIPAD]; + if (current < Round_Up(Rule.HelipadRatio*fixed(CurBuildings)) && current < (unsigned)Rule.HelipadLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_HELIPAD); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + + /* + ** An airstrip would be good. + */ + current = BQuantity[STRUCT_AIRSTRIP]; + if (current < Round_Up(Rule.AirstripRatio*fixed(CurBuildings)) && current < (unsigned)Rule.AirstripLimit) { + b = &BuildingTypeClass::As_Reference(STRUCT_AIRSTRIP); + if (Can_Build(b, ActLike) && (b->Cost_Of() < money || hasincome)) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr != NULL) { + int threat_quantity = 0; + if (enemy != NULL) { + threat_quantity = enemy->CurAircraft; + } + + *choiceptr = BuildChoiceClass((CurAircraft < (unsigned)threat_quantity) ? URGENCY_HIGH : URGENCY_MEDIUM, b->Type); + } + } + } + +#ifdef OLD + /* + ** Build a repair bay if there isn't one already available. + */ + current = BQuantity[STRUCT_REPAIR]; + if (current == 0) { + b = &BuildingTypeClass::As_Reference(STRUCT_REPAIR); + if (Can_Build(b, ActLike) && b->Cost_Of() < money) { + choiceptr = BuildChoice.Alloc(); + if (choiceptr) { + *choiceptr = BuildChoiceClass(URGENCY_MEDIUM, b->Type); + } + } + } +#endif + + /* + ** Pick the choice that is the most urgent. + */ + UrgencyType best = URGENCY_NONE; + int bestindex; + for (int index = 0; index < BuildChoice.Count(); index++) { + if (BuildChoice.Ptr(index)->Urgency > best) { + bestindex = index; + best = BuildChoice.Ptr(index)->Urgency; + } + } + if (best != URGENCY_NONE) { + BuildStructure = BuildChoice.Ptr(bestindex)->Structure; + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Unit -- Determines what unit to build next. * + * * + * This routine handles the general case of determining what units to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of games frames to delay before calling this routine again.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Unit(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildUnit != UNIT_NONE) return(TICKS_PER_SECOND); + if (CurUnits >= Control.MaxUnit) return(TICKS_PER_SECOND); + + /* + ** A computer controlled house will try to build a replacement + ** harvester if possible. + */ + if (IQ >= Rule.IQHarvester && !IsTiberiumShort && !IsHuman && BQuantity[STRUCT_REFINERY] > UQuantity[UNIT_HARVESTER] && Difficulty != DIFF_HARD) { + if (UnitTypeClass::As_Reference(UNIT_HARVESTER).Level <= (unsigned)Control.TechLevel) { + BuildUnit = UNIT_HARVESTER; + return(TICKS_PER_SECOND); + } + } + + if (Session.Type == GAME_NORMAL) { + + int counter[UNIT_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + counter[((UnitTypeClass const *)memtype)->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL && team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + TechnoTypeClass const * memtype = team->Members[subindex].Class; + + if (memtype->What_Am_I() == RTTI_UNITTYPE) { + int subtype = ((UnitTypeClass const *)memtype)->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Units.Count(); uindex++) { + UnitClass * unit = Units.Ptr(uindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those objects that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + UnitType bestlist[UNIT_COUNT]; + for (UnitType utype = UNIT_FIRST; utype < UNIT_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&UnitTypeClass::As_Reference(utype), Class->House) && UnitTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildUnit = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + + int counter[UNIT_COUNT]; + int total = 0; + UnitType index; + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + UnitTypeClass const * utype = &UnitTypeClass::As_Reference(index); + if (Can_Build(utype, ActLike) && utype->Type != UNIT_HARVESTER) { + if (utype->PrimaryWeapon != NULL) { + counter[index] = 20; + } else { + counter[index] = 1; + } + } else { + counter[index] = 0; + } + total += counter[index]; + } + + if (total > 0) { + int choice = Random_Pick(0, total-1); + for (index = UNIT_FIRST; index < UNIT_COUNT; index++) { + if (choice < counter[index]) { + BuildUnit = index; + break; + } + choice -= counter[index]; + } + } + } + + return(TICKS_PER_SECOND); +} + + +int HouseClass::AI_Vessel(void) +{ + assert(Houses.ID(this) == ID); + if (BuildVessel != VESSEL_NONE) return(TICKS_PER_SECOND); + + if (CurVessels >= Control.MaxVessel) { + return(TICKS_PER_SECOND); + } + + if (Session.Type == GAME_NORMAL) { + + int counter[VESSEL_COUNT]; + if (Session.Type == GAME_NORMAL) { + memset(counter, 0x00, sizeof(counter)); + } else { + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (Can_Build(&VesselTypeClass::As_Reference(index), Class->House) && VesselTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + counter[index] = 16; + } else { + counter[index] = 0; + } + } + } + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + counter[((VesselTypeClass const *)(team->Members[subindex].Class))->Type] = 1; + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_VESSELTYPE) { + int subtype = ((VesselTypeClass const *)(team->Members[subindex].Class))->Type; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int vindex = 0; vindex < Vessels.Count(); vindex++) { + VesselClass * unit = Vessels.Ptr(vindex); + if (unit != NULL && unit->Is_Recruitable(this) && counter[unit->Class->Type] > 0) { + counter[unit->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + VesselType bestlist[VESSEL_COUNT]; + for (VesselType utype = VESSEL_FIRST; utype < VESSEL_COUNT; utype++) { + if (counter[utype] > 0 && Can_Build(&VesselTypeClass::As_Reference(utype), Class->House) && VesselTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + + /* + ** The unit type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + BuildVessel = bestlist[Random_Pick(0, bestcount-1)]; + } + } + + if (IsBaseBuilding) { + BuildVessel = VESSEL_NONE; + } + + return(TICKS_PER_SECOND); +} + + + +/*********************************************************************************************** + * HouseClass::AI_Infantry -- Determines the infantry unit to build. * + * * + * This routine handles the general case of determining what infantry unit to build * + * next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before being called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Infantry(void) +{ + assert(Houses.ID(this) == ID); + + if (BuildInfantry != INFANTRY_NONE) return(TICKS_PER_SECOND); + if (CurInfantry >= Control.MaxInfantry) return(TICKS_PER_SECOND); + + if (Session.Type == GAME_NORMAL) { + TechnoTypeClass const * techno = 0; + int counter[INFANTRY_COUNT]; + memset(counter, 0x00, sizeof(counter)); + + /* + ** Build a list of the maximum of each type we wish to produce. This will be + ** twice the number required to fill all teams. + */ + int index; + for (index = 0; index < Teams.Count(); index++) { + TeamClass * tptr = Teams.Ptr(index); + if (tptr != NULL) { + TeamTypeClass const * team = tptr->Class; + + if (((team->IsReinforcable && !tptr->IsFullStrength) || (!tptr->IsForcedActive && !tptr->IsHasBeen && !tptr->JustAltered)) && team->House == Class->House) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + counter[((InfantryTypeClass const *)(team->Members[subindex].Class))->Type] += team->Members[subindex].Quantity + (team->IsReinforcable ? 1 : 0); + } + } + } + } + } + + /* + ** Team types that are flagged as prebuilt, will always try to produce enough + ** to fill one team of this type regardless of whether there is a team active + ** of that type. + */ + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * team = TeamTypes.Ptr(index); + if (team != NULL) { + if (team->House == Class->House && team->IsPrebuilt && (!team->IsAutocreate || IsAlerted)) { + for (int subindex = 0; subindex < team->ClassCount; subindex++) { + if (team->Members[subindex].Class->What_Am_I() == RTTI_INFANTRYTYPE) { + int subtype = ((InfantryTypeClass const *)(team->Members[subindex].Class))->Type; +// counter[subtype] = 1; + counter[subtype] = max(counter[subtype], team->Members[subindex].Quantity); + counter[subtype] = min(counter[subtype], 5); + } + } + } + } + } + + /* + ** Reduce the theoretical maximum by the actual number of objects currently + ** in play. + */ + for (int uindex = 0; uindex < Infantry.Count(); uindex++) { + InfantryClass * infantry = Infantry.Ptr(uindex); + if (infantry != NULL && infantry->Is_Recruitable(this) && counter[infantry->Class->Type] > 0) { + counter[infantry->Class->Type]--; + } + } + + /* + ** Pick to build the most needed object but don't consider those object that + ** can't be built because of scenario restrictions or insufficient cash. + */ + int bestval = -1; + int bestcount = 0; + InfantryType bestlist[INFANTRY_COUNT]; + for (InfantryType utype = INFANTRY_FIRST; utype < INFANTRY_COUNT; utype++) { + + if (utype != INFANTRY_DOG || !(IScan & INFANTRYF_DOG)) { + if (counter[utype] > 0 && Can_Build(&InfantryTypeClass::As_Reference(utype), Class->House) && InfantryTypeClass::As_Reference(utype).Cost_Of() <= Available_Money()) { + if (bestval == -1 || bestval < counter[utype]) { + bestval = counter[utype]; + bestcount = 0; + } + bestlist[bestcount++] = utype; + } + } + } + + /* + ** The infantry type to build is now known. Fetch a pointer to the techno type class. + */ + if (bestcount) { + int pick = Random_Pick(0, bestcount-1); + BuildInfantry = bestlist[pick]; + } + + } + + if (IsBaseBuilding) { + HouseClass const * enemy = NULL; + if (Enemy != HOUSE_NONE) { + enemy = HouseClass::As_Pointer(Enemy); + } + + /* + ** This structure is used to keep track of the list of infantry types that should be + ** built. The infantry type and the value assigned to it is recorded. + */ + struct { + InfantryType Type; // Infantry type. + int Value; // Relative value assigned. + } typetrack[INFANTRY_COUNT]; + int count = 0; + int total = 0; + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + if (Can_Build(&InfantryTypeClass::As_Reference(index), ActLike) && InfantryTypeClass::As_Reference(index).Level <= (unsigned)Control.TechLevel) { + typetrack[count].Value = 0; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 This looks like a potential bug. It is prob. for save game format compatibility. + int clipindex = index; + if (clipindex >= INFANTRY_RA_COUNT) clipindex -= INFANTRY_RA_COUNT; + if ((enemy != NULL && enemy->IQuantity[clipindex] > IQuantity[clipindex]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#else + if ((enemy != NULL && enemy->IQuantity[index] > IQuantity[index]) || Available_Money() > Rule.InfantryReserve || CurInfantry < CurBuildings * Rule.InfantryBaseMult) { +#endif + + switch (index) { + case INFANTRY_E1: + typetrack[count].Value = 3; + break; + + case INFANTRY_E2: + typetrack[count].Value = 5; + break; + + case INFANTRY_E3: + typetrack[count].Value = 2; + break; + + case INFANTRY_E4: + typetrack[count].Value = 5; + break; + + case INFANTRY_RENOVATOR: + if (CurInfantry > 5) { + typetrack[count].Value = 1 - max(IQuantity[index], 0); + } + break; + + case INFANTRY_TANYA: + typetrack[count].Value = 1 - max(IQuantity[index], 0); + break; + + default: + typetrack[count].Value = 0; + break; + } + } + + if (typetrack[count].Value > 0) { + typetrack[count].Type = index; + total += typetrack[count].Value; + count++; + } + } + } + + /* + ** If there is at least one choice, then pick it. The object picked + ** is influenced by the weight (value) assigned to it. This is accomplished + ** by picking a number between 0 and the total weight value. The appropriate + ** infantry object that matches the number picked is then selected to be built. + */ + if (count > 0) { + int pick = Random_Pick(0, total-1); + for (int index = 0; index < count; index++) { + if (pick < typetrack[index].Value) { + BuildInfantry = typetrack[index].Type; + break; + } + pick -= typetrack[index].Value; + } + } + } + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::AI_Aircraft -- Determines what aircraft to build next. * + * * + * This routine is used to determine the general case of what aircraft to build next. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of frame to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +int HouseClass::AI_Aircraft(void) +{ + assert(Houses.ID(this) == ID); + + if (!IsHuman && IQ >= Rule.IQAircraft) { + if (BuildAircraft != AIRCRAFT_NONE) return(TICKS_PER_SECOND); + if (CurAircraft >= Control.MaxAircraft) return(TICKS_PER_SECOND); + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_LONGBOW).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_LONGBOW; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_HIND), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_HIND).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_HELIPAD] > AQuantity[AIRCRAFT_LONGBOW] + AQuantity[AIRCRAFT_HIND]) { + BuildAircraft = AIRCRAFT_HIND; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_MIG), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_MIG).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_MIG; + return(TICKS_PER_SECOND); + } + + if (Can_Build(&AircraftTypeClass::As_Reference(AIRCRAFT_YAK), ActLike) && + AircraftTypeClass::As_Reference(AIRCRAFT_YAK).Level <= (unsigned)Control.TechLevel && + BQuantity[STRUCT_AIRSTRIP] > AQuantity[AIRCRAFT_MIG] + AQuantity[AIRCRAFT_YAK]) { + BuildAircraft = AIRCRAFT_YAK; + return(TICKS_PER_SECOND); + } + } + + return(TICKS_PER_SECOND); +} + + +/*********************************************************************************************** + * HouseClass::Production_Begun -- Records that production has begun. * + * * + * This routine is used to inform the Expert System that production of the specified object * + * has begun. This allows the AI to proceed with picking another object to begin production * + * on. * + * * + * INPUT: product -- Pointer to the object that production has just begun on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Production_Begun(TechnoClass const * product) +{ + assert(Houses.ID(this) == ID); + + if (product != NULL) { + switch (product->What_Am_I()) { + case RTTI_UNIT: + if (*((UnitClass*)product) == BuildUnit) { + BuildUnit = UNIT_NONE; + } + break; + + case RTTI_VESSEL: + if (*((VesselClass*)product) == BuildVessel) { + BuildVessel = VESSEL_NONE; + } + break; + + case RTTI_INFANTRY: + if (*((InfantryClass*)product) == BuildInfantry) { + BuildInfantry = INFANTRY_NONE; + } + break; + + case RTTI_BUILDING: + if (*((BuildingClass*)product) == BuildStructure) { + BuildStructure = STRUCT_NONE; + } + break; + + case RTTI_AIRCRAFT: + if (*((AircraftClass*)product) == BuildAircraft) { + BuildAircraft = AIRCRAFT_NONE; + } + break; + + default: + break; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Remove -- Remove object from house tracking system. * + * * + * This routine informs the Expert System that the specified object is no longer part of * + * this house's inventory. This occurs when the object is destroyed or captured. * + * * + * INPUT: techno -- Pointer to the object to remove from the tracking systems of this * + * house. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Remove(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + int type; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings--; + BQuantity[((BuildingTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_AIRCRAFT: + CurAircraft--; + AQuantity[((AircraftTypeClass const &)techno->Class_Of()).Type]--; + break; + + case RTTI_INFANTRY: + CurInfantry--; + if (!((InfantryClass *)techno)->IsTechnician) { + type = ((InfantryTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= INFANTRY_RA_COUNT) type -= INFANTRY_RA_COUNT; +#endif + IQuantity[type]--; + } + break; + + case RTTI_UNIT: + CurUnits--; + type = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= UNIT_RA_COUNT) type -= UNIT_RA_COUNT; +#endif + UQuantity[type]--; + break; + + case RTTI_VESSEL: + CurVessels--; + type = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (type >= VESSEL_RA_COUNT) type -= VESSEL_RA_COUNT; +#endif + VQuantity[type]--; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Tracking_Add -- Informs house of new inventory item. * + * * + * This function is called when the specified object is now available as part of the house's* + * inventory. This occurs when the object is newly produced and also when it is captured * + * by this house. * + * * + * INPUT: techno -- Pointer to the object that is now part of the house inventory. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/29/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Tracking_Add(TechnoClass const * techno) +{ + assert(Houses.ID(this) == ID); + + StructType building; + AircraftType aircraft; + InfantryType infantry; + UnitType unit; + VesselType vessel; + int quant; + + switch (techno->What_Am_I()) { + case RTTI_BUILDING: + CurBuildings++; + building = ((BuildingTypeClass const &)techno->Class_Of()).Type; + BQuantity[building]++; + BScan |= (1L << building); + if (Session.Type == GAME_INTERNET) { + BuildingTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_AIRCRAFT: + CurAircraft++; + aircraft = ((AircraftTypeClass const &)techno->Class_Of()).Type; + AQuantity[aircraft]++; + AScan |= (1L << aircraft); + if (Session.Type == GAME_INTERNET) { + AircraftTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_INFANTRY: + CurInfantry++; + infantry = ((InfantryTypeClass const &)techno->Class_Of()).Type; + if (!((InfantryClass *)techno)->IsTechnician) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = infantry; + if (quant >= INFANTRY_RA_COUNT) quant -= INFANTRY_RA_COUNT; + IQuantity[quant]++; +#else + IQuantity[infantry]++; +#endif + if (!((InfantryTypeClass const &)techno->Class_Of()).IsCivilian && Session.Type == GAME_INTERNET) { + InfantryTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + IScan |= (1L << infantry); + } + break; + + case RTTI_UNIT: + CurUnits++; + unit = ((UnitTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = unit; + if (quant >= UNIT_RA_COUNT) quant -= UNIT_RA_COUNT; + UQuantity[quant]++; +#else + UQuantity[unit]++; +#endif + UScan |= (1L << unit); + if (Session.Type == GAME_INTERNET) { + UnitTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + case RTTI_VESSEL: + CurVessels++; + vessel = ((VesselTypeClass const &)techno->Class_Of()).Type; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + quant = vessel; + if (quant >= VESSEL_RA_COUNT) quant -= VESSEL_RA_COUNT; + VQuantity[quant]++; +#else + VQuantity[vessel]++; +#endif + VScan |= (1L << vessel); + if (Session.Type == GAME_INTERNET) { + VesselTotals->Increment_Unit_Total(techno->Class_Of().ID); + } + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Counter -- Fetches a pointer to the factory counter value. * + * * + * Use this routine to fetch a pointer to the variable that holds the number of factories * + * that can produce the specified object type. This is a helper routine used when * + * examining the number of factories as well as adjusting their number. * + * * + * INPUT: rtti -- The RTTI of the object that could be produced. * + * * + * OUTPUT: Returns with the number of factories owned by this house that could produce the * + * object of the type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int * HouseClass::Factory_Counter(RTTIType rtti) +{ + switch (rtti) { + case RTTI_UNITTYPE: + case RTTI_UNIT: + return(&UnitFactories); + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + return(&VesselFactories); + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + return(&AircraftFactories); + + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + return(&InfantryFactories); + + case RTTI_BUILDINGTYPE: + case RTTI_BUILDING: + return(&BuildingFactories); + + default: + break; + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Active_Remove -- Remove this object from active duty for this house. * + * * + * This routine will recognize the specified object as having been removed from active * + * duty. * + * * + * INPUT: techno -- Pointer to the object to remove from active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Remove(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr - 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Active_Add -- Add an object to active duty for this house. * + * * + * This routine will recognize the specified object as having entered active duty. Any * + * abilities granted to the house by that object are now available. * + * * + * INPUT: techno -- Pointer to the object that is entering active duty. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Active_Add(TechnoClass const * techno) +{ + if (techno == NULL) return; + + if (techno->What_Am_I() == RTTI_BUILDING) { + int * fptr = Factory_Counter(((BuildingClass *)techno)->Class->ToBuild); + if (fptr != NULL) { + *fptr = *fptr + 1; + } + } +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines what zone a coordinate lies in. * + * * + * This routine will determine what zone the specified coordinate lies in with respect to * + * this house's base. A location that is too distant from the base, even though it might * + * be a building, is not considered part of the base and returns ZONE_NONE. * + * * + * INPUT: coord -- The coordinate to examine. * + * * + * OUTPUT: Returns with the base zone that the specified coordinate lies in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + if (coord == 0) return(ZONE_NONE); + + int distance = Distance(Center, coord); + if (distance <= Radius) return(ZONE_CORE); + if (distance > Radius*4) return(ZONE_NONE); + + DirType facing = Direction(Center, coord); + if (facing < DIR_NE || facing > DIR_NW) return(ZONE_NORTH); + if (facing >= DIR_NE && facing < DIR_SE) return(ZONE_EAST); + if (facing >= DIR_SE && facing < DIR_SW) return(ZONE_SOUTH); + return(ZONE_WEST); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified object lies in. * + * * + * Use this routine to determine what zone the specified object lies in. * + * * + * INPUT: object -- Pointer to the object that will be checked for zone occupation. * + * * + * OUTPUT: Returns with the base zone that the object lies in. For objects that are too * + * distant from the center of the base, ZONE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(ObjectClass const * object) const +{ + assert(Houses.ID(this) == ID); + + if (!object) return(ZONE_NONE); + return(Which_Zone(object->Center_Coord())); +} + + +/*********************************************************************************************** + * HouseClass::Which_Zone -- Determines which base zone the specified cell lies in. * + * * + * This routine is used to determine what base zone the specified cell is in. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the base zone that the cell lies in or ZONE_NONE if the cell is too far * + * away. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +ZoneType HouseClass::Which_Zone(CELL cell) const +{ + assert(Houses.ID(this) == ID); + + return(Which_Zone(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * HouseClass::Recalc_Attributes -- Recalcs all houses existence bits. * + * * + * This routine will go through all game objects and reset the existence bits for the * + * owning house. This method ensures that if the object exists, then the corresponding * + * existence bit is also set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Recalc_Attributes(void) +{ + /* + ** Clear out all tracking values that will be filled in by this + ** routine. This allows the filling in process to not worry about + ** old existing values. + */ + int index; + for (index = 0; index < Houses.Count(); index++) { + HouseClass * house = Houses.Ptr(index); + + if (house != NULL) { + house->BScan = 0; + house->ActiveBScan = 0; + house->IScan = 0; + house->ActiveIScan = 0; + house->UScan = 0; + house->ActiveUScan = 0; + house->AScan = 0; + house->ActiveAScan = 0; + house->VScan = 0; + house->ActiveVScan = 0; + } + } + + /* + ** A second pass through the sentient objects is required so that the appropriate scan + ** bits will be set for the owner house. + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + unit->House->UScan |= (1L << unit->Class->Type); + if (unit->IsLocked && (Session.Type != GAME_NORMAL || !unit->House->IsHuman || unit->IsDiscoveredByPlayer)) { + if (!unit->IsInLimbo) { + unit->House->ActiveUScan |= (1L << unit->Class->Type); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + infantry->House->IScan |= (1L << infantry->Class->Type); + if (infantry->IsLocked && (Session.Type != GAME_NORMAL || !infantry->House->IsHuman || infantry->IsDiscoveredByPlayer)) { + if (!infantry->IsInLimbo) { + infantry->House->ActiveIScan |= (1L << infantry->Class->Type); + infantry->House->OldIScan |= (1L << infantry->Class->Type); + } + } + } + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + aircraft->House->AScan |= (1L << aircraft->Class->Type); + if (aircraft->IsLocked && (Session.Type != GAME_NORMAL || !aircraft->House->IsHuman || aircraft->IsDiscoveredByPlayer)) { + if (!aircraft->IsInLimbo) { + aircraft->House->ActiveAScan |= (1L << aircraft->Class->Type); + aircraft->House->OldAScan |= (1L << aircraft->Class->Type); + } + } + } + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (building->Class->Type < 32) { + building->House->BScan |= (1L << building->Class->Type); + if (building->IsLocked && (Session.Type != GAME_NORMAL || !building->House->IsHuman || building->IsDiscoveredByPlayer)) { + if (!building->IsInLimbo) { + building->House->ActiveBScan |= (1L << building->Class->Type); + building->House->OldBScan |= (1L << building->Class->Type); + } + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + vessel->House->VScan |= (1L << vessel->Class->Type); + if (vessel->IsLocked && (Session.Type != GAME_NORMAL || !vessel->House->IsHuman || vessel->IsDiscoveredByPlayer)) { + if (!vessel->IsInLimbo) { + vessel->House->ActiveVScan |= (1L << vessel->Class->Type); + vessel->House->OldVScan |= (1L << vessel->Class->Type); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Zone_Cell -- Finds the cell closest to the center of the zone. * + * * + * This routine is used to find the cell that is closest to the center point of the * + * zone specified. Typical use of this routine is for building and unit placement so that * + * they can "cover" the specified zone. * + * * + * INPUT: zone -- The zone that the center point is to be returned. * + * * + * OUTPUT: Returns with the cell that is closest to the center point of the zone specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Zone_Cell(ZoneType zone) const +{ + assert(Houses.ID(this) == ID); + + switch (zone) { + case ZONE_CORE: + return(Coord_Cell(Center)); + + case ZONE_NORTH: + return(Coord_Cell(Coord_Move(Center, DIR_N, Radius*3))); + + case ZONE_EAST: + return(Coord_Cell(Coord_Move(Center, DIR_E, Radius*3))); + + case ZONE_WEST: + return(Coord_Cell(Coord_Move(Center, DIR_W, Radius*3))); + + case ZONE_SOUTH: + return(Coord_Cell(Coord_Move(Center, DIR_S, Radius*3))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Where_To_Go -- Determines where the object should go and wait. * + * * + * This function is called for every new unit produced or delivered in order to determine * + * where the unit should "hang out" to await further orders. The best area for the * + * unit to loiter is returned as a cell location. * + * * + * INPUT: object -- Pointer to the object that needs to know where to go. * + * * + * OUTPUT: Returns with the cell that the unit should move to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + * 11/04/1996 JLB : Simplified to use helper functions * + *=============================================================================================*/ +CELL HouseClass::Where_To_Go(FootClass const * object) const +{ + assert(Houses.ID(this) == ID); + assert(object != NULL); + + ZoneType zone; // The zone that the object should go to. + if (object->Anti_Air() + object->Anti_Armor() + object->Anti_Infantry() == 0) { + zone = ZONE_CORE; + } else { + zone = Random_Pick(ZONE_NORTH, ZONE_WEST); + } + + CELL cell = Random_Cell_In_Zone(zone); + assert(cell != 0); + + return(Map.Nearby_Location(cell, SPEED_TRACK, Map[cell].Zones[MZONE_NORMAL], MZONE_NORMAL)); +} + + +/*********************************************************************************************** + * HouseClass::Find_Juicy_Target -- Finds a suitable field target. * + * * + * This routine is used to find targets out in the field and away from base defense. * + * Typical of this would be the attack helicopters and the roving attack bands of * + * hunter killers. * + * * + * INPUT: coord -- The coordinate of the attacker. Closer targets are given preference. * + * * + * OUTPUT: Returns with a suitable target to attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/12/1995 JLB : Created. * + *=============================================================================================*/ +TARGET HouseClass::Find_Juicy_Target(COORDINATE coord) const +{ + assert(Houses.ID(this) == ID); + + UnitClass * best = 0; + int value = 0; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && !unit->IsInLimbo && !Is_Ally(unit) && unit->House->Which_Zone(unit) == ZONE_NONE) { + int val = Distance(coord, unit->Center_Coord()); + + if (unit->Anti_Air()) val *= 2; + + if (*unit == UNIT_HARVESTER) val /= 2; + + if (value == 0 || val < value) { + value = val; + best = unit; + } + } + } + if (best) { + return(best->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Fetches the total number of aircraft of the specified type. * + * * + * Call this routine to fetch the total quantity of aircraft of the type specified that is * + * owned by this house. * + * * + * INPUT: aircraft -- The aircraft type to check the quantity of. * + * * + * OUTPUT: Returns with the total quantity of all aircraft of that type that is owned by this * + * house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(AircraftType aircraft) +{ + return(AQuantity[aircraft]); +} + + +/*********************************************************************************************** + * HouseClass::Fetch_Factory -- Finds the factory associated with the object type specified. * + * * + * This is the counterpart to the Set_Factory function. It will return with a factory * + * pointer that is associated with the object type specified. * + * * + * INPUT: rtti -- The RTTI of the object type to find the factory for. * + * * + * OUTPUT: Returns with a pointer to the factory (if present) that can manufacture the * + * object type specified. * + * * + * WARNINGS: If this returns a non-NULL pointer, then the factory is probably already busy * + * producing another unit of that category. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +FactoryClass * HouseClass::Fetch_Factory(RTTIType rtti) const +{ + int factory_index = -1; + + switch (rtti) { + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = InfantryFactory; + break; + + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = UnitFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = AircraftFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = VesselFactory; + break; + + default: + factory_index = -1; + break; + } + + /* + ** Fetch the actual pointer to the factory object. If there is + ** no object factory that matches the specified rtti type, then + ** null is returned. + */ + if (factory_index != -1) { + return(Factories.Raw_Ptr(factory_index)); + } + return(NULL); +} + + +/*********************************************************************************************** + * HouseClass::Set_Factory -- Assign specified factory to house tracking. * + * * + * Call this routine when a factory has been created and it now must be passed on to the * + * house for tracking purposes. The house maintains several factory pointers and this * + * routine will ensure that the factory pointer gets stored correctly. * + * * + * INPUT: rtti -- The RTTI of the object the factory it to manufacture. * + * * + * factory -- The factory object pointer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Set_Factory(RTTIType rtti, FactoryClass * factory) +{ + int * factory_index = 0; + + assert(rtti != RTTI_NONE); + + switch (rtti) { + case RTTI_UNIT: + case RTTI_UNITTYPE: + factory_index = &UnitFactory; + break; + + case RTTI_INFANTRY: + case RTTI_INFANTRYTYPE: + factory_index = &InfantryFactory; + break; + + case RTTI_VESSEL: + case RTTI_VESSELTYPE: + factory_index = &VesselFactory; + break; + + case RTTI_BUILDING: + case RTTI_BUILDINGTYPE: + factory_index = &BuildingFactory; + break; + + case RTTI_AIRCRAFT: + case RTTI_AIRCRAFTTYPE: + factory_index = &AircraftFactory; + break; + } + + assert(factory_index != NULL); + + /* + ** Assign the factory to the appropriate slot. For the case of clearing + ** the factory out, then -1 is assigned. + */ + if (factory != NULL) { + *factory_index = factory->ID; + } else { + *factory_index = -1; + } +} + + +/*********************************************************************************************** + * HouseClass::Factory_Count -- Fetches the number of factories for specified type. * + * * + * This routine will count the number of factories owned by this house that can build * + * objects of the specified type. * + * * + * INPUT: rtti -- The type of object (RTTI) that the factories are to be counted for. * + * * + * OUTPUT: Returns with the number of factories that can build the object type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Factory_Count(RTTIType rtti) const +{ + int const * ptr = ((HouseClass *)this)->Factory_Counter(rtti); + if (ptr != NULL) { + return(*ptr); + } + return(0); +} + + +/*********************************************************************************************** + * HouseClass::Get_Quantity -- Gets the quantity of the building type specified. * + * * + * This will return the total number of buildings of that type owned by this house. * + * * + * INPUT: building -- The building type to check. * + * * + * OUTPUT: Returns with the number of buildings of that type owned by this house. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int HouseClass::Get_Quantity(StructType building) +{ + return(BQuantity[building]); +} + + +/*********************************************************************************************** + * HouseClass::Read_INI -- Reads house specific data from INI. * + * * + * This routine reads the house specific data for a particular * + * scenario from the scenario INI file. Typical data includes starting * + * credits, maximum unit count, etc. * + * * + * INPUT: buffer -- Pointer to loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + * 05/18/1995 JLB : Creates all houses. * + *=============================================================================================*/ +void HouseClass::Read_INI(CCINIClass & ini) +{ + HouseClass * p; // Pointer to current player data. + char const * hname; // Pointer to house name. + + for (HousesType index = HOUSE_FIRST; index < HOUSE_COUNT; index++) { + hname = HouseTypeClass::As_Reference(index).IniName; + + p = new HouseClass(index); + p->Control.TechLevel = ini.Get_Int(hname, "TechLevel", Scen.Scenario); + p->Control.MaxBuilding = ini.Get_Int(hname, "MaxBuilding", p->Control.MaxBuilding); + p->Control.MaxUnit = ini.Get_Int(hname, "MaxUnit", p->Control.MaxUnit); + p->Control.MaxInfantry = ini.Get_Int(hname, "MaxInfantry", p->Control.MaxInfantry); + p->Control.MaxVessel = ini.Get_Int(hname, "MaxVessel", p->Control.MaxVessel); + if (p->Control.MaxVessel == 0) p->Control.MaxVessel = p->Control.MaxUnit; + p->Control.InitialCredits = ini.Get_Int(hname, "Credits", 0) * 100; + p->Credits = p->Control.InitialCredits; + + int iq = ini.Get_Int(hname, "IQ", 0); + if (iq > Rule.MaxIQ) iq = 1; + p->IQ = p->Control.IQ = iq; + + p->Control.Edge = ini.Get_SourceType(hname, "Edge", SOURCE_NORTH); + p->IsPlayerControl = ini.Get_Bool(hname, "PlayerControl", false); + + int owners = ini.Get_Owners(hname, "Allies", (1 << HOUSE_NEUTRAL)); + p->Make_Ally(index); + p->Make_Ally(HOUSE_NEUTRAL); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + if ((owners & (1 << h)) != 0) { + p->Make_Ally(h); + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Write_INI -- Writes the house data to the INI database. * + * * + * This routine will write out all data necessary to recreate it in anticipation of a * + * new scenario. All houses (that are active) will have their scenario type data written * + * out. * + * * + * INPUT: ini -- Reference to the INI database to write the data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Write_INI(CCINIClass & ini) +{ + /* + ** The identity house control object. Only if the house value differs from the + ** identity, will the data be written out. + */ + HouseStaticClass control; + + for (HousesType i = HOUSE_FIRST; i < HOUSE_COUNT; i++) { + HouseClass * p = As_Pointer(i); + + if (p != NULL) { + char const * name = p->Class->IniName; + + ini.Clear(name); + if (i >= HOUSE_MULTI1) continue; + + if (p->Control.InitialCredits != control.InitialCredits) { + ini.Put_Int(name, "Credits", (int)(p->Control.InitialCredits / 100)); + } + + if (p->Control.Edge != control.Edge) { + ini.Put_SourceType(name, "Edge", p->Control.Edge); + } + + if (p->Control.MaxUnit > 0 && p->Control.MaxUnit != control.MaxUnit) { + ini.Put_Int(name, "MaxUnit", p->Control.MaxUnit); + } + + if (p->Control.MaxInfantry > 0 && p->Control.MaxInfantry != control.MaxInfantry) { + ini.Put_Int(name, "MaxInfantry", p->Control.MaxInfantry); + } + + if (p->Control.MaxBuilding > 0 && p->Control.MaxBuilding != control.MaxBuilding) { + ini.Put_Int(name, "MaxBuilding", p->Control.MaxBuilding); + } + + if (p->Control.MaxVessel > 0 && p->Control.MaxVessel != control.MaxVessel) { + ini.Put_Int(name, "MaxVessel", p->Control.MaxVessel); + } + + if (p->Control.TechLevel != control.TechLevel) { + ini.Put_Int(name, "TechLevel", p->Control.TechLevel); + } + + if (p->Control.IQ != control.IQ) { + ini.Put_Int(name, "IQ", p->Control.IQ); + } + + if (p->IsPlayerControl != false && p != PlayerPtr) { + ini.Put_Bool(name, "PlayerControl", p->IsPlayerControl); + } + + ini.Put_Owners(name, "Allies", p->Control.Allies & ~((1 << p->Class->House) | (1 << HOUSE_NEUTRAL))); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_No_YakMig -- Determines if no more yaks or migs should be allowed. * + * * + * This routine will examine the current yak and mig situation verses airfields. If there * + * are equal aircraft to airfields, then this routine will return TRUE. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Are all airfields full and thus no more yaks or migs are allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_No_YakMig(void) const +{ + int quantity = AQuantity[AIRCRAFT_YAK] + AQuantity[AIRCRAFT_MIG]; + + /* + ** Adjust the quantity down one if there is an aircraft in production. This will + ** allow production to resume after being held. + */ + FactoryClass const * factory = Fetch_Factory(RTTI_AIRCRAFT); + if (factory != NULL && factory->Get_Object() != NULL) { + AircraftClass const * air = (AircraftClass const *)factory->Get_Object(); + if (*air == AIRCRAFT_MIG || *air == AIRCRAFT_YAK) { + quantity -= 1; + } + } + + if (quantity >= BQuantity[STRUCT_AIRSTRIP]) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Is_Hack_Prevented -- Is production of the specified type and id prohibted? * + * * + * This is a special hack check routine to see if the object type and id specified is * + * prevented from being produced. The Yak and the Mig are so prevented if there would be * + * insufficient airfields for them to land upon. * + * * + * INPUT: rtti -- The RTTI type of the value specified. * + * * + * value -- The type number (according to the RTTI type specified). * + * * + * OUTPUT: bool; Is production of this object prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Hack_Prevented(RTTIType rtti, int value) const +{ + if (rtti == RTTI_AIRCRAFTTYPE && (value == AIRCRAFT_MIG || value == AIRCRAFT_YAK)) { + return(Is_No_YakMig()); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Fire_Sale -- Cause all buildings to be sold. * + * * + * This routine will sell back all buildings owned by this house. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was a fire sale performed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Fire_Sale(void) +{ + if (CurBuildings > 0) { + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == this && b->Strength > 0) { + b->Sell_Back(1); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * HouseClass::Do_All_To_Hunt -- Send all units to hunt. * + * * + * This routine will cause all combatants of this house to go into hunt mode. The effect of * + * this is to throw everything this house has to muster at the enemies of this house. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + * 10/02/1996 JLB : Handles aircraft too. * + *=============================================================================================*/ +void HouseClass::Do_All_To_Hunt(void) const +{ + int index; + + for (index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit->House == this && unit->IsDown && !unit->IsInLimbo) { + if (unit->Team) unit->Team->Remove(unit); + unit->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry->House == this && infantry->IsDown && !infantry->IsInLimbo) { + if (infantry->Team) infantry->Team->Remove(infantry); + infantry->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + + if (vessel->House == this && vessel->IsDown && !vessel->IsInLimbo) { + if (vessel->Team) vessel->Team->Remove(vessel); + vessel->Assign_Mission(MISSION_HUNT); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->House == this && aircraft->IsDown && !aircraft->IsInLimbo) { + if (aircraft->Team) aircraft->Team->Remove(aircraft); + aircraft->Assign_Mission(MISSION_HUNT); + } + } +} + + +/*********************************************************************************************** + * HouseClass::Is_Allowed_To_Ally -- Determines if this house is allied to make allies. * + * * + * Use this routine to determine if this house is legally allowed to ally with the * + * house specified. There are many reason why an alliance is not allowed. Typically, this * + * is when there would be no more opponents left to fight or if this house has been * + * defeated. * + * * + * INPUT: house -- The house that alliance with is desired. * + * * + * OUTPUT: bool; Is alliance with the house specified prohibited? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool HouseClass::Is_Allowed_To_Ally(HousesType house) const +{ + /* + ** Is not allowed to ally with a house that is patently invalid, such + ** as one that is illegally defined. + */ + if (house == HOUSE_NONE) { + return(false); + } + + /* + ** One cannot ally twice with the same house. + */ + if (Is_Ally(house)) { + return(false); + } + + /* + ** If the scenario is being set up, then alliances are always + ** allowed. No further checking is required. + */ + if (ScenarioInit) { + return(true); + } + + /* + ** Alliances (outside of scneario init time) are allowed only if + ** this is a multiplayer game. Otherwise, they are prohibited. + */ + if (Session.Type == GAME_NORMAL) { + return(false); + } + + /* + ** When the house is defeated, it can no longer make alliances. + */ + if (IsDefeated) { + return(false); + } + +#ifdef FIXIT_VERSION_3 + // Fix to prevent ally with computer. + if ( !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#else // FIXIT_VERSION_3 +#ifdef FIXIT_NO_COMP_ALLY + // Fix to prevent ally with computer. + if (PlayingAgainstVersion > VERSION_RED_ALERT_104 && !HouseClass::As_Pointer(house)->IsHuman) { + return(false); + } +#endif +#endif // FIXIT_VERSION_3 + + /* + ** Count the number of active houses in the game as well as the + ** number of existing allies with this house. + */ + int housecount = 0; + int allycount = 0; + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr = HouseClass::As_Pointer(house2); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated) { + housecount++; + if (Is_Ally(hptr)) { + allycount++; + } + } + } + + /* + ** Alliance is not allowed if there wouldn't be any enemies left to + ** fight. + */ + if (housecount == allycount+1) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * HouseClass::Computer_Paranoid -- Cause the computer players to becom paranoid. * + * * + * This routine will cause the computer players to become suspicious of the human * + * players and thus the computer players will band together in order to defeat the * + * human players. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +void HouseClass::Computer_Paranoid(void) +{ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { // Re-enable this for multiplayer if we support classic team/ally mode. ST - 10/29/2019 + + /* + ** Loop through every computer controlled house and make allies with all other computer + ** controlled houses and then make enemies with all other human controlled houses. + */ + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL && hptr->IsActive && !hptr->IsDefeated && !hptr->IsHuman) { + hptr->IsParanoid = true; + + /* + ** Break alliance with every human it is allied with and make friends with + ** any other computer players. + */ + for (HousesType house2 = HOUSE_MULTI1; house2 < HOUSE_COUNT; house2++) { + HouseClass * hptr2 = HouseClass::As_Pointer(house2); + if (hptr2 != NULL && hptr2->IsActive && !hptr2->IsDefeated) { + if (hptr2->IsHuman) { + hptr->Make_Enemy(house2); + } else { + hptr->Make_Ally(house2); + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Power -- Adjust the power value of the house. * + * * + * This routine will update the power output value of the house. It will cause any buildgins* + * that need to be redrawn to do so. * + * * + * INPUT: adjust -- The amount to adjust the power output value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Power(int adjust) +{ + Power += adjust; + + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Adjust_Drain -- Adjust the power drain value of the house. * + * * + * This routine will update the drain value of the house. It will cause any buildings that * + * need to be redraw to do so. * + * * + * INPUT: adjust -- The amount to adjust the drain (positive means more drain). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Adjust_Drain(int adjust) +{ + Drain += adjust; + Update_Spied_Power_Plants(); +} + + +/*********************************************************************************************** + * HouseClass::Update_Spied_Power_Plants -- Redraw power graphs on spied-upon power plants. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/11/1996 BWG : Created. * + *=============================================================================================*/ +void HouseClass::Update_Spied_Power_Plants(void) +{ + int count = CurrentObject.Count(); + if (count) { + for (int index = 0; index < count; index++) { + ObjectClass const * tech = CurrentObject[index]; + if (tech && tech->What_Am_I()==RTTI_BUILDING) { + BuildingClass *bldg = (BuildingClass *)tech; + if (!bldg->IsOwnedByPlayer && *bldg == STRUCT_POWER || *bldg == STRUCT_ADVANCED_POWER) { + if ( bldg->Spied_By() & (1<<(PlayerPtr->Class->House)) ) { + bldg->Mark(MARK_CHANGE); + } + } + } + } + } +} + + +/*********************************************************************************************** + * HouseClass::Find_Cell_In_Zone -- Finds a legal placement cell within the zone. * + * * + * Use this routine to determine where the specified object should go if it were to go * + * some random (but legal) location within the zone specified. * + * * + * INPUT: techno -- The object that is desirous of going into the zone specified. * + * * + * zone -- The zone to find a location within. * + * * + * OUTPUT: Returns with the cell that the specified object could be placed in the zone. If * + * no valid location could be found, then 0 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/01/1996 JLB : Created. * + * 11/04/1996 JLB : Not so strict on zone requirement. * + *=============================================================================================*/ +CELL HouseClass::Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const +{ + if (techno == NULL) return(0); + + int bestval = -1; + int bestcell = 0; + TechnoTypeClass const * ttype = techno->Techno_Type_Class(); + + /* + ** Pick a random location within the zone specified. + */ + CELL trycell = Random_Cell_In_Zone(zone); + + short const * list = NULL; + if (techno->What_Am_I() == RTTI_BUILDING) { + list = techno->Occupy_List(true); + } + + /* + ** Find a legal placement position as close as possible to the picked location while still + ** remaining within the zone. + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { +// if (Map.In_Radar(cell)) { + if (Map.In_Radar(cell) && Which_Zone(cell) != ZONE_NONE) { + bool ok = ttype->Legal_Placement(cell); + + /* + ** Another (adjacency) check is required for buildings. + */ + if (ok && list != NULL && !Map.Passes_Proximity_Check(ttype, techno->House->Class->House, list, cell)) { + ok = false; + } + + if (ok) { + int dist = Distance(Cell_Coord(cell), Cell_Coord(trycell)); + if (bestval == -1 || dist < bestval) { + bestval = dist; + bestcell = cell; + } + } + } + } + + /* + ** Return the best location to move to. + */ + return(bestcell); +} + + +/*********************************************************************************************** + * HouseClass::Random_Cell_In_Zone -- Find a (technically) legal cell in the zone specified. * + * * + * This routine will pick a random cell within the zone specified. The pick will be * + * clipped to the map edge when necessary. * + * * + * INPUT: zone -- The zone to pick a cell from. * + * * + * OUTPUT: Returns with a picked cell within the zone. If the entire zone lies outside of the * + * map, then a cell in the core zone is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/04/1996 JLB : Created. * + *=============================================================================================*/ +CELL HouseClass::Random_Cell_In_Zone(ZoneType zone) const +{ + COORDINATE coord = 0; + int maxdist = 0; + switch (zone) { + case ZONE_CORE: + coord = Coord_Scatter(Center, Random_Pick(0, Radius), true); + break; + + case ZONE_NORTH: + maxdist = min(Radius*3, (Coord_Y(Center) - Cell_To_Lepton(Map.MapCellY)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, (DirType)(Random_Pick(DIR_N, DIR_E)-((DirType)32)), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_EAST: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) - Coord_X(Center)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_NE, DIR_SE), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_SOUTH: + maxdist = min(Radius*3, (Cell_To_Lepton(Map.MapCellY + Map.MapCellHeight) - Coord_Y(Center)) - CELL_LEPTON_H); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SE, DIR_SW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + + case ZONE_WEST: + maxdist = min(Radius*3, (Coord_X(Center) - Cell_To_Lepton(Map.MapCellX)) - CELL_LEPTON_W); + if (maxdist < 0) break; + coord = Coord_Move(Center, Random_Pick(DIR_SW, DIR_NW), Random_Pick(min(Radius*2, maxdist), min(Radius*3, maxdist))); + break; + } + + /* + ** Double check that the location is valid and if so, convert it into a cell + ** number. + */ + CELL cell; + if (coord == 0 || !Map.In_Radar(Coord_Cell(coord))) { + if (zone == ZONE_CORE) { + + /* + ** Finding a cell within the core failed, so just pick the center + ** cell. This cell is guaranteed to be valid. + */ + cell = Coord_Cell(Center); + } else { + + /* + ** If the edge fails, then try to find a cell within the core. + */ + cell = Random_Cell_In_Zone(ZONE_CORE); + } + } else { + cell = Coord_Cell(coord); + } + + /* + ** If the randomly picked location is not in the legal map area, then clip it to + ** the legal map area. + */ + if (!Map.In_Radar(cell)) { + int x = Cell_X(cell); + int y = Cell_Y(cell); + + if (x < Map.MapCellX) x = Map.MapCellX; + if (y < Map.MapCellY) y = Map.MapCellY; + if (x >= Map.MapCellX + Map.MapCellWidth) x = Map.MapCellX + Map.MapCellWidth -1; + if (y >= Map.MapCellY + Map.MapCellHeight) y = Map.MapCellY + Map.MapCellHeight -1; + cell = XY_Cell(x, y); + } + return(cell); +} + +/*********************************************************************************************** + * HouseClass::Get_Ally_Flags -- Get the bit flags denoting the allies this house has. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the bit field storing which houses this house is allied with. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/12/2019 JAS : Created. * + *=============================================================================================*/ +unsigned HouseClass::Get_Ally_Flags() +{ + return Allies; +} + + + +/*********************************************************************************************** + * HouseClass::Check_Pertinent_Structures -- See if any useful structures remain * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 1/31/2020 3:34PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Check_Pertinent_Structures(void) +{ + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + ** + ** Game is over when no pertinent structures remain + */ + + if (!Special.IsEarlyWin) { + return; + } + + if (IsToDie || IsToWin || IsToLose) { + return; + } + + bool any_good_buildings = false; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass *b = Buildings.Ptr(index); + + if (b && b->IsActive && b->House == this) { + if (!b->Class->IsWall) { + if (!b->IsInLimbo && b->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + + if (unit && unit->IsActive && *unit == UNIT_MCV && unit->House == this) { + if (!unit->IsInLimbo && unit->Strength > 0) { + any_good_buildings = true; + break; + } + } + } + } + + if (!any_good_buildings) { + Flag_To_Die(); + } +} + + + + + +/*********************************************************************************************** + * HouseClass::Init_Unit_Trackers -- Allocate the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Init_Unit_Trackers(void) +{ + if (Session.Type == GAME_INTERNET || Session.Type == GAME_GLYPHX_MULTIPLAYER) { + AircraftTotals = new UnitTrackerClass( (int) AIRCRAFT_COUNT); + InfantryTotals = new UnitTrackerClass( (int) INFANTRY_COUNT); + UnitTotals = new UnitTrackerClass ( (int) UNIT_COUNT); + BuildingTotals = new UnitTrackerClass ( (int) STRUCT_COUNT); + VesselTotals = new UnitTrackerClass ( (int) VESSEL_COUNT); + + DestroyedAircraft = new UnitTrackerClass ( (int) AIRCRAFT_COUNT); + DestroyedInfantry = new UnitTrackerClass( (int) INFANTRY_COUNT); + DestroyedUnits = new UnitTrackerClass ( (int) UNIT_COUNT); + DestroyedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + DestroyedVessels = new UnitTrackerClass ( (int) VESSEL_COUNT); + + CapturedBuildings = new UnitTrackerClass ( (int) STRUCT_COUNT); + TotalCrates = new UnitTrackerClass ( CRATE_COUNT ); + } else { + + AircraftTotals = NULL; + InfantryTotals = NULL; + UnitTotals = NULL; + BuildingTotals = NULL; + VesselTotals = NULL; + + DestroyedAircraft = NULL; + DestroyedInfantry = NULL; + DestroyedUnits = NULL; + DestroyedBuildings = NULL; + DestroyedVessels = NULL; + + CapturedBuildings = NULL; + TotalCrates = NULL; + } +} + + + +/*********************************************************************************************** + * HouseClass::Free_Unit_Trackers -- Free the unit trackers for the house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/23/2020 11:06PM ST : Created. * + *=============================================================================================*/ +void HouseClass::Free_Unit_Trackers(void) +{ + if (AircraftTotals) { + delete AircraftTotals; + AircraftTotals = NULL; + } + + if (InfantryTotals) { + delete InfantryTotals; + InfantryTotals = NULL; + } + + if (UnitTotals) { + delete UnitTotals; + UnitTotals = NULL; + } + + if (BuildingTotals) { + delete BuildingTotals; + BuildingTotals = NULL; + } + + if (VesselTotals) { + delete VesselTotals; + VesselTotals = NULL; + } + + if (DestroyedAircraft) { + delete DestroyedAircraft; + DestroyedAircraft = NULL; + } + + if (DestroyedInfantry) { + delete DestroyedInfantry; + DestroyedInfantry = NULL; + } + + if (DestroyedUnits) { + delete DestroyedUnits; + DestroyedUnits = NULL; + } + + if (DestroyedBuildings) { + delete DestroyedBuildings; + DestroyedBuildings = NULL; + } + + if (DestroyedVessels) { + delete DestroyedVessels; + DestroyedVessels = NULL; + } + + if (CapturedBuildings) { + delete CapturedBuildings; + CapturedBuildings = NULL; + } + + if (TotalCrates) { + delete TotalCrates; + TotalCrates = NULL; + } +} \ No newline at end of file diff --git a/code/redalert/house.h b/code/redalert/house.h new file mode 100644 index 0000000..d21a745 --- /dev/null +++ b/code/redalert/house.h @@ -0,0 +1,958 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HOUSE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 21, 1994 * + * * + * Last Update : May 21, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef HOUSE_H +#define HOUSE_H + +#include "type.h" +#include "region.h" +#include "vector.h" +#include "Credits.h" + +class TriggerClass; +class FootClass; +class FactoryClass; + +#define HOUSE_NAME_MAX 12 + + +/**************************************************************************** +** Certain aspects of the house "country" are initially set by the scenario +** control file. This information is static for the duration of the current +** scenario, but is dynamic between scenarios. As such, it can't be placed in +** the static HouseTypeClass structure, but is embedded into the house +** class instead. +*/ +class HouseStaticClass { + public: + HouseStaticClass(void); + HouseStaticClass(NoInitClass const & ) {}; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value is zero is presumed for human controlled houses. + */ + int IQ; + + /* + ** This is the buildable tech level for this house. This value is used + ** for when the computer is deciding what objects to build. + */ + int TechLevel; + + /* + ** This is the original ally specification to use at scenario + ** start. Various forces during play may adjust the ally state + ** of this house. + */ + int Allies; + + /* + ** This is the maximum number allowed to be built by this house. The + ** value depends on the scenario being played. + */ + unsigned MaxUnit; + unsigned MaxBuilding; + unsigned MaxInfantry; + unsigned MaxVessel; + unsigned MaxAircraft; + + /* + ** This records the initial credits assigned to this house when the scenario + ** was loaded. + */ + long InitialCredits; + + /* + ** For generic (unspecified) reinforcements, they arrive by a common method. This + ** specifies which method is to be used. + */ + SourceType Edge; +}; + + +/**************************************************************************** +** Player control structure. Each player (computer or human) has one of +** these structures associated. These are located in a global array. +*/ +class HouseClass { + public: + RTTIType RTTI; + int ID; + + /* + ** Pointer to the HouseTypeClass that this house is "owned" by. + ** All constant data for a house type is stored in that class. + */ + CCPtr Class; + + /* + ** This is the handicap (difficulty level) assigned to this house. + */ + DiffType Difficulty; + + /* + ** Override handicap control values. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + fixed RepairDelay; + fixed BuildDelay; + + /* + ** The initial house data as loaded from the scenario control file is + ** stored here. Although this data changes for each scenario, it remains + ** static for the duration of the current scenario. + */ + HouseStaticClass Control; + + /* + ** This is the house type that this house object should act like. This + ** value controls production choices and radar cover plate imagery. + */ + HousesType ActLike; + + /* + ** Is this player active? Usually that answer is true, but for civilians, it + ** might possibly be false. + */ + unsigned IsActive:1; + + /* + ** If this house is controlled by the player, then this flag will be true. The + ** computer controls all other active houses. + */ + unsigned IsHuman:1; + unsigned WasHuman:1; + + /* + ** If the player can control units of this house even if the player doesn't + ** own units of this house, then this flag will be true. + */ + unsigned IsPlayerControl:1; + + /* + ** This flag enables production. If the flag is false, production is disabled. + ** By timing when this flag gets set, the player can be given some breathing room. + */ + unsigned IsStarted:1; + + /* + ** When alerted, the house will create teams of the special "auto" type and + ** will generate appropriate units to fill those team types. + */ + unsigned IsAlerted:1; + + /* + ** If automatic base building is on, then this flag will be set to true. + */ + unsigned IsBaseBuilding:1; + + /* + ** If the house has been discovered, then this flag will be set + ** to true. However, the trigger even associated with discovery + ** will only be executed during the next house AI process. + */ + unsigned IsDiscovered:1; + + /* + ** If Tiberium storage is maxed out, then this flag will be set. At some point + ** the player is told of this fact and then this flag is cleared. This allows the + ** player to be told, but only occasionally rather than continuously. + */ + unsigned IsMaxedOut:1; + + /* + ** If this house is played by a human in a multiplayer game, this flag + ** keeps track of whether this house has been defeated or not. + */ + unsigned IsDefeated:1; + + /* + ** These flags are used in conjunction with the BorrowedTime timer. When + ** that timer expires and one of these flags are set, then that event is + ** applied to the house. This allows a dramatic pause between the event + ** trigger and the result. + */ + unsigned IsToDie:1; + unsigned IsToWin:1; + unsigned IsToLose:1; + + /* + ** This flag is set when a transport carrying a civilian has been + ** successfully evacuated. It is presumed that a possible trigger + ** event will be sprung by this event. + */ + unsigned IsCivEvacuated:1; + + /* + ** If potentially something changed that might affect the sidebar list of + ** buildable objects, then this flag indicates that at the first LEGAL opportunity, + ** the sidebar will be recalculated. + */ + unsigned IsRecalcNeeded:1; + + /* + ** If the map has been completely revealed to the player, then this flag + ** will be set to true. By examining this flag, a second "reveal all map" + ** crate won't be given to the player. + */ + unsigned IsVisionary:1; + + /* + ** This flag is set to true when the house has determined that + ** there is insufficient Tiberium to keep the harvesters busy. + ** In such a case, the further refinery/harvester production + ** should cease. This is one of the first signs that the endgame + ** has begun. + */ + unsigned IsTiberiumShort:1; + + /* + ** These flags are used for the general house trigger events of being + ** spied and thieved. The appropriate flag will be set when the event + ** occurs. + */ + unsigned IsSpied:1; + unsigned IsThieved:1; + + /* + ** This flag is used to control non-human repairing of buildings. Each + ** house gets to repair one building per loop, and this flag controls + ** whether this house has 'spent' its repair option this time through. + */ + unsigned DidRepair:1; + + /* + ** This flag is used to control whether or not this house has the GPS + ** satellite in orbit. If the satellite's there, they have unlimited + ** radar and the map is fully revealed. + */ + unsigned IsGPSActive:1; + + /* + ** If the JustBuilt??? variable has changed, then this flag will + ** be set to true. + */ + unsigned IsBuiltSomething:1; + + /* + ** Did this house lose via resignation? + */ + unsigned IsResigner:1; + + /* + ** Did this house lose because the player quit? + */ + unsigned IsGiverUpper:1; + + /* + ** If this computer controlled house has reason to be mad at humans, + ** then this flag will be true. Such a condition prevents alliances with + ** a human and encourages the computers players to ally amongst themselves. + */ + unsigned IsParanoid:1; + + /* + ** A gap generator shrouded cells and all units of this house must perform + ** a look just in case their look radius intersects the shroud area. + */ + unsigned IsToLook:1; + + /* + ** MBL 03.23.2020 - Support for queued movement mode (informed from the client) + */ + unsigned IsQueuedMovementToggle:1; + + /* + ** This value indicates the degree of smartness to assign to this house. + ** A value of zero indicates that the player controls everything. + */ + int IQ; + + /* + ** This records the current state of the base. This state is used to control + ** what action the base will perform and directly affects production and + ** unit disposition. The state will change according to time and combat + ** events. + */ + StateType State; + + /* + ** These super weapon control objects are used to control the recharge + ** and availability of these special weapons to this house. + */ + SuperClass SuperWeapon[SPC_COUNT]; + + /* + ** This is a record of the last building that was built. For buildings that + ** were built as a part of scenario creation, it will be the last one + ** discovered. + */ + StructType JustBuiltStructure; + InfantryType JustBuiltInfantry; + UnitType JustBuiltUnit; + AircraftType JustBuiltAircraft; + VesselType JustBuiltVessel; + + /* + ** This records the number of triggers associated with this house that are + ** blocking a win condition. A win will only occur if all the blocking + ** triggers have been deleted. + */ + int Blockage; + + /* + ** For computer controlled houses, there is an artificial delay between + ** performing repair actions. This timer regulates that delay. If the + ** timer has not expired, then no repair initiation is allowed. + */ + CDTimerClass RepairTimer; + + /* + ** This timer controls the computer auto-attack logic. When this timer expires + ** and the house has been alerted, then it will create a set of attack + ** teams. + */ + CDTimerClass AlertTime; + + /* + ** This timer is used to handle the delay between some catastrophic + ** event trigger and when it is actually carried out. + */ + CDTimerClass BorrowedTime; + + /* + ** This is the last working scan bits for buildings. If a building is + ** active and owned by this house, it will have a bit set in this element + ** that corresponds to the building type number. Since this value is + ** accumulated over time, the "New" element contains the under-construction + ** version. + */ + unsigned long BScan; + unsigned long ActiveBScan; + unsigned long OldBScan; + + /* + ** This is the last working scan bits for units. For every existing unit + ** type owned by this house, a corresponding bit is set in this element. As + ** the scan bits are being constructed, they are built into the "New" element + ** and then duplicated into the regular element at the end of every logic cycle. + */ + unsigned long UScan; + unsigned long ActiveUScan; + unsigned long OldUScan; + + /* + ** Infantry type existence bits. Similar to unit and building bits. + */ + unsigned long IScan; + unsigned long ActiveIScan; + unsigned long OldIScan; + + /* + ** Aircraft type existence bits. Similar to unit and building bits. + */ + unsigned long AScan; + unsigned long ActiveAScan; + unsigned long OldAScan; + + /* + ** Vessel type existence bits. Similar to unit and building bits. + */ + unsigned long VScan; + unsigned long ActiveVScan; + unsigned long OldVScan; + + /* + ** Record of gains and losses for this house during the course of the + ** scenario. + */ + unsigned CreditsSpent; + unsigned HarvestedCredits; + int StolenBuildingsCredits; + + /* + ** This is the running count of the number of units owned by this house. This + ** value is used to keep track of ownership limits. + */ + unsigned CurUnits; + unsigned CurBuildings; + unsigned CurInfantry; + unsigned CurVessels; + unsigned CurAircraft; + + /* + ** This is the running total of the number of credits this house has accumulated. + */ + long Tiberium; + long Credits; + long Capacity; + + /* + ** Stuff to keep track of the total number of units built by this house. + */ + UnitTrackerClass * AircraftTotals; + UnitTrackerClass * InfantryTotals; + UnitTrackerClass * UnitTotals; + UnitTrackerClass * BuildingTotals; + UnitTrackerClass * VesselTotals; + + /* + ** Total number of units destroyed by this house + */ + UnitTrackerClass * DestroyedAircraft; + UnitTrackerClass * DestroyedInfantry; + UnitTrackerClass * DestroyedUnits; + UnitTrackerClass * DestroyedBuildings; + UnitTrackerClass * DestroyedVessels; + + /* + ** Total number of enemy buildings captured by this house + */ + UnitTrackerClass * CapturedBuildings; + + /* + ** Total number of crates found by this house + */ + UnitTrackerClass * TotalCrates; + + /* + ** Records the number of infantry and vehicle factories active. This value is + ** used to regulate the speed of production. + */ + int AircraftFactories; + int InfantryFactories; + int UnitFactories; + int VesselFactories; + int BuildingFactories; + + /* + ** This is the accumulation of the total power and drain factors. From these + ** values a ratio can be derived. This ratio is used to control the rate + ** of building decay. + */ + int Power; // Current power output. + int Drain; // Power consumption. + + /* + ** For human controlled houses, only one type of unit can be produced + ** at any one instant. These factory objects control this production. + */ + int AircraftFactory; + int InfantryFactory; + int UnitFactory; + int VesselFactory; + int BuildingFactory; + + /* + ** For human controlled houses, the current state of the radar map + */ + RadarEnum Radar; + + /* + ** This target value specifies where the flag is located. It might be a cell + ** or it might be an object. + */ + TARGET FlagLocation; + + /* + ** This is the flag-home-cell for this house. This is where we must bring + ** another house's flag back to, to defeat that house. + */ + CELL FlagHome; + + /* + ** For multiplayer games, each house needs to keep track of how many + ** objects of each other house they've killed. + */ + unsigned UnitsKilled[HOUSE_COUNT]; + unsigned UnitsLost; + unsigned BuildingsKilled[HOUSE_COUNT]; + unsigned BuildingsLost; + + /* + ** This keeps track of the last house to destroy one of my units. + ** It's used for scoring multiplayer games. + */ + HousesType WhoLastHurtMe; + + /* + ** Start location (waypoint index) passed in from GlyphX + */ + int StartLocationOverride; + + /* + ** This records information about the location and size of + ** the base. + */ + COORDINATE Center; // Center COORDINATE of the base. + //CELL CenterCell; // Center CELL of the base. + int Radius; // Average building distance from center (leptons). + struct { + int AirDefense; + int ArmorDefense; + int InfantryDefense; + } ZoneInfo[ZONE_COUNT]; + + /* + ** This records information about the last time a building of this + ** side was attacked. This information is used to determine proper + ** response. + */ + int LATime; // Time of attack. + RTTIType LAType; // Type of attacker. + ZoneType LAZone; // Last zone that was attacked. + HousesType LAEnemy; // Owner of attacker. + + /* + ** This target value is the building that must be captured as soon as possible. + ** Typically, this will be one of the buildings of this house that has been + ** captured and needs to be recaptured. + */ + TARGET ToCapture; + + /* + ** This value shows who is spying on this house's radar facilities. + ** This is used for the other side to be able to update their radar + ** map based on the cells that this house's units reveal. + */ + int RadarSpied; + + /* + ** Running score, based on units destroyed and units lost. + */ + int PointTotal; + + /* + ** This is the targeting directions for when this house gets a + ** special weapon. + */ + QuarryType PreferredTarget; + + /* + ** Screen shake timer. + */ + CDTimerClass ScreenShakeTime; + + private: + /* + ** Tracks number of each building type owned by this house. Even if the + ** building is in construction, it will be reflected in this total. + */ +#ifdef FIXIT_ANTS + int BQuantity[STRUCT_COUNT-3]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int UQuantity[UNIT_RA_COUNT-3]; +#else + int UQuantity[UNIT_COUNT-3]; +#endif +#else + int BQuantity[STRUCT_COUNT]; + int UQuantity[UNIT_COUNT]; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int IQuantity[INFANTRY_RA_COUNT]; +#else + int IQuantity[INFANTRY_COUNT]; +#endif + int AQuantity[AIRCRAFT_COUNT]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int VQuantity[VESSEL_RA_COUNT]; +#else + int VQuantity[VESSEL_COUNT]; +#endif + + /* + ** This timer keeps track of when an all out attack should be performed. + ** When this timer expires, send most of this house's units in an + ** attack. + */ + CDTimerClass Attack; + + public: + /* + ** This records the overriding enemy that the computer will try to + ** destroy. Typically, this is the last house to attack, but can be + ** influenced by nearness. + */ + HousesType Enemy; + + /* + ** The house expert system is regulated by this timer. Each computer controlled + ** house will process the Expert System AI at intermittent intervals. Not only will + ** this distribute the overhead more evenly, but will add variety to play. + */ + CDTimerClass AITimer; + + /* + ** For the moebius effect, this is a pointer to the unit that we + ** selected to teleport. Only one teleporter should be active per house. + */ + TARGET UnitToTeleport; + + /* + ** This elaborates the suggested objects to construct. When the specified object + ** is constructed, then this corresponding value will be reset to nill state. The + ** expert system decides what should be produced, and then records the + ** recommendation in these variables. + */ + StructType BuildStructure; + UnitType BuildUnit; + InfantryType BuildInfantry; + AircraftType BuildAircraft; + VesselType BuildVessel; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + HouseClass(HousesType house); + HouseClass(NoInitClass const & x) : Class(x), Control(x), AlertTime(x), BorrowedTime(x), Attack(x), AITimer(x), DamageTime(x), TeamTime(x), TriggerTime(x), SpeakAttackDelay(x), SpeakPowerDelay(x), SpeakMoneyDelay(x), SpeakMaxedDelay(x) {}; + ~HouseClass(void); + operator HousesType(void) const; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + CELL Random_Cell_In_Zone(ZoneType zone) const; + static void Computer_Paranoid(void); + bool Is_Allowed_To_Ally(HousesType house) const; + void Do_All_To_Hunt(void) const; + void Super_Weapon_Handler(void); + int * Factory_Counter(RTTIType rtti); + int Factory_Count(RTTIType rtti) const; + DiffType Assign_Handicap(DiffType handicap); + TARGET Find_Juicy_Target(COORDINATE coord) const; + void Print_Zone_Stats(int x, int y, ZoneType zone, MonoClass * mono) const; + CELL Where_To_Go(FootClass const * object) const; + CELL Zone_Cell(ZoneType zone) const; + ZoneType Which_Zone(COORDINATE coord) const; + ZoneType Which_Zone(ObjectClass const * object) const; + ZoneType Which_Zone(CELL cell) const; + CELL Find_Cell_In_Zone(TechnoClass const * techno, ZoneType zone) const; + ProdFailType Begin_Production(RTTIType type, int id); + ProdFailType Suspend_Production(RTTIType type); + ProdFailType Abandon_Production(RTTIType type); + bool Place_Object(RTTIType type, CELL cell); + bool Manual_Place(BuildingClass * builder, BuildingClass * object); + void Special_Weapon_AI(SpecialWeaponType id); + bool Place_Special_Blast(SpecialWeaponType id, CELL cell); + bool Flag_Attach(CELL cell, bool set_home = false); + bool Flag_Attach(UnitClass * object, bool set_home = false); + bool Flag_Remove(TARGET target, bool set_home = false); + void Init_Data(PlayerColorType color, HousesType house, int credits); + COORDINATE Find_Build_Location(BuildingClass * building) const; + BuildingClass * Find_Building(StructType type, ZoneType zone=ZONE_NONE) const; + char const * Name(void) const {return(Class->Name());} + + // Added so the ally flags could be sent to client machines - 09 / 12 / 2019 JAS + unsigned Get_Ally_Flags(); + + bool Fire_Sale(void); + bool Is_Hack_Prevented(RTTIType rtti, int value) const; + bool Is_No_YakMig(void) const; + int Expert_AI(void); + void Production_Begun(TechnoClass const * rtti); + void Sell_Wall(CELL cell); + bool Flag_To_Die(void); + bool Flag_To_Win(void); + bool Flag_To_Lose(void); + void Make_Ally(HousesType house); + void Make_Ally(ObjectClass * object) {if (object) Make_Ally(object->Owner());}; + void Make_Enemy(HousesType house); + void Make_Enemy(ObjectClass * object) {if (object) Make_Enemy(object->Owner());}; + bool Is_Ally(HousesType house) const; + bool Is_Ally(HouseClass const * house) const; + bool Is_Ally(ObjectClass const * object) const; + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + void AI(void); + bool Can_Build(RTTIType rtti, int type, HousesType house) const; + + // Factory controls. + FactoryClass * Fetch_Factory(RTTIType rtti) const; + void Set_Factory(RTTIType rtti, FactoryClass * factory); + + bool Can_Build(ObjectTypeClass const * type, HousesType house) const; + + int Get_Quantity(AircraftType aircraft); + int Get_Quantity(StructType building); + unsigned char const * Remap_Table(bool blushing=false, RemapType remap=REMAP_NORMAL) const; + + TechnoTypeClass const * Suggest_New_Object(RTTIType objectype, bool kennel=false) const; + BuildingTypeClass const * Suggest_New_Building(void) const; + void Recalc_Center(void); + bool Does_Enemy_Building_Exist(StructType) const; + void Harvested(unsigned tiberium); + void Stole(unsigned worth); + long Available_Money(void) const; + void Spend_Money(unsigned money); + void Refund_Money(unsigned money); + void Attacked(BuildingClass* source); + void Adjust_Power(int adjust); + void Adjust_Drain(int adjust); + void Update_Spied_Power_Plants(void); + int Adjust_Capacity(int adjust, bool inanger=false); + fixed Power_Fraction(void) const; + fixed Tiberium_Fraction(void) const; + void Begin_Production(void) {IsStarted = true;}; + TeamTypeClass const * Suggested_New_Team(bool alertcheck = false); + void Adjust_Threat(int region, int threat); + void Tracking_Remove(TechnoClass const * techno); + void Tracking_Add(TechnoClass const * techno); + void Active_Remove(TechnoClass const * techno); + void Active_Add(TechnoClass const * techno); + + UrgencyType Check_Attack(void) const; + UrgencyType Check_Build_Power(void) const; + UrgencyType Check_Build_Defense(void) const; + UrgencyType Check_Build_Offense(void) const; + UrgencyType Check_Build_Income(void) const; + UrgencyType Check_Fire_Sale(void) const; + UrgencyType Check_Build_Engineer(void) const; + UrgencyType Check_Raise_Money(void) const; + UrgencyType Check_Raise_Power(void) const; + UrgencyType Check_Lower_Power(void) const; + + bool AI_Attack(UrgencyType urgency); + bool AI_Build_Power(UrgencyType urgency) const; + bool AI_Build_Defense(UrgencyType urgency) const; + bool AI_Build_Offense(UrgencyType urgency) const; + bool AI_Build_Income(UrgencyType urgency) const; + bool AI_Fire_Sale(UrgencyType urgency); + bool AI_Build_Engineer(UrgencyType urgency) const; + bool AI_Raise_Money(UrgencyType urgency) const; + bool AI_Raise_Power(UrgencyType urgency) const; + bool AI_Lower_Power(UrgencyType urgency) const; + + bool Can_Make_Money(void) const { + return(Available_Money() > 300 || (BScan & STRUCTF_REFINERY)); + }; + + static void Init(void); + static void One_Time(void); + static HouseClass * As_Pointer(HousesType house); + static void Recalc_Attributes(void); + + /* + ** New default win mode to avoid griefing. ST - 1/31/2020 3:33PM + */ + void Check_Pertinent_Structures(void); + + void Init_Unit_Trackers(void); + void Free_Unit_Trackers(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static void Read_Flag_INI(char *buffer); + static void Write_Flag_INI(char *buffer); + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Special house actions. + */ + void Detach(TARGET target, bool all); + + /* + ** This vector holds the recorded status of the map regions. It is through + ** this region information that team paths are calculated. + */ + RegionClass Regions[MAP_TOTAL_REGIONS]; + + /* + ** This count down timer class decrements and then changes + ** the Atomic Bomb state. + */ + CELL NukeDest; + + /* + ** Per-house credits class to track the visible credits state for each house. Redundant in the original game, but needed + ** to preserve the exact credits count behavior in the GlyphX client. ST - 10/16/2019 2:31PM + */ + CreditClass VisibleCredits; + + bool DebugUnlockBuildables; + + /* + ** This routine completely removes this house & all its objects from the game. + */ + void Clobber_All(void); + + /* + ** This routine blows up everything in this house. Fun! + */ + void Blowup_All(void); + + /* + ** This routine gets called in multiplayer games when every unit, building, + ** and infantry for a house is destroyed. + */ + void MPlayer_Defeated(void); + + /* + ** When the game's over, this routine assigns everyone their score. + */ + void Tally_Score(void); + + friend class MapEditClass; + + private: + void Silo_Redraw_Check(long oldtib, long oldcap); + int AI_Building(void); + int AI_Unit(void); + int AI_Vessel(void); + int AI_Infantry(void); + int AI_Aircraft(void); + + /* + ** This is a bit field record of all the other houses that are allies with + ** this house. It is presumed that any house that isn't an ally, is therefore + ** an enemy. A house is always considered allied with itself. + */ + unsigned Allies; + + /* + ** General low-power related damaged is doled out whenever this timer + ** expires. + */ + CDTimerClass DamageTime; + + /* + ** Team creation is done whenever this timer expires. + */ + CDTimerClass TeamTime; + + /* + ** This controls the rate that the trigger time logic is processed. + */ + CDTimerClass TriggerTime; + + /* + ** At various times, the computer may announce the player's condition. The following + ** variables are used as countdown timers so that these announcements are paced + ** far enough apart to reduce annoyance. + */ + CDTimerClass SpeakAttackDelay; + CDTimerClass SpeakPowerDelay; + CDTimerClass SpeakMoneyDelay; + CDTimerClass SpeakMaxedDelay; + + /* + ** This structure is used to record a build request as determined by + ** the house AI processing. Higher priority build requests take precidence. + */ + struct BuildChoiceClass { + static void * operator new(size_t, void * ptr) {return(ptr);}; + UrgencyType Urgency; // The urgency of the build request. + StructType Structure; // The type of building to produce. + + BuildChoiceClass(UrgencyType u, StructType s) : Urgency(u), Structure(s) {}; + BuildChoiceClass(NoInitClass const & ) {}; + int Save(Pipe &) const {return(true);}; + int Load(Straw &) {return(true);}; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + }; + + static TFixedIHeapClass BuildChoice; + + + /* + ** These values are for multiplay only. + */ + public: + /* + ** For multiplayer games, each house instance has a remap table; the table + ** in the HousesTypeClass isn't used. This variable is set to the remap + ** table for the color the player wants to play. + */ + PlayerColorType RemapColor; + + /* + ** This is the name ("handle") the player has chosen for himself. + */ + char IniName[HOUSE_NAME_MAX]; + +#ifdef WOLAPI_INTEGRATION + // For Internet games only, unchanging name of player when game began. + // This name does not get changed to "Computer" if computer takes over for player. + char InitialName[HOUSE_NAME_MAX]; +#endif + + int QuantityB(int index) {return(BQuantity[index]);} +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int QuantityU(int index) { + if(index >= UNIT_RA_COUNT) index -= UNIT_RA_COUNT; + return(UQuantity[index]); + } + int QuantityI(int index) { + if(index >= INFANTRY_RA_COUNT) index -= INFANTRY_RA_COUNT; + return(IQuantity[index]); + } + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) { + if(index >= VESSEL_RA_COUNT) index -= VESSEL_RA_COUNT; + return(VQuantity[index]); + } +#else + int QuantityU(int index) {return(UQuantity[index]);} + int QuantityI(int index) {return(IQuantity[index]);} + int QuantityA(int index) {return(AQuantity[index]);} + int QuantityV(int index) {return(VQuantity[index]);} +#endif + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[256]; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/housecolor.h b/code/redalert/housecolor.h new file mode 100644 index 0000000..13cedd6 --- /dev/null +++ b/code/redalert/housecolor.h @@ -0,0 +1,58 @@ +// HOUSECOLOR.H +// + +#include "vectormath.h" + +struct HouseColorHD { + HouseColorHD(float3 lowerbounds, + float3 upperbounds, + float fudge, + float3 hsvshift, + float3 inputlevels, + float2 outputlevels, + float3 overallinputlevels, + float2 overalloutputlevels, + float3 radarmapcolor) + { + this->LowerBounds = lowerbounds; + this->UpperBounds = upperbounds; + this->Fudge = fudge; + this->HSVShift = hsvshift; + this->InputLevels = inputlevels; + this->OutputLevels = outputlevels; + this->OverallInputLevels = overallinputlevels; + this->OverallOutputLevels = overalloutputlevels; + this->RadarMapColor = radarmapcolor; + } + + float3 LowerBounds; + float3 UpperBounds; + float Fudge; + float3 HSVShift; + float3 InputLevels; + float2 OutputLevels; + float3 OverallInputLevels; + float2 OverallOutputLevels; + float3 RadarMapColor; +}; + +// This data has been sourced from the TD Original Skirmish Color mod by Pokey +// +static HouseColorHD *houseColors[8] = { + // Spain + new HouseColorHD(float3(0.26, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.713,-0.621, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.6421568, 0.4784698, 0.4784698)), + // Greece + new HouseColorHD(float3(0.264706f, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.310000f,-0.211000, 0.005000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0980f, 0.3922, 0.9216)), + // USSR + new HouseColorHD(float3(0.264706f, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.689000f,0.382000, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.756863f, 0.0, 0.015686)), + // England + new HouseColorHD(float3(0.26f, 1.0f, 1.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.777f,-0.196, -0.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.737255, 0.627451, 0.239216)), + // Ukraine + new HouseColorHD(float3(0.26, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.726,0.119, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.772549, 0.380392, 0.07451)), + // Germany + new HouseColorHD(float3(0.26, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.911f,-0.381, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.184314, 0.67451, 0.141176)), + // France + new HouseColorHD(float3(0.26, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.119,-0.233, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.1151961, 0.6911765, 0.4031861)), + // Turkey + new HouseColorHD(float3(0.26, 1.0f, 0.0f), float3(0.0f, 1.0f, 0.22f), 0, float3(0.685,-0.151, 0.000000), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.0f, 1.0f, 1.0f), float2(0.0f, 1.0f), float3(0.5784314, 0.1155861, 0.05387352)), +}; \ No newline at end of file diff --git a/code/redalert/hsv.cpp b/code/redalert/hsv.cpp new file mode 100644 index 0000000..e0862a4 --- /dev/null +++ b/code/redalert/hsv.cpp @@ -0,0 +1,208 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HSV.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HSV.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * HSVClass::Set -- Set the palette for this color object. * + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "hsv.h" +#include "rgb.h" + +HSVClass const HSVClass::BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * HSVClass::Adjust -- Adjust an HSV color toward specified color. * + * * + * This routine will adjust the HSV color object toward the color of the specified HSV * + * object. Typical users of this would be palette morphing or fading routines. * + * * + * INPUT: ratio -- The ratio to move the HSV object toward the color specified. A value * + * of zero means no movement at all. A value of 255 means move completely * + * toward the specified color (changed completely). * + * * + * hsv -- A reference to the color that the current HSV object is to change * + * toward. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Adjust(int ratio, HSVClass const & hsv) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = hsv.Value_Component() - Value_Component(); + Value = Value_Component() + (value * ratio) / 256; + + int saturation = hsv.Saturation_Component() - Saturation_Component(); + Saturation = Saturation_Component() + (saturation * ratio) / 256; + + int hue = hsv.Hue_Component() - Hue_Component(); + Hue = Hue_Component() + (hue * ratio) / 256; +} + + +/*********************************************************************************************** + * HSVClass::Difference -- Finds the difference between two HSV color objects. * + * * + * This routine will determine a color difference between two HSV objects. The difference * + * has no particular meaning other that larger numbers meaning greater difference. * + * * + * INPUT: hsv -- The other HSV object to compare this HSV object to. * + * * + * OUTPUT: Returns with a relative distance (in arbitrary units) between this HSV object and * + * the HSV object supplied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +int HSVClass::Difference(HSVClass const & hsv) const +{ + int hue = (int)Hue - (int)hsv.Hue; + if (hue < 0) hue = -hue; + + int saturation = (int)Saturation - (int)hsv.Saturation; + if (saturation < 0) saturation = -saturation; + + int value = (int)Value - (int)hsv.Value; + if (value < 0) value = -value; + + return(hue*hue + saturation*saturation + value*value); +} + + +/*********************************************************************************************** + * HSVClass::operator RGBClass -- Conversion operator for RGBClass object. * + * * + * This conversion operator will convert the HSV object into an RGB object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implied) of the RGBClass object that most closely * + * matches this HSVClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +HSVClass::operator RGBClass (void) const +{ + unsigned int i; // Integer part. + unsigned int f; // Fractional or remainder part. f/HSV_BASE gives fraction. + unsigned int tmp; // Temporary variable to help with calculations. + unsigned int values[7]; // Possible rgb values. Don't use zero. + + int hue = Hue_Component(); + int saturation = Saturation_Component(); + int value = Value_Component(); + int red, green, blue; + + + hue *= 6; + f = hue % 255; + + // Set up possible red, green and blue values. + values[1] = + values[2] = value; + + // + // The following lines of code change + // values[3] = (v * (255 - ( (s * f) / 255) )) / 255; + // values[4] = values[5] = (v * (255 - s)) / 255; + // values[6] = (v * (255 - (s * (255 - f)) / 255)) / 255; + // so that the are rounded divides. + // + + tmp = (saturation * f) / 255; + values[3] = (value * (255 - tmp)) / 255; + + values[4] = + values[5] = (value * (255 - saturation)) / 255; + + tmp = 255 - (saturation * (255 - f)) / 255; + values[6] = (value * tmp) / 255; + + + // This should not be rounded. + i = hue / 255; + + i += (i > 4) ? -4 : 2; + red = values[i]; + + i += (i > 4) ? -4 : 2; + blue = values[i]; + + i += (i > 4) ? -4 : 2; + green = values[i]; + + return(RGBClass(red, green, blue)); +} + + +/*********************************************************************************************** + * HSVClass::Set -- Set the palette for this color object. * + * * + * The palette will be set for this color object. Use this routine to set an arbitrary * + * color index with the HSVClass object. * + * * + * INPUT: color -- The color index to change. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void HSVClass::Set(int color) const +{ + RGBClass rgb = *this; + rgb.Set(color); +} diff --git a/code/redalert/hsv.h b/code/redalert/hsv.h new file mode 100644 index 0000000..17c4ccc --- /dev/null +++ b/code/redalert/hsv.h @@ -0,0 +1,78 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/HSV.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : HSV.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef HSV_H +#define HSV_H + +class RGBClass; +class HSVClass; + +/* +** Each color entry is represented by this class. It holds the values for the color +** attributes. The values are recorded in a range from 0 to 255 with 255 being the +** maximum. +*/ +class HSVClass +{ + private: + static HSVClass const BlackColor; + + public: + HSVClass(void) : Hue(0), Saturation(0), Value(0) {}; + HSVClass(unsigned char hue, unsigned char saturation, unsigned char value) : + Hue(hue), + Saturation(saturation), + Value(value) + {}; + operator RGBClass (void) const; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, HSVClass const & hsv); + int Difference(HSVClass const & hsv) const; + int Hue_Component(void) const {return(Hue);}; + int Saturation_Component(void) const {return(Saturation);}; + int Value_Component(void) const {return(Value);}; + void Set(int color) const; + + private: + unsigned char Hue; + unsigned char Saturation; + unsigned char Value; +}; + +#endif diff --git a/code/redalert/iconlist.cpp b/code/redalert/iconlist.cpp new file mode 100644 index 0000000..f34eaf4 --- /dev/null +++ b/code/redalert/iconlist.cpp @@ -0,0 +1,907 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Iconlist.cpp - created by ajw 07/07/98 + +// IconListClass is ListClass plus the option to include an icon on each line entry, +// the option to have the class maintain its own copies of strings passed to it +// for display, and the option to limit the maximum number of these strings that are +// kept (entries are removed from the top when this maximum is reached). +// Also added: multiple item selection capability. Note that the old selection code +// runs as normal, but it simply not used when it comes time to display. +// Also added: if mem. allocation is being done by this, the ability to break new items +// into multiple lines of text is enabled. +// Also added: extra data can be invisibly stored with each item, if memory allocation is +// being done by this. +// Extra data included 3 item preceding icons, 1 fixed position icon, an extra string, +// an extra void pointer, and a color remapping value. + +#include "iconlist.h" +#include "dibapi.h" + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +//*********************************************************************************************** +IconListClass::IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, + bool bResponsibleForStringAlloc, int iSelectionType, int iMaxItemsSaved ) : + ListClass( id, x, y, w, h, flags, up, down ) +{ + // If bResponsibleForStringAlloc, COPIES of strings are stored in the list. Deletion is + // handled by this class. Icons are different - the caller is responsible for what's on + // the other end of the pointer. + bDoAlloc = bResponsibleForStringAlloc; + // iSelectionType = 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + if( iSelectionType < 0 || iSelectionType > 2 ) iSelectionType = 1; + iSelectType = iSelectionType; + // If iMaxItemsSaved is 0, there is no limit to the number of text lines. The list can grow forever. + // Otherwise items are deleted from the head of the list when the maximum is passed. + // iMaxItemsSaved only applies when bResponsibleForStringAlloc. + iMaxItems = iMaxItemsSaved; +} + +//*********************************************************************************************** +IconListClass::~IconListClass( void ) +{ + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } +} + +/*********************************************************************************************** + * IconListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * pIcon -- Pointer to the shape to add. + * IconKind -- Indicates what type of image pIcon points to. + * szExtraDataString -- Extra string data that gets copied and stored along with item. + * szExtraDataPtr -- Extra data that gets stored along with item. + * pColorRemap -- Points to a color remapping used when drawing the item. + * + * OUTPUT: Returns new item index. * + * WARNINGS: none * + * HISTORY: 07/07/1998 ajw : Created. * + *=============================================================================================*/ +int IconListClass::Add_Item(char const * text) +{ + return Add_Item( text, NULL, NULL, ICON_SHAPE ); +} + +int IconListClass::Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + if( text ) + { + if( bDoAlloc ) + { + int iRetVal; + + char* szText = new char[ strlen( text ) + 51 ]; // 50 extra chars added for line breaks later. + strcpy( szText, text ); + + int iWidthMax, iHeight; + // Stupid usage of globals for font stuff... + if( TextFlags == TPF_TYPE ) + { + void* pFontBefore = Set_Font( TypeFontPtr ); + DWORD FontXSpacingBefore = FontXSpacing; + FontXSpacing = -2; + + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + + Set_Font( pFontBefore ); + FontXSpacing = FontXSpacingBefore; // Just in case it matters... Doubt it. + } + else + { + // Currently never called. Test well if you use IconList with a font other than TPF_TYPE, + // as the character spacing globals get set weirdly, I've found. + int iWidthToClipAt = IsScrollActive ? Width : Width - UpGadget.Width; + // This call will place '\r's in the string where line breaks should occur. + Format_Window_String_New( text, iWidthToClipAt, iWidthMax, iHeight, szText, 50 ); + } + + // Each break character causes a line to be added to list. + char szBreakchars[] = "\r\n\v\f"; + char* szToken; + char* szNextChar = szText; + szToken = strtok( szText, szBreakchars ); + while( szToken ) + { + while( szNextChar < szToken ) + { + // We expected szToken to begin at szNextChar. Since it doesn't, extra break + // characters must have been removed by strtok as they were adjacent. We want + // a line break for every break character, so add lines for each space that + // szNextChar is off by. + szNextChar++; + Add_Item_Detail( " ", szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + } + iRetVal = Add_Item_Detail( szToken, szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); + + // Expect next token two chars after the end of this one. + szNextChar = szToken + strlen( szToken ) + 1; + + // Get next token. + szToken = strtok( NULL, szBreakchars ); + } + delete [] szText; + return iRetVal; // Last value returned by ListClass::Add_Item + } + else + { + // Add one item to list. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + return ListClass::Add_Item( text ); + } + } + else + { + // (no text for new item) + if( pIcon0 || pIcon1 || pIcon2 ) + { + // Note: Cannot add an entry without text unless string allocation is being handled by me. + // Otherwise, because we want the icon to show up, create a blank entry for the ListClass. + if( bDoAlloc ) + { + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraDataPtr; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create new string, essentially blank. + char* szText = new char[2]; + strcpy( szText, " " ); + return ListClass::Add_Item( szText ); + } + else + // Cannot add entry, as text is blank and ListClass::Add_Item will do nothing. + // The Icon we want will not show up. + return List.Count() - 1; + } + else + return ListClass::Add_Item( text ); + } +} + +//*********************************************************************************************** +int IconListClass::Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, + void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ) +{ + // Broken out of above function as it is repeated. + + // Add one item to list. + // Too many entries? + if( iMaxItems && List.Count() == iMaxItems ) + { + // Delete head of list. + Remove_Item( 0 ); + } + // Create icon entry. + IconList_ItemExtras* pItemExtra = new IconList_ItemExtras; + pItemExtra->bMultiSelected = false; + pItemExtra->pIcon[0] = pIcon0; // ajw - Question: repeat the icon for each entry? make it optional? + pItemExtra->IconKind[0] = IconKind0; + pItemExtra->pIcon[1] = pIcon1; + pItemExtra->IconKind[1] = IconKind1; + pItemExtra->pIcon[2] = pIcon2; + pItemExtra->IconKind[2] = IconKind2; + pItemExtra->FixedIcon.pIcon = pFixedIcon; + pItemExtra->FixedIcon.IconKind = FixedIconKind; + pItemExtra->FixedIcon.xOffset = iXFixedIcon; + pItemExtra->FixedIcon.yOffset = iYFixedIcon; + pItemExtra->FixedIcon.iWidth = iFixedIconWidth; + pItemExtra->pvExtraData = pvExtraData; + pItemExtra->pColorRemap = pColorRemap; + if( szHelp ) + { + // Copy help into new help string. + pItemExtra->szHelp = new char[ strlen( szHelp ) + 1 ]; + strcpy( pItemExtra->szHelp, szHelp ); + } + if( szExtraDataString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szExtraDataString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szExtraDataString ); + } + ExtrasList.Add( pItemExtra ); + // Create text entry. + // Copy text to new string. + char* szTextBit = new char[ strlen( szToken ) + 1 ]; + strcpy( szTextBit, szToken ); + return ListClass::Add_Item( szTextBit ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text ) +{ + return Add_Item( Text_String(text), NULL, NULL, ICON_SHAPE ); +} + +//*********************************************************************************************** +int IconListClass::Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString /* = NULL */, + void* pvExtraDataPtr /* = NULL */, RemapControlType* pColorRemap /* = NULL */, + void* pIcon1 /* = NULL */, ICONKIND IconKind1 /* = ICON_SHAPE */, + void* pIcon2 /* = NULL */, ICONKIND IconKind2 /* = ICON_SHAPE */, + void* pFixedIcon /* = NULL */, ICONKIND FixedIconKind /* = ICON_SHAPE */, int iXFixedIcon /* = 0 */, int iYFixedIcon /* = 0 */, int iFixedIconWidth /* = -1 */ ) +{ + return Add_Item( Text_String(text), szHelp, pIcon0, IconKind0, szExtraDataString, pvExtraDataPtr, pColorRemap, + pIcon1, IconKind1, pIcon2, IconKind2, pFixedIcon, FixedIconKind, iXFixedIcon, iYFixedIcon, iFixedIconWidth ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( char const * text ) +{ + if( text ) + Remove_Item( List.ID(text) ); +} + +//*********************************************************************************************** +void IconListClass::Remove_Item( int index ) +{ + if( (unsigned)index < List.Count() ) + { + delete (IconList_ItemExtras*)ExtrasList[ index ]; + ExtrasList.Delete( index ); + if( bDoAlloc ) + // Delete alloc'ed string. + delete [] (char*)List[index]; + ListClass::Remove_Item( index ); + + // I should probably put this in ListClass:Remove_Item(), as it seems clearly to be + // missing, but I want to only affect my own new code, to not introduce possible bugs. + // Shift the selected index if appropriate... + if( SelectedIndex >= index ) + { + SelectedIndex--; + if( SelectedIndex < 0 ) + SelectedIndex = 0; + } + } +} + +/*********************************************************************************************** + * IconListClass::Draw_Entry -- Calls ListClass::Draw_Entry, then adds icon. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 07/07/1998 ajw: Created. * + *=============================================================================================*/ + +#define PREICONGAP 1 +#define ICONTEXTGAP 2 + +void IconListClass::Draw_Entry( int index, int x, int y, int width, int selected ) +{ + IconList_ItemExtras* pExtras = (IconList_ItemExtras*)ExtrasList[ index ]; + + int xText = x; + // ajw If I end up needing to use SHAPEs for icons, figure out shape width here and offset x. + bool bIconsPresent = false; + for( int iIcon = 0; iIcon != 3; iIcon++ ) + if( pExtras->pIcon[ iIcon ] && pExtras->IconKind[ iIcon ] == ICON_DIB ) + { + // Push text over to accommodate icon. + int iWidthIcon = PREICONGAP + DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + xText += iWidthIcon; + width -= iWidthIcon; + bIconsPresent = true; + } + if( bIconsPresent ) + { + xText += ICONTEXTGAP; + width -= ICONTEXTGAP; + } + + RemapControlType* pRemap = pExtras->pColorRemap; + if( !pRemap ) + { + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + // See local version of this same hack, below. + int TempTabs; + const int* TabsSave; + if( Tabs ) + { + TempTabs = *Tabs - ( xText - x ); + TabsSave = Tabs; + Tabs = &TempTabs; + } + switch( iSelectType ) + { + case 0: + // Don't draw any items selected (even if they are, really, in ListClass). + ListClass::Draw_Entry( index, xText, y, width, false ); + break; + case 1: + ListClass::Draw_Entry( index, xText, y, width, selected ); + break; + case 2: + // Ignore 'selected' parameter. We use our own records. + ListClass::Draw_Entry( index, xText, y, width, pExtras->bMultiSelected ); + break; + } + // Restore Tabs. + if( Tabs ) + Tabs = TabsSave; + } + else + { + // Use different color remapping. + // This is largely copied straight from ListClass::Draw_Entry()... + + TextPrintType flags = TextFlags; + + bool bShowSelected; + + switch( iSelectType ) + { + case 0: + bShowSelected = false; + break; + case 1: + bShowSelected = selected; + break; + case 2: + bShowSelected = pExtras->bMultiSelected; + break; + } + + if( bShowSelected ) + { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect( xText, y, xText + width - 1, y + LineHeight - 1, pRemap->Shadow ); + } + else + { + if (!(flags & TPF_USE_GRAD_PAL)) + { + flags = flags | TPF_MEDIUM_COLOR; + } + } + // Tabs hack. If there are icons, and a tab, push back the FIRST tab appropriately. + // (Ignore others. This is a hack because having more than one tab will now break this.) + if( Tabs ) + { + int tab = *Tabs - ( xText - x ); + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, &tab ); + } + else + Conquer_Clip_Text_Print( List[index], xText, y, pRemap, TBLACK, flags, width, NULL ); + } + + // Draw fixed position icon. + if( pExtras->FixedIcon.pIcon ) + { + if( pExtras->FixedIcon.IconKind == ICON_SHAPE ) + CC_Draw_Shape( pExtras->FixedIcon.pIcon, 0, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + CC_Draw_DIB( (char*)pExtras->FixedIcon.pIcon, x + pExtras->FixedIcon.xOffset, y + pExtras->FixedIcon.yOffset, pExtras->FixedIcon.iWidth, WINDOW_MAIN ); + } + + // Draw variable position left-of-text icons. + for( iIcon = 0; iIcon != 3; iIcon++ ) + { + if( pExtras->pIcon[ iIcon ] ) + { + x += PREICONGAP; + if( pExtras->IconKind[ iIcon ] == ICON_SHAPE ) + CC_Draw_Shape( pExtras->pIcon[ iIcon ], 0, x, y, WINDOW_MAIN, SHAPE_NORMAL ); + // Put similar code in here for shapes if used... + else + { + CC_Draw_DIB( (char*)pExtras->pIcon[ iIcon ], x, y, 9999, WINDOW_MAIN ); + x += DIBWidth( (char*)pExtras->pIcon[ iIcon ] ); + } + } + } +} + +//*********************************************************************************************** +int IconListClass::Action(unsigned flags, KeyNumType & key) +{ + // Overriding of function is for the sake of MultiSelecting only. + if( iSelectType == 2 ) + { + if( !( flags & LEFTRELEASE ) ) + { + if( !( flags & KEYBOARD ) ) + { + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + int iSelected = CurrentTopIndex + index; + iSelected = min( iSelected, List.Count() - 1 ); + if( iSelected >= 0 ) + ((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected = + !((IconList_ItemExtras*)ExtrasList[ iSelected ])->bMultiSelected; + } + } + } + return ListClass::Action( flags, key ); +} + +//*********************************************************************************************** +// * IconListClass::Show_Last_Item -- Scrolls listbox down to ensure that last entry is visible. +// ajw 07/09/98 +void IconListClass::Show_Last_Item() +{ + int iItemLast = List.Count() - 1; + if( iItemLast - LineCount + 1 != CurrentTopIndex ) + { + Flag_To_Redraw(); + Set_View_Index( iItemLast - LineCount + 1 ); + } +} + +//*********************************************************************************************** +bool IconListClass::bItemIsMultiSelected( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected; + else + return false; +} + +//*********************************************************************************************** +void IconListClass::MultiSelect( int index, bool bSelect ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->bMultiSelected = bSelect; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_ExtraDataString( int index ) const +{ + // Returns const pointer to the hidden "extra data" string that can be associated with each item. + // This is NULL if no extra data was assigned. + if( index < ExtrasList.Count() && index > -1 ) + { + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szExtraData; + } + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataString( int index, const char* szNewString ) +{ + if( index < ExtrasList.Count() && index > -1 ) + { + IconList_ItemExtras* pItemExtra = (IconList_ItemExtras*)ExtrasList[ index ]; + if( pItemExtra->szExtraData ) + { + // Delete the existing string. + delete [] pItemExtra->szExtraData; + } + if( szNewString ) + { + // Copy special data string into new extradata string. + pItemExtra->szExtraData = new char[ strlen( szNewString ) + 1 ]; + strcpy( pItemExtra->szExtraData, szNewString ); + } + else + pItemExtra->szExtraData = NULL; + } +} + +//*********************************************************************************************** +void* IconListClass::Get_Item_ExtraDataPtr( int index ) const +{ + // Returns the hidden "extra data" void pointer that can be associated with each item. + // This is NULL if no value was assigned. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_ExtraDataPtr( int index, void* pNewValue ) +{ + // Sets the hidden "extra data" void pointer that can be associated with each item. + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pvExtraData = pNewValue; +} + +//*********************************************************************************************** +const IconList_ItemExtras* IconListClass::Get_ItemExtras( int index ) const +{ + if( index < ExtrasList.Count() && index > -1 ) + return (IconList_ItemExtras*)ExtrasList[ index ]; + else + return NULL; +} + +//*********************************************************************************************** +const char* IconListClass::Get_Item_Help( int index ) const +{ + // Returns pointer to the string allocated for tooltip help. + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->szHelp; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Clear() +{ + // Removes all items from list. + + // Delete the IconList_ItemExtras structs created to hold extra info on each item. + for( int i = 0; i < ExtrasList.Count(); i++ ) + delete (IconList_ItemExtras*)ExtrasList[ i ]; + ExtrasList.Clear(); + + if( bDoAlloc ) + { + // Delete all alloc'ed strings. + for( int i = 0; i < List.Count(); i++ ) + delete [] (char*)List[i]; + } + + List.Clear(); + Remove_Scroll_Bar(); + CurrentTopIndex = 0; +} + +//*********************************************************************************************** +RemapControlType* IconListClass::Get_Item_Color( int index ) +{ + if( index < ExtrasList.Count() && index > -1 ) + return ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap; + else + return NULL; +} + +//*********************************************************************************************** +void IconListClass::Set_Item_Color( int index, RemapControlType* pColorRemap ) +{ + if( index < ExtrasList.Count() && index > -1 ) + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pColorRemap = pColorRemap; +} + +//*********************************************************************************************** +int IconListClass::Find( const char* szItemToFind ) +{ + // Returns -1 if szItemToFind is not found as the text BEGINNING one of the list entries, else index of item. + // Compare is case-sensitive. + for( int i = 0; i < List.Count(); i++ ) + { + if( strncmp( List[ i ], szItemToFind, strlen( szItemToFind ) ) == 0 ) + return i; + } + return -1; +} + +//*********************************************************************************************** +int IconListClass::FindColor( RemapControlType* pColorRemap ) +{ + // Returns -1 if no items of specified color are found, else first index. Assumes colorptr == colorptr is a valid equality test. + for( int i = 0; i < List.Count(); i++ ) + { + if( Get_Item_Color( i ) == pColorRemap ) + return i; + } + return -1; +} + +//*********************************************************************************************** +bool IconListClass::Set_Item( unsigned int index, const char* szText ) +{ + // Resets the text string allocated for an item. + if( !bDoAlloc || index >= List.Count() ) + return false; + + // Delete alloc'ed string. + delete [] (char*)List[ index ]; + + // Copy text to new string. + char* szTextNew = new char[ strlen( szText ) + 1 ]; + strcpy( szTextNew, szText ); + + // Reassign List's ptr. + List[ index ] = szTextNew; + + return true; +} + +//*********************************************************************************************** +bool IconListClass::Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ) +{ + if( index >= List.Count() ) + return false; + + // Sets one of the left-aligned icons. + ( (IconList_ItemExtras*)ExtrasList[ index ] )->pIcon[ iIconNumber ] = pIcon; + ( (IconList_ItemExtras*)ExtrasList[ index ] )->IconKind[ iIconNumber ] = IconKind; + return true; +} + +//*********************************************************************************************** +int IconListClass::GetRealWidth() // sigh +{ + if( IsScrollActive ) + return Width + ScrollGadget.Width; + return Width; +} + +//*********************************************************************************************** +void IconListClass::Resize( int x, int y, int w, int h ) +{ + Remove_Scroll_Bar(); // If there is one. + + X = x; + Y = y; + Width = w; + Height = h; + + Set_Position( x, y ); + + LineCount = (h-1) / LineHeight; + + + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + Flag_To_Redraw(); +} + +//*********************************************************************************************** +int IconListClass::IndexUnderMouse() +{ + // Returns index of line that mouse is currently over, or -1 for mouse not hitting valid index. + // Assumes that x position of mouse is already known to be over the iconlist. + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight + CurrentTopIndex; + if( index > List.Count() - 1 || index < 0 ) + return -1; + return index; +} + +//*********************************************************************************************** +int IconListClass::OffsetToIndex( int iIndex, int y ) +{ + // Finds the current offset of item iIndex from the current top view index, in pixels, and add it to y. + return y + ( iIndex - CurrentTopIndex ) * LineHeight; +} + +//*********************************************************************************************** +//*********************************************************************************************** +// * Format_Window_String_New +// Functions like Format_Window_String except it fixes an infinite loop bug that occurred when strings +// lacked suitable break points, eliminates the '@' as an escape character, and operates differently +// in that it leaves the original string along, writing results instead to a second string parameter, +// that is iExtraChars longer than the original string. This is all a big hack so that I can insert +// extra break characters when a break in a long single word has to be made. +// Hey - it's better than an infinite loop that forces you to reset your machine, as in the original code... + +int Format_Window_String_New( const char* string, int maxlinelen, int& width, int& height, char* szReturn, int iExtraChars ) +{ + int linelen; + int lines = 0; + width = 0; + height = 0; + + // In no string was passed in, then there are no lines. + if (!string) return(0); + + // While there are more letters left divide the line up. + while (*string) { + linelen = 0; + height += FontHeight + FontYSpacing; + lines++; + + // While the current line is less then the max length... + *szReturn = *string; + linelen += Char_Pixel_Width( *string ); + while ( linelen < maxlinelen && *string != '\r' && *string != '\0' ) + { + *++szReturn = *++string; + linelen += Char_Pixel_Width( *string ); + } + + // if the line is too long... + if (linelen >= maxlinelen) + { + /* + ** Back up to an appropriate location to break. + */ + const char* stringOverEnd = string; + while( linelen > 0 && *string != ' ' && *string != '\r' && *string != '\0' ) + { + linelen -= Char_Pixel_Width(*string--); + } + if( linelen <= 0 ) + { + // We could not find a nice break point. + // Go back one char from over-the-end point and add in a break there. + string = stringOverEnd - 1; + if( iExtraChars > 0 ) + iExtraChars--; // One less to make use of later. + else + // We've used up all our extras characters. + // Put in a break below by wiping out a valid char here. + szReturn--; + } + else + { + // Back up szReturn to same location. + szReturn -= ( stringOverEnd - string ); + } + } + + /* + ** Record the largest width of the worst case string. + */ + if (linelen > width) { + width = linelen; + } + + /* + ** Force a break at the end of the line. + */ + if (*string) { + *szReturn++ = '\r'; + string++; + } + } + return(lines); +} + +//*********************************************************************************************** +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ) +{ + // A very basic DIB drawing routine. No clipping. No edge of window overrun checking. + // If iWidth is too large, default width of dib is used. + // If iWidth is negative, dib isn't drawn. + if( pDIB && iWidth >= 0 ) + { + + int iWidthDIB = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + const char* pBits = FindDIBBits( pDIB ); + + int iSrcPitch = ( iWidthDIB + 3 ) & ~3; + + if( iWidth > iWidthDIB ) + iWidth = iWidthDIB; + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iDestPitch = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineDest = (char*)draw_window.Get_Offset() + xDest + ( yDest + iHeight - 1 ) * iDestPitch; + + const char* pLineSrc = pBits; + for( int y = 0; y != iHeight; y++ ) + { + char* pDest = pLineDest; + const char* pSrc = pLineSrc; + for( int x = 0; x != iWidth; x++ ) + { + *pDest++ = *pSrc++; + } + pLineDest -= iDestPitch; + pLineSrc += iSrcPitch; + } + draw_window.Unlock(); + } + } +// else +// debugprint( "CC_Draw_DIB bad case ------------ pDib %i, iWidth %i\n", pDIB, iWidth ); +} + + +#endif diff --git a/code/redalert/iconlist.h b/code/redalert/iconlist.h new file mode 100644 index 0000000..5f5b081 --- /dev/null +++ b/code/redalert/iconlist.h @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * IconListClass -- Exactly like ListClass, but displays an icon as well + * (actually a 'shape' image), left-aligned, covering + * any text that happens to be there... + * Also, I've added the option of making this class + * responsible for the mem alloc. of the strings, and + * an automatic limiting of entries to a set maximum. + * * + * HISTORY: 07/07/1998 ajw : Created, largely in hack mode. * + *=========================================================================*/ + +#ifndef ICONLIST_H +#define ICONLIST_H + +#include "function.h" +#include "vector.h" + +enum ICONKIND +{ + ICON_SHAPE = 0, // pIcon points to a shape. + ICON_DIB // pIcon points to DIBitmap data. +}; + +struct FIXEDICON // For putting icons in list entries at a specific fixed offset. +{ + void* pIcon; + ICONKIND IconKind; + int xOffset; + int yOffset; + int iWidth; +}; + +struct IconList_ItemExtras +{ + IconList_ItemExtras() : bMultiSelected( false ), szHelp( NULL ), szExtraData( NULL ), pvExtraData( NULL ) + { + pIcon[0] = NULL; + pIcon[1] = NULL; + pIcon[2] = NULL; + } + virtual ~IconList_ItemExtras() + { + delete [] szHelp; + delete [] szExtraData; + } + + bool bMultiSelected; // True if selected when bMultiSelect is on. + void* pIcon[3]; // Icon that appears before an item. + ICONKIND IconKind[3]; // Specifies what kind of image data pIcon points to. + char* szHelp; // Tooltip help string that can be associated with item. Allocated and deleted here. + char* szExtraData; // Extra string that can be associated with item. Allocated and deleted here. + void* pvExtraData; // Hidden pointer that can be associated with item. + RemapControlType* pColorRemap; // Pointer to a color remap, or null for default colored text. + FIXEDICON FixedIcon; +}; + +class IconListClass : public ListClass +{ + public: + IconListClass( int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down, bool bResponsibleForStringAlloc = FALSE, int iSelectionType = 1, int iMaxItemsSaved = 0 ); +// IconListClass( const IconListClass& list ); + virtual ~IconListClass( void ); + + virtual int Add_Item( char const * text ); + virtual int Add_Item( const char* text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + + virtual int Add_Item( int text ); + virtual int Add_Item( int text, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString = NULL, + void* pvExtraDataPtr = NULL, RemapControlType* pColorRemap = NULL, + void* pIcon1 = NULL, ICONKIND IconKind1 = ICON_SHAPE, + void* pIcon2 = NULL, ICONKIND IconKind2 = ICON_SHAPE, + void* pFixedIcon = NULL, ICONKIND FixedIconKind = ICON_SHAPE, int iXFixedIcon = 0, int iYFixedIcon = 0, int iFixedIconWidth = -1 ); + +// virtual int Add_Scroll_Bar(void); +// virtual void Bump(int up); +// virtual int Count(void) const {return List.Count();}; +// virtual int Current_Index(void) const; +// virtual char const * Current_Item(void) const; +// virtual int Draw_Me(int forced); +// virtual char const * Get_Item(int index) const; +// virtual int Step_Selected_Index(int forward); +// virtual void Flag_To_Redraw(void); + +// virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item( char const * text ); + virtual void Remove_Item( int ); +// virtual int Remove_Scroll_Bar(void); +// virtual void Set_Selected_Index(int index); +// virtual void Set_Selected_Index(char const * text); +// virtual void Set_Tabs(int const * tabs); +// virtual int Set_View_Index(int index); +// virtual void Step(int up); +// virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ +// virtual LinkClass & Add(LinkClass & object); +// virtual LinkClass & Add_Tail(LinkClass & object); +// virtual LinkClass & Add_Head(LinkClass & object); +// virtual GadgetClass * Remove(void); + + virtual void Show_Last_Item(); + virtual bool bItemIsMultiSelected( int index ) const; + virtual void MultiSelect( int index, bool bSelect ); + virtual const char* Get_Item_ExtraDataString( int index ) const; + virtual void Set_Item_ExtraDataString( int index, const char* szNewString ); + virtual void* Get_Item_ExtraDataPtr( int index ) const; + virtual void Set_Item_ExtraDataPtr( int index, void* pNewValue ); + const char* Get_Item_Help( int index ) const; + virtual RemapControlType* Get_Item_Color( int index ); + virtual void Set_Item_Color( int index, RemapControlType* pColorRemap ); + virtual const IconList_ItemExtras* Get_ItemExtras( int index ) const; + virtual void Clear(); + virtual int Get_View_Index() { return CurrentTopIndex; } + bool bScrollBeingDragged() + { + // Returns true if the scroll bar of the list is being dragged by the user. + return ( GadgetClass::StuckOn == &ScrollGadget ); + } + + virtual int Find( const char* szItemToFind ); + virtual int FindColor( RemapControlType* pColorRemap ); + + virtual bool Set_Item( unsigned int index, const char* szText ); + virtual bool Set_Icon( unsigned int index, unsigned int iIconNumber, void* pIcon, ICONKIND IconKind ); + + virtual int GetRealWidth(); + virtual void Resize( int x, int y, int w, int h ); + virtual int IndexUnderMouse(); + virtual int OffsetToIndex( int iIndex, int y ); + + virtual int SetSelectType( int iSelectTypeNew ) + { + // Provided to enable horrible hacks, mainly involved with dealing with ListClass's inability + // to have no item selected... + int iSelectTypeOld = iSelectType; + iSelectType = iSelectTypeNew; + return iSelectTypeOld; + } + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry( int index, int x, int y, int width, int selected ); + + virtual int Add_Item_Detail( const char* szToken, const char* szHelp, + void* pIcon0, ICONKIND IconKind0, const char* szExtraDataString, + void* pvExtraData, RemapControlType* pColorRemap, + void* pIcon1, ICONKIND IconKind1, void* pIcon2, ICONKIND IconKind2, + void* pFixedIcon, ICONKIND FixedIconKind, int iXFixedIcon, int iYFixedIcon, int iFixedIconWidth ); + + // The list of Icons. + //DynamicVectorClass IconList; + //DynamicVectorClass< IconList_ItemExtras* > ExtrasList; ajw: creates hellacious linking problems + DynamicVectorClass< void* > ExtrasList; + + bool bDoAlloc; // True if I am responsible for mem. allocation/deletion of strings. +// bool bMultiSelect; // True if we are using the multiple item selection feature. + int iSelectType; // 0 for no selection shown, 1 for normal ListClass selection, 2 for n multiple selections + int iMaxItems; // Number of items to limit list to, if bDoAlloc is true. +}; + +#endif + +#endif diff --git a/code/redalert/idata.cpp b/code/redalert/idata.cpp new file mode 100644 index 0000000..2efc287 --- /dev/null +++ b/code/redalert/idata.cpp @@ -0,0 +1,1530 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IDATA.CPP 3 3/16/97 10:16p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * InfantryTypeClass::Get_Cameo_Data -- Fetches the small cameo shape for sidebar strip. * + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry obj* + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + +static DoInfoStruct DogDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // NA + {8, 6, 6}, // DO_WALK + {104, 14,14}, // DO_FIRE_WEAPON + {0, 0, 0}, // DO_LIE_DOWN // NA + {56, 6, 6}, // DO_CRAWL + {0, 0, 0}, // DO_GET_UP + {104, 14,14}, // DO_FIRE_PRONE + {216, 18,0}, // DO_IDLE1 + {216, 18,0}, // DO_IDLE2 + {235, 7, 0}, // DO_GUN_DEATH + {242, 9, 0}, // DO_EXPLOSION_DEATH + {242, 9, 0}, // DO_EXPLOSION2_DEATH + {242, 9, 0}, // DO_GRENADE_DEATH + {251, 14,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {106, 12,14}, // DO_DOG_MAUL +}; + + +// +// For the virtual do controls, we are using the TD infantry asset animation frame numbers, take out the -94 frame offsets that were added in RA +// + +static DoInfoStruct E1DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1, 8}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 6, 8}, // DO_FIRE_PRONE + {256, 16,0}, // DO_IDLE1 + {272, 16,0}, // DO_IDLE2 + {382, 8, 0}, // DO_GUN_DEATH + {398, 8, 0}, // DO_EXPLOSION_DEATH + {398, 8, 0}, // DO_EXPLOSION2_DEATH + {406, 12,0}, // DO_GRENADE_DEATH + {418, 18,0}, // DO_FIRE_DEATH + {436, 3, 3}, // DO_GESTURE1 + {460, 3, 3}, // DO_SALUTE1 + {484, 3, 3}, // DO_GESTURE2 + {508, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E2DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {288, 1, 12}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 20,20}, // DO_FIRE_WEAPON + {224, 2, 2}, // DO_LIE_DOWN + {240, 4, 4}, // DO_CRAWL + {272, 2, 2}, // DO_GET_UP + {288, 8, 12}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510, 8, 0}, // DO_GUN_DEATH + {526, 8, 0}, // DO_EXPLOSION_DEATH + {526, 8, 0}, // DO_EXPLOSION2_DEATH + {534, 12,0}, // DO_GRENADE_DEATH + {546, 18,0}, // DO_FIRE_DEATH + {564, 3, 3}, // DO_GESTURE1 + {588, 3, 3}, // DO_SALUTE1 + {612, 3, 3}, // DO_GESTURE2 + {636, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E3DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1,10}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 10,10}, // DO_FIRE_PRONE + {272, 16,0}, // DO_IDLE1 + {288, 16,0}, // DO_IDLE2 + {398, 8, 0}, // DO_GUN_DEATH + {414, 8, 0}, // DO_EXPLOSION_DEATH + {414, 8, 0}, // DO_EXPLOSION2_DEATH + {422, 12,0}, // DO_GRENADE_DEATH + {434, 18,0}, // DO_FIRE_DEATH + {452, 3, 3}, // DO_GESTURE1 + {476, 3, 3}, // DO_SALUTE1 + {500, 3, 3}, // DO_GESTURE2 + {524, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E4DoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {256, 1,16}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 16,16}, // DO_FIRE_WEAPON + {192, 2, 2}, // DO_LIE_DOWN + {208, 4, 4}, // DO_CRAWL + {240, 2, 2}, // DO_GET_UP + {256, 16,16}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510, 8, 0}, // DO_GUN_DEATH + {526, 8, 0}, // DO_EXPLOSION_DEATH + {526, 8, 0}, // DO_EXPLOSION2_DEATH + {534, 12,0}, // DO_GRENADE_DEATH + {546, 18,0}, // DO_FIRE_DEATH + {564, 3, 3}, // DO_GESTURE1 + {588, 3, 3}, // DO_SALUTE1 + {612, 3, 3}, // DO_GESTURE2 + {636, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + + + +static DoInfoStruct E1DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1, 8}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 6, 8}, // DO_FIRE_PRONE + {256, 16,0}, // DO_IDLE1 + {272, 16,0}, // DO_IDLE2 + {382-94, 8, 0}, // DO_GUN_DEATH + {398-94, 8, 0}, // DO_EXPLOSION_DEATH + {398-94, 8, 0}, // DO_EXPLOSION2_DEATH + {406-94, 12,0}, // DO_GRENADE_DEATH + {418-94, 18,0}, // DO_FIRE_DEATH + {436-94, 3, 3}, // DO_GESTURE1 + {460-94, 3, 3}, // DO_SALUTE1 + {484-94, 3, 3}, // DO_GESTURE2 + {508-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E2DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {288, 1, 12}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 20,20}, // DO_FIRE_WEAPON + {224, 2, 2}, // DO_LIE_DOWN + {240, 4, 4}, // DO_CRAWL + {272, 2, 2}, // DO_GET_UP + {288, 8, 12}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E3DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {192, 1,10}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 10,10}, // DO_FIRE_PRONE + {272, 16,0}, // DO_IDLE1 + {288, 16,0}, // DO_IDLE2 + {398-94, 8, 0}, // DO_GUN_DEATH + {414-94, 8, 0}, // DO_EXPLOSION_DEATH + {414-94, 8, 0}, // DO_EXPLOSION2_DEATH + {422-94, 12,0}, // DO_GRENADE_DEATH + {434-94, 18,0}, // DO_FIRE_DEATH + {452-94, 3, 3}, // DO_GESTURE1 + {476-94, 3, 3}, // DO_SALUTE1 + {500-94, 3, 3}, // DO_GESTURE2 + {524-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E4DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {256, 1,16}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 16,16}, // DO_FIRE_WEAPON + {192, 2, 2}, // DO_LIE_DOWN + {208, 4, 4}, // DO_CRAWL + {240, 2, 2}, // DO_GET_UP + {256, 16,16}, // DO_FIRE_PRONE + {384, 16,0}, // DO_IDLE1 + {400, 16,0}, // DO_IDLE2 + {510-94, 8, 0}, // DO_GUN_DEATH + {526-94, 8, 0}, // DO_EXPLOSION_DEATH + {526-94, 8, 0}, // DO_EXPLOSION2_DEATH + {534-94, 12,0}, // DO_GRENADE_DEATH + {546-94, 18,0}, // DO_FIRE_DEATH + {564-94, 3, 3}, // DO_GESTURE1 + {588-94, 3, 3}, // DO_SALUTE1 + {612-94, 3, 3}, // DO_GESTURE2 + {636-94, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + + +static DoInfoStruct E6DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {82, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {67, 2, 2}, // DO_LIE_DOWN + {82, 4, 4}, // DO_CRAWL + {114, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {130, 16,0}, // DO_IDLE1 + {130, 16,0}, // DO_IDLE2 + {146, 8, 0}, // DO_GUN_DEATH + {154, 8, 0}, // DO_EXPLOSION_DEATH + {162, 8, 0}, // DO_EXPLOSION2_DEATH + {170, 12,0}, // DO_GRENADE_DEATH + {182, 18,0}, // DO_FIRE_DEATH + {200, 3, 3}, // DO_GESTURE1 + {224, 3, 3}, // DO_SALUTE1 + {200, 3, 3}, // DO_GESTURE2 + {224, 3, 3}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E7DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {128, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 7, 7}, // DO_FIRE_WEAPON + {113, 2, 2}, // DO_LIE_DOWN + {128, 4, 4}, // DO_CRAWL + {161, 2, 2}, // DO_GET_UP + {176, 7, 7}, // DO_FIRE_PRONE + {232, 17,0}, // DO_IDLE1 + {249, 13,0}, // DO_IDLE2 + {262, 8, 0}, // DO_GUN_DEATH + {270, 8, 0}, // DO_EXPLOSION_DEATH + {278, 8, 0}, // DO_EXPLOSION2_DEATH + {286, 12,0}, // DO_GRENADE_DEATH + {298, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +//Spy +static DoInfoStruct SpyDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {144, 1, 4}, // DO_PRONE + {16, 6, 6}, // DO_WALK + {64, 8, 8}, // DO_FIRE_WEAPON + {128, 2, 2}, // DO_LIE_DOWN + {144, 4, 4}, // DO_CRAWL + {176, 2, 2}, // DO_GET_UP + {192, 8, 8}, // DO_FIRE_PRONE + {256, 14,0}, // DO_IDLE1 + {270, 18,0}, // DO_IDLE2 + {288, 8, 0}, // DO_GUN_DEATH + {296, 8, 0}, // DO_EXPLOSION_DEATH + {304, 8, 0}, // DO_EXPLOSION2_DEATH + {312, 12,0}, // DO_GRENADE_DEATH + {324, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct E9DoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {8, 1, 1}, // DO_STAND_GUARD + {72, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {0, 0, 0}, // DO_FIRE_WEAPON + {56, 2, 2}, // DO_LIE_DOWN + {72, 4, 4}, // DO_CRAWL + {108, 2, 2}, // DO_GET_UP + {0, 0, 0}, // DO_FIRE_PRONE + {120, 19,0}, // DO_IDLE1 + {120, 19,0}, // DO_IDLE2 + {139, 8, 0}, // DO_GUN_DEATH + {147, 8, 0}, // DO_EXPLOSION_DEATH + {155, 8, 0}, // DO_EXPLOSION2_DEATH + {163, 12,0}, // DO_GRENADE_DEATH + {175, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct MedicDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {130, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 28,0}, // DO_FIRE_WEAPON + {114, 2, 2}, // DO_LIE_DOWN + {130, 4, 4}, // DO_CRAWL + {162, 2, 2}, // DO_GET_UP + {56, 28,0}, // DO_FIRE_PRONE + {178, 15,0}, // DO_IDLE1 + {178, 15,0}, // DO_IDLE2 + {193, 8, 0}, // DO_GUN_DEATH + {210, 8, 0}, // DO_EXPLOSION_DEATH + {202, 8, 0}, // DO_EXPLOSION2_DEATH + {217, 12,0}, // DO_GRENADE_DEATH + {229, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct GeneralDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {104, 1, 4}, // DO_PRONE + {8, 6, 6}, // DO_WALK + {56, 4, 4}, // DO_FIRE_WEAPON + {88, 2, 2}, // DO_LIE_DOWN + {104, 4, 4}, // DO_CRAWL + {136, 2, 2}, // DO_GET_UP + {152, 4, 4}, // DO_FIRE_PRONE + {184, 26,0}, // DO_IDLE1 + {184, 26,0}, // DO_IDLE2 + {210, 8, 0}, // DO_GUN_DEATH + {226, 8, 0}, // DO_EXPLOSION_DEATH + {218, 8, 0}, // DO_EXPLOSION2_DEATH + {234, 12,0}, // DO_GRENADE_DEATH + {246, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 + {0, 1, 0}, // DO_SALUTE1 + {0, 1, 0}, // DO_GESTURE2 + {0, 1, 0}, // DO_SALUTE2 + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct CivilianDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-85, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {205-85, 4, 4}, // DO_FIRE_PRONE + {189-85, 10,0}, // DO_IDLE1 + {199-85, 6, 0}, // DO_IDLE2 + {152, 8, 0}, // DO_GUN_DEATH + {160, 8, 0}, // DO_EXPLOSION_DEATH + {160, 8, 0}, // DO_EXPLOSION2_DEATH + {168, 12,0}, // DO_GRENADE_DEATH + {180, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct CivilianDoControlsVirtual[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {205, 4, 4}, // DO_FIRE_PRONE + {189, 10,0}, // DO_IDLE1 + {199, 6, 0}, // DO_IDLE2 + {329, 8, 0}, // DO_GUN_DEATH + {337, 8, 0}, // DO_EXPLOSION_DEATH + {337, 8, 0}, // DO_EXPLOSION2_DEATH + {345, 12,0}, // DO_GRENADE_DEATH + {357, 18,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +static DoInfoStruct EinsteinDoControls[DO_COUNT] = { + {0, 1, 1}, // DO_STAND_READY + {0, 1, 1}, // DO_STAND_GUARD + {0, 1, 1}, // DO_PRONE // N/A + {56, 6, 6}, // DO_WALK + {205-92, 4, 4}, // DO_FIRE_WEAPON + {0, 1, 1}, // DO_LIE_DOWN // N/A + {8, 6, 6}, // DO_CRAWL + {0, 1, 1}, // DO_GET_UP // N/A + {0, 0, 0}, // DO_FIRE_PRONE + {104, 16,0}, // DO_IDLE1 + {104, 16,0}, // DO_IDLE2 + {212-92, 8, 0}, // DO_GUN_DEATH + {220-92, 8, 0}, // DO_EXPLOSION_DEATH + {228-92, 12,0}, // DO_EXPLOSION2_DEATH + {228-92, 12,0}, // DO_GRENADE_DEATH + {240-92, 17,0}, // DO_FIRE_DEATH + {0, 1, 0}, // DO_GESTURE1 // N/A + {0, 1, 0}, // DO_SALUTE1 // N/A + {0, 1, 0}, // DO_GESTURE2 // N/A + {0, 1, 0}, // DO_SALUTE2 // N/A + {0, 0, 0}, // DO_DOG_MAUL // N/A +}; + +// Attack dogs +static InfantryTypeClass const Dog( + INFANTRY_DOG, // Infantry type number. + TXT_GUARD_DOG, // Translate name number for infantry type. + "DOG", // INI name for infantry. + 0x0015, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + DogDoControls, + DogDoControls, + 1, // Frame of projectile launch. + 1, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Minigunners +static InfantryTypeClass const E1( + INFANTRY_E1, // Infantry type number. + TXT_E1, // Translate name number for infantry type. + "E1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E1DoControls, + E1DoControlsVirtual, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Grenadiers +static InfantryTypeClass const E2( + INFANTRY_E2, // Infantry type number. + TXT_E2, // Translate name number for infantry type. + "E2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E2DoControls, + E2DoControlsVirtual, + 14, // Frame of projectile launch. + 6, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Bazooka +static InfantryTypeClass const E3( + INFANTRY_E3, // Infantry type number. + TXT_E3, // Translate name number for infantry type. + "E3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E3DoControls, + E3DoControlsVirtual, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Flamethrower +static InfantryTypeClass const E4( + INFANTRY_E4, // Infantry type number. + TXT_E4, // Translate name number for infantry type. + "E4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + E4DoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Renovator +static InfantryTypeClass const E6( + INFANTRY_RENOVATOR, // Infantry type number. + TXT_E6, // Translate name number for infantry type. + "E6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E6DoControls, + E6DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Spy +static InfantryTypeClass const E8( + INFANTRY_SPY, // Infantry type number. + TXT_E8, // Translate name number for infantry type. + "SPY", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + SpyDoControls, + SpyDoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Thief +static InfantryTypeClass const E9( + INFANTRY_THIEF, // Infantry type number. + TXT_THIEF, // Translate name number for infantry type. + "THF", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + E9DoControls, + E9DoControls, + 3, // Frame of projectile launch. + 3, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Tanya +static InfantryTypeClass const E7( + INFANTRY_TANYA, // Infantry type number. + TXT_E7, // Translate name number for infantry type. + "E7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_COMMANDO, // Transport pip shape/color to use. + E7DoControls, + E7DoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Medic( + INFANTRY_MEDIC, // Infantry type number. + TXT_MEDIC, // Translate name number for infantry type. + "MEDI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const General( + INFANTRY_GENERAL, // Infantry type number. + TXT_GENERAL, // Translate name number for infantry type. + "GNRL", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + GeneralDoControls, + GeneralDoControls, + 2, // Frame of projectile launch. + 2, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +// Civilians +static InfantryTypeClass const C1( + INFANTRY_C1, // Infantry type number. + TXT_C1, // Translate name number for infantry type. + "C1", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C2( + INFANTRY_C2, // Infantry type number. + TXT_C2, // Translate name number for infantry type. + "C2", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv2 // pointer to override remap table +); + +static InfantryTypeClass const C3( + INFANTRY_C3, // Infantry type number. + TXT_C3, // Translate name number for infantry type. + "C3", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const C4( + INFANTRY_C4, // Infantry type number. + TXT_C4, // Translate name number for infantry type. + "C4", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + true, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv4 // pointer to override remap table +); + +static InfantryTypeClass const C5( + INFANTRY_C5, // Infantry type number. + TXT_C5, // Translate name number for infantry type. + "C5", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv5 // pointer to override remap table +); + +static InfantryTypeClass const C6( + INFANTRY_C6, // Infantry type number. + TXT_C6, // Translate name number for infantry type. + "C6", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv6 // pointer to override remap table +); + +static InfantryTypeClass const C7( + INFANTRY_C7, // Infantry type number. + TXT_C7, // Translate name number for infantry type. + "C7", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv7 // pointer to override remap table +); + +static InfantryTypeClass const C8( + INFANTRY_C8, // Infantry type number. + TXT_C8, // Translate name number for infantry type. + "C8", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv8 // pointer to override remap table +); + +static InfantryTypeClass const C9( + INFANTRY_C9, // Infantry type number. + TXT_C9, // Translate name number for infantry type. + "C9", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_CIVILIAN, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv9 // pointer to override remap table +); + +// Nikoomba +static InfantryTypeClass const C10( + INFANTRY_C10, // Infantry type number. + TXT_C10, // Translate name number for infantry type. + "C10", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + true, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + RemapCiv10 // pointer to override remap table +); + +static InfantryTypeClass const Einstein( + INFANTRY_EINSTEIN, // Infantry type number. + TXT_EINSTEIN, // Translate name number for infantry type. + "EINSTEIN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + EinsteinDoControls, + 0, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const Delphi( + INFANTRY_DELPHI, // Infantry type number. + TXT_DELPHI, // Translate name number for infantry type. + "DELPHI", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + CivilianDoControls, + CivilianDoControlsVirtual, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +static InfantryTypeClass const DrChan( + INFANTRY_CHAN, // Infantry type number. + TXT_CHAN, // Translate name number for infantry type. + "CHAN", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + false, // Has crawling animation frames? + true, // Is this a civilian? + false, // Does this unit use the override remap table? + true, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + EinsteinDoControls, + EinsteinDoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + +// Shock Trooper +static InfantryTypeClass const ShockTrooper( + INFANTRY_SHOCK, // Infantry type number. + TXT_SHOCKTROOPER, // Translate name number for infantry type. + "SHOK", // INI name for infantry. + -0x0010, // Vertical offset. + 0x0038, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_FULL, // Transport pip shape/color to use. + E4DoControls, + E4DoControls, + 2, // Frame of projectile launch. + 0, // Frame of projectile launch while prone. + 0, // pointer to override remap table + 0x0018 // Horizontal offset. +); + + +static InfantryTypeClass const Mechanic( + INFANTRY_MECHANIC, // Infantry type number. + TXT_MECHANIC, // Translate name number for infantry type. + "MECH", // INI name for infantry. + 0x0035, // Vertical offset. + 0x0010, // Primary weapon offset along turret centerline. + false, // Is this a female type? + true, // Has crawling animation frames? + false, // Is this a civilian? + false, // Does this unit use the override remap table? + false, // Always use the given name for the infantry? + false, // Theater specific graphic image? + PIP_ENGINEER, // Transport pip shape/color to use. + MedicDoControls, + MedicDoControls, + 25, // Frame of projectile launch. + 25, // Frame of projectile launch while prone. + 0 // pointer to override remap table +); +#endif + +/*********************************************************************************************** + * InfantryTypeClass::InfantryTypeClass -- Constructor for infantry type class objects. * + * * + * This routine will construct the infantry type objects. It is use to create the static * + * infantry types that are used to give each of the infantry objects their characteristics. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 02/16/1996 JLB : Greatly simplified. * + *=============================================================================================*/ +InfantryTypeClass::InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * control, + DoInfoStruct const * virtual_control, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap, + int horizontaloffset) + : TechnoTypeClass(RTTI_INFANTRYTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + 0x0000, + 0x0000, + 0x0000, + is_nominal, + true, + true, + true, + false, + false, + is_theater, + false, + true, + true, + 8, + SPEED_FOOT, + horizontaloffset), + IsFemale(is_female), + IsCrawling(is_crawling), + IsCapture(false), + IsFraidyCat(false), + IsCivilian(is_civilian), + IsBomber(false), + IsDog(false), + IsRemapOverride(is_remap_override), + Type(type), + Pip(pip), + DoControls(control), + DoControlsVirtual(virtual_control), + FireLaunch(firelaunch), + ProneLaunch(pronelaunch), + OverrideRemap(override_remap) +{ + /* + ** Forced infantry overrides from the default. + */ + IsCrushable = true; + IsScanner = true; + IsRepairable = false; + IsCrew = false; + Speed = SPEED_FOOT; +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator new -- Allocate an infanty type class object. * + * * + * This will allocate an infantry type class object from the special memory pool of that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the infantry type class object allocated. If there was * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void * InfantryTypeClass::operator new(size_t) +{ + return(InfantryTypes.Alloc()); +} + + +/*********************************************************************************************** + * InfantryTypeClass::operator delete -- Frees an infantry type class object. * + * * + * This will return a previously allocated infantry type class object back to the memory * + * pool from whence it came. * + * * + * INPUT: pointer -- The pointer to the infantry type class object to free. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::operator delete(void * pointer) +{ + InfantryTypes.Free((InfantryTypeClass *)pointer); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Init_Heap -- Initialize the infantry type class heap. * + * * + * This will pre-allocate all known infantry types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Init_Heap(void) +{ + /* + ** These infantry type class objects must be allocated in the exact order that they + ** are specified in the InfantryType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new InfantryTypeClass(E1); + new InfantryTypeClass(E2); + new InfantryTypeClass(E3); + new InfantryTypeClass(E4); + new InfantryTypeClass(E6); + new InfantryTypeClass(E7); + new InfantryTypeClass(E8); + new InfantryTypeClass(E9); + new InfantryTypeClass(Medic); + new InfantryTypeClass(General); + new InfantryTypeClass(Dog); + new InfantryTypeClass(C1); + new InfantryTypeClass(C2); + new InfantryTypeClass(C3); + new InfantryTypeClass(C4); + new InfantryTypeClass(C5); + new InfantryTypeClass(C6); + new InfantryTypeClass(C7); + new InfantryTypeClass(C8); + new InfantryTypeClass(C9); + new InfantryTypeClass(C10); + new InfantryTypeClass(Einstein); + new InfantryTypeClass(Delphi); + new InfantryTypeClass(DrChan); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new InfantryTypeClass(ShockTrooper); + new InfantryTypeClass(Mechanic); +#endif +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_One_Of -- Creates an infantry object. * + * * + * This creates an infantry object, but does not attempt to place it on the map. It is * + * typically used by the scenario editor when an object is needed, but the location has * + * not yet been specified for where it should appear on the map. * + * * + * INPUT: house -- The owner of the infantry object. * + * * + * OUTPUT: Returns with a pointer to the created infantry object. If an object could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * InfantryTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new InfantryClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Create_And_Place -- Creates and places infantry object onto the map. * + * * + * This routine is used by the scenario editor to create and place an infantry object onto * + * the map at the location specified. * + * * + * INPUT: cell -- The cell location to place the infantry object at. * + * * + * house -- The owner of the infantry object. * + * * + * OUTPUT: bool; Was the infantry object successfully created and placed at the location * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + InfantryClass * i = new InfantryClass(Type, house); + if (i != NULL) { + COORDINATE coord = Map[cell].Closest_Free_Spot(Cell_Coord(cell)); + if (coord) { + return(i->Unlimbo(coord, DIR_E)); + } else { + delete i; + } + } + return(false); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Occupy_List -- Returns with default infantry occupation list. * + * * + * This routine will return with a cell offset occupation list for a generic infantry * + * object. This is typically just a single cell since infantry are never bigger than one * + * cell and this routine presumes the infantry is located in the center of the cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a cell offset list for the infantry object as if it were located * + * in the center of a cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +short const * InfantryTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + + return(&_list[0]); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * InfantryTypeClass::Display -- Displays a generic infantry object. * + * * + * This routine is used by the scenario editor to display a generic representation of the * + * infantry object for the scenario editor. It simply draws a single (nice profile) view * + * of the infantry type. * + * * + * INPUT: x,y -- The display coordinates to render the infantry object at. * + * * + * window -- The window that the display coordinates are relative to. * + * * + * house -- The house colors to use when rendering this infantry object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Display(int x, int y, WindowNumberType window, HousesType house) const +{ + if (house != HOUSE_NONE) { + + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = 2; + } + + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Prep_For_Add -- Prepares the scenario editor for adding of infantry object.* + * * + * This routine will prepare the scenario editor so that the infantry objects appear on * + * the object list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::Prep_For_Add(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + Map.Add_To_List(&As_Reference(index)); + } +} +#endif + + +/*********************************************************************************************** + * InfantryTypeClass::From_Name -- Converts an ASCII name into an infantry type number. * + * * + * This routine is used to convert the infantry ASCII name as specified into an infantry * + * type number. This is called from the INI reader routine in the process if creating the * + * infantry objects needed for the scenario. * + * * + * INPUT: name -- The ASCII name to convert into an infantry type number. * + * * + * OUTPUT: Returns with the infantry type number that corresponds to the infantry ASCII name * + * specified. If no match could be found, then INFANTRY_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +InfantryType InfantryTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (InfantryType classid = INFANTRY_FIRST; classid < INFANTRY_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(INFANTRY_NONE); +} + + +/*********************************************************************************************** + * InfantryTypeClass::One_Time -- Performs any one time processing for infantry system. * + * * + * This routine will perform one time processing for the infantry type system. This is * + * generally restricted to loading of the infantry shape data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryTypeClass::One_Time(void) +{ + for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + InfantryTypeClass const * uclass; + CCFileClass file; + + uclass = &As_Reference(index); + + /* + ** Generic shape for all houses load method. + */ + _makepath(fullname, NULL, NULL, uclass->Graphic_Name(), ".SHP"); + + #ifndef NDEBUG + RawFileClass sfile(fullname); + if (sfile.Is_Available()) { + ((void const *&)uclass->ImageData) = Load_Alloc_Data(sfile); + } else { + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->ImageData) = MFCD::Retrieve(fullname); + #endif + + /* + ** The small build image icon sized shapes are always generic. + */ + char buffer[_MAX_FNAME]; + sprintf(buffer, "%.4sICON", uclass->Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + + #ifndef NDEBUG + RawFileClass ifile(fullname); + if (ifile.Is_Available()) { + ((void const *&)uclass->CameoData) = Load_Alloc_Data(ifile); + } else { + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + } + #else + ((void const *&)uclass->CameoData) = MFCD::Retrieve(fullname); + #endif + } +} + + +/*********************************************************************************************** + * InfantryTypeClass::Full_Name -- Fetches the full name text number. * + * * + * This routine will fetch the full name text number for this infantry type. It examines * + * the special custom name flag to determine whether the custom name or the generic name * + * is to be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with text number for the name to give this infantry type object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/29/1995 JLB : Created. * + *=============================================================================================*/ +int InfantryTypeClass::Full_Name(void) const +{ + if (Debug_Map || !IsNominal || Rule.IsNamed || Type == INFANTRY_C10 || Type == INFANTRY_DELPHI || Type == INFANTRY_EINSTEIN) { + return(TechnoTypeClass::Full_Name()); + } + return(TXT_CIVILIAN); +} + + +/*********************************************************************************************** + * InfantryTypeClass::As_Reference -- Fetches a reference to the infantry type specified. * + * * + * Use this routine to convert an infantry type number into a reference to the infantry * + * type class object it represents. * + * * + * INPUT: type -- The infantry type number to convert into a infantry type class object. * + * * + * OUTPUT: Returns with a reference to the infantry type class object specified. * + * * + * WARNINGS: Be sure that the type parameter is legal, otherwise the results are undefined. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +InfantryTypeClass & InfantryTypeClass::As_Reference(InfantryType type) +{ + return(*InfantryTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * InfantryTypeClass::Read_INI -- Fetches infantry override values from the INI database. * + * * + * This routine will retrieve the override values for this infantry type class object from * + * the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to retrieve the data from. * + * * + * OUTPUT: bool; Was the infantry section for this type found and data retrieved from it? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsFraidyCat = ini.Get_Bool(Name(), "Fraidycat", IsFraidyCat); + IsCapture = ini.Get_Bool(Name(), "Infiltrate", IsCapture); + IsBomber = ini.Get_Bool(Name(), "C4", IsBomber); + IsDog = ini.Get_Bool(Name(), "IsCanine", IsDog); + if (IsBomber) IsCapture = true; + if (IsDog) IsLeader = false; + return(true); + } + return(false); +} + + +void InfantryTypeClass::Dimensions(int & width, int & height) const +{ +#ifdef WIN32 + width = 14; + height = 20; +#else + width = 12; + height = 16; +#endif +} \ No newline at end of file diff --git a/code/redalert/image.h b/code/redalert/image.h new file mode 100644 index 0000000..d12f2d8 --- /dev/null +++ b/code/redalert/image.h @@ -0,0 +1,43 @@ +// Image.h +// + +#define MAX_IMAGE_SHAPES 256 +#define MAX_IMAGE_FRAMES 12 +#define MAX_HOUSE_COLORS 8 + +struct HouseImage_t { + unsigned int image[MAX_IMAGE_SHAPES][MAX_IMAGE_FRAMES]; +}; + +struct Image_t { + Image_t(); + ~Image_t(); + + char name[512]; + int64_t namehash; + HouseImage_t HouseImages[MAX_HOUSE_COLORS]; + unsigned int numAnimFrames; + int width; + int height; + int renderwidth[MAX_IMAGE_SHAPES]; + int renderheight[MAX_IMAGE_SHAPES]; + bool perFrameRenderDimen; + int numFrames; + void* IconMapPtr; +}; + +__forceinline Image_t::Image_t() { + IconMapPtr = NULL; + numAnimFrames = 0; + perFrameRenderDimen = false; + memset(HouseImages, 0, sizeof(HouseImages)); +} + +__forceinline Image_t::~Image_t() { +} + +Image_t* Image_LoadImage(const char* name, bool loadAnims = false, bool loadHouseColor = false); +Image_t* Image_CreateImageFrom8Bit(const char* name, int Width, int Height, unsigned char* data, unsigned char *remap = NULL); +Image_t* Find_Image(const char* name); +void Image_Add8BitImage(Image_t* image, int HouseId, int ShapeID, int Width, int Height, unsigned char* data, unsigned char* remap); +bool Image_Add32BitImage(const char* name, Image_t* image, int HouseId, int ShapeID, int frameId); \ No newline at end of file diff --git a/code/redalert/imgutil.h b/code/redalert/imgutil.h new file mode 100644 index 0000000..98f35ea --- /dev/null +++ b/code/redalert/imgutil.h @@ -0,0 +1,206 @@ +// IMGUTIL.H +// + +#ifdef _DEBUG +#pragma optimize( "", on ) +#endif + +// SRGB -> Linear color space conversion. +inline float ByteToLinear(byte p) { + float v; + v = ((float)p) / 255.0f; + v = (double)v >= 0.0404499992728233 ? (float)pow((200.0 * (double)v + 11.0) / 211.0, 2.40000009536743) : (float)((double)v * 25.0 / 323.0); + return v; +} + +inline float ToSRGB(float v) +{ + return (double)v >= 0.0031308 ? (float)((pow((double)v, 0.416666656732559) * 211.0 - 11.0) / 200.0) : (float)((double)v * 323.0 / 25.0); +} + +inline float PixelToHue(float R, float G, float B) { + if (R == G && G == B) + return 0; // 0 makes as good an UNDEFINED value as any + + float r = (float)R / 255.0f; + float g = (float)G / 255.0f; + float b = (float)B / 255.0f; + + float max, min; + float delta; + float hue = 0.0f; + + max = r; min = r; + + if (g > max) max = g; + if (b > max) max = b; + + if (g < min) min = g; + if (b < min) min = b; + + delta = max - min; + + if (r == max) { + hue = (g - b) / delta; + } + else if (g == max) { + hue = 2 + (b - r) / delta; + } + else if (b == max) { + hue = 4 + (r - g) / delta; + } + hue *= 60; + + if (hue < 0.0f) { + hue += 360.0f; + } + return hue; +} + +struct Turple { + Turple() { + memset(this, 0, sizeof(Turple)); + } + Turple(float Item1, float Item2, float Item3) { + this->Item1 = Item1; + this->Item2 = Item2; + this->Item3 = Item3; + this->Item4 = -1; + } + Turple(float Item1, float Item2, float Item3, float Item4) { + this->Item1 = Item1; + this->Item2 = Item2; + this->Item3 = Item3; + this->Item4 = Item4; + } + float Item1; + float Item2; + float Item3; + float Item4; +}; + + +float step(float a, float x) +{ + return x >= a; +} + +inline float4 lerp(float4 a, float4 b, float w) +{ + float4 l; + l.x = a.x + w * (b.x - a.x); + l.y = a.y + w * (b.y - a.y); + l.z = a.z + w * (b.z - a.z); + l.w = a.w + w * (b.w - a.w); + return l; +} + +inline float3 lerp(float3 a, float3 b, float w) +{ + float3 l; + l.x = a.x + w * (b.x - a.x); + l.y = a.y + w * (b.y - a.y); + l.z = a.z + w * (b.z - a.z); + return l; +} + +inline float3 frac(float3 v) +{ + float3 f; + f.x = v.x - floor(v.x); + f.y = v.y - floor(v.y); + f.z = v.z - floor(v.z); + return f; +} + +inline float frac(float v) +{ + return v - floor(v); +} + +inline float lerp(float a, float b, float w) +{ + return a + w * (b - a); +} + + +inline float3 clamp(float3 x, float a, float b) +{ + float3 r; + r.x = max(a, min(b, x.x)); + r.y = max(a, min(b, x.y)); + r.z = max(a, min(b, x.z)); + return r; +} + +inline float saturate(float x) +{ + return max(0, min(1, x)); +} + +inline void RenderTeamPalette(byte* pixel, HouseColorHD &teamColor) { + float linear1 = ByteToLinear(pixel[2]); + float linear2 = ByteToLinear(pixel[1]); + float linear3 = ByteToLinear(pixel[0]); + float num1 = linear1; + float num2 = linear2; + float num3 = (float)linear3; + Turple valueTuple1 = Turple(0.0f, -0.3333333, 0.6666667, -1 ); + Turple valueTuple2; + + if ((double)num2 >= (double)num3) + valueTuple2 = Turple( num2, num3, valueTuple1.Item1, valueTuple1.Item2 ); + else + valueTuple2 = Turple( num3, num2, valueTuple1.Item4, valueTuple1.Item3 ); + + Turple valueTuple3; + if ((double)num1 >= (double)valueTuple2.Item1) + valueTuple3 = Turple(num1, valueTuple2.Item2, valueTuple2.Item3, valueTuple2.Item1); + else + valueTuple3 = Turple(valueTuple2.Item1, valueTuple2.Item2, valueTuple2.Item4, num1); + double num4 = (double)valueTuple3.Item1 - (double)min(valueTuple3.Item4, valueTuple3.Item2); + float num5 = 1E-10f; + float num6 = (float)num4; + float num7 = num5; + float num8 = abs(valueTuple3.Item3 + (float)(((double)valueTuple3.Item4 - (double)valueTuple3.Item2) / (6.0 * (double)num6 + (double)num7))); + float num9 = num6 / (valueTuple3.Item1 + num7); + double num10 = (double)valueTuple3.Item1; + float num11 = num8; + float num12 = num9; + float num13 = (float)num10; + float num14 = PixelToHue(teamColor.LowerBounds.x, teamColor.LowerBounds.y, teamColor.LowerBounds.z) / 360; + float num15 = PixelToHue(teamColor.UpperBounds.x, teamColor.UpperBounds.y, teamColor.UpperBounds.z) / 360; + + if ((double)num11 >= (double)num14 && (double)num15 >= (double)num11) + { + float num16 = (float)((double)num11 / ((double)num15 - (double)num14) * ((double)num15 + (double)teamColor.Fudge - ((double)num14 - (double)teamColor.Fudge))) + teamColor.HSVShift.x; + float t = num12 + teamColor.HSVShift.y; + float num17 = num13 + teamColor.HSVShift.z; + Turple valueTuple4 = Turple(1.0f, 0.6666667, 0.3333333, 3); + Turple valueTuple5 = Turple(abs(frac(num16 + valueTuple4.Item1) * 6 - valueTuple4.Item4), abs(frac(num16 + valueTuple4.Item2) * 6 - valueTuple4.Item4), abs(frac(num16 + valueTuple4.Item3) * 6 - valueTuple4.Item4)); + float num18 = num17 * lerp(valueTuple4.Item1, saturate(valueTuple5.Item1 - valueTuple4.Item1), t); + float num19 = num17 * lerp(valueTuple4.Item1, saturate(valueTuple5.Item2 - valueTuple4.Item1), t); + float num20 = num17 * lerp(valueTuple4.Item1, saturate(valueTuple5.Item3 - valueTuple4.Item1), t); + Turple valueTuple6 = Turple(min(1, max(0.0f, num18 - teamColor.InputLevels.x) / (teamColor.InputLevels.z - teamColor.InputLevels.x)), min(1, max(0.0f, num19 - teamColor.InputLevels.x) / (teamColor.InputLevels.z - teamColor.InputLevels.x)), min(1, max(0.0f, num20 - teamColor.InputLevels.x) / (teamColor.InputLevels.z - teamColor.InputLevels.x))); + valueTuple6.Item1 = (float)pow((double)valueTuple6.Item1, (double)teamColor.InputLevels.y); + valueTuple6.Item2 = (float)pow((double)valueTuple6.Item2, (double)teamColor.InputLevels.y); + valueTuple6.Item3 = (float)pow((double)valueTuple6.Item3, (double)teamColor.InputLevels.y); + num1 = lerp(teamColor.OutputLevels.x, teamColor.OutputLevels.y, valueTuple6.Item1); + num2 = lerp(teamColor.OutputLevels.x, teamColor.OutputLevels.y, valueTuple6.Item2); + num3 = lerp(teamColor.OutputLevels.x, teamColor.OutputLevels.y, valueTuple6.Item3); + } + + Turple valueTuple7 = Turple(min(1, max(0.0f, num1 - teamColor.OverallInputLevels.x) / (teamColor.OverallInputLevels.z - teamColor.OverallInputLevels.x)), min(1, max(0.0f, num2 - teamColor.OverallInputLevels.x) / (teamColor.OverallInputLevels.z - teamColor.OverallInputLevels.x)), min(1, max(0.0f, num3 - teamColor.OverallInputLevels.x) / (teamColor.OverallInputLevels.z - teamColor.OverallInputLevels.x))); + valueTuple7.Item1 = (float)pow((double)valueTuple7.Item1, (double)teamColor.OverallInputLevels.y); + valueTuple7.Item2 = (float)pow((double)valueTuple7.Item2, (double)teamColor.OverallInputLevels.y); + valueTuple7.Item3 = (float)pow((double)valueTuple7.Item3, (double)teamColor.OverallInputLevels.y); + float v1 = lerp(teamColor.OverallOutputLevels.x, teamColor.OverallOutputLevels.y, valueTuple7.Item1); + float v2 = lerp(teamColor.OverallOutputLevels.x, teamColor.OverallOutputLevels.y, valueTuple7.Item2); + float v3 = lerp(teamColor.OverallOutputLevels.x, teamColor.OverallOutputLevels.y, valueTuple7.Item3); + pixel[2] = (byte)((double)ToSRGB(v1) * (double)255.0f); + pixel[1] = (byte)((double)ToSRGB(v2) * (double)255.0f); + pixel[0] = (byte)((double)ToSRGB(v3) * (double)255.0f); +} +#ifdef _DEBUG +#pragma optimize( "", off ) +#endif \ No newline at end of file diff --git a/code/redalert/infantry.cpp b/code/redalert/infantry.cpp new file mode 100644 index 0000000..edcbf75 --- /dev/null +++ b/code/redalert/infantry.cpp @@ -0,0 +1,4335 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INFANTRY.CPP 2 3/03/97 10:35p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : October 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * InfantryClass::Draw_It -- Draws a unit object. * + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * InfantryClass::Init -- Initialize the infantry object system. * + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle* + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't o* + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * InfantryClass::Set_Primary_Facing -- Change infantry primary facing -- always and instantl* + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + + +int const InfantryClass::HumanShape[32] = {0,0,7,7,7,7,6,6,6,6,5,5,5,5,5,4,4,4,3,3,3,3,2,2,2,2,1,1,1,1,1,0}; + + +/*************************************************************************** +** This is the array of constant data associated with infantry maneuvers. It +** specifies the frame rate as well as if the animation can be aborted. +*/ +// interruptible, mobile, randomstart, rate +DoStruct const InfantryClass::MasterDoControls[DO_COUNT] = { + {true, false, false, 0}, // DO_STAND_READY + {true, false, false, 0}, // DO_STAND_GUARD + {true, false, false, 0}, // DO_PRONE + {true, true, true, 2}, // DO_WALK + {true, false, false, 1}, // DO_FIRE_WEAPON + {false, true, false, 2}, // DO_LIE_DOWN + {true, true, true, 2}, // DO_CRAWL + {false, false, false, 3}, // DO_GET_UP + {true, false, false, 1}, // DO_FIRE_PRONE + {true, false, false, 2}, // DO_IDLE1 + {true, false, false, 2}, // DO_IDLE2 + {false, false, false, 2}, // DO_GUN_DEATH + {false, false, false, 2}, // DO_EXPLOSION_DEATH + {false, false, false, 2}, // DO_EXPLOSION2_DEATH + {false, false, false, 2}, // DO_GRENADE_DEATH + {false, false, false, 2}, // DO_FIRE_DEATH + {false, false, false, 2}, // DO_GESTURE1 + {false, false, false, 2}, // DO_SALUTE1 + {false, false, false, 2}, // DO_GESTURE2 + {false, false, false, 2}, // DO_SALUTE2 + {false, false, false, 2}, // DO_DOG_MAUL +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * InfantryClass::Debug_Dump -- Displays debug information about infantry unit. * + * * + * This routine is used by the debug version to display pertinent information about the * + * infantry unit. * + * * + * INPUT: mono -- The monochrome screen to display the debug information to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Debug_Dump(MonoClass * mono) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + + mono->Print(Text_String(TXT_DEBUG_INFANTRY)); + mono->Set_Cursor(1, 11);mono->Printf("%3d", Doing); + mono->Set_Cursor(8, 11);mono->Printf("%3d", Fear); + + mono->Fill_Attrib(66, 13, 12, 1, IsTechnician ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsStoked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 15, 12, 1, IsProne ? MonoClass::INVERSE : MonoClass::NORMAL); + + FootClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * InfantryClass::InfantryClass -- The constructor for infantry objects. * + * * + * This is the constructor used when creating an infantry unit. All values are required * + * except for facing and position. If these are absent, then the infantry is created in * + * a state of limbo -- not placed upon the map. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass::InfantryClass(InfantryType classid, HousesType house) : + FootClass(RTTI_INFANTRY, Infantry.ID(this), house), + Class(InfantryTypes.Ptr((int)classid)), + Doing(DO_NOTHING), + Comment(0), + IsTechnician(false), + IsStoked(false), + IsProne(false), + IsZoneCheat(false), + WasSelected(false), + Fear(FEAR_NONE), + StopDriverFrame(-1), + LookCell(0) +{ + House->Tracking_Add(this); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + IsCloakable = Class->IsCloakable; +#endif + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + if (Class->Is_Two_Shooter()) { + IsSecondShot = false; + } else { + IsSecondShot = true; + } + Strength = Class->MaxStrength; + + /* + ** Civilians carry much less ammo than soldiers do. + */ + Ammo = Class->MaxAmmo; +} + + +/*********************************************************************************************** + * InfantryClass::~InfantryClass -- Default destructor for infantry units. * + * * + * This is the default destructor for infantry type units. It will put the infantry into * + * a limbo state if it isn't already in that state and the game is still active. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/10/1995 JLB : Created. * + *=============================================================================================*/ +InfantryClass::~InfantryClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * InfantryClass::operator new -- Allocates an infantry object from the free pool. * + * * + * This will allocate an infantry object from the infantry object free pool. If there is * + * no available slot, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated infantry object or NULL if none could be * + * allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * InfantryClass::operator new(size_t) +{ + void * ptr = Infantry.Allocate(); + if (ptr != NULL) { + ((InfantryClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * InfantryClass::operator delete -- Returns the infantry object back to the free pool * + * * + * This routine is used return an infantry object back to the system. * + * * + * INPUT: ptr -- Pointer to the infantry object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((InfantryClass *)ptr)->IsActive = false; + } + Infantry.Free((InfantryClass *)ptr); +} + + +/*********************************************************************************************** + * InfantryClass::Take_Damage -- Applies damage to the infantry unit. * + * * + * This routine applies the damage specified to the infantry object. It is possible that * + * this routine will DESTROY the infantry unit in the process. * + * * + * INPUT: damage -- The damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead -- The warhead type that is inflicting the damage. * + * * + * source -- Who is responsible for inflicting the damage. * + * * + * OUTPUT: bool; Was the infantry unit destroyed by this damage? * + * * + * WARNINGS: Since the infantry unit could be destroyed by this routine, be sure to check * + * for this in the code that follows the call to Take_Damage(). * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 03/31/1995 JLB : Revenge factor. * + *=============================================================================================*/ +ResultType InfantryClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Prone infantry take only half damage, but never below one damage point. + */ + if (IsProne && damage > 0) { + damage = damage * Rule.ProneDamageBias; + } + + /* + ** If we're taking damage from a dog, we have to decide if we're the + ** target of the dog. Dogs don't spill collateral damage onto anyone + ** else, so if we're the target of a valid dog, take full damage, but if + ** we're not the target, or the dog doesn't exist, then take no damage. + */ + if (source != NULL && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + /* + ** hack for dog: if you're hit by a dog, and you're the target, your + ** damage gets upped to max. + */ + + if (res == RESULT_NONE) return(res); + + if (res == RESULT_DESTROYED) { + if (*this == INFANTRY_TANYA) { + IsTanyaDead = true; + } + Death_Announcement(source); + Stop_Driver(); + Stun(); + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + VocType sound; + VocType altsound; + sound = Sim_Random_Pick(VOC_SCREAM1, VOC_SCREAM11); + altsound = VOC_YELL1; + if (*this == INFANTRY_TANYA) { + sound = altsound = VOC_TANYA_DIE; + } + if (Class->IsDog) { + sound = altsound = VOC_DOG_HURT; + } + + /* + ** The type of warhead determines the animation the infantry + ** will perform when killed. + */ + bool delthis = false; + TARGET us = As_Target(); + switch (WarheadTypeClass::As_Pointer(warhead)->InfantryDeath) { + default: + case 0: + delthis = true; + break; + + case 1: + Sound_Effect(sound, Coord); + Do_Action(DO_GUN_DEATH, true); + break; + + case 2: + Sound_Effect(sound, Coord); + Do_Action(DO_EXPLOSION_DEATH, true); + break; + + case 3: + Sound_Effect(sound, Coord); + Do_Action(DO_GRENADE_DEATH, true); + break; + + case 4: + Sound_Effect(altsound, Coord); + Do_Action(DO_FIRE_DEATH, true); + break; + + case 5: + Sound_Effect(sound, Coord); + AnimType anim = ANIM_ELECT_DIE; + if (Class->IsDog) anim = ANIM_DOG_ELECT_DIE; + new AnimClass(anim, Coord); + delthis = true; + break; + } + + if (delthis) { + delete this; + } + return(res); + } + + /* + ** When infantry gets hit, it gets scared. + */ + if (res != RESULT_DESTROYED) { + COORDINATE source_coord = (source) ? source->Coord : NULL; + + /* + ** If an engineer is damaged and it is just sitting there, then tell it + ** to go do something since it will definitely die if it doesn't. + */ + if (!House->IsHuman && *this == INFANTRY_RENOVATOR && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA)) { + Assign_Mission(MISSION_HUNT); + } + + if (source != NULL) { + Scatter(source_coord); + } + + if (source != NULL && Fear < FEAR_SCARED) { + if (Class->IsFraidyCat) { + Fear = FEAR_PANIC; + } else { + Fear = FEAR_SCARED; + } + } else { + + /* + ** Increase the fear of the infantry by a bit. The fear increases more + ** quickly if the infantry is damaged. + */ + int morefear = FEAR_ANXIOUS; + if (Health_Ratio() > Rule.ConditionRed) morefear /= 2; + if (Health_Ratio() > Rule.ConditionYellow) morefear /= 2; + Fear = FearType(min((int)Fear + morefear, FEAR_MAXIMUM)); + } + } + return(res); +} + + +/*********************************************************************************************** + * InfantryClass::Shape_Number -- Fetch the shape number for this infantry. * + * * + * This will determine the shape number to use for this infantry soldier. The shape number * + * is relative to the shape file associated with this infantry unit. * + * * + * INPUT: Window we will be drawing into * + * * + * OUTPUT: Returns with the shape number for this infantry object to be used when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 9/4/2019 1:45PM ST : Added window parameter * + *=============================================================================================*/ +int InfantryClass::Shape_Number(WindowNumberType window) const +{ + /* + ** Fetch the shape pointer to use for the infantry. This is controlled by what + ** choreograph sequence the infantry is performing, it's facing, and whether it + ** is prone. + */ + DoType doit = Doing; + if (doit == DO_NOTHING) doit = DO_STAND_READY; + + /* + ** Hold the walk pose for a couple of frames after we come to a stop to try and avoid the problem where a moving infantry + ** goes into the stand pose for a single frame when pausing in the assigned cell destination. ST - 9/4/2019 1:39PM + */ + if (doit == DO_STAND_READY) { + if (window == WINDOW_VIRTUAL) { + if (StopDriverFrame != -1) { + if (Frame - StopDriverFrame <= 2) { + if (Path[0] != FACING_NONE) { + doit = DO_WALK; + } + } + } + } + } + + /* + ** The animation frame numbers may be different when rendering in legacy mode vs. exporting for render in GlyphX. ST - 9/5/2019 12:34PM + */ + const DoInfoStruct *do_controls = (window == WINDOW_VIRTUAL) ? Class->DoControlsVirtual : Class->DoControls; + + /* + ** The infantry shape is always modulo the number of animation frames + ** of the action stage that the infantry is doing. + */ + int shapenum = Fetch_Stage() % max(do_controls[doit].Count, 1); + + /* + ** If facing makes a difference, then the shape number will be incremented + ** by the facing accordingly. + */ + if (do_controls[doit].Jump) { + shapenum += HumanShape[Dir_To_32(PrimaryFacing.Current())] * do_controls[doit].Jump; + } + + /* + ** Finally, the shape number is biased according to the starting frame number for + ** that action in the infantry shape file. + */ + shapenum += do_controls[doit].Frame; + + /* + ** Return with the final infantry shape number. + */ + return(shapenum); +} + + +/*********************************************************************************************** + * InfantryClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Converted to infantry support. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class by seeing if there is shape imagery for it. If + ** there is no shape image, then it certainly can't be drawn -- bail. + */ + void const * shapefile = Get_Image_Data(); + + if (shapefile == NULL) return; + + y += 4; + x -= 2; + + /* + ** Actually draw the root body of the unit. + */ + Techno_Draw_Object(shapefile, Shape_Number(window), x, y, window); + + FootClass::Draw_It(x, y, window); +} + +extern bool MPSuperWeaponDisable; + +/*********************************************************************************************** + * InfantryClass::Per_Cell_Process -- Handles special operations that occur once per cell. * + * * + * This routine will handle any special operations that need to be performed once each * + * cell travelled. This includes radioing a transport that it is now clear and the * + * transport is free to leave. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 03/01/1995 JLB : Capture building options. * + * 05/31/1995 JLB : Capture is always successful now. * + *=============================================================================================*/ +void InfantryClass::Per_Cell_Process(PCPType why) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + CellClass * cellptr = &Map[Coord]; + + if (why == PCP_END) { + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** capture, then capture it. + */ + if (Mission == MISSION_CAPTURE) { + TechnoClass * tech = cellptr->Cell_Building(); + + if (tech == NULL) tech = cellptr->Cell_Techno(); + if (tech != NULL && (tech->As_Target() == NavCom || tech->As_Target() == TarCom)) { + if (*this == INFANTRY_RENOVATOR) { + + /* + ** An engineer will either mega-repair a friendly or allied + ** building or it will damage/capture an enemy building. Whether + ** it damages or captures depends on how badly damaged the + ** enemy building is. + */ +#ifdef FIXIT_ENGINEER_CAPTURE + if (House->Is_Ally(tech)) { +#else + if (tech->House->Is_Ally(House)) { +#endif + tech->Renovate(); + } else { + bool iscapturable = false; + if (tech->What_Am_I() == RTTI_BUILDING) { + iscapturable = ((BuildingClass *)tech)->Class->IsCaptureable; + } +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) { +#else + if (tech->Health_Ratio() <= Rule.ConditionRed && iscapturable) { +#endif + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + tech->House->IsThieved = true; + tech->Captured(House); + } else { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + int damage = min( (short)((int)(tech->Techno_Type_Class()->MaxStrength) * EngineerDamage), tech->Strength-1); +#else + int damage = min( (tech->Techno_Type_Class()->MaxStrength) / 3, tech->Strength-1); +#endif + tech->Take_Damage(damage, 0, WARHEAD_HE, this, true); + } + BEnd(BENCH_PCP); + delete this; + return; + } + + } else { + if (*this != INFANTRY_SPY && tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + if (*this == INFANTRY_SPY) { + int housespy = (1 << (House->Class->House)); +// tech->House->IsSpied = true; + + if (tech->Trigger.Is_Valid()) { + tech->Trigger->Spring(TEVENT_SPIED, this); + } + + if (IsOwnedByPlayer) Speak(VOX_BUILDING_INFILTRATED); + + tech->Mark(MARK_OVERLAP_UP); + tech->SpiedBy |= housespy; + tech->Mark(MARK_OVERLAP_DOWN); + if (tech->What_Am_I() == RTTI_BUILDING) { + StructType build = *(BuildingClass *)tech; + if (build == STRUCT_RADAR /* || build == STRUCT_EYE */ ) { + tech->House->RadarSpied |= housespy; + } + + if (Session.Type == GAME_NORMAL || !MPSuperWeaponDisable) { + + // If they're spying on a sub pen, give 'em a sonar pulse + if (build == STRUCT_SUB_PEN) { + House->SuperWeapon[SPC_SONAR_PULSE].Enable(true, true, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_SONAR_PULSE, House); + } + } else { + if (IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_SONAR_PULSE); + Map.Column[1].Flag_To_Redraw(); + } + } + } + // If they're spying on an airfield, they get Parabombs + if (build == STRUCT_AIRSTRIP) { + House->SuperWeapon[SPC_PARA_BOMB].Enable(true, true, false); + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (House->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, SPC_PARA_BOMB, House); + } + } + else { + if (IsOwnedByPlayer) { + Map.Add(RTTI_SPECIAL, SPC_PARA_BOMB); + Map.Column[1].Flag_To_Redraw(); + } + } + } + } + } + + } else { + + if (*this == INFANTRY_THIEF) { // Thief just raided a storage facility + tech->House->IsThieved = true; + + if (tech->What_Am_I() == RTTI_BUILDING) { + BuildingClass * bldg = (BuildingClass *)tech; + if (bldg->Class->Capacity) { + + /* + ** If we just raided a storage facility (refinery or silo) + ** then give the thief up to half the capacity of the + ** storage facility. + */ + if (IsOwnedByPlayer || bldg->IsOwnedByPlayer) Speak(VOX_MONEY_STOLEN); +#ifdef OBSOLETE + long capacity = bldg->Class->Capacity * 256; + capacity /= (bldg->House->Tiberium+1); + int bldgcap = bldg->Class->Capacity; + + long cash = (bldgcap * 256) / (capacity+1); + if (cash > (bldgcap / 2)) cash = bldgcap / 2; +#else + long cash = bldg->House->Available_Money() / 2; +#endif + bldg->House->Spend_Money(cash); + House->Refund_Money(cash); + } + } + } + } + } + BEnd(BENCH_PCP); + delete this; + return; + + } else { + + #ifdef OBSOLETE + // are we trying to repair a bridge? + if (Is_Target_Cell(TarCom) ) { + CELL cell = Coord_Cell(Coord); + if (cell == ::As_Cell(NavCom)) { + TemplateType tt = cellptr->TType; + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + int h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D) { + new TemplateClass(TemplateType(cellptr->TType-1), cell); + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } else { + + // Trying to repair multi-segment bridge. Look for the + // start tile, then fix it, and determine the direction to + // go in and repair it all that way. + TemplateType newtt = TEMPLATE_BRIDGE_1A; + int xmov = -1; // coords to move to for next template + int ymov = 2; + bool valid = false; + switch (tt) { + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_1C: + valid = true; + break; + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_2C: + newtt = TEMPLATE_BRIDGE_2A; + xmov = 2; + ymov = -1; + valid = true; + break; + case TEMPLATE_BRIDGE_3C: + case TEMPLATE_BRIDGE_3D: + newtt = TEMPLATE_BRIDGE_3A; + valid = true; + break; + case TEMPLATE_BRIDGE_3E: + newtt = TEMPLATE_BRIDGE_3A; + xmov = 2; + ymov = -1; + valid = true; + break; + } + + // Did we find a valid repairable bridge piece? + if (valid) { + bool doing = true; + while (doing) { + new TemplateClass(TemplateType(newtt), cell); + cell += (MAP_CELL_W * ymov) + xmov; + if (xmov < 0) { + xmov = -1; + ymov = 1; + } else { + xmov = 1; + ymov = -1; + } + cellptr = &Map[cell]; + tt = cellptr->TType; + if ((tt >= TEMPLATE_BRIDGE_3B && tt <= TEMPLATE_BRIDGE_3F) || + tt == TEMPLATE_BRIDGE_1B || tt == TEMPLATE_BRIDGE_1C || + tt == TEMPLATE_BRIDGE_2B || tt == TEMPLATE_BRIDGE_2C ) { + + if (tt >= TEMPLATE_BRIDGE_3B) { + newtt = TEMPLATE_BRIDGE_3A; + } else { + if (tt < TEMPLATE_BRIDGE_2A) { + newtt = TEMPLATE_BRIDGE_1A; + } else { + newtt = TEMPLATE_BRIDGE_2A; + } + } + icon = cellptr->TIcon; + w = TemplateTypeClass::As_Reference(cellptr->TType).Width; + h = TemplateTypeClass::As_Reference(cellptr->TType).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + } else { + doing = false; + } + } + Map.Zone_Reset(MZONEF_ALL); + delete this; + return; + } + } + } + } else { + #endif + if (!Target_Legal(NavCom)) { + Enter_Idle_Mode(); + if (Map[Coord].Cell_Building()) { + Scatter(0, true); + } + } + #ifdef OBSOLETE + } + #endif + } + } + + /* + ** Infantry entering a transport vehicle will break radio contact + ** at attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno != NULL && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** If the infantry unit is entering a cell that contains the building it is trying to + ** sabotage, then sabotage it. + */ + if (Mission == MISSION_SABOTAGE) { + BuildingClass * building = cellptr->Cell_Building(); + if (building != NULL && building->As_Target() == NavCom) { + if (!building->IronCurtainCountDown && building->Mission != MISSION_DECONSTRUCTION) { + building->IsGoingToBlow = true; + building->Clicked_As_Target(PlayerPtr->Class->House, (Rule.C4Delay * TICKS_PER_MINUTE) / 2); // 2019/09/20 JAS - Added record of who clicked on the object + building->Clicked_As_Target(building->Owner(), (Rule.C4Delay * TICKS_PER_MINUTE) / 2); + building->CountDown = Rule.C4Delay * TICKS_PER_MINUTE; + building->WhomToRepay = As_Target(); + } + NavCom = TARGET_NONE; + Do_Uncloak(); + Arm = Rearm_Delay(true); + Scatter(building->Center_Coord(), true, true); // RUN AWAY! + BEnd(BENCH_PCP); + return; + } else { + if (::As_Target(Coord_Cell(Center_Coord())) == NavCom) { + Explosion_Damage(Coord, Rule.BridgeStrength, this, WARHEAD_HE); + + Stop_Driver(); + Scatter(Adjacent_Cell(Coord, PrimaryFacing), true, true); + Assign_Mission(MISSION_MOVE); + + CELL cell = Coord_Cell(Center_Coord()); + CellClass * cellptr = &Map[cell]; + if (!Target_Legal(NavCom) || Map[As_Cell(NavCom)].Land_Type() == LAND_WATER) { + Mark(MARK_DOWN); // Needed only so that Tanya will get destroyed by the explosion. + } + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + Explosion_Damage(Coord, Rule.BridgeStrength, NULL, WARHEAD_HE); + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + + Mark(MARK_DOWN); + } + } + } + + /* + ** If this unit is on a teather, then cut it at this time so that + ** the "parent" unit is free to proceed. Note that the parent + ** unit might actually be a building. + */ + if (IsTethered) { + Transmit_Message(RADIO_UNLOADED); + if (House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE) { + Do_Action(DO_GESTURE1); + } else { + Do_Action(DO_GESTURE2); + } + + /* + ** Special voice play. + */ + if (*this == INFANTRY_TANYA) { + Sound_Effect(VOC_TANYA_LAUGH, Coord); + } + + /* + ** If the cell is now full of infantry, tell them all to scatter + ** in order to make room for more. + */ + if ((cellptr->Flag.Composite & 0x01F) == 0x01F) { + cellptr->Incoming(0, true, true); +// cellptr->Incoming(0, true); + } + } + + /* + ** When the infantry reaches the center of the cell, it may begin a new mission. + */ + if (MissionQueue == MISSION_NONE && !Target_Legal(NavCom) && !Target_Legal(TarCom) && !In_Radio_Contact()) { + Enter_Idle_Mode(); + } + Commence(); + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = cellptr->Cell_Building(); + if (bldng != NULL && *bldng == STRUCT_APMINE) { + /* + ** Show the animation and get rid of the land mine + */ + COORDINATE blcoord = bldng->Center_Coord(); + new AnimClass(Combat_Anim(Rule.APMineDamage, WARHEAD_HE, cellptr->Land_Type()), blcoord); + delete bldng; + int damage; + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * obj = Infantry.Ptr(index); + if (obj != NULL && !obj->IsInLimbo) { + int dist = ::Distance(obj->Coord, blcoord); + if (dist <= 0xC0) { + damage = Rule.APMineDamage; + obj->Take_Damage(damage, 0, WARHEAD_HE); + } + } + } + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + + /* + ** If the last cell we looked from isn't adjacent to our current cell, + ** perform a full look. + */ + CELL cell = Coord_Cell(Coord); + if (::Distance(Cell_X(cell), Cell_Y(cell), Cell_X(LookCell), Cell_Y(LookCell)) > 1) { + Look(false); + } else { + Look(true); + } + +#if 1 +/* +** If after all is said and done, the unit finishes its move on an impassable cell, then +** it must presume that it is in the case of a unit driving onto a bridge that blows up +** before the unit completes it's move. In such a case the unit should have been destroyed +** anyway, so blow it up now. +*/ +LandType land = Map[Coord].Land_Type(); +if (!IsDriving && !Class->IsBomber && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; +} +#endif + + } + + if (IsActive) { + FootClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * InfantryClass::Detach -- Removes the specified target from targeting computer. * + * * + * This is a support routine that removes the target specified from any targeting or * + * navigation computers. When a target is destroyed or removed from the game system, * + * the target must be removed from any tracking systems of the other units. This routine * + * handles removal for infantry units. * + * * + * INPUT: target -- The target to remove from the infantry unit's tracking systems. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Detach(TARGET target, bool all) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (TarCom == target) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + FootClass::Detach(target, all); +} + + +/*********************************************************************************************** + * InfantryClass::Init -- Initialize the infantry object system. * + * * + * This routine will force the infantry object system into its empty initial state. It * + * is called when the scenario needs to be cleared in preparation for a scenario load. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Init(void) +{ + Infantry.Free_All(); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Destination -- Gives the infantry a movement destination. * + * * + * This routine updates the infantry's navigation computer so that the infantry will * + * travel to the destination target specified. * + * * + * INPUT: target -- The target to have the infantry unit move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Assign_Destination(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Special flag so that infantry will start heading in the right direction immediately. + */ + if (IsDriving && !IsFormationMove && Target_Legal(target) && Map[Center_Coord()].Is_Clear_To_Move(Class->Speed, true, false)) { + Stop_Driver(); + } + + /* + ** When telling an infantry soldier to move to a location twice, then this + ** means that movement is more important than safety. Get up and run! + */ + if (House->IsHuman && Target_Legal(target) && NavCom == target && IsProne && !Class->IsFraidyCat && !Class->IsDog) { + Do_Action(DO_GET_UP); + } + + /* + ** If telling a dog to attack a human, start the dog running + */ + TechnoClass * tech = As_Techno(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + FootClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * InfantryClass::Assign_Target -- Gives the infantry a combat target. * + * * + * This routine will update the infantry's targeting computer so that it will try to * + * attack the target specified. This might result in it moving to be within range and thus * + * also cause adjustment of the navigation computer. * + * * + * INPUT: target -- The target that this infantry should attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 06/30/1995 JLB : Tries to capture target if possible. * + *=============================================================================================*/ +void InfantryClass::Assign_Target(TARGET target) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Path[0] = FACING_NONE; + if (Class->IsDog) { + if (::As_Object(target) && ::As_Object(target)->What_Am_I() != RTTI_INFANTRY) { + target = TARGET_NONE; + } + } + FootClass::Assign_Target(target); + + /* + ** If this is an infantry that can only capture, then also assign its destination to the + ** target specified. + */ + if (!Target_Legal(NavCom) && Class->IsCapture && !Is_Weapon_Equipped()) { + BuildingClass const * building = As_Building(target); + if (building != NULL && building->Class->IsCaptureable) { + Assign_Destination(target); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::AI -- Handles the infantry non-graphic related AI processing. * + * * + * This routine is used to handle the non-graphic AI processing the infantry requires. * + * Call this routine ONCE per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 08/14/1996 JLB : Simplified. * + *=============================================================================================*/ +void InfantryClass::AI(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + FootClass::AI(); + + if (!IsActive) { + return; + } + + if (IsUnloading) Mark(MARK_CHANGE_REDRAW); + + /* + ** Infantry that are not on the ground should always be redrawn. Such is + ** the case when they are parachuting to the ground. + */ + if (In_Which_Layer() != LAYER_GROUND) { + Mark(MARK_CHANGE); + } + + /* + ** Special hack to make sure that if this infantry is in firing animation, but the + ** stage class isn't set, then abort the firing flag. + */ + if (IsFiring && Fetch_Rate() == 0) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Do_Action(DO_STAND_READY); + Mark(MARK_OVERLAP_DOWN); + } + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsFiring && !IsFalling && !IsDriving && (Doing == DO_NOTHING || MasterDoControls[Doing].Interrupt)) { + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + + /* + ** Special hack to make sure the dog never attacks a cell. + */ + if (Class->IsDog && Target_Legal(TarCom) && Is_Target_Cell(TarCom)) { + Assign_Target(TARGET_NONE); + } + + /* + ** Handle any infantry fear logic or related actions. + */ + Fear_AI(); + + /* + ** Special victory dance action. + */ + if (!Target_Legal(NavCom) && !IsProne && IsStoked && Comment == 0) { + IsStoked = false; + Do_Action(Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2); + } + + /* + ** Determine if this infantry unit should fire off an + ** attack or not. + */ + Firing_AI(); + + /* + ** Handle the completion of the animation sequence. + */ + Doing_AI(); + + /* + ** Perform movement operations at this time. + */ + Movement_AI(); +} + + +/*********************************************************************************************** + * InfantryClass::Can_Enter_Cell -- Determines if the infantry can enter the cell specified. * + * * + * This routine is used to examine the cell specified and determine if the infantry is * + * allowed to enter it. It is used by the path finding algorithm. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: Returns the type of blockage in the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** If we are moving into an illegal cell, then we can't do that. + */ + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** If moving off the edge of the map, then consider that an illegal move. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + CellClass * cellptr = &Map[cell]; + + /* + ** Walls are considered impassable for infantry UNLESS the wall has a hole + ** in it. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (otype.IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (otype.IsWall) { + if ((cellptr->OverlayData / 16) != otype.DamageLevels) { + + /* + ** If the wall can be destroyed, then return this fact instead of + ** a complete failure to enter. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->Is_Wall_Destroyer()) { + return(MOVE_DESTROYABLE); + } + return(MOVE_NO); + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + MoveType retval = MOVE_OK; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** Always allow movement if the cell is the object to be captured or sabotaged. + */ + if (((Mission == MISSION_ENTER && In_Radio_Contact()) || Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && + (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { + + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target() && Is_Target_Unit(ArchiveTarget)) { + return(MOVE_NO); + } + + /* + ** If object is a land mine, allow movement + */ + if (obj->What_Am_I() == RTTI_BUILDING) { + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) { + obj = obj->Next; + continue; + } else { + if (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House)) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) { + obj = obj->Next; + continue; + } + } + } + } + + /* + ** Special case check so that a landed aircraft that is in radio contact, will not block + ** a capture attempt. It is presumed that this case happens when a helicopter is landed + ** at a helipad. + */ +// if ((Mission != MISSION_CAPTURE && Mission != MISSION_SABOTAGE) || obj->What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass *)obj)->In_Radio_Contact()) { + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ +// if (obj->What_Am_I() == RTTI_BUILDING || obj->What_Am_I() == RTTI_AIRCRAFT || obj->What_Am_I() == RTTI_UNIT) { +// if ((Mission == MISSION_CAPTURE || Mission == MISSION_SABOTAGE) && (obj->As_Target() == NavCom || obj->As_Target() == TarCom)) { +// return(MOVE_OK); +// } +// } + + /* + ** Special check to always allow entry into the building that this infantry + ** is trying to capture. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Allied objects block movement using different rules than for enemy + ** objects. + */ + if (House->Is_Ally(obj) || ScenarioInit) { + switch (obj->What_Am_I()) { + + /* + ** A unit blocks as either a moving blockage or a stationary temp blockage. + ** This depends on whether the unit is currently moving or not. + */ + case RTTI_UNIT: + if (((UnitClass *)obj)->IsDriving || Target_Legal(((UnitClass *)obj)->NavCom)) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + break; + + /* + ** Aircraft and buildings always block movement. If for some reason there is an + ** allied terrain object, that blocks movement as well. + */ + case RTTI_TERRAIN: + case RTTI_AIRCRAFT: + case RTTI_BUILDING: + return(MOVE_NO); + + default: + break; + } + + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** Any non-allied blockage is considered impassible if the infantry + ** is not equipped with a weapon. + */ + if (Combat_Damage() <= 0) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the infantry is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: +#ifdef OBSOLETE + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + case RTTI_INFANTRY: + if ( *(InfantryClass *)obj == INFANTRY_SPY && !Class->IsDog) { + retval = MOVE_TEMP; + break; + } + // otherwise, fall thru. + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } +// } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If foot soldiers cannot travel on the cell -- consider it impassable. + */ + if (retval == MOVE_OK && !IsTethered && Ground[cellptr->Land_Type()].Cost[SPEED_FOOT] == 0) { + +#ifdef OBSOLETE + /* + ** Special case - if it's an engineer, and the cell under consideration + ** is his NavCom, and his mission is mission_capture, then he's most + ** likely moving to his final destination to repair a bridge, so we + ** should let him. + */ + if (*this == INFANTRY_RENOVATOR && Is_Target_Cell(TarCom) && (cell == ::As_Cell(NavCom)) && (cellptr->TType == TEMPLATE_BRIDGE1D || cellptr->TType == TEMPLATE_BRIDGE2D || (cellptr->TType >= TEMPLATE_BRIDGE_1C && cellptr->TType <= TEMPLATE_BRIDGE_3E) ) ) { + return(MOVE_OK); + } +#endif + return(MOVE_NO); + } + + /* + ** if a unit has the cell reserved then we just can't go in there. + */ + if (retval == MOVE_OK && cellptr->Flag.Occupy.Vehicle) { + return(MOVE_NO); + } + + /* + ** if a block of infantry has the cell reserved then there are two + ** possibilities... + */ + if (cellptr->InfType != HOUSE_NONE) { + if (House->Is_Ally(cellptr->InfType)) { + if ((cellptr->Flag.Composite & 0x1F) == 0x1f) { + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } + } else { + if (Combat_Damage() > 0) { + if (retval < MOVE_DESTROYABLE) { + retval = MOVE_DESTROYABLE; + } + } else { + return(MOVE_NO); + } + } + } + + /* + ** If it is still ok to move the infantry, then perform the last check + ** to see if the cell is already full of infantry. + */ + if (retval == MOVE_OK && (cellptr->Flag.Composite & 0x1F) == 0x1F) { + return(MOVE_NO); + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * InfantryClass::Overlap_List -- The list of cells that the infantry overlaps, but doesn't occ* + * * + * This is a rendering support routine that will return a pointer to a list of cell offsets * + * that specify the cells the infantry unit is currently overlapping (graphic wise) but * + * is not considered to occupy. This list is used to update the map display. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to an offset list for cells that the unit overlaps but doesn't * + * occupy. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +#ifdef PARTIAL +short const * InfantryClass::Overlap_List(bool redraw) const +#else +short const * InfantryClass::Overlap_List(bool ) const +#endif +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsDog) { + return(Coord_Spillage_List(Coord, 24 + (Doing == DO_DOG_MAUL ? 40 : 0) + (Doing >= DO_GUN_DEATH && Doing <= DO_FIRE_DEATH ? 40 : 0) )); + } else { + + /* + ** The default infantry rectangle will be as large as the largest shape the infantry + ** can be. + */ + +#ifdef PARTIAL + Rect rect(-16, -24, 32, 36); + + /* + ** If this is for a visual change redraw, then the overlap list will be based + ** on the actual dimensions of the shape data. If the dimensions have already + ** been calculated then use them, otherwise, use the default large rectangle + ** previously created. + */ + if (Height == 0 && !Is_Selected_By_Player() && redraw && Class->DimensionData != NULL) { + int shapenum = Shape_Number(); + if (!Class->DimensionData[shapenum].Is_Valid()) { + Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + rect = Class->DimensionData[shapenum]; + rect.Y += 4; + rect.X -= 2; + } + return(Coord_Spillage_List(Coord, rect, true)); +#else + + static Rect rect(-16, -24, 32, 36); + return(Coord_Spillage_List(Coord, rect, true)); +#endif + +// return(Coord_Spillage_List(Coord, 24 /*+ ((Doing > DO_WALK || IsSelected)?12:0)*/ )); + } +} + + +/*********************************************************************************************** + * InfantryClass::Can_Fire -- Can the infantry fire its weapon? * + * * + * Determines if the infantry unit can fire on the target. If it can't fire, then the * + * reason why is returned. * + * * + * INPUT: target -- The target to determine if the infantry can fire upon. * + * * + * OUTPUT: Returns the fire error type that indicates if the infantry can fire and if it * + * can't, why not. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 06/27/1995 JLB : Flame thrower can fire while prone now. * + *=============================================================================================*/ +FireErrorType InfantryClass::Can_Fire(TARGET target, int which) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Don't allow firing if the infantry is still firing on previous target. + */ +// if (IsFiring) return(FIRE_REARM); + + /* + ** If a medic is shooting at a healed target, let's declare the target + ** illegal so he won't be constantly healing healed infantrymen. + */ + if (Combat_Damage() < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + TechnoClass * targ = As_Techno(target); +#else + InfantryClass * targ = As_Infantry(target); +#endif + if (targ == NULL || targ->Health_Ratio() >= Rule.ConditionGreen) { + return(FIRE_ILLEGAL); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (IsDriving || (Target_Legal(NavCom) && Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt)) { + return(FIRE_MOVING); + } + + /* + ** Only one dog can fire on an infantry at a time + */ + if (Class->IsDog) { + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass *dog = Infantry.Ptr(index); + if (dog != this && dog->Class->IsDog && (dog->IsFiring || dog->IsInLimbo) && dog->TarCom == target) { + return(FIRE_ILLEGAL); + } + } + } + + return(FootClass::Can_Fire(target, which)); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * * + * This routine will determine the coordinate to use when this infantry fires. The * + * coordinate is the location where bullets appear (or fire effects appear) when the * + * object fires its weapon. * + * * + * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * + * weapon, 1 means secondary weapon. * + * * + * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * + * should appear. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/12/2019 SKY : Created. * + *=============================================================================================*/ +COORDINATE InfantryClass::Fire_Coord(int which) const +{ + COORDINATE coord = FootClass::Fire_Coord(which); + + /* + ** Since electric weapons draw a zap between start and end points, prone infantry that fire one + ** need to adjust their fire coordinate so the start point looks correct. + */ + if (IsProne) { + TechnoTypeClass const & tclass = *Techno_Type_Class(); + WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; + if (weapon && weapon->IsElectric) { + coord = Coord_Add(coord, XY_Coord(0, 48)); + } + } + + return coord; +} + + +/*********************************************************************************************** + * InfantryClass::Enter_Idle_Mode -- The infantry unit enters idle mode by this routine. * + * * + * Use this routine when the infantry unit as accomplished its task and needs to find * + * something to do. The default behavior is to enter some idle state such as guarding. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Enter_Idle_Mode(bool ) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (Target_Legal(TarCom)) { + order = MISSION_ATTACK; + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + } else { + + Handle_Navigation_List(); + + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + if (Mission == MISSION_CAPTURE) { + order = MISSION_CAPTURE; + } + if (Mission == MISSION_SABOTAGE) { + order = MISSION_SABOTAGE; + } + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsZombie || MissionControl[Mission].IsParalyzed) { + return; + } + + if (Class->IsDog) { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + ArchiveTarget = ::As_Target(Coord_Cell(Center_Coord())); + } + } else { + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + if (Is_Weapon_Equipped()) { + order = MISSION_GUARD_AREA; + } else { + order = MISSION_GUARD; + } + } + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * InfantryClass::Random_Animate -- Randomly animate the infantry (maybe) * + * * + * This routine is the random animator initiator for infantry units. This routine should * + * be called regularly. On occasion, it will cause the infantry to go into an idle * + * animation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 12/13/1994 JLB : Does random facing change. * + * 07/02/1995 JLB : Nikoomba special effects. * + *=============================================================================================*/ +bool InfantryClass::Random_Animate(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Is_Ready_To_Random_Animate()) { + IdleTimer = Random_Pick(Rule.RandomAnimateTime * (TICKS_PER_MINUTE/2), Rule.RandomAnimateTime * (TICKS_PER_MINUTE*2)); + + /* + ** Scared infantry will always follow the golden rule of civilians; + ** "When in darkness or in doubt, run in circles, scream, and shout!" + */ + if (Class->IsFraidyCat && !House->IsHuman && Fear > FEAR_ANXIOUS) { + Scatter(NULL, true); + return(true); + } + + switch (Random_Pick(0, 10)) { + case 0: + if (Class->IsDog) { + Do_Action(DO_IDLE1); + } + break; + + case 1: + Do_Action(DO_SALUTE1); + break; + + case 2: + Do_Action(DO_SALUTE2); + break; + + case 3: + Do_Action(DO_GESTURE1); + break; + + case 4: + Do_Action(DO_GESTURE2); + break; + + case 5: + Do_Action(DO_IDLE1); + break; + + case 6: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + break; + + case 7: + Do_Action(DO_IDLE2); + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!Is_Selected_By_Player() && IsOwnedByPlayer && *this == INFANTRY_TANYA && Sim_Random_Pick(0, 2) == 0) { + Sound_Effect(VOC_TANYA_SHAKE, Coord); + } + break; + + /* + ** On occasion, civilian types will wander about. + */ + case 8: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + if (!House->IsHuman && Class->IsFraidyCat) { + Scatter(NULL, true); + } + break; + + case 9: + case 10: + Mark(MARK_CHANGE_REDRAW); + PrimaryFacing.Set(Facing_Dir(Random_Pick(FACING_N, FACING_NW))); + Mark(MARK_CHANGE_REDRAW); + + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Scatter -- Causes the infantry to scatter to nearby cell. * + * * + * This routine is used when the infantry should scatter to a nearby cell. Scattering * + * occurs as an occasional consequence of being fired upon. It is one of the features * + * that makes infantry so "charming". * + * * + * INPUT: threat -- The coordinate source of the threat that is causing the infantry to * + * scatter. If the threat isn't from a particular direction, then this * + * parameter will be NULL. * + * * + * forced -- The threat is real and a serious effort to scatter should be made. * + * * + * nokidding-- The scatter should affect the player's infantry even if it otherwise * + * wouldn't have. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/12/1994 JLB : Flame thrower infantry always scatter. * + * 08/02/1996 JLB : Added the nokidding parameter * + *=============================================================================================*/ +void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** A unit that is in the process of going somewhere will never scatter. + */ + if (IsDriving) forced = false; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + /* + ** If the infantry is currently engaged in legitimate combat, then don't + ** scatter unless forced to. + */ + if (!Class->IsFraidyCat && Target_Legal(TarCom) && !forced) return; + + /* + ** Don't scatter if performing an action that can't be interrupted. + */ + if (Doing != DO_NOTHING && !MasterDoControls[Doing].Interrupt) return; + + /* + ** For human players, don't scatter the infantry, if the special + ** flag has not been enabled that allows infantry scatter. + */ + if (!Rule.IsScatter && !nokidding && House->IsHuman && !forced && !Team.Is_Valid()) return; + + if (forced || Class->IsFraidyCat /*|| !(Random_Pick(1, 4) == 1)*/) { + FacingType toface; + + if (threat) { + toface = Dir_Facing(Direction8(threat, Coord)); + toface = toface + FacingType(Random_Pick(0, 4)-2); + } else { + COORDINATE coord = Coord_Fraction(Center_Coord()); + + if (coord != 0x00800080L) { + toface = Dir_Facing((DirType)Desired_Facing8(0x0080, 0x0080, Coord_X(coord), Coord_Y(coord))); + } else { + toface = Dir_Facing(PrimaryFacing.Current()); + } + toface = toface + FacingType(Random_Pick(0, 4)-2); + } + + CELL newcell = 0; + CELL altcell = 0; + FacingType face; + for (face = FACING_N; face < FACING_COUNT; face++) { + FacingType newface = toface + face; + newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) { + if (altcell == 0) altcell = newcell; + if (!Map[newcell].Is_Bridge_Here()) break; +// Assign_Mission(MISSION_MOVE); +// Assign_Destination(::As_Target(newcell)); + } + } + if (face == FACING_COUNT) { + newcell = 0; + } + + if (newcell == 0) { + newcell = altcell; + } + + if (newcell != 0) { + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(newcell)); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Look -- Performs a look around (map reveal) action. * + * * + * This routine will reveal the map around this object. * + * * + * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * + * If it is absolutely known that the object has only moved one * + * cell from its previous location that it performed a Look() at, * + * then set this parameter to TRUE. It will only perform the look * + * check on the perimeter cells. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is slow, try to call it only when necessary. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Look(bool incremental) +{ + LookCell = Coord_Cell(Coord); + FootClass::Look(incremental); +} + + +/*********************************************************************************************** + * InfantryClass::Do_Action -- Launches the infantry into an animation sequence. * + * * + * This starts the infantry into a choreographed animation sequence. These sequences can * + * be as simple as standing up or lying down, but can also be complex, such as dying or * + * performing some idle animation. * + * * + * INPUT: todo -- The choreographed sequence to start. * + * * + * force -- Force starting this animation even if the current animation is flagged * + * as uninterruptible. This is necessary for death animations. * + * * + * OUTPUT: bool; Was the animation started? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Do_Action(DoType todo, bool force) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (todo == DO_NOTHING || Class->DoControls[todo].Count == 0) { + return(false); + } + + if (*this == INFANTRY_SPY && todo >= DO_GESTURE1) { + todo = (DoType)(DO_IDLE1 + Random_Pick(0,1)); + } + + if (todo != Doing && (Doing == DO_NOTHING || force || MasterDoControls[Doing].Interrupt)) { + Mark(MARK_OVERLAP_UP); + Doing = todo; + Mark(MARK_OVERLAP_DOWN); + if (todo == DO_IDLE1 || todo == DO_IDLE2) { + Set_Rate(Options.Normalize_Delay(MasterDoControls[Doing].Rate)); + } else { + Set_Rate(MasterDoControls[Doing].Rate); + } + Set_Stage(0); + + /* + ** Kludge to make sure that if infantry is in the dying animation, it isn't still + ** moving as well. + */ + if (Strength == 0) { + Stop_Driver(); + } + + /* + ** Make sure dogs don't try to go somewhere while they're mauling + */ + if (todo == DO_DOG_MAUL) { + Stop_Driver(); + Assign_Destination(TARGET_NONE); + } + + /* + ** Since the animation sequence might be interrupted. Set any flags + ** necessary so that if interrupted, the affect on the infantry is + ** still accomplished. + */ + switch (todo) { + case DO_LIE_DOWN: + IsProne = true; + break; + + case DO_GET_UP: + IsProne = false; + break; + + default: + break; + } + + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Stop_Driver -- Stops the infantry from moving any further. * + * * + * This is used to stop the infantry from animating in movement. This function will stop * + * the infantry moving and revert it to either a prone or standing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the driving stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Stop_Driver(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Head_To_Coord()) { + + /* + ** Remove the "reservation" bit in the destination location. + */ + Clear_Occupy_Bit(Head_To_Coord()); + } + + /* + ** Set the occupation bit at the current location. + */ + Set_Occupy_Bit(Coord); + + if (Doing != DO_STAND_READY) { + StopDriverFrame = Frame; + } + + if (Class->IsDog) { + Do_Action(DO_STAND_READY); + } else { + if (IsProne) { + Do_Action(DO_PRONE); + } else { + Do_Action(DO_STAND_READY); + } + } + + if (Can_Enter_Cell(Coord_Cell(Coord)) == MOVE_OK) { + IsZoneCheat = false; + } else { + IsZoneCheat = true; + } + + return(FootClass::Stop_Driver()); +} + + +/*********************************************************************************************** + * InfantryClass::Start_Driver -- Handles giving immediate destination and move orders. * + * * + * Use this routine to being the infantry moving toward the destination specified. The * + * destination is first checked to see if there is a free spot available. Then the infantry * + * reserves that spot and begins movement toward it. * + * * + * INPUT: headto -- The coordinate location desired for the infantry to head to. * + * * + * OUTPUT: bool; Was the infantry successfully started on its journey? Failure may be because * + * the specified destination could not contain the infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/21/1994 JLB : Created. * + * 05/14/1995 JLB : Tries to move to closest spot possible. * + * 05/15/1995 JLB : Uses closest spot if moving onto transport. * + *=============================================================================================*/ +bool InfantryClass::Start_Driver(COORDINATE & headto) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + COORDINATE old = headto; + + /* + ** Convert the head to coordinate to a legal sub-position location. + */ + headto = Map[headto].Closest_Free_Spot(Coord_Move(headto, Direction(headto)+DIR_S, 0x007C)); + if (!headto && Can_Enter_Cell(Coord_Cell(old)) == MOVE_OK) { + headto = Map[old].Closest_Free_Spot(Coord_Move(old, Direction(headto)+DIR_S, 0x0080), true); + } + + /* + ** If the infantry started moving, then fixup the occupation bits. + */ + if (headto && FootClass::Start_Driver(headto)) { + if (!IsActive) return(false); + + /* + ** Remove the occupation bit from the infantry's current location. + */ + Clear_Occupy_Bit(Coord); + + /* + ** Set the occupation bit for the new headto location. + */ + Set_Occupy_Bit(headto); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Limbo -- Performs cleanup operations needed when limboing. * + * * + * This routine will clean up the infantry occupation bits (as necessary) as well as stop * + * the infantry movement process when it gets limboed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Limbo(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + Stop_Driver(); + + Clear_Occupy_Bit(Coord); + } + return(FootClass::Limbo()); +} + + +/*********************************************************************************************** + * InfantryClass::Fire_At -- Fires projectile from infantry unit. * + * * + * Use this routine when the infantry unit wishes to fire a projectile. This routine * + * will launch the projectile and perform any other necessary infantry specific operations. * + * * + * INPUT: target -- The target of the attack. * + * * + * which -- Which weapon to use for firing. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with pointer to the projectile launched. If none could be launched, then * + * NULL is returned. If there is already the maximum bullet objects in play, then * + * this could happen. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * InfantryClass::Fire_At(TARGET target, int which) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + + BulletClass * bullet = FootClass::Fire_At(target, which); + if (bullet != NULL && !IsInLimbo) { + + /* + ** For fraidycat infantry that run out of ammo, always go into + ** a maximum fear state at that time. + */ + if (Class->IsFraidyCat && !Ammo) { + Fear = FEAR_MAXIMUM; + if (Mission == MISSION_ATTACK || Mission == MISSION_HUNT) { + Assign_Mission(MISSION_GUARD); + } + } + } + return(bullet); +} + + +/*********************************************************************************************** + * InfantryClass::Unlimbo -- Unlimbo infantry unit in legal sub-location. * + * * + * This will attempt to unlimbo the infantry unit at the designated coordinate, but will * + * ensure that the coordinate is a legal subposition. * + * * + * INPUT: coord -- The coordinate to unlimbo the infantry at. * + * * + * facing -- The desired initial facing for the infantry unit. * + * * + * strength -- The desired initial strength for the infantry unit. * + * * + * mission -- The desired initial mission for the infantry unit. * + * * + * OUTPUT: bool; Was the infantry unlimboed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Make sure that the infantry start in a legal position on the map. + */ + coord = Map[coord].Closest_Free_Spot(coord, ScenarioInit); + if (coord == NULL) { + return(false); + } + + if (FootClass::Unlimbo(coord, facing)) { + + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->IScan |= (1L << Class->Type); + House->ActiveIScan |= (1L << Class->Type); + + /* + ** If there is no sight range, then this object isn't discovered by the player unless + ** it actually appears in a cell mapped by the player. + */ + if (Class->SightRange == 0) { + IsDiscoveredByPlayer = false; + } + + Set_Occupy_Bit(coord); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Greatest_Threat -- Determines greatest threat (target) for infantry unit. * + * * + * This routine intercepts the Greatest_Threat request and adds the appropriate target * + * types to search for. For regular infantry, this consists of all the ground types. For * + * rocket launching infantry, this also includes aircraft. * + * * + * INPUT: threat -- The basic threat control value. * + * * + * OUTPUT: Returns with the best target for this infantry unit to attack. If no suitable * + * target could be found, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 09/28/1995 JLB : Engineers try to recapture buildings first. * + *=============================================================================================*/ +TARGET InfantryClass::Greatest_Threat(ThreatType threat) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Engineers consider only buildings that can be captured as being a threat. All others + ** are ignored. If there is a building that needs to be recaptured and it is nearby + ** then automatically head toward it to recapture it. + */ + if (!House->IsHuman && Class->IsCapture && !Is_Weapon_Equipped()) { + if (House->ToCapture != TARGET_NONE && Distance(House->ToCapture) < 0x0F00) { + return(House->ToCapture); + } + threat = threat | THREAT_CAPTURE; + } + + if (!Is_Weapon_Equipped()) { + if (!Class->IsCapture && *this != INFANTRY_RENOVATOR && *this != INFANTRY_SPY && *this != INFANTRY_THIEF) { + return(TARGET_NONE); + } + } + + /* + ** Special hack to make Tanya not auto-fire if controlled by a + ** human player. + */ + if (*this == INFANTRY_TANYA && House->IsHuman) { + return(TARGET_NONE); + } + + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + + /* + ** Organic weapon types don't consider anything but infantry to be a threat. Such + ** weapon types would be the dog jaw and the medic first aid kit. + */ + if (Is_Weapon_Equipped() && Class->PrimaryWeapon->WarheadPtr->IsOrganic) { + threat = threat & ~(THREAT_BUILDINGS|THREAT_VEHICLES|THREAT_BOATS|THREAT_AIR); + } + + /* + ** Human controlled infantry don't automatically fire upon buildings. + */ + if (Is_Weapon_Equipped() && House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } + + /* + ** If this is a bomber type, then allow buildings to be considered a threat. + */ + if (Class->IsBomber && !House->IsHuman) { + threat = threat | THREAT_BUILDINGS; + } + + /* + ** Special hack: if it's a thief, then the only possible objects to + ** consider are tiberium-processing objects (silos & refineries). + */ + if (*this == INFANTRY_THIEF) { + threat = threat | THREAT_CAPTURE | THREAT_TIBERIUM; +// threat = (ThreatType)(THREAT_CAPTURE | THREAT_TIBERIUM); + } + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * InfantryClass::Response_Select -- Plays infantry audio response due to being selected. * + * * + * This routine handles playing an audio response as a result of the player selecting the * + * infantry unit. This occurs prior to giving it an order and may not be followed by any * + * order at all. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Select(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response = VOC_NONE; + if (Class->IsFemale) { + response = VOC_GIRL_YEAH; + } else { + response = VOC_GUY_YEAH; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_YES,VOC_ENG_ENG}; + static VocType _ein_response[] = {VOC_E_AH}; + static VocType _dog_response[] = {VOC_DOG_YES}; + static VocType _spy_response[] = {VOC_SPY_COMMANDER,VOC_SPY_YESSIR}; + static VocType _medic_response[] = {VOC_MED_REPORTING,VOC_MED_YESSIR}; + static VocType _tanya_response[] = {VOC_TANYA_YEA,VOC_TANYA_YES,VOC_TANYA_WHATS}; + static VocType _thief_response[] = {VOC_THIEF_YEA,VOC_THIEF_WHAT}; + static VocType _default_response[] = {VOC_ACKNOWL,VOC_REPORT,VOC_REPORT,VOC_YESSIR,VOC_YESSIR,VOC_READY,VOC_AWAIT}; + static VocType _stavros[] = {VOC_STAVCMDR,VOC_STAVYES}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic_response[] = {VOC_MECHHOWDY1,VOC_MECHHUH1,VOC_MECHLAFF1}; + static VocType _shock_response[] = {VOC_STYES1,VOC_STJUMP1,VOC_STJUICE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic_response; + size = ARRAY_SIZE(_mechanic_response); + break; + case INFANTRY_SHOCK: + response = _shock_response; + size = ARRAY_SIZE(_shock_response); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Move -- Plays infantry response to movement order. * + * * + * When the infantry is given the order to move, this routine handles the audio response * + * generated by the infantry unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Move(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _dog_response[] = {VOC_DOG_BARK}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_ROCK}; +#else + static VocType _tanya_response[] = {VOC_TANYA_THERE,VOC_TANYA_GIVE}; +#endif + static VocType _thief_response[] = {VOC_THIEF_MOVEOUT,VOC_THIEF_OKAY,VOC_THIEF_AFFIRM}; + static VocType _default_response[] = {VOC_ROGER,VOC_RIGHT_AWAY,VOC_UGOTIT,VOC_AFFIRM,VOC_AFFIRM}; + static VocType _stavros[] = {VOC_STAVMOV,VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYES1,VOC_MECHRISE1,VOC_MECHHEAR1,VOC_MECHBOSS1}; + static VocType _shock[] = {VOC_STPOWER1,VOC_STDANCE1,VOC_STCHRGE1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; + +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Response_Attack -- Plays infantry audio response to attack order. * + * * + * When the player gives an infantry unit the order to attack, this routine handles * + * the audio response by that unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + * 05/05/1995 JLB : Rambo response types added. * + *=============================================================================================*/ +void InfantryClass::Response_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (!AllowVoice) return; + + if (Class->IsCivilian && *this != INFANTRY_EINSTEIN) { + VocType response; + if (Class->IsFemale) { + response = VOC_GIRL_OKAY; + } else { + response = VOC_GUY_OKAY; + } + Sound_Effect(response, fixed(1), ID+1); + + } else { + static VocType _eng_response[] = {VOC_ENG_AFFIRM,VOC_ENG_AFFIRM}; + static VocType _dog_response[] = {VOC_DOG_GROWL2}; + static VocType _ein_response[] = {VOC_E_OK,VOC_E_YES}; + static VocType _spy_response[] = {VOC_SPY_ONWAY,VOC_SPY_KING,VOC_SPY_INDEED}; + static VocType _medic_response[] = {VOC_MED_AFFIRM,VOC_MED_MOVEOUT}; +#ifdef ENGLISH + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH}; +#else + static VocType _tanya_response[] = {VOC_TANYA_CHEW,VOC_TANYA_CHING,VOC_TANYA_LAUGH,VOC_TANYA_ROCK}; +#endif + static VocType _thief_response[] = {VOC_NONE}; + static VocType _default_response[] = {VOC_RIGHT_AWAY,VOC_AFFIRM,VOC_AFFIRM,VOC_UGOTIT,VOC_NO_PROB,VOC_YESSIR,VOC_YESSIR,VOC_YESSIR}; + static VocType _stavros[] = {VOC_STAVCRSE}; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static VocType _mechanic[] = {VOC_MECHYEEHAW1,VOC_MECHHOTDIG1,VOC_MECHWRENCH1}; + static VocType _shock[] = {VOC_STLIGHT1,VOC_STBURN1,VOC_STCRISP1,VOC_STSHOCK1}; +#endif + + int size = 0; + VocType * response = NULL; + HousesType house = PlayerPtr->ActLike; + switch (Class->Type) { + case INFANTRY_GENERAL: + if (house != HOUSE_USSR && house != HOUSE_BAD) { + response = _stavros; + size = ARRAY_SIZE(_stavros); + } else { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } + house = HOUSE_USSR; + break; + + case INFANTRY_DOG: + response = _dog_response; + size = ARRAY_SIZE(_dog_response); + break; + + case INFANTRY_SPY: + response = _spy_response; + size = ARRAY_SIZE(_spy_response); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(house == HOUSE_USSR) { + response = _default_response; + size = ARRAY_SIZE(_default_response); + } +#endif + break; + + case INFANTRY_EINSTEIN: + response = _ein_response; + size = ARRAY_SIZE(_ein_response); + break; + + case INFANTRY_RENOVATOR: + response = _eng_response; + size = ARRAY_SIZE(_eng_response); + break; + + case INFANTRY_MEDIC: + response = _medic_response; + size = ARRAY_SIZE(_medic_response); + break; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case INFANTRY_MECHANIC: + response = _mechanic; + size = ARRAY_SIZE(_mechanic); + break; + + case INFANTRY_SHOCK: + response = _shock; + size = ARRAY_SIZE(_shock); + break; +#endif + case INFANTRY_TANYA: + response = _tanya_response; + size = ARRAY_SIZE(_tanya_response); + break; + + case INFANTRY_THIEF: + response = _thief_response; + size = ARRAY_SIZE(_thief_response); + break; + + default: + response = _default_response; + size = ARRAY_SIZE(_default_response); + break; + } + if (response != NULL) { + Sound_Effect(response[Sim_Random_Pick(0, size-1)], fixed(1), ID+1, 0, house); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Infantry units might be able to capture -- check. * + * * + * This routine checks to see if the infantry unit can capture the specified object rather * + * than merely attacking it. If this is the case, then ACTION_CAPTURE will be returned. * + * * + * INPUT: object -- The object that the mouse is currently over. * + * * + * OUTPUT: Returns the action that will be performed if the mouse were clicked over the * + * object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/01/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(ObjectClass const * object) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + assert(object != NULL); + + ActionType action = FootClass::What_Action(object); + + /* + ** If this is an engineer/renovator, we have to make some adjustments. + ** If the cursor is over an enemy building, return action-none. If it's + ** over a friendly building, we have to return action-capture so he can + ** renovate it. + ** However, abort the whole thing if the building is a barrel or mine. + */ + if (*this == INFANTRY_RENOVATOR && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + BuildingClass const * bldg = (BuildingClass *)object; + if (bldg->Class->IsRepairable) { + if (House->Is_Ally(bldg)) { + if (bldg->Health_Ratio() == 1) { + return(ACTION_NO_GREPAIR); + } + return(ACTION_GREPAIR); + } else { + + if (bldg->Class->IsCaptureable) { +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + if (bldg->Health_Ratio() <= EngineerCaptureLevel) { +#else + if (bldg->Health_Ratio() <= Rule.ConditionRed) { +#endif + return(ACTION_CAPTURE); + } + return(ACTION_DAMAGE); + } + +// if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Class->IsCaptureable) { + } + } + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. In CSII, if this is a mechanic and the + ** cursor's over a friendly vehicle, execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (House->Is_Ally(object)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (object->What_Am_I() == RTTI_INFANTRY && object != this && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (object->What_Am_I() == RTTI_UNIT || object->What_Am_I() == RTTI_AIRCRAFT) ) ) { + + if (object->Health_Ratio() < Rule.ConditionGreen) { +// If it's a mechanic force-moving into an APC, don't try to heal it. + if(*this == INFANTRY_MECHANIC && object->What_Am_I() == RTTI_UNIT && *(UnitClass *)object == UNIT_APC && (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)) ) { + } else { + return(ACTION_HEAL); + } + } + } +#else + if(object->What_Am_I() == RTTI_INFANTRY && object != this) { + if (object->Health_Ratio() < Rule.ConditionGreen) { + return(ACTION_HEAL); + } + } +#endif + if(!object->Is_Techno() || !((TechnoClass *)object)->Techno_Type_Class()->Max_Passengers()) { + if (action == ACTION_GUARD_AREA || action == ACTION_MOVE) { + return(action); + } + return ((action == ACTION_TOGGLE_SELECT) ? ACTION_TOGGLE_SELECT : ACTION_SELECT); + } + } else { + return(ACTION_NOMOVE); + } + } + +#ifdef OBSOLETE + /* + ** See if it's a thief attacking an enemy vehicle, let him CAPTURE it. + */ + if (*this == INFANTRY_THIEF && object->What_Am_I() == RTTI_UNIT) { + if (((UnitClass *)object)->House != House) { + return(ACTION_CAPTURE); + } + } +#endif + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK && object->What_Am_I() != RTTI_INFANTRY) { + action = ACTION_NONE; + } + + /* + ** See if it's a commando, and if he's attacking a building, + ** have him return ACTION_SABOTAGE instead + */ + if (Class->IsBomber && action == ACTION_ATTACK && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * obj = (BuildingClass *)object; + /* + ** Hack: Tanya should shoot barrels, bomb other structures. + */ + if (obj->Class->IsRepairable) { +// if (*obj != STRUCT_BARREL && *obj != STRUCT_BARREL3) { + return(ACTION_SABOTAGE); + } else { + return(ACTION_ATTACK); + } + } + + /* + ** See if this infantry is trying to move onto where a land mine is. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING && House->IsPlayerControl) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** There is no self-select action available for infantry types. + */ + if (action == ACTION_SELF) { + action = ACTION_NONE; + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (object->What_Am_I() != RTTI_VESSEL || *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((InfantryClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + + if (Class->IsCapture && action == ACTION_ATTACK) { + if (!House->Is_Ally(object) && ( +//Disable capturing of helicopters (object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) || + (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable) ) + ) { + + if (*this == INFANTRY_THIEF && (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->Capacity == 0)) { + action = ACTION_NONE; + } else { + + /* + ** If we're trying to capture a building, make sure we can get + ** to it. Find an adjacent cell that's the same zone as us. + ** The target circumstance is a naval yard that doesn't touch + ** the shore - a total island. In that case, we can't capture + ** it, so we shouldn't show the action-capture cursor. + */ + action = ACTION_CAPTURE; + if (object->What_Am_I() == RTTI_BUILDING) { + CELL cell = ::As_Cell(object->As_Target()); + int targzone = Map[::As_Cell(As_Target())].Zones[Class->MZone]; + short const *list = ((BuildingClass *)object)->Class->Occupy_List(false); + bool found = false; + while (*list != REFRESH_EOL && !found) { + CELL newcell = cell + *list++; + for (FacingType i=FACING_N; i < FACING_COUNT; i++) { + CELL adjcell = Adjacent_Cell(newcell, i); + if ((unsigned)adjcell >= MAP_CELL_TOTAL) continue; + if (Map[adjcell].Zones[Class->MZone] == targzone) { + found = true; + break; + } + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + } else { + if (!Is_Weapon_Equipped()) { + action = ACTION_NONE; + } + } + } + + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Active_Click_With -- Handles action when clicking with infantry soldier. * + * * + * This routine is called when the player clicks over an object while this infantry soldier * + * is selected. Capture attempts are prohibited if the infantry cannot capture. The * + * command might respond if told to sabotage something. * + * * + * INPUT: action -- The action that is nominally to be performed. * + * * + * object -- The object over which the mouse was clicked. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + action = What_Action(object); + + switch (action) { + case ACTION_GREPAIR: + case ACTION_DAMAGE: + case ACTION_CAPTURE: + action = ACTION_CAPTURE; + break; + + case ACTION_HEAL: + action = ACTION_ATTACK; + break; + +// case ACTION_ENTER: +// action = ACTION_MOVE; +// break; + + case ACTION_SABOTAGE: + case ACTION_ATTACK: + case ACTION_GUARD_AREA: + case ACTION_MOVE: + action = action; + break; + + default: +// action = ACTION_NONE; + break; + } + + FootClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * InfantryClass::Set_Occupy_Bit -- Sets the occupy bit cell and bit pos * + * * + * INPUT: CELL - the cell we are setting the bit in * + * * + * int - the spot index we are setting the bit for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=============================================================================================*/ +void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Set the occupy position for the spot that we passed in + */ + Map[cell].Flag.Composite |= (1 << spot_index); + + /* + ** Record the type of infantry that now owns the cell + */ + Map[cell].InfType = Owner(); +} + + +/*************************************************************************** + * InfantryClass::Clear_Occupy_Bit -- Clears occupy bit and given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/08/1995 PWG : Created. * + *=========================================================================*/ +void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + /* + ** Clear the occupy bit for the infantry in that cell + */ + Map[cell].Flag.Composite &= ~(1 << spot_index); + + /* + ** If he was the last infantry recorded in the cell then + ** remove the infantry ownership flag. + */ + if (!(Map[cell].Flag.Composite & 0x1F)) { + Map[cell].InfType = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * InfantryClass::Full_Name -- Fetches the full name of the infantry unit. * + * * + * This routine will return with the full name (as a text number) for this infantry * + * unit. Typically, this is the normal name, but in cases of civilian type survivors from * + * a building explosion, it might be a technician instead. In such a case, the special * + * technician name number is returned instead. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the full name to use for this infantry unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 10/28/1996 JLB : Spy returns "enemy soldier" text name. * + *=============================================================================================*/ +int InfantryClass::Full_Name(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (IsTechnician) { + return(TXT_TECHNICIAN); + } + + if (*this == INFANTRY_SPY && !House->IsPlayerControl) { + return(TXT_E1); + } + + return(Class->Full_Name()); +} + + +/*********************************************************************************************** + * InfantryClass::Mission_Attack -- Intercept attack mission for special handling. * + * * + * This routine intercepts the normal attack mission and if an engineer is detected and the * + * target is a building, then the engineer will be automatically assigned the capture * + * mission. In other cases, the normal attack logic will proceed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + * 04/15/1996 BWG : Engineers can only attack their own house's buildings now. * + * 05/29/1996 JLB : Engineers can now damage/capture enemy buildings. * + *=============================================================================================*/ +int InfantryClass::Mission_Attack(void) +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + if (Class->IsBomber && As_Building(TarCom)) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_SABOTAGE); + return(1); + } + + if (Class->IsCapture && As_Building(TarCom) != NULL && As_Building(TarCom)->Class->IsCaptureable) { + Assign_Destination(TarCom); + Assign_Mission(MISSION_CAPTURE); + return(1); + } + + return(FootClass::Mission_Attack()); +} + + +/*********************************************************************************************** + * InfantryClass::What_Action -- Determines what action to perform for the cell specified. * + * * + * This routine will determine what action to perform if the mouse was clicked on the cell * + * specified. This is just a courier function since the lower level classes actually * + * perform the work. The need for this routine at this level is due to the existence of * + * a similarly named function at this level as well. C++ namespace rules require this * + * function courier to be in place or an error will result. * + * * + * INPUT: cell -- The cell that the mouse might be clicked upon. * + * * + * OUTPUT: Returns with the action that would be given to this infantry unit if the mouse * + * were clicked at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType InfantryClass::What_Action(CELL cell) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + ActionType action = FootClass::What_Action(cell); + + /* + ** Dogs can only attack infantrymen + */ + if (Class->IsDog && action == ACTION_ATTACK) { + action = ACTION_NONE; + } + + /* + ** If this is a medic, and the cursor's over a friendly infantryman, + ** execute an action-attack. + */ + if (Combat_Damage() < 0 && House->IsPlayerControl) { + if (action == ACTION_ATTACK) { + action = ACTION_NOMOVE; + } + } + + /* + ** Demolitioners may destroy a bridge + */ + if (Class->IsBomber && action == ACTION_MOVE && !Special.IsCaptureTheFlag) { + switch (Map[cell].TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: +// case TEMPLATE_BRIDGE_3A: +// case TEMPLATE_BRIDGE_3B: + return(ACTION_SABOTAGE); + } + } + +#ifdef OBSOLETE + /* + ** Engineers may repair a destroyed bridge. + */ + if (*this == INFANTRY_RENOVATOR && action == ACTION_NOMOVE) { + /* + ** If they're pointing on the wrong side of the bridge, ignore it + ** 'cause we can't get there. + */ + TemplateType tt = Map[cell].TType; + if (tt == TEMPLATE_BRIDGE1D || tt == TEMPLATE_BRIDGE2D || + tt == TEMPLATE_BRIDGE_1C || tt == TEMPLATE_BRIDGE_2C || + (tt >= TEMPLATE_BRIDGE_3C && tt <= TEMPLATE_BRIDGE_3E) ) { + /* + ** We know they're pointing at a destroyed bridge cell. If the cell + ** they're pointing at is surrounded by impassables, return this + ** cell as impassable. But, if any cell surrounding this cell is + ** passable, return that this is a capturable cell. + */ + if (Map[cell].Land_Type() == LAND_ROCK) { + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + + if (tt == TEMPLATE_BRIDGE_3C) return(ACTION_CAPTURE); + int y = Cell_Y(cell); + if (y) { + LandType above = Map[(CELL)(cell-(MAP_CELL_W-1))].Land_Type(); + if (above == LAND_CLEAR || above == LAND_ROAD) { + if (Map[(CELL)(cell-(MAP_CELL_W-1))].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + if (y < MAP_CELL_H) { + LandType below = Map[(CELL)(cell + MAP_CELL_W-1)].Land_Type(); + if (below == LAND_CLEAR || below == LAND_ROAD) { + if (Map[(CELL)(cell+MAP_CELL_W-1)].Zone == Map[As_Cell(As_Target())].Zone) { + return(ACTION_CAPTURE); + } + return(ACTION_NOMOVE); + } + } + } + return(ACTION_NOMOVE); + } + } +#endif + return(action); +} + + +/*********************************************************************************************** + * InfantryClass::Class_Of -- Returns the class reference for this object. * + * * + * This routine will return a reference to the infantry type class object that describes * + * this infantry's characteristics. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the InfantryTypeClass object associated with this * + * infantry object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & InfantryClass::Class_Of(void) const +{ + assert(Infantry.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * InfantryClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Cellnum, CellSublocation, Missionname, * + * Facingnum, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Read_INI(CCINIClass & ini) +{ + InfantryClass * infantry; // Working infantry pointer. + HousesType inhouse; // Infantry house. + InfantryType classid; // Infantry class. + char buf[128]; + char * validation; + DirType dir; + TriggerTypeClass * tp; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Get an infantry entry + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** 1st token: house name. + */ + inhouse = HouseTypeClass::From_Name(strtok(buf, ",\n\r")); + if (inhouse != HOUSE_NONE) { + + /* + ** 2nd token: infantry type name. + */ + classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r")); + + if (classid != INFANTRY_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + infantry = new InfantryClass(classid, inhouse); + if (infantry != NULL) { + + /* + ** 3rd token: strength. + */ + int strength = atoi(strtok(NULL, ",\n\r")); + + /* + ** 4th token: cell #. + */ + CELL cell = atoi(strtok(NULL, ",\n\r")); + COORDINATE coord = Cell_Coord(cell); + + /* + ** 5th token: cell sub-location. + */ + int sub = atoi(strtok(NULL, ",")); + coord = Coord_Add(Coord_Whole(coord), StoppingCoordAbs[ sub ]); + + /* + ** Fetch the mission and facing. + */ + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + validation = strtok(NULL, ",\n\r"); + if (validation) { + dir = (DirType)atoi(validation); + validation = strtok(NULL, ",\n\r"); + if (validation) { + tp = TriggerTypeClass::From_Name(validation); + } else { + tp = NULL; + } + } else { + dir = (DirType)0; + tp = NULL; + } + + infantry->Trigger = NULL; + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + infantry->Trigger = tt; + } + } + + if (infantry->Unlimbo(coord, dir)) { + infantry->Strength = (int)infantry->Class_Of().MaxStrength * fixed(strength, 256); + if (infantry->Strength > infantry->Class->MaxStrength-3) infantry->Strength = infantry->Class->MaxStrength; + // infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength); + if (Session.Type == GAME_NORMAL || infantry->House->IsHuman) { + infantry->Assign_Mission(mission); + infantry->Commence(); + } else { + infantry->Enter_Idle_Mode(); + } + } else { + + /* + ** If the infantry could not be unlimboed, then this is a big error. + ** Delete the infantry. + */ + delete infantry; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Write_INI -- Store the infantry to the INI database. * + * * + * This will store all the infantry objects to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database to store the infantry data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing infantry data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the infantry data out. + */ + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (!infantry->IsInLimbo) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%d,%s", + infantry->House->Class->IniName, + infantry->Class->IniName, + infantry->Health_Ratio()*256, + Coord_Cell(infantry->Coord), + CellClass::Spot_Index(infantry->Coord), + MissionClass::Mission_Name((infantry->Mission == MISSION_NONE) ? + infantry->MissionQueue : infantry->Mission), + infantry->PrimaryFacing.Current(), + infantry->Trigger.Is_Valid() ? infantry->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Fear_AI -- Process any fear related affects on this infantry. * + * * + * Use this routine to handle the fear logic for this infantry. It will slowly increase * + * the bravery of the infantry as well as cause it to stand up or lie down as appropriate. * + * It will even handle the special fraidy cat logic for civilian infantry. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once per game logic loop per infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Fear_AI(void) +{ + /* + ** After a time, the infantry will gain courage. + */ + if (Fear > 0) { + + Fear--; + + /* + ** When an armed civilian becomes unafraid, he will then reload + ** another clip into his pistol. + */ + if (Fear == 0 && Ammo == 0 && Is_Weapon_Equipped()) { + Ammo = Class->MaxAmmo; + } + + /* + ** Stand up if brave and lie down if afraid. + */ + if (IsProne) { + if (Fear < FEAR_ANXIOUS) { + Do_Action(DO_GET_UP); + } + } else { + + /* + ** Drop to the ground if anxious. Don't drop to the ground while moving + ** and the special elite flag is active. + */ + if (!Class->IsDog && Height == 0 && Fear >= FEAR_ANXIOUS && ((!Target_Legal(NavCom) && !IsDriving))) { + Do_Action(DO_LIE_DOWN); + } + } + } + + /* + ** When in darkness or in doubt, + ** run in circles, scream, and shout. + */ + if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsFalling && !IsDriving && !Target_Legal(NavCom)) { + Scatter(0, true); + } +} + + +/*********************************************************************************************** + * InfantryClass::Edge_Of_World_AI -- Detects when infantry has left the map. * + * * + * This routine will detect when the infantry has left the edge of the world and will * + * delete it as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the infantry unit deleted by this routine? * + * * + * WARNINGS: Be sure the check the return value and if true, abort any further processing * + * for this infantry unit. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Edge_Of_World_AI(void) +{ + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Team.Is_Valid() && IsLocked) Team->IsLeaveMap = true; + + if (!Team.Is_Valid() && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) { + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * InfantryClass::Firing_AI -- Handles firing and combat AI for the infantry. * + * * + * This will examine the infantry and determine what firing action is required. It will * + * search for targets, starting firing animations, and launch bullets as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Firing_AI(void) +{ + if (Target_Legal(TarCom)) { + int primary = What_Weapon_Should_I_Use(TarCom); + + if (!IsFiring) { + switch (Can_Fire(TarCom, primary)) { + case FIRE_ILLEGAL: + if (Combat_Damage(primary) < 0) { + ObjectClass * targ= As_Object(TarCom); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (targ) { + if( (targ->What_Am_I() == RTTI_INFANTRY && *this == INFANTRY_MEDIC) || + (*this == INFANTRY_MECHANIC && (targ->What_Am_I() == RTTI_AIRCRAFT || targ->What_Am_I() == RTTI_UNIT )) ) { + + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } + } else { + Assign_Target(TARGET_NONE); + } +#else + if (targ && targ->What_Am_I() == RTTI_INFANTRY) { + if (targ->Health_Ratio() >= Rule.ConditionGreen) { + Assign_Target(TARGET_NONE); + } + } else { + Assign_Target(TARGET_NONE); + } +#endif + } else if (Class->IsDog) { + Assign_Target(TARGET_NONE); + } + break; + + case FIRE_CLOAKED: + Do_Uncloak(); + break; + + case FIRE_OK: + /* + ** Start firing animation. + */ + if (IsProne) { + Do_Action(DO_FIRE_PRONE); + } else { + Do_Action(DO_FIRE_WEAPON); + } + + Mark(MARK_OVERLAP_UP); + IsFiring = true; + Mark(MARK_OVERLAP_DOWN); + + PrimaryFacing.Set(Direction8(Center_Coord(), As_Coord(TarCom))); + + /* + ** If the target is in range, and the NavCom is the same, then just + ** stop and keep firing. + */ + if (TarCom == NavCom) { + NavCom = TARGET_NONE; + Path[0] = FACING_NONE; + } + break; + } + } + + /* + ** If in the middle of firing animation, then only + ** process that. Infantry cannot fire and move simultaneously. + ** At some point in the firing animation process, a projectile + ** will be launched. When the required animation frames have + ** been completed, the firing animation stops. + */ + int firestage = Class->FireLaunch; + if (IsProne) firestage = Class->ProneLaunch; + + if (IsFiring && Fetch_Stage() == firestage) { + + /* + ** Target might have changed during the firing animation + */ + if (Can_Fire(TarCom, primary) == FIRE_OK) { + Fire_At(TarCom, primary); + + /* + ** Run away from slowly approaching projectiles. + */ + if (Class->PrimaryWeapon->MaxSpeed < Rule.Incoming) { + Map[::As_Cell(TarCom)].Incoming(Coord, true); + } + + /* + ** If it's a dog, get rid of him (he'll be re-created when he hits) + */ + if (Class->IsDog) { + WasSelected = IsSelected; + ScenarioInit++; + Limbo(); + ScenarioInit--; + } + } else { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + } + } else { + if (IsFiring) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Doing_AI -- Handles the animation AI processing. * + * * + * Infantry can be in one of many different animation sequences. At the conclusion of each * + * sequence, the infantry will quite likely transition to a new animation state. This * + * routine handles detecting when that trasition should occur and starting the infantry * + * into its new state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Doing_AI(void) +{ + if (Doing == DO_NOTHING || Fetch_Stage() >= Class->DoControls[Doing].Count) { + switch (Doing) { + default: + if (IsDriving) { + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL, true); + } else { + Do_Action(DO_WALK, true); + } + } + } else { + if (Class->IsDog) { + Do_Action(DO_STAND_READY, true); + } else { + if (IsProne) { + Do_Action(DO_PRONE, true); + } else { + Do_Action(DO_STAND_READY, true); + } + } + } + break; + + case DO_DOG_MAUL: + Do_Action(DO_STAND_READY, true); + break; + + case DO_GUN_DEATH: + case DO_EXPLOSION_DEATH: + case DO_EXPLOSION2_DEATH: + case DO_GRENADE_DEATH: + case DO_FIRE_DEATH: + if (Fetch_Stage() >= Class->DoControls[Doing].Count) { + AnimClass* anim = NULL; + LandType land = Map[Center_Coord()].Land_Type(); + if (land != LAND_ROCK && land != LAND_WATER && land != LAND_RIVER) { + if (Doing == DO_GUN_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_GRENADE_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE1, Coord_Add(Center_Coord(), XYP_Coord(-10, 3))); + } + if (Doing == DO_EXPLOSION_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE3, Coord_Add(Center_Coord(), XYP_Coord(-2, 4))); + } + if (Doing == DO_EXPLOSION2_DEATH && !Class->IsDog && Height==0) { + anim = new AnimClass(ANIM_CORPSE2, Center_Coord()); + } + } + if (anim != NULL) { + anim->OwnerHouse = House->Class->House; + } + delete this; + return; + } + } + } +} + + +/*********************************************************************************************** + * InfantryClass::Movement_AI -- This routine handles all infantry movement logic. * + * * + * It examines the infantry state and determines what movement action should be initiated * + * or processed. It handles the actual movement of the infantry as well as any path finding * + * or infantry startup logic. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per infantry unit per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void InfantryClass::Movement_AI(void) +{ + /* + ** Special hack check to ensure that infantry will never get stuck in a movement order if + ** there is no place to go. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Enter_Idle_Mode(); + } + + if (!IsFiring && !IsFalling && Doing != DO_DOG_MAUL) { + if (!IsDriving) { + + /* + ** When in guard mode, never allow a valid navcom. + */ + if (Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && Target_Legal(NavCom)) { + Assign_Destination(TARGET_NONE); +// if (IsTethered) Scatter(0, true); + } + + /* + ** Double check to make sure it doesn't have a movement destination into a zone + ** that it can't travel to. In such a case, abort the movement process by clearing + ** the navigation computer. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && !IsDriving && !IsTethered && Target_Legal(NavCom) && IsLocked && Map[Coord].Zones[Class->MZone] != Map[As_Cell(NavCom)].Zones[Class->MZone]) { +// hack: if it's tanya, spy, or engineer, let 'em move there anyway. + if (!Class->IsCapture && Mission != MISSION_ENTER) { +// if (*this != INFANTRY_TANYA && *this != INFANTRY_SPY && *this != INFANTRY_RENOVATOR) { + Assign_Destination(TARGET_NONE); + } + } + + /* + ** A head to coordinate is needed. If there is no path + ** available, then create one. + */ + if (Target_Legal(NavCom) && Strength && Mission != MISSION_GUARD) { + + /* + ** Determine if the next cell in the list is available + ** to be entered. If not, then abort the path and try + ** again. + */ + if (Path[0] != FACING_NONE && Can_Enter_Cell(Adjacent_Cell(Coord_Cell(Center_Coord()), Path[0])) != MOVE_OK) { + Path[0] = FACING_NONE; + } + + /* + ** Check to see if the target is closer than expected. This occurs + ** when heading toward a moving object and that object is heading + ** toward the unit. Shorten the precalculated path to be no longer + ** than the distance to the target. + */ + int d = Lepton_To_Cell(Distance(NavCom)); + if (d < CONQUER_PATH_MAX) { + Path[d] = FACING_NONE; + } + + /* + ** Find a path to follow if one isn't already calculated. + */ + if (Path[0] == FACING_NONE) { + + /* + ** Calculate the path from the current location to the + ** destination indicated by the navigation computer. If there + ** was a fundamental error with finding a path, then this + ** indicates that basic path & movement logic needs to be + ** aborted. + */ + if (PathDelay != 0) { + return; + } + if (!Basic_Path()) { + + /* + ** Check to ensure that if a computer controlled unit is in + ** hunt mode, but cannot reach the target it would like to, + ** abort the target tracking and let the normal hunt logic + ** assign a new one. + */ + if (!House->IsHuman && Mission == MISSION_HUNT) { + Assign_Destination(TARGET_NONE); + Assign_Target(TARGET_NONE); + } else { + + /* + ** If the infantry unit is close enough to the target, then + ** tell it to stop. + */ + if (Distance(NavCom) < Rule.CloseEnoughDistance && !IsTethered) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** Update the try try again counter so that this + ** infantry unit will try again at a later time. + */ + if (TryTryAgain) { + TryTryAgain--; + } else { + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + //If we're trying to enter a transport we need to fail so others can try to enter. - LLL 4/17/2020 + if (Mission == MISSION_ENTER) { + Mission = MISSION_NONE; + Assign_Mission(MISSION_GUARD); + Commence(); + + Transmit_Message(RADIO_OVER_OUT); + } + + /* + ** Abort the target and destination process since the path + ** could not be found. In such a case, processing should stop + ** or else the game will bog down with repeated path failures. + ** Only perform the abort of the target is in a different zone. + */ + if ((!IsZoneCheat || Can_Enter_Cell(Coord_Cell(Coord)) != MOVE_NO) && IsLocked && Target_Legal(NavCom) && Map[As_Cell(NavCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Destination(TARGET_NONE); + } + if (IsLocked && Target_Legal(TarCom) && Map[As_Cell(TarCom)].Zones[Class->MZone] != Map[Coord].Zones[Class->MZone]) { + Assign_Target(TARGET_NONE); + } + } + } + } + Stop_Driver(); + return; + } + TryTryAgain = PATH_RETRY; + } + + /* + ** Determine the coordinate to head to based on the infantry's + ** current location and the next location in the path. + */ + COORDINATE acoord = Adjacent_Cell(Coord, Path[0]); + CELL acell = Coord_Cell(acoord); + + if (Can_Enter_Cell(acell) != MOVE_OK) { + + if ((Mission == MISSION_MOVE || Mission == MISSION_ENTER) && !IsTethered /*&& House->IsHuman*/ && Distance(NavCom) < Rule.CloseEnoughDistance) { + Assign_Destination(TARGET_NONE); + } else { + + /* + ** If blocked by a moving block then just exit start of move and + ** try again next tick. + */ + if (Can_Enter_Cell(acell) == MOVE_DESTROYABLE) { + if (Map[acell].Cell_Object()) { + if (!House->Is_Ally(Map[acell].Cell_Object())) { + Override_Mission(MISSION_ATTACK, Map[acell].Cell_Object()->As_Target(), TARGET_NONE); + } + } else { + if (Map[acell].Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(Map[acell].Overlay).IsWall) { + Override_Mission(MISSION_ATTACK, ::As_Target(acell), TARGET_NONE); + } + } + } + } + + Path[0] = FACING_NONE; + Stop_Driver(); + if (IsNewNavCom) Sound_Effect(VOC_SCOLD); + IsNewNavCom = false; + + } else { + if (Start_Driver(acoord)) { + if (!IsActive) return; + PrimaryFacing.Set(Direction8(Center_Coord(), Head_To_Coord())); + if (IsFormationMove) { + Set_Speed(Ground[Map[Coord].Land_Type()].Cost[FormationSpeed] * 255); + } else { + Set_Speed(0xFF); + } + + if (Class->IsDog) { + + /* + ** Dog crawl animation is actually the run animation. + */ + if (Target_Legal(TarCom)) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } else { + if (IsProne) { + Do_Action(DO_CRAWL); + } else { + Do_Action(DO_WALK); + } + } + } + } + } + + } else { + + /* + ** The infantry knows where it should be headed, so head there. Check + ** to see if the infantry is "close enough" to the desired location that + ** it should just consider itself to have arrived. In this case, force + ** the infantry to the destination location and mark this path step + ** as complete. + */ + Mark(MARK_UP); + if (Distance(Head_To_Coord()) < 0x0010) { + + memcpy(&Path[0], &Path[1], sizeof(Path)-sizeof(Path[0])); + Path[(sizeof(Path)/sizeof(Path[0]))-1] = FACING_NONE; + Coord = Head_To_Coord(); + Per_Cell_Process(PCP_END); + if (!IsActive || IsInLimbo) return; + + Stop_Driver(); + if (!IsActive || IsInLimbo) return; + + if (Coord_Cell(Coord) == As_Cell(NavCom)) { + NavCom = TARGET_NONE; + if (Mission == MISSION_MOVE) { + Enter_Idle_Mode(); + } + //Stop_Driver(); + Path[0] = FACING_NONE; + } + } else { + int movespeed = Speed; + + /* + ** When prone, the infantry moves at half speed or double + ** speed. This depends on whether the infantry actually has + ** prone animation stages. Civilians don't, and so they + ** run instead. + */ + if (Class->IsDog && Target_Legal(TarCom)) { + movespeed *= 2; + } + + if (IsProne && !Class->IsDog) { + if ((Class->IsFraidyCat && !Class->IsCrawling) ) { + movespeed = Speed*2; + } else { + movespeed = Speed/2; + } + } + + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + + /* + ** Advance the infantry as far as it should go. + */ + MPHType maxspeed = MPHType(min(Class->MaxSpeed * SpeedBias * House->GroundspeedBias, MPH_LIGHT_SPEED)); + + if (IsFormationMove) maxspeed = FormationMaxSpeed; + + Coord = Coord_Move(Coord, Direction(Head_To_Coord()), maxspeed * fixed(movespeed, 256)); + } + Mark(MARK_DOWN); + } + IsNewNavCom = false; + } +} + + +/*********************************************************************************************** + * InfantryClass::Get_Image_Data -- Fetches the image data for this infantry unit. * + * * + * The image data for the infantry differs from normal if this is a spy. A spy always * + * appears like a minigunner to the non-owning players. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the image data to use for this infantry soldier. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * InfantryClass::Get_Image_Data(void) const +{ + if (!IsOwnedByPlayer && *this == INFANTRY_SPY) { + return(MFCD::Retrieve("E1.SHP")); + } + return(TechnoClass::Get_Image_Data()); +} + + +/*********************************************************************************************** + * InfantryClass::Is_Ready_To_Random_Anima -- Checks to see if it is ready to perform an idle * + * * + * This routine will examine this infantry and determine if it is allowed and ready to * + * perform an idle animation. The conditions under which idle animations can be performed * + * are restrictive. Hence this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this infantry ready to do an idle animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/01/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Is_Ready_To_Random_Animate(void) const +{ + /* + ** See if the base classes (more rudimentary checking) determines that idle animations + ** cannot occur. If they cannot, then return with the failure code. + */ + if (!FootClass::Is_Ready_To_Random_Animate()) { + return(false); + } + + /* + ** While the infantry is in the air (such as when paradropping), it won't be allowed + ** to idle animate. + */ + if (Height > 0) { + return(false); + } + + /* + ** When the infantry is walking or otherwise engauged in travel, it won't idle animate. + */ + if (IsDriving) { + return(false); + } + + /* + ** When prone, idle animations cannot occur. This is primarily because there are no prone + ** idle animations. + */ + if (IsProne) { + return(false); + } + + /* + ** When firing, the infantry should not perform any idle animations. + */ + if (IsFiring) { + return(false); + } + + /* + ** Only if the infantry is in guard or ready stance is idle animations allowed. This is + ** because the idle animations start and end with these frames. + */ + if (Doing != DO_STAND_GUARD && Doing != DO_STAND_READY) { + return(false); + } + + /* + ** Since no reason was found to indicate it is not a good time to idle + ** animate, then it must be a good time to do so. + */ + return(true); +} + + +/*********************************************************************************************** + * InfantryClass::Paradrop -- Handles paradropping infantry. * + * * + * This routine will paradrop this soldier at the location specified. It will cause the * + * soldier to hunt if controlled by the computer and to guard if controlledy by the * + * human. * + * * + * INPUT: coord -- The coordinate to paradrop the soldier to. * + * * + * OUTPUT: bool; Was the paradrop successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool InfantryClass::Paradrop(COORDINATE coord) +{ + if (FootClass::Paradrop(coord)) { + if (House->IsHuman) { + Assign_Mission(MISSION_GUARD); + } else { + Assign_Mission(MISSION_HUNT); + } + return(true); + } + return(false); +} \ No newline at end of file diff --git a/code/redalert/infantry.h b/code/redalert/infantry.h new file mode 100644 index 0000000..332c746 --- /dev/null +++ b/code/redalert/infantry.h @@ -0,0 +1,239 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INFANTRY.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INFANTRY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 15, 1994 * + * * + * Last Update : August 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INFANTRY_H +#define INFANTRY_H + + +class InfantryClass : public FootClass +{ + public: + CCPtr Class; + + /* + ** If the infantry is undergoing some choreographed animation sequence, then + ** this holds the particular sequence number. The frame of animation is kept + ** track of by the regular frame tracking system. When performing an animation + ** sequence, the infantry cannot perform anything else (even move). + */ + DoType Doing; + + /* + ** Certain infantry will either perform some comment or say something after an + ** amount of time has expired subsequent to an significant event. This is the + ** timer the counts down. + */ + CDTimerClass Comment; + + /* + ** If this civilian is actually a technician, then this flag will be true. + ** It should only be set for the civilian type infantry. Typically, the + ** technician appears after a building is destroyed. + */ + unsigned IsTechnician:1; + + /* + ** If the infantry just performed some feat, then it may respond with an action. + ** This flag will be true if an action is to be performed when the Comment timer + ** has expired. + */ + unsigned IsStoked:1; + + /* + ** This flag indicates if the infantry unit is prone. Prone infantry become that way + ** when they are fired upon. Infantry in the prone position are less vulnerable to + ** combat. + */ + unsigned IsProne:1; + + /* + ** If the infantry is allowed to move one cell from one zone to another, then this + ** flag will be true. It exists only so that when a bridge is destroyed, the bomb + ** placer is allowed to run from the destroyed bridge cell back onto a real cell. + */ + unsigned IsZoneCheat:1; + + /* + ** This flag is set for the dogs, when they launch into bullet mode. + ** it's to remember if the unit was selected, and if it was, then + ** when the dog is re-enabled, he'll reselect himself. + */ + unsigned WasSelected:1; + + /* + ** The fear rating of this infantry unit. The more afraid the infantry, the more + ** likely it is to panic and seek cover. + */ + FearType Fear; + + /* + ** Track when movement last stopped. + */ + long StopDriverFrame; + + /* + ** Track the last cell we looked from. + */ + CELL LookCell; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + InfantryClass(InfantryType classid, HousesType house); + InfantryClass(NoInitClass const & x) : FootClass(x), Class(x), Comment(x) {}; + virtual ~InfantryClass(void); + operator InfantryType(void) const {return Class->Type;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + static void Init(void); + + virtual void Assign_Destination(TARGET); + + /* + ** Query functions. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + void const * Get_Image_Data(void) const; + int Shape_Number(WindowNumberType window = WINDOW_MAIN) const; + virtual ObjectTypeClass const & Class_Of(void) const; + virtual int Full_Name(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType facing); + virtual bool Paradrop(COORDINATE coord); + virtual bool Limbo(void); + virtual void Detach(TARGET target, bool all); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw = false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell) {FootClass::Active_Click_With(action, cell);} + + /* + ** Combat related. + */ + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual BulletClass * Fire_At(TARGET target, int which); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual void Assign_Target(TARGET); + void Set_Occupy_Bit(COORDINATE coord) {Set_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Set_Occupy_Bit(CELL cell, int spot_index); + void Clear_Occupy_Bit(COORDINATE coord) {Clear_Occupy_Bit(Coord_Cell(coord), CellClass::Spot_Index(coord));}; + void Clear_Occupy_Bit(CELL cell, int spot_index); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Stop_Driver(void); + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual void AI(void); + void Fear_AI(void); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual int Mission_Attack(void); + bool Edge_Of_World_AI(void); + void Firing_AI(void); + void Doing_AI(void); + void Movement_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "INFANTRY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Movement and animation. + */ + virtual bool Do_Action(DoType todo, bool force=false); + virtual bool Random_Animate(void); + virtual MoveType Can_Enter_Cell(CELL , FacingType =FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + virtual void Look(bool incremental=false); + + /* + ** Translation table to convert facing into infantry shape number. This special + ** table is needed since several facing stages are reused and flipped about the Y + ** axis. + */ + static int const HumanShape[32]; + + private: + + static DoStruct const MasterDoControls[DO_COUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/ini.cpp b/code/redalert/ini.cpp new file mode 100644 index 0000000..c195c32 --- /dev/null +++ b/code/redalert/ini.cpp @@ -0,0 +1,1287 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INI.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * INIClass::Find_Entry -- Find specified entry within section. * + * INIClass::Find_Section -- Find the specified section within the INI data. * + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name* + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * INIClass::Load -- Load INI data from the file specified. * + * INIClass::Load -- Load the INI data from the data stream (straw). * + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * INIClass::Put_PKey -- Stores the key to the INI database. * + * INIClass::Put_String -- Output a string to the section and entry specified. * + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * INIClass::Save -- Save the ini data to the file specified. * + * INIClass::Save -- Saves the INI data to a pipe stream. * + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * INIClass::~INIClass -- Destructor for INI handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include "ini.h" +#include "readline.h" +#include "xpipe.h" +#include "b64pipe.h" +#include "xstraw.h" +#include "b64straw.h" + + +#ifdef FIXIT_FAST_LOAD +#include "cstraw.h" +#endif + + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +//#pragma warning 665 9 + + +/*********************************************************************************************** + * INIClass::~INIClass -- Destructor for INI handler. * + * * + * This is the destructor for the INI class. It handles deleting all of the allocations * + * it might have done. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::~INIClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * INIClass::Clear -- Clears out a section (or all sections) of the INI data. * + * * + * This routine is used to clear out the section specified. If no section is specified, * + * then the entire INI data is cleared out. Optionally, this routine can be used to clear * + * out just an individual entry in the specified section. * + * * + * INPUT: section -- Pointer to the section to clear out [pass NULL to clear all]. * + * * + * entry -- Pointer to optional entry specifier. If this parameter is specified, * + * then only this specific entry (if found) will be cleared. Otherwise, * + * the entire section specified will be cleared. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 08/21/1996 JLB : Optionally clears section too. * + * 11/02/1996 JLB : Updates the index list. * + *=============================================================================================*/ +bool INIClass::Clear(char const * section, char const * entry) +{ + if (section == NULL) { + SectionList.Delete(); + SectionIndex.Clear(); + } else { + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + if (entry != NULL) { + INIEntry * entptr = secptr->Find_Entry(entry); + if (entptr != NULL) { + /* + ** Remove the entry from the entry index list. + */ + secptr->EntryIndex.Remove_Index(entptr->Index_ID()); + + delete entptr; + } + } else { + /* + ** Remove this section index from the section index list. + */ + SectionIndex.Remove_Index(secptr->Index_ID()); + + delete secptr; + } + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load INI data from the file specified. * + * * + * Use this routine to load the INI class with the data from the specified file. * + * * + * INPUT: file -- Reference to the file that will be used to fill up this INI manager. * + * * + * OUTPUT: bool; Was the file loaded successfully? * + * * + * WARNINGS: This routine allocates memory. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Load(FileClass & file) +{ + return(Load(FileStraw(file))); +} + + +/*********************************************************************************************** + * INIClass::Load -- Load the INI data from the data stream (straw). * + * * + * This will fetch data from the straw and build an INI database from it. * + * * + * INPUT: straw -- The straw that the data will be provided from. * + * * + * OUTPUT: bool; Was the database loaded ok? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_FAST_LOAD +bool INIClass::Load(Straw & ffile) +#else +bool INIClass::Load(Straw & file) +#endif +{ + bool end_of_file = false; + char buffer[MAX_LINE_LENGTH]; + +#ifdef FIXIT_FAST_LOAD + CacheStraw file; + file.Get_From(ffile); +#endif + + /* + ** Prescan until the first section is found. + */ + while (!end_of_file) { + Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (end_of_file) return(false); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + } + + /* + ** Process a section. The buffer is prefilled with the section name line. + */ + while (!end_of_file) { + + buffer[0] = ' '; + char * ptr = strchr(buffer, ']'); + if (ptr) *ptr = '\0'; + strtrim(buffer); + INISection * secptr = new INISection(strdup(buffer)); + if (secptr == NULL) { + Clear(); + return(false); + } + + /* + ** Read in the entries of this section. + */ + while (!end_of_file) { + + /* + ** If this line is the start of another section, then bail out + ** of the entry loop and let the outer section loop take + ** care of it. + */ + int len = Read_Line(file, buffer, sizeof(buffer), end_of_file); + if (buffer[0] == '[' && strchr(buffer, ']') != NULL) break; + + /* + ** Determine if this line is a comment or blank line. Throw it out if it is. + */ + Strip_Comments(buffer); + if (len == 0 || buffer[0] == ';' || buffer[0] == '=') continue; + + /* + ** The line isn't an obvious comment. Make sure that there is the "=" character + ** at an appropriate spot. + */ + char * divider = strchr(buffer, '='); + if (!divider) continue; + + /* + ** Split the line into entry and value sections. Be sure to catch the + ** "=foobar" and "foobar=" cases. These lines are ignored. + */ + *divider++ = '\0'; + strtrim(buffer); + if (!strlen(buffer)) continue; + + strtrim(divider); + if (!strlen(divider)) continue; + + INIEntry * entryptr = new INIEntry(strdup(buffer), strdup(divider)); + if (entryptr == NULL) { + delete secptr; + Clear(); + return(false); + } + + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + secptr->EntryList.Add_Tail(entryptr); + } + + /* + ** All the entries for this section have been parsed. If this section is blank, then + ** don't bother storing it. + */ + if (secptr->EntryList.Is_Empty()) { + delete secptr; + } else { + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + SectionList.Add_Tail(secptr); + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Save -- Save the ini data to the file specified. * + * * + * Use this routine to save the ini data to the file specified. All existing data in the * + * file, if it was present, is replaced. * + * * + * INPUT: file -- Reference to the file to write the INI data to. * + * * + * OUTPUT: bool; Was the data written to the file? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(FileClass & file) const +{ + return(Save(FilePipe(file))); +} + + +/*********************************************************************************************** + * INIClass::Save -- Saves the INI data to a pipe stream. * + * * + * This routine will output the data of the INI file to a pipe stream. * + * * + * INPUT: pipe -- Reference to the pipe stream to pump the INI image to. * + * * + * OUTPUT: Returns with the number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Save(Pipe & pipe) const +{ + int total = 0; + + INISection * secptr = SectionList.First(); + while (secptr && secptr->Is_Valid()) { + + /* + ** Output the section identifier. + */ + total += pipe.Put("[", 1); + total += pipe.Put(secptr->Section, strlen(secptr->Section)); + total += pipe.Put("]", 1); + total += pipe.Put("\r\n", strlen("\r\n")); + + /* + ** Output all the entries and values in this section. + */ + INIEntry * entryptr = secptr->EntryList.First(); + while (entryptr && entryptr->Is_Valid()) { + total += pipe.Put(entryptr->Entry, strlen(entryptr->Entry)); + total += pipe.Put("=", 1); + total += pipe.Put(entryptr->Value, strlen(entryptr->Value)); + total += pipe.Put("\r\n", strlen("\r\n")); + + entryptr = entryptr->Next(); + } + + /* + ** After the last entry in this section, output an extra + ** blank line for readability purposes. + */ + total += pipe.Put("\r\n", strlen("\r\n")); + + secptr = secptr->Next(); + } + total += pipe.End(); + + return(total); +} + + +/*********************************************************************************************** + * INIClass::Find_Section -- Find the specified section within the INI data. * + * * + * This routine will scan through the INI data looking for the section specified. If the * + * section could be found, then a pointer to the section control data is returned. * + * * + * INPUT: section -- The name of the section to search for. Don't enclose the name in * + * brackets. Case is NOT sensitive in the search. * + * * + * OUTPUT: Returns with a pointer to the INI section control structure if the section was * + * found. Otherwise, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +INIClass::INISection * INIClass::Find_Section(char const * section) const +{ + if (section != NULL) { + + long crc = CRCEngine()(section, strlen(section)); + + if (SectionIndex.Is_Present(crc)) { + return(SectionIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Section_Count -- Counts the number of sections in the INI data. * + * * + * This routine will scan through all the sections in the INI data and return a count * + * of the number it found. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of sections recorded in the INI data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Section_Count(void) const +{ + return(SectionIndex.Count()); +} + + +/*********************************************************************************************** + * INIClass::Entry_Count -- Fetches the number of entries in a specified section. * + * * + * This routine will examine the section specified and return with the number of entries * + * associated with it. * + * * + * INPUT: section -- Pointer to the section that will be examined. * + * * + * OUTPUT: Returns with the number entries in the specified section. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index manager. * + *=============================================================================================*/ +int INIClass::Entry_Count(char const * section) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->EntryIndex.Count()); + } + return(0); +} + + +/*********************************************************************************************** + * INIClass::Find_Entry -- Find specified entry within section. * + * * + * This support routine will find the specified entry in the specified section. If found, * + * a pointer to the entry control structure will be returned. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- Pointer to the entry name to search for. * + * * + * OUTPUT: If the entry was found, then a pointer to the entry control structure will be * + * returned. Otherwise, NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::Find_Entry(char const * section, char const * entry) const +{ + INISection * secptr = Find_Section(section); + if (secptr != NULL) { + return(secptr->Find_Entry(entry)); + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Get_Entry -- Get the entry identifier name given ordinal number and section name. * + * * + * This will return the identifier name for the entry under the section specified. The * + * ordinal number specified is used to determine which entry to retrieve. The entry * + * identifier is the text that appears to the left of the "=" character. * + * * + * INPUT: section -- The section to use. * + * * + * index -- The ordinal number to use when fetching an entry name. * + * * + * OUTPUT: Returns with a pointer to the entry name. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +char const * INIClass::Get_Entry(char const * section, int index) const +{ + INISection * secptr = Find_Section(section); + + if (secptr != NULL && index < secptr->EntryIndex.Count()) { + INIEntry * entryptr = secptr->EntryList.First(); + + while (entryptr != NULL && entryptr->Is_Valid()) { + if (index == 0) return(entryptr->Entry); + index--; + entryptr = entryptr->Next(); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_UUBlock -- Store a binary encoded data block into the INI database. * + * * + * Use this routine to store an arbitrary length binary block of data into the INI database.* + * This routine will covert the data into displayable form and then break it into lines * + * that are stored in sequence to the section. A section used to store data in this * + * fashion can not be used for any other entries. * + * * + * INPUT: section -- The section identifier to place the data into. * + * * + * block -- Pointer to the block of binary data to store. * + * * + * len -- The length of the binary data. * + * * + * OUTPUT: bool; Was the data stored to the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_UUBlock(char const * section, void const * block, int len) +{ + if (section == NULL || block == NULL || len < 1) return(false); + + Clear(section); + + BufferStraw straw(block, len); + Base64Straw bstraw(Base64Straw::ENCODE); + bstraw.Get_From(straw); + + int counter = 1; + + for (;;) { + char buffer[71]; + char sbuffer[32]; + + int length = bstraw.Get(buffer, sizeof(buffer)-1); + buffer[length] = '\0'; + if (length == 0) break; + + sprintf(sbuffer, "%d", counter); + Put_String(section, sbuffer, buffer); + counter++; + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_UUBlock -- Fetch an encoded block from the section specified. * + * * + * This routine will take all the entries in the specified section and decompose them into * + * a binary block of data that will be stored into the buffer specified. By using this * + * routine [and the Put_UUBLock counterpart], arbitrary blocks of binary data may be * + * stored in the INI file. A section processed by this routine can contain no other * + * entries than those put there by a previous call to Put_UUBlock. * + * * + * INPUT: section -- The section name to process. * + * * + * block -- Pointer to the buffer that will hold the retrieved data. * + * * + * len -- The length of the buffer. The retrieved data will not fill past this * + * limit. * + * * + * OUTPUT: Returns with the number of bytes decoded into the buffer specified. * + * * + * WARNINGS: If the number of bytes retrieved exactly matches the length of the buffer * + * specified, then you might have a condition of buffer "overflow". * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_UUBlock(char const * section, void * block, int len) const +{ + if (section == NULL) return(0); + + Base64Pipe b64pipe(Base64Pipe::DECODE); + BufferPipe bpipe(block, len); + + b64pipe.Put_To(&bpipe); + + int total = 0; + int counter = Entry_Count(section); + for (int index = 0; index < counter; index++) { + char buffer[128]; + + int length = Get_String(section, Get_Entry(section, index), "=", buffer, sizeof(buffer)); + int outcount = b64pipe.Put(buffer, length); + total += outcount; + } + total += b64pipe.End(); + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_TextBlock -- Stores a block of text into an INI section. * + * * + * This routine will take an arbitrarily long block of text and store it into the INI * + * database. The text is broken up into lines and each line is then stored as a numbered * + * entry in the specified section. A section used to store text in this way can not be used * + * to hold any other entries. The text is presumed to contain space characters scattered * + * throughout it and that one space between words and sentences is natural. * + * * + * INPUT: section -- The section to place the text block into. * + * * + * text -- Pointer to a null terminated text string that holds the block of * + * text. The length can be arbitrary. * + * * + * OUTPUT: bool; Was the text block placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_TextBlock(char const * section, char const * text) +{ + if (section == NULL) return(false); + + Clear(section); + + int index = 1; + while (text != NULL && *text != NULL) { + + char buffer[128]; + + strncpy(buffer, text, 75); + buffer[75] = '\0'; + + char b[32]; + sprintf(b, "%d", index); + + /* + ** Scan backward looking for a good break position. + */ + int count = strlen(buffer); + if (count > 0) { + if (count >= 75) { + while (count) { + char c = buffer[count]; + + if (isspace(c)) break; + count--; + } + + if (count == 0) { + break; + } else { + buffer[count] = '\0'; + } + } + + strtrim(buffer); + Put_String(section, b, buffer); + index++; + text = ((char *)text) + count; + } else { + break; + } + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_TextBlock -- Fetch a block of normal text. * + * * + * This will take all entries in the specified section and format them into a block of * + * normalized text. That is, text with single spaces between each concatenated line. All * + * entries in the specified section are processed by this routine. Use Put_TextBlock to * + * build the entries in the section. * + * * + * INPUT: section -- The section name to process. * + * * + * buffer -- Pointer to the buffer that will hold the complete text. * + * * + * len -- The length of the buffer specified. The text will, at most, fill this * + * buffer with the last character being forced to null. * + * * + * OUTPUT: Returns with the number of characters placed into the buffer. The trailing null * + * is not counted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_TextBlock(char const * section, char * buffer, int len) const +{ + if (len <= 0) return(0); + + buffer[0] = '\0'; + if (len <= 1) return(0); + + int elen = Entry_Count(section); + int total = 0; + for (int index = 0; index < elen; index++) { + + /* + ** Add spacers between lines of fetched text. + */ + if (index > 0) { + *buffer++ = ' '; + len--; + total++; + } + + Get_String(section, Get_Entry(section, index), "", buffer, len); + + int partial = strlen(buffer); + total += partial; + buffer += partial; + len -= partial; + if (len <= 1) break; + } + return(total); +} + + +/*********************************************************************************************** + * INIClass::Put_Int -- Stores a signed integer into the INI data base. * + * * + * Use this routine to store an integer value into the section and entry specified. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier used for the integer number. * + * * + * number -- The integer number to store in the database. * + * * + * format -- The format to store the integer. The format is generally only a * + * cosmetic affect. The Get_Int operation will interpret the value the * + * same regardless of what format was used to store the integer. * + * * + * 0 : plain decimal digit * + * 1 : hexadecimal digit (trailing "h") * + * 2 : hexadecimal digit (leading "$") * + * * + * OUTPUT: bool; Was the number stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +bool INIClass::Put_Int(char const * section, char const * entry, int number, int format) +{ + char buffer[MAX_LINE_LENGTH]; + + switch (format) { + default: + case 0: + sprintf(buffer, "%d", number); + break; + + case 1: + sprintf(buffer, "%Xh", number); + break; + + case 2: + sprintf(buffer, "$%X", number); + break; + } + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Int -- Fetch an integer entry from the specified section. * + * * + * This routine will fetch an integer value from the entry and section specified. If no * + * entry could be found, then the default value will be returned instead. * + * * + * INPUT: section -- The section name to search under. * + * * + * entry -- The entry name to search for. * + * * + * defvalue -- The default value to use if the specified entry could not be found. * + * * + * OUTPUT: Returns with the integer value specified in the INI database or else returns the * + * default value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 07/10/1996 JLB : Handles multiple integer formats. * + *=============================================================================================*/ +int INIClass::Get_Int(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + + if (*entryptr->Value == '$') { + sscanf(entryptr->Value, "$%x", &defvalue); + } else { + if (tolower(entryptr->Value[strlen(entryptr->Value)-1]) == 'h') { + sscanf(entryptr->Value, "%xh", &defvalue); + } else { + defvalue = atoi(entryptr->Value); + } + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_Hex -- Store an integer into the INI database, but use a hex format. * + * * + * This routine is similar to the Put_Int routine, but the number is stored as a hexadecimal* + * number. * + * * + * INPUT: section -- The identifier for the section that the entry will be placed in. * + * * + * entry -- The entry identifier to tag to the integer number specified. * + * * + * number -- The number to assign the the specified entry and placed in the * + * specified section. * + * * + * OUTPUT: bool; Was the number placed into the INI database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Hex(char const * section, char const * entry, int number) +{ + char buffer[MAX_LINE_LENGTH]; + + sprintf(buffer, "%X", number); + return(Put_String(section, entry, buffer)); +} + + +/*********************************************************************************************** + * INIClass::Get_Hex -- Fetches integer [hex format] from the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. The * + * value is interpreted as a hexadecimal number and then returned. If no entry could be * + * found, then the default value is returned instead. * + * * + * INPUT: section -- The section identifier to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- The default value to use if the entry could not be located. * + * * + * OUTPUT: Returns with the integer value from the specified section and entry. If no entry * + * could be found, then the default value will be returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_Hex(char const * section, char const * entry, int defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + sscanf(entryptr->Value, "%x", &defvalue); + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::Put_String -- Output a string to the section and entry specified. * + * * + * This routine will put an arbitrary string to the section and entry specified. Any * + * previous matching entry will be replaced. * + * * + * INPUT: section -- The section identifier to place the string under. * + * * + * entry -- The entry identifier to identify this string [placed under the section]* + * * + * string -- Pointer to the string to assign to this entry. * + * * + * OUTPUT: bool; Was the entry assigned without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +bool INIClass::Put_String(char const * section, char const * entry, char const * string) +{ + if (section == NULL || entry == NULL) return(false); + + INISection * secptr = Find_Section(section); + + if (secptr == NULL) { + secptr = new INISection(strdup(section)); + if (secptr == NULL) return(false); + SectionList.Add_Tail(secptr); + SectionIndex.Add_Index(secptr->Index_ID(), secptr); + } + + /* + ** Remove the old entry if found. + */ + INIEntry * entryptr = secptr->Find_Entry(entry); + if (entryptr != NULL) { + secptr->EntryIndex.Remove_Index(entryptr->Index_ID()); + delete entryptr; + } + + /* + ** Create and add the new entry. + */ + if (string != NULL && strlen(string) > 0) { + entryptr = new INIEntry(strdup(entry), strdup(string)); + + if (entryptr == NULL) { + return(false); + } + secptr->EntryList.Add_Tail(entryptr); + secptr->EntryIndex.Add_Index(entryptr->Index_ID(), entryptr); + } + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_String -- Fetch the value of a particular entry in a specified section. * + * * + * This will retrieve the entire text to the right of the "=" character. The text is * + * found by finding a matching entry in the section specified. If no matching entry could * + * be found, then the default value will be stored in the output string buffer. * + * * + * INPUT: section -- Pointer to the section name to search under. * + * * + * entry -- The entry identifier to search for. * + * * + * defvalue -- If no entry could be found, then this text will be returned. * + * * + * buffer -- Output buffer to store the retrieved string into. * + * * + * size -- The size of the output buffer. The maximum string length that could * + * be retrieved will be one less than this length. This is due to the * + * forced trailing zero added to the end of the string. * + * * + * OUTPUT: Returns with the length of the string retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int INIClass::Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const +{ + /* + ** Verify that the parameters are nominally legal. + */ + if (section == NULL || entry == NULL) { + if (buffer != NULL && size > 0) { + buffer[0] = '\0'; + } + return(0); + } + + /* + ** Fetch the entry string if it is present. If not, then the normal default + ** value will be used as the entry value. + */ + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr) { + if (entryptr->Value) { + defvalue = entryptr->Value; + } + } + + /* + ** Fill in the buffer with the entry value and return with the length of the string. + */ + if (buffer == NULL || !size) { + return(0); + } else if (defvalue == NULL) { + buffer[0] = '\0'; + return(0); + } else if (buffer == defvalue) { + return(strlen(buffer)); + } else { + strncpy(buffer, defvalue, size); + buffer[size-1] = '\0'; + strtrim(buffer); + return(strlen(buffer)); + } +} + + +/*********************************************************************************************** + * INIClass::Put_Bool -- Store a boolean value into the INI database. * + * * + * Use this routine to place a boolean value into the INI database. The boolean value will * + * be stored as "yes" or "no". * + * * + * INPUT: section -- The section to place the entry and boolean value into. * + * * + * entry -- The entry identifier to tag to the boolean value. * + * * + * value -- The boolean value to place into the database. * + * * + * OUTPUT: bool; Was the boolean value placed into the database? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Bool(char const * section, char const * entry, bool value) +{ + if (value) { + return(Put_String(section, entry, "yes")); + } else { + return(Put_String(section, entry, "no")); + } +} + + +/*********************************************************************************************** + * INIClass::Get_Bool -- Fetch a boolean value for the section and entry specified. * + * * + * This routine will search under the section specified, looking for a matching entry. If * + * one is found, the value is interpreted as a boolean value and then returned. In the case * + * of no matching entry, the default value will be returned instead. The boolean value * + * is interpreted using the standard boolean conventions. e.g., "Yes", "Y", "1", "True", * + * "T" are all consider to be a TRUE boolean value. * + * * + * INPUT: section -- The section to search under. * + * * + * entry -- The entry to search for. * + * * + * defvalue -- The default value to use if no matching entry could be located. * + * * + * OUTPUT: Returns with the boolean value of the specified section and entry. If no match * + * then the default boolean value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Get_Bool(char const * section, char const * entry, bool defvalue) const +{ + /* + ** Verify that the parameters are nominally correct. + */ + if (section == NULL || entry == NULL) return(defvalue); + + INIEntry * entryptr = Find_Entry(section, entry); + if (entryptr && entryptr->Value != NULL) { + switch (toupper(*entryptr->Value)) { + case 'Y': + case 'T': + case '1': + return(true); + + case 'N': + case 'F': + case '0': + return(false); + } + } + return(defvalue); +} + + +/*********************************************************************************************** + * INIClass::INISection::Find_Entry -- Finds a specified entry and returns pointer to it. * + * * + * This routine scans the supplied entry for the section specified. This is used for * + * internal database maintenance. * + * * + * INPUT: entry -- The entry to scan for. * + * * + * OUTPUT: Returns with a pointer to the entry control structure if the entry was found. * + * Otherwise it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + * 11/02/1996 JLB : Uses index handler. * + *=============================================================================================*/ +INIClass::INIEntry * INIClass::INISection::Find_Entry(char const * entry) const +{ + if (entry != NULL) { + int crc = CRCEngine()(entry, strlen(entry)); + if (EntryIndex.Is_Present(crc)) { + return(EntryIndex.Fetch_Index(crc)); + } + } + return(NULL); +} + + +/*********************************************************************************************** + * INIClass::Put_PKey -- Stores the key to the INI database. * + * * + * The key stored to the database will have both the exponent and modulus portions saved. * + * Since the fast key only requires the modulus, it is only necessary to save the slow * + * key to the database. However, storing the slow key stores the information necessary to * + * generate the fast and slow keys. Because public key encryption requires one key to be * + * completely secure, only store the fast key in situations where the INI database will * + * be made public. * + * * + * INPUT: key -- The key to store the INI database. * + * * + * OUTPUT: bool; Was the key stored to the database? * + * * + * WARNINGS: Store the fast key for public INI database availability. Store the slow key if * + * the INI database is secure. * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_PKey(PKey const & key) +{ + char buffer[512]; + + int len = key.Encode_Modulus(buffer); + Put_UUBlock("PublicKey", buffer, len); + + len = key.Encode_Exponent(buffer); + Put_UUBlock("PrivateKey", buffer, len); + return(true); +} + + +/*********************************************************************************************** + * INIClass::Get_PKey -- Fetch a key from the ini database. * + * * + * This routine will fetch the key from the INI database. The key fetched is controlled by * + * the parameter. There are two choices of key -- the fast or slow key. * + * * + * INPUT: fast -- Should the fast key be retrieved? The fast key has the advantage of * + * requiring only the modulus value. * + * * + * OUTPUT: Returns with the key retrieved. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey INIClass::Get_PKey(bool fast) const +{ + PKey key; + char buffer[512]; + + /* + ** When retrieving the fast key, the exponent is a known constant. Don't parse the + ** exponent from the database. + */ + if (fast) { + BigInt exp = PKey::Fast_Exponent(); + exp.DEREncode((unsigned char *)buffer); + key.Decode_Exponent(buffer); + } else { + Get_UUBlock("PrivateKey", buffer, sizeof(buffer)); + key.Decode_Exponent(buffer); + } + + Get_UUBlock("PublicKey", buffer, sizeof(buffer)); + key.Decode_Modulus(buffer); + + return(key); +} + + +/*********************************************************************************************** + * INIClass::Get_Fixed -- Fetch a fixed point number from the section & entry. * + * * + * This routine will examine the section and entry specified and interpret the value * + * as if it were a fixed point number. The format of the fixed point number can be * + * percentage (e.g. 100%) or a floating point number (e.g., 1.7). * + * * + * INPUT: section -- Pointer to the section identifier to look under. * + * * + * entry -- Pointer to the entry identifier to examine. * + * * + * defvalue -- If the section and entry could not be found, then this value will * + * be returned. * + * * + * OUTPUT: Returns with the fixed point number that occurs at the section and entry * + * specified. If it could not be found, then the default value is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +fixed INIClass::Get_Fixed(char const * section, char const * entry, fixed defvalue) const +{ + char buffer[128]; + fixed retval = defvalue; + + if (Get_String(section, entry, "", buffer, sizeof(buffer))) { + retval = fixed(buffer); + } + return(retval); +} + + +/*********************************************************************************************** + * INIClass::Put_Fixed -- Store a fixed point number to the INI database. * + * * + * Use this routine to output a fixed point number to the database. The entry will be * + * placed in the section and entry specified. If there was any existing entry, it will * + * be replaced. * + * * + * INPUT: section -- Pointer to the section identifier. * + * * + * entry -- Pointer to the entry identifier to use for this value. * + * * + * value -- The value to store in the database. * + * * + * OUTPUT: bool; Was the data stored? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool INIClass::Put_Fixed(char const * section, char const * entry, fixed value) +{ + return(Put_String(section, entry, value.As_ASCII())); +} + + +/*********************************************************************************************** + * INIClass::Strip_Comments -- Strips comments of the specified text line. * + * * + * This routine will scan the string (text line) supplied and if any comment portions are * + * found, they will be trimmed off. Leading and trailing blanks are also removed. * + * * + * INPUT: buffer -- Pointer to the null terminate string to be processed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void INIClass::Strip_Comments(char * buffer) +{ + if (buffer != NULL) { + char * comment = strchr(buffer, ';'); + if (comment) { + *comment = '\0'; + strtrim(buffer); + } + } +} diff --git a/code/redalert/ini.h b/code/redalert/ini.h new file mode 100644 index 0000000..50e04c8 --- /dev/null +++ b/code/redalert/ini.h @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INI.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INI.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/15/96 * + * * + * Last Update : May 15, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INI_H +#define INI_H + +#include +#include "listnode.h" +#include "pipe.h" +#include "wwfile.h" +#include "pk.h" +#include "fixed.h" +#include "crc.h" +#include "search.h" + +/* +** This is an INI database handler class. It handles a database with a disk format identical +** to the INI files commonly used by Windows. +*/ +class INIClass { + public: + INIClass(void) {} + ~INIClass(void); + + /* + ** Fetch and store INI data. + */ + bool Load(FileClass & file); + bool Load(Straw & file); + int Save(FileClass & file) const; + int Save(Pipe & file) const; + + /* + ** Erase all data within this INI file manager. + */ + bool Clear(char const * section = 0, char const * entry = 0); + + int Line_Count(char const * section) const; + bool Is_Loaded(void) const {return(!SectionList.Is_Empty());} + int Size(void) const; + bool Is_Present(char const * section, char const * entry = 0) const {if (entry == 0) return(Find_Section(section) != 0);return(Find_Entry(section, entry) != 0);} + + /* + ** Fetch the number of sections in the INI file or verify if a specific + ** section is present. + */ + int Section_Count(void) const; + bool Section_Present(char const * section) const {return(Find_Section(section) != NULL);} + + /* + ** Fetch the number of entries in a section or get a particular entry in a section. + */ + int Entry_Count(char const * section) const; + char const * Get_Entry(char const * section, int index) const; + + /* + ** Get the various data types from the section and entry specified. + */ + int Get_String(char const * section, char const * entry, char const * defvalue, char * buffer, int size) const; + int Get_Int(char const * section, char const * entry, int defvalue=0) const; + int Get_Hex(char const * section, char const * entry, int defvalue=0) const; + bool Get_Bool(char const * section, char const * entry, bool defvalue=false) const; + int Get_TextBlock(char const * section, char * buffer, int len) const; + int Get_UUBlock(char const * section, void * buffer, int len) const; + PKey Get_PKey(bool fast) const; + fixed Get_Fixed(char const * section, char const * entry, fixed defvalue) const; + + /* + ** Put a data type to the section and entry specified. + */ + bool Put_Fixed(char const * section, char const * entry, fixed value); + bool Put_String(char const * section, char const * entry, char const * string); + bool Put_Hex(char const * section, char const * entry, int number); + bool Put_Int(char const * section, char const * entry, int number, int format=0); + bool Put_Bool(char const * section, char const * entry, bool value); + bool Put_TextBlock(char const * section, char const * text); + bool Put_UUBlock(char const * section, void const * block, int len); + bool Put_PKey(PKey const & key); + + protected: + enum {MAX_LINE_LENGTH=128}; + + /* + ** The value entries for the INI file are stored as objects of this type. + ** The entry identifier and value string are combined into this object. + */ + struct INIEntry : Node { + INIEntry(char * entry = 0, char * value = 0) : Entry(entry), Value(value) {} + ~INIEntry(void) {free(Entry);Entry = 0;free(Value);Value = 0;} + int Index_ID(void) const {return(CRCEngine()(Entry, strlen(Entry)));}; + + char * Entry; + char * Value; + }; + + /* + ** Each section (bracketed) is represented by an object of this type. All entries + ** subordinate to this section are attached. + */ + struct INISection : Node { + INISection(char * section) : Section(section) {} + ~INISection(void) {free(Section);Section = 0;EntryList.Delete();} + INIEntry * Find_Entry(char const * entry) const; + int Index_ID(void) const {return(CRCEngine()(Section, strlen(Section)));}; + + char * Section; + List EntryList; + IndexClassEntryIndex; + }; + + /* + ** Utility routines to help find the appropriate section and entry objects. + */ + INISection * Find_Section(char const * section) const; + INIEntry * Find_Entry(char const * section, char const * entry) const; + static void Strip_Comments(char * buffer); + + /* + ** This is the list of all sections within this INI file. + */ + List SectionList; + + IndexClass SectionIndex; +}; + + +#endif diff --git a/code/redalert/inibin.cpp b/code/redalert/inibin.cpp new file mode 100644 index 0000000..793ce6d --- /dev/null +++ b/code/redalert/inibin.cpp @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\inibin.cpv 4.40 04 Jul 1996 16:14:52 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIBIN.CPP * + * * + * Programmer : David R. Dettmer * + * * + * Start Date : October 20, 1995 * + * * + * Last Update : November 7, 1995 [DRD] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + diff --git a/code/redalert/inicode.cpp b/code/redalert/inicode.cpp new file mode 100644 index 0000000..705ab26 --- /dev/null +++ b/code/redalert/inicode.cpp @@ -0,0 +1,363 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\inicode.cpv 4.38 03 Jul 1996 05:14:04 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INICODE.CPP * + * * + * Programmer : David R Dettmer * + * * + * Start Date : November 7, 1995 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +#ifdef TOFIX + +void Get_Scenario_Digest(char * digest, char * buffer) +{ + char buf[128]; // Working string staging buffer. + char stage[sizeof(BigInt)*2]; + + char * stage_ptr = &stage[0]; + int len = strlen(buffer) + 2; + char * tbuffer = buffer + len; + + WWGetPrivateProfileString("DIGEST", NULL, NULL, tbuffer, sizeof(_staging_buffer)-len, buffer); + stage[0] = '\0'; + while (*tbuffer != '\0') { + + WWGetPrivateProfileString("DIGEST", tbuffer, NULL, buf, sizeof(buf)-1, buffer); + strcat(stage, buf); + tbuffer += strlen(tbuffer)+1; + } + + len = strlen(stage); + char * dbuffer = &stage[0]; + tbuffer = &stage[0]; + for (int index = 0; index < len/2; index++) { + int c; + if (isdigit(*tbuffer)) { + c = (*tbuffer) - '0'; + } else { + c = 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + c <<= 4; + if (isdigit(*tbuffer)) { + c |= (*tbuffer) - '0'; + } else { + c |= 10 + (toupper(*tbuffer) - 'A'); + } + tbuffer++; + *dbuffer++ = c; + } + + /* + ** Decode and decrypt the number. + */ + BigInt hash = 0; + hash.DERDecode((unsigned char*)stage); + + BigInt d; + d = d.Decode_ASCII(KEY_D); + BigInt n; + n = n.Decode_ASCII(KEY_N); + + hash = hash.exp_b_mod_c(d, n); + + memcpy(digest, &hash, 20); + + buffer = strstr(buffer, "[DIGEST]"); + if (buffer) { + *buffer = '\0'; + } +} + + +bool Read_Scenario_INI_Write_INB( char *root, bool fresh) +{ + char *buffer; // Scenario.ini staging buffer pointer. + char *binbuf; // Scenario.inb staging buffer pointer. + char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + char buf[256]; // Working string staging buffer. + char scenarioname[40]; + int len; + unsigned char val; + + /* + ** Fetch working pointer to the INI staging buffer. Make sure that the buffer + ** is cleared out before proceeding. (Don't use the HidPage for this, since + ** the HidPage may be needed for various uncompressions during the INI + ** parsing.) + */ + buffer = (char *)_staging_buffer; + memset(buffer, '\0', sizeof(_staging_buffer)); + + /* + ** Create scenario filename and read the file. + ** The previous routine verifies that the file is available. + */ + + sprintf(fname,"%s.INI",root); + CCFileClass file(fname); + + file.Read(buffer, sizeof(_staging_buffer)-1); + + /* + ** Fetch and slice off any message digest attached. + */ + char digest[20]; + Get_Scenario_Digest(digest, buffer); + + char real_digest[20]; + SHAEngine digest_engine; + digest_engine.Hash(buffer, strlen(buffer)); + digest_engine.Result(real_digest); + + if (memcmp(digest, real_digest, sizeof(real_digest)) != 0) { + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } + + sprintf(fname,"%s.INB",root); + file.Set_Name(fname); + file.Cache(16384); + file.Open(WRITE); + + unsigned long crc = Ini_Binary_Version(); + + file.Write( (char *)&crc, sizeof(crc) ); + + binbuf = (char *)Alloc( sizeof(_staging_buffer), MEM_NORMAL ); + + if (binbuf) { + Write_Bin_Init( binbuf, sizeof(_staging_buffer) ); + } else { + Print_Error_End_Exit( "Unable to alloc space for writing INB" ); + } + + /* + ** Fetch the appropriate movie names from the INI file. + */ + WWGetPrivateProfileString("Basic", "Name", "", scenarioname, sizeof(scenarioname), buffer); + WWGetPrivateProfileString("Basic", "Intro", "x", Scen.IntroMovie, sizeof(Scen.IntroMovie), buffer); + WWGetPrivateProfileString("Basic", "Brief", "x", Scen.BriefMovie, sizeof(Scen.BriefMovie), buffer); + WWGetPrivateProfileString("Basic", "Win", "x", Scen.WinMovie, sizeof(Scen.WinMovie), buffer); + WWGetPrivateProfileString("Basic", "Lose", "x", Scen.LoseMovie, sizeof(Scen.LoseMovie), buffer); + WWGetPrivateProfileString("Basic", "Action", "x", Scen.ActionMovie, sizeof(Scen.ActionMovie), buffer); + Scen.IsToCarryOver = WWGetPrivateProfileInt("Basic", "ToCarryOver", 0, buffer); + Scen.IsToInherit = WWGetPrivateProfileInt("Basic", "ToInherit", 0, buffer); + + Write_Bin_String( scenarioname, strlen(scenarioname), binbuf ); + Write_Bin_String( Scen.IntroMovie, strlen(Scen.IntroMovie), binbuf ); + Write_Bin_String( Scen.BriefMovie, strlen(Scen.BriefMovie), binbuf ); + Write_Bin_String( Scen.WinMovie, strlen(Scen.WinMovie), binbuf ); + Write_Bin_String( Scen.LoseMovie, strlen(Scen.LoseMovie), binbuf ); + Write_Bin_String( Scen.ActionMovie, strlen(Scen.ActionMovie), binbuf ); + + /* + ** Fetch the transition theme for this scenario. + */ + Scen.TransitTheme = THEME_NONE; + WWGetPrivateProfileString("Basic", "Theme", "No Theme", buf, sizeof(buf), buffer); + Scen.TransitTheme = Theme.From_Name(buf); + + WWGetPrivateProfileString( "Basic", "Player", "Greece", buf, 127, buffer); + Scen.PlayerHouse = HouseTypeClass::From_Name(buf); + if (Scen.PlayerHouse >= HOUSE_MULTI1) { + Scen.PlayerHouse = HOUSE_GREECE; + } +// TCTC To Fix? +// Scen.CarryOverPercent = WWGetPrivateProfileInt( "Basic", "CarryOverMoney", 100, buffer); + Scen.CarryOverCap = WWGetPrivateProfileInt( "Basic", "CarryOverCap", -1, buffer); + Scen.Percent = WWGetPrivateProfileInt( "Basic", "Percent", 0, buffer); + + Write_Bin_Num( &Scen.TransitTheme, sizeof(Scen.TransitTheme), binbuf ); + Write_Bin_Num( &Scen.PlayerHouse, sizeof(Scen.PlayerHouse), binbuf ); + Write_Bin_Num( &Scen.CarryOverPercent, 1, binbuf ); + Write_Bin_Num( &Scen.CarryOverCap, 2, binbuf ); + Write_Bin_Num( &Scen.Percent, 1, binbuf ); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + Scen.CarryOverPercent = Cardinal_To_Fixed(100, Scen.CarryOverPercent); + + PlayerPtr = HouseClass::As_Pointer( Scen.PlayerHouse ); + assert(PlayerPtr != NULL); + PlayerPtr->IsHuman = true; + + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = MIN(Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent), (Scen.CarryOverCap * 100) ); + } else { + carryover = Fixed_To_Cardinal(Scen.CarryOverMoney, Scen.CarryOverPercent); + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { + + Assign_Houses(); + } + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(buffer); + Call_Back(); + + /* + ** Attempt to read the map's binary image file; if fails, read the + ** template data from the INI, for backward compatibility + */ + if (fresh) { + if (!Map.Read_Binary(root, &ScenarioCRC)) { + return( false ); + } + } + Call_Back(); + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(buffer); + Call_Back(); + + VesselClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(buffer); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(buffer); + Call_Back(); + + /* + ** Read in any briefing text. + */ + char * stage = &Scen.BriefingText[0]; + *stage = '\0'; + int index = 1; + + /* + ** Build the full text of the mission objective. + */ + for (;;) { + char buff[16]; + + sprintf(buff, "%d", index++); + *stage = '\0'; + WWGetPrivateProfileString("Briefing", buff, "", stage, 255, buffer); + if (strlen(stage) == 0) break; + strcat(stage, " "); + stage += strlen(stage); + } + + len = Write_Bin_Length( binbuf ); + if (len != -1) { + file.Write( binbuf, len ); + } + + Free( binbuf ); + file.Close(); + + return(true); +} + +#endif + + + + + + + + + diff --git a/code/redalert/init.cpp b/code/redalert/init.cpp new file mode 100644 index 0000000..9ec0c7f --- /dev/null +++ b/code/redalert/init.cpp @@ -0,0 +1,3778 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INIT.CPP 8 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : January 20, 1992 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Anim_Init -- Initialize the VQ animation control structure. * + * Bootstrap -- Perform the initial bootstrap procedure. * + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * Init_Color_Remaps -- Initialize the text remap tables. * + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * Init_Fonts -- Initialize all the game font pointers. * + * Init_Game -- Main game initialization routine. * + * Init_Heaps -- Initialize the game heaps and buffers. * + * Init_Keys -- Initialize the cryptographic keys. * + * Init_Mouse -- Initialize the mouse system. * + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * Init_Random -- Initializes the random-number generator * + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * Load_Recording_Values -- Loads recording values from recording file * + * Load_Title_Page -- Load the background art for the title page. * + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * Parse_Command_Line -- Parses the command line parameters. * + * Parse_INI_File -- Parses CONQUER.INI for special options * + * Play_Intro -- plays the introduction & logo movies * + * Save_Recording_Values -- Saves recording values to a recording file * + * Select_Game -- The game's main menu * + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "loaddlg.h" +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#include "WSPIPX.h" +#include "internet.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX + +#endif +#include +#include +#ifndef WIN32 +#include +#endif +#include "ccdde.h" +#include "AUDIOMIX.H" + +#include + +#ifdef DONGLE +#include "cbn_.h" +#endif + +#ifdef MPEGMOVIE // Denzil 6/25/98 +#include "mpgset.h" +#endif + +RemapControlType SidebarScheme; + +//#include "WolDebug.h" + +#ifdef CHEAT_KEYS +extern bool bNoMovies; +#endif + +/**************************************** +** Function prototypes for this module ** +*****************************************/ +static void Play_Intro(bool sequenced=false); +static void Init_Color_Remaps(void); +static void Init_Heaps(void); +static void Init_Expansion_Files(void); +static void Init_One_Time_Systems(void); +static void Init_Fonts(void); +static void Init_CDROM_Access(void); +static void Init_Bootstrap_Mixfiles(void); +static void Init_Secondary_Mixfiles(void); +static void Init_Mouse(void); +static void Bootstrap(void); +//static void Init_Authorization(void); +static void Init_Bulk_Data(void); +static void Init_Keys(void); + +extern int UnitBuildPenalty; + +extern "C" { +extern long RandNumb; +} +#ifndef WIN32 +static int UsePageFaultHandler = 1; // 1 = install PFH +#endif //WIN32 + +//extern int SimRandIndex; +void Init_Random(void); + +#define ATTRACT_MODE_TIMEOUT 3600 // timeout for attract mode + +bool Load_Recording_Values(CCFileClass & file); +bool Save_Recording_Values(CCFileClass & file); + +#ifdef WOLAPI_INTEGRATION +extern int WOL_Main(); +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +#ifdef FIXIT_VERSION_3 +bool Expansion_Dialog( bool bCounterstrike ); +#endif + +extern bool Is_Mission_Counterstrike (char *file_name); + +/*********************************************************************************************** + * Load_Prolog_Page -- Loads the special pre-prolog "please wait" page. * + * * + * This loads and displays the prolog page that is displayed before the prolog movie * + * is played. This page is necessary because there is much loading that occurs before * + * the prolog movie is played and looking at a picture is better than looking at a blank * + * screen. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Load_Prolog_Page(void) +{ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen("PROLOG.PCX", &HidPage, (unsigned char*)CCPalette.Get_Data()); + HidPage.Blit(SeenPage); +#else + Load_Picture("PROLOG.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + HidPage.Blit(SeenPage); +#endif + CCPalette.Set(); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Init_Game -- Main game initialization routine. * + * * + * Perform all one-time game initializations here. This includes all * + * allocations and table setups. The intro and other one-time startup * + * tasks are also performed here. * + * * + * INPUT: argc,argv -- Command line arguments. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this ONCE! * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +#include "sha.h" +//#include +bool Init_Game(int , char * []) +{ + /* + ** Allocate the benchmark tracking objects only if the machine and + ** compile flags indicate. + */ + #ifdef CHEAT_KEYS + //if (Processor() >= 2) { + // Benches = new Benchmark [BENCH_COUNT]; + //} + #endif + + /* + ** Initialize the encryption keys. + */ + Init_Keys(); + + /* + ** Bootstrap as much as possible before error-prone initializations are + ** performed. This bootstrap process will enable the error message + ** handler to function. + */ + Bootstrap(); + + //////////////////////////////////////// + // The editor needs to not start the mouse up. - 7/22/2019 JAS + if (!RunningFromEditor) + { + /* + ** Check for an initialize a working mouse pointer. Display error and bail if + ** no mouse driver is installed. + */ + Init_Mouse(); + } + /* + ** Initialize access to the CD-ROM and ensure that the CD is inserted. This can, and + ** most likely will, result in a visible prompt. + */ +#if (0) //PG + Init_CDROM_Access(); +#endif + if (Special.IsFromInstall) { + Load_Prolog_Page(); + } + + /* + ** Register and cache any secondary mixfiles. + */ + Init_Secondary_Mixfiles(); + + /* + ** This is a special hack to initialize the heaps that must be in place before the + ** rules file is processed. These heaps should properly be allocated as a consequence + ** of processing the rules.ini file, but that is a bit beyond the capabilities of + ** the rule parser routine (currently). + */ + HouseTypes.Set_Heap(HOUSE_COUNT); + BuildingTypes.Set_Heap(STRUCT_COUNT); + AircraftTypes.Set_Heap(AIRCRAFT_COUNT); + InfantryTypes.Set_Heap(INFANTRY_COUNT); + BulletTypes.Set_Heap(BULLET_COUNT); + AnimTypes.Set_Heap(ANIM_COUNT); + UnitTypes.Set_Heap(UNIT_COUNT); + VesselTypes.Set_Heap(VESSEL_COUNT); + TemplateTypes.Set_Heap(TEMPLATE_COUNT); + TerrainTypes.Set_Heap(TERRAIN_COUNT); + OverlayTypes.Set_Heap(OVERLAY_COUNT); + SmudgeTypes.Set_Heap(SMUDGE_COUNT); + + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + // Heap init moved here from globals.cpp. ST - 5/20/2019 + CCPtr::Set_Heap(&Aircraft); + CCPtr::Set_Heap(&Anims); + CCPtr::Set_Heap(&Buildings); + CCPtr::Set_Heap(&Bullets); + CCPtr::Set_Heap(&Factories); + CCPtr::Set_Heap(&Houses); + CCPtr::Set_Heap(&Infantry); + CCPtr::Set_Heap(&Overlays); + CCPtr::Set_Heap(&Smudges); + CCPtr::Set_Heap(&Teams); + CCPtr::Set_Heap(&TeamTypes); + CCPtr::Set_Heap(&Templates); + CCPtr::Set_Heap(&Terrains); + CCPtr::Set_Heap(&Triggers); + CCPtr::Set_Heap(&TriggerTypes); + + CCPtr::Set_Heap(&HouseTypes); + CCPtr::Set_Heap(&BuildingTypes); + CCPtr::Set_Heap(&AircraftTypes); + CCPtr::Set_Heap(&InfantryTypes); + CCPtr::Set_Heap(&BulletTypes); + CCPtr::Set_Heap(&AnimTypes); + CCPtr::Set_Heap(&UnitTypes); + CCPtr::Set_Heap(&VesselTypes); + CCPtr::Set_Heap(&TemplateTypes); + CCPtr::Set_Heap(&TerrainTypes); + CCPtr::Set_Heap(&OverlayTypes); + CCPtr::Set_Heap(&SmudgeTypes); + + /* + ** Find and process any rules for this game. + */ + if (RuleINI.Load(CCFileClass("RULES.INI"), false)) { + Rule.Process(RuleINI); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + // Aftermath runtime change 9/29/98 + // This is safe to do, as only rules for aftermath units are included in this ini. + if (Is_Aftermath_Installed() == true) { + if (AftermathINI.Load(CCFileClass("AFTRMATH.INI"), false)) { + Rule.Process(AftermathINI); + } + } +#endif + + Session.MaxPlayers = Rule.MaxPlayers; + + /* + ** Initialize the game object heaps as well as other rules-dependant buffer allocations. + */ + Init_Heaps(); + + /* + ** Initialize the animation system. + */ + Anim_Init(); + +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 + if (SpawnedFromWChat){ + Special.IsFromWChat = true; + } +#endif +#endif + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if( Using_DVD() ) + { + #ifdef MCIMPEG + MciMovie = new MCIMovie(MainWindow); + #endif + MpgSettings = new MPGSettings(NULL); //RawFileClass(CONFIG_FILE_NAME)); + } + #endif + + // Init the new audio system. +// jmarshall + AudMix_Init(); +// jmarshall end + + /* + ** Play the startup animation. + */ + if (!Special.IsFromInstall && !Special.IsFromWChat) { + VisiblePage.Clear(); +// Mono_Printf("Playing Intro\n"); + Play_Intro(); + memset(CurrentPalette, 0x01, 768); + WhitePalette.Set(); + } else { + memset(CurrentPalette, 0x01, 768); + } + + /* + ** Initialize the text remap tables. + */ + Init_Color_Remaps(); + + /* + ** Get authorization to access the game. + */ +// Init_Authorization(); +// Show_Mouse(); + + /* + ** Set the logic page to the seenpage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** If not automatically launching into the intro, then display the title + ** page while the bulk data is cached. + */ + if (!Special.IsFromInstall) { + Load_Title_Page(true); + + Hide_Mouse(); + Fancy_Text_Print(TXT_STAND_BY, 160*RESFACTOR, 120*RESFACTOR, &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TPF_CENTER|TPF_TEXT|TPF_DROPSHADOW); + Show_Mouse(); + + CCPalette.Set(FADE_PALETTE_SLOW); + Call_Back(); + } + + /* + ** Initialize the bulk data. This takes the longest time and must be performed once + ** before the regular game starts. + */ + Init_Bulk_Data(); + + /* + ** Initialize the multiplayer score values + */ + Session.GamesPlayed = 0; + Session.NumScores = 0; + Session.CurGame = 0; + for (int i = 0; i < MAX_MULTI_NAMES; i++) { + Session.Score[i].Name[0] = '\0'; + Session.Score[i].Wins = 0; + for (int j = 0; j < MAX_MULTI_GAMES; j++) { + Session.Score[i].Kills[j] = -1; // -1 = this player didn't play this round + } + } + + /* + ** Copy the title screen's palette into the GamePalette & OriginalPalette, + ** because the options Load routine uses these palettes to set the brightness, etc. + */ + GamePalette = CCPalette; +// InGamePalette = CCPalette; + OriginalPalette = CCPalette; + + /* + ** Read game options, so the GameSpeed is initialized when multiplayer + ** dialogs are invoked. (GameSpeed must be synchronized between systems.) + */ + Options.Load_Settings(); + + /* + ** Initialise the color lookup tables for the chronal vortex + */ + ChronalVortex.Stop(); + ChronalVortex.Setup_Remap_Tables(Scen.Theater); + + return(true); +} + +#ifdef WINSOCK_IPX // Steve Tall missed this one - ajw +extern bool Get_Broadcast_Addresses (void); +#endif + + +jmp_buf select_game_jump; + +/*********************************************************************************************** + * Select_Game -- The game's main menu * + * * + * INPUT: * + * fade if true, will fade the palette in gradually * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=============================================================================================*/ +bool Select_Game(bool fade) +{ + // Enums in Select_Game() must match order of buttons in Main_Menu(). +#ifdef FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO_CS, // Expansion scenario to play. + SEL_NEW_SCENARIO_AM, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#else // FIXIT_VERSION_3 + enum { + SEL_TIMEOUT = -1, // main menu timeout--go into attract mode + SEL_NEW_SCENARIO, // Expansion scenario to play. + SEL_START_NEW_GAME, // start a new game +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + SEL_INTERNET, +#endif //WIN32 +//#if defined(MPEGMOVIE) // Denzil 6/25/98 +// SEL_MOVIESETTINGS, +//#endif + SEL_LOAD_MISSION, // load a saved game + SEL_MULTIPLAYER_GAME, // play modem/null-modem/network game + SEL_INTRO, // couch-potato mode + SEL_EXIT, // exit to DOS + SEL_FAME, // view the hall o' fame + SEL_NONE, // placeholder default value + }; +#endif // FIXIT_VERSION_3 + + bool gameloaded=false; // Has the game been loaded from the menu? + int selection; // the default selection + bool process = true; // false = break out of while loop + bool display = true; + +#ifdef DONGLE + /* These where added by ColinM for the dongle checking */ + short iRet = 0; + unsigned short iPortNr = 1; /* automatic port scan enabled */ + unsigned char cSCodeSER[] = "\x41\x42"; + unsigned long ulIdRet = 0; + unsigned char cBoxName[]= "\x00\x00"; +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int cdcheck = 0; + bool cs = Is_Counterstrike_Installed(); +#endif + + setjmp(select_game_jump); + + if (Scen.warpIntoMap) { + Scen.warpIntoMap = false; + return true; + } + +// #ifndef DVD // Denzil - We want the menu screen ajw No we don't +// if (Special.IsFromInstall) { +// display = false; +// } +// #endif + + Show_Mouse(); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; // Assume new units disabled, unless specifically .INI enabled or multiplayer negotiations enable it. +#endif + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** Enable the DDE Server so we can get internet start game packets from WChat + */ + DDEServer.Enable(); +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + /* + ** [Re]set any globals that need it, in preparation for a new scenario + */ + GameActive = true; + DoList.Init(); + #ifdef MIRROR_QUEUE + MirrorList.Init(); + #endif + OutList.Init(); + Frame = 0; + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + PlayerWins = false; + PlayerLoses = false; + Session.ObiWan = false; + Debug_Unshroud = false; + Map.Set_Cursor_Shape(0); + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; + Session.DesiredFrameRate = 30; +#if(TIMING_FIX) + NewMaxAheadFrame1 = 0; + NewMaxAheadFrame2 = 0; +#endif + +/* ColinM added to check for dongle */ +#ifdef DONGLE + iRet = CbN_BoxReady( iPortNr , cBoxName); + if (cBoxName[0] != 0xc5 && cBoxName[0] != 0xc9) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } + + iRet = CbN_ReadSER( iPortNr, cSCodeSER, &ulIdRet); + if (ulIdRet != 0xa0095) + { + WWMessageBox().Process("Please ensure dongle is attached. Run the dongle batch file too.", TXT_OK); + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** Init multiplayer game scores. Let Wins accumulate; just init the current + ** Kills for this game. Kills of -1 means this player didn't play this round. + */ + for (int i = 0 ; i < MAX_MULTI_GAMES; i++) { + Session.Score[i].Kills[Session.CurGame] = -1; + } + + /* + ** Set default mouse shape + */ + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + + /* + ** If the last game we played was a multiplayer game, jump right to that + ** menu by pre-setting 'selection'. + */ + if (Session.Type == GAME_NORMAL) { + selection = SEL_NONE; + } else { + selection = SEL_MULTIPLAYER_GAME; + } + + /* + ** Main menu processing; only do this if we're not in editor mode. + */ + if (!Debug_Map) { + + /* + ** Menu selection processing loop + */ + Theme.Queue_Song(THEME_CRUS); + + /* + ** If we're playing back a recording, load all pertinent values & skip + ** the menu loop. Hide the now-useless mouse pointer. + */ + if (Session.Play && Session.RecordFile.Is_Available()) { + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else + Session.Play = false; + } + +#ifndef FIXIT_VERSION_3 +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat + */ + if (SpawnedFromWChat) { + Special.IsFromInstall = false; //Dont play intro if we were spawned from wchat + selection = SEL_INTERNET; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + display = false; + Set_Logic_Page(SeenBuff); + } +#endif //WIN32 +#endif + + while (process) { + /* + ** Redraw the title page if needed + */ + if (display) { + Hide_Mouse(); + + /* + ** Display the title page; fade it in if this is the first time + ** through the loop, and the 'fade' flag is true + */ + Load_Title_Page(); + GamePalette = CCPalette; + + HidPage.Blit(SeenPage); +// if (fade) { +// WhitePalette.Set(); +// CCPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// fade = false; +// } else { + CCPalette.Set(); +// } + + Set_Logic_Page(SeenBuff); + // display = false; + Show_Mouse(); + } + else { + if (RunningAsDLL) { //PG + return true;; + } + } + + /* + ** Display menu and fetch selection from player. + */ + if (Special.IsFromInstall) selection = SEL_START_NEW_GAME; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle case where we were spawned from Wchat and our start game + ** packet has already arrived + */ + if (Special.IsFromWChat && DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + selection = SEL_MULTIPLAYER_GAME; + Theme.Queue_Song(THEME_QUIET); + Session.Type = GAME_INTERNET; + } else { + /* + ** We werent spawned but we could still receive a DDE packet from wchat + */ + if (DDEServer.Get_MPlayer_Game_Info()) { + Check_From_WChat(NULL); + /* + ** Make sure top and bottom of screen are clear in 640x480 mode + */ + if (ScreenHeight == 480) { + VisiblePage.Fill_Rect (0, 0, 639, 40, 0); + VisiblePage.Fill_Rect (0, 440, 639, 479, 0); + } + } + } +#endif //WIN32 +#endif + +#ifdef WOLAPI_INTEGRATION + if( pWolapi ) + selection = SEL_MULTIPLAYER_GAME; // We are returning from a game. +#endif + + if (selection == SEL_NONE) { +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + g_inMainMenu = true; + selection = Main_Menu(ATTRACT_MODE_TIMEOUT); + } + Call_Back(); + + switch (selection) { + + /* + ** Pick an expansion scenario. + */ +#ifdef FIXIT_VERSION_3 + case SEL_NEW_SCENARIO_CS: + case SEL_NEW_SCENARIO_AM: +#else // FIXIT_VERSION_3 + case SEL_NEW_SCENARIO: +#endif // FIXIT_VERSION_3 + Scen.CarryOverMoney = 0; + IsTanyaDead = false; + SaveTanya = false; + +#ifdef FIXIT_VERSION_3 + if( selection == SEL_NEW_SCENARIO_CS ) + { + if(!Force_CD_Available(2)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( true )){ + selection = SEL_NONE; + break; + } + } + else + { + if(!Force_CD_Available(3)) { + selection = SEL_NONE; + break; + } + if(!Expansion_Dialog( false )){ + selection = SEL_NONE; + break; + } + } +#else // FIXIT_VERSION_3 + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (cs) { + cdcheck = 2; + } + if (Is_Aftermath_Installed()) { + if (!cdcheck) { + cdcheck = 3; + } else { + cdcheck = 4; // special case: means check for 3 or 4 + } + } + if(!Force_CD_Available(cdcheck)) { + return(false); + } + #else + if(!Force_CD_Available(2)) { + return(false); + } + #endif + if(!Expansion_Dialog()){ + selection = SEL_NONE; + break; + } +#endif // FIXIT_VERSION_3 + +#ifdef FIXIT_DIFFICULTY + #ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch (Fetch_Difficulty(cdcheck >= 3)) { + #else + switch (Fetch_Difficulty()) { + #endif + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } +#endif + Theme.Fade_Out(); + Theme.Queue_Song(THEME_FIRST); + Session.Type = GAME_NORMAL; + process = false; + break; + + /* + ** SEL_START_NEW_GAME: Play the game + */ + case SEL_START_NEW_GAME: + if (Special.IsFromInstall) { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } else { + switch (Fetch_Difficulty()) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } + + Scen.CarryOverMoney = 0; + BuildLevel = 10; + IsTanyaDead = false; + SaveTanya = false; + Whom = HOUSE_GOOD; + + if (!Special.IsFromInstall) { + if (AntsEnabled) { + Scen.Set_Scenario_Name("SCA01EA.INI"); + } else { + switch (WWMessageBox().Process(TXT_CHOOSE, TXT_ALLIES, TXT_CANCEL, TXT_SOVIET)) { + case 2: + Scen.Set_Scenario_Name("SCU01EA.INI"); + break; + default: + selection = SEL_NONE; + continue; + case 0: + Scen.Set_Scenario_Name("SCG01EA.INI"); + break; + + } + } + + Theme.Fade_Out(); + Load_Title_Page(); + } else { + Theme.Fade_Out(); + + Choose_Side(); + Hide_Mouse(); + + if (CurrentCD == 0) { + Scen.Set_Scenario_Name("SCG01EA.INI"); + } else { + Scen.Set_Scenario_Name("SCU01EA.INI"); + } + } + + Session.Type = GAME_NORMAL; + process = false; + break; + + /* + ** Load a saved game. + */ + case SEL_LOAD_MISSION: + if (LoadOptionsClass(LoadOptionsClass::LOAD).Process()) { + Theme.Queue_Song(THEME_FIRST); + process = false; + gameloaded = true; + } else { + display = true; + selection = SEL_NONE; + } + break; + + /* + ** SEL_MULTIPLAYER_GAME: set 'Session.Type' to NULL-modem, modem, or + ** network play. + */ + case SEL_MULTIPLAYER_GAME: + switch (Session.Type) { + + /* + ** If 'Session.Type' isn't already set up for a multiplayer game, + ** we must prompt the user for which type of multiplayer game + ** they want. + */ + case GAME_NORMAL: + Session.Type = Select_MPlayer_Game(); + + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + break; + + case GAME_SKIRMISH: +#if (0)//PG + if ( !Com_Scenario_Dialog(true) ) { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + else + { + // Ever hits? Session.Type set to GAME_SKIRMISH without user selecting in Select_MPlayer_Game()? +#ifdef FIXIT_VERSION_3 + // If mission is Counterstrike, CS CD will be required. But aftermath units require AM CD. + bAftermathMultiplayer = Is_Aftermath_Installed() && !Is_Mission_Counterstrike( Scen.ScenarioName ); + // ajw I'll bet this was needed before also... + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); +#endif + } +#endif//PG + break; + + case GAME_NULL_MODEM: + case GAME_MODEM: +#if (0) + if ( Session.Type != GAME_SKIRMISH && NullModem.Num_Connections() ) { + NullModem.Init_Send_Queue(); + + if ((Session.Type == GAME_NULL_MODEM && + Session.ModemType == MODEM_NULL_HOST) || + (Session.Type == GAME_MODEM && + Session.ModemType == MODEM_DIALER) ) { + + if ( !Com_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } else { + if ( !Com_Show_Scenario_Dialog() ) { + Session.Type = Select_Serial_Dialog(); + if (Session.Type == GAME_NORMAL) { // user hit Cancel + display = true; + selection = SEL_NONE; + } + } + } + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } +#endif + break; + +#ifndef WOLAPI_INTEGRATION +#if defined(WIN32) && !defined(INTERNET_OFF) // Denzil 5/1/98 - Internet play + /* + ** Handle being spawned from WChat. Internet play based on IPX code. + */ + case GAME_INTERNET: // ajw No longer hit. + if (Special.IsFromWChat) { + /* + ** Give myself focus. + */ + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, ShowCommand ); + +#ifdef WINSOCK_IPX + + if (PacketTransport ) delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert ( PacketTransport != NULL); + + if (PacketTransport->Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + } else { + delete PacketTransport; + PacketTransport = NULL; + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + +#else //WINSOCK_IPX + + WWDebugString ("RA95 - About to initialise Winsock.\n"); + if (Winsock.Init()) { + WWDebugString ("RA95 - About to read multiplayer settings.\n"); + Session.Read_MultiPlayer_Settings (); + Server = PlanetWestwoodIsHost; + + WWDebugString ("RA95 - About to set addresses.\n"); + Winsock.Set_Host_Address(PlanetWestwoodIPAddress); + + WWDebugString ("RA95 - About to call Start_Server or Start_Client.\n"); + if (Server) { + Winsock.Start_Server(); + } else { + Winsock.Start_Client(); + } + + + /* + ** Flush out any pending packets from a previous game. + */ + WWDebugString ("RA95 - About to flush packet queue.\n"); + WWDebugString ("RA95 - Allocating scrap memory.\n"); + char *temp_buffer = new char[1024]; + + WWDebugString ("RA95 - Creating timer class instance.\n"); + CountDownTimerClass ptimer; + + WWDebugString ("RA95 - Entering read loop.\n"); + while (Winsock.Read(temp_buffer, 1024)) { + + WWDebugString ("RA95 - Discarding a packet.\n"); + ptimer.Set (30, true); + while (ptimer.Time()) {}; + WWDebugString ("RA95 - Ready to check for more packets.\n"); + + } + WWDebugString ("RA95 - About to delete scrap memory.\n"); + delete temp_buffer; + + + + } else { + WWDebugString ("RA95 - Winsock failed to initialise.\n"); + Session.Type = GAME_NORMAL; + selection = SEL_EXIT; + Special.IsFromWChat = false; + break; + } +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + if (DDEServer.Get_MPlayer_Game_Info()) { + WWDebugString ("RA95 - About to call Read_Game_Options.\n"); + Read_Game_Options( NULL ); + } else { + Read_Game_Options( "C&CSPAWN.INI" ); + } +#ifdef WINSOCK_IPX + WWDebugString ("RA95 - About to set addresses.\n"); + PacketTransport->Set_Broadcast_Address (PlanetWestwoodIPAddress); +#endif //WINSOCK_IPX + if (PlanetWestwoodIsHost) { + + WWDebugString ("RA95 - About to call Server_Remote_Connect.\n"); + if (Server_Remote_Connect()) { + WWDebugString ("RA95 - Server_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } else { + WWDebugString ("RA95 - About to call Client_Remote_Connect.\n"); + if (Client_Remote_Connect()) { + WWDebugString ("RA95 - Client_Remote_Connect returned success.\n"); + break; + } else { + /* + ** We failed to connect to the other player + */ +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#else //WINSOCK_IPX + Winsock.Close(); +#endif //WINSOCK_IPX + Session.Type = GAME_NORMAL; + selection = SEL_NONE; + DDEServer.Delete_MPlayer_Game_Info(); // Make sure we dont go round in an infinite loop + break; + } + } + + } else { + Session.Type = Select_MPlayer_Game(); + if (Session.Type == GAME_NORMAL) { // 'Cancel' + display = true; + selection = SEL_NONE; + } + } + break; + +#endif //WIN32 +#endif // !WOLAPI_INTEGRATION + + } + + switch (Session.Type) { + /* + ** Modem, Null-Modem or internet + */ + case GAME_MODEM: + case GAME_NULL_MODEM: +#ifndef WOLAPI_INTEGRATION + case GAME_INTERNET: +#endif + case GAME_SKIRMISH: + Theme.Fade_Out(); + process = false; + Options.ScoreVolume = Options.MultiScoreVolume; + break; + +#ifdef WOLAPI_INTEGRATION // implies also WINSOCK_IPX + case GAME_INTERNET: + if( PacketTransport ) + delete PacketTransport; + PacketTransport = new UDPInterfaceClass; + assert( PacketTransport != NULL ); + if( PacketTransport->Init() ) + { + switch( WOL_Main() ) + { + case 1: + // Start game. +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + break; + case 0: + // User cancelled. + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + break; + case -1: + // Patch was downloaded. Exit app. + Theme.Fade_Out(); + BlackPalette.Set( FADE_PALETTE_SLOW ); + return false; + } + } + else + { + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_MULTIPLAYER_GAME; //SEL_NONE; + delete PacketTransport; + PacketTransport = NULL; + } + break; +#endif + + /* + ** Network (IPX): start a new network game. + */ + case GAME_IPX: + WWDebugString ("RA95 - Game type is IPX.\n"); + /* + ** Init network system & remote-connect + */ +#ifdef WINSOCK_IPX + if (PacketTransport ) delete PacketTransport; +// if (WWMessageBox().Process("Select a protocol to use for network play.", "UDP", "IPX")) { + PacketTransport = new IPXInterfaceClass; + assert ( PacketTransport != NULL); +// }else{ +// PacketTransport = new UDPInterfaceClass; //IPXInterfaceClass; +// assert ( PacketTransport != NULL); +// if (!Get_Broadcast_Addresses()) { +// Session.Type = GAME_NORMAL; +// display = true; +// selection = SEL_NONE; +// delete PacketTransport; +// PacketTransport = NULL; +// break; +// } +// } + +#endif //WINSOCK_IPX + WWDebugString ("RA95 - About to call Init_Network.\n"); + if (Session.Type == GAME_IPX && Init_Network() && Remote_Connect()) { +#ifdef FIXIT_VERSION_3 + Options.ScoreVolume = Options.MultiScoreVolume; +#else + Options.ScoreVolume = 0; +#endif + process = false; + Theme.Fade_Out(); + } else { // user hit cancel, or init failed + Session.Type = GAME_NORMAL; + display = true; + selection = SEL_NONE; +#ifdef WINSOCK_IPX + delete PacketTransport; + PacketTransport = NULL; +#endif //WINSOCK_IPX + } + break; + + } + break; + + /* + ** Play a VQ + */ + case SEL_INTRO: + Theme.Fade_Out(); + + if (Debug_Flag) { + Play_Intro(Debug_Flag); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, true); // no transition picture to briefing + Keyboard->Clear(); + Play_Movie(VQ_SIZZLE, THEME_NONE, true); + Play_Movie(VQ_SIZZLE2, THEME_NONE, true); + } + + Theme.Queue_Song(THEME_CRUS); + + display = true; + fade = true; + selection = SEL_NONE; + + break; + + /* + ** Exit to DOS. + */ + case SEL_EXIT: + Theme.Fade_Out(); + BlackPalette.Set(FADE_PALETTE_SLOW); + return(false); + + /* + ** Display the hall of fame. + */ + case SEL_FAME: + break; + + case SEL_TIMEOUT: + if (Session.Attract && Session.RecordFile.Is_Available()) { + Session.Play = true; + if (Session.RecordFile.Open(READ)) { + Load_Recording_Values(Session.RecordFile); + process = false; + Theme.Fade_Out(); + } else { + Session.Play = false; + selection = SEL_NONE; + } + } else { + selection = SEL_NONE; + } + break; + + default: + break; + } + } + } else { + + /* + ** For Debug_Map (editor) mode to load scenario + */ + Scen.Set_Scenario_Name("SCG01EA.INI"); + } + + /* + ** Don't carry stray keystrokes into game. + */ + Keyboard->Clear(); + + /* + ** Initialize the random number generator(s) + */ + Init_Random(); + + /* + ** Save initialization values if we're recording this game. + */ + if (Session.Record) { + if (Session.RecordFile.Open(WRITE)) { + Save_Recording_Values(Session.RecordFile); + } else { + Session.Record = false; + } + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + switch(Session.Type) { + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: +#ifdef FIXIT_VERSION_3 + if( !bAftermathMultiplayer ) { +#else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS) { +#endif + NewUnitsEnabled = SecretUnitsEnabled = false; + } else { + NewUnitsEnabled = true; + } +// debugprint( "Non Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); + break; + case GAME_INTERNET: +#if (0) + if( !pWolapi ) + { +// debugprint( "pWolapi is null on internet game!" ); + Fatal( "pWolapi is null on internet game!" ); + } + //if( pWolapi->bEnableNewAftermathUnits ) + if( bAftermathMultiplayer ) + NewUnitsEnabled = true; + else + NewUnitsEnabled = SecretUnitsEnabled = false; +// debugprint( "Internet game: NewUnitsEnabled = %i\n", NewUnitsEnabled ); +#endif + break; + default: + break; + } +#endif + /* + ** Load the scenario. Specify variation 'A' for the editor; for the game, + ** don't specify a variation, to make 'Set_Scenario_Name()' pick a random one. + ** Skip this if we've already loaded a save-game. + */ + if (!gameloaded && !Session.LoadGame) { +// if (Debug_Map) { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, SCEN_VAR_A); +// } else { +// Set_Scenario_Name(Scen.ScenarioName, Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir); +// } + + /* + ** Start_Scenario() changes the palette; so, fade out & clear the screen + ** before calling it. + */ + Hide_Mouse(); + + if (selection != SEL_START_NEW_GAME) { + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + HiddenPage.Clear(); + VisiblePage.Clear(); + + } + Show_Mouse(); + + if ( !Start_Scenario(Scen.ScenarioName, Session.Type != GAME_SKIRMISH ) ) + { + return(false); + } + if (Special.IsFromInstall) Show_Mouse(); + Special.IsFromInstall = false; + } + + /* + ** For multiplayer games, initialize the inter-player message system. + ** Do this after loading the scenario, so the map's upper-left corner is + ** properly set. + */ + Session.Messages.Init( + Map.TacPixelX, Map.TacPixelY, // x,y for messages + 6, // max # msgs + MAX_MESSAGE_LENGTH-14, // max msg length + 7 * RESFACTOR, // font height in pixels + -1, -1, // x,y for edit line (appears above msgs) + 0,//BG 1, // enable edit overflow + 20, // min, + MAX_MESSAGE_LENGTH - 14, // max for trimming overflow +#ifdef WIN32 + Lepton_To_Pixel(Map.TacLeptonWidth)); // Width in pixels of buffer +#else + (320-SIDEBAR_WID)); // Width in pixels of buffer +#endif + + if (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH && + !Session.Play) { + if (Session.Type == GAME_TEN) { +#if(TEN) + Session.Create_TEN_Connections(); +#endif // TEN + } else if (Session.Type == GAME_MPATH) { +#if(MPATH) + Session.Create_MPATH_Connections(); +#endif + } else { + Session.Create_Connections(); + } + } + + + /* + ** If this isnt an internet game that set the unit build rate to its default value + */ + if (Session.Type != GAME_INTERNET){ + UnitBuildPenalty = 100; + } + + /* + ** Hide the SeenPage; force the map to render one frame. The caller can + ** then fade the palette in. + ** (If we loaded a game, this step will fade out the title screen. If we + ** started a scenario, Start_Scenario() will have played a couple of VQ + ** movies, which will have cleared the screen to black already.) + */ + Call_Back(); + Hide_Mouse(); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// Fade_Palette_To(BlackPalette, FADE_PALETTE_MEDIUM, Call_Back); +#ifdef WIN32 + HiddenPage.Clear(); + VisiblePage.Clear(); +#else + HidPage.Clear(); + SeenPage.Clear(); +#endif //WIN32 + Show_Mouse(); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + /* + ** Sidebar is always active in hi-res. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + } +#endif //WIN32 + Map.Flag_To_Redraw(); + Call_Back(); + Map.Render(); + +#ifdef WOLAPI_INTEGRATION + + //ajw debugging only +// debugprint( "Debugging Session...\n" ); +// debugprint( "Session.Players count is %i.\n", Session.Players.Count() ); + for (i = 0; i < Session.Players.Count(); i++) + { + NetNumType net; + NetNodeType node; + Session.Players[i]->Address.Get_Address( net, node ); +// debugprint( "Player %i, %s, color %i, ip %i.%i.%i.%i.%i.%i\n", i, Session.Players[i]->Name, +// Session.Players[i]->Player.Color, node[0], node[1], node[2], node[3], node[4], node[5] ); + } +// debugprint( "PlanetWestwoodPortNumber is %i\n", PlanetWestwoodPortNumber ); + +#endif + + return(true); +} + + +/*********************************************************************************************** + * Play_Intro -- plays the introduction & logo movies * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + * 05/08/1996 JLB : Modified for Red Alert and direction control. * + *=============================================================================================*/ +static void Play_Intro(bool sequenced) +{ + static VQType _counter = VQ_FIRST; + + Keyboard->Clear(); + if (sequenced) { + if (_counter <= VQ_FIRST) _counter = VQ_COUNT; + if (_counter == VQ_COUNT) _counter--; + if (_counter == VQ_REDINTRO) _counter--; + if (_counter == VQ_TITLE) _counter--; + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(VQType(_counter--), THEME_NONE); + +// Show_Mouse(); + } else { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef WIN32 + Play_Movie(VQ_REDINTRO, THEME_NONE, false); +#else + Play_Movie(VQ_TITLE, THEME_NONE, false); +#endif + } +} + + +/*********************************************************************************************** + * Anim_Init -- Initialize the VQ animation control structure. * + * * + * VQ animations are controlled by a structure passed to the VQ player. This routine * + * initializes the structure to values required by C&C. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only need to call this routine once at the beginning of the game. * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ + +#ifdef WIN32 +#ifdef MOVIE640 +//GraphicBufferClass VQ640(711, 400, (void *)NULL); +#endif +#endif +void Anim_Init(void) +{ +#if (0) +#ifdef WIN32 + + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); + AnimControl.DrawFlags = VQACFGF_TOPLEFT; + AnimControl.DrawFlags |= VQACFGF_BUFFER; +//AnimControl.DrawFlags |= VQACFGF_NODRAW; +//BG - M. Grayford says turn this off AnimControl.DrawFlags |= VQACFGF_NOSKIP; + + AnimControl.DrawFlags |= VQACFGF_NOSKIP; + AnimControl.FrameRate = -1; + AnimControl.DrawRate = -1; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.ImageBuf = (unsigned char *)SysMemPage.Get_Offset(); +#ifdef MOVIE640 + if(IsVQ640) { + AnimControl.ImageWidth = 711; + AnimControl.ImageHeight = 400; + AnimControl.ImageBuf = (unsigned char *)VQ640.Get_Offset(); + } +#endif + AnimControl.Vmode = 0; + AnimControl.OptionFlags |= VQAOPTF_CAPTIONS|VQAOPTF_EVA; + if (SlowPalette) { + AnimControl.OptionFlags |= VQAOPTF_SLOWPAL; + } + AnimControl.SoundObject = SoundObject; + AnimControl.PrimaryBufferPtr = PrimaryBufferPtr; + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#else //WIN32 + /* Configure player with INI file */ + VQA_DefaultConfig(&AnimControl); +// void const * font = Load_Font(FONT8); +// AnimControl.EVAFont = (char *)font; +// AnimControl.CapFont = (char *)font; + AnimControl.DrawerCallback = VQ_Call_Back; + AnimControl.ImageWidth = 320; + AnimControl.ImageHeight = 200; + AnimControl.Vmode = MCGA_MODE; + AnimControl.VBIBit = VertBlank; + AnimControl.DrawFlags |= VQACFGF_TOPLEFT; + AnimControl.OptionFlags |= VQAOPTF_HMIINIT|VQAOPTF_CAPTIONS|VQAOPTF_EVA; +// AnimControl.AudioBuf = (unsigned char *)HidPage.Get_Buffer(); +// AnimControl.AudioBufSize = 32768U; + AnimControl.DigiCard = NewConfig.DigitCard; + AnimControl.HMIBufSize = 8192; + AnimControl.DigiHandle = Get_Digi_Handle(); + AnimControl.Volume = 0x00FF; + AnimControl.AudioRate = 22050; +// if (NewConfig.Speed) AnimControl.AudioRate = 11025; + + if (!Debug_Quiet && Get_Digi_Handle() != -1) { + AnimControl.OptionFlags |= VQAOPTF_AUDIO; + } + + if (MonoClass::Is_Enabled()) { + AnimControl.OptionFlags |= VQAOPTF_MONO; + } + +#endif //WIN32 +#endif +} + + +/*********************************************************************************************** + * Parse_Command_Line -- Parses the command line parameters. * + * * + * This routine should be called before the graphic mode is initialized. It examines the * + * command line parameters and sets the appropriate globals. If there is an error, then * + * it outputs a command summary and then returns false. * + * * + * INPUT: argc -- The number of command line arguments. * + * * + * argv -- Pointer to character string array that holds the individual arguments. * + * * + * OUTPUT: bool; Was the command line parsed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/18/1995 JLB : Created. * + *=============================================================================================*/ +bool Parse_Command_Line(int argc, char * argv[]) +{ + /* + ** Parse the command line and set globals to reflect the parameters + ** passed in. + */ + Whom = HOUSE_GOOD; + Special.Init(); + + Debug_Map = false; + Debug_Unshroud = false; + + for (int index = 1; index < argc; index++) { + char * string; // Pointer to argument. + long code = 0; + + char arg_string[512]; + int str_len = strlen(argv[index]); + char *src = argv[index]; + char *dest = arg_string; + for (int i=0 ; i key during the first movie run. +// BreakoutAllowed = false; + break; + +#if(TEN) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + +#if(MPATH) + case PARM_ALLOW_SOLO: + Session.AllowSolo = 1; + break; +#endif + + default: + processed = false; + break; + } + if (processed) continue; + + +#ifdef CHEAT_KEYS + /* + ** Scenario Editor Mode + */ + if (stricmp(string, "-CHECKMAP") == 0) { + Debug_Check_Map = true; + continue; + } + +#endif + + /* + ** File search path override. + */ + if (strstr(string, "-CD")) { + CCFileClass::Set_Search_Drives(&string[3]); + continue; + } + +#if (0) + /* + ** Build speed modifier + */ + if (strstr (string, "-UNITRATE:")){ + int unit_rate; + sscanf (string, "-UNITRATE:%d", &unit_rate); + UnitBuildPenalty = unit_rate; + } +#endif //(0) + + /* + ** Specify destination connection for network play + */ + if (strstr(string, "-DESTNET")) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the command-line string, pulling off each address piece + */ + int i = 0; + char * p = strtok(string + 8, "."); + while (p) { + int x; + + sscanf(p, "%x", &x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL, "."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + continue; + } + + /* + ** Specify socket ID, as an offset from 0x4000. + */ + if (strstr(string, "-SOCKET")) { + unsigned short socket; + + socket = (unsigned short)(atoi(string + strlen("SOCKET"))); + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + continue; + } + + /* + ** Set the Net Stealth option + */ + if (strstr(string, "-STEALTH")) { + Session.NetStealth = true; + continue; + } + + /* + ** Set the Net Protection option + */ + if (strstr(string, "-MESSAGES")) { + Session.NetProtect = false; + continue; + } + + /* + ** Allow "attract" mode + */ + if (strstr(string, "-ATTRACT")) { + Session.Attract = true; + continue; + } + + +#ifdef WIN32 + /* + ** Set screen to 640x480 instead of 640x400 + */ + if (strstr(string, "-480")) { + ScreenHeight = 480; + continue; + } + + /* + ** Check for spawn from WChat + */ +#ifndef FIXIT_VERSION_3 // WChat eliminated. + if (strstr(string,"-WCHAT")){ + SpawnedFromWChat = true; + } +#endif + +#endif + +#ifdef CHEAT_KEYS + /* + ** Specify the random number seed (for debugging) + */ + if (strstr(string, "-SEED")) { + CustomSeed = (unsigned short)(atoi(string + strlen("SEED"))); + continue; + } + +#ifndef WIN32 + /* + ** Don't install Page Fault Handler (MUST use this for debugger) + */ + if (stricmp(string, "-NOPFS") == 0) { + UsePageFaultHandler = 0; + continue; + } +#endif + +#endif + + +#if(TEN) + /* + ** Enable TEN + */ + if (strstr(string, "TEN")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_TEN; + Special.IsFromInstall = false; + // + // Create the Ten network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + Ten = new TenConnManClass(); + Ten->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + Ten->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // TEN + +#if(MPATH) + /* + ** Enable MPATH + */ + if (strstr(string, "MPATH")) { + +#ifdef CHEAT_KEYS + Debug_Flag = true; + MonoClass::Enable(); +#endif + + Session.Type = GAME_MPATH; + Special.IsFromInstall = false; + // + // Create the MPath network manager. This allows us to keep + // the packet queues clean even while we're initializing the game, + // so the queues don't fill up in case we're slow, or the user + // didn't insert a CD. + // + MPath = new MPlayerManClass(); + MPath->Init(); + strcpy(Session.OptionsFile, "OPTIONS.INI"); + MPath->Flush_All(); + continue; + } + + /* + ** Set the game options filename + */ + if (strstr(string, "OPTIONS:")) { + strcpy(Session.OptionsFile, string + 8); + continue; + } +#endif // MPATH + + +#ifdef NEVER + /* + ** Handle the prog init differently in this case. + */ + if (strstr(string, "-V")) { + continue; + } +#endif + + /* + ** look for passed-in video mode to default to + */ +#ifndef WIN32 + if (strnicmp(string, "-V", strlen("-V")) == 0) { + Set_Video_Mode(MCGA_MODE); // do this to get around first_time variable... + Set_Original_Video_Mode(atoi(string+2)); + continue; + } +#endif + +#ifdef CHEAT_KEYS + if (strstr(string,"-NOMOVIES")){ + bNoMovies = true; + } +#endif + + /* + ** Special command line control parsing. + */ + if (strnicmp(string, "-X", strlen("-O")) == 0) { + string += strlen("-X"); + while (*string) { + char code = *string++; + switch (toupper(code)) { + +#ifdef CHEAT_KEYS + /* + ** Monochrome debug screen enable. + */ + case 'M': + MonoClass::Enable(); + break; + + /* + ** Inert weapons -- no units take damage. + */ + case 'I': + Special.IsInert = true; + break; + + /* + ** Hussled recharge timer. + */ + case 'H': + Special.IsSpeedBuild = true; + break; + + /* + ** "Record" a multi-player game + */ + case 'X': + Session.Record = 1; + break; + + /* + ** "Play Back" a multi-player game + */ + case 'Y': + Session.Play = 1; + break; + + /* + ** Print lots of debug stuff about events & packets + */ + case 'P': + Debug_Print_Events = true; + break; +#endif + + /* + ** Quiet mode override control. + */ + case 'Q': + Debug_Quiet = true; + break; + + default: + puts(TEXT_INVALID); + return(false); + } + + } + + continue; + } + } + return(true); +} + + +/*********************************************************************************************** + * Obfuscate -- Sufficiently transform parameter to thwart casual hackers. * + * * + * This routine borrows from CRC and PGP technology to sufficiently alter the parameter * + * in order to make it difficult to reverse engineer the key phrase. This is designed to * + * be used for hidden game options that will be released at a later time over Westwood's * + * Web page or through magazine hint articles. * + * * + * This algorithm is cryptographically categorized as a "one way hash". * + * * + * Since this is a one way transformation, it becomes much more difficult to reverse * + * engineer the pass phrase even if the resultant pass code is known. This has an added * + * benefit of making this algorithm immune to traditional cryptographic attacks. * + * * + * The largest strength of this transformation algorithm lies in the restriction on the * + * source vector being legal ASCII uppercase characters. This restriction alone makes even * + * a simple CRC transformation practically impossible to reverse engineer. This algorithm * + * uses far more than a simple CRC transformation to achieve added strength from advanced * + * attack methods. * + * * + * INPUT: string -- Pointer to the key phrase that will be transformed into a code. * + * * + * OUTPUT: Returns with the code that the key phrase is translated into. * + * * + * WARNINGS: A zero length pass phrase results in a 0x00000000 result code. * + * * + * HISTORY: * + * 08/19/1995 JLB : Created. * + *=============================================================================================*/ +long Obfuscate(char const * string) +{ + char buffer[1024]; + + if (!string) return(0); + memset(buffer, '\xA5', sizeof(buffer)); + + /* + ** Copy key phrase into a working buffer. This hides any transformation done + ** to the string. + */ + strncpy(buffer, string, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; + int length = strlen(buffer); + + /* + ** Only upper case letters are significant. + */ + strupr(buffer); + + /* + ** Ensure that only visible ASCII characters compose the key phrase. This + ** discourages the direct forced illegal character input method of attack. + */ + int index; + for (index = 0; index < length; index++) { + if (!isgraph(buffer[index])) { + buffer[index] = 'A' + (index%26); + } + } + + /* + ** Increase the strength of even short pass phrases by extending the + ** length to be at least a minimum number of characters. This helps prevent + ** a weak pass phrase from compromising the obfuscation process. This + ** process also forces the key phrase to be an even multiple of four. + ** This is necessary to support the cypher process that occurs later. + */ + if (length < 16 || (length & 0x03)) { + int maxlen = 16; + if (((length+3) & 0x00FC) > maxlen) { + maxlen = ((length+3) & 0x00FC); + } + for (index = length; index < maxlen; index++) { + buffer[index] = 'A' + ((('?' ^ buffer[index-length]) + index) % 26); + } + length = index; + buffer[length] = '\0'; + } + + /* + ** Transform the buffer into a number. This transformation is character + ** order dependant. + */ + long code = Calculate_CRC(buffer, length); + + /* + ** Record a copy of this initial transformation to be used in a later + ** self referential transformation. + */ + long copy = code; + + /* + ** Reverse the character string and combine with the previous transformation. + ** This doubles the workload of trying to reverse engineer the CRC calculation. + */ + strrev(buffer); + code ^= Calculate_CRC(buffer, length); + + /* + ** Perform a self referential transformation. This makes a reverse engineering + ** by using a cause and effect attack more difficult. + */ + code = code ^ copy; + + /* + ** Unroll and combine the code value into the pass phrase and then perform + ** another self referential transformation. Although this is a trivial cypher + ** process, it gives the sophisticated hacker false hope since the strong + ** cypher process occurs later. + */ + strrev(buffer); // Restore original string order. + for (index = 0; index < length; index++) { + code ^= (unsigned char)buffer[index]; + unsigned char temp = (unsigned char)code; + buffer[index] ^= temp; + code >>= 8; + code |= (((long)temp)<<24); + } + + /* + ** Introduce loss into the vector. This strengthens the key against traditional + ** cryptographic attack engines. Since this also weakens the key against + ** unconventional attacks, the loss is limited to less than 10%. + */ + for (index = 0; index < length; index++) { + static unsigned char _lossbits[] = {0x00,0x08,0x00,0x20,0x00,0x04,0x10,0x00}; + static unsigned char _addbits[] = {0x10,0x00,0x00,0x80,0x40,0x00,0x00,0x04}; + + buffer[index] |= _addbits[index % (sizeof(_addbits)/sizeof(_addbits[0]))]; + buffer[index] &= ~_lossbits[index % (sizeof(_lossbits)/sizeof(_lossbits[0]))]; + } + + /* + ** Perform a general cypher transformation on the vector + ** and use the vector itself as the cypher key. This is a variation on the + ** cypher process used in PGP. It is a very strong cypher process with no known + ** weaknesses. However, in this case, the cypher key is the vector itself and this + ** opens up a weakness against attacks that have access to this transformation + ** algorithm. The sheer workload of reversing this transformation should be enough + ** to discourage even the most determined hackers. + */ + for (index = 0; index < length; index += 4) { + short key1 = buffer[index]; + short key2 = buffer[index+1]; + short key3 = buffer[index+2]; + short key4 = buffer[index+3]; + short val1 = key1; + short val2 = key2; + short val3 = key3; + short val4 = key4; + + val1 *= key1; + val2 += key2; + val3 += key3; + val4 *= key4; + + short s3 = val3; + val3 ^= val1; + val3 *= key1; + short s2 = val2; + val2 ^= val4; + val2 += val3; + val2 *= key3; + val3 += val2; + + val1 ^= val2; + val4 ^= val3; + + val2 ^= s3; + val3 ^= s2; + + buffer[index] = val1; + buffer[index+1] = val2; + buffer[index+2] = val3; + buffer[index+3] = val4; + } + + /* + ** Convert this final vector into a cypher key code to be + ** returned by this routine. + */ + code = Calculate_CRC(buffer, length); + + /* + ** Return the final code value. + */ + return(code); +} + + +/*********************************************************************************************** + * Calculate_CRC -- Calculates a one-way hash from a data block. * + * * + * This routine is used to create a hash value from a data block. The algorithm is similar * + * to a CRC, but is faster. * + * * + * INPUT: buffer -- Pointer to a buffer of data to be 'hashed'. * + * * + * len -- The length of the buffer to compute the hash upon. * + * * + * OUTPUT: Returns with a 32bit hash value calculated from the specified buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/02/1996 JLB : Created. * + *=============================================================================================*/ +extern "C" { +long Calculate_CRC(void * buffer, long len) +{ + return(CRCEngine()(buffer, len)); +} +} + + +/*************************************************************************** + * Init_Random -- Initializes the random-number generator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +void Init_Random(void) +{ + #ifdef WIN32 + + /* + ** Gather some "random" bits from the system timer. Actually, only the + ** low order millisecond bits are secure. The other bits could be + ** easily guessed from the system clock (most clocks are fairly accurate + ** and thus predictable). + */ + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + CryptRandom.Seed_Bit(t.wSecond); + CryptRandom.Seed_Bit(t.wSecond>>1); + CryptRandom.Seed_Bit(t.wSecond>>2); + CryptRandom.Seed_Bit(t.wSecond>>3); + CryptRandom.Seed_Bit(t.wSecond>>4); + CryptRandom.Seed_Bit(t.wMinute); + CryptRandom.Seed_Bit(t.wMinute>>1); + CryptRandom.Seed_Bit(t.wMinute>>2); + CryptRandom.Seed_Bit(t.wMinute>>3); + CryptRandom.Seed_Bit(t.wMinute>>4); + CryptRandom.Seed_Bit(t.wHour); + CryptRandom.Seed_Bit(t.wDay); + CryptRandom.Seed_Bit(t.wDayOfWeek); + CryptRandom.Seed_Bit(t.wMonth); + CryptRandom.Seed_Bit(t.wYear); + #else + + /* + ** Gather some "random" bits from the DOS mode timer. + */ + struct timeb t; + ftime(&t); + CryptRandom.Seed_Byte(t.millitm); + CryptRandom.Seed_Byte(t.time); + #endif + +#ifdef FIXIT_MULTI_SAVE + // + // If we've loaded a multiplayer save game, return now; the random # + // class is loaded along with ScenarioClass. + // + if (Session.LoadGame) { + return; + } + + // + // If we're playing a recording, the Seed is loaded in + // Load_Recording_Values(). Just init the random # and return. + // + if (Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#else + /* + ** Do nothing if we've loaded a multiplayer game, or we're playing back + ** a recording; the random number generator is initialized by loading + ** the game. + */ + if (Session.LoadGame || Session.Play) { + RandNumb = Seed; + Scen.RandomNumber = Seed; + return; + } +#endif // FIXIT_MULTI_SAVE + + /* + ** Initialize the random number Seed. For multiplayer, this will have been done + ** in the connection dialogs. For single-player games, AND if we're not playing + ** back a recording, init the Seed to a random value. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH && + !Session.Play) { + + /* + ** Set the optional user-specified seed + */ + if (CustomSeed != 0) { + Seed = CustomSeed; + } else { + srand(time(NULL)); + Seed = rand(); + } + } + + /* + ** Initialize the random-number generators + */ + Scen.RandomNumber = Seed; + RandNumb = Seed; +} + + +/*********************************************************************************************** + * Load_Title_Page -- Load the background art for the title page. * + * * + * This routine will load the background art in a machine independent format. There is * + * different art required for the hi-res and lo-res versions of the game. * + * * + * INPUT: visible -- Should the title page art be copied to the visible page by this * + * routine? * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the mouse is hidden if the image is to be copied to the visible page. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +void Load_Title_Page(bool visible) +{ +#ifdef WIN32 + Load_Title_Screen("TITLE.PCX", &HidPage, (unsigned char*)CCPalette.Get_Data()); + if (visible) { + HidPage.Blit(SeenPage); + } +#else + Load_Picture("TITLE.CPS", HidPage, HidPage, CCPalette, BM_DEFAULT); + if (visible) { + HidPage.Blit(SeenPage); + } +#endif +} + + +/*********************************************************************************************** + * Init_Color_Remaps -- Initialize the text remap tables. * + * * + * There are various color scheme remap tables that are dependant upon the color remap * + * information embedded within the palette control file. This routine will fetch that * + * data and build the text remap tables as indicated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + * 09/11/2019 ST : The default resolution doesn't have to match the size of the palette image* + *=============================================================================================*/ +static void Init_Color_Remaps(void) +{ + /* + ** Setup the remap tables. PALETTE.CPS contains a special set of pixels in + ** the upper-left corner. Each row of 16 pixels is one range of colors. The + ** first row represents unity (the default color units are drawn in); rows + ** after that are the remap colors. + */ + +#ifdef WIN32 + GraphicBufferClass temp_page(320, 200, (void*)NULL); + temp_page.Clear(); + Load_Picture("PALETTE.CPS", temp_page, temp_page, NULL, BM_DEFAULT); +// jmarshall + temp_page.BlitLegacy(HidPage, (BOOL)false); +// jmarshall end +#else + Load_Picture("PALETTE.CPS", HidPage, HidPage, NULL, BM_DEFAULT); +#endif + for (PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++) { + + unsigned char * ptr = ColorRemaps[pcolor].RemapTable; + + for (int color = 0; color < 256; color++) { + ptr[color] = color; + } + + int index; + for (index = 0; index < 16; index++) { + ptr[HidPage.Get_Pixel(index, 0)] = HidPage.Get_Pixel(index, pcolor); + } + for (index = 0; index < 6; index++) { + ColorRemaps[pcolor].FontRemap[10+index] = HidPage.Get_Pixel(2+index, pcolor); + } + ColorRemaps[pcolor].BrightColor = WHITE; +// ColorRemaps[pcolor].BrightColor = HidPage.Get_Pixel(1, pcolor); + ColorRemaps[pcolor].Color = HidPage.Get_Pixel(4, pcolor); + + ColorRemaps[pcolor].Shadow = HidPage.Get_Pixel(10, pcolor); + ColorRemaps[pcolor].Background = HidPage.Get_Pixel(9, pcolor); + ColorRemaps[pcolor].Corners = HidPage.Get_Pixel(7, pcolor); + ColorRemaps[pcolor].Highlight = HidPage.Get_Pixel(4, pcolor); + ColorRemaps[pcolor].Bright = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Underline = HidPage.Get_Pixel(0, pcolor); + ColorRemaps[pcolor].Bar = HidPage.Get_Pixel(6, pcolor); + + /* + ** This must grab from column 4 because the multiplayer color dialog palette counts + ** on this to be true. + */ + ColorRemaps[pcolor].Box = HidPage.Get_Pixel(4, pcolor); + } + + /* 12/9/2019 SKY - Swap Blue and Grey color remaps */ + { + RemapControlType temp; + memcpy(&temp, &ColorRemaps[PCOLOR_BLUE], sizeof(RemapControlType)); + memcpy(&ColorRemaps[PCOLOR_BLUE], &ColorRemaps[PCOLOR_GREY], sizeof(RemapControlType)); + memcpy(&ColorRemaps[PCOLOR_GREY], &temp, sizeof(RemapControlType)); + } + + /* + ** Now do the special dim grey scheme + */ + for (int color = 0; color < 256; color++) { + GreyScheme.RemapTable[color] = color; + } + for (int index = 0; index < 6; index++) { + GreyScheme.FontRemap[10+index] = HidPage.Get_Pixel(9+index, PCOLOR_GREY) & 0x00FF; + } + GreyScheme.BrightColor = HidPage.Get_Pixel(3, PCOLOR_GREY) & 0x00FF; + GreyScheme.Color = HidPage.Get_Pixel(7, PCOLOR_GREY) & 0x00FF; + + GreyScheme.Shadow = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(15, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Background = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(14, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Corners = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(13, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Highlight = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(9, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bright = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Underline = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(5, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Bar = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + GreyScheme.Box = ColorRemaps[PCOLOR_GREY].RemapTable[HidPage.Get_Pixel(11, PCOLOR_GREY) & 0x00FF]; + + /* + ** Set up the metallic remap table for the font that prints over the tabs + */ + memset ((void*)&MetalScheme, 4, sizeof(MetalScheme)); + for (int color_counter = 0; color_counter < 16; color_counter++) { + MetalScheme.FontRemap[color_counter] = color_counter; + } + MetalScheme.FontRemap[1] = 128; + MetalScheme.FontRemap[2] = 12; + MetalScheme.FontRemap[3] = 13; + MetalScheme.FontRemap[4] = 14; + MetalScheme.Color = 128; + MetalScheme.Background = 0; + MetalScheme.Underline = 128; + + /* + ** Set up the font remap table for the mission briefing font + */ + for (int colr = 0; colr < 16; colr++) { + ColorRemaps[PCOLOR_TYPE].FontRemap[colr] = HidPage.Get_Pixel(colr, PCOLOR_TYPE); + } + + ColorRemaps[PCOLOR_TYPE].Shadow = 11; + ColorRemaps[PCOLOR_TYPE].Background = 10; + ColorRemaps[PCOLOR_TYPE].Corners = 10; + ColorRemaps[PCOLOR_TYPE].Highlight = 9; + ColorRemaps[PCOLOR_TYPE].Bright = 15; + ColorRemaps[PCOLOR_TYPE].Underline = 11; + ColorRemaps[PCOLOR_TYPE].Bar = 11; + ColorRemaps[PCOLOR_TYPE].Box = 10; + ColorRemaps[PCOLOR_TYPE].BrightColor = 15; + ColorRemaps[PCOLOR_TYPE].Color = 9; + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); +// GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_BLUE]); +} + + +/*********************************************************************************************** + * Init_Heaps -- Initialize the game heaps and buffers. * + * * + * This routine will allocate the game heaps and buffers. The rules file has already been * + * processed by the time that this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Heaps(void) +{ + /* + ** Initialize the game object heaps. + */ + Vessels.Set_Heap(Rule.VesselMax); + Units.Set_Heap(Rule.UnitMax); + Factories.Set_Heap(Rule.FactoryMax); + Terrains.Set_Heap(Rule.TerrainMax); + Templates.Set_Heap(Rule.TemplateMax); + Smudges.Set_Heap(Rule.SmudgeMax); + Overlays.Set_Heap(Rule.OverlayMax); + Infantry.Set_Heap(Rule.InfantryMax); + Bullets.Set_Heap(Rule.BulletMax); + Buildings.Set_Heap(Rule.BuildingMax); + Anims.Set_Heap(Rule.AnimMax); + Aircraft.Set_Heap(Rule.AircraftMax); + Triggers.Set_Heap(Rule.TriggerMax); + TeamTypes.Set_Heap(Rule.TeamTypeMax); + Teams.Set_Heap(Rule.TeamMax); + Houses.Set_Heap(HOUSE_MAX); + TriggerTypes.Set_Heap(Rule.TrigTypeMax); +// Weapons.Set_Heap(Rule.WeaponMax); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + SpeechBuffer[index] = new char [SPEECH_BUFFER_SIZE]; + SpeechRecord[index] = VOX_NONE; + assert(SpeechBuffer[index] != NULL); + } + + /* + ** Allocate the theater buffer block. + */ + TheaterBuffer = new Buffer(THEATER_BUFFER_SIZE); + assert(TheaterBuffer != NULL); +} + + +/*********************************************************************************************** + * Init_Expansion_Files -- Fetch any override expansion mixfiles. * + * * + * This routine will search for and register/cache any override mixfiles found. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Expansion_Files(void) +{ + /* + ** Need to search the search paths. ST - 3/15/2019 2:18PM + */ + const char *path = ".\\"; + char search_path[_MAX_PATH]; + char scan_path[_MAX_PATH]; + + for (int p = 0; p < 100; p++) { + + strcpy(search_path, path); + if (search_path[strlen(search_path) - 1] != '\\') { + strcat(search_path, "\\"); + } + + strcpy(scan_path, search_path); + strcat(scan_path, "SC*.MIX"); + + WIN32_FIND_DATA find_data; + memset(&find_data, 0, sizeof(find_data)); + HANDLE file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MFCD(ptr, &FastKey); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + memset(&find_data, 0, sizeof(find_data)); + strcpy(scan_path, search_path); + strcat(scan_path, "Ss*.MIX"); + file_handle = FindFirstFile(scan_path, &find_data); + if (file_handle != INVALID_HANDLE_VALUE) + { + do + { + char *ptr = strdup(find_data.cFileName); + new MFCD(ptr, &FastKey); + } while (FindNextFile(file_handle, &find_data)); + FindClose(file_handle); + } + + path = CDFileClass::Get_Search_Path(p); + + if (path == NULL) { + break; + } + } + +#if (0) + /* + ** Before all else, cache any additional mixfiles. + */ + struct find_t ff; // for _dos_findfirst + if (!_dos_findfirst("SC*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + MFCD::Cache(ptr); + } while (!_dos_findnext(&ff)); + } + if (!_dos_findfirst("SS*.MIX", _A_NORMAL, &ff)) { + char * ptr; + do { + ptr = strdup(ff.name); + new MFCD(ptr, &FastKey); + } while (!_dos_findnext(&ff)); + } +#endif +} + + +/*********************************************************************************************** + * Init_One_Time_Systems -- Initialize internal pointers to the bulk data. * + * * + * This performs the one-time processing required after the bulk data has been cached but * + * before the game actually starts. Typically, this routine extracts pointers to all the * + * embedded data sub-files within the main game data mixfile. This routine must be called * + * AFTER the bulk data has been cached. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine AFTER the bulk data has been cached. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_One_Time_Systems(void) +{ + Call_Back(); + Map.One_Time(); + Logic.One_Time(); + Options.One_Time(); + Session.One_Time(); + + ObjectTypeClass::One_Time(); + BuildingTypeClass::One_Time(); + BulletTypeClass::One_Time(); + HouseTypeClass::One_Time(); + TemplateTypeClass::One_Time(); + OverlayTypeClass::One_Time(); + SmudgeTypeClass::One_Time(); + TerrainTypeClass::One_Time(); + UnitTypeClass::One_Time(); + VesselTypeClass::One_Time(); + InfantryTypeClass::One_Time(); + AnimTypeClass::One_Time(); + AircraftTypeClass::One_Time(); + HouseClass::One_Time(); +} + + +/*********************************************************************************************** + * Init_Fonts -- Initialize all the game font pointers. * + * * + * This routine is used to fetch pointers to the game fonts. The mixfile containing these * + * fonts must have been previously cached. This routine is a necessary prerequisite to * + * displaying any dialogs or printing any text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Fonts(void) +{ + Metal12FontPtr = MFCD::Retrieve("12METFNT.FNT"); + MapFontPtr = MFCD::Retrieve("HELP.FNT"); + Font6Ptr = MFCD::Retrieve("6POINT.FNT"); + GradFont6Ptr = MFCD::Retrieve("GRAD6FNT.FNT"); + EditorFont = MFCD::Retrieve("EDITFNT.FNT"); + Font8Ptr = MFCD::Retrieve("8POINT.FNT"); + FontPtr = (char *)Font8Ptr; + Set_Font(FontPtr); + Font3Ptr = MFCD::Retrieve("3POINT.FNT"); + ScoreFontPtr = MFCD::Retrieve("SCOREFNT.FNT"); + FontLEDPtr = MFCD::Retrieve("LED.FNT"); + VCRFontPtr = MFCD::Retrieve("VCR.FNT"); + TypeFontPtr = MFCD::Retrieve("8POINT.FNT"); //("TYPE.FNT"); //VG 10/17/96 +} + + +/*********************************************************************************************** + * Init_CDROM_Access -- Initialize the CD-ROM access handler. * + * * + * This routine is called to setup the CD-ROM access or emulation handler. It will ensure * + * that the appropriate CD-ROM is present (dependant on the RequiredCD global). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The fonts, palettes, and other bootstrap systems must have been initialized * + * prior to calling this routine since this routine will quite likely display * + * a dialog box requesting the appropriate CD be inserted. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_CDROM_Access(void) +{ + VisiblePage.Clear(); + HidPage.Clear(); + +#ifdef FIXIT_VERSION_3 + // Determine if we're going to be running from a DVD. + // The entire session will either require a DVD, or the regular CDs. Never both. + // Call Using_DVD() to determine which case it is. + // Here we set the value that Using_DVD() returns. + Determine_If_Using_DVD(); + // Force_CD_Available() is modified when Using_DVD() is true so that all requests become requests for the DVD. +#endif + + /* + ** Always try to look at the CD-ROM for data files. + */ + if (!CCFileClass::Is_There_Search_Drives()) { + + /* + ** This call is needed because of a side effect of this function. It will examine the + ** CD-ROMs attached to this computer and set the appropriate status values. Without this + ** call, the "?:\\" could not be filled in correctly. + */ + Force_CD_Available(-1); + + /* + ** If there are no search drives specified then we must be playing + ** off cd, so read files from there. + */ + int error; + + do { + error = CCFileClass::Set_Search_Drives("?:\\"); + switch (error) { + case 1: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(TXT_CD_ERROR1, TXT_OK); + Prog_End("Init_CDROM_Access - CD_ERROR1", true); + Emergency_Exit(EXIT_FAILURE); + + case 2: + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + if (WWMessageBox().Process(TXT_CD_DIALOG_1, TXT_OK, TXT_CANCEL) == 1) { + Prog_End("Init_CDROM_Access - CD_ERROR2", true); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + + default: + VisiblePage.Clear(); + Show_Mouse(); + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Init_CDROM_Access - Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + Hide_Mouse(); + break; + } + } while (error); + + RequiredCD = -1; + } else { + + /* + ** If there are search drives specified then all files are to be + ** considered local. + */ + RequiredCD = -2; + } +} + + +/*********************************************************************************************** + * Init_Bootstrap_Mixfiles -- Registers and caches any mixfiles needed for bootstrapping. * + * * + * This routine will register the initial mixfiles that are required to display error * + * messages and get input from the player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine before any dialogs would be displayed to the * + * player. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bootstrap_Mixfiles(void) +{ + int temp = RequiredCD; + RequiredCD = -2; + +#ifdef WOLAPI_INTEGRATION + CCFileClass fileWolapiMix( "WOLAPI.MIX" ); + if( fileWolapiMix.Is_Available() ) + { + new MFCD( "WOLAPI.MIX", &FastKey ); + MFCD::Cache( "WOLAPI.MIX" ); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + CCFileClass file2("EXPAND2.MIX"); + if (file2.Is_Available()) { + new MFCD("EXPAND2.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND2.MIX"); + assert(ok); + } +#endif + +#ifdef FIXIT_CSII // Ok. ajw + //bool ok1; + #if 0 + new MFCD("HIRES1.MIX", &FastKey); + ok1 = MFCD::Cache("HIRES1.MIX"); + assert(ok1); + #else + //new MFCD("LORES1.MIX", &FastKey); + //ok1 = MFCD::Cache("LORES1.MIX"); + //assert(ok1); + #endif +#endif + +#ifdef FIXIT_ANTS // Ok. ajw + CCFileClass file("EXPAND.MIX"); + if (file.Is_Available()) { + new MFCD("EXPAND.MIX", &FastKey); + bool ok = MFCD::Cache("EXPAND.MIX"); + assert(ok); + } +#endif + + new MFCD("REDALERT.MIX", &FastKey); + + /* + ** Bootstrap enough of the system so that the error dialog box can successfully + ** be displayed. + */ + new MFCD("LOCAL.MIX", &FastKey); // Cached. + bool ok = MFCD::Cache("LOCAL.MIX"); + assert(ok); + +#if 1 + new MFCD("HIRES.MIX", &FastKey); + ok = MFCD::Cache("HIRES.MIX"); + assert(ok); + + new MFCD("NCHIRES.MIX", &FastKey); //Non-cached hires stuff incl VQ palettes +#else + new MFCD("LORES.MIX", &FastKey); + ok = MFCD::Cache("LORES.MIX"); + assert(ok); +#endif //WIN32 + + RequiredCD = temp; +} + + +/*********************************************************************************************** + * Init_Secondary_Mixfiles -- Register and cache secondary mixfiles. * + * * + * This routine is used to register the mixfiles that are needed for main menu processing. * + * Call this routine before the main menu is display and processed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +//#define DENZIL_MIXEXTRACT +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outfile); +#endif + +static void Init_Secondary_Mixfiles(void) +{ + MainMix = new MFCD("MAIN.MIX", &FastKey); + assert(MainMix != NULL); + + //Denzil extract mixfile + #ifdef DENZIL_MIXEXTRACT + #if(0) + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES1.MIX", "o:\\projects\\radvd\\data\\extract\\movies1.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #else + Extract("CONQUER.MIX", "o:\\projects\\radvd\\data\\extract\\conquer.mix"); + Extract("EDHI.MIX", "o:\\projects\\radvd\\data\\extract\\edhi.mix"); + Extract("EDLO.MIX", "o:\\projects\\radvd\\data\\extract\\edlo.mix"); + Extract("GENERAL.MIX", "o:\\projects\\radvd\\data\\extract\\general.mix"); + Extract("INTERIOR.MIX", "o:\\projects\\radvd\\data\\extract\\interior.mix"); + Extract("MOVIES2.MIX", "o:\\projects\\radvd\\data\\extract\\movies2.mix"); + Extract("SCORES.MIX", "o:\\projects\\radvd\\data\\extract\\scores.mix"); + Extract("SNOW.MIX", "o:\\projects\\radvd\\data\\extract\\snow.mix"); + Extract("SOUNDS.MIX", "o:\\projects\\radvd\\data\\extract\\sounds.mix"); + Extract("RUSSIAN.MIX", "o:\\projects\\radvd\\data\\extract\\russian.mix"); + Extract("ALLIES.MIX", "o:\\projects\\radvd\\data\\extract\\allies.mix"); + Extract("TEMPERAT.MIX", "o:\\projects\\radvd\\data\\extract\\temperat.mix"); + #endif + #endif + + /* + ** Inform the file system of the various MIX files. + */ + ConquerMix = new MFCD("CONQUER.MIX", &FastKey); // Cached. +// new MFCD("TRANSIT.MIX", &FastKey); + + if (GeneralMix == NULL) GeneralMix = new MFCD("GENERAL.MIX", &FastKey); // Never cached. + + if (CCFileClass("MOVIES1.MIX").Is_Available()) { + MoviesMix = new MFCD("MOVIES1.MIX", &FastKey); // Never cached. + } else { + MoviesMix = new MFCD("MOVIES2.MIX", &FastKey); // Never cached. + } + assert(MoviesMix != NULL); + + /* + ** Register the score mixfile. + */ + ScoresPresent = true; + ScoreMix = new MFCD("SCORES.MIX", &FastKey); + ThemeClass::Scan(); + + /* + ** These are sound card specific, but the install program would have + ** copied the correct versions to the hard drive. + */ + new MFCD("SPEECH.MIX", &FastKey); // Never cached. + new MFCD("SOUNDS.MIX", &FastKey); // Cached. + new MFCD("RUSSIAN.MIX", &FastKey); // Cached. + new MFCD("ALLIES.MIX", &FastKey); // Cached. +} + + +/*********************************************************************************************** + * Bootstrap -- Perform the initial bootstrap procedure. * + * * + * This routine will load and initialize the game engine such that a dialog box could be * + * displayed. Because this is very critical, call this routine before any other game * + * initialization code. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Bootstrap(void) +{ + BlackPalette.Set(); + + /* + ** Be sure to short circuit the CD-ROM check if there is a CD-ROM override + ** path. + */ + if (CCFileClass::Is_There_Search_Drives()) { + RequiredCD = -2; + } + + /* + ** Process the message loop until we are in focus. We need to be in focus to read pixels from + ** the screen. + */ +#if (0) //PG + #ifdef WIN32 + do { + Keyboard->Check(); + } while (!GameInFocus); + AllSurfaces.SurfacesRestored = false; + #endif + + /* + ** Perform any special debug-only processing. This includes preparing the + ** monochrome screen. + */ + Mono_Clear_Screen(); +#endif + /* + ** Register and make resident all local mixfiles with particular emphasis + ** on the mixfiles that are necessary to display and error messages and + ** process further initialization. + */ + Init_Bootstrap_Mixfiles(); + + /* + ** Initialize the resident font pointers. + */ + Init_Fonts(); + +#ifndef WIN32 + /* + ** Install the hard error handler. + */ + _harderr(harderr_handler); // BG: Install hard error handler + + /* + ** Install a Page Fault handler + */ + if (UsePageFaultHandler) { + Install_Page_Fault_Handle(); + } +#endif + + /* + ** Setup the keyboard processor in preparation for the game. + */ + #ifdef WIN32 + Keyboard->Clear(); + #else + Keyboard_Attributes_Off(BREAKON | SCROLLLOCKON | TRACKEXT | PAUSEON | CTRLSON | CTRLCON | FILTERONLY | TASKSWITCHABLE); + Keyboard_Attributes_On(PASSBREAKS); + Keyboard->Clear(); + #endif + + /* + ** This is the shape staging buffer. It must always be available, so it is + ** allocated here and never freed. The library sets the globals ShapeBuffer + ** and ShapeBufferSize to these values, so it can be accessed for other + ** purposes. + */ + Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Fetch the language text from the hard drive first. If it cannot be + ** found on the hard drive, then look for it in the mixfile. + */ +#ifdef STEVES_LOAD_OVERRIDE + RawFileClass strings ("CONQUER.ENG"); + if (strings.Is_Available()){ + SystemStrings = new char [strings.Size()]; + strings.Read((void*)SystemStrings, strings.Size()); + }else{ + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); + } +#else + SystemStrings = (char const *)MFCD::Retrieve(Language_Name("CONQUER")); +#endif + DebugStrings = (char const *)MFCD::Retrieve("DEBUG.ENG"); + + /* + ** Default palette initialization. + */ + // PG_TO_FIX. This doesn't seem right. ST - 5/9/2019 + memmove((unsigned char *)&GamePalette[0], (void *)MFCD::Retrieve("TEMPERAT.PAL"), 768L); +// jmarshall + memmove((unsigned char*)&CCPalette[0], (void*)MFCD::Retrieve("TEMPERAT.PAL"), 768L); +// jmarshall end + WhitePalette[0] = BlackPalette[0]; + GamePalette.Set(); + + /* + ** Initialize expansion files (if present). Expansion files must be located + ** in the current directory. + */ + Init_Expansion_Files(); + + SidebarScheme.Background = BLACK; + SidebarScheme.Corners = LTGREY; + SidebarScheme.Shadow = DKGREY; + SidebarScheme.Highlight = WHITE; + SidebarScheme.Color = LTGREY; + SidebarScheme.Bright = WHITE; + SidebarScheme.BrightColor = WHITE; + SidebarScheme.Box = LTGREY; + GadgetClass::Set_Color_Scheme(&SidebarScheme); +} + + +/*********************************************************************************************** + * Init_Mouse -- Initialize the mouse system. * + * * + * This routine will ensure that a valid mouse driver is present and a working mouse * + * pointer can be displayed. The mouse is hidden when this routine exits. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Mouse(void) +{ + /* + ** Since there is no mouse shape currently available we need + ** to set one of our own. + */ +#ifdef WIN32 + ShowCursor(false); +#endif + if (MouseInstalled) { + void const * temp_mouse_shapes = MFCD::Retrieve("MOUSE.SHP"); + if (temp_mouse_shapes) { + Set_Mouse_Cursor(0, 0, 0); + while (Get_Mouse_State() > 1) Show_Mouse(); + } + } else { + char buffer[255]; + GamePalette.Set(); + GamePalette.Set(); + sprintf(buffer, TEXT_NO_MOUSE); + VisiblePage.Clear(); + WWMessageBox().Process(buffer, TXT_OK); + Prog_End("Init_Mouse", true); + Emergency_Exit(1); + } + + Map.Set_Default_Mouse(MOUSE_NORMAL, false); + Show_Mouse(); + while (Get_Mouse_State() > 1) Show_Mouse(); + Call_Back(); + Hide_Mouse(); +} + + +#ifdef OBSOLETE +/*********************************************************************************************** + * Init_Authorization -- Verifies that the player is authorized to play the game. * + * * + * This is a development routine that restricts access to the game by way of passwords. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Authorization(void) +{ + if (Special.IsFromInstall) return; + + Load_Title_Page(); +#ifdef WIN32 + Wait_Vert_Blank(); +#else //WIN32 + Init_Delay(); + Wait_Vert_Blank(VertBlank); +#endif //WIN32 + + CCPalette.Set(); +// Set_Palette(Palette); + HidPage.Blit(SeenPage); + Show_Mouse(); + + /* + ** Fetch the type of game to be played. This will be either + ** C&C:Red Alert or C&C:Plus. + */ +//tryagain: + + bool ok = Debug_Flag; + int counter = 3; + + if (Debug_Flag) ok = true; + + /* + ** C&C:Red Alert requires a password for legal entry. Try (three times) to get a correct + ** password. If not found, then try again. + */ + bool skipper = false; +#ifdef NEVER + while (!ok && counter) { + SmartPtr str = Fetch_Password(TXT_PASSWORD_CAPTION, TXT_PASSWORD_MESSAGE, TXT_OK); + SmartPtr lptr = &CheatCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &EditorCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + break; + } + } + lptr = &PlayCodes[0]; + while (*lptr) { + if (Obfuscate(str) == *lptr++) { + ok = true; + skipper = true; + break; + } + } + + if (ok) break; + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Delay(TIMER_SECOND*(4-counter)*1); + if (WWMessageBox().Process(TXT_PASSWORD_ERROR, TXT_TRY_AGAIN, TXT_CANCEL)) { + goto tryagain; + } + + counter--; + if (counter == 0) goto tryagain; + } +#endif + + if (!skipper) { + CCPalette.Set(); + } + + Hide_Mouse(); + Load_Title_Page(); + HidPage.Blit(SeenPage); + Show_Mouse(); + Call_Back(); +} +#endif + + +/*********************************************************************************************** + * Init_Bulk_Data -- Initialize the time-consuming mixfile caching. * + * * + * This routine is called to handle the time consuming process of game initialization. * + * The title page will be displayed when this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will take a very long time. * + * * + * HISTORY: * + * 06/03/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Bulk_Data(void) +{ + /* + ** Cache the main game data. This operation can take a very long time. + */ + MFCD::Cache("CONQUER.MIX"); + if (SampleType != 0 && !Debug_Quiet) { + MFCD::Cache("SOUNDS.MIX"); + MFCD::Cache("RUSSIAN.MIX"); + MFCD::Cache("ALLIES.MIX"); + } + Call_Back(); + + /* + ** Fetch the tutorial message data. + */ + INIClass ini; + ini.Load(CCFileClass("TUTORIAL.INI")); + for (int index = 0; index < ARRAY_SIZE(TutorialText); index++) { + TutorialText[index] = NULL; + + char buffer[128]; + char num[10]; + sprintf(num, "%d", index); + if (ini.Get_String("Tutorial", num, "", buffer, sizeof(buffer))) { + TutorialText[index] = strdup(buffer); + } + } + + /* + ** Perform one-time game system initializations. + */ + Init_One_Time_Systems(); +} + + +/*********************************************************************************************** + * Init_Keys -- Initialize the cryptographic keys. * + * * + * This routine will initialize the fast cryptographic key. It will also initialize the * + * slow one if this is a scenario editor version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Init_Keys(void) +{ + RAMFileClass file((void*)Keys, strlen(Keys)); + INIClass ini; + ini.Load(file); + + FastKey = ini.Get_PKey(true); +#ifdef SCENARIO_EDITOR + //SlowKey = ini.Get_PKey(false); +#endif +} + + +/*************************************************************************** + * Save_Recording_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_Recording_Values(CCFileClass & file) +{ + Session.Save(file); + file.Write(&BuildLevel, sizeof(BuildLevel)); + file.Write(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Write(&Seed, sizeof(Seed)); + file.Write(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Write(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Write(&Whom, sizeof(Whom)); + file.Write(&Special, sizeof(SpecialClass)); + file.Write(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_Recording_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_Recording_Values(CCFileClass & file) +{ + Session.Load(file); + file.Read(&BuildLevel, sizeof(BuildLevel)); + file.Read(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Read(&Seed, sizeof(Seed)); + file.Read(&Scen.Scenario, sizeof(Scen.Scenario)); + file.Read(Scen.ScenarioName, sizeof(Scen.ScenarioName)); + file.Read(&Whom, sizeof(Whom)); + file.Read(&Special, sizeof(SpecialClass)); + file.Read(&Options, sizeof(GameOptionsClass)); + return (true); +} + +extern "C" { +void __PRO(void) { +// printf("_pro\n"); +} +} + +#ifdef DENZIL_MIXEXTRACT +void Extract(char* filename, char* outname) + { + CCFileClass inFile(filename); + CCFileClass outFile(outname); + + inFile.Open(); + outFile.Open(WRITE); + + void* buffer = malloc(32768); + + if (buffer) + { + unsigned long size = inFile.Size(); + unsigned long bytes; + + while (size > 0) + { + bytes = inFile.Read(buffer, 32768); + outFile.Write(buffer, bytes); + size -= bytes; + } + + free(buffer); + } + } +#endif + + +#ifdef FIXIT_VERSION_3 + +bool bUsingDVD = false; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +bool Is_DVD_Installed() +{ + bool bInstalled; + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + return false; + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "DVD", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bInstalled = false; + else + bInstalled = (bool)dwValue; // (Presumably true, if it's there...) + + RegCloseKey( hKey ); + + return bInstalled; +} + +//*********************************************************************************************** +bool Determine_If_Using_DVD() +{ + // Determines if the user has a DVD currently available. If they do, we'll use it throughout the + // session. Else we won't check for it again and will always ask for CDs. + if( Is_DVD_Installed() ) + { + if( Force_CD_Available( 5 ) ) + { + bUsingDVD = true; + } + else + { + // User hit cancel. Allow things to progress normally. They will be prompted for + // a Red Alert disk as usual. + bUsingDVD = false; + } + } + else + bUsingDVD = false; + + return bUsingDVD; +} + +//*********************************************************************************************** +bool Using_DVD() +{ + return bUsingDVD; +} + +#endif + + + + +/*********************************************************************************************** + * Free_Heaps -- Clear out the heaps before exit * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/18/2019 11:59AM ST : Created. * + *=============================================================================================*/ +void Free_Heaps(void) +{ + HouseTypes.Clear(); + BuildingTypes.Clear(); + AircraftTypes.Clear(); + InfantryTypes.Clear(); + BulletTypes.Clear(); + AnimTypes.Clear(); + UnitTypes.Clear(); + VesselTypes.Clear(); + TemplateTypes.Clear(); + TerrainTypes.Clear(); + OverlayTypes.Clear(); + SmudgeTypes.Clear(); +#if (0) + HouseTypeClass::Init_Heap(); + BuildingTypeClass::Init_Heap(); + AircraftTypeClass::Init_Heap(); + InfantryTypeClass::Init_Heap(); + BulletTypeClass::Init_Heap(); + AnimTypeClass::Init_Heap(); + UnitTypeClass::Init_Heap(); + VesselTypeClass::Init_Heap(); + TemplateTypeClass::Init_Heap(); + TerrainTypeClass::Init_Heap(); + OverlayTypeClass::Init_Heap(); + SmudgeTypeClass::Init_Heap(); + + // Heap init moved here from globals.cpp. ST - 5/20/2019 + CCPtr::Set_Heap(&Aircraft); + CCPtr::Set_Heap(&Anims); + CCPtr::Set_Heap(&Buildings); + CCPtr::Set_Heap(&Bullets); + CCPtr::Set_Heap(&Factories); + CCPtr::Set_Heap(&Houses); + CCPtr::Set_Heap(&Infantry); + CCPtr::Set_Heap(&Overlays); + CCPtr::Set_Heap(&Smudges); + CCPtr::Set_Heap(&Teams); + CCPtr::Set_Heap(&TeamTypes); + CCPtr::Set_Heap(&Templates); + CCPtr::Set_Heap(&Terrains); + CCPtr::Set_Heap(&Triggers); + CCPtr::Set_Heap(&TriggerTypes); + + CCPtr::Set_Heap(&HouseTypes); + CCPtr::Set_Heap(&BuildingTypes); + CCPtr::Set_Heap(&AircraftTypes); + CCPtr::Set_Heap(&InfantryTypes); + CCPtr::Set_Heap(&BulletTypes); + CCPtr::Set_Heap(&AnimTypes); + CCPtr::Set_Heap(&UnitTypes); + CCPtr::Set_Heap(&VesselTypes); + CCPtr::Set_Heap(&TemplateTypes); + CCPtr::Set_Heap(&TerrainTypes); + CCPtr::Set_Heap(&OverlayTypes); + CCPtr::Set_Heap(&SmudgeTypes); +#endif + + Vessels.Clear(); + Units.Clear(); + Factories.Clear(); + Terrains.Clear(); + Templates.Clear(); + Smudges.Clear(); + Overlays.Clear(); + Infantry.Clear(); + Bullets.Clear(); + Buildings.Clear(); + Anims.Clear(); + Aircraft.Clear(); + Triggers.Clear(); + TeamTypes.Clear(); + Teams.Clear(); + Houses.Clear(); + TriggerTypes.Clear(); + + /* + ** Speech holding tank buffer. Since speech does not mix, it can be placed + ** into a custom holding tank only as large as the largest speech file to + ** be played. + */ + for (int index = 0; index < ARRAY_SIZE(SpeechBuffer); index++) { + if (SpeechBuffer[index]) { + delete [] SpeechBuffer[index]; + SpeechBuffer[index] = NULL; + } + } + + /* + ** Allocate the theater buffer block. + */ + if (TheaterBuffer) { + delete TheaterBuffer; + TheaterBuffer = NULL; + } +} \ No newline at end of file diff --git a/code/redalert/inline.h b/code/redalert/inline.h new file mode 100644 index 0000000..458e4e8 --- /dev/null +++ b/code/redalert/inline.h @@ -0,0 +1,1066 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INLINE.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INLINE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/21/96 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * Cell_Coord -- Convert a cell to a coordinate value. * + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * Cell_X -- Fetch the X cell component from the cell value. * + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * Coord_Add -- Adds coordinates together. * + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * Coord_Mid -- Finds the midpoint between two coordinates. * + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * Coord_Sub -- Subtracts one coordinate from another. * + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * Dir_Facing -- Convert a DirType into a FacingType value. * + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * Direction -- Calculates the DirType from one cell to another. * + * Direction -- Determines the facing value from one coordinate to another. * + * Direction256 -- Calculate the facing value from one coordinate to another. * + * Direction8 -- Fetches the direction from one coordinate to another. * + * Distance -- Finds the distance between two arbitrary points. * + * Facing_Dir -- Convert a FacingType into a DirType. * + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * Percent_Chance -- Calculate a percentage chance event. * + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * Random_Pick -- Pick a random number in a specified range. * + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * Text_String -- Convert a text number into a text pointer. * + * XYP_COORD -- Convert pixel components into a coordinate value. * + * XYP_Coord -- Combine pixel values into a coordinate. * + * XY_Cell -- Create a cell from X and Y cell components. * + * XY_Coord -- Convert X Y lepton components into a COORD. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INLINE_H +#define INLINE_H + + +/*********************************************************************************************** + * Lepton_To_Pixel -- Convert a lepton value into pixel value. * + * * + * Use this routine to convert the specified lepton value into it's pixel corresponding * + * value. The pixel value returned will be the closest pixel value to the lepton value. It * + * will round up or down as necessary. * + * * + * INPUT: lepton -- The lepton value to convert into a pixel value. * + * * + * OUTPUT: Returns with the lepton value rounded to the nearest pixel component. * + * * + * WARNINGS: Precision is not maintained by this routine. Thus, if a value is converted to * + * pixel and then back to leptons, the value will probably not be the same. * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Pixel(LEPTON lepton) +{ + return (((int)(signed short)lepton * ICON_PIXEL_W) + (ICON_LEPTON_W / 2) - ((lepton < 0) ? (ICON_LEPTON_W - 1) : 0)) / ICON_LEPTON_W; +} + + +/*********************************************************************************************** + * Pixel_To_Lepton -- Convert pixel value into lepton equivalent. * + * * + * This routine will take the specified pixel value and convert it into lepton value. * + * * + * INPUT: pixel -- The pixel value to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the pixel value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Pixel_To_Lepton(int pixel) +{ + return (LEPTON)(((pixel * ICON_LEPTON_W) + (ICON_PIXEL_W / 2) - ((pixel < 0) ? (ICON_PIXEL_W - 1) : 0)) / ICON_PIXEL_W); +} + + +/*********************************************************************************************** + * XY_Coord -- Convert X Y lepton components into a COORD. * + * * + * This routine will take the specified X and Y lepton components and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y lepton components to combine. * + * * + * OUTPUT: Returns with a coordinate value that is created from the X and Y lepton components.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XY_Coord(LEPTON x, LEPTON y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = x; + coord.Sub.Y.Raw = y; + return(coord.Coord); +} + + +/*********************************************************************************************** + * XYP_COORD -- Convert pixel components into a coordinate value. * + * * + * This routine will take the specified pixel components and convert and combine them into * + * a coordinate value. * + * * + * INPUT: x,y -- The X and Y pixel components to coerce into a coordinate value. * + * * + * OUTPUT: Returns with the coordinate value that matches the pixel values specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_COORD(int x, int y) +{ + return(XY_Coord(Pixel_To_Lepton(x), Pixel_To_Lepton(y))); +} + + +/*********************************************************************************************** + * Coord_XCell -- Fetch the X cell component from a coordinate value. * + * * + * This routine will extract the X cell component from the coordinate value specified and * + * return the value. * + * * + * INPUT: coord -- The coordinate value to extract the X component from. * + * * + * OUTPUT: Returns with the X cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_XCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_YCell -- Fetch the Y cell component from a coordinate. * + * * + * This routine will extract the Y cell component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate to extract the Y cell from. * + * * + * OUTPUT: Returns with just the Y cell component of the coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Coord_YCell(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell); +} + + +/*********************************************************************************************** + * XY_Cell -- Create a cell from X and Y cell components. * + * * + * This routine will construct a cell value by taking the X and Y cell value components * + * and combining them appropriately. * + * * + * INPUT: x,y -- The X and Y cell components to combine. * + * * + * OUTPUT: Returns with the CELL value created from the specified components. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL XY_Cell(int x, int y) +{ + CELL_COMPOSITE cell; + cell.Cell = 0; + cell.Sub.X = x; + cell.Sub.Y = y; + return(cell.Cell); +} + + +/*********************************************************************************************** + * Cell_To_Lepton -- Convert a cell distance into a lepton distance. * + * * + * This routine will take the cell distance specified and convert it into a lepton distance.* + * * + * INPUT: cell_distance -- The distance in cells to convert. * + * * + * OUTPUT: Returns with the lepton equivalent of the cell distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Cell_To_Lepton(int cell_distance) +{ + LEPTON_COMPOSITE lepton; + lepton.Sub.Cell = (unsigned char)cell_distance; + lepton.Sub.Lepton = 0; + return(lepton.Raw); +} + + +/*********************************************************************************************** + * Lepton_To_Cell -- Convert lepton distance to cell distance. * + * * + * This routine will convert the specified lepton distance into the closest cell distance * + * possible. This might require rounding up or down as necessary. * + * * + * INPUT: lepton_distance -- The lepton distance to convert. * + * * + * OUTPUT: Returns with the cell distance that most closely corresponds to the lepton * + * distance specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Lepton_To_Cell(LEPTON lepton_distance) +{ + if (((LEPTON_COMPOSITE &)lepton_distance).Sub.Lepton >= (CELL_LEPTON_W/2)) { + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell + 1); + } + return(((LEPTON_COMPOSITE &)lepton_distance).Sub.Cell); +} + + +/*********************************************************************************************** + * Coord_X -- Fetches the X lepton component from a coordinate value. * + * * + * This routine will extract the X lepton component from the coordinate. * + * * + * INPUT: coord -- The coordinate to extract the X lepton equivalent from. * + * * + * OUTPUT: Returns with the X lepton portion of the coordinate value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_X(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Raw); +} + + +/*********************************************************************************************** + * Coord_Y -- Fetch the Y lepton component from the coordinate value. * + * * + * This routine will extract the Y lepton component from the coordinate value specified. * + * * + * INPUT: coord -- The coordinate value to dissect. * + * * + * OUTPUT: Returns with the Y lepton component from the specified coordinate value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline LEPTON Coord_Y(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Raw); +} + + +/*********************************************************************************************** + * Cell_X -- Fetch the X cell component from the cell value. * + * * + * This routine will extract the X cell component from the cell value specified. * + * * + * INPUT: cell -- The cell to extract. * + * * + * OUTPUT: Returns with the X cell component portion of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_X(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.X); +} + + +/*********************************************************************************************** + * Cell_Y -- Fetch the Y cell component from the cell value specified. * + * * + * This routine will extract the Y cell component from the cell value. * + * * + * INPUT: cell -- The cell value to extract from. * + * * + * OUTPUT: Returns with the Y cell component of the cell value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Cell_Y(CELL cell) +{ + return(((CELL_COMPOSITE &)cell).Sub.Y); +} + + +/*********************************************************************************************** + * Coord_XLepton -- Fetch the X sub-cell lepton component from the coordinate. * + * * + * This routine will extract just the X sub cell lepton component from the coordinate * + * specified. * + * * + * INPUT: coord -- The coordinate value to extract from. * + * * + * OUTPUT: Returns with the X lepton component of the coordinate that is part of the cell. * + * Thus, a coordinate that exactly lines up on the left edge of a cell would return * + * zero. One that exactly lines up on the right edge would return CELL_LEPTON_W. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/21/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_XLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton); +} + + +/*********************************************************************************************** + * Coord_YLepton -- Fetches the Y lepton sub-cell component from the coordinate. * + * * + * This routine will extract the sub-cell Y lepton portion of the coordinate. * + * * + * INPUT: coord -- The coordinate to dissect. * + * * + * OUTPUT: Returns with just the Y lepton portion of the coordinate and only for the sub-cell * + * it refers to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline int Coord_YLepton(COORDINATE coord) +{ + return(((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton); +} + + +/*********************************************************************************************** + * XYP_Coord -- Combine pixel values into a coordinate. * + * * + * This will convert X and Y pixel values into a coordinate. Primarily this is used for * + * converting mouse clicks into coordinate values. * + * * + * INPUT: x,y -- The X and Y pixel coordinates to convert. Origin is upper left corner. * + * * + * OUTPUT: Returns with the coordinate that most closely corresponds to the pixel values * + * specified. * + * * + * WARNINGS: The coordinate is relative to the upper left corner (0,0). To conver the * + * coordinate to a game relative one, it must be biased by the display coordinate * + * of the tactical map and the screen position of the tactical display. * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE XYP_Coord(int x, int y) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = Pixel_To_Lepton(x); + coord.Sub.Y.Raw = Pixel_To_Lepton(y); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Cell_Coord -- Convert a cell to a coordinate value. * + * * + * This routine will convert the specified cell into a coordinat value. The coordinate * + * will refer to the center of the cell specified. * + * * + * INPUT: cell -- The cell to convert into a coordinate. * + * * + * OUTPUT: Returns with the coordinate that refers to the center of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Cell_Coord(CELL cell) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.X); + coord.Sub.X.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + coord.Sub.Y.Sub.Cell = (unsigned char)(((CELL_COMPOSITE &)cell).Sub.Y); + coord.Sub.Y.Sub.Lepton = (unsigned char)(CELL_LEPTON_W / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Snap -- Coerce coordinate to refer to center of a cell. * + * * + * This routine will take the specified coordinate and force it to refer to the center of * + * the cell. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with the specified coordinate after it has been modified to refer to the * + * center of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Snap(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = CELL_LEPTON_W/2; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = CELL_LEPTON_W/2; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Fraction -- Discards all but the sub-cell components of the coordinate. * + * * + * Use this routine to discard the cell components of the coordinate, leaving only the * + * sub-cell component. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with just the sub-cell components intact from the supplied coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Fraction(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Cell = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Cell = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Whole -- Discards the sub-cell components of the coordinate. * + * * + * This routine will discard the sub-cell components, leaving only the whole cell portion. * + * * + * INPUT: coord -- The coordinate to modify. * + * * + * OUTPUT: Returns with only the whole cell components of the coordinate intact. The * + * resulting coordinate will refer to the upper left corner of the cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Whole(COORDINATE coord) +{ + ((COORD_COMPOSITE &)coord).Sub.X.Sub.Lepton = 0; + ((COORD_COMPOSITE &)coord).Sub.Y.Sub.Lepton = 0; + return(coord); +} + + +/*********************************************************************************************** + * Coord_Add -- Adds coordinates together. * + * * + * This routine will add one coordinate to another. Actually, it adds the X and Y components* + * separately (signed) and then recombines them back into a coordinate. * + * * + * INPUT: coord1 -- One coordinate to add. * + * * + * coord2 -- The other coordinate to add. * + * * + * OUTPUT: Returns with the logical add of the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Add(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Sub -- Subtracts one coordinate from another. * + * * + * This routine will subtract one coordinate from the other. The coordinates are broken * + * up into their X and Y components, the subtraction is performed, and then they are * + * recombined back into a coordinate to be returned. * + * * + * INPUT: coord1 -- The coordinate to be subtracted from. * + * * + * coord2 -- The coordinate to subtract. * + * * + * OUTPUT: Returns with the result of subtracting coord2 from coord1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Sub(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.X.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.X.Raw); + coord.Sub.Y.Raw = (LEPTON)((int)(short)((COORD_COMPOSITE &)coord1).Sub.Y.Raw - (int)(short)((COORD_COMPOSITE &)coord2).Sub.Y.Raw); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Coord_Mid -- Finds the midpoint between two coordinates. * + * * + * This will find the coordinate that is exactly between the two coordinates specified. * + * * + * INPUT: coord1 -- The first coordinate. * + * * + * coord2 -- The second coordinate. * + * * + * OUTPUT: Returns with the midpoint between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Coord_Mid(COORDINATE coord1, COORDINATE coord2) +{ + COORD_COMPOSITE coord; + + coord.Sub.X.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.X.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.X.Raw) / 2); + coord.Sub.Y.Raw = (LEPTON)(((int)((COORD_COMPOSITE &)coord1).Sub.Y.Raw + (int)((COORD_COMPOSITE &)coord2).Sub.Y.Raw) / 2); + return(coord.Coord); +} + + +/*********************************************************************************************** + * Dir_Facing -- Convert a DirType into a FacingType value. * + * * + * Use this routine to convert the specified DirType value into the closest FacingType * + * value that matches it. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with a FacingType value that most closely matches the DirType specified. * + * * + * WARNINGS: Precision of direction is lost by this transformation. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_Facing(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing+0x10)&0xFF)>>5); +} + + +/*********************************************************************************************** + * Facing_Dir -- Convert a FacingType into a DirType. * + * * + * This will conver the specified FacingType value into the DirType that exactly matches * + * it. * + * * + * INPUT: facing -- The FacingType to convert. * + * * + * OUTPUT: Returns with the DirType that exactly matches the facing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Facing_Dir(FacingType facing) +{ + return (DirType)((int)facing << 5); +} + + +/*********************************************************************************************** + * Dir_To_16 -- Convert a facing to a 0..15 value. * + * * + * Use this routine to convert a DirType into a 0 through 15 value. * + * * + * INPUT: facing -- The DirType to convert into a 0..15 value. * + * * + * OUTPUT: Returns with the facing converted into a value where 0 equals North, 4 equals * + * East, 8 equals South, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_16(DirType facing) +{ + return Facing16[facing]; +} + + +/*********************************************************************************************** + * Dir_To_32 -- Convert a DirType into a 0..31 value. * + * * + * This will convert the DirType specified, into a 0 through 31 value where zero is North, * + * and rotates clockwise. The return value is baised to take into consideration the * + * distortion caused by 3D studio upon the game vehicle objects. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the facing converted into a value from zero to 31. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline int Dir_To_32(DirType facing) +{ + return Facing32[facing]; +} + + +/*********************************************************************************************** + * Direction256 -- Calculate the facing value from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate that facing will be calcuated to. * + * * + * OUTPUT: Returns with the DirType that is the facing from coord1 to coord2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction256(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Determines the facing value from one coordinate to another. * + * * + * This will determine the DirType from the first coordinate to the second. * + * * + * INPUT: coord1 -- The first coordinate that facing will be calculated from. * + * * + * coord2 -- The second coordinate to calculate facing to. * + * * + * OUTPUT: Returns with the DirType that represents the facing from coordinate 1 to coordinate* + * 2. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing256(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction8 -- Fetches the direction from one coordinate to another. * + * * + * This will calculate the facing from the first coordinate to the second. The return value * + * is of limited accuracy, but the calculation is fast. * + * * + * INPUT: coord1 -- The coordinate to calculate the facing from. * + * * + * coord2 -- The coordinate to figure the facing to. * + * * + * OUTPUT: Returns with the DirType to get from coordinate 1 to coordinate 2. * + * * + * WARNINGS: The return DirType is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction8(COORDINATE coord1, COORDINATE coord2) +{ + return (Desired_Facing8(Coord_X(coord1), Coord_Y(coord1), Coord_X(coord2), Coord_Y(coord2))); +} + + +/*********************************************************************************************** + * Direction -- Calculates the DirType from one cell to another. * + * * + * This routine will calculate the facing to get from one cell to another. Since dealing * + * with cells is much less precise than with coordinates, the return value is only * + * accurate to 8 facings. * + * * + * INPUT: cell1 -- The cell to calculate the DirType from. * + * * + * cell2 -- The cell to calculate the DirType to. * + * * + * OUTPUT: Returns with the DirType to get from the first cell to the second. * + * * + * WARNINGS: The return value is only accurate to the 8 primary compass rose directions. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline DirType Direction(CELL cell1, CELL cell2) +{ + return (Desired_Facing8(Cell_X(cell1), Cell_Y(cell1), Cell_X(cell2), Cell_Y(cell2))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, FacingType dir) +{ + return (Coord_Snap(Coord_Add(AdjacentCoord[(int)dir & 0x07], coord))); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This will coerce the coordinate specified so that it will refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: coord -- The coordinate to calculate the adjacency from. * + * * + * dir -- The direction to travel to calculate the adjacent cell. * + * * + * OUTPUT: Returns with the coordinate the refers to the adjacent cell in the direciton * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline COORDINATE Adjacent_Cell(COORDINATE coord, DirType dir) +{ + return Adjacent_Cell(coord, Dir_Facing(dir)); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, FacingType dir) +{ + return (CELL)(cell + AdjacentCell[dir]); +} + + +/*********************************************************************************************** + * Adjacent_Cell -- Calculate the adjacent cell in the direction specified. * + * * + * This routine will take the specified cell and coerce it to refer to the immediately * + * adjacent cell in the direction specified. * + * * + * INPUT: cell -- The cell to coerce into an adjacent cell. * + * * + * dir -- The direction to determine the adjacent cell. * + * * + * OUTPUT: Returns with the cell value that represents the adjacent cell in the direction * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline CELL Adjacent_Cell(CELL cell, DirType dir) +{ + return (CELL)(cell + AdjacentCell[Dir_Facing(dir)]); +} + + +/*********************************************************************************************** + * Dir_To_8 -- Convert a DirType into a value from 0 to 7. * + * * + * This routine will convert a DirType value into a facing number from 0 to 7. * + * * + * INPUT: facing -- The DirType to convert. * + * * + * OUTPUT: Returns with the DirType converted to a number from 0 to 7 with 0 being North and * + * rotating clockwise. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline FacingType Dir_To_8(DirType facing) +{ + return (FacingType)(((unsigned char)((int)facing|0x10))>>5); +} + + +/*********************************************************************************************** + * Text_String -- Convert a text number into a text pointer. * + * * + * This routine will convert text numbers (as generated elsewhere) into an actual text * + * pointer that can be used for normal purposes. * + * * + * INPUT: string -- The text number to extract a pointer to. * + * * + * OUTPUT: Returns with a pointer to the text that represents the text number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline char const * Text_String(int string) +{ +#ifdef FIXIT_NAME_OVERRIDE + if (string < 0 && abs(string) < ARRAY_SIZE(NameOverride)) { + return(NameOverride[-(string+1)]); + } +#endif + + if (string < 1000) return(Extract_String(SystemStrings, string)); + return(Extract_String(DebugStrings, string-1000)); +} + + +/*********************************************************************************************** + * Random_Pick -- Pick a random number in a specified range. * + * * + * This routine is used to pick a game influencing random number between (inclusive) the * + * range specified. * + * * + * INPUT: a -- Low limit of range to pick from. * + * * + * b -- High limit of range to pick from. * + * * + * OUTPUT: Returns with a random number picked between (inclusive) the range of values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +template inline T Random_Pick(T a, T b) +{ + return T(Scen.RandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Percent_Chance -- Calculate a percentage chance event. * + * * + * This will calculate a percentage chance and return with 'true' as likely as the * + * chance value would occur (or less) on a random pick from 1 to 100. Thus a * + * Percent_Chance(50) would return 'true' 50 percent of the time. Percent_Chance(25) would * + * return 'true' 25% of the time, etc. * + * * + * INPUT: percent -- The percent value to calculate the chance upon. * + * * + * OUTPUT: Returns with 'true' in the same percentage as the percentage number supplied. * + * * + * WARNINGS: This affects the game syncronization random number generator and should be used * + * for those events that could affect the game engine. * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Percent_Chance(int percent) +{ + return (Scen.RandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Sim_Random_Pick -- Picks a random number that will not affect the game. * + * * + * Use this routine to pick a random number such that it will be used so that it won't * + * actually affect the outcome of the game. It is critical to use this routine for any * + * random need that won't be needed on other machines in a multiplayer game. The result * + * can be freely used as long as it doesn't affect the outcome of the game. * + * * + * INPUT: a -- Low range of the random number to pick. * + * * + * b -- High range of the random number to pick. * + * * + * OUTPUT: Returns with a random number between (inclusive) the range limit values * + * specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +extern RandomClass NonCriticalRandomNumber; +template inline T Sim_Random_Pick(T a, T b) +{ + return T(NonCriticalRandomNumber((int)a, (int)b)); +}; + + +/*********************************************************************************************** + * Sim_Percent_Chance -- Calculates a percentage chance event for local events. * + * * + * This routine is similar to the normal Percent_Chance() routine except that it doesn't * + * alter the main random number gerenator sequence. As such, this routine should be used * + * for those events that should have a random character, but will either not affect the * + * game engine or are only calculated on one machine in a multiplayer game. * + * * + * INPUT: percent -- The percent chance to calculate the possible return of 'true' on. * + * * + * OUTPUT: Returns 'true' with the same percentage chance as the percent number specified. * + * A percent value of 50 means 50%, 25 means 25%, etc. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +inline bool Sim_Percent_Chance(int percent) +{ + return (NonCriticalRandomNumber(0, 99) < percent); +} + + +/*********************************************************************************************** + * Distance -- Finds the distance between two arbitrary points. * + * * + * This finds the (Dragon Strike) distance between two arbitrary points in flat space. * + * It does this by adding 1/2 the smaller absolute axis difference to the other absolute * + * axis distance. The result is rough but quick to calculate. * + * * + * INPUT: x1,y1 -- Coordinate location for point 1. * + * * + * x2,y2 -- Coordinate location for point 2. * + * * + * OUTPUT: Returns with the rough distance between the two points. The value returned is * + * expressed in the same units as the parameters were specified in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 JLB : Created. * + *=============================================================================================*/ +inline int Distance(int x1, int y1, int x2, int y2) +{ + int diff1 = y1 - y2; + if (diff1 < 0) diff1 = -diff1; + int diff2 = x1 - x2; + if (diff2 < 0) diff2 = -diff2; + if (diff1 > diff2) { + return(diff1 + ((unsigned)diff2 / 2)); + } + return(diff2 + ((unsigned)diff1 / 2)); +} + + +#endif + diff --git a/code/redalert/int.cpp b/code/redalert/int.cpp new file mode 100644 index 0000000..9b8c0f9 --- /dev/null +++ b/code/redalert/int.cpp @@ -0,0 +1,43 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INT.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "mp.h" +#include "int.h" + +int bignum::Error = 0; +bool bignum::Carry = false; +bool bignum::Borrow = false; +bignum bignum::Remainder; + diff --git a/code/redalert/int.h b/code/redalert/int.h new file mode 100644 index 0000000..e59c56a --- /dev/null +++ b/code/redalert/int.h @@ -0,0 +1,301 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INT.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef INT_H +#define INT_H + +#include +#include +#include +#include "mp.h" +#include "straw.h" + +//#pragma warn -inl + +template +class Int { + public: + + /* + ** Constructors and initializers. + */ + Int(void) {XMP_Init(®[0], 0, PRECISION);} + Int(unsigned long value) {XMP_Init(®[0], value, PRECISION);} + + void Randomize(Straw & rng, int bitcount) {XMP_Randomize(®[0], rng, bitcount, PRECISION);} + void Randomize(Straw & rng, const Int & minval, const Int & maxval) {XMP_Randomize(®[0], rng, minval, maxval, PRECISION); reg[0] |= 1;} + + /* + ** Convenient conversion operators to get at the underlying array of + ** integers. Big number math is basically manipulation of arbitrary + ** length arrays. + */ + operator digit * () {return & reg[0];} + operator const digit * () const {return & reg[0];} + + /* + ** Array access operator (references bit position). Bit 0 is the first bit. + */ + bool operator[](unsigned bit) const {return(XMP_Test_Bit(®[0], bit));} + + /* + ** Unary operators. + */ + Int & operator ++ (void) {XMP_Inc(®[0], PRECISION);return(*this);} + Int & operator -- (void) {XMP_Dec(®[0], PRECISION);return(*this);} + int operator ! (void) const {return(XMP_Test_Eq_Int(®[0], 0, PRECISION));} + Int operator ~ (void) {XMP_Not(®[0], PRECISION);return(*this);} + Int operator - (void) const {Int a = *this;a.Negate();return (a);} + + /* + ** Attribute query functions. + */ + int ByteCount(void) const {return(XMP_Count_Bytes(®[0], PRECISION));} + int BitCount(void) const {return(XMP_Count_Bits(®[0], PRECISION));} + bool Is_Negative(void) const {return(XMP_Is_Negative(®[0], PRECISION));} + unsigned MaxBitPrecision() const {return PRECISION*(sizeof(unsigned long)*CHAR_BIT);} + bool IsSmallPrime(void) const {return(XMP_Is_Small_Prime(®[0], PRECISION));} + bool SmallDivisorsTest(void) const {return(XMP_Small_Divisors_Test(®[0], PRECISION));} + bool FermatTest(unsigned rounds) const {return(XMP_Fermat_Test(®[0], rounds, PRECISION));} + bool IsPrime(void) const {return(XMP_Is_Prime(®[0], PRECISION));} + bool RabinMillerTest(Straw & rng, unsigned int rounds) const {return(XMP_Rabin_Miller_Test(rng, ®[0], rounds, PRECISION));} + + /* + ** 'in-place' binary operators. + */ + Int & operator += (const Int & number) {Carry = XMP_Add(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator -= (const Int & number) {Borrow = XMP_Sub(®[0], ®[0], number, 0, PRECISION);return(*this);} + Int & operator *= (const Int & multiplier) {Remainder = *this;Error=XMP_Signed_Mult(®[0], Remainder, multiplier, PRECISION);return(*this);} + Int & operator /= (const Int & t) {*this = (*this) / t;return *this;} + Int & operator %= (const Int & t) {*this = (*this) % t;return *this;} + Int & operator <<= (int bits) {XMP_Shift_Left_Bits(®[0], bits, PRECISION);return *this;} + Int & operator >>= (int bits) {XMP_Shift_Right_Bits(®[0], bits, PRECISION);return *this;} + + /* + ** Mathematical binary operators. + */ + Int operator + (const Int & number) const {Int term;Carry = XMP_Add(term, ®[0], number, 0, PRECISION);return(term);} + Int operator + (unsigned short b) const {Int result;Carry=XMP_Add_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator + (digit b, const Int & a) {return(Int(b) + a);} + Int operator - (const Int & number) const {Int term;Borrow = XMP_Sub(term, ®[0], number, 0, PRECISION);return(term);} + Int operator - (unsigned short b) const {Int result;Borrow = XMP_Sub_Int(result, ®[0], b, 0, PRECISION);return(result);} +// friend Int operator - (digit b, const Int & a) {return(Int(b) - a);} + Int operator * (const Int & multiplier) const {Int result;Error=XMP_Signed_Mult(result, ®[0], multiplier, PRECISION);return result;} + Int operator * (unsigned short b) const {Int result;Error=XMP_Unsigned_Mult_Int(result, ®[0], b, PRECISION);return(result);} +// friend Int operator * (digit b, const Int & a) {return(Int(b) * a);} + Int operator / (const Int & divisor) const {Int quotient = *this;XMP_Signed_Div(Remainder, quotient, ®[0], divisor, PRECISION);return (quotient);} + Int operator / (unsigned long b) const {return(*this / Int(b));} + Int operator / (unsigned short divisor) const {Int quotient;Error=XMP_Unsigned_Div_Int(quotient, ®[0], divisor, PRECISION);return(quotient);} +// friend Int operator / (digit a, const Int & b) {return(Int(a) / b);} + Int operator % (const Int & divisor) const {Int remainder;XMP_Signed_Div(remainder, Remainder, ®[0], divisor, PRECISION);return(remainder);} + Int operator % (unsigned long b) const {return(*this % Int(b));} + unsigned short operator % (unsigned short divisor) const {return(XMP_Unsigned_Div_Int(Remainder, ®[0], divisor, PRECISION));} +// friend Int operator % (digit a, const Int & b) {return(Int(a) % b);} + + /* + ** Bitwise binary operators. + */ + Int operator >> (int bits) const {Int result = *this; XMP_Shift_Right_Bits(result, bits, PRECISION);return result;} + Int operator << (int bits) const {Int result = *this; XMP_Shift_Left_Bits(result, bits, PRECISION);return result;} + + /* + ** Comparison binary operators. + */ + int operator == (const Int &b) const {return (memcmp(®[0], &b.reg[0], (MAX_BIT_PRECISION/CHAR_BIT))==0);} + int operator != (const Int& b) const {return !(*this == b);} + int operator > (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) > 0);} + int operator >= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) >= 0);} + int operator < (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) < 0);} + int operator <= (const Int & number) const {return(XMP_Compare(®[0], number, PRECISION) <= 0);} + + /* + ** Misc. mathematical and logical functions. + */ + void Negate(void) {XMP_Neg(®[0], PRECISION);} + Int Abs(void) {XMP_Abs(®[0], PRECISION);return(*this);} + Int times_b_mod_c(Int const & multiplier, Int const & modulus) const { + Int result; + Error=xmp_stage_modulus(modulus, PRECISION); + Error=XMP_Mod_Mult(result, ®[0], multiplier, PRECISION); + XMP_Mod_Mult_Clear(PRECISION); + return result; + } + + Int exp_b_mod_c(const Int & e, const Int & m) const { + Int result; + Error=xmp_exponent_mod(result, ®[0], e, m, PRECISION); + return result; + } + + + static Int Unsigned_Mult(Int const & multiplicand, Int const & multiplier) {Int product;Error=XMP_Unsigned_Mult(&product.reg[0], &multiplicand.reg[0], &multiplier.reg[0], PRECISION);return(product);} + static void Unsigned_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {Error=XMP_Unsigned_Div(remainder, quotient, dividend, divisor, PRECISION);} + static void Signed_Divide(Int & remainder, Int & quotient, const Int & dividend, const Int & divisor) {XMP_Signed_Div(remainder, quotient, dividend, divisor, PRECISION);} + Int Inverse(const Int & modulus) const {Int result;XMP_Inverse_A_Mod_B(result, ®[0], modulus, PRECISION);return(result);} + + static Int Decode_ASCII(char const * string) {Int result;XMP_Decode_ASCII(string, result, PRECISION);return(result);} + + // Number (sign independand) inserted into buffer. + int Encode(unsigned char *output) const {return(XMP_Encode(output, ®[0], PRECISION));} + int Encode(unsigned char * output, unsigned length) const {return(XMP_Encode(output, length, ®[0], PRECISION));} + void Signed_Decode(const unsigned char * from, int frombytes) {XMP_Signed_Decode(®[0], from, frombytes, PRECISION);} + void Unsigned_Decode(const unsigned char * from, int frombytes) {XMP_Unsigned_Decode(®[0], from, frombytes, PRECISION);} + + // encode Int using Distinguished Encoding Rules, returns size of output + int DEREncode(unsigned char * output) const {return(XMP_DER_Encode(®[0], output, PRECISION));} + void DERDecode(const unsigned char *input) {XMP_DER_Decode(®[0], input, PRECISION);} + + // Friend helper functions. + friend Int Generate_Prime(Straw & rng, int pbits, Int const * = 0); + friend Int Gcd(const Int & a, const Int & b); +// friend bool NextPrime(Int & p, const Int & max, bool blumInt=false); +// friend Int a_exp_b_mod_pq(const Int & a, const Int & ep, const Int & eq, const Int & p, const Int & q, const Int & u); + + static int Error; + + // Carry result from last addition. + static bool Carry; + + // Borrow result from last subtraction. + static bool Borrow; + + // Remainder value from the various division routines. + static Int Remainder; + + + private: + digit reg[PRECISION]; + + + struct RemainderTable + { + RemainderTable(const Int & p) : HasZeroEntry(false) + { + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] = p % primeTable[i]; + } + } + bool HasZero() const {return(HasZeroEntry);} + void Increment(unsigned short increment = 1) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += increment; + while (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + void Increment(const RemainderTable & rtQ) + { + HasZeroEntry = false; + for (unsigned int i = 0; i < ARRAY_SIZE(primeTable); i++) { + table[i] += rtQ.table[i]; + if (table[i] >= primeTable[i]) { + table[i] -= primeTable[i]; + } + HasZeroEntry = (HasZeroEntry || !table[i]); + } + } + + bool HasZeroEntry; + unsigned short table[ARRAY_SIZE(primeTable)]; + }; + +}; + + +template +T Gcd(const T & a, const T & n) +{ + T g[3]={n, a, 0UL}; + + unsigned int i = 1; + while (!!g[i%3]) { + g[(i+1)%3] = g[(i-1)%3] % g[i%3]; + i++; + } + return g[(i-1)%3]; +} + + + +//#pragma warning 604 9 +//#pragma warning 595 9 +template +T Generate_Prime(Straw & rng, int pbits, T const *) +{ + T minQ = (T(1UL) << (unsigned short)(pbits-(unsigned short)2)); + T maxQ = ((T(1UL) << (unsigned short)(pbits-(unsigned short)1)) - (unsigned short)1); + + T q; + T p; + + do { + q.Randomize(rng, minQ, maxQ); + p = (q*2) + (unsigned short)1; + + T::RemainderTable rtQ(q); + T::RemainderTable rtP(p); + + while (rtQ.HasZero() || rtP.HasZero() || !q.IsPrime() || !p.IsPrime()) { + q += 2; + p += 4; + if (q > maxQ) break; + + rtQ.Increment(2); + rtP.Increment(4); + } + } while (q > maxQ); + + return(p); +} + + + + + + +typedef Int bignum; +typedef Int BigInt; + + + +//BigInt Gcd(const BigInt & a, const BigInt & n); +//BigInt Generate_Prime(RandomNumberGenerator & rng, int pbits, BigInt const * dummy); + +#endif + diff --git a/code/redalert/internet.cpp b/code/redalert/internet.cpp new file mode 100644 index 0000000..b6b0128 --- /dev/null +++ b/code/redalert/internet.cpp @@ -0,0 +1,975 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/INTERNET.CPP 6 3/17/97 1:05a Steve_tall $ */ +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : INTERNET.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : August 5th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Miscellaneous junk related to H2H internet connection. * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * Check_From_WChat -- Interprets start game packet from WChat * + * Read_Game_Options -- Read the game setup options from the wchat packet * + * Is_User_WChat_Registered -- retrieve the users wchat entry from registry * + * Spawn_WChat -- spawns or switches focus to wchat * + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "ccdde.h" + + +extern bool SpawnedFromWChat; + +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(void); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +#endif + +/*************************************************************************** +** Internet specific globals +*/ +char PlanetWestwoodHandle[] = {"Handle"}; //Planet WW user name +char PlanetWestwoodPassword[] = {"Password"}; //Planet WW password +char PlanetWestwoodIPAddress[IP_ADDRESS_MAX] = {"206.154.108.87"}; //IP of server or other player +long PlanetWestwoodPortNumber = 1234; //Port number to send to +bool PlanetWestwoodIsHost = false; //Flag true if player has control of game options +unsigned long PlanetWestwoodGameID; //Game ID +unsigned long PlanetWestwoodStartTime; //Time that game was started +HWND WChatHWND = 0; //Handle to Wchat window. +bool GameStatisticsPacketSent; //Flag that game stats have been sent to wchat +bool ConnectionLost; //Flag that the connection to the other player was lost +int WChatMaxAhead; +int WChatSendRate; +bool SpawnedFromWChat; +int ShowCommand; + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +/*********************************************************************************************** + * Check_From_WChat -- This function reads in C&CSPAWN.INI and interprets it. * + * C&CSPAWN.INI is now sent to us by WCHAT via DDE * + * * + * * + * INPUT: Name of C&CSPAWN.INI file. If NULL then get file from DDE Server. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/9/96 1:44PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +void Check_From_WChat(char *wchat_name) +{ + + char default_string[] = {"Error"}; + char key_string[256]; + char *ini_file; + RawFileClass wchat_file; + + /* + ** Get a pointer to C&CSPAWN.INI either by reading it from disk or getting it from + ** the DDE server. + */ + if (wchat_name){ + ini_file = new char [8192]; + }else{ + ini_file = DDEServer.Get_MPlayer_Game_Info(); + +#ifdef NEVER + /* + ** Save it to disk as well so I can see it + */ + RawFileClass anotherfile ("FROMCHAT.TXT"); + anotherfile.Write(ini_file, DDEServer.Get_MPlayer_Game_Info_Length()); +#endif + + } + + if (wchat_name){ + wchat_file.Set_Name(wchat_name); + } + + if (!wchat_name || wchat_file.Is_Available()){ + + /* + ** Read the ini file from disk if we founf it there + */ + if (wchat_name){ + wchat_file.Read(ini_file, wchat_file.Size()); + } + + /* + ** Get the IP address + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Address", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + strcpy (PlanetWestwoodIPAddress, key_string); + + + + /* + ** Get the port number + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Port", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + PlanetWestwoodPortNumber = atol(key_string); + + + /* + ** Get host or client + */ + key_string[0] = 0; + + WWGetPrivateProfileString("Internet", + "Host", + default_string, + key_string, + sizeof(key_string), + ini_file); + + + if (!strcmp (key_string, default_string)) { + if (wchat_name) delete ini_file; + return; + } + + if (strchr (key_string, '1')){ + PlanetWestwoodIsHost = true; + }else{ + PlanetWestwoodIsHost = false; + } + + + Special.IsFromWChat = true; + } + + if (wchat_name) delete ini_file; + +} +#endif // !WOLAPI_INTEGRATION + + +/*************************************************************************** + * Read_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * name of C&CSPAWN.INI file. Null if data should be got from DDE server* * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: \ * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +#ifndef WOLAPI_INTEGRATION +int Read_Game_Options(char *name) +{ + char *buffer; + + char filename[256] = {"INVALID.123"}; + + if (name){ + strcpy (filename, name); + } + + /*------------------------------------------------------------------------ + Create filename and read the file. + ------------------------------------------------------------------------*/ + CCFileClass file (filename); + + if (name && !file.Is_Available()) { + return(0); + } else { + if (name){ + buffer = new char [8192]; // INI staging buffer pointer. + memset(buffer, '\0', 8192); + file.Read(buffer, 8192-1); + file.Close(); + }else{ + buffer = DDEServer.Get_MPlayer_Game_Info(); + } + } + + /*------------------------------------------------------------------------ + Get the player's name + ------------------------------------------------------------------------*/ + WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle), buffer); + strcpy(Session.GameName, Session.Handle); + Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); + Session.PrefColor = Session.ColorIdx; + int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); + Session.House = (HousesType) ((int)HOUSE_USSR + temp); + + Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); + Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); + Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); + Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); + Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); + BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); + Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); + Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); + Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + + PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + + Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription), + buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + + if (PlanetWestwoodIsHost){ + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed then force the CD + ** to be there. + ** + */ + if (Session.Options.ScenarioIndex == -1) { + //WWDebugString ("RA95I - Session.Options.ScenarioIndex == -1\n"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) { +#else + if ( Expansion_CS_Present() ) { +#endif + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index=Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + //WWDebugString ("RA95I - CounterStrike CD not in drive\n"); + + if (!Force_CD_Available (RequiredCD)){ + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } + } + //WWDebugString ("RA95I - Returned from Force_CD_Available()\n"); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + + /* + ** See if that scenario is available now. Its fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + //if (Session.Options.ScenarioIndex == -1) + // WWDebugString ("RA95I - Session.Options.ScenarioIndex is still -1\n"); + + } + } + + + Options.GameSpeed = 0; + + + //MPlayerLocalID = Build_MPlayerID (MPlayerColorIdx, MPlayerHouse); + + Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + + if (name) delete buffer; + return (1); + +} +#endif // !WOLAPI_INTEGRATION + + + + +/*********************************************************************************************** + * Get_Registry_Sub_Key -- search a registry key for a sub-key * + * * + * * + * * + * INPUT: handle of key to search * + * text to search for * + * true if old key should be closed when new key opened * + * * + * OUTPUT: handle to the key we found or 0 * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:11PM ST : Created * + *=============================================================================================*/ + +extern HKEY Get_Registry_Sub_Key (HKEY base_key, char *search_key, BOOL close); + + + +void Just_Path(char *path, char *destpath) +{ + char *terminator = NULL; //He'll be back. + + strcpy (destpath, path); + terminator = strrchr (destpath, '\\'); + if (terminator){ + *terminator = 0; + } +} + + + + + +/*********************************************************************************************** + * Is_User_WChat_Registered -- retrieve the users wchat entry from the registry * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if users wchat entry was found in the registry * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 1/12/96 2:13PM ST : Created * + *=============================================================================================*/ +bool Is_User_WChat_Registered(char *buffer, int buffer_len) +{ + // ST - 5/13/2019 + buffer; buffer_len; + return false; + +#if (0) + HKEY key; + char user_handle[256]; + DWORD user_handle_size = sizeof (user_handle); + char user_pword[256]; + DWORD user_pword_size = sizeof (user_pword); + + + /* + ** Check HKEY_CLASSES_ROOT first. Old versions of Wchat register there + */ + key = Get_Registry_Sub_Key (HKEY_CLASSES_ROOT, "Wchat", FALSE); + + if (key){ + key = Get_Registry_Sub_Key (key, "Nick1", TRUE); + if (key){ + + if (RegQueryValue(key, "Nick", user_handle, (long*)&user_handle_size) == ERROR_SUCCESS){ + + if (RegQueryValue(key, "Pass", user_pword, (long*)&user_pword_size) == ERROR_SUCCESS){ + + /* + ** If the first char of the users name is non-numberic and there is a password + ** then return success + */ + if ((user_handle[0] < '0' || user_handle[0] > '9') && user_pword[0]){ + RegCloseKey( key ); + return (TRUE); + } + } + } + } + + RegCloseKey ( key ); + } + + + + /* + ** Check HKEY_LOCAL_MACKINE/Software + */ + user_handle_size = sizeof (user_handle); + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "UserName", NULL, NULL, (unsigned char*)user_handle, &user_handle_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + memcpy (buffer, user_handle, min((unsigned)buffer_len, user_handle_size)); + + /* + ** If the first char of the users name is non-numeric then return success + */ + if (user_handle[0] < '0' || user_handle[0] > '9'){ + return (TRUE); + }else{ + return (FALSE); + } +#endif +} + + + +/*********************************************************************************************** + * Spawn_WChat -- spawns or switches focus to wchat * + * * + * * + * * + * INPUT: can launch. If set then we are allowed to launch WChat if not already running * + * * + * OUTPUT: True if wchat was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Poke_WChat(void); +bool Spawn_WChat(bool can_launch) +{ + // ST - 5/13/2019 + can_launch; + return false; + +#if (0) + WWDebugString ("RA95 - In Spawn_WChat.\n"); + char packet[10] = {"Hello"}; + HWND chat_window = NULL; + + /* + ** See if WChat is already running... + */ + if (WChatHWND && IsWindow (WChatHWND) ){ + chat_window = WChatHWND; + }else{ + chat_window = FindWindow ( "OWL_Window", "Westwood Chat" ); + } + + if (chat_window){ + /* + ** WChat is already running. Minimize myself then try to give it focus. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + + /* + ** Send chat a tickle message so it knows to send the game stats to the server. + */ + if (GameStatisticsPacketSent && !PlanetWestwoodIsHost) { + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + } + + //Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_TICKLE); + /* + ** Give the focus to WChat + */ + SetForegroundWindow ( chat_window ); + ShowWindow ( chat_window, SW_RESTORE ); + return(true); + } + + + /* + ** Fail if we aren't allowed to launch wchat and we couldnt find its window. + */ + if (!can_launch) return (false); + + + /* + ** Find where WChat was installed to + */ + HKEY key; + char wchat_loc[256]; + DWORD wchat_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "WChat", TRUE); + if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "UserName", TRUE); + //if (!key) return (FALSE); + + //key = Get_Registry_Sub_Key (key, "Nick", TRUE); + //if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)wchat_loc, &wchat_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + char justpath [256]; + Just_Path(wchat_loc, justpath); + + /* + ** We found WChat in the registry. Minimize myself then try to spawn it. + */ + BlackPalette.Set(); + VisiblePage.Clear(); + ShowWindow (MainWindow, SW_MINIMIZE); + /* + ** Give windoze a couple of secs to sort itself out. + */ + CountDownTimerClass wibble_timer; + wibble_timer.Set ( 60 * 3, true); + + while (wibble_timer.Time()){ + /* + ** Call our message loop to make sure we get all the messages that are sent to us + ** when we minimise. + */ + Keyboard->Check(); + } + bool success = CreateProcess (wchat_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + + if (success){ + return (true); + }else{ + ShowWindow (MainWindow, SW_RESTORE); + while ( Keyboard->Check() ) {}; + return (false); + } +#endif +} + +#endif //#ifndef WOLAPI_INTEGRATION + +/*********************************************************************************************** + * Spawn_Registration_App -- spawns the C&C/Planet westwood registration app * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: True if app was spawned * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/8/96 12:33PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Spawn_Registration_App(void) +{ + // ST - 5/13/2019 + return false; +#if (0) + /* + ** Find where inetreg was installed to + */ + + HKEY key; + char inetreg_loc[256]; + DWORD inetreg_loc_size = 256; + + key = Get_Registry_Sub_Key (HKEY_LOCAL_MACHINE, "SOFTWARE", FALSE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "Westwood", TRUE); + if (!key) return (FALSE); + + key = Get_Registry_Sub_Key (key, "InetReg", TRUE); + if (!key) return (FALSE); + + if (RegQueryValueEx(key, "InstallPath", NULL, NULL, (unsigned char*)inetreg_loc, &inetreg_loc_size) != ERROR_SUCCESS){ + RegCloseKey(key); + return (FALSE); + } + + RegCloseKey(key); + + PROCESS_INFORMATION process_info; + STARTUPINFO start_info; + char justpath [256]; + memset ((void*)&start_info, 0, sizeof(start_info)); + start_info.cb = sizeof(start_info); + Just_Path(inetreg_loc, justpath); + + BOOL success = CreateProcess (inetreg_loc, NULL, NULL, NULL, false, 0, NULL, justpath, &start_info, &process_info); + if (success){ + //WaitForSingleObject (process_info.hProcess, 1000*10000); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_RESTORE ); + } + return (success); +#endif +} + +#endif //#ifndef WOLAPI_INTEGRATION + + +/*********************************************************************************************** + * Do_The_Internet_Menu_Thang -- Handle case where user clicks on 'Internet' button * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 8:30PM ST : Created * + *=============================================================================================*/ +#ifndef WOLAPI_INTEGRATION +bool Do_The_Internet_Menu_Thang(void) +{ +#if (0) //PG + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + char packet[10] = {"Hello"}; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 120 *factor; // dialog width + int d_dialog_h = 80*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int width; + int height; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //buttons = &cancelbtn; + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + char users_name[256]; + int buffer_len = sizeof (users_name); + bool process; + bool display; + KeyNumType input; + + + if (!Special.IsFromWChat && !SpawnedFromWChat){ + /* + ** If the user is registered with Planet Westwood then spawn WChat. + */ + if (Is_User_WChat_Registered(users_name, buffer_len)){ + GameStatisticsPacketSent = false; + if (!Spawn_WChat(true)){ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + WWMessageBox().Process(TXT_ERROR_UNABLE_TO_RUN_WCHAT, TXT_OK); + LogicPage->Clear(); + return(false); + } + }else{ + /* + ** OK, whatever, just run WChat anyway. + */ + if (!Spawn_WChat (true)){ + + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + Set_Palette(CCPalette); + + //WWMessageBox().Process(TXT_EXPLAIN_REGISTRATION, TXT_CANCEL); + WWMessageBox().Process(TXT_NO_REG_APP, TXT_CANCEL); + } + Load_Title_Page(true); + return(false); + } + } + + /* + ** + ** User is registered and we spawned WChat. Wait for a game start message from WChat. + ** + */ + + process = true; + display = true; + + while (process){ + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored = FALSE; + display = true; + } + + if (display) { + Set_Logic_Page(SeenBuff); + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + //cancelbtn.Zap(); + //buttons = &cancelbtn; + + /* + .................... Rebuild the button list .................... + */ + //buttons->Draw_All(); + cancelbtn.Draw_Me(true); + + Show_Mouse(); + display = false; + } + + + /* + ** See if the game start packet has arrived from wchat yet. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + //MessageBox (NULL, "About to restore focus to C&C95", "C&C95", MB_OK); + //SetForegroundWindow ( MainWindow ); + //ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + return(true); + } + + //input = buttons->Input(); + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + } + + } +#endif + return (false); +} + +#endif //#ifndef WOLAPI_INTEGRATION + + + +#endif //WIN32 diff --git a/code/redalert/internet.h b/code/redalert/internet.h new file mode 100644 index 0000000..458f8a7 --- /dev/null +++ b/code/redalert/internet.h @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/Internet.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 7 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTERNET_H +#define INTERNET_H + +#define IP_ADDRESS_MAX 40 + + +void Register_Game_Start_Time(void); +void Register_Game_End_Time(void); +void Send_Statistics_Packet(void); +void Check_From_WChat(char *wchat_name); +bool Do_The_Internet_Menu_Thang (void); +bool Server_Remote_Connect(void); +bool Client_Remote_Connect(void); +int Read_Game_Options(char *name); + +extern char PlanetWestwoodIPAddress[IP_ADDRESS_MAX]; +extern long PlanetWestwoodPortNumber; +extern bool PlanetWestwoodIsHost; + +#endif diff --git a/code/redalert/interpal.cpp b/code/redalert/interpal.cpp new file mode 100644 index 0000000..226aa4a --- /dev/null +++ b/code/redalert/interpal.cpp @@ -0,0 +1,465 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTERPAL.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTERPAL.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : December 7th 1995 * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This module contains functions to allow use of old 320x200 animations on a 640x400 screen * + * * + * Functions: * + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * Write_Interpolation_Palette -- writes an interpolation palette to disk * + * Create_Palette_Interpolation_Table -- build the palette interpolation table * + * Increase_Palette_Luminance -- increase the contrast of a palette * + * Interpolate_2X_Scale -- Stretch a 320x200 graphic buffer into 640x400 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +BOOL InterpolationPaletteChanged = FALSE; +extern "C" { +extern void __cdecl Asm_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Double (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +extern void __cdecl Asm_Interpolate_Line_Interpolate (unsigned char* src_ptr , + unsigned char* dest_ptr , + int lines , + int src_width , + int dest_width); + +} + +#define SIZE_OF_PALETTE 256 +extern "C"{ + unsigned char PaletteInterpolationTable[SIZE_OF_PALETTE][SIZE_OF_PALETTE]; + unsigned char * InterpolationPalette; +} + + + +/*********************************************************************************************** + * Read_Interpolation_Palette -- reads an interpolation palette table from disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Read_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (palette_file.Is_Available()) { + palette_file.Open(READ); + palette_file.Read(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + InterpolationPaletteChanged = FALSE; + } +} + + +/*********************************************************************************************** + * Write_Interpolation_Palette -- writes an interpolation palette table to disk * + * * + * * + * * + * INPUT: name of palette file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:15PM ST : Created * + *=============================================================================================*/ + +void Write_Interpolation_Palette (char const * palette_file_name) +{ + CCFileClass palette_file(palette_file_name); + + if (!palette_file.Is_Available()) { + palette_file.Open(WRITE); + palette_file.Write(&PaletteInterpolationTable[0][0], 256*256); + palette_file.Close(); + } +} + + + + + +/*************************************************************************** + * CREATE_PALETTE_INTERPOLATION_TABLE * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/06/1995 MG : Created. * + *=========================================================================*/ +void Create_Palette_Interpolation_Table( void ) +{ + +// Asm_Create_Palette_Interpolation_Table(); + + #if (1) + + int i; + int j; + int p; + unsigned char * first_palette_ptr; + unsigned char * second_palette_ptr; + unsigned char * match_pal_ptr; + int first_r; + int first_g; + int first_b; + int second_r; + int second_g; + int second_b; + int diff_r; + int diff_g; + int diff_b; + int dest_r; + int dest_g; + int dest_b; + int distance; + int closest_distance; + int index_of_closest_color; + + // + // Create an interpolation table for the current palette. + // + first_palette_ptr = (unsigned char *) InterpolationPalette; + for ( i = 0; i < SIZE_OF_PALETTE; i ++ ) { + + // + // Get the first palette entry's RGB. + // + first_r = *first_palette_ptr; + first_palette_ptr ++; + first_g = *first_palette_ptr; + first_palette_ptr ++; + first_b = *first_palette_ptr; + first_palette_ptr ++; + + second_palette_ptr = (unsigned char *) InterpolationPalette; + for ( j = 0; j < SIZE_OF_PALETTE; j ++ ) { + // + // Get the second palette entry's RGB. + // + second_r = *second_palette_ptr; + second_palette_ptr ++; + second_g = *second_palette_ptr; + second_palette_ptr ++; + second_b = *second_palette_ptr; + second_palette_ptr ++; + + // + // Now calculate the RGB halfway between the first and second colors. + // + dest_r = ( first_r + second_r ) >> 1; + dest_g = ( first_g + second_g ) >> 1; + dest_b = ( first_b + second_b ) >> 1; + + // + // Now find the color in the palette that most closely matches the interpolated color. + // + index_of_closest_color = 0; +// closest_distance = (256 * 256) * 3; + closest_distance = 500000; + match_pal_ptr = (unsigned char *) InterpolationPalette; + for ( p = 0; p < SIZE_OF_PALETTE; p ++ ) { + diff_r = ( ((int) (*match_pal_ptr)) - dest_r ); + match_pal_ptr ++; + diff_g = ( ((int) (*match_pal_ptr)) - dest_g ); + match_pal_ptr ++; + diff_b = ( ((int) (*match_pal_ptr)) - dest_b ); + match_pal_ptr ++; + + distance = ( diff_r * diff_r ) + ( diff_g * diff_g ) + ( diff_b * diff_b ); + if ( distance < closest_distance ) { + closest_distance = distance; + index_of_closest_color = p; + } + } + + PaletteInterpolationTable[ i ][ j ] = (unsigned char) index_of_closest_color; + } + } + + #endif + InterpolationPaletteChanged = FALSE; + return; + +} + + + + + + + + + +/*********************************************************************************************** + * Increase_Palette_Luminance -- increase contrast of colours in a palette * + * * + * * + * * + * INPUT: ptr to palette * + * percentage increase of red * + * percentage increase of green * + * percentage increase of blue * + * cap value for colours * + * * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/12/95 12:16PM ST : Created * + *=============================================================================================*/ + +void Increase_Palette_Luminance (unsigned char * palette , int red_percentage , int green_percentage , int blue_percentage , int cap) +{ + + unsigned int red; + unsigned int green; + unsigned int blue; + for (int i=0 ; iGet_IsDirectDraw()) { + if (!source->Lock()) { + if (dest == &SeenBuff) Show_Mouse(); + return; + } + source_locked = TRUE; + } + if (dest->Get_IsDirectDraw()) { + if (!dest->Lock()) { + if (source_locked) { + source->Unlock(); + } + if (dest == &SeenBuff) Show_Mouse(); + return; + } + dest_locked = TRUE; + } + + + // + // Get pointers to the source and destination buffers. + // + src_ptr = (unsigned char *) source->Get_Offset(); + dest_ptr = (unsigned char *) dest->Get_Offset(); + end_of_source = src_ptr + ( source->Get_Width() * source->Get_Height() ); + + // + // Get width of source and dest buffers. + // + src_width = source->Get_Width(); + dest_width = 2*(dest->Get_Width() + dest->Get_XAdd() + dest->Get_Pitch()); + last_dest_ptr = dest_ptr; + + /* + ** Call the appropriate assembly language copy routine + */ +#if (1) + switch (CopyType) { + case 0: + Asm_Interpolate ( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 1: + Asm_Interpolate_Line_Double( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + + case 2: + Asm_Interpolate_Line_Interpolate( src_ptr , dest_ptr , source->Get_Height() , src_width , dest_width); + break; + } +#endif + +#if (0) + // + // Copy over the first pixel (upper left). + // + *dest_ptr = *src_ptr; + src_ptr ++; + dest_ptr ++; + + // + // Scale copy. + // + width_counter = 0; + while ( src_ptr < end_of_source ) { + + // + // Blend this pixel with the one to the left and place this new color in the dest buffer. + // + *dest_ptr = PaletteInterpolationTable[ (*src_ptr) ][ (*( src_ptr - 1 )) ]; + dest_ptr ++; + + // + // Now place the source pixel into the dest buffer. + // + *dest_ptr = *src_ptr; + + src_ptr ++; + dest_ptr ++; + + width_counter ++; + if ( width_counter == src_width ) { + width_counter = 0; + last_dest_ptr += dest_width; + dest_ptr = last_dest_ptr; + } + } + +#endif + if (source_locked) source->Unlock(); + if (dest_locked) dest->Unlock(); + if (dest == &SeenBuff) Show_Mouse(); + +//BG long *longptr = (long *)&PaletteInterpolationTable[0][0]; +//BG Mono_Printf("Clock cycles: %08x\n",*longptr); +#endif +} +#endif + + + + + diff --git a/code/redalert/intro.cpp b/code/redalert/intro.cpp new file mode 100644 index 0000000..14435ed --- /dev/null +++ b/code/redalert/intro.cpp @@ -0,0 +1,70 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTRO.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "VQAMOVIE.H" + + + +_VQAHandle * Open_Movie(char * name) +{ + _VQAHandle * vqa = VQA_Alloc(); + if (vqa) { + //VQA_Init(vqa, MixFileHandler); + + if (VQA_Open(vqa, name) != 0) { + VQA_Free(vqa); + vqa = 0; + } + } + return(vqa); +} + +/*********************************************************************************************** + * Choose_Side -- play the introduction movies, select house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 5/08/1995 BWG : Created. * + *=============================================================================================*/ +void Choose_Side(void) // ajw - In RA, all this did was play a movie. Denzil is using it in its original sense. +{ + Whom = HOUSE_GOOD; + Play_Movie(VQ_INTRO_MOVIE, THEME_NONE, false); +} diff --git a/code/redalert/intro.h b/code/redalert/intro.h new file mode 100644 index 0000000..7ebde0d --- /dev/null +++ b/code/redalert/intro.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/INTRO.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : INTRO.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : May 8, 1995 * + * * + * Last Update : May 8, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef INTRO_H +#define INTRO_H + +void Choose_Side(void); + +#endif diff --git a/code/redalert/io/eventqueue.cpp b/code/redalert/io/eventqueue.cpp new file mode 100644 index 0000000..eef7a05 --- /dev/null +++ b/code/redalert/io/eventqueue.cpp @@ -0,0 +1,157 @@ +#include "../FUNCTION.H" +#include "EventQueue.h" + +/* + Clears the event queue and frees memory +*/ +void EventQueueClass::Clear() +{ + while (this->eventQueue.size() > 0) { + this->eventQueue.pop(); + } +} + +/* + Inserts a new event message event into the FIFO buffer. +*/ +void EventQueueClass::Put(EventMessage input) +{ + if ( Is_Buffer_Full() ) return; + this->eventQueue.push( input ); +} + +/* + Check if the buffer contains an element +*/ +bool EventQueueClass::Check() const +{ + return !this->Is_Buffer_Empty(); +} + +/* + Get the first (unprocessed) element in the event queue +*/ +EventMessage EventQueueClass::Get() +{ + // wait for buffer + while( !this->Check() ) { } + + EventMessage returnVal = this->eventQueue.front(); + this->eventQueue.pop(); + + return returnVal; +} + +/* + Returns ASCII value of the key in the buffer +*/ +char EventQueueClass::Get_ASCII() +{ + // get the last key from the buffer + EventMessage eventMessage = this->Get(); + return eventMessage.Keyboard.ASCII; +} + +EventMessage::EventMessage(KeyNumType key, char ascii, bool shift, bool alt, bool control, bool capsLock) { + this->Clear(); + + this->Valid = true; + this->EventType = EventMessageType::EVENT_KEYBOARD; + + this->Keyboard.Key = key; + this->Keyboard.Shift = shift; + this->Keyboard.Alt = alt; + this->Keyboard.Control = control; + this->Keyboard.CapsLock = capsLock; + this->Keyboard.ASCII = ascii; +} + +EventMessage::EventMessage(int x, int y, unsigned short button, MouseButtonState buttonState) +{ + this->Clear(); + + this->Valid = true; + this->EventType = EventMessageType::EVENT_MOUSE; + + this->Mouse.x = x; + this->Mouse.y = y; + this->Mouse.Button = button; + this->Mouse.ButtonState = buttonState; + + this->Flags = 0; + + if (buttonState == MouseButtonState::MOUSE_BUTTON_UP) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTUP : GadgetClass::RIGHTUP; + } + + if (buttonState == MouseButtonState::MOUSE_BUTTON_DOWN) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTPRESS : GadgetClass::RIGHTPRESS; + } + + if (buttonState == MouseButtonState::MOUSE_BUTTON_HOLD) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTHELD : GadgetClass::RIGHTHELD; + } + + if (buttonState == MouseButtonState::MOUSE_BUTTON_RELEASE) { + this->Flags = (button == KN_LMOUSE) ? GadgetClass::LEFTRELEASE : GadgetClass::RIGHTRELEASE; + } +} + + +void EventMessage::Key_Flags(KeyNumType& key, int& flags) +{ + if (this->EventType == EventMessageType::EVENT_KEYBOARD) + { + flags = this->Flags; + flags |= GadgetClass::KEYBOARD; + + unsigned short outputKey = this->Keyboard.Key; + + if (this->Keyboard.Shift) outputKey |= KN_SHIFT_BIT; + if (this->Keyboard.Alt) outputKey |= KN_ALT_BIT; + if (this->Keyboard.Control) outputKey |= KN_CTRL_BIT; + + key = (KeyNumType)outputKey; + } + + if (this->EventType == EventMessageType::EVENT_MOUSE) + { + key = (KeyNumType)this->Mouse.Button; + + if (key == KN_LMOUSE) { + switch (this->Mouse.ButtonState) + { + case MouseButtonState::MOUSE_BUTTON_UP: + flags = GadgetClass::LEFTUP; + break; + case MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::LEFTPRESS; + break; + case MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::LEFTRELEASE; + break; + case MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::LEFTHELD; + break; + } + } + + if (key == KN_RMOUSE) { + switch (this->Mouse.ButtonState) + { + case MouseButtonState::MOUSE_BUTTON_UP: + flags = GadgetClass::LEFTUP; + break; + case MouseButtonState::MOUSE_BUTTON_DOWN: + flags = GadgetClass::RIGHTPRESS; + break; + case MouseButtonState::MOUSE_BUTTON_RELEASE: + flags = GadgetClass::RIGHTRELEASE; + break; + case MouseButtonState::MOUSE_BUTTON_HOLD: + flags = GadgetClass::RIGHTHELD; + break; + } + } + } +} \ No newline at end of file diff --git a/code/redalert/io/eventqueue.h b/code/redalert/io/eventqueue.h new file mode 100644 index 0000000..be92699 --- /dev/null +++ b/code/redalert/io/eventqueue.h @@ -0,0 +1,121 @@ +#pragma once +#include + +/* +* WWKeyboardClass::Buff_Get -- Lowlevel function to get a key from key buffer* +* WWKeyboardClass::Check -- Checks to see if a key is in the buffer* +* WWKeyboardClass::Down -- Checks to see if the specified key is being held down.* +* WWKeyboardClass::Fetch_Element -- Extract the next element in the keyboard buffer.* +* WWKeyboardClass::Get -- Logic to get a metakey from the buffer* +* WWKeyboardClass::Message_Handler -- Process a windows message as it relates to the keyboar* +* WWKeyboardClass::Peek_Element -- Fetches the next element in the keyboard buffer.* +* WWKeyboardClass::To_ASCII -- Convert the key value into an ASCII representation.* +* WWKeyboardClass::Available_Buffer_Room -- Fetch the quantity of free elements in the keybo* +* ----------------------------------------------*/ + +/** +* EVENT QUEUE class +* Supports pumping of event messages using a FIFO buffer. +*/ + +extern UserInputClass UserInput; + +/* + Event type definitions +*/ +typedef enum EventMessageType { + EVENT_NONE, + EVENT_KEYBOARD, + EVENT_MOUSE +}; + +/* + Event types +*/ +class EventMessage { + public: + EventMessageType EventType; + bool Valid; + + EventMessage() { + this->EventType == EVENT_NONE; + + this->Keyboard.Key = KN_NONE; + this->Keyboard.Shift = false; + this->Keyboard.Alt = false; + this->Keyboard.Control = false; + this->Keyboard.CapsLock = false; + + this->Mouse.x = -1; + this->Mouse.y = -1; + this->Mouse.Button = 0; + this->Mouse.ButtonState = MouseButtonState::MOUSE_BUTTON_UP; + + this->Flags = 0; + + this->Valid = false; + } + + // for keyboard + struct { + KeyNumType Key; + char ASCII; + bool Shift; + bool Alt; + bool Control; + bool CapsLock; + } Keyboard; + + // for mouse + struct { + int x; + int y; + unsigned short Button; + MouseButtonState ButtonState; + } Mouse; + + // for cascading functions + unsigned short Flags; + + EventMessage( KeyNumType key, char asciis, bool shift, bool alt, bool control, bool capsLock ); + EventMessage( int x, int y, unsigned short button = KN_LMOUSE, MouseButtonState buttonState = MouseButtonState::MOUSE_BUTTON_DOWN ); + + void Key_Flags(KeyNumType& key, int& flags); + private: + void Clear() { + this->Keyboard.Key = KN_NONE; + this->Keyboard.Shift = false; + this->Keyboard.Alt = false; + this->Keyboard.Control = false; + this->Keyboard.CapsLock = false; + + this->Mouse.x = -1; + this->Mouse.y = -1; + this->Mouse.Button = 0; + this->Mouse.ButtonState = MouseButtonState::MOUSE_BUTTON_UP; + + this->Flags = 0; + + this->Valid = false; + } +}; + +class EventQueueClass { + public: + int Get_Mouse_X() const { return UserInput.Mouse.X; } + int Get_Mouse_Y() const { return UserInput.Mouse.Y; } + + void Clear(); + void Put(EventMessage input); + bool Check() const; + EventMessage Get(); + char EventQueueClass::Get_ASCII(); + + private: + const int MaxCapacity = 100; + + std::queue< EventMessage > eventQueue; + + bool Is_Buffer_Full() const { return this->eventQueue.size() == MaxCapacity; } + bool Is_Buffer_Empty() const { return this->eventQueue.size() == 0; } +}; diff --git a/code/redalert/iomap.cpp b/code/redalert/iomap.cpp new file mode 100644 index 0000000..c46ff5d --- /dev/null +++ b/code/redalert/iomap.cpp @@ -0,0 +1,517 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IOMAP.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOMAP.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : March 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * All map-related loading/saving routines should go in this module, so it can be overlayed. * + *---------------------------------------------------------------------------------------------* + * Functions: * + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * CellClass::Load -- Reads from a save game file. * + * CellClass::Save -- Write to a save game file. * + * CellClass::Should_Save -- Should the cell be written to disk? * + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * MouseClass::Load -- Loads from a save game file. * + * MouseClass::Save -- Saves to a save game file. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * CellClass::Should_Save -- Should the cell be written to disk? * + * * + * This function will determine if the cell needs to be written to disk. Any cell that * + * contains special data should be written to disk. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Should this cell's data be written to disk? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Should_Save(void) const +{ + static CellClass const _identity_cell; + + return(memcmp(&_identity_cell, this, sizeof(*this)) != 0); +} + + +/*********************************************************************************************** + * CellClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Load(Straw & file) +{ + file.Get(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool CellClass::Save(Pipe & file) const +{ + file.Put(this, sizeof(*this)); + return(true); +} + + +/*********************************************************************************************** + * CellClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CellClass::Code_Pointers(void) +{ + if (Cell_Occupier() != NULL) { + OccupierPtr = (ObjectClass *)OccupierPtr->As_Target(); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL && Overlapper[index]->IsActive) { + Overlapper[index] = (ObjectClass *)Overlapper[index]->As_Target(); + } else { + Overlapper[index] = NULL; + } + } +} + + +/*********************************************************************************************** + * CellClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void CellClass::Decode_Pointers(void) +{ + if (OccupierPtr != NULL) { + OccupierPtr = As_Object((TARGET)OccupierPtr, false); + assert(OccupierPtr != NULL); + } + + for (int index = 0; index < ARRAY_SIZE(Overlapper); index++) { + if (Overlapper[index] != NULL) { + Overlapper[index] = As_Object((TARGET)Overlapper[index], false); + assert(Overlapper[index] != NULL); + } + } +} + + +/*********************************************************************************************** + * MouseClass::Load -- Loads from a save game file. * + * * + * Loading the map is very complicated. Here are the steps: * + * - Read the Theater for this save-game * + * - call Init_Theater to perform theater-specific inits * + * - call Free_Cells to free the cell array, because loading the map object will overwrite * + * the pointer to the cell array * + * - read the map object from disk * + * - call Alloc_Cells to re-allocate the cell array * + * - call Init_Cells to set the cells to a known state, because not every cell will be loaded * + * - read the cell objects into the cell array * + * - After the map & all objects have been loaded & the pointers decoded, Init_IO() >MUST< be * + * called to restore the map's button list to the proper state. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool MouseClass::Load(Straw & file) +{ + /* + ** Load Theater: Even though this value is located in the DisplayClass, + ** it must be loaded first so initialization can be done before any other + ** map data is loaded. If initialization isn't done first, data read from + ** disk will be over-written when initialization occurs. This code must + ** go in the most-derived Map class. + */ + TheaterType theater; + if (file.Get(&theater, sizeof(theater)) != sizeof(theater)) { + return(false); + } + +#ifdef WIN32 +LastTheater = THEATER_NONE; +#endif + + /* + ** Remove any old theater specific uncompressed shapes + */ +#ifdef WIN32 +// if (theater != LastTheater) { + Reset_Theater_Shapes(); +// } +#endif //WIN32 + + /* + ** Init display mixfiles + */ + Init_Theater(theater); + TerrainTypeClass::Init(Scen.Theater); + TemplateTypeClass::Init(Scen.Theater); + OverlayTypeClass::Init(Scen.Theater); + UnitTypeClass::Init(Scen.Theater); + InfantryTypeClass::Init(Scen.Theater); + BuildingTypeClass::Init(Scen.Theater); + BulletTypeClass::Init(Scen.Theater); + AnimTypeClass::Init(Scen.Theater); + AircraftTypeClass::Init(Scen.Theater); + VesselTypeClass::Init(Scen.Theater); + SmudgeTypeClass::Init(Scen.Theater); + + //LastTheater = Scen.Theater; + + /* + ** Free the cell array, because we're about to overwrite its pointers + */ + Free_Cells(); + + /* + ** Read the entire map object in. Only read in sizeof(MouseClass), so if we're + ** in editor mode, none of the map editor object is read in. + */ + file.Get(this, sizeof(*this)); +#ifdef SCENARIO_EDITOR + new(this) MapEditClass(NoInitClass()); +#else + new(this) MouseClass(NoInitClass()); +#endif + + /* + ** Reallocate the cell array + */ + Alloc_Cells(); + + /* + ** Init all cells to empty + */ + Init_Cells(); + + /* + ** Read # cells saved + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Read cells + */ + for (int index = 0; index < count; index++) { + CELL cell = 0; + if (file.Get(&cell, sizeof(cell)) != sizeof(cell)) { + return(false); + } + + if (!(*this)[cell].Load(file)) { + return(false); + } + } + + LastTheater = Scen.Theater; + return(true); +} + + +/*********************************************************************************************** + * MouseClass::Save -- Save to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/26/1996 JLB : Cleaned up. * + *=============================================================================================*/ +bool MouseClass::Save(Pipe & file) const +{ + /* + ** Save Theater >first< + */ + TheaterType theater = Scen.Theater; + file.Put(&theater, sizeof(theater)); + + file.Put(this, sizeof(*this)); + + /* + ** Count how many cells will be saved. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + count++; + } + cellptr++; + } + + /* + ** write out count of the cells. + */ + file.Put(&count, sizeof(count)); + + /* + ** Save cells that need it + */ + cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (cellptr->Should_Save()) { + file.Put(&cell, sizeof(cell)); + cellptr->Save(file); + count--; + } + cellptr++; + } + + if (count != 0) return(false); + + return(true); +} + + +/*********************************************************************************************** + * DisplayClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Code_Pointers(void) +{ + /* + ** Code PendingObjectPtr. + */ + if (PendingObjectPtr) { + PendingObjectPtr = (ObjectClass *)PendingObjectPtr->As_Target(); + } + + /* + ** Fix for saving game while in structure placement mode. ST - 4/15/2020 2:41PM + */ + memset(CursorShapeSave, 0, sizeof(CursorShapeSave)); + if (CursorSize && CursorSize != CursorShapeSave) { + + int save_buffer_element_size = sizeof(CursorShapeSave) / sizeof(CursorShapeSave[0]); + + int index = 0; + + while (index < save_buffer_element_size - 2 && CursorSize[index] != REFRESH_EOL) { + CursorShapeSave[index] = CursorSize[index]; + index++; + } + CursorShapeSave[index] = REFRESH_EOL; + } + + /* + ** Chain to parent. + */ + MapClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * DisplayClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void DisplayClass::Decode_Pointers(void) +{ + /* + ** Decode PendingObjectPtr. We can't decode PendingObject here, because we'd + ** have to reference PendingObjectPtr->Class_Of(), and the object that + ** PendingObjectPtr is pointing to hasn't been decoded yet. Since we can't + ** decode PendingObjectPtr, we can't set the placement cursor shape here + ** either. These have to be done as last-minute fixups. + */ + if (PendingObjectPtr) { + PendingObjectPtr = As_Object((TARGET)PendingObjectPtr, false); + assert(PendingObjectPtr != NULL); + } + + if (CursorSize) { + CursorSize = CursorShapeSave; + } + + /* + ** Chain to parent. + */ + MapClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * MapClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Code_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Code_Pointers(); + cellptr++; + } +} + + +/*********************************************************************************************** + * MapClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void MapClass::Decode_Pointers(void) +{ + CellClass * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + cellptr->Decode_Pointers(); + cellptr++; + } +} + diff --git a/code/redalert/ioobj.cpp b/code/redalert/ioobj.cpp new file mode 100644 index 0000000..3cee98e --- /dev/null +++ b/code/redalert/ioobj.cpp @@ -0,0 +1,898 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IOOBJ.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IOOBJ.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 16, 1995 * + * * + * Last Update : May 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * LayerClass::Load -- Reads from a save game file. * + * LayerClass::Save -- Write to a save game file. * + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * ReinforcementClass::Code_Pointers -- codes class's pointers for load/save * + * ReinforcementClass::Decode_Pointers -- decodes pointers for load/save * + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TeamTypeClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Code_Pointers(void) +{ + /* + ** Code the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = (TechnoTypeClass *)Members[i].Class->As_Target(); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TeamTypeClass::Decode_Pointers(void) +{ + /* + ** Decode the Class array + */ + for (int i = 0; i < ClassCount; i++) { + Members[i].Class = As_TechnoType((TARGET)Members[i].Class); + assert(Members[i].Class != NULL); + } +} + + +/*********************************************************************************************** + * TeamClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 05/13/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Code_Pointers(void) +{ + /* + ** Code the 'Member' + */ + if (Member) { + Member = (FootClass *)Member->As_Target(); + } +} + + +/*********************************************************************************************** + * TeamClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +void TeamClass::Decode_Pointers(void) +{ + /* + ** Decode the 'Member' + */ + if (Member) { + Member = (FootClass *)As_Techno((TARGET)Member, false); + assert(Member != NULL); + } +} + + +/*********************************************************************************************** + * TriggerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Code_Pointers(void) +{ + Action1.Code_Pointers(); + Action2.Code_Pointers(); +} + + +/*********************************************************************************************** + * TriggerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Decode_Pointers(void) +{ + Action1.Decode_Pointers(); + Action2.Decode_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Code_Pointers(void) +{ + /* + ** Code 'Payback' + */ + if (Payback) { + Payback = (TechnoClass *)Payback->As_Target(); + } + + /* + ** Chain to parent + */ + ObjectClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * BulletClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void BulletClass::Decode_Pointers(void) +{ + /* + ** Decode 'Payback' + */ + if (Payback) { + Payback = As_Techno((TARGET)Payback, false); + assert(Payback != NULL); + } + + /* + ** Chain to parent + */ + ObjectClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * FactoryClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Code_Pointers(void) +{ + if (Object) { + Object = (TechnoClass *)Object->As_Target(); + } + + ((HouseClass *&)House) = (HouseClass *)House->Class->House; +} + + +/*********************************************************************************************** + * FactoryClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FactoryClass::Decode_Pointers(void) +{ + if (Object) { + Object = As_Techno((TARGET)Object, false); + assert(Object != NULL); + } + + unsigned int house_ptr_val = *((unsigned int*)&House); + ((HouseClass *&)House) = HouseClass::As_Pointer((HousesType)house_ptr_val); + + assert(House != NULL); +} + + +/*********************************************************************************************** + * LayerClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the cell's data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Load(Straw & file) +{ + /* + ** Read # elements in the layer + */ + int count; + if (file.Get(&count, sizeof(count)) != sizeof(count)) { + return(false); + } + + /* + ** Clear the array + */ + Clear(); + + /* + ** Read in all array elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr; + if (file.Get(&ptr, sizeof(ObjectClass *)) != sizeof(ObjectClass *)) { + return(false); + } + Add(ptr); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the cell's data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +bool LayerClass::Save(Pipe & file) const +{ + /* + ** Save # array elements + */ + int count = Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save all elements + */ + for (int index = 0; index < count; index++) { + ObjectClass * ptr = (*this)[index]; + file.Put(&ptr, sizeof(ObjectClass *)); + } + + return(true); +} + + +/*********************************************************************************************** + * LayerClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Code_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + assert(obj != NULL); + (*this)[index] = (ObjectClass *)(obj->As_Target()); + } +} + + +/*********************************************************************************************** + * LayerClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void LayerClass::Decode_Pointers(void) +{ + for (int index = 0; index < Count(); index++) { + TARGET target = (TARGET)(*this)[index]; + (*this)[index] = (ObjectClass *)As_Object(target, false); + assert((*this)[index] != NULL); + } +} + + +/*********************************************************************************************** + * HouseClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * HouseClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void HouseClass::Decode_Pointers(void) +{ + /* + ** Re-assign the house's remap table (for multiplayer game loads) + ** Loading the house from disk will have over-written the house's RemapTable, so + ** Init_Data() is called to reset it to a valid pointer. + */ + + Init_Data(RemapColor, ActLike, Credits); +} + + +/*********************************************************************************************** + * ScoreClass::Code_Pointers -- codes class's pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Code_Pointers(void) +{ + RealTime.Stop(); +} + + +/*********************************************************************************************** + * ScoreClass::Decode_Pointers -- decodes pointers for load/save * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ScoreClass::Decode_Pointers(void) +{ + RealTime.Start(); +} + + +/*********************************************************************************************** + * FootClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Code_Pointers(void) +{ + if (Member != NULL && Member->IsActive) { + Member = (FootClass *)Member->As_Target(); + } else { + Member = TARGET_NONE; + } + + TechnoClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * FootClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void FootClass::Decode_Pointers(void) +{ + if ((TARGET)Member != TARGET_NONE) { + Member = (FootClass *)As_Techno((TARGET)Member, false); + assert(Member != NULL); + } + + TechnoClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Code_Pointers(void) +{ + /* + ** Code 'Radio' + */ + if (Radio) { + Radio = (RadioClass *)Radio->As_Target(); + } + + MissionClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * RadioClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void RadioClass::Decode_Pointers(void) +{ + /* + ** Decode 'Radio' + */ + if (Radio) { + Radio = As_Techno((TARGET)Radio, false); + assert(Radio != NULL); + } + + MissionClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Code_Pointers(void) +{ + CargoClass::Code_Pointers(); + RadioClass::Code_Pointers(); +} + + +/*********************************************************************************************** + * TechnoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void TechnoClass::Decode_Pointers(void) +{ + CargoClass::Decode_Pointers(); + RadioClass::Decode_Pointers(); +} + + +/*********************************************************************************************** + * CargoClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Code_Pointers(void) +{ + /* + ** Code 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)CargoHold->As_Target(); + } +} + + +/*********************************************************************************************** + * CargoClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void CargoClass::Decode_Pointers(void) +{ + /* + ** Decode 'CargoHold' + */ + if (CargoHold) { + CargoHold = (FootClass *)As_Techno((TARGET)CargoHold, false); + assert(CargoHold != NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Code_Pointers -- codes class's pointers for load/save * + * * + * This routine "codes" the pointers in the class by converting them to a number * + * that still represents the object pointed to, but isn't actually a pointer. This * + * allows a saved game to properly load without relying on the games data still * + * being in the exact same location. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Code_Pointers(void) +{ + if (Next) { + Next = (ObjectClass *)Next->As_Target(); + } +} + + +/*********************************************************************************************** + * ObjectClass::Decode_Pointers -- decodes pointers for load/save * + * * + * This routine "decodes" the pointers coded in Code_Pointers by converting the * + * code values back into object pointers. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/02/1995 BR : Created. * + *=============================================================================================*/ +void ObjectClass::Decode_Pointers(void) +{ + if (Next) { + Next = As_Object((TARGET)Next, false); + assert(Next != NULL); + } +} diff --git a/code/redalert/ipx.cpp b/code/redalert/ipx.cpp new file mode 100644 index 0000000..c5ce6da --- /dev/null +++ b/code/redalert/ipx.cpp @@ -0,0 +1,1164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPX.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.CPP * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 15, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Pitfalls: * + * - Never try to use a closed socket; always check the return code from * + * IPX_Open_Socket(). * + * - Always give IPX an outstanding ECB for listening, before you send. * + * - It turns out that IPX is pretty bad about saving registers, so if * + * you have any register variables in your program, they may get * + * trashed. To circumvent this, all functions in this module save & * + * restore the registers before invoking any IPX or NETX function. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * IPX_Close_Socket -- closes an open socket * + * IPX_Get_Connection_Number -- gets local Connection Number * + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * IPX_Get_User_ID -- gets user ID from Connection Number * + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * IPX_Send_Packet -- commands IPX to send a packet * + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * IPX_Cancel_Event -- cancels an operation in progress * + * Let_IPX_Breath -- gives IPX some CPU time * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include +//#include +#include "ipx.h" + +#ifdef WIN32 +#include "ipx95.h" +#endif //WIN32 + + +// Turn off "expression is not meaningful". +//#pragma warning 628 9 + + +/*************************************************************************** + * IPX_SPX_Installed -- checks for installation of IPX/SPX * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = not installed; 1 = IPX only, 2 = IPX and SPX are installed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/14/1994 BR : Created. * + *=========================================================================*/ +int IPX_SPX_Installed(void) +{ + return false; +#ifdef WIN32 + +#ifdef TIBERIAN_SUN + return(false); +#else +#if (0)//PG + if ( Load_IPX_Dll () ){ + return ( IPX_Initialise() ); + }else{ + return(false); + } +#endif +#endif + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Init all registers to 0's + //------------------------------------------------------------------------ + memset (®s, 0, sizeof(regs)); + segread (&sregs); + memset (&rmi, 0, sizeof(rmi)); + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call, function 0x300 + //------------------------------------------------------------------------ + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x002f; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the real-mode interrupt handler. + // To test for the presence of IPX, set AH to 0x7a, AL to 0, and invoke + // interrupt 0x2f (the "multiplex" interrupt). If IPX is installed, + // AL will be 0xff, and ES:DI will contain the IPX/SPX function address. + //------------------------------------------------------------------------ + rmi.eax = 0x00007a00; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // If IPX isn't there, return 0 + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) != 0xff) { + return(0); + } + + //------------------------------------------------------------------------ + // Test for SPX by invoking the IPX_SPX function with BX = 0x10, and AL = 0. + // If SPX is present, AL will be 0xff. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = 0x00000010; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // SPX is installed; return '2' + //------------------------------------------------------------------------ + if ( (rmi.eax & 0x00ff) == 0xff) { + return(2); + } + + //------------------------------------------------------------------------ + // SPX is not installed; return '1' + //------------------------------------------------------------------------ + return(1); +#endif //WIN32 + +} /* end of IPX_SPX_Installed */ + + +/*************************************************************************** + * IPX_Open_Socket -- opens an IPX socket for sending or receiving * + * * + * INPUT: * + * socket the socket number to open * + * * + * OUTPUT: * + * 0 = OK * + * -1 = IPX not installed * + * 0xfe = socket table is full * + * 0xff = socket is already open * + * * + * WARNINGS: * + * The application must define its socket number carefully. Use * + * values from 0x4000 to 0x8000 for custom socket numbers. The app * + * must know its own socket number as well as the socket number of * + * a destination workstation. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ + +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Open_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int rc; + + //------------------------------------------------------------------------ + // Open the socket: + // DX = socket number + // AL = 0 for short-lived socket, 0xff for long-lived socket + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG (&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF (&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_OPEN_SOCKET; // function code + rmi.edx = socket; // desired socket # + rmi.eax = 0x00ff; // make this a long-lived socket + + //........................................................................ + // call DPMI + //........................................................................ + int386x (DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0xff); + + return(rc); + +} /* end of IPX_Open_Socket */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Close_Socket -- closes an open socket * + * * + * INPUT: * + * socket socket number to close * + * * + * OUTPUT: * + * 0 = ok, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP + +int IPX_Close_Socket(unsigned short socket) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Close the socket: + // DX = socket number + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_CLOSE_SOCKET; + rmi.edx = socket; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(0); + +} /* end of IPX_Close_Socket */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_Connection_Number -- gets local Connection Number * + * * + * This Novell call will the return the user's local "Connection Number". * + * This value will be 0 if the user isn't logged into Novell, so this * + * routine can be used to detect if other calls (such as Get_Local_Target) * + * will be OK. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Connection Number, 0 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Connection_Number(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + int num; + + //------------------------------------------------------------------------ + // Call Interrupt 0x21, with AH = 0xdc. This tells Novell to put the local + // connection number into AL. + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000dc00; + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + num = rmi.eax & 0x00ff; + + return(num); + +} /* end of IPX_Get_Connection_Number */ +#endif //WIN32 + + +/*************************************************************************** + * IPX_Get_1st_Connection_Num -- gets 1st Connect Number for given user * + * * + * This gets the Connection Number for the given User ID. Since a user * + * may be logged in more than once, this just returns the first connection * + * found and ignores the others. * + * * + * INPUT: * + * username name of the user to get the Connection Number for * + * * + * OUTPUT: * + * first-found Connection Number for that user, 0 if user not logged in * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_1st_Connection_Num (char * username) +{ + struct request_buffer { + unsigned short len; // username length + 5 + unsigned char buffer_type; // ConnectionNum = 0x15 + unsigned short object_type; // set ot 0x0100 + unsigned char name_len; // length of username + char name [48]; // copy of username + unsigned short reserved; + }; + struct reply_buffer { + unsigned short len; + unsigned char number_connections; // will be 0 - 100 + unsigned char connection_num [100]; // array of connection numbers + unsigned short reserved[2]; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + int num_conns; // # connections returned + int conn_num; // connection number + int rc; + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = (unsigned short)(strlen(username) + 5); + reqbuf->buffer_type = 0x15; + reqbuf->object_type = 0x0100; + reqbuf->name_len = (unsigned char) strlen(username); + strcpy(reqbuf->name, username); + reqbuf->reserved = reqbuf->reserved; // prevent compiler warning + replybuf->len = 101; + replybuf->reserved[0] = replybuf->reserved[0]; // prevent compiler warning + replybuf->reserved[0] = replybuf->reserved[1]; // prevent compiler warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Stash the 1st connection number + //------------------------------------------------------------------------ + rc = (rmi.eax & 0x00ff); // if AL !=0, error + num_conns = replybuf->number_connections; // # times user is logged in + conn_num = (int )replybuf->connection_num[0]; // 1st connection # + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // Return error if function failed, or user not logged in + //------------------------------------------------------------------------ + if (rc != 0 || num_conns==0) { + return(0); + } + else { + return(conn_num); + } + +} /* end of IPX_Get_1st_Connection_Num */ + +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Internet_Address -- gets Network Number & Node Address * + * * + * Once you've obtained a Connection Number from IPX_Get_Connection_Number * + * or IPX_Get_1st_Connection_Num, use this function to translate it into * + * a Network Number and Node Address; then, place those numbers in the * + * IPX header for outgoing packets. * + * * + * INPUT: * + * connection_number Connection Number to translate * + * network_number ptr: will hold Network Number * + * physical_node ptr: will hold Node Address * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * If connection_number is 0 and NETX isn't running, this routine * + * will just put garbage into the network_number and physical_node. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Internet_Address(int connection_number, + unsigned char * network_number, unsigned char * physical_node) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // Internet = 0x13 + unsigned char connection_number; // Conn. Number to translate + }; + struct reply_buffer { + unsigned short len; + unsigned char network_number [4]; // filled in by IPX + unsigned char physical_node [6]; // filled in by IPX + unsigned short server_socket; // filled in by IPX, but don't use! + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x13; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = 12; + replybuf->network_number[0] = replybuf->network_number[0]; // suppress warning + replybuf->physical_node[0] = replybuf->physical_node[0]; // suppress warning + replybuf->server_socket = replybuf->server_socket; // suppress warning + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + memcpy(network_number, replybuf->network_number, 4); + memcpy(physical_node, replybuf->physical_node, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_Internet_Address */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_User_ID -- gets user ID from Connection Number * + * * + * INPUT: * + * connection_number Connection Number to get User ID for * + * user_id ptr to buffer to put User ID into; * + * size must be >= 48 chars * + * * + * OUTPUT: * + * 0 = OK, -1 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_User_ID(int connection_number, char * user_id) +{ + struct request_buffer { + unsigned short len; + unsigned char buffer_type; // 0x16 = UserID buffer type + unsigned char connection_number; // Connection Number to get ID for + }; + struct reply_buffer { + unsigned short len; + unsigned char object_id[4]; + unsigned char object_type[2]; + char object_name[48]; + char login_time[7]; + unsigned short reserved; + }; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer * reqbuf; + struct reply_buffer * replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Error if invalid connection is given + //------------------------------------------------------------------------ + if (connection_number==0) { + return(-1); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + reqbuf->len = 2; + reqbuf->buffer_type = 0x16; + reqbuf->connection_number = (unsigned char)connection_number; + replybuf->len = sizeof(struct reply_buffer) - 2; + replybuf->object_id[0] = replybuf->object_id[0]; // suppress warnings + replybuf->object_type[0] = replybuf->object_type[0]; // suppress warnings + replybuf->login_time[0] = replybuf->login_time[0]; // suppress warnings + replybuf->reserved = replybuf->reserved; // suppress warnings + + //------------------------------------------------------------------------ + // Invoke Int 21 with AH=0xe3, DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = 0x21; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.eax = 0x0000e300; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + //------------------------------------------------------------------------ + // Fill in the caller's buffer with the user name + //------------------------------------------------------------------------ + strncpy(user_id, replybuf->object_name, 48); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(0); + +} /* end of IPX_Get_User_ID */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Listen_For_Packet -- commands IPX to listen for a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are "listening" on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas to * + * store the incoming data in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of data buffer, for the packet * + * Packet[1].Length: size of the data buffer * + * * + * When the packet is received, ECBType.CompletionCode will be 0 if * + * successful. Otherwise, some error occurred. * + * * + * You should initialize the ECB to 0's before filling it in. * + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB; MUST be real-mode memory * + * * + * OUTPUT: * + * 0 = OK, IPX error otherwise * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Listen_For_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_LISTEN_FOR_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + return(rmi.eax & 0x00ff); + +} /* end of IPX_Listen_For_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Send_Packet -- commands IPX to send a packet * + * * + * Before calling this function, you must fill in an ECB: * + * SocketNumber: must contain the socket you've opened, * + * and are sending on * + * Event_Service_Routine: optionally points to a callback routine * + * PacketCount: set to 2, to tell IPX there are 2 areas the * + * outgoing data is stored in * + * Packet[0].Address: set to the address of an IPXHeaderType * + * Packet[0].Length: sizeof(IPXHeaderType) * + * Packet[1].Address: address of buffer containing data to send * + * Packet[1].Length: size of the data buffer * + * ImmediateAddress: must be filled in with the node address of * + * the bridge that will route the message; * + * fill this in by calling IPX_Get_Local_Target * + * * + * Also, you must fill in the IPXHeaderType with certain values: * + * PacketType: set to 4 for IPX * + * DestNetworkNumber: Network Number of the destination system * + * DestNetworkNode: Node Address of the destination system * + * DestNetworkSocket: the destination system's socket to send to; * + * this doesn't have to be the same as the * + * socket opened on the local machine. * + * * + * You should initialize the ECB & IPXHeader to 0's before filling them in.* + * * + * INPUT: * + * ecb_ptr pointer to a filled-in ECB * + * * + * OUTPUT: * + * none. This function doesn't return anything; the caller must check * + * its ECB.CompletionCode for: 0 = OK, IPX Error otherwise. * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void IPX_Send_Packet(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Call IPX with ES:SI=ecb_ptr + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_SEND_PACKET; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of IPX_Send_Packet */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Get_Local_Target -- fills in ImmediateAddress field of ECB * + * * + * Use this function to fill in the ECB's ImmediateAddress field when * + * sending a packet. The Immediate Address is the node address of the * + * bridge that must route the message. If there is no bridge, it's * + * filled in with the destination Node Address. In either case, it * + * will contain the proper value for sending. * + * * + * INPUT: * + * dest_network destination Network Number * + * dest_node destination Node Address * + * dest_socket destination Socket Number * + * bridge_address field to fill in with Immediate Address * + * * + * OUTPUT: * + * 0 = OK, error otherwise * + * * + * WARNINGS: * + * If the Connection Number is 0 (user not logged in), dest_network * + * and dest_node will be garbage, and this routine will probably crash. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address) +{ + struct request_buffer { + unsigned char network_number [4]; + unsigned char physical_node [6]; + unsigned short socket; + }; + struct reply_buffer { + unsigned char local_target [6]; + }; + unsigned int rc; + union REGS regs; + struct SREGS sregs; + RMIType rmi; + struct request_buffer *reqbuf; + struct reply_buffer *replybuf; + unsigned short segment; // for DOS allocation + unsigned short selector; // for DOS allocation + + //------------------------------------------------------------------------ + // Allocate DOS memory to store the buffers passed to the interrupt + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = (sizeof(struct request_buffer) + // # paragraphs to allocate + sizeof(struct reply_buffer) + 15) >> 4; + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(-1); + } + + //------------------------------------------------------------------------ + // Get pointers to allocated memory. + // 'reqbuf' is just the returned real-mode segment, multiplied by 16. + // 'replybuf' is an offset from 'reqbuf'. + //------------------------------------------------------------------------ + segment = regs.w.ax; + selector = regs.w.dx; + reqbuf = (struct request_buffer *)(segment << 4); + replybuf = (struct reply_buffer *) + (((char *)reqbuf) + sizeof (struct request_buffer)); + + //------------------------------------------------------------------------ + // Init the contents of the request & reply buffers + //------------------------------------------------------------------------ + memcpy(reqbuf->network_number, dest_network, 4); + memcpy(reqbuf->physical_node, dest_node, 6); + reqbuf->socket = dest_socket; + + //------------------------------------------------------------------------ + // Invoke IPX with DS:SI=&request_buffer, ES:DI=&reply_buffer + //------------------------------------------------------------------------ + //........................................................................ + // Fill in registers for the DPMI call + //........................................................................ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //........................................................................ + // Fill in registers for the interrupt call + //........................................................................ + rmi.ebx = IPX_GET_LOCAL_TARGET; + rmi.ds = segment; + rmi.esi = 0; + rmi.es = segment; + rmi.edi = sizeof(struct request_buffer); + //........................................................................ + // call DPMI + //........................................................................ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + memcpy(bridge_address, replybuf->local_target, 6); + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + return(rc); + +} /* end of IPX_Get_Local_Target */ +#endif //WIN32 + +/*************************************************************************** + * IPX_Cancel_Event -- cancels an operation in progress * + * * + * INPUT: * + * ecb_ptr pointer to ECB event to cancel * + * * + * OUTPUT: * + * ??? * + * * + * WARNINGS: * + * The ECB must be located in real-mode memory, as well as the values * + * pointed to in the ECB (the IPX Header, the buffer to send, etc.) * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +int IPX_Cancel_Event(struct ECB *ecb_ptr) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + unsigned int rc; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_CANCEL_EVENT; + rmi.es = (short)((long)ecb_ptr >> 4); + rmi.esi = (long)((long)ecb_ptr & 0x000f); + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + + rc = (rmi.eax & 0x00ff); + + return(rc); + +} /* end of IPX_Cancel_Event */ +#endif //WIN32 + +/*************************************************************************** + * Let_IPX_Breath -- gives IPX some CPU time * + * * + * Use this function if you're polling the ECB's InUse flag, waiting * + * for it to go to 0: * + * * + * while ECBType.InUse * + * Let_IPX_Breath(); * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/15/1994 BR : Created. * + *=========================================================================*/ +#ifndef WIN32 // WIN32 version is in IPX95.CPP +void Let_IPX_Breath(void) +{ + union REGS regs; + struct SREGS sregs; + RMIType rmi; + + //------------------------------------------------------------------------ + // Fill in registers for the DPMI call + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + memset (&rmi, 0 ,sizeof(rmi)); + regs.w.ax = DPMI_CALL_REAL_INT; // DPMI function to call + regs.w.bx = IPX_INT; // interrupt # to invoke + sregs.es = FP_SEG(&rmi); // tell DPMI where the RMI is + regs.x.edi = FP_OFF(&rmi); + + //------------------------------------------------------------------------ + // Fill in registers for the interrupt call + //------------------------------------------------------------------------ + rmi.ebx = IPX_RELINQUISH_CONTROL; + + //------------------------------------------------------------------------ + // call DPMI + //------------------------------------------------------------------------ + int386x(DPMI_INT, ®s, ®s, &sregs); + +} /* end of Let_IPX_Breath */ +#endif //WIN32 +/**************************** end of ipx.cpp *******************************/ diff --git a/code/redalert/ipx.h b/code/redalert/ipx.h new file mode 100644 index 0000000..79edd2f --- /dev/null +++ b/code/redalert/ipx.h @@ -0,0 +1,188 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPX.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX.H * + * * + * Programmer : Barry Nance * + * from Client/Server LAN Programming * + * Westwood-ized by Bill Randolph * + * * + * Start Date : December 14, 1994 * + * * + * Last Update : December 14, 1994 [BR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPX_H +#define IPX_H + +/* +******************************** Structures ********************************* +*/ +typedef unsigned char NetNumType[4]; +typedef unsigned char NetNodeType[6]; +typedef char UserID[48]; + +/*--------------------------------------------------------------------------- +This is the IPX Packet structure. It's followed by the data itself, which +can be up to 546 bytes long. Annotation of 'IPX' means IPX will set this +field; annotation of 'APP' means the application must set the field. +NOTE: All header fields are ordered high-byte,low-byte. +---------------------------------------------------------------------------*/ +typedef struct IPXHEADER { + unsigned short CheckSum; // IPX: Not used; always 0xffff + unsigned short Length; // IPX: Total size, incl header & data + unsigned char TransportControl; // IPX: # bridges message crossed + unsigned char PacketType; // APP: Set to 4 for IPX (5 for SPX) + unsigned char DestNetworkNumber[4]; // APP: destination Network Number + unsigned char DestNetworkNode[6]; // APP: destination Node Address + unsigned short DestNetworkSocket; // APP: destination Socket Number + unsigned char SourceNetworkNumber[4]; // IPX: source Network Number + unsigned char SourceNetworkNode[6]; // IPX: source Node Address + unsigned short SourceNetworkSocket; // IPX: source Socket Number +} IPXHeaderType; + +/*--------------------------------------------------------------------------- +This is the IPX Event Control Block. It serves as a communications area +between IPX and the application for a single IPX operation. You should set +up a separate ECB for each IPX operation you perform. +---------------------------------------------------------------------------*/ +typedef struct ECB { + void *Link_Address; + void (*Event_Service_Routine)(void); // APP: event handler (NULL=none) + unsigned char InUse; // IPX: 0 = event complete + unsigned char CompletionCode; // IPX: event's return code + unsigned short SocketNumber; // APP: socket to send data through + unsigned short ConnectionID; // returned by Listen (???) + unsigned short RestOfWorkspace; + unsigned char DriverWorkspace[12]; + unsigned char ImmediateAddress[6]; // returned by Get_Local_Target + unsigned short PacketCount; + struct { + void *Address; + unsigned short Length; + } Packet[2]; +} ECBType; + + +/*--------------------------------------------------------------------------- +This structure is used for calling DPMI function 0x300, Call-Real-Mode- +Interrupt. It passes register values to & from the interrupt handler. +---------------------------------------------------------------------------*/ +typedef struct { + long edi; + long esi; + long ebp; + long Reserved; + long ebx; + long edx; + long ecx; + long eax; + short Flags; + short es; + short ds; + short fs; + short gs; + short ip; + short cs; + short sp; + short ss; +} RMIType; + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +These defines are for the IPX functions. The function number is specified +by placing it in BX when IPX is called. There are two ways to invoke IPX: +use interrupt 0x7a, or use a function whose address is obtained by calling +interrupt 0x2f with AX=0x7a00; the function address is returned in ES:DI. +This is the preferred method, since other apps are known to use int 0x7a. +---------------------------------------------------------------------------*/ +#define IPX_OPEN_SOCKET 0x0000 +#define IPX_CLOSE_SOCKET 0x0001 +#define IPX_GET_LOCAL_TARGET 0x0002 +#define IPX_SEND_PACKET 0x0003 +#define IPX_LISTEN_FOR_PACKET 0x0004 +#define IPX_SCHEDULE_EVENT 0x0005 +#define IPX_CANCEL_EVENT 0x0006 +#define IPX_GET_INTERVAL_MARKER 0x0008 +#define IPX_GET_INTERNETWORK_ADDRESS 0x0009 +#define IPX_RELINQUISH_CONTROL 0x000A +#define IPX_DISCONNECT_FROM_TARGET 0x000B + +/*--------------------------------------------------------------------------- +These defines are for various IPX error codes: +---------------------------------------------------------------------------*/ +#define IPXERR_CONNECTION_SEVERED 0x00ec +#define IPXERR_CONNECTION_FAILED 0x00ed +#define IPXERR_NO_CONNECTION 0x00ee +#define IPXERR_CONNECTION_TABLE_FULL 0x00ef +#define IPXERR_NO_CANCEL_ECB 0x00f9 +#define IPXERR_NO_PATH 0x00fa +#define IPXERR_ECB_INACTIVE 0x00fc +#define IPXERR_INVALID_PACKET_LENGTH 0x00fd +#define IPXERR_SOCKET_TABLE_FULL 0x00fe +#define IPXERR_SOCKET_ERROR 0x00ff + +/*--------------------------------------------------------------------------- +These defines are for various interrupt vectors and DPMI functions: +---------------------------------------------------------------------------*/ +#define IPX_INT 0x007a +#define DPMI_INT 0x0031 +#define DPMI_ALLOC_DOS_MEM 0x0100 +#define DPMI_FREE_DOS_MEM 0x0101 +#define DPMI_CALL_REAL_INT 0x0300 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/* +******************************** Prototypes ********************************* +*/ +/*--------------------------------------------------------------------------- +NOTE: All routines return 0 for a success code and one of the above IPX +error codes if there's an error, EXCEPT: +- IPX_SPX_Installed (0 = not installed) +- Get_Connection_Number / Get_1st_Connection_Number (0 = no connection) +---------------------------------------------------------------------------*/ +/* +.................................. ipx.cpp .................................. +*/ +int IPX_SPX_Installed(void); +int IPX_Open_Socket(unsigned short socket); +int IPX_Close_Socket(unsigned short socket); +int IPX_Get_Connection_Number(void); +int IPX_Get_1st_Connection_Num (char *username); +int IPX_Get_Internet_Address(int connection_number, + unsigned char *network_number, unsigned char *physical_node); +int IPX_Get_User_ID(int connection_number, char *user_id); +int IPX_Listen_For_Packet(struct ECB *ecb_ptr); +void IPX_Send_Packet(struct ECB *ecb_ptr); +int IPX_Get_Local_Target(unsigned char *dest_network, unsigned char *dest_node, + unsigned short dest_socket, unsigned char *bridge_address); +int IPX_Cancel_Event(struct ECB *ecb_ptr); +void Let_IPX_Breath(void); + +#endif + +/***************************** end of ipx.h ********************************/ + diff --git a/code/redalert/ipx95.cpp b/code/redalert/ipx95.cpp new file mode 100644 index 0000000..784f4a4 --- /dev/null +++ b/code/redalert/ipx95.cpp @@ -0,0 +1,231 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : July 10th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Windows 95 equivelent functions from IPX.CPP * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +#ifdef WIN32 + +#include "ipx95.h" + + // Stub in old IPX here ST - 12/20/2018 1:53PM +extern "C" { + extern BOOL __stdcall IPX_Initialise(void) { return 0; } + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer) { return 0; } + extern void __stdcall IPX_Shut_Down95(void) {} + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*) { return 0; } + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int) { return 0; } + extern BOOL __stdcall IPX_Start_Listening95(void) { return 0; } + extern int __stdcall IPX_Open_Socket95(int socket) { return 0; } + extern void __stdcall IPX_Close_Socket95(int socket) {} + extern int __stdcall IPX_Get_Connection_Number95(void) { return 0; } + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*) { return 0; } +} + +#if (0) +/* +** Instance handle for the THIPX32 .DLL +*/ +HINSTANCE IpxDllInstance = NULL; + +/* +** Function pointers +*/ +//extern "C" { + IPXInitialiseType IPX_Initialise = NULL; + IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95 = NULL; + IPXShutDown95Type IPX_Shut_Down95 = NULL; + IPXSendPacket95Type IPX_Send_Packet95 = NULL; + IPXBroadcastPacket95Type IPX_Broadcast_Packet95 = NULL; + IPXStartListening95Type IPX_Start_Listening95 = NULL; + IPXOpenSocket95Type IPX_Open_Socket95 = NULL; + IPXCloseSocket95Type IPX_Close_Socket95 = NULL; + IPXGetConnectionNumber95Type IPX_Get_Connection_Number95 = NULL; + IPXGetLocalTarget95 IPX_Get_Local_Target95 = NULL; +//} + +char const *FunctionNames[] = { + "_IPX_Initialise", + "_IPX_Get_Outstanding_Buffer95", + "_IPX_Shut_Down95", + "_IPX_Send_Packet95", + "_IPX_Broadcast_Packet95", + "_IPX_Start_Listening95", + "_IPX_Open_Socket95", + "_IPX_Close_Socket95", + "_IPX_Get_Connection_Number95", + "_IPX_Get_Local_Target95", + NULL +}; +#endif + +extern void Get_OS_Version (void); +bool WindowsNT = false; + + +/*********************************************************************************************** + * Load_IPX_Dll -- Loads the THIPX32 DLL * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if DLL loaded * + * * + * WARNINGS: This call will fail under NT due to a side effect of loading the THIPX32.DLL * + * which causes the 16 bit DLL THIPX16.DLL to load. * + * * + * HISTORY: * + * 4/1/97 11:40AM ST : Created * + *=============================================================================================*/ +bool Load_IPX_Dll (void) +{ + //ST 5/13/2019 + return false; + +#if (0) + Get_OS_Version(); + if (WindowsNT) return (false); + + SetErrorMode( SEM_NOOPENFILEERRORBOX ); + IpxDllInstance = LoadLibrary ( "THIPX32.DLL" ); + SetErrorMode ( 0 ); + + if ( IpxDllInstance ){ + + const char *function_name; + unsigned long *fptr = (unsigned long *) &IPX_Initialise; + int count = 0; + + do { + function_name = FunctionNames[count]; + if (function_name){ + *fptr = (unsigned long) GetProcAddress (IpxDllInstance, function_name); + assert (*fptr != NULL); + fptr++; + count++; + } + + } while ( function_name ); + + return (true); + + }else{ + return (false); + } +#endif +} + + + +/*********************************************************************************************** + * Unload_IPX_Dll -- Frees the THIPX32 library * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/1/97 2:37PM ST : Created * + *=============================================================================================*/ +void Unload_IPX_Dll (void) +{ + //ST 5/13/2019 + +#if (0) + if (IpxDllInstance){ + FreeLibrary (IpxDllInstance); + IpxDllInstance = NULL; + } +#endif +} + + +int IPX_Open_Socket(unsigned short socket) +{ + return -1; //ST 5/13/2019 + //return ( IPX_Open_Socket95((int)socket)); +} + +int IPX_Close_Socket(unsigned short socket) +{ + //ST 5/13/2019 IPX_Close_Socket95((int)socket); + return (0); +} + + +int IPX_Get_Connection_Number(void) +{ + return -1;//ST 5/13/2019 + //return (IPX_Get_Connection_Number95()); +} + + + +int IPX_Broadcast_Packet(unsigned char * buf, int buflen) +{ + return 0; //ST 5/13/2019 + //return(IPX_Broadcast_Packet95(buf, buflen)); +} + +#if (0) //ST 5/13/2019 +extern "C"{ + extern void __cdecl Int3(void); +} +#endif + +int IPX_Get_Local_Target(unsigned char * dest_network, unsigned char * dest_node, + unsigned short dest_socket, unsigned char * bridge_address) +{ + //Int3(); + + return 0; //ST 5/13/2019 + //return (IPX_Get_Local_Target95(dest_network, dest_node, dest_socket, bridge_address)); +} + + +#endif //WIN32 diff --git a/code/redalert/ipx95.h b/code/redalert/ipx95.h new file mode 100644 index 0000000..54bb5fa --- /dev/null +++ b/code/redalert/ipx95.h @@ -0,0 +1,101 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPX95PP * + * * + * Programmer : Steve Tall * + * * + * Start Date : January 22nd, 1996 * + * * + * Last Update : January 22nd, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#if (0) + +/* +** Types for function pointers +*/ +typedef BOOL __stdcall (*IPXInitialiseType) (void); +typedef BOOL __stdcall (*IPXGetOutstandingBuffer95Type) (unsigned char*); +typedef void __stdcall (*IPXShutDown95Type) (void); +typedef int __stdcall (*IPXSendPacket95Type) (unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); +typedef int __stdcall (*IPXBroadcastPacket95Type) (unsigned char *, int); +typedef BOOL __stdcall (*IPXStartListening95Type) (void); +typedef int __stdcall (*IPXOpenSocket95Type) (int); +typedef void __stdcall (*IPXCloseSocket95Type) (int); +typedef int __stdcall (*IPXGetConnectionNumber95Type) (void); +typedef int __stdcall (*IPXGetLocalTarget95) (unsigned char *, unsigned char*, unsigned short, unsigned char*); + + + +/* +** Function pointers +*/ +//extern "C"{ + extern IPXInitialiseType IPX_Initialise; + extern IPXGetOutstandingBuffer95Type IPX_Get_Outstanding_Buffer95; + extern IPXShutDown95Type IPX_Shut_Down95; + extern IPXSendPacket95Type IPX_Send_Packet95; + extern IPXBroadcastPacket95Type IPX_Broadcast_Packet95; + extern IPXStartListening95Type IPX_Start_Listening95; + extern IPXOpenSocket95Type IPX_Open_Socket95; + extern IPXCloseSocket95Type IPX_Close_Socket95; + extern IPXGetConnectionNumber95Type IPX_Get_Connection_Number95; + extern IPXGetLocalTarget95 IPX_Get_Local_Target95; +//} + +/* +** Functions +*/ +bool Load_IPX_Dll (void); +void Unload_IPX_Dll (void); +#endif + +#if (1) +extern "C"{ + extern BOOL __stdcall IPX_Initialise(void); + extern BOOL __stdcall IPX_Get_Outstanding_Buffer95(unsigned char *buffer); + extern void __stdcall IPX_Shut_Down95(void); + extern int __stdcall IPX_Send_Packet95(unsigned char *, unsigned char *, int, unsigned char*, unsigned char*); + extern int __stdcall IPX_Broadcast_Packet95(unsigned char *, int); + extern BOOL __stdcall IPX_Start_Listening95(void); + extern int __stdcall IPX_Open_Socket95(int socket); + extern void __stdcall IPX_Close_Socket95(int socket); + extern int __stdcall IPX_Get_Connection_Number95(void); + extern int __stdcall IPX_Get_Local_Target95(unsigned char *, unsigned char*, unsigned short, unsigned char*); +} +#endif //(1) + +extern bool WindowsNT; + diff --git a/code/redalert/ipxaddr.cpp b/code/redalert/ipxaddr.cpp new file mode 100644 index 0000000..4cde126 --- /dev/null +++ b/code/redalert/ipxaddr.cpp @@ -0,0 +1,506 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXADDR.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXAddressClass::IPXAddressClass -- class constructor * + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * IPXAddressClass::Set_Address -- sets the IPX address values * + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * IPXAddressClass::operator== -- overloaded comparison operator * + * IPXAddressClass::operator!= -- overloaded comparison operator * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include "ipxaddr.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#endif //WINSOCK_IPX + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor * + * * + * This default constructor generates a broadcast address. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(void) +{ + NetworkNumber[0] = 0xff; + NetworkNumber[1] = 0xff; + NetworkNumber[2] = 0xff; + NetworkNumber[3] = 0xff; + NodeAddress[0] = 0xff; + NodeAddress[1] = 0xff; + NodeAddress[2] = 0xff; + NodeAddress[3] = 0xff; + NodeAddress[4] = 0xff; + NodeAddress[5] = 0xff; + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 2 * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber, net, 4); + memcpy(NodeAddress, node, 6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::IPXAddressClass -- class constructor form 3 * + * * + * This form of the constructor takes an IPX header as an argument. It * + * extracts the address from the Source address fields in the header. * + * * + * INPUT: * + * header Header from which to extract the address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +IPXAddressClass::IPXAddressClass(IPXHeaderType *header) +{ + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + +} /* end of IPXAddressClass */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(NetNumType net, NetNodeType node) +{ + memcpy(NetworkNumber,net,4); + memcpy(NodeAddress,node,6); + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Set_Address -- sets the IPX values from a header * + * * + * This routine extracts the source addresses from the given IPX header. * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Set_Address(IPXHeaderType *header) +{ +#ifdef WINSOCK_IPX + ProtocolEnum protocol = PROTOCOL_IPX; + if ( PacketTransport ) protocol = PacketTransport->Get_Protocol(); + + switch ( protocol ) { + + case PROTOCOL_IPX: + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + break; + + case PROTOCOL_UDP: + unsigned char *addr = (unsigned char*) header; + memset (NodeAddress, 0, 6); + memcpy (NodeAddress, addr, 4); + memset (NetworkNumber, 0, 4); + break; + } +#else //WINSOCK_IPX + + if (header) { + memcpy(NetworkNumber,header->SourceNetworkNumber,4); + memcpy(NodeAddress,header->SourceNetworkNode,6); + } else { + /* + ** Address is meaningless when using winsock + */ + memset(NetworkNumber, 1 ,4); + memset(NodeAddress, 1, 6); + } + +#endif //WINSOCK_IPX + +} /* end of Set_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- retrieves the IPX address values * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(NetNumType net, NetNodeType node) +{ + memcpy(net,NetworkNumber,4); + memcpy(node,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Get_Address -- copies address into an IPX header * + * * + * INPUT: * + * net Network Number for this address * + * node Node Address for this address * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +void IPXAddressClass::Get_Address(IPXHeaderType *header) +{ + memcpy(header->DestNetworkNumber,NetworkNumber,4); + memcpy(header->DestNetworkNode,NodeAddress,6); + +} /* end of Get_Address */ + + +/*************************************************************************** + * IPXAddressClass::Is_Broadcast -- tells if this is a broadcast address * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::Is_Broadcast(void) +{ + if ( NetworkNumber[0] == 0xff && + NetworkNumber[1] == 0xff && + NetworkNumber[2] == 0xff && + NetworkNumber[3] == 0xff && + NodeAddress[0] == 0xff && + NodeAddress[1] == 0xff && + NodeAddress[2] == 0xff && + NodeAddress[3] == 0xff && + NodeAddress[4] == 0xff && + NodeAddress[5] == 0xff) { + return(1); + } + else { + return(0); + } + +} /* end of Is_Broadcast */ + + +/*************************************************************************** + * IPXAddressClass::operator== -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = not equal, 1 = equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator == (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(1); + } + else { + return(0); + } + + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(1); + } + else { + return(0); + } + } + +} /* end of operator== */ + + +/*************************************************************************** + * IPXAddressClass::operator!= -- overloaded comparison operator * + * * + * Since, if NETX isn't running, the network number on a received packet * + * can be bogus (all 0's), only the node address is used for comparison * + * purposes here. * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * 0 = equal, 1 = not equal * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator != (IPXAddressClass & addr) +{ + //------------------------------------------------------------------------ + // If either Network Number is all 0's (which can happen if the system is + // not running NETX), compare only the Node Addresses. + //------------------------------------------------------------------------ + if ( (NetworkNumber[0]==0 && + NetworkNumber[1]==0 && + NetworkNumber[2]==0 && + NetworkNumber[3]==0) || + (addr.NetworkNumber[0]==0 && + addr.NetworkNumber[1]==0 && + addr.NetworkNumber[2]==0 && + addr.NetworkNumber[3]==0) ) { + + if (memcmp(NodeAddress,addr.NodeAddress,6)==0) { + return(0); + } + else { + return(1); + } + } + //------------------------------------------------------------------------ + // Otherwise, compare both the Network Numbers and Node Addresses + //------------------------------------------------------------------------ + else { + if (memcmp(NodeAddress,addr.NodeAddress,6)==0 && + memcmp(NetworkNumber,addr.NetworkNumber,4)==0) { + return(0); + } + else { + return(1); + } + } + +} /* end of operator!= */ + + +/*************************************************************************** + * IPXAddressClass::operator > -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator > (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) > 0); + +} /* end of operator> */ + + +/*************************************************************************** + * IPXAddressClass::operator < -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator < (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) < 0); + +} /* end of operator< */ + + +/*************************************************************************** + * IPXAddressClass::operator >= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = greater or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator >= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) >= 0); + +} /* end of operator>= */ + + +/*************************************************************************** + * IPXAddressClass::operator <= -- overloaded comparison operator * + * * + * INPUT: * + * addr address to compare to * + * * + * OUTPUT: * + * TRUE = less or equal, FALSE = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/19/1994 BR : Created. * + *=========================================================================*/ +int IPXAddressClass::operator <= (IPXAddressClass & addr) +{ + return(memcmp(this, &addr, 10) <= 0); + +} /* end of operator<= */ + +/************************** end of ipxaddr.cpp *****************************/ diff --git a/code/redalert/ipxaddr.h b/code/redalert/ipxaddr.h new file mode 100644 index 0000000..e2748ec --- /dev/null +++ b/code/redalert/ipxaddr.h @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXADDR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXADDR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : December 19, 1994 [BR] * + * * + * This class is useful for any IPX-related code. It's just a utility * + * to help manage those annoying IPX address fields. This class lets you * + * compare addresses, copy addresses to & from the IPX header, etc. * + * * + * The class has no virtual functions, so you can treat this class just * + * like a data structure; it can be loaded & saved, and even transmitted * + * across the net. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXADDR_H +#define IPXADDR_H + +#include "ipx.h" // for NetNumType & NetNodeType + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXAddressClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructors: + .....................................................................*/ + IPXAddressClass(void); + IPXAddressClass(NetNumType net, NetNodeType node); + IPXAddressClass(IPXHeaderType *header); + + /*..................................................................... + Set the address from explicit variables, or from the SOURCE values + in an IPX packet header. + .....................................................................*/ + void Set_Address(NetNumType net, NetNodeType node); + void Set_Address(IPXHeaderType *header); + /*..................................................................... + Get the address values explicitly, or copy them into the DESTINATION + values in an IPX packet header. + .....................................................................*/ + void Get_Address (NetNumType net, NetNodeType node); + void Get_Address(IPXHeaderType *header); + + /*..................................................................... + Tells if this address is a broadcast address + .....................................................................*/ + int Is_Broadcast(void); + + /*..................................................................... + Overloaded operators: + .....................................................................*/ + int operator == (IPXAddressClass & addr); + int operator != (IPXAddressClass & addr); + int operator > (IPXAddressClass &addr); + int operator < (IPXAddressClass &addr); + int operator >= (IPXAddressClass &addr); + int operator <= (IPXAddressClass &addr); + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /* + --------------------------- Private Interface ---------------------------- + */ + private: + NetNumType NetworkNumber; + NetNodeType NodeAddress; +}; + +#endif + +/**************************** end of ipxaddr.h *****************************/ diff --git a/code/redalert/ipxconn.cpp b/code/redalert/ipxconn.cpp new file mode 100644 index 0000000..ea138ac --- /dev/null +++ b/code/redalert/ipxconn.cpp @@ -0,0 +1,876 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXCONN.CPP 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXConnClass::IPXConnClass -- class constructor * + * IPXConnClass::~IPXConnClass -- class destructor * + * IPXConnClass::Init -- hardware-specific initialization routine * + * IPXConnClass::Configure -- One-time initialization routine * + * IPXConnClass::Start_Listening -- commands IPX to listen * + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * IPXConnClass::Open_Socket -- opens communications socket * + * IPXConnClass::Close_Socket -- closes the socket * + * IPXConnClass::Send_To -- sends the packet to the given address * + * IPXConnClass::Broadcast -- broadcasts the given packet * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include +//#include +#include +#include "ipxconn.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" + +#else + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 +#endif //WINSOCK_IPX + + + +/* +********************************* Globals *********************************** +*/ +unsigned short IPXConnClass::Socket; +int IPXConnClass::ConnectionNum; +ECBType * IPXConnClass::ListenECB; +IPXHeaderType * IPXConnClass::ListenHeader; +char * IPXConnClass::ListenBuf; +ECBType * IPXConnClass::SendECB; +IPXHeaderType * IPXConnClass::SendHeader; +char * IPXConnClass::SendBuf; +long IPXConnClass::Handler; +int IPXConnClass::Configured = 0; +int IPXConnClass::SocketOpen = 0; +int IPXConnClass::Listening = 0; +int IPXConnClass::PacketLen; + + +/*************************************************************************** + * IPXConnClass::IPXConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * address address of destination (NULL = no address) * + * id connection's unique numerical ID * + * name connection's name * + * extralen max size of app-specific extra bytes (optional) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXConnClass::IPXConnClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 2, // retry delta + -1, // max retries + 60, // timeout + extralen) // (currently, this is only used by the Global Channel) +{ + NetNumType net; + NetNodeType node; + + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + if (address) + Address = (*address); + ID = id; + strcpy (Name, name); + +#ifdef WINSOCK_IPX + Address.Get_Address(net,node); + memcpy(ImmediateAddress,node,6); + Immed_Set = 0; +#else + if ( !Winsock.Get_Connected() ) { + /*------------------------------------------------------------------------ + If our Address field is an actual address (ie NULL wasn't passed to the + constructor), pre-compute the ImmediateAddress value for the SendECB. + This allows pre-computing of the ImmediateAddress for all connections + created after Configure() is called. + ------------------------------------------------------------------------*/ + if (!Address.Is_Broadcast() && Configured==1) { + Address.Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + if (IPX_Get_Local_Target (net, node, Socket, ImmediateAddress)!=0) { + memcpy(ImmediateAddress,node,6); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(ImmediateAddress,node,6); + } + + Immed_Set = 1; + } + else { + memset (ImmediateAddress, 0, 6); + Immed_Set = 0; + } + } +#endif //WINSOCK_IPX +} /* end of IPXConnClass */ + + +/*************************************************************************** + * IPXConnClass::Init -- hardware-specific initialization routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Init (void) +{ + /*------------------------------------------------------------------------ + Invoke the parent's Init routine + ------------------------------------------------------------------------*/ + ConnectionClass::Init(); + +} /* end of Init */ + + +/*************************************************************************** + * IPXConnClass::Configure -- One-time initialization routine * + * * + * This routine sets up static members that are shared by all IPX * + * connections (ie those variables used by the Send/Listen/Broadcast * + * routines). * + * * + * INPUT: * + * socket socket ID for sending & receiving * + * conn_num local IPX Connection Number (0 = not logged in) * + * listen_ecb ptr to ECBType for listening * + * send_ecb ptr to ECBType for sending * + * listen_header ptr to IPXHeaderType for listening * + * send_header ptr to IPXHeaderType for sending * + * listen_buf ptr to buffer for listening * + * send_buf ptr to buffer for sending * + * handler_rm_ptr REAL-MODE pointer to event service routine * + * (high word = segment, low word = offset) * + * maxpacketlen max packet size to listen for * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - All pointers must be protected-mode pointers, but must point to * + * DOS real-mode memory (except the Handler segment/offset) * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Configure (unsigned short socket, int conn_num, + ECBType *listen_ecb, ECBType *send_ecb, IPXHeaderType *listen_header, + IPXHeaderType *send_header, char *listen_buf, char *send_buf, + long handler_rm_ptr, int maxpacketlen) +{ + /*------------------------------------------------------------------------ + Save the values passed in + ------------------------------------------------------------------------*/ + Socket = socket; + ConnectionNum = conn_num; + ListenECB = listen_ecb; + SendECB = send_ecb; + ListenHeader = listen_header; + SendHeader = send_header; + ListenBuf = listen_buf; + SendBuf = send_buf; + Handler = handler_rm_ptr; + PacketLen = maxpacketlen; + + Configured = 1; + +} /* end of Configure */ + + +/*************************************************************************** + * IPXConnClass::Start_Listening -- commands IPX to listen * + * * + * This routine may be used to start listening in polled mode (if the * + * ECB's Event_Service_Routine is NULL), or in interrupt mode; it's * + * up to the caller to fill the ECB in. If in polled mode, Listening * + * must be restarted every time a packet comes in. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - The ListenECB must have been properly filled in by the IPX Manager.* + * - Configure must be called before calling this routine. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Start_Listening(void) +{ +#ifdef WIN32 + +#ifdef WINSOCK_IPX + /* + ** Open the socket. + */ + if (!Open_Socket(Socket)) + return(false); + + /* + ** start listening on the socket. + */ + if ( PacketTransport->Start_Listening () ) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } + +#else + if (Winsock.Get_Connected ()) return (true); + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) + return(false); + + if (IPX_Start_Listening95()) { + Listening =1; + return (true); + } else { + Close_Socket(Socket); + return (false); + } +#endif //WINSOCK_IPX + +#else //WIN32 + + void *hdr_ptr; + unsigned long hdr_val; + void *buf_ptr; + unsigned long buf_val; + int rc; + + /*------------------------------------------------------------------------ + Don't do a thing unless we've been configured, and we're not listening. + ------------------------------------------------------------------------*/ + if (Configured==0 || Listening==1) { + return(0); + } + + /*------------------------------------------------------------------------ + Open the Socket + ------------------------------------------------------------------------*/ + if (!Open_Socket(Socket)) { + return(0); + } + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(ListenECB, 0, sizeof(ECBType)); + memset(ListenHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)ListenHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + + buf_val = (unsigned long)ListenBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in the ECB + ------------------------------------------------------------------------*/ + ListenECB->SocketNumber = Socket; + ListenECB->PacketCount = 2; + ListenECB->Packet[0].Address = hdr_ptr; + ListenECB->Packet[0].Length = sizeof(IPXHeaderType); + ListenECB->Packet[1].Address = buf_ptr; + ListenECB->Packet[1].Length = (unsigned short)PacketLen; + + ((long &)ListenECB->Event_Service_Routine) = Handler; + + /*------------------------------------------------------------------------ + Command IPX to listen + ------------------------------------------------------------------------*/ + rc = IPX_Listen_For_Packet(ListenECB); + if (rc!=0) { + Close_Socket(Socket); + return(0); + } + else { + Listening = 1; + return(1); + } + +#endif //WIN32 + +} /* end of Start_Listening */ + + +/*************************************************************************** + * IPXConnClass::Stop_Listening -- commands IPX to stop listen * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * - This routine MUST NOT be called if IPX is not listening already! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Stop_Listening(void) +{ +#ifdef WINSOCK_IPX + if ( PacketTransport ) PacketTransport->Stop_Listening(); + Listening = 0; + + // All done. + return(1); +#else + /*------------------------------------------------------------------------ + Don't do anything unless we're already Listening. + ------------------------------------------------------------------------*/ + if (Listening==0) { + return(0); + } + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Listening = 0; + return (true); + } else { + IPX_Shut_Down95(); + Close_Socket(Socket); + } + +#else //WIN32 + + /*------------------------------------------------------------------------ + Shut IPX down. + ------------------------------------------------------------------------*/ + IPX_Cancel_Event(ListenECB); + Close_Socket(Socket); + +#endif //WIN32 + + Listening = 0; + + /*------------------------------------------------------------------------ + All done. + ------------------------------------------------------------------------*/ + return(1); +#endif //WINSOCK_IPX + +} /* end of Stop_Listening */ + + +/*************************************************************************** + * IPXConnClass::Send -- sends a packet; invoked by SequencedConnection * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send(char *buf, int buflen, void *, int) +{ + /*------------------------------------------------------------------------ + Invoke our own Send_To routine, filling in our Address as the destination. + ------------------------------------------------------------------------*/ + if (Immed_Set) { + return(Send_To (buf, buflen, &Address, ImmediateAddress)); + } + else { + return(Send_To (buf, buflen, &Address, NULL)); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXConnClass::Open_Socket -- opens communications socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Open_Socket(unsigned short socket) +{ + int rc; +#ifdef WINSOCK_IPX + rc = PacketTransport->Open_Socket(socket); + + SocketOpen = rc; + return ( rc ); + +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 1; + return (true); + } + + SocketOpen = 0; + + /*------------------------------------------------------------------------ + Try to open a listen socket. The socket may have been left open by + a previously-crashed program, so ignore the state of the SocketOpen + flag for this call; use IPX to determine if the socket was already open. + ------------------------------------------------------------------------*/ + rc = IPX_Open_Socket(socket); + if (rc) { + + /*..................................................................... + If already open, close & reopen it + .....................................................................*/ + if (rc==IPXERR_SOCKET_ERROR) { +#ifdef WIN32 + WWDebugString ("Error -- Specified socket is already open"); +#endif //WIN32 + IPX_Close_Socket(socket); + rc = IPX_Open_Socket(socket); + } + + /*.................................................................. + Still can't open: return error + ..................................................................*/ + if (rc) { + return(0); + } + } + + SocketOpen = 1; + + return(1); +#endif //WINSOCK_IPX + +} /* end of Open_Socket */ + + +/*************************************************************************** + * IPXConnClass::Close_Socket -- closes the socket * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Calling this routine when the sockets aren't open may crash! * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +void IPXConnClass::Close_Socket(unsigned short socket) +{ +#ifdef WINSOCK_IPX + socket = socket; + PacketTransport->Close_Socket(); + SocketOpen = 0; +#else //WINSOCK_IPX + if (Winsock.Get_Connected()) { + SocketOpen = 0; + return; + } + + /*------------------------------------------------------------------------ + Never, ever, ever, under any circumstances whatsoever, close a socket + that isn't open. You'll regret it forever (or until at least until + you're through rebooting, which, if you're on a Pentium is the same + thing). + ------------------------------------------------------------------------*/ + if (SocketOpen==1) { + IPX_Close_Socket(socket); + } + + SocketOpen = 0; +#endif //WINSOCK_IPX +} /* end of Close_Socket */ + + +/*************************************************************************** + * IPXConnClass::Send_To -- sends the packet to the given address * + * * + * The "ImmediateAddress" field of the SendECB must be filled in with the * + * address of a bridge, or the node address of the destination if there * + * is no bridge. The NETX call to find this address will always crash * + * if NETX isn't loaded (ConnectionNum is 0), so this case is trapped & * + * prevented. * + * Also, if the address of this IPX connection is known when the * + * constructor is called, and Configure has been called, Get_Local_Target * + * is called to precompute the ImmediateAddress; this case is detected & * + * if the value is already computed, it's just memcpy'd over. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address Address to send to * + * immed ImmediateAddress value, NULL if none * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed) +{ +#ifdef WINSOCK_IPX + + immed = immed; + assert ( immed == NULL ); + PacketTransport->WriteTo ( (void*)buf, buflen, (void*) address ); + return (true); + +#else //WINSOCK_IPX + NetNumType net; + NetNodeType node; + int rc; + +#ifdef WIN32 + + unsigned char send_address[6]; + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return (true); + } + + if (immed) { + memcpy(send_address, immed, 6); +#ifdef FIXIT_DESTNET + // fixes DESTNET + address->Get_Address(net,node); +#else + // breaks DESTNET + memcpy(node, immed, 6); + memset (net, 0, sizeof(net) ); +#endif + } else { + address->Get_Address(net,node); + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get the + bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, &send_address[0]); + if (rc!=0) { + return(false); + } + } else { + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, and + just hope there's no network bridge in the path. + .....................................................................*/ + memcpy(send_address,node,6); + } + } + + return (IPX_Send_Packet95(&send_address[0], (unsigned char*)buf, buflen, (unsigned char*)net, (unsigned char*)node)); + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + + /*------------------------------------------------------------------------ + Get the bridge address + ------------------------------------------------------------------------*/ + if (immed) { + memcpy(SendECB->ImmediateAddress, immed, 6); + } + else { + address->Get_Address(net,node); + + /*..................................................................... + If the user is logged in & has a valid Novell Connection Number, get + the bridge address the "official" way + .....................................................................*/ + if (ConnectionNum != 0) { + rc = IPX_Get_Local_Target (net, node, Socket, + SendECB->ImmediateAddress); + if (rc!=0) { + return(0); + } + } + /*..................................................................... + Otherwise, use the destination node address as the ImmediateAddress, + and just hope there's no network bridge in the path. + .....................................................................*/ + else { + memcpy(SendECB->ImmediateAddress,node,6); + } + } + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + address->Get_Address(SendHeader); // fill in header addresses + SendHeader->DestNetworkSocket = Socket; // destination socket id + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) + Let_IPX_Breath(); + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX + +} /* end of Send_To */ + + +/*************************************************************************** + * IPXConnClass::Broadcast -- broadcasts the given packet * + * * + * INPUT: * + * socket desired socket ID number * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/16/1994 BR : Created. * + *=========================================================================*/ +int IPXConnClass::Broadcast(char *buf, int buflen) +{ +#ifdef WINSOCK_IPX + PacketTransport->Broadcast (buf, buflen); + return (true); + +#else //WINSOCK_IPX + +#ifdef WIN32 + + if (Winsock.Get_Connected()) { + Winsock.Write((void*)buf, buflen); + return(true); + } else { + return (IPX_Broadcast_Packet95((unsigned char*)buf, buflen)); + } + +#else //WIN32 + + void *hdr_ptr; + void *buf_ptr; + unsigned long hdr_val; + unsigned long buf_val; + + /*------------------------------------------------------------------------ + Clear the ECB & header + ------------------------------------------------------------------------*/ + memset(SendECB, 0, sizeof(ECBType)); + memset(SendHeader, 0, sizeof(IPXHeaderType)); + + /*------------------------------------------------------------------------ + Copy the message into the SendBuf + ------------------------------------------------------------------------*/ + memcpy (SendBuf,buf,buflen); + + /*------------------------------------------------------------------------ + Convert protected-mode ptrs to real-mode ptrs + ------------------------------------------------------------------------*/ + hdr_val = (unsigned long)SendHeader; + hdr_ptr = (void *)(((hdr_val & 0xffff0) << 12) | (hdr_val & 0x000f)); + buf_val = (unsigned long)SendBuf; + buf_ptr = (void *)(((buf_val & 0xffff0) << 12) | (buf_val & 0x000f)); + + /*------------------------------------------------------------------------ + Fill in ECB + ------------------------------------------------------------------------*/ + SendECB->SocketNumber = Socket; // my output socket + SendECB->PacketCount = 2; // 2 data areas + SendECB->Packet[0].Address = hdr_ptr; + SendECB->Packet[0].Length = sizeof(IPXHeaderType); + SendECB->Packet[1].Address = buf_ptr; + SendECB->Packet[1].Length = (unsigned short)buflen; + SendECB->ImmediateAddress[0] = 0xff; + SendECB->ImmediateAddress[1] = 0xff; + SendECB->ImmediateAddress[2] = 0xff; + SendECB->ImmediateAddress[3] = 0xff; + SendECB->ImmediateAddress[4] = 0xff; + SendECB->ImmediateAddress[5] = 0xff; + + /*------------------------------------------------------------------------ + Fill in outgoing header + ------------------------------------------------------------------------*/ + SendHeader->PacketType = 4; // 4 = IPX packet + SendHeader->DestNetworkNumber[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNumber[1] = 0xff; + SendHeader->DestNetworkNumber[2] = 0xff; + SendHeader->DestNetworkNumber[3] = 0xff; + SendHeader->DestNetworkNode[0] = 0xff; // 0xff = broadcast + SendHeader->DestNetworkNode[1] = 0xff; + SendHeader->DestNetworkNode[2] = 0xff; + SendHeader->DestNetworkNode[3] = 0xff; + SendHeader->DestNetworkNode[4] = 0xff; + SendHeader->DestNetworkNode[5] = 0xff; + SendHeader->DestNetworkSocket = Socket; // destination socket # + + /*------------------------------------------------------------------------ + Send the packet + ------------------------------------------------------------------------*/ + IPX_Send_Packet(SendECB); + + /*------------------------------------------------------------------------ + Wait for send to complete + ------------------------------------------------------------------------*/ + while (SendECB->InUse) { + Let_IPX_Breath(); + } + + if (SendECB->CompletionCode!=0) { + return(0); + } + else { + return(1); + } + +#endif //WIN32 +#endif //WINSOCK_IPX +} /* end of Broadcast */ + +/************************** end of ipxconn.cpp *****************************/ + diff --git a/code/redalert/ipxconn.h b/code/redalert/ipxconn.h new file mode 100644 index 0000000..3c96055 --- /dev/null +++ b/code/redalert/ipxconn.h @@ -0,0 +1,203 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for IPX communications. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * SequencedConnClass. It guarantees order of delivery of packets. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXCONN_H +#define IPXCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "ipxaddr.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Various useful enums: + .....................................................................*/ + enum IPXConnTag { + CONN_NAME_MAX = 40 // max # chars allowed for connection name + }; + + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXConnClass(int numsend, int numrecieve, int maxlen, + unsigned short magicnum, IPXAddressClass *address, int id, char *name, + int extralen = 0); + virtual ~IPXConnClass () {}; + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + The Configure function is for configuring all connections at once. + It's static because it doesn't apply to any specific connection, but + all of them. + .....................................................................*/ + static void Configure(unsigned short socket, + int conn_num, ECBType *listen_ecb, ECBType *send_ecb, + IPXHeaderType *listen_header, IPXHeaderType *send_header, + char *listen_buf, char *send_buf, long handler_rm_ptr, + int maxpacketlen); + + /*..................................................................... + These routines tell IPX to start listening for packets, and to stop + listening for packets. They're static because they affect all + connections at once (there's no way to turn listening on for only one + connection; it's all or nothing). + .....................................................................*/ + static int Start_Listening (void); + static int Stop_Listening (void); + + /*..................................................................... + The Destination IPX Address for this connection + .....................................................................*/ + IPXAddressClass Address; + + /*..................................................................... + The "Immediate" (Bridge) address for this connection, and a flag + telling if the address has been precomputed. + .....................................................................*/ + NetNodeType ImmediateAddress; + int Immed_Set; + + /*..................................................................... + Each IPX Connection can have a Name & Unique numerical ID + .....................................................................*/ + int ID; + char Name[CONN_NAME_MAX]; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + + /*..................................................................... + This is the overloaded Send routine declared in ConnectionClass, and + used in SequencedConnClass. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + /*..................................................................... + These are the routines that access IPX. Open_Socket & Close_Socket are + static because they're called by Start_Listening & Stop_Listening. + Send_To & Broadcast are static since they're direct interfaces to IPX, + and there's only one IPX instance running. + .....................................................................*/ + static int Open_Socket(unsigned short socket); + static void Close_Socket(unsigned short socket); + static int Send_To(char *buf, int buflen, IPXAddressClass *address, + NetNodeType immed); + static int Broadcast(char *buf, int buflen); + + /*..................................................................... + The socket ID for this connection + .....................................................................*/ + static unsigned short Socket; + + /*..................................................................... + User's local Connection # (0 = not logged in) + .....................................................................*/ + static int ConnectionNum; + + /*..................................................................... + This is a static version of MaxPacketLen, which is the size of the + app's packets, plus our internal CommHeaderType. It's used in the + Start_Listening routine. + .....................................................................*/ + static int PacketLen; + + /*..................................................................... + Variables for Listening (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *ListenECB; + static IPXHeaderType *ListenHeader; + static char *ListenBuf; + + /*..................................................................... + Variables for Sending (created by the IPXManagerClass, and passed + in via Init). All IPX connections share these buffers. + .....................................................................*/ + static ECBType *SendECB; + static IPXHeaderType *SendHeader; + static char *SendBuf; + + /*..................................................................... + This is a REAL-MODE pointer to the event-service-routine for IPX. + If it's 0, IPX will operate in polled mode. Otherwise, the high word + must contain the segment, and the low word must contain the offset. + CS will be the high word value when the routine is called. (Requiring + the segment/offset to be computed by the caller gives the caller + control over CS.) + .....................................................................*/ + static long Handler; + + /*..................................................................... + This status flag tells us if Configure() has been called or not. + .....................................................................*/ + static int Configured; + + /*..................................................................... + This status flag tells us if the socket has been opened or not. + .....................................................................*/ + static int SocketOpen; + + /*..................................................................... + This status flag tells us if Start_Listening() has been called or not. + .....................................................................*/ + static int Listening; +}; + +#endif + +/*************************** end of ipxconn.h ******************************/ diff --git a/code/redalert/ipxgconn.cpp b/code/redalert/ipxgconn.cpp new file mode 100644 index 0000000..4186cb1 --- /dev/null +++ b/code/redalert/ipxgconn.cpp @@ -0,0 +1,531 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXGCONN.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : July 6, 1995 [BRR] * + *-------------------------------------------------------------------------* + * Functions: * + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * IPXGlobalConnClass::~IPXGlobalConnClass -- class destructor * + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue* + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue* + * IPXGlobalConnClass::Send -- sends a packet * + * IPXGlobalConnClass::Service_Receive_Queue -- services receive queue * + * IPXGlobalConnClass::Set_Bridge -- Sets up connection to cross a bridge* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include +#include "ipxgconn.h" + + +/*************************************************************************** + * IPXGlobalConnClass::IPXGlobalConnClass -- class constructor * + * * + * This routine chains to the parent constructor, but it adjusts the size * + * of the packet by the added bytes in the GlobalHeaderType structure. * + * This forces the parent classes to allocate the proper sized PacketBuf * + * for outgoing packets, and to set MaxPacketLen to the proper value. * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the receive queue * + * maxlen max length of an application packet * + * product_id unique ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXGlobalConnClass::IPXGlobalConnClass (int numsend, int numreceive, + int maxlen, unsigned short product_id) : + IPXConnClass (numsend, numreceive, + maxlen + sizeof(GlobalHeaderType) - sizeof(CommHeaderType), + GLOBAL_MAGICNUM, // magic number for this connection + NULL, // IPX Address (none) + 0, // Connection ID + "", // Connection Name + sizeof (IPXAddressClass)) // extra storage for the sender's address +{ + int i; + + ProductID = product_id; + IsBridge = 0; + + for (i = 0; i < 4; i++) { + LastPacketID[i] = 0xffffffff; + } + LastRXIndex = 0; + +} /* end of IPXGlobalConnClass */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a GlobalHeaderType and * + * queues the resulting packet into the Send Queue. The packet's * + * MagicNumber, Code, PacketID, destination Address and ProductID are set * + * here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * address address to send the packet to (NULL = Broadcast) * + * ack_req 1 = ACK is required for this packet; 0 = isn't * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req) +{ + IPXAddressClass dest_addr; + + /*------------------------------------------------------------------------ + Store the packet's Magic Number + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.MagicNumber = MagicNum; + + /*------------------------------------------------------------------------ + If this is a ACK-required packet, sent to a specific system, mark it as + ACK-required; otherwise, mark as no-ACK-required. + ------------------------------------------------------------------------*/ + if (ack_req && address != NULL) { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_ACK; + } + else { + ((GlobalHeaderType *)PacketBuf)->Header.Code = PACKET_DATA_NOACK; + } + + /*------------------------------------------------------------------------ + Fill in the packet ID. This will have very limited meaning; it only + allows us to determine if an ACK packet we receive later goes with this + packet; it doesn't let us detect re-sends of other systems' packets. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->Header.PacketID = Queue->Send_Total(); + + /*------------------------------------------------------------------------ + Set the product ID for this packet. + ------------------------------------------------------------------------*/ + ((GlobalHeaderType *)PacketBuf)->ProductID = ProductID; + + /*------------------------------------------------------------------------ + Set this packet's destination address. If no address is specified, use + a Broadcast address (which IPXAddressClass's default constructor creates). + ------------------------------------------------------------------------*/ + if (address != NULL) { + dest_addr = (*address); + } + + /*------------------------------------------------------------------------ + Copy the application's data + ------------------------------------------------------------------------*/ + memcpy(PacketBuf + sizeof(GlobalHeaderType), buf, buflen); + + /*------------------------------------------------------------------------ + Queue it, along with the destination address + ------------------------------------------------------------------------*/ + return(Queue->Queue_Send(PacketBuf,buflen + sizeof(GlobalHeaderType), + &dest_addr, sizeof (IPXAddressClass))); + +} /* end of Send_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes GlobalHeaderType) * + * buflen length of buffer to process * + * address the address of the sender (the IPX Manager class must * + * extract this from the IPX Header of the received packet.) * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Receive_Packet (void * buf, int buflen, + IPXAddressClass *address) +{ + GlobalHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + GlobalHeaderType *entry_data; // ptr to queue entry data + GlobalHeaderType ackpacket; // ACK packet to send + int i; + int resend; + + /*------------------------------------------------------------------------ + Check the magic # + ------------------------------------------------------------------------*/ + packet = (GlobalHeaderType *)buf; + if (packet->Header.MagicNumber!=MagicNum) { + return(0); + } + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Header.Code) { + //..................................................................... + // DATA_ACK: Check for a resend by comparing the source address & + // ID of this packet with our last 4 received packets. + // Send an ACK for the packet, regardless of whether it's a resend + // or not. + //..................................................................... + case PACKET_DATA_ACK: + { + //.................................................................. + // Check for a resend + //.................................................................. + resend = 0; + for (i = 0; i < 4; i++) { + if ((unsigned int)i >= Queue->Receive_Total()) { + break; + } + if ((*address)==LastAddress[i] && + packet->Header.PacketID==LastPacketID[i]) { + resend = 1; + break; + } + } + + bool send_ack = true; + + //.................................................................. + // If it's not a resend, queue it; then, record the sender's address + // & the packet ID for future resend detection. + //.................................................................. + if (!resend) { + if (Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass))) { + LastAddress[LastRXIndex] = (*address); + LastPacketID[LastRXIndex] = packet->Header.PacketID; + LastRXIndex++; + if (LastRXIndex >= 4) { + LastRXIndex = 0; + } + }else{ + //.................................................................. + // Don't send an ack if we didn't have room to store the packet. + //.................................................................. + send_ack = false; + } + } + + + //.................................................................. + // Send an ACK for this packet + //.................................................................. + if (send_ack) { + ackpacket.Header.MagicNumber = MagicNum; + ackpacket.Header.Code = PACKET_ACK; + ackpacket.Header.PacketID = packet->Header.PacketID; + ackpacket.ProductID = ProductID; + Send ((char *)&ackpacket, sizeof(GlobalHeaderType), + address, sizeof(IPXAddressClass)); + } + + + break; + } + /*..................................................................... + DATA_NOACK: Queue this message, along with the sender's address. + Don't bother checking for a Re-Send, since the other system will only + send this packet once. + .....................................................................*/ + case PACKET_DATA_NOACK: + Queue->Queue_Receive (buf, buflen, address, sizeof(IPXAddressClass)); + break; + + /*..................................................................... + ACK: If this ACK is for any of my packets, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: + for (i = 0; i < Queue->Num_Send(); i++) { + /*............................................................... + Get queue entry ptr + ...............................................................*/ + send_entry = Queue->Get_Send(i); + + /*............................................................... + If ptr is valid, get ptr to its data + ...............................................................*/ + entry_data = (GlobalHeaderType *)(send_entry->Buffer); + + /*............................................................... + If ACK is for this entry, mark it + ...............................................................*/ + if (packet->Header.PacketID==entry_data->Header.PacketID && + entry_data->Header.Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + break; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + } + + return(1); + +} /* end of Receive_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * address filled in with sender's address * + * product_id filled in with sender's ProductID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + GlobalHeaderType *packet; + int packetlen; // size of received packet + + /*------------------------------------------------------------------------ + Return if nothing to do + ------------------------------------------------------------------------*/ + if (Queue->Num_Receive() == 0) { + return(0); + } + + /*------------------------------------------------------------------------ + Get ptr to the next available entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Get_Receive(0); + + /*------------------------------------------------------------------------ + Read it if it's un-read + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + + /*..................................................................... + Mark as read + .....................................................................*/ + rec_entry->IsRead = 1; + + /*..................................................................... + Copy data packet + .....................................................................*/ + packet = (GlobalHeaderType *)(rec_entry->Buffer); + packetlen = rec_entry->BufLen - sizeof(GlobalHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(GlobalHeaderType), packetlen); + } + (*buflen) = packetlen; + (*product_id) = packet->ProductID; + (*address) = (*((IPXAddressClass *)(rec_entry->ExtraBuffer))); + + return(1); + } + + return(0); + +} /* end of Get_Packet */ + + +/*************************************************************************** + * IPXGlobalConnClass::Send -- sends a packet * + * * + * This routine gets invoked by NonSequencedConn, when it's processing * + * the Send & Receive Queues. The buffer provided will already have the * + * GlobalHeaderType header embedded in it. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * extrabuf extra buffer to send * + * extralen length of extra buffer * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Send(char *buf, int buflen, void *extrabuf, int ) +{ + IPXAddressClass *addr; + int rc; + + /*------------------------------------------------------------------------ + Extract the packet's embedded IPX address + ------------------------------------------------------------------------*/ + addr = (IPXAddressClass *)extrabuf; + + /*------------------------------------------------------------------------ + If it's a broadcast address, broadcast it + ------------------------------------------------------------------------*/ + if (addr->Is_Broadcast()) { + return(Broadcast (buf, buflen)); + } + + /*------------------------------------------------------------------------ + Otherwise, send it + ------------------------------------------------------------------------*/ + else { + if (IsBridge && !memcmp (addr, BridgeNet, 4)) { + rc = Send_To (buf, buflen, addr, BridgeNode); + } + else { + rc = Send_To (buf, buflen, addr, NULL); + } + return (rc); + } + +} /* end of Send */ + + +/*************************************************************************** + * IPXGlobalConnClass::Service_Receive_Queue -- services the receive queue * + * * + * This routine is necessary because the regular ConnectionClass checks * + * for sequential packet ID's before removing them from the receive queue; * + * this class cannot do that. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXGlobalConnClass::Service_Receive_Queue (void) +{ + int i; + ReceiveQueueType *rec_entry; // ptr to receive entry header + + //------------------------------------------------------------------------ + // Remove all dead packets: If a packet's been read, throw it away. + //------------------------------------------------------------------------ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + Queue->UnQueue_Receive(NULL,NULL,i,NULL,NULL); + i--; + } + } + + return (1); + +} /* end of Service_Receive_Queue */ + + +/*************************************************************************** + * Set_Bridge -- Sets up connection to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXGlobalConnClass::Set_Bridge(NetNumType bridge) +{ +#ifdef WINSOCK_IPX + + if (Configured) { + bridge = bridge; + IsBridge = 0; + } + +#else //WINSOCK_IPX + if (Configured) { + memcpy (BridgeNet, bridge, 4); + memset (BridgeNode, 0xff, 6); + + if (IPX_Get_Local_Target (BridgeNet, BridgeNode, Socket, BridgeNode)==0) { + IsBridge = 1; + } + else { + IsBridge = 0; + } + } +#endif //WINSOCK_IPX + +} /* end of Set_Bridge */ + + +/************************** end of ipxgconn.cpp ****************************/ diff --git a/code/redalert/ipxgconn.h b/code/redalert/ipxgconn.h new file mode 100644 index 0000000..7215cd6 --- /dev/null +++ b/code/redalert/ipxgconn.h @@ -0,0 +1,203 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXGCONN.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXGCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 11, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class is a special type of IPX Connection. It can talk to more * + * than one system at a time. It can Broadcast packets to all systems, * + * or send a packet to one individual system. The packets it sends to * + * individual systems can be DATA_NOACK or DATA_ACK packets, but the * + * packets broadcast have to be DATA_NOACK packets. This class is for * + * only the crudest "Who-are-you" type of network communications. Once * + * the IPX Address of another system is identified, a "real" IPX * + * Connection should be created, & further communications done through it. * + * * + * This means that the packet ID field no longer can be used to detect * + * resends, since the receive queue may receive a lot more packets than * + * we send out. So, re-sends cannot be detected; the application must be * + * designed so that it can handle multiple copies of the same packet. * + * * + * The class uses a slightly different header from the normal Connections; * + * this header includes the ProductID of the sender, so multiple * + * applications can share the same socket, but by using different product * + * ID's, can distinguish between their own packets & others. * + * * + * Because of this additional header, and because Receive ACK/Retry logic * + * is different (we can't detect resends), the following routines are * + * overloaded: * + * Send_Packet: must embed the product ID into the packet header * + * Receive_Packet: must detect resends via the LastAddress & * + * LastPacketID arrays. This class doesn't ACK * + * packets until Service_Receive_Queue is called; * + * the parent classes ACK the packet when it's * + * received. * + * Get_Packet: extracts the product ID from the header; * + * doesn't care about reading packets in order * + * Send is capable of broadcasting the packet, or sending * + * to a specific address * + * * + * This class also has the ability to cross a Novell Network Bridge. * + * You provide the class with the bridge address, and subsequent * + * broadcasts are sent across the bridge as well as to the local network. * + * Address-specific sends contain the destination network's address, * + * so they cross a bridge automatically; it's just the broadcasts * + * that need to know the bridge address. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXGLOBALCONN_H +#define IPXGLOBALCONN_H + + +#include "ipxconn.h" + + +/* +********************************** Defines ********************************** +*/ +//--------------------------------------------------------------------------- +// This is the header for Global Connection messages. It includes the usual +// "standard" header that the other connections do; but it also includes an +// IPX address field, so the application can get the address of the sender +// of this message. This address field must be provided in by the IPX +// Connection Manager class, when it calls this class's Receive_Packet +// function. +//--------------------------------------------------------------------------- +typedef struct { + CommHeaderType Header; + unsigned short ProductID; +} GlobalHeaderType; + + +/* +***************************** Class Declaration ***************************** +*/ +class IPXGlobalConnClass : public IPXConnClass +{ + //------------------------------------------------------------------------ + // Public Interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Some useful enums: + //..................................................................... + enum GlobalConnectionEnum { + //.................................................................. + // This is the magic number for all Global Connections. Having the + // same magic number across products lets us ID different products + // on the net. If you change the fundamental connection protocol, + // you must use a different magic number. + //.................................................................. + //GLOBAL_MAGICNUM = 0x1234, // used for C&C 1 + GLOBAL_MAGICNUM = 0x1235, // used for C&C 0 + //.................................................................. + // These are the values used for the ProductID field in the Global + // Message structure. It also should be the Magic Number used for + // the private connections within that product. + // This list should be continually updated & kept current. Never + // ever ever use an old product ID for your product! + //.................................................................. + COMMAND_AND_CONQUER = 0xaa01, + COMMAND_AND_CONQUER0 = 0xaa00 + }; + + //..................................................................... + // Constructor/destructor. + //..................................................................... + IPXGlobalConnClass (int numsend, int numrecieve, int maxlen, + unsigned short product_id); + virtual ~IPXGlobalConnClass () {}; + + //..................................................................... + // Send/Receive routines. + //..................................................................... + virtual int Send_Packet (void * buf, int buflen, + IPXAddressClass *address, int ack_req); + virtual int Receive_Packet (void * buf, int buflen, + IPXAddressClass *address); + virtual int Get_Packet (void * buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id); + + //..................................................................... + // This is for telling the connection it can cross a bridge. + //..................................................................... + void Set_Bridge (NetNumType bridge); + + //..................................................................... + // The Product ID for this product. + //..................................................................... + unsigned short ProductID; + + //..................................................................... + // This describes the address of a bridge we have to cross. This class + // supports crossing only one bridge. Storing the bridge's network + // number allows us to obtain its local target address only once, then + // re-use it. + //..................................................................... + NetNumType BridgeNet; + NetNodeType BridgeNode; + int IsBridge; + + //------------------------------------------------------------------------ + // Protected Interface + //------------------------------------------------------------------------ + protected: + + //..................................................................... + // This is the overloaded Send routine declared in ConnectionClass, and + // used in SequencedConnClass. This special version sends to the address + // stored in the extra buffer within the Queue. + //..................................................................... + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); + + //..................................................................... + // This routine is overloaded from SequencedConnClass, because the + // Global Connection needs to ACK its packets differently from the + // other connections. + //..................................................................... + virtual int Service_Receive_Queue (void); + + private: + //..................................................................... + // Since we can't detect resends by using the PacketID (since we're + // receiving packets from a variety of sources, all using different + // ID's), we'll have to remember the last 'n' packet addresses & id's + // for comparison purposes. + // Note that, if network traffic is heavy, it's still possible for an + // app to receive the same packet twice! + //..................................................................... + IPXAddressClass LastAddress[4]; // array of last 4 addresses + unsigned long LastPacketID[4]; // array of last 4 packet ID's + int LastRXIndex; // index of next avail pos +}; + +#endif + +/*************************** end of ipxgconn.h *****************************/ diff --git a/code/redalert/ipxmgr.cpp b/code/redalert/ipxmgr.cpp new file mode 100644 index 0000000..34b8956 --- /dev/null +++ b/code/redalert/ipxmgr.cpp @@ -0,0 +1,2066 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXMGR.CPP 3 10/13/97 2:20p Steve_t $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : May 4, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * IPXManagerClass::IPXManagerClass -- class constructor * + * IPXManagerClass::~IPXManagerClass -- class destructor * + * IPXManagerClass::Init -- initialization routine * + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * IPXManagerClass::Create_Connection -- creates a new connection * + * IPXManagerClass::Delete_Connection -- deletes a connection * + * IPXManagerClass::Num_Connections -- gets the # of connections * + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * IPXManagerClass::Connection_Name -- gets name for given connection * + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * IPXManagerClass::Connection_Index -- gets given connection's index * + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * IPXManagerClass::Get_Private_Message -- Polls Private Message queue * + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * IPXManagerClass::Global_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Global_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Private_Num_Send -- gets # entries in send queue * + * IPXManagerClass::Private_Num_Receive -- gets # entries in recv queue * + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include +//#include PG +//#include PG +#include "ipxmgr.h" +#include "wwlib32.h" // to enable mono output + +#ifdef WINSOCK_IPX + +#include "WSProto.h" +#include "WSPIPX.h" + +#else //WINSOCK_IPX + +#include "ipx95.h" +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +// Turn off "expression is not meaningful". +//#pragma warning 628 9 + +//#include "WolDebug.h" + +/*************************************************************************** + * IPXManagerClass::IPXManagerClass -- class constructor * + * * + * INPUT: * + * glb_maxlen Global Channel maximum packet length * + * pvt_maxlen Private Channel maximum packet length * + * socket socket ID to use * + * product_id a unique numerical ID for this product * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::IPXManagerClass (int glb_maxlen, int pvt_maxlen, + int glb_num_packets, int pvt_num_packets, unsigned short socket, + unsigned short product_id) +{ + int i; +#ifdef WINSOCK_IPX + /* + ** Find out if Packet protocol services are available through Winsock. + */ + if ( PacketTransport ) { + delete PacketTransport; + PacketTransport = NULL; + } + PacketTransport = new WinsockInterfaceClass; + assert ( PacketTransport != NULL ); + + if ( PacketTransport->Init() ){ + IPXStatus = 1; + }else{ + IPXStatus = 0; + } + delete PacketTransport; + PacketTransport = NULL; + +#else //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Initialize data members + //------------------------------------------------------------------------ + //........................................................................ + // IPXStatus = 1 if IPX is installed, 0 if not + //........................................................................ + if (IPX_SPX_Installed()==0) { + IPXStatus = 0; + } + else { + IPXStatus = 1; + } +#endif //WINSOCK_IPX + + //........................................................................ + // Set listening state flag to off + //........................................................................ + Listening = 0; + + //........................................................................ + // No memory has been alloc'd yet + //........................................................................ + RealMemAllocd = 0; + + //........................................................................ + // Set max packet sizes, for allocating real-mode memory + //........................................................................ + Glb_MaxPacketLen = glb_maxlen; + Glb_NumPackets = glb_num_packets; + Pvt_MaxPacketLen = pvt_maxlen; + Pvt_NumPackets = pvt_num_packets; + + //........................................................................ + // Save the app's product ID + //........................................................................ + ProductID = product_id; + + //........................................................................ + // Save our socket ID number + //........................................................................ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + + //------------------------------------------------------------------------ + // Get the user's IPX local connection number + //------------------------------------------------------------------------ + ConnectionNum = 0; +#ifndef WINSOCK_IPX + if (IPXStatus) { + ConnectionNum = IPX_Get_Connection_Number(); + } +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Init connection states + //------------------------------------------------------------------------ + NumConnections = 0; + CurConnection = 0; + for (i = 0; i < CONNECT_MAX; i++) { + Connection[i] = 0; + } + GlobalChannel = 0; + + SendOverflows = 0; + ReceiveOverflows = 0; + BadConnection = CONNECTION_NONE; + + //------------------------------------------------------------------------ + // Init timing parameters + //------------------------------------------------------------------------ + RetryDelta = 2; // 2 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 60; // report bad connection after 1 second + +} /* end of IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::~IPXManagerClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +IPXManagerClass::~IPXManagerClass() +{ + int i; + + //------------------------------------------------------------------------ + // Stop all IPX events + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free all protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + //------------------------------------------------------------------------ + // Free all real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 +#ifndef WINSOCK_IPX +//PG Unload_IPX_Dll(); +#endif +#endif +#endif +} /* end of ~IPXManagerClass */ + + +/*************************************************************************** + * IPXManagerClass::Init -- initialization routine * + * * + * This routine allocates memory, & initializes variables * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Init() +{ + int i; + + if (Session.Type != GAME_INTERNET) { + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Stop Listening + //------------------------------------------------------------------------ + if (Listening) { + IPXConnClass::Stop_Listening(); + Listening = 0; + } + + //------------------------------------------------------------------------ + // Free Real-mode memory + //------------------------------------------------------------------------ + if (RealMemAllocd) { + Free_RealMode_Mem(); + RealMemAllocd = 0; + } + + } else { + /* + ** Pretend IPX is available for Internet games whether it is or not + */ + IPXStatus = 1; + } + + //------------------------------------------------------------------------ + // Free protected-mode memory + //------------------------------------------------------------------------ + if (GlobalChannel) { + delete GlobalChannel; + GlobalChannel = 0; + } + for (i = 0; i < NumConnections; i++) { + delete Connection[i]; + Connection[i] = 0; + } + NumConnections = 0; + + if (Session.Type != GAME_INTERNET) { + //------------------------------------------------------------------------ + // Allocate real-mode memory + //------------------------------------------------------------------------ + if (!Alloc_RealMode_Mem()) return(0); + RealMemAllocd = 1; + } + + //------------------------------------------------------------------------ + // Allocate the Global Channel + //------------------------------------------------------------------------ + GlobalChannel = new IPXGlobalConnClass (Glb_NumPackets, Glb_NumPackets, + Glb_MaxPacketLen, ProductID); + if (!GlobalChannel) { + return(0); + } + GlobalChannel->Init(); + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + + //------------------------------------------------------------------------ + // Configure the IPX Connections + //------------------------------------------------------------------------ + IPXConnClass::Configure(Socket, ConnectionNum, ListenECB, SendECB, + FirstHeaderBuf, SendHeader, FirstDataBuf, SendBuf, Handler, PacketLen); + + //------------------------------------------------------------------------ + // Start Listening + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (!IPXConnClass::Start_Listening()) return(0); + } + Listening = 1; + + return(1); + +} /* end of Init */ + + +/*************************************************************************** + * IPXManagerClass::Is_IPX -- tells if IPX is installed or not * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = IPX is installed; 0 = isn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Is_IPX(void) +{ + return(IPXStatus); + +} /* end of Is_IPX */ + + +/*************************************************************************** + * IPXManagerClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters for all existing connections, and * + * all connections created from now on. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + int i; + + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + if (GlobalChannel) { + GlobalChannel->Set_Retry_Delta (RetryDelta); + GlobalChannel->Set_Max_Retries (MaxRetries); + GlobalChannel->Set_TimeOut (Timeout); + } + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Set_Retry_Delta (RetryDelta); + Connection[i]->Set_Max_Retries (MaxRetries); + Connection[i]->Set_TimeOut (Timeout); + } + +} /* end of Set_Timing */ + + +/*************************************************************************** + * IPXManagerClass::Create_Connection -- creates a new connection * + * * + * INPUT: * + * id application-specific numerical ID for this connection * + * node ptr to IPXNodeIDType (name & address) * + * address IPX address for this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * Never create a connection with an 'id' of -1. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Create_Connection(int id, char *name, + IPXAddressClass *address) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no more room + //------------------------------------------------------------------------ + if (NumConnections==CONNECT_MAX) { + return(0); + } + + //------------------------------------------------------------------------ + // Create new connection + //------------------------------------------------------------------------ + Connection[NumConnections] = new IPXConnClass(Pvt_NumPackets, + Pvt_NumPackets, Pvt_MaxPacketLen, ProductID, address, id, name); + if (!Connection[NumConnections]) { + return(0); + } + + Connection[NumConnections]->Init (); + Connection[NumConnections]->Set_Retry_Delta (RetryDelta); + Connection[NumConnections]->Set_Max_Retries (MaxRetries); + Connection[NumConnections]->Set_TimeOut (Timeout); + + NumConnections++; + + return(1); + +} /* end of Create_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Delete_Connection -- deletes a connection * + * * + * INPUT: * + * id ID of connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Delete_Connection(int id) +{ + int i,j; + + //------------------------------------------------------------------------ + // Error if IPX not installed + //------------------------------------------------------------------------ + if (!IPXStatus) { + return(0); + } + + //------------------------------------------------------------------------ + // Error if no connections to delete + //------------------------------------------------------------------------ + if (NumConnections==0) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop through all connections + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + //..................................................................... + // If a match, delete it + //..................................................................... + if (Connection[i]->ID==id) { + delete Connection[i]; + + //.................................................................. + // Move array elements back one index + //.................................................................. + for (j = i; j < NumConnections - 1; j++) { + Connection[j] = Connection[j+1]; + } + + //.................................................................. + // Adjust counters + //.................................................................. + NumConnections--; + if (CurConnection >= NumConnections) + CurConnection = 0; + return(1); + } + } + + //------------------------------------------------------------------------ + // No match; error + //------------------------------------------------------------------------ + return(0); + +} /* end of Delete_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Num_Connections -- gets the # of connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of connections * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Num_Connections(void) +{ + return(NumConnections); + +} /* end of Num_Connections */ + + +/*************************************************************************** + * IPXManagerClass::Connection_ID -- gets the given connection's ID * + * * + * INPUT: * + * index index of connection to retrieve * + * * + * OUTPUT: * + * ID for that connection, CONNECTION_NONE if invalid index * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(Connection[index]->ID); + } + else { + return(CONNECTION_NONE); + } +} /* end of Connection_ID */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Name -- retrieves name for given connection * + * * + * INPUT: * + * id ID of connection to get name of * + * * + * OUTPUT: * + * ptr to connection's name, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +char *IPXManagerClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(Connection[i]->Name); + } + } + + return(NULL); + +} /* end of Connection_Name */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Address -- retrieves connection's address * + * * + * INPUT: * + * id ID of connection to get address for * + * * + * OUTPUT: * + * pointer to IXPAddressClass, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/19/1995 BR : Created. * + *=========================================================================*/ +IPXAddressClass * IPXManagerClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(&Connection[i]->Address); + } + } + + return(NULL); + +} /* end of Connection_Address */ + + +/*************************************************************************** + * IPXManagerClass::Connection_Index -- gets given connection's index * + * * + * INPUT: * + * ID to retrieve index for * + * * + * OUTPUT: * + * index for this connection, CONNECTION_NONE if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->ID==id) { + return(i); + } + } + + return(CONNECTION_NONE); + +} /* end of Connection_Index */ + + +/*************************************************************************** + * IPXManagerClass::Set_Connection_Parms -- sets connection's name & id * + * * + * INPUT: * + * index connection index * + * id new connection ID * + * name new connection name * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Connection_Parms(int index, int id, char *name) +{ + if (index >= NumConnections) + return; + + Connection[index]->ID = id; + strcpy(Connection[index]->Name,name); + +} /* end of Set_Connection_Parms */ + + +/*************************************************************************** + * IPXManagerClass::Send_Global_Message -- sends a Global Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buf * + * ack_req 1 = ACK required; 0 = no ACK required * + * address address to send to; NULL = broadcast * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Global_Message(void *buf, int buflen, + int ack_req, IPXAddressClass *address) +{ + int rc; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + rc = GlobalChannel->Send_Packet (buf, buflen, address, ack_req); + if (!rc) { + SendOverflows++; + } + + return(rc); + +} /* end of Send_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Global_Message -- polls the Global Message queue * + * * + * INPUT: * + * buf buffer to store received packet * + * buflen length of data placed in 'buf' * + * address IPX address of sender * + * product_id product ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Global_Message(void *buf, int *buflen, + IPXAddressClass *address, unsigned short *product_id) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) return(0); + + return(GlobalChannel->Get_Packet (buf, buflen, address, product_id)); + +} /* end of Get_Global_Message */ + + +/*************************************************************************** + * IPXManagerClass::Send_Private_Message -- Sends a Private Message * + * * + * INPUT: * + * buf buffer to send * + * buflen length of 'buf' * + * conn_id connection ID to send to (CONNECTION_NONE = all) * + * ack_req 1 = ACK required; 0 = no ACK required * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Send_Private_Message(void *buf, int buflen, int ack_req, + int conn_id) +{ + int i; // loop counter + int connect_idx; // index of channel to send to, if specified + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Send the message to all connections + //------------------------------------------------------------------------ + if (conn_id==CONNECTION_NONE) { + //..................................................................... + // Check for room in all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() == + Connection[i]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + } + + //..................................................................... + // Send packet to all connections + //..................................................................... + for (i = 0; i < NumConnections; i++) { + Connection[i]->Send_Packet (buf, buflen, ack_req); + } + return(1); + } + + //------------------------------------------------------------------------ + // Send the message to the specified connection + //------------------------------------------------------------------------ + else { + connect_idx = Connection_Index (conn_id); + if (connect_idx == CONNECTION_NONE) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Check for room in the connection + //..................................................................... + if (Connection[connect_idx]->Queue->Num_Send() == + Connection[connect_idx]->Queue->Max_Send()) { + SendOverflows++; + return(0); + } + + //..................................................................... + // Send the packet to that connection + //..................................................................... + Connection[connect_idx]->Send_Packet (buf, buflen, ack_req); + return(1); + } + +} /* end of Send_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Get_Private_Message -- Polls the Private Message queue * + * * + * INPUT: * + * buf buffer to store incoming packet * + * buflen length of data placed in 'buf' * + * conn_id filled in with connection ID of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Private_Message(void *buf, int *buflen, int *conn_id) +{ + int i; + int rc; + int c_id; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // Safety check: ensure CurConnection is in range. + //------------------------------------------------------------------------ + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //------------------------------------------------------------------------ + // Scan all connections for a received packet, starting with 'CurConnection' + //------------------------------------------------------------------------ + for (i = 0; i < NumConnections; i++) { + + //..................................................................... + // Check this connection for a packet + //..................................................................... + rc = Connection[CurConnection]->Get_Packet (buf, buflen); + c_id = Connection[CurConnection]->ID; + + //..................................................................... + // Increment CurConnection to the next connection index + //..................................................................... + CurConnection++; + if (CurConnection >= NumConnections) { + CurConnection = 0; + } + + //..................................................................... + // If we got a packet, return the connection ID + //..................................................................... + if (rc) { + (*conn_id) = c_id; + return(1); + } + } + + return(0); + +} /* end of Get_Private_Message */ + + +/*************************************************************************** + * IPXManagerClass::Service -- main polling routine for IPX Connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Service(void) +{ + int rc = 1; + int i; + CommHeaderType *packet; + int packetlen; + IPXAddressClass address; + +#ifdef WINSOCK_IPX + + + unsigned char temp_receive_buffer[1024]; + int temp_receive_buffer_len; + int temp_address_len; + + + char temp_address [128]; + + if ( PacketTransport ) { + + do { + temp_receive_buffer_len = sizeof (temp_receive_buffer); + temp_address_len = sizeof (temp_address); + packetlen = PacketTransport->Read ( temp_receive_buffer, temp_receive_buffer_len, temp_address, temp_address_len ); + if ( packetlen ) { + CurDataBuf = (char*)temp_receive_buffer; + address = *((IPXAddressClass*) temp_address); + + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + /* + ** Put the packet in the Global Queue + */ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + + /* + ** Find the Private Queue that this packet is for + */ + bool found_address = false; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + found_address = true; + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + if( Session.Type == GAME_INTERNET ) + { + /* + ** This packet came from an unknown source. If it looks like one of our players + ** packets then it might be from a player whos IP has changed. + */ + if (!found_address) { + if (packet->Code == ConnectionClass::PACKET_DATA_NOACK){ + /* + ** Magic number and packet code are valid. It's probably a C&C packet. + */ + EventClass *event = (EventClass*) (((char*) packet) + sizeof (CommHeaderType)); + + /* + ** If this is a framesync packet then grab the address and match it to an existing player. + */ + if (event->Type == EventClass::FRAMESYNC) { + int id = event->ID; + + assert (id != PlayerPtr->ID); + for ( int i=1 ; iPlayer.ID == id) { + + int iConnectionIndex = Connection_Index(id); + if( iConnectionIndex != CONNECTION_NONE ) // (else Create_Connections() has not yet been called) + { + /* + ** Found a likely candidate. Update his address. It should be OK to drop this + ** packet since it's a framesync packet and will will pick up the next one. + */ + Session.Players[i]->Address = address; + Connection[iConnectionIndex]->Address = address; + } + break; + } + } + } + } + } + } + } + } + } + + } while (packetlen); + + } + + + + +#else //WINSOCK_IPX + +#ifdef WIN32 + + unsigned char temp_receive_buffer[1024]; + int recv_length; + + if (Winsock.Get_Connected() || Special.IsFromWChat) { + + if (!Winsock.Get_Connected()) return (0); + + /* + ** This is an internet connection so get the packets from winsock + */ + while ((recv_length = Winsock.Read(temp_receive_buffer, 1024))!=0) { + + CurHeaderBuf = NULL; + CurDataBuf = (char*)&temp_receive_buffer[0]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = recv_length; + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + + } + } else { +#if(0)//PG + while (IPX_Get_Outstanding_Buffer95(&temp_receive_buffer[0])) { + + CurHeaderBuf = (IPXHEADER*)&temp_receive_buffer[0]; + CurDataBuf = (char*)&temp_receive_buffer[sizeof(IPXHeaderType)]; + + /*..................................................................... + Compute the length of the packet (byte-swap the length in the IPX hdr) + .....................................................................*/ + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + /*..................................................................... + Extract the sender's address from the IPX header + .....................................................................*/ + address.Set_Address (CurHeaderBuf); + + /*..................................................................... + Examine the Magic Number of the received packet to determine if this + packet goes into the Global Queue, or into one of the Private Queues + .....................................................................*/ + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + /*.................................................................. + Put the packet in the Global Queue + ..................................................................*/ + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) + ReceiveOverflows++; + } else { + if (packet->MagicNumber == ProductID) { + /*.................................................................. + Find the Private Queue that this packet is for + ..................................................................*/ + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) + ReceiveOverflows++; + break; + } + } + } + } + } +#endif + } + + +#else //WIN32 + + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + //------------------------------------------------------------------------ + // Loop until there are no more packets to process. + //------------------------------------------------------------------------ + while (1) { + + //..................................................................... + // Check the BufferFlags for the "current" buffer; if it's empty, + // break; out of the loop. + //..................................................................... + if (BufferFlags[CurIndex]==0) { + break; + } + + //..................................................................... + // Compute the length of the packet (byte-swap the length in the IPX hdr) + //..................................................................... + packetlen = ((CurHeaderBuf->Length & 0xff) << 8) | + (CurHeaderBuf->Length >> 8); + packetlen -= sizeof(IPXHeaderType); + + //..................................................................... + // Extract the sender's address from the IPX header + //..................................................................... + address.Set_Address (CurHeaderBuf); + + //..................................................................... + // Examine the Magic Number of the received packet to determine if this + // packet goes into the Global Queue, or into one of the Private Queues + //..................................................................... + packet = (CommHeaderType *)CurDataBuf; + if (packet->MagicNumber == GlobalChannel->Magic_Num()) { + + //.................................................................. + // Put the packet in the Global Queue + //.................................................................. + if (!GlobalChannel->Receive_Packet (packet, packetlen, &address)) { + ReceiveOverflows++; + } + } + + //..................................................................... + // Find the Private Queue that this packet is for + //..................................................................... + else if (packet->MagicNumber == ProductID) { + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Address == address) { + if (!Connection[i]->Receive_Packet (packet, packetlen)) { + ReceiveOverflows++; + } + break; + } + } + } + + //..................................................................... + // Set the current BufferFlags to 0 (since we've cleaned out this buffer) + //..................................................................... + BufferFlags[CurIndex] = 0; + + //..................................................................... + // Go to the next packet buffer + //..................................................................... + CurIndex++; + CurHeaderBuf = (IPXHeaderType *)(((char *)CurHeaderBuf) + FullPacketLen); + CurDataBuf = ((char *)CurDataBuf) + FullPacketLen; + if (CurIndex >= NumBufs) { + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + CurIndex = 0; + } + } + +#endif //WIN32 +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // Service all connections. If a connection reports that it's gone "bad", + // report an error to the caller. If it's the Global Channel, un-queue the + // send entry that's holding things up. This will keep the Global Channel + // from being clogged by one un-ACK'd outgoing packet. + //------------------------------------------------------------------------ + if (GlobalChannel) { + if (!GlobalChannel->Service()) { + GlobalChannel->Queue->UnQueue_Send (NULL, NULL, 0); + rc = 0; + } + } + for (i = 0; i < NumConnections; i++) { + if (!Connection[i]->Service()) { + rc = 0; + BadConnection = Connection[i]->ID; + } + } + + if (rc) { + BadConnection = CONNECTION_NONE; + } + + return(rc); + +} /* end of Service */ + +/*************************************************************************** + * IPXManagerClass::Get_Bad_Connection -- returns bad connection ID * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ID of bad connection; CONNECTION_NONE if none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +int IPXManagerClass::Get_Bad_Connection(void) +{ + return(BadConnection); + +} /* end of Get_Bad_Connection */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Send Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Send(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Send()); + +} /* end of Global_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Global_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # entries in the Global Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Global_Num_Receive(void) +{ + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening) { + return(0); + } + + return(GlobalChannel->Queue->Num_Receive()); + +} /* end of Global_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Send -- reports # entries in send queue * + * * + * INPUT: * + * # entries in the Private Send Queue * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Send(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) { + return(0); + } + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Send()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Send() > maxnum) { + maxnum = Connection[i]->Queue->Num_Send(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Send */ + + +/*************************************************************************** + * IPXManagerClass::Private_Num_Receive -- reports # entries in recv queue * + * * + * INPUT: * + * id ID of connection to query * + * * + * OUTPUT: * + * # entries in the Private Receive Queue * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Private_Num_Receive(int id) +{ + int i; + int maxnum; + + //------------------------------------------------------------------------ + // Error if IPX not installed or not Listening + //------------------------------------------------------------------------ + if (!IPXStatus || !Listening || (NumConnections==0)) + return(0); + + //------------------------------------------------------------------------ + // If connection ID specified, return that connection's # of packets + //------------------------------------------------------------------------ + if (id != CONNECTION_NONE) { + i = Connection_Index(id); + if (i != CONNECTION_NONE) { + return(Connection[i]->Queue->Num_Receive()); + } + else { + return(0); + } + + } + + //------------------------------------------------------------------------ + // Otherwise, return the max # of all connections + //------------------------------------------------------------------------ + else { + maxnum = 0; + for (i = 0; i < NumConnections; i++) { + if (Connection[i]->Queue->Num_Receive() > maxnum) { + maxnum = Connection[i]->Queue->Num_Receive(); + } + } + return(maxnum); + } + +} /* end of Private_Num_Receive */ + + +/*************************************************************************** + * IPXManagerClass::Set_Socket -- sets socket ID for all connections * + * * + * INPUT: * + * socket new socket ID to use * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Do not call this function after communications have started; you * + * must call it before calling Init(). * + * The socket number is byte-swapped, since IPX requires socket ID's * + * to be stored high/low. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Socket(unsigned short socket) +{ + Socket = (unsigned short)( (((unsigned long)socket & 0x00ff) << 8) | + (((unsigned long)socket & 0xff00) >> 8)); + +} /* end of Set_Socket */ + + +/*************************************************************************** + * IPXManagerClass::Response_Time -- Returns largest Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * largest avg response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Response_Time(void) +{ + unsigned long resp; + unsigned long maxresp = 0; + int i; + + for (i = 0; i < NumConnections; i++) { + resp = Connection[i]->Queue->Avg_Response_Time(); + if (resp > maxresp) { + maxresp = resp; + } + } + + return(maxresp); + +} /* end of Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Global_Response_Time -- Returns Avg Response Time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * avg global channel response time * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +unsigned long IPXManagerClass::Global_Response_Time(void) +{ + if (GlobalChannel) { + return (GlobalChannel->Queue->Avg_Response_Time()); + } + else { + return (0); + } + +} /* end of Global_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Reset_Response_Time -- Reset response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Reset_Response_Time(void) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + Connection[i]->Queue->Reset_Response_Time(); + } + + if (GlobalChannel) + GlobalChannel->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * IPXManagerClass::Oldest_Send -- gets ptr to oldest send buf * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * buf ptr * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/04/1995 BRR : Created. * + *=========================================================================*/ +void * IPXManagerClass::Oldest_Send(void) +{ + int i,j; + unsigned long time; + unsigned long mintime = 0xffffffff; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < NumConnections; i++) { + + send_entry = NULL; + + for (j = 0; j < Connection[i]->Queue->Num_Send(); j++) { + send_entry = Connection[i]->Queue->Get_Send(j); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && + send_entry->IsACK == 0) { + break; + } + else { + send_entry = NULL; + } + } + } + + if (send_entry!=NULL) { + + time = send_entry->FirstTime; + + if (time < mintime) { + mintime = time; + buf = send_entry->Buffer; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * IPXManagerClass::Set_Bridge -- prepares to cross a bridge * + * * + * This routine is designed to prevent the connection from having to * + * call Get_Local_Target, except the minimum number of times, since that * + * routine is buggy & goes away for long periods sometimes. * + * * + * INPUT: * + * bridge network number of the destination bridge * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 07/06/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Set_Bridge(NetNumType bridge) +{ + if (GlobalChannel) { + GlobalChannel->Set_Bridge(bridge); + } + +} /* end of Set_Bridge */ + + +/*************************************************************************** + * IPXManagerClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index connection index to configure (-1 = Global Channel) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical value of 1st name in the array * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void IPXManagerClass::Configure_Debug(int index, int type_offset, + int type_size, char **names, int namestart, int namecount) +{ + if (index == -1) { + GlobalChannel->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + else if (Connection[index]) { + Connection[index]->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); + } + +} /* end of Configure_Debug */ + + +/*************************************************************************** + * IPXManagerClass::Mono_Debug_Print -- debug output routine * + * * + * INPUT: * + * index index of connection to display (-1 = Global Channel) * + * refresh 1 = complete screen refresh * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/25/1995 BR : Created. * + *=========================================================================*/ +void IPXManagerClass::Mono_Debug_Print(int index, int refresh) +{ +#ifdef WWLIB32_H + char txt[80]; + int i; + + if (index == -1) + GlobalChannel->Queue->Mono_Debug_Print (refresh); + + else if (Connection[index]) + Connection[index]->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (20,1); + Mono_Printf ("IPX Queue:"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (43,1); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,2); + Mono_Printf ("Receive Overflows:"); + + } + + Mono_Set_Cursor (32,1); + Mono_Printf ("%d",index); + + Mono_Set_Cursor (32,2); + if (index == -1) { + Mono_Printf ("%d ", GlobalChannel->Queue->Avg_Response_Time()); + } + else { + Mono_Printf ("%d ", Connection[index]->Queue->Avg_Response_Time()); + } + + Mono_Set_Cursor (59,1); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", ReceiveOverflows); + + for (i = 0; i < NumBufs; i++) { + if (BufferFlags[i]) { + txt[i] = 'X'; + } + else { + txt[i] = '_'; + } + } + txt[i] = 0; + Mono_Set_Cursor ((80-NumBufs)/2,3); + Mono_Printf ("%s",txt); + +#else + index = index; + refresh = refresh; +#endif + +} /* end of Mono_Debug_Print */ + + +/*************************************************************************** + * IPXManagerClass::Alloc_RealMode_Mem -- allocates real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Alloc_RealMode_Mem(void) +{ + +#ifdef WIN32 + return (1); +#else + + union REGS regs; + struct SREGS sregs; + int size; // required size of allocation + unsigned char *realmode; // start addresses of real-mode data + int realmodelen; // length of real-mode data + unsigned long func_val; + char *p; // for parsing buffer + int i; + + //------------------------------------------------------------------------ + // Compute # of buffers we need to allocate, & the max size of each one + //------------------------------------------------------------------------ + NumBufs = Glb_NumPackets + (Pvt_NumPackets * CONNECT_MAX); + + PacketLen = Glb_MaxPacketLen + sizeof (GlobalHeaderType); + if (Pvt_MaxPacketLen + sizeof (CommHeaderType) > PacketLen) + PacketLen = Pvt_MaxPacketLen + sizeof (CommHeaderType); + + FullPacketLen = PacketLen + sizeof(IPXHeaderType); + + //------------------------------------------------------------------------ + // Compute the size of everything we'll ever need, allocate it in one big + // chunk. The memory is used as follows: + // - Real-mode assembly IPX callback routine, plus its data, + // (which includes the ListenECB) + // - Array of IPX Packet buffers (IPXHeader plus data buffer) + // - SendECB: ECB for sending + // - SendHeader: IPX Header for sending + // - SendBuf: Packet buffer for sending + // - BufferFlags: 1 byte for each incoming packet buffer; 1=in use, 0=free + //------------------------------------------------------------------------ + realmode = (unsigned char *)Get_RM_IPX_Address(); + realmodelen = Get_RM_IPX_Size(); + size = realmodelen + // assembly routine & its data + (FullPacketLen * NumBufs) + // array of packet buffers + sizeof(ECBType) + // SendECB + FullPacketLen + // SendHeader & SendBuf + NumBufs; // BufferFlags + if (size > 65535) { + return(0); + } + + //------------------------------------------------------------------------ + // Allocate DOS memory for the ECB, IPXHeader & packet buffers: + // AX = 0x100 + // BX = # paragraphs to allocate + // - if Success, AX = real-mode segment, DX = selector + // - if Failure, carry flag is set + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_ALLOC_DOS_MEM; // DPMI function to call + regs.x.ebx = ((size + 15) >> 4); // # paragraphs to allocate + int386x (DPMI_INT, ®s, ®s, &sregs); // allocate the memory + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + return(0); + } + + //------------------------------------------------------------------------ + // Save the values of the returned segment & selector + //------------------------------------------------------------------------ + Selector = regs.w.dx; + Segment = regs.w.ax; + RealMemSize = size; + RealModeData = (RealModeDataType *)(((long)Segment) << 4); + + //------------------------------------------------------------------------ + // Lock the memory (since we're servicing interrupts with it) + // AX = 0x600 + // BX:CX = starting linear address of memory to lock + // SI:DI = size of region to lock (in bytes) + // - If Failure, carry flag is set. + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_LOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + return(0); + } + + //------------------------------------------------------------------------ + // Copy the Real-mode code into our memory buffer + //------------------------------------------------------------------------ + p = (char *)(((long)Segment) << 4); + memcpy (p,realmode,realmodelen); + p += realmodelen; + + //------------------------------------------------------------------------ + // Compute & save the entry point for the real-mode packet handler + //------------------------------------------------------------------------ + func_val = (unsigned long)RealModeData; + Handler = (((func_val & 0xffff0) << 12) | + ((func_val & 0x000f) + RealModeData->FuncOffset)); + + //------------------------------------------------------------------------ + // Fill in buffer pointers + //------------------------------------------------------------------------ + ListenECB = &(RealModeData->ListenECB); + + FirstHeaderBuf = (IPXHeaderType *)p; + FirstDataBuf = (((char *)FirstHeaderBuf) + sizeof(IPXHeaderType)); + CurIndex = 0; + CurHeaderBuf = FirstHeaderBuf; + CurDataBuf = FirstDataBuf; + p += FullPacketLen * NumBufs; + + SendECB = (ECBType *)p; + p += sizeof (ECBType); + + SendHeader = (IPXHeaderType *)p; + p += sizeof (IPXHeaderType); + + SendBuf = (char *)p; + p += PacketLen; + + BufferFlags = (char *)p; + + //------------------------------------------------------------------------ + // Fill in the real-mode routine's data (The ECB will be filled in when we + // command IPX to Listen). + //------------------------------------------------------------------------ + RealModeData->NumBufs = (short)NumBufs; + RealModeData->BufferFlags = (char *) + ((((long)BufferFlags & 0xffff0) << 12) | + ((long)BufferFlags & 0x0000f)); + RealModeData->PacketSize = (short)FullPacketLen; + RealModeData->FirstPacketBuf = (IPXHeaderType *) + ((((long)FirstHeaderBuf & 0xffff0) << 12) | + ((long)FirstHeaderBuf & 0x0000f)); + RealModeData->CurIndex = 0; + RealModeData->CurPacketBuf = RealModeData->FirstPacketBuf; + RealModeData->Semaphore = 0; + RealModeData->ReEntrantCount = 0; + + //------------------------------------------------------------------------ + // Init state of all buffers to empty + //------------------------------------------------------------------------ + for (i = 0; i < NumBufs; i++) { + BufferFlags[i] = 0; + } + + //------------------------------------------------------------------------ + // Check the start & end markers in the real-mode memory area + //------------------------------------------------------------------------ + if (RealModeData->Marker1 != 0x1111 || + RealModeData->Marker2 != 0x2222) { + Free_RealMode_Mem(); + return(0); + } + else { + return(1); + } +#endif //WIN32 +} /* end of Alloc_Realmode_Mem */ + + +/*************************************************************************** + * IPXManagerClass::Free_RealMode_Mem -- frees real-mode memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int IPXManagerClass::Free_RealMode_Mem(void) +{ + +#ifdef WIN32 + + return (1); + +#else //WIN32 + + union REGS regs; + struct SREGS sregs; + int rc = 1; + + //------------------------------------------------------------------------ + // Unlock the memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_UNLOCK_MEM; // DPMI function to call + regs.x.ebx = ((long)RealModeData & 0xffff0000) >> 16; + regs.x.ecx = ((long)RealModeData & 0x0000ffff); + regs.x.esi = ((long)RealMemSize & 0xffff0000) >> 16; + regs.x.edi = ((long)RealMemSize & 0x0000ffff); + int386x (DPMI_INT, ®s, ®s, &sregs); // call DPMI + + //------------------------------------------------------------------------ + // If the carry flag is set, DPMI is indicating an error. + //------------------------------------------------------------------------ + if (regs.x.cflag) { + rc = 0; + } + + //------------------------------------------------------------------------ + // Free DOS memory + //------------------------------------------------------------------------ + memset (®s, 0 ,sizeof(regs)); + segread (&sregs); + regs.x.eax = DPMI_FREE_DOS_MEM; // DPMI function to call + regs.x.edx = Selector; // ptr to free + int386x (DPMI_INT, ®s, ®s, &sregs); // free the memory + + return(rc); +#endif //WIN32 + +} /* end of Free_Realmode_Mem */ + +/*************************** end of ipxmgr.cpp *****************************/ diff --git a/code/redalert/ipxmgr.h b/code/redalert/ipxmgr.h new file mode 100644 index 0000000..6993e90 --- /dev/null +++ b/code/redalert/ipxmgr.h @@ -0,0 +1,394 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/IPXMGR.H 1 3/03/97 10:24a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : IPXMGR.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for IPX network communications. It * + * creates, manages, & orchestrates multiple IPX connections, as well as * + * the "global" connection ("Global Channel"), which can talk to any * + * system on the net. * + * * + * Use the Global Channel to query systems for their names, ID's, & * + * IPX addresses. Then, create a Private Connection with each system * + * that joins your game, and use the Private Channel to send game packets * + * (the private channel will perform somewhat faster, & gives you better * + * control than the Global Channel; it can detect retries, and the Global * + * Channel can't). * + * * + * HOW THIS CLASS WORKS: * + * This class has to set up an IPX Event Service Routine in low (DOS) * + * memory. So, it uses DPMI to allocate & lock a chunk of DOS memory; * + * this memory is used for all incoming packet buffers, the outgoing * + * packet buffer, and the actual code for the event handler. The real- * + * mode handler code & this class share a portion of memory that's mapped * + * into a "RealModeDataType" structure. As packets come in, the handler * + * points IPX to the next available packet buffer & restarts listening; * + * it sets a flag to tell this class that a packet is present at that * + * buffer slot. This class must read all the packets & determine which * + * connection they go with (the Global Channel, or one of the Private * + * Channels). This parsing is done in the Service routine for this class. * + * * + * Constructor: Just inits some variables, checks to see if IPX is there * + * Destructor: Complete shutdown; stops IPX listening, frees all memory * + * Init: Should only be called once (but can be called more); * + * allocates all memory, creates the Global Channel * + * connection, starts IPX listening. By not placing this * + * step in the constructor, the app can control when * + * listening actually starts; also, you don't get a bunch * + * of allocations just by declaring an IPXManagerClass * + * instance. You have to call Init() for the allocations * + * to occur. * + * Connection utilities: Create & manage Private Connections. Each * + * connection has its own IPX address, numerical ID, and * + * character name (presumably the name of the other * + * player). * + * Send/Get_Global_Message: adds a packet to the Global Connection queue, * + * or reads from the queue. The caller should check the * + * ProductID value from returned packets to be sure it's * + * talking to the right product. * + * Send/Get_Private_Message: adds a packet to a Private Connection queue, * + * or reads from the queue * + * Service: Checks the Real-Mode-Memory packet array to see if any * + * new packets have come in; if they have, it parses them * + * & distributes them to the right connection queue. The * + * queue's Service routine handles ACK'ing or Resending * + * packets. * + * * + * Here's a memory map of the Real-Mode memory block. 'N' is the number * + * of packet buffers allocated in low memory: * + * * + * ---------------------------------- * + * | Shared-memory data | * + * |--------------------------------| * + * | Real-mode event handler code | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 0 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 1 | * + * |--------------------------------| * + * | IPX Header & Packet Buffer 2 | * + * |--------------------------------| * + * | . . . | * + * |--------------------------------| * + * | IPX Header & Packet Buffer N | * + * |--------------------------------| * + * | Send Event Control Block | * + * |--------------------------------| * + * | Send IPX Header | * + * |--------------------------------| * + * | Send Packet Buffer | * + * |--------------------------------| * + * | Flags Array [N] | * + * ---------------------------------- * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IPXMANAGER_H +#define IPXMANAGER_H + + +/* +********************************* Includes ********************************** +*/ +#include "ipxconn.h" +#include "ipxgconn.h" +#include "ipxaddr.h" +#include "connmgr.h" + +/* +********************************** Defines ********************************** +*/ +/*--------------------------------------------------------------------------- +This is Virgin Interactive Entertainment's registered socket ID. +---------------------------------------------------------------------------*/ +#define VIRGIN_SOCKET 0x8813 + +/*--------------------------------------------------------------------------- +This is the maximum number of IPX connections supported. Just change this +value to support more. +---------------------------------------------------------------------------*/ +#define CONNECT_MAX 7 + +/*--------------------------------------------------------------------------- +These routines report the location & length of the real-mode routine, as +it's stored in protected-mode memory. +---------------------------------------------------------------------------*/ +extern "C" { + void * __cdecl Get_RM_IPX_Address(void); + long __cdecl Get_RM_IPX_Size(void); +} + +/* +***************************** Class Declaration ***************************** +*/ +class IPXManagerClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + IPXManagerClass (int glb_maxlen, int pvt_maxlen, int glb_num_packets, + int pvt_num_packets, unsigned short socket, unsigned short product_id); + virtual ~IPXManagerClass (); // stop listening + + /*..................................................................... + Initialization routines. + .....................................................................*/ + int Init (void); + int Is_IPX(void); + virtual void Set_Timing (unsigned long retrydelta, unsigned long maxretries, + unsigned long timeout); + void Set_Bridge(NetNumType bridge); + + /*..................................................................... + These routines control creation of the "Connections" (data queues) for + each remote system. + .....................................................................*/ + int Create_Connection(int id, char *name, IPXAddressClass *address); + int Delete_Connection(int id); + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + char *Connection_Name(int id); + IPXAddressClass * Connection_Address(int id); + virtual int Connection_Index(int id); + void Set_Connection_Parms(int index, int id, char *name); + + /*..................................................................... + This is how the application sends & receives messages. + .....................................................................*/ + int Send_Global_Message (void *buf, int buflen, int ack_req = 0, + IPXAddressClass *address = NULL); + int Get_Global_Message (void *buf, int *buflen, IPXAddressClass *address, + unsigned short *product_id); + + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, int *conn_id); + + /*..................................................................... + The main polling routine; should be called as often as possible. + .....................................................................*/ + virtual int Service (void); + + /*..................................................................... + This routine reports which connection has an error on it. + .....................................................................*/ + int Get_Bad_Connection(void); + + /*..................................................................... + Queue utility routines. The application can determine how many + messages are in the send/receive queues. + .....................................................................*/ + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + /*..................................................................... + This routine changes the socket ID assigned the IPX Manager when it + was constructed. Do not call this function after calling Init()! + The Socket ID should be known by both ends of the communications before + any packets are sent. + .....................................................................*/ + void Set_Socket(unsigned short socket); + + /*..................................................................... + Routines to return the largest average queue response time, and to + reset the response time for all queues. + .....................................................................*/ + virtual unsigned long Response_Time(void); + unsigned long Global_Response_Time(void); + virtual void Reset_Response_Time(void); + + /*..................................................................... + This routine returns a pointer to the oldest non-ACK'd buffer I've sent. + .....................................................................*/ + void * Oldest_Send(void); + + /*..................................................................... + Debug routines + .....................................................................*/ + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh = 0); + + /* + --------------------------- Private Interface ---------------------------- + */ + private: + /*..................................................................... + These routines allocate & free the DOS Real-mode memory block. + .....................................................................*/ + int Alloc_RealMode_Mem(void); + int Free_RealMode_Mem(void); + + /*..................................................................... + Misc variables + .....................................................................*/ + unsigned int IPXStatus : 1; // 0 = no IPX, 1 = IPX found + unsigned int Listening : 1; // 1 = Listening is on + unsigned int RealMemAllocd : 1; // 1 = Real-mode memory has been alloc'd + + /*..................................................................... + Packet Sizes, used for allocating real-mode memory + .....................................................................*/ + int Glb_MaxPacketLen; // Global Channel maximum packet size + int Glb_NumPackets; // # Global send/receive packets + int Pvt_MaxPacketLen; // Private Channel maximum packet size + int Pvt_NumPackets; // # Private send/receive packets + + /*..................................................................... + The ProductID is used in the Global Channel's packet header, and it's + used for the Private Channels' Magic Number. + .....................................................................*/ + unsigned short ProductID; // product ID + + /*..................................................................... + The Socket ID, and local Novell Connection Number + .....................................................................*/ + unsigned short Socket; // Our socket ID for sending/receiving + int ConnectionNum; // local connection #, 0=not logged in + + /*..................................................................... + Array of connection queues + .....................................................................*/ + IPXConnClass * Connection[CONNECT_MAX]; // array of connection object ptrs + int NumConnections; // # connection objects in use + IPXGlobalConnClass *GlobalChannel; // the Global Channel + + /*..................................................................... + Current queue for polling for received packets + .....................................................................*/ + int CurConnection; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /*--------------------------------------------------------------------- + Real-mode memory pointers and such + ---------------------------------------------------------------------*/ + /*..................................................................... + This is a structure that mirrors data in real-mode memory: + .....................................................................*/ + typedef struct { + short Marker1; // the byte ID marker + ECBType ListenECB; // the Listening ECB + short NumBufs; // # of buffers we're giving to the handler + char *BufferFlags; // array of buffer-avail flags + short PacketSize; // size of packet including IPX header + IPXHeaderType *FirstPacketBuf; // ptr to 1st packet buffer + short CurIndex; // handler's current packet index + IPXHeaderType *CurPacketBuf; // handler's current packet buf + short FuncOffset; // contains offset of code + char Semaphore; // prevents re-entrancy + short ReEntrantCount; // times we've been called re-entrantly + short StackPtr; // real-mode stack pointer + short StackSeg; // real-mode stack segment + short StackPtr_int; // internal stack pointer + short StackSeg_int; // internal stack segment + short StackCheck; // stack check value (0x1234) + short Stack[256]; // actual stack space + short StackSpace; // label for top of stack + short Marker2; // the byte ID marker + } RealModeDataType; + + /*..................................................................... + The number & size of packet buffers in low memory + .....................................................................*/ + int NumBufs; // # packet buffers allocated + int PacketLen; // size of packet without IPX header + int FullPacketLen; // size of packet including IPX header + + /*..................................................................... + Selector & Segment of the DOS allocation; + Size of the allocation; + Ptr to the real-mode assembly data area + .....................................................................*/ + unsigned short Selector; // selector of DOS allocation pointer + unsigned short Segment; // real-mode segment of DOS allocation + int RealMemSize; // size of real mode memory allocated + RealModeDataType *RealModeData; // assembly routine & its data + + /*..................................................................... + This is a real-mode pointer to the address of the real-mode assembly + entry point. + .....................................................................*/ + long Handler; + + /*..................................................................... + Event Control Block for listening; contained within the real-mode + assembly routine's data area + .....................................................................*/ + ECBType *ListenECB; // ECB for listening + + /*..................................................................... + ptr to the 1st header & data buffers in the packet buffer array + .....................................................................*/ + IPXHeaderType *FirstHeaderBuf; // array of packet headers & buffers + char *FirstDataBuf; // 1st data buffer area + + /*..................................................................... + Current packet index & ptrs for parsing packets + .....................................................................*/ + int CurIndex; // Current packet index, for reading + IPXHeaderType *CurHeaderBuf; // Current packet ptr, for reading + char *CurDataBuf; // Current actual data ptr + + /*..................................................................... + ECB, header, & buffer for sending + .....................................................................*/ + ECBType *SendECB; // ECB for sending + IPXHeaderType *SendHeader; // Header for sending + char *SendBuf; // buffer for sending + + /*..................................................................... + Flags indicating whether a buffer contains data or not (1 = full) + The IPXManager must clear this flag; the real-mode routine will set it. + .....................................................................*/ + char *BufferFlags; // array of rx-buffer-avail flags + + /*..................................................................... + Various Statistics + .....................................................................*/ + int SendOverflows; + int ReceiveOverflows; + int BadConnection; +}; + +#endif + +/*************************** end of ipxmgr.h *******************************/ diff --git a/code/redalert/itable.cpp b/code/redalert/itable.cpp new file mode 100644 index 0000000..2cf4865 --- /dev/null +++ b/code/redalert/itable.cpp @@ -0,0 +1,1443 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +unsigned short IndexTable[] = { + 0, // Index = 0, Token = 0 + 0, // Index = 0, Token = 1 + 0, // Index = 0, Token = 2 + 0, // Index = 0, Token = 3 + 32, // Index = 0, Token = 4 + 64, // Index = 0, Token = 5 + 96, // Index = 0, Token = 6 + 128, // Index = 0, Token = 7 + 0, // Index = 0, Token = 8 + 0, // Index = 0, Token = 9 + 0, // Index = 0, Token = 10 + 0, // Index = 0, Token = 11 + 32, // Index = 0, Token = 12 + 64, // Index = 0, Token = 13 + 96, // Index = 0, Token = 14 + 128, // Index = 0, Token = 15 + 0, // Index = 1, Token = 0 + 0, // Index = 1, Token = 1 + 0, // Index = 1, Token = 2 + 0, // Index = 1, Token = 3 + 48, // Index = 1, Token = 4 + 80, // Index = 1, Token = 5 + 112, // Index = 1, Token = 6 + 144, // Index = 1, Token = 7 + 0, // Index = 1, Token = 8 + 0, // Index = 1, Token = 9 + 0, // Index = 1, Token = 10 + 0, // Index = 1, Token = 11 + 48, // Index = 1, Token = 12 + 80, // Index = 1, Token = 13 + 112, // Index = 1, Token = 14 + 144, // Index = 1, Token = 15 + 16, // Index = 2, Token = 0 + 16, // Index = 2, Token = 1 + 16, // Index = 2, Token = 2 + 16, // Index = 2, Token = 3 + 64, // Index = 2, Token = 4 + 96, // Index = 2, Token = 5 + 128, // Index = 2, Token = 6 + 160, // Index = 2, Token = 7 + 16, // Index = 2, Token = 8 + 16, // Index = 2, Token = 9 + 16, // Index = 2, Token = 10 + 16, // Index = 2, Token = 11 + 64, // Index = 2, Token = 12 + 96, // Index = 2, Token = 13 + 128, // Index = 2, Token = 14 + 160, // Index = 2, Token = 15 + 32, // Index = 3, Token = 0 + 32, // Index = 3, Token = 1 + 32, // Index = 3, Token = 2 + 32, // Index = 3, Token = 3 + 80, // Index = 3, Token = 4 + 112, // Index = 3, Token = 5 + 144, // Index = 3, Token = 6 + 176, // Index = 3, Token = 7 + 32, // Index = 3, Token = 8 + 32, // Index = 3, Token = 9 + 32, // Index = 3, Token = 10 + 32, // Index = 3, Token = 11 + 80, // Index = 3, Token = 12 + 112, // Index = 3, Token = 13 + 144, // Index = 3, Token = 14 + 176, // Index = 3, Token = 15 + 48, // Index = 4, Token = 0 + 48, // Index = 4, Token = 1 + 48, // Index = 4, Token = 2 + 48, // Index = 4, Token = 3 + 96, // Index = 4, Token = 4 + 128, // Index = 4, Token = 5 + 160, // Index = 4, Token = 6 + 192, // Index = 4, Token = 7 + 48, // Index = 4, Token = 8 + 48, // Index = 4, Token = 9 + 48, // Index = 4, Token = 10 + 48, // Index = 4, Token = 11 + 96, // Index = 4, Token = 12 + 128, // Index = 4, Token = 13 + 160, // Index = 4, Token = 14 + 192, // Index = 4, Token = 15 + 64, // Index = 5, Token = 0 + 64, // Index = 5, Token = 1 + 64, // Index = 5, Token = 2 + 64, // Index = 5, Token = 3 + 112, // Index = 5, Token = 4 + 144, // Index = 5, Token = 5 + 176, // Index = 5, Token = 6 + 208, // Index = 5, Token = 7 + 64, // Index = 5, Token = 8 + 64, // Index = 5, Token = 9 + 64, // Index = 5, Token = 10 + 64, // Index = 5, Token = 11 + 112, // Index = 5, Token = 12 + 144, // Index = 5, Token = 13 + 176, // Index = 5, Token = 14 + 208, // Index = 5, Token = 15 + 80, // Index = 6, Token = 0 + 80, // Index = 6, Token = 1 + 80, // Index = 6, Token = 2 + 80, // Index = 6, Token = 3 + 128, // Index = 6, Token = 4 + 160, // Index = 6, Token = 5 + 192, // Index = 6, Token = 6 + 224, // Index = 6, Token = 7 + 80, // Index = 6, Token = 8 + 80, // Index = 6, Token = 9 + 80, // Index = 6, Token = 10 + 80, // Index = 6, Token = 11 + 128, // Index = 6, Token = 12 + 160, // Index = 6, Token = 13 + 192, // Index = 6, Token = 14 + 224, // Index = 6, Token = 15 + 96, // Index = 7, Token = 0 + 96, // Index = 7, Token = 1 + 96, // Index = 7, Token = 2 + 96, // Index = 7, Token = 3 + 144, // Index = 7, Token = 4 + 176, // Index = 7, Token = 5 + 208, // Index = 7, Token = 6 + 240, // Index = 7, Token = 7 + 96, // Index = 7, Token = 8 + 96, // Index = 7, Token = 9 + 96, // Index = 7, Token = 10 + 96, // Index = 7, Token = 11 + 144, // Index = 7, Token = 12 + 176, // Index = 7, Token = 13 + 208, // Index = 7, Token = 14 + 240, // Index = 7, Token = 15 + 112, // Index = 8, Token = 0 + 112, // Index = 8, Token = 1 + 112, // Index = 8, Token = 2 + 112, // Index = 8, Token = 3 + 160, // Index = 8, Token = 4 + 192, // Index = 8, Token = 5 + 224, // Index = 8, Token = 6 + 256, // Index = 8, Token = 7 + 112, // Index = 8, Token = 8 + 112, // Index = 8, Token = 9 + 112, // Index = 8, Token = 10 + 112, // Index = 8, Token = 11 + 160, // Index = 8, Token = 12 + 192, // Index = 8, Token = 13 + 224, // Index = 8, Token = 14 + 256, // Index = 8, Token = 15 + 128, // Index = 9, Token = 0 + 128, // Index = 9, Token = 1 + 128, // Index = 9, Token = 2 + 128, // Index = 9, Token = 3 + 176, // Index = 9, Token = 4 + 208, // Index = 9, Token = 5 + 240, // Index = 9, Token = 6 + 272, // Index = 9, Token = 7 + 128, // Index = 9, Token = 8 + 128, // Index = 9, Token = 9 + 128, // Index = 9, Token = 10 + 128, // Index = 9, Token = 11 + 176, // Index = 9, Token = 12 + 208, // Index = 9, Token = 13 + 240, // Index = 9, Token = 14 + 272, // Index = 9, Token = 15 + 144, // Index = 10, Token = 0 + 144, // Index = 10, Token = 1 + 144, // Index = 10, Token = 2 + 144, // Index = 10, Token = 3 + 192, // Index = 10, Token = 4 + 224, // Index = 10, Token = 5 + 256, // Index = 10, Token = 6 + 288, // Index = 10, Token = 7 + 144, // Index = 10, Token = 8 + 144, // Index = 10, Token = 9 + 144, // Index = 10, Token = 10 + 144, // Index = 10, Token = 11 + 192, // Index = 10, Token = 12 + 224, // Index = 10, Token = 13 + 256, // Index = 10, Token = 14 + 288, // Index = 10, Token = 15 + 160, // Index = 11, Token = 0 + 160, // Index = 11, Token = 1 + 160, // Index = 11, Token = 2 + 160, // Index = 11, Token = 3 + 208, // Index = 11, Token = 4 + 240, // Index = 11, Token = 5 + 272, // Index = 11, Token = 6 + 304, // Index = 11, Token = 7 + 160, // Index = 11, Token = 8 + 160, // Index = 11, Token = 9 + 160, // Index = 11, Token = 10 + 160, // Index = 11, Token = 11 + 208, // Index = 11, Token = 12 + 240, // Index = 11, Token = 13 + 272, // Index = 11, Token = 14 + 304, // Index = 11, Token = 15 + 176, // Index = 12, Token = 0 + 176, // Index = 12, Token = 1 + 176, // Index = 12, Token = 2 + 176, // Index = 12, Token = 3 + 224, // Index = 12, Token = 4 + 256, // Index = 12, Token = 5 + 288, // Index = 12, Token = 6 + 320, // Index = 12, Token = 7 + 176, // Index = 12, Token = 8 + 176, // Index = 12, Token = 9 + 176, // Index = 12, Token = 10 + 176, // Index = 12, Token = 11 + 224, // Index = 12, Token = 12 + 256, // Index = 12, Token = 13 + 288, // Index = 12, Token = 14 + 320, // Index = 12, Token = 15 + 192, // Index = 13, Token = 0 + 192, // Index = 13, Token = 1 + 192, // Index = 13, Token = 2 + 192, // Index = 13, Token = 3 + 240, // Index = 13, Token = 4 + 272, // Index = 13, Token = 5 + 304, // Index = 13, Token = 6 + 336, // Index = 13, Token = 7 + 192, // Index = 13, Token = 8 + 192, // Index = 13, Token = 9 + 192, // Index = 13, Token = 10 + 192, // Index = 13, Token = 11 + 240, // Index = 13, Token = 12 + 272, // Index = 13, Token = 13 + 304, // Index = 13, Token = 14 + 336, // Index = 13, Token = 15 + 208, // Index = 14, Token = 0 + 208, // Index = 14, Token = 1 + 208, // Index = 14, Token = 2 + 208, // Index = 14, Token = 3 + 256, // Index = 14, Token = 4 + 288, // Index = 14, Token = 5 + 320, // Index = 14, Token = 6 + 352, // Index = 14, Token = 7 + 208, // Index = 14, Token = 8 + 208, // Index = 14, Token = 9 + 208, // Index = 14, Token = 10 + 208, // Index = 14, Token = 11 + 256, // Index = 14, Token = 12 + 288, // Index = 14, Token = 13 + 320, // Index = 14, Token = 14 + 352, // Index = 14, Token = 15 + 224, // Index = 15, Token = 0 + 224, // Index = 15, Token = 1 + 224, // Index = 15, Token = 2 + 224, // Index = 15, Token = 3 + 272, // Index = 15, Token = 4 + 304, // Index = 15, Token = 5 + 336, // Index = 15, Token = 6 + 368, // Index = 15, Token = 7 + 224, // Index = 15, Token = 8 + 224, // Index = 15, Token = 9 + 224, // Index = 15, Token = 10 + 224, // Index = 15, Token = 11 + 272, // Index = 15, Token = 12 + 304, // Index = 15, Token = 13 + 336, // Index = 15, Token = 14 + 368, // Index = 15, Token = 15 + 240, // Index = 16, Token = 0 + 240, // Index = 16, Token = 1 + 240, // Index = 16, Token = 2 + 240, // Index = 16, Token = 3 + 288, // Index = 16, Token = 4 + 320, // Index = 16, Token = 5 + 352, // Index = 16, Token = 6 + 384, // Index = 16, Token = 7 + 240, // Index = 16, Token = 8 + 240, // Index = 16, Token = 9 + 240, // Index = 16, Token = 10 + 240, // Index = 16, Token = 11 + 288, // Index = 16, Token = 12 + 320, // Index = 16, Token = 13 + 352, // Index = 16, Token = 14 + 384, // Index = 16, Token = 15 + 256, // Index = 17, Token = 0 + 256, // Index = 17, Token = 1 + 256, // Index = 17, Token = 2 + 256, // Index = 17, Token = 3 + 304, // Index = 17, Token = 4 + 336, // Index = 17, Token = 5 + 368, // Index = 17, Token = 6 + 400, // Index = 17, Token = 7 + 256, // Index = 17, Token = 8 + 256, // Index = 17, Token = 9 + 256, // Index = 17, Token = 10 + 256, // Index = 17, Token = 11 + 304, // Index = 17, Token = 12 + 336, // Index = 17, Token = 13 + 368, // Index = 17, Token = 14 + 400, // Index = 17, Token = 15 + 272, // Index = 18, Token = 0 + 272, // Index = 18, Token = 1 + 272, // Index = 18, Token = 2 + 272, // Index = 18, Token = 3 + 320, // Index = 18, Token = 4 + 352, // Index = 18, Token = 5 + 384, // Index = 18, Token = 6 + 416, // Index = 18, Token = 7 + 272, // Index = 18, Token = 8 + 272, // Index = 18, Token = 9 + 272, // Index = 18, Token = 10 + 272, // Index = 18, Token = 11 + 320, // Index = 18, Token = 12 + 352, // Index = 18, Token = 13 + 384, // Index = 18, Token = 14 + 416, // Index = 18, Token = 15 + 288, // Index = 19, Token = 0 + 288, // Index = 19, Token = 1 + 288, // Index = 19, Token = 2 + 288, // Index = 19, Token = 3 + 336, // Index = 19, Token = 4 + 368, // Index = 19, Token = 5 + 400, // Index = 19, Token = 6 + 432, // Index = 19, Token = 7 + 288, // Index = 19, Token = 8 + 288, // Index = 19, Token = 9 + 288, // Index = 19, Token = 10 + 288, // Index = 19, Token = 11 + 336, // Index = 19, Token = 12 + 368, // Index = 19, Token = 13 + 400, // Index = 19, Token = 14 + 432, // Index = 19, Token = 15 + 304, // Index = 20, Token = 0 + 304, // Index = 20, Token = 1 + 304, // Index = 20, Token = 2 + 304, // Index = 20, Token = 3 + 352, // Index = 20, Token = 4 + 384, // Index = 20, Token = 5 + 416, // Index = 20, Token = 6 + 448, // Index = 20, Token = 7 + 304, // Index = 20, Token = 8 + 304, // Index = 20, Token = 9 + 304, // Index = 20, Token = 10 + 304, // Index = 20, Token = 11 + 352, // Index = 20, Token = 12 + 384, // Index = 20, Token = 13 + 416, // Index = 20, Token = 14 + 448, // Index = 20, Token = 15 + 320, // Index = 21, Token = 0 + 320, // Index = 21, Token = 1 + 320, // Index = 21, Token = 2 + 320, // Index = 21, Token = 3 + 368, // Index = 21, Token = 4 + 400, // Index = 21, Token = 5 + 432, // Index = 21, Token = 6 + 464, // Index = 21, Token = 7 + 320, // Index = 21, Token = 8 + 320, // Index = 21, Token = 9 + 320, // Index = 21, Token = 10 + 320, // Index = 21, Token = 11 + 368, // Index = 21, Token = 12 + 400, // Index = 21, Token = 13 + 432, // Index = 21, Token = 14 + 464, // Index = 21, Token = 15 + 336, // Index = 22, Token = 0 + 336, // Index = 22, Token = 1 + 336, // Index = 22, Token = 2 + 336, // Index = 22, Token = 3 + 384, // Index = 22, Token = 4 + 416, // Index = 22, Token = 5 + 448, // Index = 22, Token = 6 + 480, // Index = 22, Token = 7 + 336, // Index = 22, Token = 8 + 336, // Index = 22, Token = 9 + 336, // Index = 22, Token = 10 + 336, // Index = 22, Token = 11 + 384, // Index = 22, Token = 12 + 416, // Index = 22, Token = 13 + 448, // Index = 22, Token = 14 + 480, // Index = 22, Token = 15 + 352, // Index = 23, Token = 0 + 352, // Index = 23, Token = 1 + 352, // Index = 23, Token = 2 + 352, // Index = 23, Token = 3 + 400, // Index = 23, Token = 4 + 432, // Index = 23, Token = 5 + 464, // Index = 23, Token = 6 + 496, // Index = 23, Token = 7 + 352, // Index = 23, Token = 8 + 352, // Index = 23, Token = 9 + 352, // Index = 23, Token = 10 + 352, // Index = 23, Token = 11 + 400, // Index = 23, Token = 12 + 432, // Index = 23, Token = 13 + 464, // Index = 23, Token = 14 + 496, // Index = 23, Token = 15 + 368, // Index = 24, Token = 0 + 368, // Index = 24, Token = 1 + 368, // Index = 24, Token = 2 + 368, // Index = 24, Token = 3 + 416, // Index = 24, Token = 4 + 448, // Index = 24, Token = 5 + 480, // Index = 24, Token = 6 + 512, // Index = 24, Token = 7 + 368, // Index = 24, Token = 8 + 368, // Index = 24, Token = 9 + 368, // Index = 24, Token = 10 + 368, // Index = 24, Token = 11 + 416, // Index = 24, Token = 12 + 448, // Index = 24, Token = 13 + 480, // Index = 24, Token = 14 + 512, // Index = 24, Token = 15 + 384, // Index = 25, Token = 0 + 384, // Index = 25, Token = 1 + 384, // Index = 25, Token = 2 + 384, // Index = 25, Token = 3 + 432, // Index = 25, Token = 4 + 464, // Index = 25, Token = 5 + 496, // Index = 25, Token = 6 + 528, // Index = 25, Token = 7 + 384, // Index = 25, Token = 8 + 384, // Index = 25, Token = 9 + 384, // Index = 25, Token = 10 + 384, // Index = 25, Token = 11 + 432, // Index = 25, Token = 12 + 464, // Index = 25, Token = 13 + 496, // Index = 25, Token = 14 + 528, // Index = 25, Token = 15 + 400, // Index = 26, Token = 0 + 400, // Index = 26, Token = 1 + 400, // Index = 26, Token = 2 + 400, // Index = 26, Token = 3 + 448, // Index = 26, Token = 4 + 480, // Index = 26, Token = 5 + 512, // Index = 26, Token = 6 + 544, // Index = 26, Token = 7 + 400, // Index = 26, Token = 8 + 400, // Index = 26, Token = 9 + 400, // Index = 26, Token = 10 + 400, // Index = 26, Token = 11 + 448, // Index = 26, Token = 12 + 480, // Index = 26, Token = 13 + 512, // Index = 26, Token = 14 + 544, // Index = 26, Token = 15 + 416, // Index = 27, Token = 0 + 416, // Index = 27, Token = 1 + 416, // Index = 27, Token = 2 + 416, // Index = 27, Token = 3 + 464, // Index = 27, Token = 4 + 496, // Index = 27, Token = 5 + 528, // Index = 27, Token = 6 + 560, // Index = 27, Token = 7 + 416, // Index = 27, Token = 8 + 416, // Index = 27, Token = 9 + 416, // Index = 27, Token = 10 + 416, // Index = 27, Token = 11 + 464, // Index = 27, Token = 12 + 496, // Index = 27, Token = 13 + 528, // Index = 27, Token = 14 + 560, // Index = 27, Token = 15 + 432, // Index = 28, Token = 0 + 432, // Index = 28, Token = 1 + 432, // Index = 28, Token = 2 + 432, // Index = 28, Token = 3 + 480, // Index = 28, Token = 4 + 512, // Index = 28, Token = 5 + 544, // Index = 28, Token = 6 + 576, // Index = 28, Token = 7 + 432, // Index = 28, Token = 8 + 432, // Index = 28, Token = 9 + 432, // Index = 28, Token = 10 + 432, // Index = 28, Token = 11 + 480, // Index = 28, Token = 12 + 512, // Index = 28, Token = 13 + 544, // Index = 28, Token = 14 + 576, // Index = 28, Token = 15 + 448, // Index = 29, Token = 0 + 448, // Index = 29, Token = 1 + 448, // Index = 29, Token = 2 + 448, // Index = 29, Token = 3 + 496, // Index = 29, Token = 4 + 528, // Index = 29, Token = 5 + 560, // Index = 29, Token = 6 + 592, // Index = 29, Token = 7 + 448, // Index = 29, Token = 8 + 448, // Index = 29, Token = 9 + 448, // Index = 29, Token = 10 + 448, // Index = 29, Token = 11 + 496, // Index = 29, Token = 12 + 528, // Index = 29, Token = 13 + 560, // Index = 29, Token = 14 + 592, // Index = 29, Token = 15 + 464, // Index = 30, Token = 0 + 464, // Index = 30, Token = 1 + 464, // Index = 30, Token = 2 + 464, // Index = 30, Token = 3 + 512, // Index = 30, Token = 4 + 544, // Index = 30, Token = 5 + 576, // Index = 30, Token = 6 + 608, // Index = 30, Token = 7 + 464, // Index = 30, Token = 8 + 464, // Index = 30, Token = 9 + 464, // Index = 30, Token = 10 + 464, // Index = 30, Token = 11 + 512, // Index = 30, Token = 12 + 544, // Index = 30, Token = 13 + 576, // Index = 30, Token = 14 + 608, // Index = 30, Token = 15 + 480, // Index = 31, Token = 0 + 480, // Index = 31, Token = 1 + 480, // Index = 31, Token = 2 + 480, // Index = 31, Token = 3 + 528, // Index = 31, Token = 4 + 560, // Index = 31, Token = 5 + 592, // Index = 31, Token = 6 + 624, // Index = 31, Token = 7 + 480, // Index = 31, Token = 8 + 480, // Index = 31, Token = 9 + 480, // Index = 31, Token = 10 + 480, // Index = 31, Token = 11 + 528, // Index = 31, Token = 12 + 560, // Index = 31, Token = 13 + 592, // Index = 31, Token = 14 + 624, // Index = 31, Token = 15 + 496, // Index = 32, Token = 0 + 496, // Index = 32, Token = 1 + 496, // Index = 32, Token = 2 + 496, // Index = 32, Token = 3 + 544, // Index = 32, Token = 4 + 576, // Index = 32, Token = 5 + 608, // Index = 32, Token = 6 + 640, // Index = 32, Token = 7 + 496, // Index = 32, Token = 8 + 496, // Index = 32, Token = 9 + 496, // Index = 32, Token = 10 + 496, // Index = 32, Token = 11 + 544, // Index = 32, Token = 12 + 576, // Index = 32, Token = 13 + 608, // Index = 32, Token = 14 + 640, // Index = 32, Token = 15 + 512, // Index = 33, Token = 0 + 512, // Index = 33, Token = 1 + 512, // Index = 33, Token = 2 + 512, // Index = 33, Token = 3 + 560, // Index = 33, Token = 4 + 592, // Index = 33, Token = 5 + 624, // Index = 33, Token = 6 + 656, // Index = 33, Token = 7 + 512, // Index = 33, Token = 8 + 512, // Index = 33, Token = 9 + 512, // Index = 33, Token = 10 + 512, // Index = 33, Token = 11 + 560, // Index = 33, Token = 12 + 592, // Index = 33, Token = 13 + 624, // Index = 33, Token = 14 + 656, // Index = 33, Token = 15 + 528, // Index = 34, Token = 0 + 528, // Index = 34, Token = 1 + 528, // Index = 34, Token = 2 + 528, // Index = 34, Token = 3 + 576, // Index = 34, Token = 4 + 608, // Index = 34, Token = 5 + 640, // Index = 34, Token = 6 + 672, // Index = 34, Token = 7 + 528, // Index = 34, Token = 8 + 528, // Index = 34, Token = 9 + 528, // Index = 34, Token = 10 + 528, // Index = 34, Token = 11 + 576, // Index = 34, Token = 12 + 608, // Index = 34, Token = 13 + 640, // Index = 34, Token = 14 + 672, // Index = 34, Token = 15 + 544, // Index = 35, Token = 0 + 544, // Index = 35, Token = 1 + 544, // Index = 35, Token = 2 + 544, // Index = 35, Token = 3 + 592, // Index = 35, Token = 4 + 624, // Index = 35, Token = 5 + 656, // Index = 35, Token = 6 + 688, // Index = 35, Token = 7 + 544, // Index = 35, Token = 8 + 544, // Index = 35, Token = 9 + 544, // Index = 35, Token = 10 + 544, // Index = 35, Token = 11 + 592, // Index = 35, Token = 12 + 624, // Index = 35, Token = 13 + 656, // Index = 35, Token = 14 + 688, // Index = 35, Token = 15 + 560, // Index = 36, Token = 0 + 560, // Index = 36, Token = 1 + 560, // Index = 36, Token = 2 + 560, // Index = 36, Token = 3 + 608, // Index = 36, Token = 4 + 640, // Index = 36, Token = 5 + 672, // Index = 36, Token = 6 + 704, // Index = 36, Token = 7 + 560, // Index = 36, Token = 8 + 560, // Index = 36, Token = 9 + 560, // Index = 36, Token = 10 + 560, // Index = 36, Token = 11 + 608, // Index = 36, Token = 12 + 640, // Index = 36, Token = 13 + 672, // Index = 36, Token = 14 + 704, // Index = 36, Token = 15 + 576, // Index = 37, Token = 0 + 576, // Index = 37, Token = 1 + 576, // Index = 37, Token = 2 + 576, // Index = 37, Token = 3 + 624, // Index = 37, Token = 4 + 656, // Index = 37, Token = 5 + 688, // Index = 37, Token = 6 + 720, // Index = 37, Token = 7 + 576, // Index = 37, Token = 8 + 576, // Index = 37, Token = 9 + 576, // Index = 37, Token = 10 + 576, // Index = 37, Token = 11 + 624, // Index = 37, Token = 12 + 656, // Index = 37, Token = 13 + 688, // Index = 37, Token = 14 + 720, // Index = 37, Token = 15 + 592, // Index = 38, Token = 0 + 592, // Index = 38, Token = 1 + 592, // Index = 38, Token = 2 + 592, // Index = 38, Token = 3 + 640, // Index = 38, Token = 4 + 672, // Index = 38, Token = 5 + 704, // Index = 38, Token = 6 + 736, // Index = 38, Token = 7 + 592, // Index = 38, Token = 8 + 592, // Index = 38, Token = 9 + 592, // Index = 38, Token = 10 + 592, // Index = 38, Token = 11 + 640, // Index = 38, Token = 12 + 672, // Index = 38, Token = 13 + 704, // Index = 38, Token = 14 + 736, // Index = 38, Token = 15 + 608, // Index = 39, Token = 0 + 608, // Index = 39, Token = 1 + 608, // Index = 39, Token = 2 + 608, // Index = 39, Token = 3 + 656, // Index = 39, Token = 4 + 688, // Index = 39, Token = 5 + 720, // Index = 39, Token = 6 + 752, // Index = 39, Token = 7 + 608, // Index = 39, Token = 8 + 608, // Index = 39, Token = 9 + 608, // Index = 39, Token = 10 + 608, // Index = 39, Token = 11 + 656, // Index = 39, Token = 12 + 688, // Index = 39, Token = 13 + 720, // Index = 39, Token = 14 + 752, // Index = 39, Token = 15 + 624, // Index = 40, Token = 0 + 624, // Index = 40, Token = 1 + 624, // Index = 40, Token = 2 + 624, // Index = 40, Token = 3 + 672, // Index = 40, Token = 4 + 704, // Index = 40, Token = 5 + 736, // Index = 40, Token = 6 + 768, // Index = 40, Token = 7 + 624, // Index = 40, Token = 8 + 624, // Index = 40, Token = 9 + 624, // Index = 40, Token = 10 + 624, // Index = 40, Token = 11 + 672, // Index = 40, Token = 12 + 704, // Index = 40, Token = 13 + 736, // Index = 40, Token = 14 + 768, // Index = 40, Token = 15 + 640, // Index = 41, Token = 0 + 640, // Index = 41, Token = 1 + 640, // Index = 41, Token = 2 + 640, // Index = 41, Token = 3 + 688, // Index = 41, Token = 4 + 720, // Index = 41, Token = 5 + 752, // Index = 41, Token = 6 + 784, // Index = 41, Token = 7 + 640, // Index = 41, Token = 8 + 640, // Index = 41, Token = 9 + 640, // Index = 41, Token = 10 + 640, // Index = 41, Token = 11 + 688, // Index = 41, Token = 12 + 720, // Index = 41, Token = 13 + 752, // Index = 41, Token = 14 + 784, // Index = 41, Token = 15 + 656, // Index = 42, Token = 0 + 656, // Index = 42, Token = 1 + 656, // Index = 42, Token = 2 + 656, // Index = 42, Token = 3 + 704, // Index = 42, Token = 4 + 736, // Index = 42, Token = 5 + 768, // Index = 42, Token = 6 + 800, // Index = 42, Token = 7 + 656, // Index = 42, Token = 8 + 656, // Index = 42, Token = 9 + 656, // Index = 42, Token = 10 + 656, // Index = 42, Token = 11 + 704, // Index = 42, Token = 12 + 736, // Index = 42, Token = 13 + 768, // Index = 42, Token = 14 + 800, // Index = 42, Token = 15 + 672, // Index = 43, Token = 0 + 672, // Index = 43, Token = 1 + 672, // Index = 43, Token = 2 + 672, // Index = 43, Token = 3 + 720, // Index = 43, Token = 4 + 752, // Index = 43, Token = 5 + 784, // Index = 43, Token = 6 + 816, // Index = 43, Token = 7 + 672, // Index = 43, Token = 8 + 672, // Index = 43, Token = 9 + 672, // Index = 43, Token = 10 + 672, // Index = 43, Token = 11 + 720, // Index = 43, Token = 12 + 752, // Index = 43, Token = 13 + 784, // Index = 43, Token = 14 + 816, // Index = 43, Token = 15 + 688, // Index = 44, Token = 0 + 688, // Index = 44, Token = 1 + 688, // Index = 44, Token = 2 + 688, // Index = 44, Token = 3 + 736, // Index = 44, Token = 4 + 768, // Index = 44, Token = 5 + 800, // Index = 44, Token = 6 + 832, // Index = 44, Token = 7 + 688, // Index = 44, Token = 8 + 688, // Index = 44, Token = 9 + 688, // Index = 44, Token = 10 + 688, // Index = 44, Token = 11 + 736, // Index = 44, Token = 12 + 768, // Index = 44, Token = 13 + 800, // Index = 44, Token = 14 + 832, // Index = 44, Token = 15 + 704, // Index = 45, Token = 0 + 704, // Index = 45, Token = 1 + 704, // Index = 45, Token = 2 + 704, // Index = 45, Token = 3 + 752, // Index = 45, Token = 4 + 784, // Index = 45, Token = 5 + 816, // Index = 45, Token = 6 + 848, // Index = 45, Token = 7 + 704, // Index = 45, Token = 8 + 704, // Index = 45, Token = 9 + 704, // Index = 45, Token = 10 + 704, // Index = 45, Token = 11 + 752, // Index = 45, Token = 12 + 784, // Index = 45, Token = 13 + 816, // Index = 45, Token = 14 + 848, // Index = 45, Token = 15 + 720, // Index = 46, Token = 0 + 720, // Index = 46, Token = 1 + 720, // Index = 46, Token = 2 + 720, // Index = 46, Token = 3 + 768, // Index = 46, Token = 4 + 800, // Index = 46, Token = 5 + 832, // Index = 46, Token = 6 + 864, // Index = 46, Token = 7 + 720, // Index = 46, Token = 8 + 720, // Index = 46, Token = 9 + 720, // Index = 46, Token = 10 + 720, // Index = 46, Token = 11 + 768, // Index = 46, Token = 12 + 800, // Index = 46, Token = 13 + 832, // Index = 46, Token = 14 + 864, // Index = 46, Token = 15 + 736, // Index = 47, Token = 0 + 736, // Index = 47, Token = 1 + 736, // Index = 47, Token = 2 + 736, // Index = 47, Token = 3 + 784, // Index = 47, Token = 4 + 816, // Index = 47, Token = 5 + 848, // Index = 47, Token = 6 + 880, // Index = 47, Token = 7 + 736, // Index = 47, Token = 8 + 736, // Index = 47, Token = 9 + 736, // Index = 47, Token = 10 + 736, // Index = 47, Token = 11 + 784, // Index = 47, Token = 12 + 816, // Index = 47, Token = 13 + 848, // Index = 47, Token = 14 + 880, // Index = 47, Token = 15 + 752, // Index = 48, Token = 0 + 752, // Index = 48, Token = 1 + 752, // Index = 48, Token = 2 + 752, // Index = 48, Token = 3 + 800, // Index = 48, Token = 4 + 832, // Index = 48, Token = 5 + 864, // Index = 48, Token = 6 + 896, // Index = 48, Token = 7 + 752, // Index = 48, Token = 8 + 752, // Index = 48, Token = 9 + 752, // Index = 48, Token = 10 + 752, // Index = 48, Token = 11 + 800, // Index = 48, Token = 12 + 832, // Index = 48, Token = 13 + 864, // Index = 48, Token = 14 + 896, // Index = 48, Token = 15 + 768, // Index = 49, Token = 0 + 768, // Index = 49, Token = 1 + 768, // Index = 49, Token = 2 + 768, // Index = 49, Token = 3 + 816, // Index = 49, Token = 4 + 848, // Index = 49, Token = 5 + 880, // Index = 49, Token = 6 + 912, // Index = 49, Token = 7 + 768, // Index = 49, Token = 8 + 768, // Index = 49, Token = 9 + 768, // Index = 49, Token = 10 + 768, // Index = 49, Token = 11 + 816, // Index = 49, Token = 12 + 848, // Index = 49, Token = 13 + 880, // Index = 49, Token = 14 + 912, // Index = 49, Token = 15 + 784, // Index = 50, Token = 0 + 784, // Index = 50, Token = 1 + 784, // Index = 50, Token = 2 + 784, // Index = 50, Token = 3 + 832, // Index = 50, Token = 4 + 864, // Index = 50, Token = 5 + 896, // Index = 50, Token = 6 + 928, // Index = 50, Token = 7 + 784, // Index = 50, Token = 8 + 784, // Index = 50, Token = 9 + 784, // Index = 50, Token = 10 + 784, // Index = 50, Token = 11 + 832, // Index = 50, Token = 12 + 864, // Index = 50, Token = 13 + 896, // Index = 50, Token = 14 + 928, // Index = 50, Token = 15 + 800, // Index = 51, Token = 0 + 800, // Index = 51, Token = 1 + 800, // Index = 51, Token = 2 + 800, // Index = 51, Token = 3 + 848, // Index = 51, Token = 4 + 880, // Index = 51, Token = 5 + 912, // Index = 51, Token = 6 + 944, // Index = 51, Token = 7 + 800, // Index = 51, Token = 8 + 800, // Index = 51, Token = 9 + 800, // Index = 51, Token = 10 + 800, // Index = 51, Token = 11 + 848, // Index = 51, Token = 12 + 880, // Index = 51, Token = 13 + 912, // Index = 51, Token = 14 + 944, // Index = 51, Token = 15 + 816, // Index = 52, Token = 0 + 816, // Index = 52, Token = 1 + 816, // Index = 52, Token = 2 + 816, // Index = 52, Token = 3 + 864, // Index = 52, Token = 4 + 896, // Index = 52, Token = 5 + 928, // Index = 52, Token = 6 + 960, // Index = 52, Token = 7 + 816, // Index = 52, Token = 8 + 816, // Index = 52, Token = 9 + 816, // Index = 52, Token = 10 + 816, // Index = 52, Token = 11 + 864, // Index = 52, Token = 12 + 896, // Index = 52, Token = 13 + 928, // Index = 52, Token = 14 + 960, // Index = 52, Token = 15 + 832, // Index = 53, Token = 0 + 832, // Index = 53, Token = 1 + 832, // Index = 53, Token = 2 + 832, // Index = 53, Token = 3 + 880, // Index = 53, Token = 4 + 912, // Index = 53, Token = 5 + 944, // Index = 53, Token = 6 + 976, // Index = 53, Token = 7 + 832, // Index = 53, Token = 8 + 832, // Index = 53, Token = 9 + 832, // Index = 53, Token = 10 + 832, // Index = 53, Token = 11 + 880, // Index = 53, Token = 12 + 912, // Index = 53, Token = 13 + 944, // Index = 53, Token = 14 + 976, // Index = 53, Token = 15 + 848, // Index = 54, Token = 0 + 848, // Index = 54, Token = 1 + 848, // Index = 54, Token = 2 + 848, // Index = 54, Token = 3 + 896, // Index = 54, Token = 4 + 928, // Index = 54, Token = 5 + 960, // Index = 54, Token = 6 + 992, // Index = 54, Token = 7 + 848, // Index = 54, Token = 8 + 848, // Index = 54, Token = 9 + 848, // Index = 54, Token = 10 + 848, // Index = 54, Token = 11 + 896, // Index = 54, Token = 12 + 928, // Index = 54, Token = 13 + 960, // Index = 54, Token = 14 + 992, // Index = 54, Token = 15 + 864, // Index = 55, Token = 0 + 864, // Index = 55, Token = 1 + 864, // Index = 55, Token = 2 + 864, // Index = 55, Token = 3 + 912, // Index = 55, Token = 4 + 944, // Index = 55, Token = 5 + 976, // Index = 55, Token = 6 + 1008, // Index = 55, Token = 7 + 864, // Index = 55, Token = 8 + 864, // Index = 55, Token = 9 + 864, // Index = 55, Token = 10 + 864, // Index = 55, Token = 11 + 912, // Index = 55, Token = 12 + 944, // Index = 55, Token = 13 + 976, // Index = 55, Token = 14 + 1008, // Index = 55, Token = 15 + 880, // Index = 56, Token = 0 + 880, // Index = 56, Token = 1 + 880, // Index = 56, Token = 2 + 880, // Index = 56, Token = 3 + 928, // Index = 56, Token = 4 + 960, // Index = 56, Token = 5 + 992, // Index = 56, Token = 6 + 1024, // Index = 56, Token = 7 + 880, // Index = 56, Token = 8 + 880, // Index = 56, Token = 9 + 880, // Index = 56, Token = 10 + 880, // Index = 56, Token = 11 + 928, // Index = 56, Token = 12 + 960, // Index = 56, Token = 13 + 992, // Index = 56, Token = 14 + 1024, // Index = 56, Token = 15 + 896, // Index = 57, Token = 0 + 896, // Index = 57, Token = 1 + 896, // Index = 57, Token = 2 + 896, // Index = 57, Token = 3 + 944, // Index = 57, Token = 4 + 976, // Index = 57, Token = 5 + 1008, // Index = 57, Token = 6 + 1040, // Index = 57, Token = 7 + 896, // Index = 57, Token = 8 + 896, // Index = 57, Token = 9 + 896, // Index = 57, Token = 10 + 896, // Index = 57, Token = 11 + 944, // Index = 57, Token = 12 + 976, // Index = 57, Token = 13 + 1008, // Index = 57, Token = 14 + 1040, // Index = 57, Token = 15 + 912, // Index = 58, Token = 0 + 912, // Index = 58, Token = 1 + 912, // Index = 58, Token = 2 + 912, // Index = 58, Token = 3 + 960, // Index = 58, Token = 4 + 992, // Index = 58, Token = 5 + 1024, // Index = 58, Token = 6 + 1056, // Index = 58, Token = 7 + 912, // Index = 58, Token = 8 + 912, // Index = 58, Token = 9 + 912, // Index = 58, Token = 10 + 912, // Index = 58, Token = 11 + 960, // Index = 58, Token = 12 + 992, // Index = 58, Token = 13 + 1024, // Index = 58, Token = 14 + 1056, // Index = 58, Token = 15 + 928, // Index = 59, Token = 0 + 928, // Index = 59, Token = 1 + 928, // Index = 59, Token = 2 + 928, // Index = 59, Token = 3 + 976, // Index = 59, Token = 4 + 1008, // Index = 59, Token = 5 + 1040, // Index = 59, Token = 6 + 1072, // Index = 59, Token = 7 + 928, // Index = 59, Token = 8 + 928, // Index = 59, Token = 9 + 928, // Index = 59, Token = 10 + 928, // Index = 59, Token = 11 + 976, // Index = 59, Token = 12 + 1008, // Index = 59, Token = 13 + 1040, // Index = 59, Token = 14 + 1072, // Index = 59, Token = 15 + 944, // Index = 60, Token = 0 + 944, // Index = 60, Token = 1 + 944, // Index = 60, Token = 2 + 944, // Index = 60, Token = 3 + 992, // Index = 60, Token = 4 + 1024, // Index = 60, Token = 5 + 1056, // Index = 60, Token = 6 + 1088, // Index = 60, Token = 7 + 944, // Index = 60, Token = 8 + 944, // Index = 60, Token = 9 + 944, // Index = 60, Token = 10 + 944, // Index = 60, Token = 11 + 992, // Index = 60, Token = 12 + 1024, // Index = 60, Token = 13 + 1056, // Index = 60, Token = 14 + 1088, // Index = 60, Token = 15 + 960, // Index = 61, Token = 0 + 960, // Index = 61, Token = 1 + 960, // Index = 61, Token = 2 + 960, // Index = 61, Token = 3 + 1008, // Index = 61, Token = 4 + 1040, // Index = 61, Token = 5 + 1072, // Index = 61, Token = 6 + 1104, // Index = 61, Token = 7 + 960, // Index = 61, Token = 8 + 960, // Index = 61, Token = 9 + 960, // Index = 61, Token = 10 + 960, // Index = 61, Token = 11 + 1008, // Index = 61, Token = 12 + 1040, // Index = 61, Token = 13 + 1072, // Index = 61, Token = 14 + 1104, // Index = 61, Token = 15 + 976, // Index = 62, Token = 0 + 976, // Index = 62, Token = 1 + 976, // Index = 62, Token = 2 + 976, // Index = 62, Token = 3 + 1024, // Index = 62, Token = 4 + 1056, // Index = 62, Token = 5 + 1088, // Index = 62, Token = 6 + 1120, // Index = 62, Token = 7 + 976, // Index = 62, Token = 8 + 976, // Index = 62, Token = 9 + 976, // Index = 62, Token = 10 + 976, // Index = 62, Token = 11 + 1024, // Index = 62, Token = 12 + 1056, // Index = 62, Token = 13 + 1088, // Index = 62, Token = 14 + 1120, // Index = 62, Token = 15 + 992, // Index = 63, Token = 0 + 992, // Index = 63, Token = 1 + 992, // Index = 63, Token = 2 + 992, // Index = 63, Token = 3 + 1040, // Index = 63, Token = 4 + 1072, // Index = 63, Token = 5 + 1104, // Index = 63, Token = 6 + 1136, // Index = 63, Token = 7 + 992, // Index = 63, Token = 8 + 992, // Index = 63, Token = 9 + 992, // Index = 63, Token = 10 + 992, // Index = 63, Token = 11 + 1040, // Index = 63, Token = 12 + 1072, // Index = 63, Token = 13 + 1104, // Index = 63, Token = 14 + 1136, // Index = 63, Token = 15 + 1008, // Index = 64, Token = 0 + 1008, // Index = 64, Token = 1 + 1008, // Index = 64, Token = 2 + 1008, // Index = 64, Token = 3 + 1056, // Index = 64, Token = 4 + 1088, // Index = 64, Token = 5 + 1120, // Index = 64, Token = 6 + 1152, // Index = 64, Token = 7 + 1008, // Index = 64, Token = 8 + 1008, // Index = 64, Token = 9 + 1008, // Index = 64, Token = 10 + 1008, // Index = 64, Token = 11 + 1056, // Index = 64, Token = 12 + 1088, // Index = 64, Token = 13 + 1120, // Index = 64, Token = 14 + 1152, // Index = 64, Token = 15 + 1024, // Index = 65, Token = 0 + 1024, // Index = 65, Token = 1 + 1024, // Index = 65, Token = 2 + 1024, // Index = 65, Token = 3 + 1072, // Index = 65, Token = 4 + 1104, // Index = 65, Token = 5 + 1136, // Index = 65, Token = 6 + 1168, // Index = 65, Token = 7 + 1024, // Index = 65, Token = 8 + 1024, // Index = 65, Token = 9 + 1024, // Index = 65, Token = 10 + 1024, // Index = 65, Token = 11 + 1072, // Index = 65, Token = 12 + 1104, // Index = 65, Token = 13 + 1136, // Index = 65, Token = 14 + 1168, // Index = 65, Token = 15 + 1040, // Index = 66, Token = 0 + 1040, // Index = 66, Token = 1 + 1040, // Index = 66, Token = 2 + 1040, // Index = 66, Token = 3 + 1088, // Index = 66, Token = 4 + 1120, // Index = 66, Token = 5 + 1152, // Index = 66, Token = 6 + 1184, // Index = 66, Token = 7 + 1040, // Index = 66, Token = 8 + 1040, // Index = 66, Token = 9 + 1040, // Index = 66, Token = 10 + 1040, // Index = 66, Token = 11 + 1088, // Index = 66, Token = 12 + 1120, // Index = 66, Token = 13 + 1152, // Index = 66, Token = 14 + 1184, // Index = 66, Token = 15 + 1056, // Index = 67, Token = 0 + 1056, // Index = 67, Token = 1 + 1056, // Index = 67, Token = 2 + 1056, // Index = 67, Token = 3 + 1104, // Index = 67, Token = 4 + 1136, // Index = 67, Token = 5 + 1168, // Index = 67, Token = 6 + 1200, // Index = 67, Token = 7 + 1056, // Index = 67, Token = 8 + 1056, // Index = 67, Token = 9 + 1056, // Index = 67, Token = 10 + 1056, // Index = 67, Token = 11 + 1104, // Index = 67, Token = 12 + 1136, // Index = 67, Token = 13 + 1168, // Index = 67, Token = 14 + 1200, // Index = 67, Token = 15 + 1072, // Index = 68, Token = 0 + 1072, // Index = 68, Token = 1 + 1072, // Index = 68, Token = 2 + 1072, // Index = 68, Token = 3 + 1120, // Index = 68, Token = 4 + 1152, // Index = 68, Token = 5 + 1184, // Index = 68, Token = 6 + 1216, // Index = 68, Token = 7 + 1072, // Index = 68, Token = 8 + 1072, // Index = 68, Token = 9 + 1072, // Index = 68, Token = 10 + 1072, // Index = 68, Token = 11 + 1120, // Index = 68, Token = 12 + 1152, // Index = 68, Token = 13 + 1184, // Index = 68, Token = 14 + 1216, // Index = 68, Token = 15 + 1088, // Index = 69, Token = 0 + 1088, // Index = 69, Token = 1 + 1088, // Index = 69, Token = 2 + 1088, // Index = 69, Token = 3 + 1136, // Index = 69, Token = 4 + 1168, // Index = 69, Token = 5 + 1200, // Index = 69, Token = 6 + 1232, // Index = 69, Token = 7 + 1088, // Index = 69, Token = 8 + 1088, // Index = 69, Token = 9 + 1088, // Index = 69, Token = 10 + 1088, // Index = 69, Token = 11 + 1136, // Index = 69, Token = 12 + 1168, // Index = 69, Token = 13 + 1200, // Index = 69, Token = 14 + 1232, // Index = 69, Token = 15 + 1104, // Index = 70, Token = 0 + 1104, // Index = 70, Token = 1 + 1104, // Index = 70, Token = 2 + 1104, // Index = 70, Token = 3 + 1152, // Index = 70, Token = 4 + 1184, // Index = 70, Token = 5 + 1216, // Index = 70, Token = 6 + 1248, // Index = 70, Token = 7 + 1104, // Index = 70, Token = 8 + 1104, // Index = 70, Token = 9 + 1104, // Index = 70, Token = 10 + 1104, // Index = 70, Token = 11 + 1152, // Index = 70, Token = 12 + 1184, // Index = 70, Token = 13 + 1216, // Index = 70, Token = 14 + 1248, // Index = 70, Token = 15 + 1120, // Index = 71, Token = 0 + 1120, // Index = 71, Token = 1 + 1120, // Index = 71, Token = 2 + 1120, // Index = 71, Token = 3 + 1168, // Index = 71, Token = 4 + 1200, // Index = 71, Token = 5 + 1232, // Index = 71, Token = 6 + 1264, // Index = 71, Token = 7 + 1120, // Index = 71, Token = 8 + 1120, // Index = 71, Token = 9 + 1120, // Index = 71, Token = 10 + 1120, // Index = 71, Token = 11 + 1168, // Index = 71, Token = 12 + 1200, // Index = 71, Token = 13 + 1232, // Index = 71, Token = 14 + 1264, // Index = 71, Token = 15 + 1136, // Index = 72, Token = 0 + 1136, // Index = 72, Token = 1 + 1136, // Index = 72, Token = 2 + 1136, // Index = 72, Token = 3 + 1184, // Index = 72, Token = 4 + 1216, // Index = 72, Token = 5 + 1248, // Index = 72, Token = 6 + 1280, // Index = 72, Token = 7 + 1136, // Index = 72, Token = 8 + 1136, // Index = 72, Token = 9 + 1136, // Index = 72, Token = 10 + 1136, // Index = 72, Token = 11 + 1184, // Index = 72, Token = 12 + 1216, // Index = 72, Token = 13 + 1248, // Index = 72, Token = 14 + 1280, // Index = 72, Token = 15 + 1152, // Index = 73, Token = 0 + 1152, // Index = 73, Token = 1 + 1152, // Index = 73, Token = 2 + 1152, // Index = 73, Token = 3 + 1200, // Index = 73, Token = 4 + 1232, // Index = 73, Token = 5 + 1264, // Index = 73, Token = 6 + 1296, // Index = 73, Token = 7 + 1152, // Index = 73, Token = 8 + 1152, // Index = 73, Token = 9 + 1152, // Index = 73, Token = 10 + 1152, // Index = 73, Token = 11 + 1200, // Index = 73, Token = 12 + 1232, // Index = 73, Token = 13 + 1264, // Index = 73, Token = 14 + 1296, // Index = 73, Token = 15 + 1168, // Index = 74, Token = 0 + 1168, // Index = 74, Token = 1 + 1168, // Index = 74, Token = 2 + 1168, // Index = 74, Token = 3 + 1216, // Index = 74, Token = 4 + 1248, // Index = 74, Token = 5 + 1280, // Index = 74, Token = 6 + 1312, // Index = 74, Token = 7 + 1168, // Index = 74, Token = 8 + 1168, // Index = 74, Token = 9 + 1168, // Index = 74, Token = 10 + 1168, // Index = 74, Token = 11 + 1216, // Index = 74, Token = 12 + 1248, // Index = 74, Token = 13 + 1280, // Index = 74, Token = 14 + 1312, // Index = 74, Token = 15 + 1184, // Index = 75, Token = 0 + 1184, // Index = 75, Token = 1 + 1184, // Index = 75, Token = 2 + 1184, // Index = 75, Token = 3 + 1232, // Index = 75, Token = 4 + 1264, // Index = 75, Token = 5 + 1296, // Index = 75, Token = 6 + 1328, // Index = 75, Token = 7 + 1184, // Index = 75, Token = 8 + 1184, // Index = 75, Token = 9 + 1184, // Index = 75, Token = 10 + 1184, // Index = 75, Token = 11 + 1232, // Index = 75, Token = 12 + 1264, // Index = 75, Token = 13 + 1296, // Index = 75, Token = 14 + 1328, // Index = 75, Token = 15 + 1200, // Index = 76, Token = 0 + 1200, // Index = 76, Token = 1 + 1200, // Index = 76, Token = 2 + 1200, // Index = 76, Token = 3 + 1248, // Index = 76, Token = 4 + 1280, // Index = 76, Token = 5 + 1312, // Index = 76, Token = 6 + 1344, // Index = 76, Token = 7 + 1200, // Index = 76, Token = 8 + 1200, // Index = 76, Token = 9 + 1200, // Index = 76, Token = 10 + 1200, // Index = 76, Token = 11 + 1248, // Index = 76, Token = 12 + 1280, // Index = 76, Token = 13 + 1312, // Index = 76, Token = 14 + 1344, // Index = 76, Token = 15 + 1216, // Index = 77, Token = 0 + 1216, // Index = 77, Token = 1 + 1216, // Index = 77, Token = 2 + 1216, // Index = 77, Token = 3 + 1264, // Index = 77, Token = 4 + 1296, // Index = 77, Token = 5 + 1328, // Index = 77, Token = 6 + 1360, // Index = 77, Token = 7 + 1216, // Index = 77, Token = 8 + 1216, // Index = 77, Token = 9 + 1216, // Index = 77, Token = 10 + 1216, // Index = 77, Token = 11 + 1264, // Index = 77, Token = 12 + 1296, // Index = 77, Token = 13 + 1328, // Index = 77, Token = 14 + 1360, // Index = 77, Token = 15 + 1232, // Index = 78, Token = 0 + 1232, // Index = 78, Token = 1 + 1232, // Index = 78, Token = 2 + 1232, // Index = 78, Token = 3 + 1280, // Index = 78, Token = 4 + 1312, // Index = 78, Token = 5 + 1344, // Index = 78, Token = 6 + 1376, // Index = 78, Token = 7 + 1232, // Index = 78, Token = 8 + 1232, // Index = 78, Token = 9 + 1232, // Index = 78, Token = 10 + 1232, // Index = 78, Token = 11 + 1280, // Index = 78, Token = 12 + 1312, // Index = 78, Token = 13 + 1344, // Index = 78, Token = 14 + 1376, // Index = 78, Token = 15 + 1248, // Index = 79, Token = 0 + 1248, // Index = 79, Token = 1 + 1248, // Index = 79, Token = 2 + 1248, // Index = 79, Token = 3 + 1296, // Index = 79, Token = 4 + 1328, // Index = 79, Token = 5 + 1360, // Index = 79, Token = 6 + 1392, // Index = 79, Token = 7 + 1248, // Index = 79, Token = 8 + 1248, // Index = 79, Token = 9 + 1248, // Index = 79, Token = 10 + 1248, // Index = 79, Token = 11 + 1296, // Index = 79, Token = 12 + 1328, // Index = 79, Token = 13 + 1360, // Index = 79, Token = 14 + 1392, // Index = 79, Token = 15 + 1264, // Index = 80, Token = 0 + 1264, // Index = 80, Token = 1 + 1264, // Index = 80, Token = 2 + 1264, // Index = 80, Token = 3 + 1312, // Index = 80, Token = 4 + 1344, // Index = 80, Token = 5 + 1376, // Index = 80, Token = 6 + 1408, // Index = 80, Token = 7 + 1264, // Index = 80, Token = 8 + 1264, // Index = 80, Token = 9 + 1264, // Index = 80, Token = 10 + 1264, // Index = 80, Token = 11 + 1312, // Index = 80, Token = 12 + 1344, // Index = 80, Token = 13 + 1376, // Index = 80, Token = 14 + 1408, // Index = 80, Token = 15 + 1280, // Index = 81, Token = 0 + 1280, // Index = 81, Token = 1 + 1280, // Index = 81, Token = 2 + 1280, // Index = 81, Token = 3 + 1328, // Index = 81, Token = 4 + 1360, // Index = 81, Token = 5 + 1392, // Index = 81, Token = 6 + 1408, // Index = 81, Token = 7 + 1280, // Index = 81, Token = 8 + 1280, // Index = 81, Token = 9 + 1280, // Index = 81, Token = 10 + 1280, // Index = 81, Token = 11 + 1328, // Index = 81, Token = 12 + 1360, // Index = 81, Token = 13 + 1392, // Index = 81, Token = 14 + 1408, // Index = 81, Token = 15 + 1296, // Index = 82, Token = 0 + 1296, // Index = 82, Token = 1 + 1296, // Index = 82, Token = 2 + 1296, // Index = 82, Token = 3 + 1344, // Index = 82, Token = 4 + 1376, // Index = 82, Token = 5 + 1408, // Index = 82, Token = 6 + 1408, // Index = 82, Token = 7 + 1296, // Index = 82, Token = 8 + 1296, // Index = 82, Token = 9 + 1296, // Index = 82, Token = 10 + 1296, // Index = 82, Token = 11 + 1344, // Index = 82, Token = 12 + 1376, // Index = 82, Token = 13 + 1408, // Index = 82, Token = 14 + 1408, // Index = 82, Token = 15 + 1312, // Index = 83, Token = 0 + 1312, // Index = 83, Token = 1 + 1312, // Index = 83, Token = 2 + 1312, // Index = 83, Token = 3 + 1360, // Index = 83, Token = 4 + 1392, // Index = 83, Token = 5 + 1408, // Index = 83, Token = 6 + 1408, // Index = 83, Token = 7 + 1312, // Index = 83, Token = 8 + 1312, // Index = 83, Token = 9 + 1312, // Index = 83, Token = 10 + 1312, // Index = 83, Token = 11 + 1360, // Index = 83, Token = 12 + 1392, // Index = 83, Token = 13 + 1408, // Index = 83, Token = 14 + 1408, // Index = 83, Token = 15 + 1328, // Index = 84, Token = 0 + 1328, // Index = 84, Token = 1 + 1328, // Index = 84, Token = 2 + 1328, // Index = 84, Token = 3 + 1376, // Index = 84, Token = 4 + 1408, // Index = 84, Token = 5 + 1408, // Index = 84, Token = 6 + 1408, // Index = 84, Token = 7 + 1328, // Index = 84, Token = 8 + 1328, // Index = 84, Token = 9 + 1328, // Index = 84, Token = 10 + 1328, // Index = 84, Token = 11 + 1376, // Index = 84, Token = 12 + 1408, // Index = 84, Token = 13 + 1408, // Index = 84, Token = 14 + 1408, // Index = 84, Token = 15 + 1344, // Index = 85, Token = 0 + 1344, // Index = 85, Token = 1 + 1344, // Index = 85, Token = 2 + 1344, // Index = 85, Token = 3 + 1392, // Index = 85, Token = 4 + 1408, // Index = 85, Token = 5 + 1408, // Index = 85, Token = 6 + 1408, // Index = 85, Token = 7 + 1344, // Index = 85, Token = 8 + 1344, // Index = 85, Token = 9 + 1344, // Index = 85, Token = 10 + 1344, // Index = 85, Token = 11 + 1392, // Index = 85, Token = 12 + 1408, // Index = 85, Token = 13 + 1408, // Index = 85, Token = 14 + 1408, // Index = 85, Token = 15 + 1360, // Index = 86, Token = 0 + 1360, // Index = 86, Token = 1 + 1360, // Index = 86, Token = 2 + 1360, // Index = 86, Token = 3 + 1408, // Index = 86, Token = 4 + 1408, // Index = 86, Token = 5 + 1408, // Index = 86, Token = 6 + 1408, // Index = 86, Token = 7 + 1360, // Index = 86, Token = 8 + 1360, // Index = 86, Token = 9 + 1360, // Index = 86, Token = 10 + 1360, // Index = 86, Token = 11 + 1408, // Index = 86, Token = 12 + 1408, // Index = 86, Token = 13 + 1408, // Index = 86, Token = 14 + 1408, // Index = 86, Token = 15 + 1376, // Index = 87, Token = 0 + 1376, // Index = 87, Token = 1 + 1376, // Index = 87, Token = 2 + 1376, // Index = 87, Token = 3 + 1408, // Index = 87, Token = 4 + 1408, // Index = 87, Token = 5 + 1408, // Index = 87, Token = 6 + 1408, // Index = 87, Token = 7 + 1376, // Index = 87, Token = 8 + 1376, // Index = 87, Token = 9 + 1376, // Index = 87, Token = 10 + 1376, // Index = 87, Token = 11 + 1408, // Index = 87, Token = 12 + 1408, // Index = 87, Token = 13 + 1408, // Index = 87, Token = 14 + 1408, // Index = 87, Token = 15 + 1392, // Index = 88, Token = 0 + 1392, // Index = 88, Token = 1 + 1392, // Index = 88, Token = 2 + 1392, // Index = 88, Token = 3 + 1408, // Index = 88, Token = 4 + 1408, // Index = 88, Token = 5 + 1408, // Index = 88, Token = 6 + 1408, // Index = 88, Token = 7 + 1392, // Index = 88, Token = 8 + 1392, // Index = 88, Token = 9 + 1392, // Index = 88, Token = 10 + 1392, // Index = 88, Token = 11 + 1408, // Index = 88, Token = 12 + 1408, // Index = 88, Token = 13 + 1408, // Index = 88, Token = 14 + 1408 // Index = 88, Token = 15 +}; + diff --git a/code/redalert/jshell.cpp b/code/redalert/jshell.cpp new file mode 100644 index 0000000..9254ec3 --- /dev/null +++ b/code/redalert/jshell.cpp @@ -0,0 +1,659 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/JSHELL.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 2, 1994 * + * * + * Last Update : May 11, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Build_Translucent_Table -- Creates a translucent control table. * + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * Fatal -- General purpose fatal error handler. * + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * Set_Window -- Sets the window dimensions to that specified. * + * Small_Icon -- Create a small icon from a big one. * + * Translucent_Table_Size -- Determines the size of a translucent table. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wwfile.h" + + +/*********************************************************************************************** + * Small_Icon -- Create a small icon from a big one. * + * * + * This routine will extract the specified icon from the icon data file and convert that * + * icon into a small (3x3) representation. Typical use of this mini-icon is for the radar * + * map. * + * * + * INPUT: iconptr -- Pointer to the icon data file. * + * * + * iconnum -- The embedded icon number to convert into a small image. * + * * + * OUTPUT: Returns with a pointer to the small icon imagery. This is exactly 9 bytes long. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + *=============================================================================================*/ +void * Small_Icon(void const * iconptr, int iconnum) +{ + static unsigned char _icon[9]; + IControl_Type const * iptr = (IControl_Type const *)iconptr; + unsigned char * data; + + if (iconptr) { + iconnum = ((char *)((char *)iptr + iptr->Map))[iconnum]; + data = &((unsigned char *)((unsigned char *)iptr + iptr->Icons))[iconnum*(24*24)]; +// data = &iptr->Icons[iconnum*(24*24)]; + + for (int index = 0; index < 9; index++) { + int _offsets[9] = { + 4+4*24, + 12+4*24, + 20+4*24, + 4+12*24, + 12+12*24, + 20+12*24, + 4+20*24, + 12+20*24, + 20+20*24 + }; + _icon[index] = data[_offsets[index]]; + } + } + + return(_icon); +} + + +/*********************************************************************************************** + * Set_Window -- Sets the window dimensions to that specified. * + * * + * Use this routine to set the windows dimensions to the coordinates and dimensions * + * specified. * + * * + * INPUT: x -- Window X pixel position. * + * * + * y -- Window Y pixel position. * + * * + * w -- Window width in pixels. * + * * + * h -- Window height in pixels. * + * * + * OUTPUT: none * + * * + * WARNINGS: The X and width values are truncated to an even 8 pixel boundary. This is * + * the same as stripping off the lower 3 bits. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void Set_Window(int window, int x, int y, int w, int h) +{ + WindowList[window][WINDOWWIDTH] = w; + WindowList[window][WINDOWHEIGHT] = h; + WindowList[window][WINDOWX] = x; + WindowList[window][WINDOWY] = y; +} + + +/*********************************************************************************************** + * Fatal -- General purpose fatal error handler. * + * * + * This is a very simple general purpose fatal error handler. It goes directly to text * + * mode, prints the error, and then aborts with a failure code. * + * * + * INPUT: message -- The text message to display. * + * * + * ... -- Any optional parameters that are used in formatting the message. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine never returns. The game exits immediately. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void Fatal(char const * message, ...) +{ + va_list va; + + va_start(va, message); + Prog_End(message, true); + vfprintf(stderr, message, va); + Mono_Printf(message); + if (!RunningAsDLL) { //PG + Emergency_Exit(EXIT_FAILURE); + } +} + + +#ifdef NEVER +void File_Fatal(char const * message) +{ + //Prog_End(); + perror(message); + Emergency_Exit(EXIT_FAILURE); +} +#endif + + + +/*********************************************************************************************** + * Load_Uncompress -- Loads and uncompresses data to a buffer. * + * * + * This is the C++ counterpart to the Load_Uncompress function. It will load the file * + * specified into the graphic buffer indicated and uncompress it. * + * * + * INPUT: file -- The file to load and uncompress. * + * * + * uncomp_buff -- The graphic buffer that initial loading will use. * + * * + * dest_buff -- The buffer that will hold the uncompressed data. * + * * + * reserved_data -- This is an optional pointer to a buffer that will hold any * + * reserved data the compressed file may contain. This is * + * typically a palette. * + * * + * OUTPUT: Returns with the size of the uncompressed data in the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +long Load_Uncompress(FileClass &file, BuffType &uncomp_buff, BuffType &dest_buff, void * reserved_data) +{ + unsigned short size; + void * sptr = uncomp_buff.Get_Buffer(); + void * dptr = dest_buff.Get_Buffer(); + int opened = false; + CompHeaderType header; + + /* + ** The file must be opened in order to be read from. If the file + ** isn't opened, then open it. Record this fact so that it can be + ** restored to its closed state at the end. + */ + if (!file.Is_Open()) { + if (!file.Open()) { + return(0); + } + opened = true; + } + + /* + ** Read in the size of the file (supposedly). + */ + file.Read(&size, sizeof(size)); + + /* + ** Read in the header block. This block contains the compression type + ** and skip data (among other things). + */ + file.Read(&header, sizeof(header)); + size -= sizeof(header); + + /* + ** If there are skip bytes then they must be processed. Either read + ** them into the buffer provided or skip past them. No check is made + ** to ensure that the reserved data buffer is big enough (watch out!). + */ + if (header.Skip) { + size -= header.Skip; + if (reserved_data) { + file.Read(reserved_data, header.Skip); + } else { + file.Seek(header.Skip, SEEK_CUR); + } + header.Skip = 0; + } + + /* + ** Determine where is the proper place to load the data. If both buffers + ** specified are identical, then the data should be loaded at the end of + ** the buffer and decompressed at the beginning. + */ + if (uncomp_buff.Get_Buffer() == dest_buff.Get_Buffer()) { + sptr = (char *)sptr + uncomp_buff.Get_Size()-(size+sizeof(header)); + } + + /* + ** Read in the bulk of the data. + */ + Mem_Copy(&header, sptr, sizeof(header)); + file.Read((char *)sptr + sizeof(header), size); + + /* + ** Decompress the data. + */ + size = (unsigned int) Uncompress_Data(sptr, dptr); + + /* + ** Close the file if necessary. + */ + if (opened) { + file.Close(); + } + return((long)size); +} + + +int Load_Picture(char const * filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char * palette, PicturePlaneType ) +{ + return(Load_Uncompress(CCFileClass(filename), scratchbuf, destbuf, palette ) / 8000); +} + + +/*********************************************************************************************** + * Load_Alloc_Data -- Allocates a buffer and loads the file into it. * + * * + * This is the C++ replacement for the Load_Alloc_Data function. It will allocate the * + * memory big enough to hold the file and then read the file into it. * + * * + * INPUT: file -- The file to read. * + * * + * mem -- The memory system to use for allocation. * + * * + * OUTPUT: Returns with a pointer to the allocated and filled memory block. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void * Load_Alloc_Data(FileClass &file) +{ + void * ptr = 0; + long size = file.Size(); + + ptr = new char [size]; + if (ptr) { + file.Read(ptr, size); + } + return(ptr); +} + + +/*********************************************************************************************** + * Translucent_Table_Size -- Determines the size of a translucent table. * + * * + * Use this routine to determine how big the translucent table needs * + * to be given the specified number of colors. This value is typically * + * used when allocating the buffer for the translucent table. * + * * + * INPUT: count -- The number of colors that are translucent. * + * * + * OUTPUT: Returns the size of the translucent table. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +long Translucent_Table_Size(int count) +{ + return(256L + (256L * count)); +} + + +/*********************************************************************************************** + * Build_Translucent_Table -- Creates a translucent control table. * + * * + * The table created by this routine is used by Draw_Shape (GHOST) to * + * achieve a translucent affect. The original color of the shape will * + * show through. This differs from the fading effect, since that * + * affect only alters the background color toward a single destination * + * color. * + * * + * INPUT: palette -- Pointer to the control palette. * + * * + * control -- Pointer to array of structures that control how * + * the translucent table will be built. * + * * + * count -- The number of entries in the control array. * + * * + * buffer -- Pointer to buffer to place the translucent table. * + * If NULL is passed in, then the buffer will be * + * allocated. * + * * + * OUTPUT: Returns with pointer to the translucent table. * + * * + * WARNINGS: This routine is exceedingly slow. Use sparingly. * + * * + * HISTORY: * + * 04/02/1994 JLB : Created. * + *=============================================================================================*/ +void * Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + int index; // Working color index. + + if (count && control/* && palette*/) { // palette can't be NULL... ST - 5/9/2019 + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Build_Fading_Table(palette.Get_Data(), (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +/*********************************************************************************************** + * Conquer_Build_Translucent_Table -- Builds fading table for shadow colors only. * + * * + * This routine will build a translucent (fading) table to remap colors into the shadow * + * color region of the palette. Shadow colors are not affected by this translucent table. * + * This means that a shape can be overlapped any number of times and the imagery will * + * remain deterministic (and constant). * + * * + * INPUT: palette -- Pointer to the palette to base the translucent process on. * + * * + * control -- Pointer to special control structure that specifies the * + * target color, and percentage of fade. * + * * + * count -- The number of colors to be remapped (entries in the control array). * + * * + * buffer -- Pointer to the staging buffer that will hold the translucent table * + * data. If this parameter is NULL, then an appropriate sized table * + * will be allocated. * + * * + * OUTPUT: Returns with a pointer to the translucent table data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +void * Conquer_Build_Translucent_Table(PaletteClass const & palette, TLucentType const * control, int count, void * buffer) +{ + unsigned char const *table; // Remap table pointer. + + if (count && control) { + if (!buffer) { + buffer = new char [Translucent_Table_Size(count)]; + } + + if (buffer) { + memset(buffer, -1, 256); + table = (unsigned char*)buffer + 256; + + /* + ** Build the individual remap tables for each translucent color. + */ + for (int index = 0; index < count; index++) { + ((unsigned char*)buffer)[control[index].SourceColor] = index; + Conquer_Build_Fading_Table(palette, (void*)table, control[index].DestColor, control[index].Fading); + table = (unsigned char*)table + 256; + } + } + } + return(buffer); +} + + +void * Make_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + *ptr++ = palette.Closest_Color(trycolor); + } + } + return(dest); +} + + +void * Conquer_Build_Fading_Table(PaletteClass const & palette, void * dest, int color, int frac) +{ + if (dest) { + unsigned char * ptr = (unsigned char *)dest; +// HSVClass desthsv = palette[color]; + + /* + ** Find an appropriate remap color index for every color in the palette. + ** There are certain exceptions to this, but they are trapped within the + ** loop. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + + /* + ** If this color should not be remapped, then it will be stored as a remap + ** to itself. This is effectively no remap. + */ + if (index > PaletteClass::COLOR_COUNT-16 || index == 0) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. This special range is used for shadows or other effects that are + ** not compounded if additively applied. + */ + int best = -1; + int bvalue = 0; + for (int id = PaletteClass::COLOR_COUNT-16; id < PaletteClass::COLOR_COUNT-1; id++) { + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + *ptr++ = best; + } + } + } + return(dest); +} + + +#ifdef OBSOLETE +//int Desired_Facing8(int x1, int y1, int x2, int y2) +DirType xDesired_Facing8(int x1, int y1, int x2, int y2) +{ + int index = 0; // Facing composite value. + + /* + ** Figure the absolute X difference. This determines + ** if the facing is leftward or not. + */ + int xdiff = x2-x1; + if (xdiff < 0) { + index |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Figure the absolute Y difference. This determines + ** if the facing is downward or not. This also clarifies + ** exactly which quadrant the facing lies. + */ + int ydiff = y1-y2; + if (ydiff < 0) { + index ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** If on the diagonal, then incorporate this into the facing + ** and then bail. The facing is known. + */ + if (((bigger+1)/2) <= smaller) { + index += 0x0020; + return(DirType(index)); + } + + /* + ** Determine if the facing is closer to the Y axis or + ** the X axis. + */ + int adder = (index & 0x0040); + if (xdiff == bigger) { + adder ^= 0x0040; + } + index += adder; + + return(DirType(index)); +} + + +//int Desired_Facing256(int srcx, int srcy, int dstx, int dsty) +DirType xDesired_Facing256(int srcx, int srcy, int dstx, int dsty) +{ + int composite=0; // Facing built from intermediate calculations. + + /* + ** Fetch the absolute X difference. This also gives a clue as + ** to which hemisphere the direction lies. + */ + int xdiff = dstx - srcx; + if (xdiff < 0) { + composite |= 0x00C0; + xdiff = -xdiff; + } + + /* + ** Fetch the absolute Y difference. This clarifies the exact + ** quadrant that the direction lies. + */ + int ydiff = srcy - dsty; + if (ydiff < 0) { + composite ^= 0x0040; + ydiff = -ydiff; + } + + /* + ** Bail early if the coordinates are the same. This check also + ** has the added bonus of ensuring that checking for division + ** by zero is not needed in the following section. + */ + if (xdiff == 0 && ydiff == 0) return(DirType(0xFF)); + + /* + ** Determine which of the two direction offsets it bigger. The + ** offset direction that is bigger (X or Y) will indicate which + ** orthogonal direction the facing is closer to. + */ + unsigned bigger; + unsigned smaller; + if (xdiff < ydiff) { + smaller = xdiff; + bigger = ydiff; + } else { + smaller = ydiff; + bigger = xdiff; + } + + /* + ** Now that the quadrant is known, we need to determine how far + ** from the orthogonal directions, the facing lies. This value + ** is calculated as a ratio from 0 (matches orthogonal) to 31 + ** (matches diagonal). + */ + int frac = (smaller * 32U) / bigger; + + /* + ** Given the quadrant and knowing whether the facing is closer + ** to the X or Y axis, we must make an adjustment toward the + ** subsequent quadrant if necessary. + */ + int adder = (composite & 0x0040); + if (xdiff > ydiff) { + adder ^= 0x0040; + } + if (adder) { + frac = (adder - frac)-1; + } + + /* + ** Integrate the fraction value into the quadrant. + */ + composite += frac; + + /* + ** Return with the final facing value. + */ + return(DirType(composite & 0x00FF)); +} +#endif diff --git a/code/redalert/jshell.h b/code/redalert/jshell.h new file mode 100644 index 0000000..b215b52 --- /dev/null +++ b/code/redalert/jshell.h @@ -0,0 +1,478 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/JSHELL.H 1 3/03/97 10:24a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : JSHELL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef JSHELL_H +#define JSHELL_H + + +#include + +#ifdef WIN32 +//#define getch Get_Key_Num +//#define kbhit Check_Key_Num +#include "key.h" +#else +#include +#endif + +/* +** Interface class to the keyboard. This insulates the game from library vagaries. Most +** notable being the return values are declared as "int" in the library whereas C&C +** expects it to be of KeyNumType. +*/ +#ifdef WIN32 +//#define KeyNumType int +//#define KeyASCIIType int + +//lint -esym(1725,KeyboardClass::MouseQX,KeyboardClass::MouseQY) +struct KeyboardClass : public WWKeyboardClass + +#else +struct KeyboardClass +#endif +{ + + /* + ** This flag is used to indicate whether the WW library has taken over + ** the keyboard or not. If not, then the normal console input + ** takes precedence. + */ + unsigned IsLibrary; + +#ifndef WIN32 + int &MouseQX; + int &MouseQY; + + KeyboardClass() : + IsLibrary(true), + MouseQX(::MouseQX), + MouseQY(::MouseQY) + {} + KeyNumType Get(void) {return (IsLibrary ? (KeyNumType)Get_Key_Num() : (KeyNumType)getch());}; + KeyNumType Check(void) {return (IsLibrary ? (KeyNumType)Check_Key_Num() : (KeyNumType)kbhit());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)KN_To_KA(key));}; + void Clear(void) {if (IsLibrary) Clear_KeyBuffer();}; + int Down(KeyNumType key) {return(Key_Down(key));}; +#else + KeyboardClass() : IsLibrary(true) {} + KeyNumType Get(void) {return ((KeyNumType)WWKeyboardClass::Get());}; + KeyNumType Check(void) {return ((KeyNumType)WWKeyboardClass::Check());}; + KeyASCIIType To_ASCII(KeyNumType key) {return((KeyASCIIType)WWKeyboardClass::To_ASCII(key));}; + void Clear(void) {WWKeyboardClass::Clear();}; + int Down(KeyNumType key) {return(WWKeyboardClass::Down(key));}; +#endif + + int Mouse_X(void) {return(Get_Mouse_X());}; + int Mouse_Y(void) {return(Get_Mouse_Y());}; +}; + + +/* +** These templates allow enumeration types to have simple bitwise +** arithmatic performed. The operators must be instatiated for the +** enumerated types desired. +*/ +template inline T operator ++(T & a) +{ + a = (T)((int)a + (int)1); + return(a); +} +template inline T operator ++(T & a, int) +{ + T aa = a; + a = (T)((int)a + (int)1); + return(aa); +} +template inline T operator --(T & a) +{ + a = (T)((int)a - (int)1); + return(a); +} +template inline T operator --(T & a, int) +{ + T aa = a; + a = (T)((int)a - (int)1); + return(aa); +} +template inline T operator |(T t1, T t2) +{ + return((T)((int)t1 | (int)t2)); +} +template inline T operator &(T t1, T t2) +{ + return((T)((int)t1 & (int)t2)); +} +template inline T operator ~(T t1) +{ + return((T)(~(int)t1)); +} + +#ifndef WIN32 +template inline T min(T value1, T value2) +{ + if (value1 < value2) { + return(value1); + } + return(value2); +} +int min(int, int); +long min(long, long); + +template inline T max(T value1, T value2) +{ + if (value1 > value2) { + return(value1); + } + return(value2); +} +int max(int, int); +long max(long, long); +#endif + +template inline void swap(T &value1, T &value2) +{ + T temp = value1; + value1 = value2; + value2 = temp; +} +int swap(int, int); +long swap(long, long); + +template inline +T Bound(T original, T minval, T maxval) +{ + if (original < minval) return(minval); + if (original > maxval) return(maxval); + return(original); +}; +int Bound(signed int, signed int, signed int); +unsigned Bound(unsigned, unsigned, unsigned); +long Bound(long, long, long); + +template +T _rotl(T X, int n) +{ + return((T)(( ( ( X ) << n ) | ( ( X ) >> ( (sizeof(T)*8) - n ) ) ))); +} + + +/* +** This macro serves as a general way to determine the number of elements +** within an array. +*/ +#define ARRAY_LENGTH(x) int(sizeof(x)/sizeof(x[0])) +#define ARRAY_SIZE(x) int(sizeof(x)/sizeof(x[0])) + + +/* +** The shape flags are likely to be "or"ed together and other such bitwise +** manipulations. These instatiated operator templates allow this. +*/ +inline ShapeFlags_Type operator |(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator &(ShapeFlags_Type, ShapeFlags_Type); +inline ShapeFlags_Type operator ~(ShapeFlags_Type); + + +void __cdecl Set_Bit(void * array, int bit, int value); +int __cdecl Get_Bit(void const * array, int bit); +int __cdecl First_True_Bit(void const * array); +int __cdecl First_False_Bit(void const * array); +int __cdecl Bound(int original, int min, int max); + +#if (0) +void Set_Bit(void * array, int bit, int value); +#pragma aux Set_Bit parm [esi] [ecx] [eax] \ + modify [esi ebx] = \ + "mov ebx,ecx" \ + "shr ebx,5" \ + "and ecx,01Fh" \ + "btr [esi+ebx*4],ecx" \ + "or eax,eax" \ + "jz ok" \ + "bts [esi+ebx*4],ecx" \ + "ok:" + +int Get_Bit(void const * array, int bit); +#pragma aux Get_Bit parm [esi] [eax] \ + modify [esi ebx] \ + value [eax] = \ + "mov ebx,eax" \ + "shr ebx,5" \ + "and eax,01Fh" \ + "bt [esi+ebx*4],eax" \ + "setc al" + +int First_True_Bit(void const * array); +#pragma aux First_True_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +int First_False_Bit(void const * array); +#pragma aux First_False_Bit parm [esi] \ + modify [esi ebx] \ + value [eax] = \ + "mov eax,-32" \ + "again:" \ + "add eax,32" \ + "mov ebx,[esi]" \ + "not ebx" \ + "add esi,4" \ + "bsf ebx,ebx" \ + "jz again" \ + "add eax,ebx" + +#ifdef OBSOLETE +extern int Bound(int original, int min, int max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jl okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "jg okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jl okmax" \ + "mov eax,ecx" \ + "okmax:" + +extern unsigned Bound(unsigned original, unsigned min, unsigned max); +#pragma aux Bound parm [eax] [ebx] [ecx] \ + modify [eax] \ + value [eax] = \ + "cmp ebx,ecx" \ + "jb okorder" \ + "xchg ebx,ecx" \ + "okorder: cmp eax,ebx" \ + "ja okmin" \ + "mov eax,ebx" \ + "okmin: cmp eax,ecx" \ + "jb okmax" \ + "mov eax,ecx" \ + "okmax:" +#endif + + +unsigned Fixed_To_Cardinal(unsigned base, unsigned fixed); +#pragma aux Fixed_To_Cardinal parm [eax] [edx] \ + modify [edx] \ + value [eax] = \ + "mul edx" \ + "add eax,080h" \ + "test eax,0FF000000h" \ + "jz ok" \ + "mov eax,000FFFFFFh" \ + "ok:" \ + "shr eax,8" + + +unsigned Cardinal_To_Fixed(unsigned base, unsigned cardinal); +#pragma aux Cardinal_To_Fixed parm [ebx] [eax] \ + modify [edx] \ + value [eax] = \ + "or ebx,ebx" \ + "jz fini" \ + "shl eax,8" \ + "xor edx,edx" \ + "div ebx" \ + "fini:" + + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + +#endif + +/* +** Timer objects that fetch the appropriate timer value according to +** the type of timer they are. +*/ +extern long Frame; +class FrameTimerClass +{ + public: + long operator () (void) const {return(Frame);}; + operator long (void) const {return(Frame);}; +}; + + +#ifndef WIN32 +extern bool TimerSystemOn; +extern "C" { + long Get_System_Tick_Count(void); + long Get_User_Tick_Count(void); +} +//bool Init_Timer_System(unsigned int freq, int partial=false); +bool Remove_Timer_System(void); +#else +extern WinTimerClass * WindowsTimer; +#endif + +#ifndef SYSTEM_TIMER_CLASS +#define SYSTEM_TIMER_CLASS +class SystemTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + #else + long operator () (void) const {return(Get_System_Tick_Count());}; + operator long (void) const {return(Get_System_Tick_Count());}; + #endif +}; +#endif + + +class UserTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_User_Tick_Count());}; + #else + long operator () (void) const {return(Get_User_Tick_Count());}; + operator long (void) const {return(Get_User_Tick_Count());}; + #endif +}; + + +template +void Bubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (array[index] > array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (*array[index] > *array[index+1]) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +void PNBubble_Sort(T * array, int count) +{ + if (array != NULL && count > 1) { + bool swapflag; + + do { + swapflag = false; + for (int index = 0; index < count-1; index++) { + if (stricmp(array[index]->Name(), array[index+1]->Name()) > 0) { + T temp = array[index]; + array[index] = array[index+1]; + array[index+1] = temp; + swapflag = true; + } + } + } while (swapflag); + } +} + +template +class SmartPtr +{ + public: + SmartPtr(NoInitClass const &) {} + SmartPtr(T * realptr = 0) : Pointer(realptr) {} + SmartPtr(SmartPtr const & rvalue) : Pointer(rvalue.Pointer) {} + ~SmartPtr(void) {Pointer = 0;} + + operator T * (void) const {return(Pointer);} + + operator long (void) const {return((long)Pointer);} + + SmartPtr operator ++ (int) {assert(Pointer != 0);SmartPtr temp = *this;++Pointer;return(temp);} + SmartPtr & operator ++ (void) {assert(Pointer != 0);++Pointer;return(*this);} + SmartPtr operator -- (int) {assert(Pointer != 0);SmartPtr temp = *this;--Pointer;return(temp);} + SmartPtr & operator -- (void) {assert(Pointer != 0);--Pointer;return(*this);} + + SmartPtr & operator = (SmartPtr const & rvalue) {Pointer = rvalue.Pointer;return(*this);} + T * operator -> (void) const {assert(Pointer != 0);return(Pointer);} + T & operator * (void) const {assert(Pointer != 0);return(*Pointer);} + + private: + T * Pointer; +}; + + +#endif diff --git a/code/redalert/keyboard.h b/code/redalert/keyboard.h new file mode 100644 index 0000000..33f6ddf --- /dev/null +++ b/code/redalert/keyboard.h @@ -0,0 +1,649 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\conquer.cpv 4.74 23 Sep 1996 12:36:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + + +class WWKeyboardClass +{ + public: + /* Define the base constructor and destructors for the class */ + WWKeyboardClass(); + + /* Define the functions which work with the Keyboard Class */ + unsigned short Check(void) const; + unsigned short Get(void); + bool Put(unsigned short key); + void Clear(void); + char To_ASCII(unsigned short num); + bool Down(unsigned short key); + + /* Define the main hook for the message processing loop. */ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + int MouseQX; + int MouseQY; + + private: + + /* + ** This is a keyboard state array that is used to aid in translating + ** KN_ keys into KA_ keys. + */ + unsigned char KeyState[256]; + + /* + ** This is the circular keyboard holding buffer. It holds the VK key and + ** the current shift state at the time the key was added to the queue. + */ + unsigned short Buffer[256]; // buffer which holds actual keypresses + + unsigned short Buff_Get(void); + unsigned short Fetch_Element(void); + unsigned short Peek_Element(void) const; + bool Put_Element(unsigned short val); + bool Is_Buffer_Full(void) const; + bool Is_Buffer_Empty(void) const; + static bool Is_Mouse_Key(unsigned short key); + void Fill_Buffer_From_System(void); + bool Put_Key_Message(unsigned short vk_key, bool release = false); + + /* + ** These are the tracking pointers to maintain the + ** circular keyboard list. + */ + int Head; + int Tail; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA // ; +#define VK_NONE_BB 0xBB // = +#define VK_NONE_BC 0xBC // , +#define VK_NONE_BD 0xBD // - +#define VK_NONE_BE 0xBE // . +#define VK_NONE_BF 0xBF // / +#define VK_NONE_C0 0xC0 // ` +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB // [ +#define VK_NONE_DC 0xDC // '\' +#define VK_NONE_DD 0xDD // ] +#define VK_NONE_DE 0xDE // ' +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE, + KA_EXTEND = VK_ESCAPE, + KA_RETURN = VK_RETURN, + KA_BACKSPACE = VK_BACK, + KA_TAB = VK_TAB , + KA_DELETE = VK_DELETE, /* */ + KA_INSERT = VK_INSERT, /* */ + KA_PGDN = VK_NEXT, /* */ + KA_DOWNRIGHT = VK_NEXT, + KA_DOWN = VK_DOWN, /* */ + KA_END = VK_END, /* */ + KA_DOWNLEFT = VK_END, + KA_RIGHT = VK_RIGHT, /* */ + KA_KEYPAD5 = VK_SELECT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT, /* */ + KA_PGUP = VK_PRIOR, /* */ + KA_UPRIGHT = VK_PRIOR, + KA_UP = VK_UP, /* */ + KA_HOME = VK_HOME, /* */ + KA_UPLEFT = VK_HOME, + KA_F12 = VK_F12, + KA_F11 = VK_F11, + KA_F10 = VK_F10, + KA_F9 = VK_F9, + KA_F8 = VK_F8, + KA_F7 = VK_F7, + KA_F6 = VK_F6, + KA_F5 = VK_F5, + KA_F4 = VK_F4, + KA_F3 = VK_F3, + KA_F2 = VK_F2, + KA_F1 = VK_F1, + KA_LMOUSE = VK_LBUTTON, + KA_RMOUSE = VK_RBUTTON, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, +}; + + +typedef enum KeyNumType { + KN_NONE = 0, + + KN_0 = VK_0, + KN_1 = VK_1, + KN_2 = VK_2, + KN_3 = VK_3, + KN_4 = VK_4, + KN_5 = VK_5, + KN_6 = VK_6, + KN_7 = VK_7, + KN_8 = VK_8, + KN_9 = VK_9, + KN_A = VK_A, + KN_B = VK_B, + KN_BACKSLASH = VK_NONE_DC, + KN_BACKSPACE = VK_BACK, + KN_C = VK_C, + KN_CAPSLOCK = VK_CAPITAL, + KN_CENTER = VK_CLEAR, + KN_COMMA = VK_NONE_BC, + KN_D = VK_D, + KN_DELETE = VK_DELETE, + KN_DOWN = VK_DOWN, + KN_DOWNLEFT = VK_END, + KN_DOWNRIGHT = VK_NEXT, + KN_E = VK_E, + KN_END = VK_END, + KN_EQUAL = VK_NONE_BB, + KN_ESC = VK_ESCAPE, + KN_E_DELETE = VK_DELETE, + KN_E_DOWN = VK_NUMPAD2, + KN_E_END = VK_NUMPAD1, + KN_E_HOME = VK_NUMPAD7, + KN_E_INSERT = VK_INSERT, + KN_E_LEFT = VK_NUMPAD4, + KN_E_PGDN = VK_NUMPAD3, + KN_E_PGUP = VK_NUMPAD9, + KN_E_RIGHT = VK_NUMPAD6, + KN_E_UP = VK_NUMPAD8, + KN_F = VK_F, + KN_F1 = VK_F1, + KN_F10 = VK_F10, + KN_F11 = VK_F11, + KN_F12 = VK_F12, + KN_F2 = VK_F2, + KN_F3 = VK_F3, + KN_F4 = VK_F4, + KN_F5 = VK_F5, + KN_F6 = VK_F6, + KN_F7 = VK_F7, + KN_F8 = VK_F8, + KN_F9 = VK_F9, + KN_G = VK_G, + KN_GRAVE = VK_NONE_C0, + KN_H = VK_H, + KN_HOME = VK_HOME, + KN_I = VK_I, + KN_INSERT = VK_INSERT, + KN_J = VK_J, + KN_K = VK_K, + KN_KEYPAD_ASTERISK= VK_MULTIPLY, + KN_KEYPAD_MINUS = VK_SUBTRACT, + KN_KEYPAD_PLUS = VK_ADD, + KN_KEYPAD_RETURN = VK_RETURN, + KN_KEYPAD_SLASH = VK_DIVIDE, + KN_L = VK_L, + KN_LALT = VK_MENU, + KN_LBRACKET = VK_NONE_DB, + KN_LCTRL = VK_CONTROL, + KN_LEFT = VK_LEFT, + KN_LMOUSE = VK_LBUTTON, + KN_LSHIFT = VK_SHIFT, + KN_M = VK_M, + KN_MINUS = VK_NONE_BD, + KN_N = VK_N, + KN_NUMLOCK = VK_NUMLOCK, + KN_O = VK_O, + KN_P = VK_P, + KN_PAUSE = VK_PAUSE, + KN_PERIOD = VK_NONE_BE, + KN_PGDN = VK_NEXT, + KN_PGUP = VK_PRIOR, + KN_PRNTSCRN = VK_PRINT, + KN_Q = VK_Q, + KN_R = VK_R, + KN_RALT = VK_MENU, + KN_RBRACKET = VK_NONE_DD, + KN_RCTRL = VK_CONTROL, + KN_RETURN = VK_RETURN, + KN_RIGHT = VK_RIGHT, + KN_RMOUSE = VK_RBUTTON, + KN_RSHIFT = VK_SHIFT, + KN_S = VK_S, + KN_SCROLLLOCK = VK_SCROLL, + KN_SEMICOLON = VK_NONE_BA, + KN_SLASH = VK_NONE_BF, + KN_SPACE = VK_SPACE, + KN_SQUOTE = VK_NONE_DE, + KN_T = VK_T, + KN_TAB = VK_TAB, + KN_U = VK_U, + KN_UP = VK_UP, + KN_UPLEFT = VK_HOME, + KN_UPRIGHT = VK_PRIOR, + KN_V = VK_V, + KN_W = VK_W, + KN_X = VK_X, + KN_Y = VK_Y, + KN_Z = VK_Z, + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +} KeyNumType; + + +extern WWKeyboardClass *_Kbd; + +#endif diff --git a/code/redalert/language.h b/code/redalert/language.h new file mode 100644 index 0000000..fc31a8b --- /dev/null +++ b/code/redalert/language.h @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifdef ENGLISH +#define TEXT_ERROR_TIMER "Error - Timer system failed to initialise due to system instability. You need to restart Windows." +#define TEXT_SHORT_TITLE "Red Alert" +#define TEXT_MEMORY_ERROR "Error - out of memory." +#define TEXT_ABORT "Abort" +#define TEXT_DDRAW_ERROR "Error - Unable to allocate primary video buffer - aborting." +#define TEXT_TITLE "Command & Conquer : Red Alert" +#define TEXT_VIDEO_ERROR "Error - Unable to set the video mode." +#define TEXT_INSUFFICIENT "Insufficient Disk Space to run Red Alert.\n" +#define TEXT_MUST_HAVE "You must have %d megabytes of free disk space." +#define TEXT_CRITICALLY_LOW "Warning - you are critically low on free disk space for saving games. Do you want to play Red Alert anyway?" +#define TEXT_NO_RAM "Insufficient RAM available.\n" +#define TEXT_USE_START_MENU "\n\rIf you have Windows 95 running you should start Red Alert\r\nDOS version via the Windows 95 Start Menu.\n\rPress any key.\n\r" +#define TEXT_SETUP_FIRST "Run SETUP program first.\n" +#define TEXT_NO_MOUSE "Red Alert is unable to detect your mouse driver." +#define TEXT_FILE_ERROR "FILE ERROR" +#define TEXT_PRESS_KEY "Press any key to retry." +#define TEXT_ESC_KEY "Press to exit program." +#define TEXT_TO_EXIT "Press any key to exit program." +#define TEXT_INVALID "Invalid option switch.\n" +#define TEXT_MAP_ERROR "Map Error!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continue" +#define TEXT_OPTIONS "Red Alert (c) 1996, Westwood Studios\r\n" \ + "Parameters:\r\n" \ + " -DESTNET = Specify Network Number of destination system\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Network Socket ID (0 - 16383)\n" \ + " -STEALTH = Hide multiplayer names (\"Boss mode\")\r\n" \ + " -MESSAGES = Allow messages from outside this game.\r\n" \ + "\r\n" +#endif + + +#ifdef GERMAN +#define TEXT_ERROR_TIMER "Fehler - das Timer-System konnte aufgrund einer Instabilit„t des Systems nicht initialisiert werden. Bitte starten Sie Windows neu." +#define TEXT_SHORT_TITLE "C&C:AR" +#define TEXT_MEMORY_ERROR "Fehler - Kein Speicher mehr." +#define TEXT_ABORT "Abbrechen" +#define TEXT_DDRAW_ERROR "Fehler - Kann prim„ren Videopuffer nicht finden - Abbruch." +#define TEXT_TITLE "Command & Conquer : Alarmstufe Rot" +#define TEXT_VIDEO_ERROR "Fehler - Kann Grafikmodus nicht einstellen." +#define TEXT_INSUFFICIENT "Nicht genug Festplattenplatz fr Command & Conquer:AR.\n" +#define TEXT_MUST_HAVE "Sie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_CRITICALLY_LOW "Nicht genug Festplattenplatz fr Command & Conquer:AR.\nSie brauchen %d MByte freien Platz auf der Festplatte." +#define TEXT_NO_RAM "Zuwenig Hauptspeicher verfgbar.\n" +#define TEXT_USE_START_MENU "\n\rWenn auf Ihrem Rechner Windows 95 läuft,\n\rstarten Sie die DOS-Version von Alarmstufe Rot\n\rbitte über das Start-Menü von Windows 95.\n\rZum Weitermachen beliebige Taste drücken.\n\r" +#define TEXT_SETUP_FIRST "Bitte erst das SETUP-Programm starten.\n" +#define TEXT_NO_MOUSE "C&C:AR kann Ihren Maustreiber nicht finden..." +#define TEXT_FILE_ERROR "DATEIFEHLER" +#define TEXT_PRESS_KEY "Beliebige Taste drcken fr erneuten Versuch." +#define TEXT_ESC_KEY " drcken, um das Programm zu verlassen." +#define TEXT_TO_EXIT "Beliebige Taste drcken, um das Programm zu verlassen." +#define TEXT_INVALID "Ungltiger Parameter.\n" +#define TEXT_MAP_ERROR "Kartenfehler!" +#define TEXT_STOP "Halt" +#define TEXT_CONTINUE "Weiter" +#define TEXT_OPTIONS "C&C: Alarmstufe Rot (c) 1996, Westwood Studios\r\n" \ + "Parameter:\r\n" \ + " -DESTNET = Netzwerkkennung des Zielrechners festlegen\r\n" \ + " (Syntax: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = Kennung des Netzwerk-Sockets (0 - 16383)\n" \ + " -STEALTH = Namen im Mehrspieler-Modus verstecken (\"Boss-Modus\")\r\n" \ + " -MESSAGES = Mitteilungen von ausserhalb des Spiels zulassen\r\n" \ + "\r\n" +#endif + + +#ifdef FRENCH +#define TEXT_ERROR_TIMER "Error - L'horloge système n'a pas pu s'initialiser en raison de l'instabilit‚ du sytème. Vous devez red‚marrer Windows." +#define TEXT_SHORT_TITLE "Alerte Rouge" +#define TEXT_MEMORY_ERROR "Erreur - Plus de m‚moire." +#define TEXT_ABORT "Interrompre" +#define TEXT_DDRAW_ERROR " Erreur - Impossible d'allouer le tampon vid‚o principal - Interruption." +#define TEXT_TITLE "Command & Conquer : Alerte Rouge" +#define TEXT_VIDEO_ERROR " Erreur - Impossible d'‚tablir le mode vid‚o." +#define TEXT_INSUFFICIENT "Espace disque insuffisant pour lancer Command & Conquer.\n" +#define TEXT_MUST_HAVE "Vous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_CRITICALLY_LOW "Espace disque insuffisant pour lancer Command & Conquer.\nVous devez disposer de %d Mo d'espace disponsible sur le disque dur." +#define TEXT_NO_RAM "M‚moire vive (RAM) insuffisante.\n" +#define TEXT_USE_START_MENU "\n\rSi vous ˆtes dans Windows 95 vous devez lancer Alerte Rouge\r\nDOS … partir du menu D‚marrer de Windows 95.\n\rAppuyez sur n'importe quelle touche.\n\r" +#define TEXT_SETUP_FIRST "Lancez d'abord le programme de configuration SETUP.\n" +#define TEXT_NO_MOUSE "Alerte Rouge ne peut pas d‚tecter votre gestionnaire de souris." +#define TEXT_FILE_ERROR "ERREUR DE FICHIER" +#define TEXT_PRESS_KEY "Appuyez sur une touche pour recommencer." +#define TEXT_ESC_KEY "Appuyez sur Echap pour quitter le programme." +#define TEXT_TO_EXIT "Appuyez sur une touche pour quitter le programme." +#define TEXT_INVALID "Commande d'option invalide.\n" +#define TEXT_MAP_ERROR "Erreur de carte!" +#define TEXT_STOP "Stop" +#define TEXT_CONTINUE "Continuer" +#define TEXT_OPTIONS "Alerte Rouge (c) 1996, Westwood Studios\r\n" \ + "Paramètres:\r\n" \ + " -DESTNET = Sp‚cifier le num‚ro de r‚seau du systŠme de destination\r\n" \ + " (Syntaxe: DESTNETxx.xx.xx.xx)\r\n" \ + " -SOCKET = ID Socket r‚seau (0  16383)\r\n" \ + " -STEALTH = Cacher les noms en mode multijoueurs (\"Mode Boss\")\r\n" \ + " -MESSAGES = Autorise les messages ext‚rieurs … ce jeu.\r\n" \ + "\r\n" +#endif + diff --git a/code/redalert/layer.cpp b/code/redalert/layer.cpp new file mode 100644 index 0000000..a0cff5e --- /dev/null +++ b/code/redalert/layer.cpp @@ -0,0 +1,161 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LAYER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : March 10, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LayerClass::Sort -- Perform an incremental sort pass on the layer's objects. * + * LayerClass::Sorted_Add -- Adds object in sorted order to layer. * + * LayerClass::Submit -- Adds an object to a layer list. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "layer.h" + + +/*********************************************************************************************** + * LayerClass::Submit -- Adds an object to a layer list. * + * * + * This routine is used to add an object to the layer list. If the list is full, then the * + * object is not added. * + * * + * INPUT: object -- Pointer to the object to add. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 05/31/1994 JLB : Allows sorted insert. * + * 01/02/1995 JLB : Fixed to work with EMSListOf template. * + *=============================================================================================*/ +bool LayerClass::Submit(ObjectClass const * object, bool sort) +{ + /* + ** Add the object to the layer. Either at the end (if "sort" is false) or at the + ** appropriately sorted position. + */ + if (sort) { + return(Sorted_Add(object)); + } + return(Add((ObjectClass *)object)); +} + + +/*********************************************************************************************** + * LayerClass::Sort -- Handles sorting the objects in the layer. * + * * + * This routine is used if the layer objects must be sorted and sorting is to occur now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Don't call this routine too often since it does take a bit of time to * + * execute. It is a single pass binary sort and thus isn't horribly slow, * + * but it does take some time. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 03/10/1995 JLB : Uses comparison operator. * + *=============================================================================================*/ +void LayerClass::Sort(void) +{ + for (int index = 0; index < Count()-1; index++) { + if (*(*this)[index+1] < *(*this)[index]) { + ObjectClass * temp; + + temp = (*this)[index+1]; + (*this)[index+1] = (*this)[index]; + (*this)[index] = temp; + } + } +} + + +/*********************************************************************************************** + * DynamicVectorClass::Sorted_Add -- Adds object in sorted order to vector. * + * * + * Use this routine to add an object to the vector but it will be inserted in sorted * + * order. This depends on the ">" operator being defined for the vector object. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added to the vector successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +int LayerClass::Sorted_Add(ObjectClass const * const object) +{ + if ((unsigned)ActiveCount >= Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the right sorted position. + */ + int index; + for (index = 0; index < ActiveCount; index++) { + if ((*(*this)[index]) > (*object)) { + break; + } + } + + /* + ** Make room if the insertion spot is not at the end of the vector. + */ + for (int i = ActiveCount-1; i >= index; i--) { + (*this)[i+1] = (*this)[i]; + } + (*this)[index] = (ObjectClass *)object; + ActiveCount++; + return(true); +} + + diff --git a/code/redalert/layer.h b/code/redalert/layer.h new file mode 100644 index 0000000..f7bcbad --- /dev/null +++ b/code/redalert/layer.h @@ -0,0 +1,64 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LAYER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LAYER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 31, 1994 * + * * + * Last Update : May 31, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LAYER_H +#define LAYER_H + +#include "vector.h" + +class ObjectClass; + +class LayerClass : public DynamicVectorClass +{ + public: + + //----------------------------------------------------------------- + void Sort(void); + bool Submit(ObjectClass const * object, bool sort=false); + int Sorted_Add(ObjectClass const * const object); + + + virtual void Init(void) {Clear();}; + virtual void One_Time(void) {}; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/code/redalert/lcw.cpp b/code/redalert/lcw.cpp new file mode 100644 index 0000000..e7fc70f --- /dev/null +++ b/code/redalert/lcw.cpp @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +/*************************************************************************** + * LCW_Uncomp -- Decompress an LCW encoded data block. * + * * + * Uncompress data to the following codes in the format b = byte, w = word * + * n = byte code pulled from compressed data. * + * * + * Command code, n |Description * + * ------------------------------------------------------------------------* + * n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest * + * n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source* + * n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 * + * n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 * + * n=11111110,w1,b1 |long run of byte b1 for w1 bytes * + * n=10000000 |end of data reached * + * * + * * + * INPUT: * + * void * source ptr * + * void * destination ptr * + * unsigned long length of uncompressed data * + * * + * * + * OUTPUT: * + * unsigned long # of destination bytes written * + * * + * WARNINGS: * + * 3rd argument is dummy. It exists to provide cross-platform * + * compatibility. Note therefore that this implementation does not * + * check for corrupt source data by testing the uncompressed length. * + * * + * HISTORY: * + * 03/20/1995 IML : Created. * + *=========================================================================*/ +int LCW_Uncomp(void const * source, void * dest, unsigned long ) +{ + unsigned char * source_ptr, * dest_ptr, * copy_ptr, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* Read in the operation code. */ + op_code = *source_ptr++; + + if (!(op_code & 0x80)) { + + /* Do a short copy from destination. */ + count = (op_code >> 4) + 3; + copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8)); + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + if (!(op_code & 0x40)) { + + if (op_code == 0x80) { + + /* Return # of destination bytes written. */ + return ((unsigned long) (dest_ptr - (unsigned char*) dest)); + + } else { + + /* Do a medium copy from source. */ + count = op_code & 0x3f; + + while (count--) *dest_ptr++ = *source_ptr++; + } + + } else { + + if (op_code == 0xfe) { + + /* Do a long run. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + word_data = data = *(source_ptr + 2); + word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data; + source_ptr += 3; + + copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3); + count -= (copy_ptr - dest_ptr); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + word_dest_ptr = (unsigned*) dest_ptr; + + dest_ptr += (count & 0xfffffffc); + + while (word_dest_ptr < (unsigned*) dest_ptr) { + *word_dest_ptr = word_data; + *(word_dest_ptr + 1) = word_data; + word_dest_ptr += 2; + } + + copy_ptr = dest_ptr + (count & 0x3); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + } else { + + if (op_code == 0xff) { + + /* Do a long copy from destination. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8); + source_ptr += 4; + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + /* Do a medium copy from destination. */ + count = (op_code & 0x3f) + 3; + copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + source_ptr += 2; + + while (count--) *dest_ptr++ = *copy_ptr++; + } + } + } + } + } +} diff --git a/code/redalert/lcw.h b/code/redalert/lcw.h new file mode 100644 index 0000000..f8b97a4 --- /dev/null +++ b/code/redalert/lcw.h @@ -0,0 +1,46 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCW_H +#define LCW_H + + +int LCW_Uncomp(void const * source, void * dest, unsigned long length=0); + +extern "C" { +int __cdecl LCW_Comp(void const * source, void * dest, int length); +} + +#endif diff --git a/code/redalert/lcwpipe.cpp b/code/redalert/lcwpipe.cpp new file mode 100644 index 0000000..50e84fb --- /dev/null +++ b/code/redalert/lcwpipe.cpp @@ -0,0 +1,310 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lcwpipe.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWPipe::LCWPipe -- Constructor for the LCW processor pipe. * + * * + * This will initialize the LCWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::LCWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LCWPipe::~LCWPipe -- Deconstructor for the LCW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWPipe::~LCWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWPipe::Put -- Send some data through the LCW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LCW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : ((int)sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LCW_Uncomp(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LCW_Comp(Buffer, Buffer2, BlockSize); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LCW_Comp(source, Buffer2, BlockSize); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LCWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LCW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LCW_Comp(Buffer, Buffer2, Counter); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/code/redalert/lcwpipe.h b/code/redalert/lcwpipe.h new file mode 100644 index 0000000..fae2a16 --- /dev/null +++ b/code/redalert/lcwpipe.h @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWPIPE_H +#define LCWPIPE_H + +#include "pipe.h" + + +/* +** Performs LCW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LCWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWPipe(CompControl, int blocksize=1024*8); + virtual ~LCWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWPipe(LCWPipe & rvalue); + LCWPipe & operator = (LCWPipe const & pipe); +}; + + +#endif diff --git a/code/redalert/lcwstraw.cpp b/code/redalert/lcwstraw.cpp new file mode 100644 index 0000000..8d0ab9e --- /dev/null +++ b/code/redalert/lcwstraw.cpp @@ -0,0 +1,176 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LCWStraw::Get -- Fetch data through the LCW processor. * + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lcwstraw.h" +#include "lcw.h" +#include +#include + + +/*********************************************************************************************** + * LCWStraw::LCWStraw -- Constructor for LCW straw object. * + * * + * This will initialize the LCW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::LCWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LCWStraw::~LCWStraw -- Destructor for the LCW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LCWStraw::~LCWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LCWStraw::Get -- Fetch data through the LCW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LCWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LCW_Uncomp(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LCW_Comp(Buffer, &Buffer2[sizeof(BlockHeader)], BlockHeader.UncompCount); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/code/redalert/lcwstraw.h b/code/redalert/lcwstraw.h new file mode 100644 index 0000000..48b877e --- /dev/null +++ b/code/redalert/lcwstraw.h @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LCWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LCWSTRAW_H +#define LCWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LCW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LCWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LCWStraw(CompControl control, int blocksize=1024*8); + virtual ~LCWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LCW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LCWStraw(LCWStraw & rvalue); + LCWStraw & operator = (LCWStraw const & pipe); +}; + + +#endif diff --git a/code/redalert/lcwuncmp.cpp b/code/redalert/lcwuncmp.cpp new file mode 100644 index 0000000..be3906a --- /dev/null +++ b/code/redalert/lcwuncmp.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LCWUNCMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WESTWOOD LIBRARY (PSX) * + * * + * File Name : LCWUNCMP.CPP * + * * + * Programmer : Ian M. Leslie * + * * + * Start Date : May 17, 1995 * + * * + * Last Update : May 17, 1995 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +extern "C" { + +/*************************************************************************** + * LCW_UNCOMPRESS -- Decompress an LCW encoded data block. * + * * + * Uncompress data to the following codes in the format b = byte, w = word * + * n = byte code pulled from compressed data. * + * * + * Command code, n |Description * + * ------------------------------------------------------------------------* + * n=0xxxyyyy,yyyyyyyy |short copy back y bytes and run x+3 from dest * + * n=10xxxxxx,n1,n2,...,nx+1|med length copy the next x+1 bytes from source* + * n=11xxxxxx,w1 |med copy from dest x+3 bytes from offset w1 * + * n=11111111,w1,w2 |long copy from dest w1 bytes from offset w2 * + * n=11111110,w1,b1 |long run of byte b1 for w1 bytes * + * n=10000000 |end of data reached * + * * + * * + * INPUT: * + * void * source ptr * + * void * destination ptr * + * unsigned long length of uncompressed data * + * * + * * + * OUTPUT: * + * unsigned long # of destination bytes written * + * * + * WARNINGS: * + * 3rd argument is dummy. It exists to provide cross-platform * + * compatibility. Note therefore that this implementation does not * + * check for corrupt source data by testing the uncompressed length. * + * * + * HISTORY: * + * 03/20/1995 IML : Created. * + *=========================================================================*/ +unsigned long __cdecl LCW_Uncompress (void * source, void * dest, unsigned long ) +//unsigned long LCW_Uncompress (void * source, void * dest, unsigned long length) +{ + unsigned char * source_ptr, * dest_ptr, * copy_ptr, op_code, data; + unsigned count, * word_dest_ptr, word_data; + + /* Copy the source and destination ptrs. */ + source_ptr = (unsigned char*) source; + dest_ptr = (unsigned char*) dest; + + while (1 /*TRUE*/) { + + /* Read in the operation code. */ + op_code = *source_ptr++; + + if (!(op_code & 0x80)) { + + /* Do a short copy from destination. */ + count = (op_code >> 4) + 3; + copy_ptr = dest_ptr - ((unsigned) *source_ptr++ + (((unsigned) op_code & 0x0f) << 8)); + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + if (!(op_code & 0x40)) { + + if (op_code == 0x80) { + + /* Return # of destination bytes written. */ + return ((unsigned long) (dest_ptr - (unsigned char*) dest)); + + } else { + + /* Do a medium copy from source. */ + count = op_code & 0x3f; + + while (count--) *dest_ptr++ = *source_ptr++; + } + + } else { + + if (op_code == 0xfe) { + + /* Do a long run. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + word_data = data = *(source_ptr + 2); + word_data = (word_data << 24) + (word_data << 16) + (word_data << 8) + word_data; + source_ptr += 3; + + copy_ptr = dest_ptr + 4 - ((unsigned) dest_ptr & 0x3); + count -= (copy_ptr - dest_ptr); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + word_dest_ptr = (unsigned*) dest_ptr; + + dest_ptr += (count & 0xfffffffc); + + while (word_dest_ptr < (unsigned*) dest_ptr) { + *word_dest_ptr = word_data; + *(word_dest_ptr + 1) = word_data; + word_dest_ptr += 2; + } + + copy_ptr = dest_ptr + (count & 0x3); + while (dest_ptr < copy_ptr) *dest_ptr++ = data; + + } else { + + if (op_code == 0xff) { + + /* Do a long copy from destination. */ + count = *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + copy_ptr = (unsigned char*) dest + *(source_ptr + 2) + ((unsigned) *(source_ptr + 3) << 8); + source_ptr += 4; + + while (count--) *dest_ptr++ = *copy_ptr++; + + } else { + + /* Do a medium copy from destination. */ + count = (op_code & 0x3f) + 3; + copy_ptr = (unsigned char*) dest + *source_ptr + ((unsigned) *(source_ptr + 1) << 8); + source_ptr += 2; + + while (count--) *dest_ptr++ = *copy_ptr++; + } + } + } + } + } +} + +} diff --git a/code/redalert/led.h b/code/redalert/led.h new file mode 100644 index 0000000..90d4d0d --- /dev/null +++ b/code/redalert/led.h @@ -0,0 +1,43 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\led.h_v 4.42 04 Jul 1996 16:10:40 JOE_BOSTIC $ */ + +#ifndef LED_H +#define LED_H + +class LEDClass +{ + public: + typedef enum ControlType { + LED_NOCHANGE, // Do nothing (just query). + LED_OFF, // Turn LED off. + LED_ON, // Turn LED on. + LED_TOGGLE // Toggle LED state. + } ControlType; + + protected: + static int Shift_Control(ControlType control, char bit); + + public: + static int Scroll_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x01);}; + static int Caps_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x02);}; + static int Num_Lock(ControlType control=LED_TOGGLE) {return Shift_Control(control, 0x04);}; + + private: + static void Send_To_Keyboard(unsigned char val); +}; + +#endif diff --git a/code/redalert/license.txt b/code/redalert/license.txt new file mode 100644 index 0000000..a4adf6d --- /dev/null +++ b/code/redalert/license.txt @@ -0,0 +1,712 @@ +Electronic Arts Inc. released only TiberianDawn.dll and RedAlert.dll and their corresponding source code under the GPL V3 below, with additional terms at the bottom. + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. + +ADDITIONAL TERMS per GNU GPL Section 7 +No trademark or publicity rights are granted. This license does NOT give you +any right, title or interest in "Command & Conquer" or any other Electronic Arts trademark. You may not distribute any +modification of this program using any Electronic Arts trademark or claim any affiliation or association with Electronic Arts Inc. +or its affiliates or their employees. + +Any propagation or conveyance of this program must include this copyright +notice and these terms. + +If you convey this program (or any modifications of it) and assume +contractual liability for the program to recipients of it, you agree to +indemnify Electronic Arts for any liability that those contractual +assumptions impose on Electronic Arts. + +You may not misrepresent the origins of this program; modified versions of +the program must be marked as such and not identified as the original program. + +This disclaimer supplements the one included in the General Public License. +TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS PROGRAM IS +PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY OF ANY KIND, AND +YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF SATISFACTORY QUALITY AND +PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS DISCLAIMS ANY AND ALL EXPRESS, +IMPLIED OR STATUTORY WARRANTIES, INCLUDING IMPLIED WARRANTIES OF +MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A PARTICULAR PURPOSE, +NONINFRINGEMENT OF THIRD PARTY RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A +COURSE OF DEALING, USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT +AGAINST INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL +MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE UNINTERRUPTED OR +ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE WITH THIRD PARTY SOFTWARE +OR THAT ANY ERRORS IN THE PROGRAM WILL BE CORRECTED. NO ORAL OR WRITTEN ADVICE +PROVIDED BY ELECTRONIC ARTS OR ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A +WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON +IMPLIED WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A +CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY +TO YOU. \ No newline at end of file diff --git a/code/redalert/link.cpp b/code/redalert/link.cpp new file mode 100644 index 0000000..fb39adf --- /dev/null +++ b/code/redalert/link.cpp @@ -0,0 +1,398 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LinkClass::Add -- This object adds itself to the given list * + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * LinkClass::Get_Next -- Fetches the next object in list. * + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * LinkClass::Head_Of_List -- Finds the head of the list. * + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * LinkClass::Remove -- Removes the specified object from the list. * + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * LinkClass::Zap -- Forces the link pointers to NULL. * + * LinkClass::operator= -- Assignment operator for linked list class object. * + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "link.h" + + +/*********************************************************************************************** + * LinkClass::LinkClass -- Copy constructor for linked list object. * + * * + * This copy constructor, unlike the assignment operator, doesn't have to deal with an * + * already initialized and legal link object to the left of the "=". It merely puts the * + * destination object into the same list as the source object. * + * * + * INPUT: link -- Reference to the object on the right of the "=". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::LinkClass(LinkClass const & link) : + Next(0), Prev(0) +{ + /* + ** Add this object to the same list that the copy object + ** resides in. + */ + Add((LinkClass &)link); +} + + +/*********************************************************************************************** + * LinkClass::~LinkClass -- Default destructor for linked list object. * + * * + * This default destructor will remove the object from any linked list it may be part of. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass::~LinkClass(void) +{ + Remove(); +} + + +/*********************************************************************************************** + * LinkClass::Zap -- Forces the link pointers to NULL. * + * * + * This routine will "zap" out the link pointers. This is usually necessary when the link * + * pointers start in an undefined state, but we KNOW that they aren't pointing to anything * + * valid. In such a case it becomes necessary to zap them so that when the object is added * + * to a list, it will be added correctly. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void LinkClass::Zap(void) +{ + Next = 0; + Prev = 0; +} + + +/*********************************************************************************************** + * LinkClass::operator= -- Assignment operator for linked list class object. * + * * + * The assignment operator makes sure that the previous and next pointers remain valid. * + * Because this class only consists of pointers, the assignment operator doesn't actually * + * transfer any data from the source object. It merely makes the destination object part * + * of the same list as the source object. In essence, this is transferring information * + * but not the actual values. * + * * + * If the destination object is already part of another list, it is removed from that list * + * before being added to the source object's list. This ensures that either list remains * + * in a valid condition. * + * * + * INPUT: link -- The object to the right of the "=" operator. * + * * + * OUTPUT: Returns a reference to the rightmost object -- per standard assignment rules. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::operator = (LinkClass const & link) +{ + if (&link == this) return(*this); + + Remove(); + Add((LinkClass &)link); + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Get_Next -- Fetches the next object in list. * + * * + * This routine will return with a pointer to the next object in the list. If there are * + * no more objects, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with pointer to next object in list or NULL if at end of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Next(void) const +{ + return(Next); +} + + +/*********************************************************************************************** + * LinkClass::Get_Prev -- Fetches previous object in linked list. * + * * + * Use this routine to get a pointer to the previous object in the linked list. If there * + * are no previous objects (such as at the head of the list), then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the previous object in the list or NULL if none. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Get_Prev(void) const +{ + return(Prev); +} + + +/*********************************************************************************************** + * LinkClass::Head_Of_List -- Finds the head of the list. * + * * + * Use this routine to scan for and return a reference to the object at the head of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Head_Of_List(void) +{ + LinkClass * link = this; + while (link->Prev) { + link = link->Prev; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Tail_Of_List -- Scans for the object at the end of the list. * + * * + * Use this routine to scan for and return a reference to the object at the end of the * + * list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the object at the end of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Tail_Of_List(void) +{ + LinkClass * link = this; + while (link->Next) { + link = link->Next; + if (link == this) break; // Safety check + } + return(*link); +} + + +/*********************************************************************************************** + * LinkClass::Add -- This object adds itself to the given list * + * * + * Use this routine to add a link object to the list, but to be added right after the * + * given link. This allows inserting a link in the middle of the chain. A quite necessary * + * ability if the chain is order dependant (e.g., the gadget system). * + * * + * INPUT: list -- gadget object to add this one to * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Save ptr to next gadget. + */ + ptr = list.Next; + + /* + ** Link myself in after 'list'. + */ + list.Next = this; + Prev = &list; + + /* + ** Link myself to next gadget, if there is one. + */ + Next = ptr; + if (ptr) { + ptr->Prev = this; + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Add_Head -- This gadget makes itself the head of the given list. * + * * + * INPUT: list -- the list to make myself the head of * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Head(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Head_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Prev = this; + Next = ptr; + Prev = NULL; + + return(*this); +} + + +/*********************************************************************************************** + * LinkClass::Add_Tail -- Add myself to the end of the given list. * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: the head of the list * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & LinkClass::Add_Tail(LinkClass & list) +{ + LinkClass * ptr; + + /* + ** Get head of given list. + */ + ptr = &list.Tail_Of_List(); + + /* + ** Link myself in front of it. + */ + ptr->Next = this; + Prev = ptr; + Next = NULL; + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * LinkClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass * LinkClass::Remove(void) +{ + LinkClass * head = &Head_Of_List(); + LinkClass * tail = &Tail_Of_List(); + + if (Prev) { + Prev->Next = Next; + } + if (Next) { + Next->Prev = Prev; + } + Prev = 0; + Next = 0; + + if (head==this) { + if (tail==this) { + return(0); + } + return(&tail->Head_Of_List()); + } + return(head); +} + + diff --git a/code/redalert/link.h b/code/redalert/link.h new file mode 100644 index 0000000..a2981b9 --- /dev/null +++ b/code/redalert/link.h @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LINK_H +#define LINK_H + + +/* +** This implements a simple linked list. It is possible to add, remove, and traverse the +** list. Since this is a doubly linked list, it is possible to remove an entry from the +** middle of an existing list. +*/ +class LinkClass +{ + public: + LinkClass(NoInitClass const &) {}; + LinkClass(void) : Next(0), Prev(0) {}; + virtual ~LinkClass(void); + + virtual LinkClass * Get_Next(void) const; + virtual LinkClass * Get_Prev(void) const; + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual LinkClass & Head_Of_List(void); + virtual LinkClass & Tail_Of_List(void); + virtual void Zap(void); + virtual LinkClass * Remove(void); + + LinkClass & operator=(LinkClass const & link); // Assignment operator. + LinkClass(LinkClass const & link); // Copy constructor. + + private: + /* + ** Pointers to previous and next link objects in chain. + */ + LinkClass * Next; + LinkClass * Prev; +}; + +#endif diff --git a/code/redalert/lint.h b/code/redalert/lint.h new file mode 100644 index 0000000..c24b512 --- /dev/null +++ b/code/redalert/lint.h @@ -0,0 +1,185 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LINT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LINT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/28/96 * + * * + * Last Update : March 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +// overloading data member (e.g., IsToRedraw) +//lint -e1516 + +// function distinguishing error (actually not an error when conversion operators are used). +//lint -e1053 + + +/* +** The current version of Lint doesn't know how to deal with conversion operators. It +** erroneously generates this error message as a result. Unfortunately, we have to throw +** out the baby with the bathwater on this one. +*/ +//lint -e58 -e56 + +/* +** Hiding a non virtual member function is generally ok. +*/ +//lint -e1511 + +/* +** The warning about not initializing a member during construction is +** valid. However, C&C takes advantage of this by using the in place +** new operator during the load game process. Disable the warning, but +** grudgingly. +*/ +//lint -e1401 + +/* +** Disable warning about non virtual base class destructor for these objects. +*/ +//lint -esym(1509,GraphicBufferClass,GraphicViewPortClass,BufferClass,VideoViewPortClass,GetCDClass) +//lint -esym(1509,BasicTimerClass,FlyClass,FuseClass,StageClass,FlasherClass,CargoClass,DoorClass) +//lint -esym(1509,AbstractTypeClass) + +/* +** The "unusual cast" warning is the result of derived casting. This is +** because there is no "dynamic_cast<>" implemented in Watcom (version 10.5). +*/ +//lint -e740 -e571 + +/* +** "function not previously declared inline" is probably not worth noting. +*/ +//lint -e1727 + +/* +** Allow sub-integer loss of precision. This will allow assigning ints to +** chars without warning. +*/ +//lint -e734 + +/* +** Shifting an integer left is always ok. +*/ +//lint -e701 -e703 + +/* +** Allow repeated include files. The body of the include file should be +** coded to ensure that it is processed only once. +*/ +//lint -e537 + +/* +** Implicitly converting an enum to an integer is almost always ok. +*/ +//lint -e641 + +/* +** It is possible to have template functions for "++" and "--" that +** don't require one of the parameters to be a class object. +*/ +//lint -e1042 + +/* +** Redundant declarations are ok. They are a bit harder to maintain, +** but they facilitate keeping modules less interdependant (include +** file wise). +*/ +//lint -e963 -e762 -e763 + +/* +** Not having a default constructor is ok. +*/ +//lint -e1712 + +/* +** Private constructors are ok. In fact, they are necessary if the +** class object must never be instantiated outside of the class +** itself. +*/ +//lint -e1704 + +/* +** Ignoring the return value from a function is ok. It is very +** common for certain side-effect type functions. +*/ +//lint -e534 + +/* +** Implicitly converting from a signed to an unsigned parameter (or +** visa versa) is ok. +*/ +//lint -e732 -e502 -e713 -e737 -eau + +/* +** Allow functions to overload and hide base functions. This is a +** technique of inheritance that handles function parameter changes. +*/ +//lint -e1411 + +/* +** If a switch statement doesn't have a case for every value (enums) +** but it does have a "default" case, then don't warn about it. +*/ +//lint -e788 + +/* +** If bitwize arithmetic is performed on compatible enumeration types, +** then don't complain. Many enums are used in this fashion. +*/ +//lint -e655 -e656 + +/* +** If a data member is not explicitly initialized in the initializer +** list, this is ok. +*/ +//lint -e1542 + +/* +** Calling "new" when not part of an assignment operation can be valid. This +** is true if the "new" operator has been overloaded and the class keeps +** track of itself. +*/ +//lint -e522 + +/* +** A class that is zero bytes long is ok. This is how method classes +** usually work. +*/ +//lint -e1501 + +/* +** Boolean passed to function is ok. +*/ +//lint -e730 + +/* +** Signed/unsigned mix with relational... ignore for now. +*/ +//lint -e574 + diff --git a/code/redalert/list.cpp b/code/redalert/list.cpp new file mode 100644 index 0000000..e5fa6f7 --- /dev/null +++ b/code/redalert/list.cpp @@ -0,0 +1,969 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LIST.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 23, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ListClass::Add -- This object adds itself to the given list * + * ListClass::Add_Head -- This gadget makes itself the head of the given list. * + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * ListClass::Add_Item -- Adds an item to the list box. * + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * ListClass::Add_Tail -- Add myself to the end of the given list. * + * ListClass::Bump -- Bumps the list box up/down one "page". * + * ListClass::Current_Index -- Fetches the current selected index. * + * ListClass::Current_Item -- Fetches pointer to current item string. * + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * ListClass::Draw_Me -- Draws the listbox. * + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * ListClass::Remove -- Removes the specified object from the list. * + * ListClass::Remove_Item -- Remove specified text from list box. * + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * ListClass::Step -- Moves the list view one line in direction specified. * + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * ListClass::~ListClass -- Destructor for list class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** + * ListClass::ListClass -- class constructor * + * * + * INPUT: id button ID * + * * + * x,y upper-left corner, in pixels * + * * + * w,h width, height, in pixels * + * * + * list ptr to array of char strings to list* + * * + * flags, style flags for mouse, style of listbox * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +ListClass::ListClass (int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + TextFlags = flags; + IsScrollActive = false; + Tabs = 0; + SelectedIndex = 0; + CurrentTopIndex = 0; + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + + +ListClass::ListClass(ListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + + +void ListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + + +/*********************************************************************************************** + * ListClass::~ListClass -- Destructor for list class objects. * + * * + * This is the destructor for list objects. It handles removing anything it might have * + * allocated. This is typically the scroll bar. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ListClass::~ListClass(void) +{ + Remove_Scroll_Bar(); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds an item to the list box. * + * * + * This will add the specified string to the list box. The string is added to the end * + * of the list. * + * * + * INPUT: text -- Pointer to the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(char const * text) +{ + if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + } + return(List.Count() - 1); +} + + +/*********************************************************************************************** + * ListClass::Add_Item -- Adds a text item (as number) to the list box. * + * * + * This will add the text as specified by the text number provided, to the list box. * + * The string is added to the end of the list. * + * * + * INPUT: text -- The text number for the string to add to the list box. * + * OUTPUT: none * + * WARNINGS: Once a string is added to the list box in this fashion, there is no method of * + * retrieving the text number as it relates to any particular index in the list. * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Item(int text) +{ + if (text != TXT_NONE) { + Add_Item(Text_String(text)); + } + return(List.Count() - 1); +} + + +void ListClass::Remove_Item(int index) +{ + if (index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + + +/*********************************************************************************************** + * ListClass::Remove_Item -- Remove specified text from list box. * + * * + * This routine will remove the specified text string from the list box. * + * * + * INPUT: text -- Pointer to the string to remove. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text pointer passed into this routine MUST be the same text pointer that * + * was used to add the string to the list. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Remove_Item(char const * text) +{ + if (text) { + Remove_Item(List.ID(text)); + } +} + + +/*************************************************************************** + * ListClass::Action -- If clicked on, do this! * + * * + * INPUT: int flags -- combination of mouse flags indicating * + * what action to take. * + * * + * OUTPUT: bool result. * + * * + * WARNINGS: none. * + * * + * HISTORY: 01/05/1995 MML : Created. * + *=========================================================================*/ +int ListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + if (SelectedIndex == -1) SelectedIndex = 0; + } + } + return(ControlClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * ListClass::Draw_Me -- Draws the listbox. * + * * + * This routine will render the listbox. * + * * + * INPUT: forced -- Should the listbox be redrawn even if it already thinks it doesn't * + * need to be? This is true when something outside of the gadget system * + * has trashed the screen. * + * * + * OUTPUT: Was the listbox redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box (X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Bump -- Bumps the list box up/down one "page". * + * * + * Use this routine to adjust the "page" that is being viewed in the list box. The view * + * will move up or down (as specified) one page (screen full) of text strings. * + * * + * INPUT: up -- Should the adjustment be up? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Step -- Moves the list view one line in direction specified. * + * * + * This routine will move the current view "page" one line in the direction specified. * + * * + * INPUT: up -- Should the view be moved upward? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + + +/*********************************************************************************************** + * ListClass::Get_Item -- Fetches an arbitrary item string. * + * * + * This routine will fetch an item string from the list box. The item fetched can be any * + * one of the ones in the list. * + * * + * INPUT: index -- The index to examine and return the text pointer from. * + * * + * OUTPUT: Returns with the text pointer to the string at the index position specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Get_Item(int index) const +{ + if (List.Count() == 0) { + return NULL; + } + index = min(index, List.Count()-1); + return(List[index]); +} + + +/*********************************************************************************************** + * ListClass::Current_Item -- Fetches pointer to current item string. * + * * + * This routine will fetch a pointer to the currently selected item's text. * + * * + * INPUT: none * + * * + * OUTPUT: Return with pointer to currently selected text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ListClass::Current_Item(void) const +{ + if (List.Count() <= SelectedIndex) { + return(0); + } + return(List[SelectedIndex]); +} + + +/*********************************************************************************************** + * ListClass::Current_Index -- Fetches the current selected index. * + * * + * This routine will fetch the index number for the currently selected line. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the index of the currently selected line. This ranges from zero to * + * the number of items in the list minus one. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + + +/*********************************************************************************************** + * ListClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * * + * key -- The key value at the time of the event. * + * * + * whom -- Which gadget is being touched. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + + +/*********************************************************************************************** + * ListClass::Set_View_Index -- Sets the top line for the current list view. * + * * + * This routine is used to set the line that will be at the top of the list view. This is * + * how the view can be scrolled up and down. This does not affect the currently selected * + * item. * + * * + * INPUT: index -- The line (index) to move to the top of the list view. * + * * + * OUTPUT: bool; Was the view actually changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, max(0, List.Count() - LineCount)); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Add_Scroll_Bar -- Adds a scroll bar to the list box. * + * * + * This routine will add a scroll bar (with matching arrows) to the list box. They are * + * added to the right edge and cause the interior of the list box to become narrower. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar added? * + * * + * WARNINGS: The list box becomes narrower when the scroll bar is added. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Remove_Scroll_Bar -- Removes the scroll bar if present * + * * + * Use this routine to remove any attached scroll bar to this list box. If the scroll bar * + * is not present, then no action occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the scroll bar removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ListClass::Set_Tabs -- Sets the tab stop list to be used for text printing. * + * * + * This sets the tab stop list to be used for text printing. It specifies a series of * + * pixel offsets for each tab stop. The offsets are from the starting pixel position that * + * the text begins at. * + * * + * INPUT: tabs -- Pointer to a list of tab pixel offsets. * + * * + * OUTPUT: none * + * * + * WARNINGS: Only a pointer to the tabs is recorded by the ListClass object. Make sure that * + * the list remains intact for the duration of the existence of this object. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + + +/*********************************************************************************************** + * ListClass::Draw_Entry -- Draws a list box text line as indicated. * + * * + * This routine is called by the Draw_Me function when it desired to redraw a particular * + * text line in the list box. * + * * + * INPUT: index -- The index of the list entry to draw. This index is based on the * + * total list and NOT the current visible view page. * + * * + * x,y -- Pixel coordinates for the upper left corner of the text entry. * + * * + * width -- The maximum width that the text may draw over. It is expected that * + * this drawing routine entirely fills this length. * + * * + * selected -- bool; Is this a selected (highlighted) listbox entry? * + * * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + TextPrintType flags = TextFlags; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, scheme, TBLACK, flags, width, Tabs); + +} + + +/*********************************************************************************************** + * ListClass::Add -- Adds myself to list immediately after given object * + * * + * Adds the list box to the chain, immediately after the given object. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * * + * INPUT: object -- Pointer to the object to be added right after this one. * + * * + * OUTPUT: Returns with a pointer to the head of the list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Head -- Adds myself to head of the given list * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: Returns with a reference to the object at the head of the list. This should be * + * the same object that is passed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + + +/*********************************************************************************************** + * ListClass::Add_Tail -- Adds myself to tail of given list * + * * + * Adds the list box to the tail of the give chain. The order will be: * + * - Listbox * + * - Up arrow (if active) * + * - Down arrow (if active) * + * - Scroll gadget (if active) * + * * + * INPUT: list -- list to add myself to * + * * + * OUTPUT: none * + * * + * WARNINGS: The previous and next pointers for the added object MUST have been properly * + * initialized for this routine to work correctly. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +LinkClass & ListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + + +/*********************************************************************************************** + * ListClass::Remove -- Removes the specified object from the list. * + * * + * This routine will remove the specified object from the list of objects. Because of the * + * previous and next pointers, it is possible to remove an object from the list without * + * knowing the head of the list. To do this, just call Remove() with the parameter of * + * "this". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the new head of list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +GadgetClass * ListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + + +/*********************************************************************************************** + * ListClass::Set_Selected_Index -- Set the top of the listbox to index specified. * + * * + * This routine will set the top line of the listbox to the index value specified. * + * * + * INPUT: index -- The index to set the top of the listbox to. * + * * + * OUTPUT: none * + * * + * WARNINGS: The requested index may be adjusted to fit within legal parameters. * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + * 01/23/1996 JLB : Forces selected index to always be zero for a null list. * + *=============================================================================================*/ +void ListClass::Set_Selected_Index(int index) +{ + if (index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } else { + SelectedIndex = 0; + } +} + + +/*********************************************************************************************** + * ListClass::Step_Selected_Index -- Change the listbox top line in direction specified. * + * * + * This routine will scroll the top line of the listbox in the direction specified. * + * * + * INPUT: step -- The direction (and amount) to adjust the listbox. If negative value, then * + * the top line is scrolled upward. * + * * + * OUTPUT: Returns with the original top line index number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int ListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + + +void ListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + + +void ListClass::Set_Selected_Index(char const * text) +{ + if (text && List.Count() > 0) { + for (int index = 0; index < List.Count(); index++) { + if (stricmp(List[index], text) == 0) { + Set_Selected_Index(index); + break; + } + } + } +} diff --git a/code/redalert/list.h b/code/redalert/list.h new file mode 100644 index 0000000..1805cd8 --- /dev/null +++ b/code/redalert/list.h @@ -0,0 +1,799 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LIST.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LIST.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LIST_H +#define LIST_H + +#include "control.h" +#include "shapebtn.h" +#include "slider.h" + + +/*************************************************************************** + * ListClass -- Like a Windows ListBox structure * + * * + * INPUT: int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * UWORD flags -- see enumeration choices * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class ListClass : public ControlClass +{ + public: + ListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + ListClass(ListClass const & list); + virtual ~ListClass(void); + + virtual int Add_Item(char const * text); + virtual int Add_Item(int text); + virtual int Add_Scroll_Bar(void); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual char const * Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual char const * Get_Item(int index) const; + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(char const * text); + virtual void Remove_Item(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(char const * text); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Entry(int index, int x, int y, int width, int selected); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const *Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. The pointers + ** are stored in EMS. The text that is pointed to may also be in EMS. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + + +template +class TListClass : public ControlClass +{ + public: + TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down); + TListClass(TListClass const & list); + virtual ~TListClass(void); + T operator [] (int index) const {return(List[index]);}; + T & operator [] (int index) {return(List[index]);}; + + virtual int Add_Item(T text); + virtual int Add_Scroll_Bar(void); + virtual void Insert_Item(T item); + virtual void Bump(int up); + virtual int Count(void) const {return List.Count();}; + virtual int Current_Index(void) const; + virtual T Current_Item(void) const; + virtual int Draw_Me(int forced); + virtual int Step_Selected_Index(int forward); + virtual void Flag_To_Redraw(void); + virtual T Get_Item(int index) const {return(List[index]);}; + + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + virtual void Remove_Item(T); + virtual void Remove_Index(int); + virtual int Remove_Scroll_Bar(void); + virtual void Set_Selected_Index(int index); + virtual void Set_Selected_Index(T item); + virtual void Set_Tabs(int const * tabs); + virtual int Set_View_Index(int index); + virtual void Step(int up); + virtual void Set_Position(int x, int y); + + /* + ** These overloaded list routines handle adding/removing the scroll bar + ** automatically when the list box is added or removed. + */ + virtual LinkClass & Add(LinkClass & object); + virtual LinkClass & Add_Tail(LinkClass & object); + virtual LinkClass & Add_Head(LinkClass & object); + virtual GadgetClass * Remove(void); + + protected: + virtual int Action(unsigned flags, KeyNumType &key); + + /* + ** This controls what the text looks like. It uses the basic TPF_ flags that + ** are used to control Fancy_Text_Print(). + */ + TextPrintType TextFlags; + + /* + ** This is a series of tabstop pixel positions to use when processing any + ** characters found in a list box string. The tabs are a series of + ** pixel offsets from the starting pixel position of the text. + */ + int const * Tabs; + + /* + ** The actual list of text pointers is maintained by this list manager. + */ + DynamicVectorClass List; + + /* + ** This is the total pixel height of a standard line of text. This is greatly + ** influenced by the TextFlags value. + */ + int LineHeight; + + /* + ** This is the number of text lines that can fit within the list box. + */ + int LineCount; + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + unsigned IsScrollActive:1; + ShapeButtonClass UpGadget; + ShapeButtonClass DownGadget; + SliderClass ScrollGadget; + + /* + ** This is the currently selected index. It is highlighted. + */ + int SelectedIndex; + + /* + ** This specifies the line (index) that is at the top of the list box. + */ + int CurrentTopIndex; +}; + +template +TListClass::TListClass(int id, int x, int y, int w, int h, TextPrintType flags, void const * up, void const * down) : + ControlClass(id, x, y, w, h, LEFTPRESS | LEFTRELEASE | KEYBOARD, false), + UpGadget(0, up, x+w, y), + DownGadget(0, down, x+w, y+h), + ScrollGadget(0, x+w, y, 0, h, true), + TextFlags(flags), + Tabs(0), + IsScrollActive(false), + SelectedIndex(0), + CurrentTopIndex(0) +{ + /* + ** Set preliminary values for the slider related gadgets. They don't automatically + ** appear at this time, but there are some values that can be pre-filled in. + */ + UpGadget.X -= UpGadget.Width; + DownGadget.X -= DownGadget.Width; + DownGadget.Y -= DownGadget.Height; + ScrollGadget.X -= max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = Y+UpGadget.Height; + ScrollGadget.Height -= UpGadget.Height + DownGadget.Height; + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); + + /* + ** Set the list box to a default state. + */ + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TextFlags); + LineHeight = FontHeight+FontYSpacing-1; + LineCount = (h-1) / LineHeight; +} + +template +TListClass::TListClass(TListClass const & list) : + ControlClass(list), + TextFlags(list.TextFlags), + Tabs(list.Tabs), + List(list.List), + LineHeight(list.LineHeight), + LineCount(list.LineCount), + IsScrollActive(list.IsScrollActive), + UpGadget(list.UpGadget), + DownGadget(list.DownGadget), + ScrollGadget(list.ScrollGadget), + SelectedIndex(list.SelectedIndex), + CurrentTopIndex(list.CurrentTopIndex) +{ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); +} + +template +void TListClass::Set_Position(int x, int y) +{ + UpGadget.X = x + Width - UpGadget.Width; + UpGadget.Y = y; + DownGadget.X = x + Width - DownGadget.Width; + DownGadget.Y = y + Height - DownGadget.Height; + ScrollGadget.X = x + Width - max(UpGadget.Width, DownGadget.Width); + ScrollGadget.Y = y + UpGadget.Height; + ScrollGadget.Height = Height - (UpGadget.Height + DownGadget.Height); + ScrollGadget.Width = max(UpGadget.Width, DownGadget.Width); +} + +template +TListClass::~TListClass(void) +{ + Remove_Scroll_Bar(); +} + +template +void TListClass::Insert_Item(T item) +{ + if (Current_Index() >= Count()) { + List.Add(item); + } else { + List.Add(item); + + /* + ** Move all trailing items upward. + */ + for (int index = List.Count()-1; index >= Current_Index(); index--) { + List[index+1] = List[index]; + } + + /* + ** Insert the new item into the location at the current index. + */ + List[Current_Index()] = item; + } +} + + +template +int TListClass::Add_Item(T text) +{ +// if (text) { + List.Add(text); + Flag_To_Redraw(); + + /* + ** Add scroll gadget if the list gets too large to display all of the items + ** at the same time. + */ + if (List.Count() > LineCount) { + Add_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one more entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } +// } + return(List.Count() - 1); +} + +template +void TListClass::Remove_Index(int index) +{ + if ((unsigned)index < List.Count()) { + List.Delete(index); + + /* + ** If the list is now small enough to display completely within the list box region, + ** then delete the slider gadget (if they are present). + */ + if (List.Count() <= LineCount) { + Remove_Scroll_Bar(); + } + + /* + ** Tell the slider that there is one less entry in the list. + */ + if (IsScrollActive) { + ScrollGadget.Set_Maximum(List.Count()); + } + + /* + ** If we just removed the selected entry, select the previous one + */ + if (SelectedIndex >= List.Count()) { + SelectedIndex--; + if (SelectedIndex < 0) { + SelectedIndex = 0; + } + } + + /* + ** If we just removed the top-displayed entry, step up one item + */ + if (CurrentTopIndex >= List.Count()) { + CurrentTopIndex--; + if (CurrentTopIndex < 0) + CurrentTopIndex = 0; + if (IsScrollActive) + ScrollGadget.Step(1); + } + } +} + +template +void TListClass::Remove_Item(T text) +{ + Remove_Index(List.ID(text)); +} + +template +int TListClass::Action(unsigned flags, KeyNumType & key) +{ + if (flags & LEFTRELEASE) { + key = KN_NONE; + flags &= (~LEFTRELEASE); + ControlClass::Action(flags, key); + return(true); + } else { + + /* + ** Handle keyboard events here. + */ + if (flags & KEYBOARD) { + + /* + ** Process the keyboard character. If indicated, consume this keyboard event + ** so that the edit gadget ID number is not returned. + */ + if (key == KN_UP) { + Step_Selected_Index(-1); + key = KN_NONE; + } else if (key == KN_DOWN) { + Step_Selected_Index(1); + key = KN_NONE; + } else { + flags &= ~KEYBOARD; + } + + } else { + + int index = Get_Mouse_Y() - (Y+1); + index = index / LineHeight; + SelectedIndex = CurrentTopIndex + index; + SelectedIndex = min(SelectedIndex, List.Count()-1); + } + } + return(ControlClass::Action(flags, key)); +} + +template +int TListClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + + /* + ** Turn off the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + Draw_Box(X, Y, Width, Height, BOXSTYLE_BOX, true); + + /* + ** Draw List. + */ + if (List.Count()) { + for (int index = 0; index < LineCount; index++) { + int line = CurrentTopIndex + index; + + if (List.Count() > line) { + + /* + ** Prints the text and handles right edge clipping and tabs. + */ + List[line]->Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// List[index].Draw_It(line, X+1, Y+(LineHeight*index)+1, Width-2, LineHeight, (line == SelectedIndex), TextFlags); +// Draw_Entry(line, X+1, Y+(LineHeight*index)+1, Width-2, (line == SelectedIndex)); + } + } + } + + /* + ** Turn on the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + +template +void TListClass::Bump(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +template +void TListClass::Step(int up) +{ + if (IsScrollActive) { + if (ScrollGadget.Step(up)) { + CurrentTopIndex = ScrollGadget.Get_Value(); + Flag_To_Redraw(); + } + } +} + +#ifdef NEVER +template +T TListClass::Get_Item(int index) const +{ + index = min(index, List.Count()); + return(List[index]); +} +#endif + +template +T TListClass::Current_Item(void) const +{ + static T _temp; + if (List.Count() <= SelectedIndex) { + return(_temp); + } + return(List[SelectedIndex]); +} + +template +int TListClass::Current_Index(void) const +{ + return(SelectedIndex); +} + +template +void TListClass::Peer_To_Peer(unsigned flags, KeyNumType &, ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == &UpGadget) { + Step(true); + } + if (&whom == &DownGadget) { + Step(false); + } + } + + /* + ** The slider has changed, so reflect the current list position + ** according to the slider setting. + */ + if (&whom == &ScrollGadget) { + Set_View_Index(ScrollGadget.Get_Value()); + } +} + +template +int TListClass::Set_View_Index(int index) +{ + index = Bound(index, 0, List.Count() - LineCount); + if (index != CurrentTopIndex) { + CurrentTopIndex = index; + Flag_To_Redraw(); + if (IsScrollActive) { + ScrollGadget.Set_Value(CurrentTopIndex); + } + return(true); + } + return(false); +} + +template +int TListClass::Add_Scroll_Bar(void) +{ + if (!IsScrollActive) { + IsScrollActive = true; + + /* + ** Everything has been created successfully. Flag the list box to be + ** redrawn because it now must be made narrower to accomodate the new + ** slider gadgets. + */ + Flag_To_Redraw(); + Width -= ScrollGadget.Width; + + /* + ** Tell the newly created gadgets that they should inform this list box + ** whenever they get touched. In this way, the list box will automatically + ** be updated under control of the slider buttons. + */ + UpGadget.Make_Peer(*this); + DownGadget.Make_Peer(*this); + ScrollGadget.Make_Peer(*this); + + /* + ** Add these newly created gadgets to the same gadget list that the + ** list box is part of. + */ + UpGadget.Add(*this); + DownGadget.Add(*this); + ScrollGadget.Add(*this); + + /* + ** Make sure these added gadgets get redrawn at the next opportunity. + */ + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + + /* + ** Inform the slider of the size of the window and the current view position. + */ + ScrollGadget.Set_Maximum(List.Count()); + ScrollGadget.Set_Thumb_Size(LineCount); + ScrollGadget.Set_Value(CurrentTopIndex); + + /* + ** Return with success flag. + */ + return(true); + } + return(false); +} + +template +int TListClass::Remove_Scroll_Bar(void) +{ + if (IsScrollActive) { + IsScrollActive = false; + Width += ScrollGadget.Width; + ScrollGadget.Remove(); + UpGadget.Remove(); + DownGadget.Remove(); + Flag_To_Redraw(); + return(true); + } + return(false); +} + +template +void TListClass::Set_Tabs(int const * tabs) +{ + Tabs = tabs; +} + +#ifdef NEVER +template +void TListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + LineHeight - 1, CC_GREEN_SHADOW); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(List[index], x, y, CC_GREEN, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print(List[index], x, y, (selected ? BLUE : WHITE), TBLACK, TextFlags, width, Tabs); + } +} +#endif + +template +LinkClass & TListClass::Add(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add(list); + DownGadget.Add(list); + UpGadget.Add(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add(list)); +} + +template +LinkClass & TListClass::Add_Head(LinkClass & list) +{ + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + ScrollGadget.Add_Head(list); + DownGadget.Add_Head(list); + UpGadget.Add_Head(list); + } + + /* + ** Add myself to the list, then return. + */ + return(ControlClass::Add_Head(list)); +} + +template +LinkClass & TListClass::Add_Tail(LinkClass & list) +{ + /* + ** Add myself to the list. + */ + ControlClass::Add_Tail(list); + + /* + ** Add the scroll bar gadgets if they're active. + */ + if (IsScrollActive) { + UpGadget.Add_Tail(list); + DownGadget.Add_Tail(list); + ScrollGadget.Add_Tail(list); + } + + return(Head_Of_List()); +} + +template +GadgetClass * TListClass::Remove(void) +{ + /* + ** Remove the scroll bar if it's active + */ + if (IsScrollActive) { + ScrollGadget.Remove(); + DownGadget.Remove(); + UpGadget.Remove(); + } + + /* + ** Remove myself & return + */ + return(ControlClass::Remove()); +} + +template +void TListClass::Set_Selected_Index(int index) +{ + if ((unsigned)index < List.Count()) { + SelectedIndex = index; + Flag_To_Redraw(); + if (SelectedIndex < CurrentTopIndex) { + Set_View_Index(SelectedIndex); + } + if (SelectedIndex >= CurrentTopIndex+LineCount) { + Set_View_Index(SelectedIndex-(LineCount-1)); + } + } +} + +template +int TListClass::Step_Selected_Index(int step) +{ + int old = SelectedIndex; + + Set_Selected_Index(old + step); + return(old); +} + +template +void TListClass::Flag_To_Redraw(void) +{ + if (IsScrollActive) { + UpGadget.Flag_To_Redraw(); + DownGadget.Flag_To_Redraw(); + ScrollGadget.Flag_To_Redraw(); + } + ControlClass::Flag_To_Redraw(); +} + +template +void TListClass::Set_Selected_Index(T text) +{ + for (int index = 0; index < Count(); index++) { + if (text == Get_Item(index)) { + Set_Selected_Index(index); + break; + } + } +} + + +#endif diff --git a/code/redalert/listnode.h b/code/redalert/listnode.h new file mode 100644 index 0000000..679f91d --- /dev/null +++ b/code/redalert/listnode.h @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LISTNODE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LISTNODE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/16/96 * + * * + * Last Update : May 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LISTNODE_H +#define LISTNODE_H + +#include +#include + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +//#pragma warn -inl + +/* +** This is a doubly linked list node. Typical use of this node is to derive +** objects from this node. The interface class for this node can be used for +** added convenience. +*/ +class GenericList; +class GenericNode { + public: + GenericNode(void) : NextNode(0), PrevNode(0) {} + ~GenericNode(void) {Unlink();} + GenericNode(GenericNode & node) {node.Link(this);} + GenericNode & operator = (GenericNode & node) { + if (&node != this) { + node.Link(this); + } + return(*this); + } + + void Unlink(void) { + if (Is_Valid()) { + PrevNode->NextNode = NextNode; + NextNode->PrevNode = PrevNode; + PrevNode = 0; + NextNode = 0; + } + } + + GenericList * Main_List(void) const { + GenericNode const * node = this; + while (node->PrevNode) { + node = PrevNode; + } + return((GenericList *)this); + } + void Link(GenericNode * node) { + assert(node != NULL); + node->Unlink(); + node->NextNode = NextNode; + node->PrevNode = this; + if (NextNode) NextNode->PrevNode = node; + NextNode = node; + } + + GenericNode * Next(void) const {return(NextNode);} + GenericNode * Prev(void) const {return(PrevNode);} + bool Is_Valid(void) const {return(this != NULL && NextNode != NULL && PrevNode != NULL);} + + protected: + GenericNode * NextNode; + GenericNode * PrevNode; +}; + + +/* +** This is a generic list handler. It manages N generic nodes. Use the interface class +** to the generic list for added convenience. +*/ +class GenericList { + public: + GenericList(void) { + FirstNode.Link(&LastNode); + } + + GenericNode * First(void) const {return(FirstNode.Next());} + GenericNode * Last(void) const {return(LastNode.Prev());} + bool Is_Empty(void) const {return(!FirstNode.Next()->Is_Valid());} + void Add_Head(GenericNode * node) {FirstNode.Link(node);} + void Add_Tail(GenericNode * node) {LastNode.Prev()->Link(node);} + void Delete(void) {while (FirstNode.Next()->Is_Valid()) delete FirstNode.Next();} + + protected: + GenericNode FirstNode; + GenericNode LastNode; + + private: + GenericList(GenericList & list); + GenericList & operator = (GenericList const &); +}; + + + +/* +** This node class serves only as an "interface class" for the normal node +** object. In order to use this interface class you absolutely must be sure +** that the node is the root base object of the "class T". If it is true that the +** address of the node is the same as the address of the "class T", then this +** interface class will work. You can usually ensure this by deriving the +** class T object from this node. +*/ +template class List; +template +class Node : public GenericNode { + public: + List * Main_List(void) const {return((List *)GenericNode::Main_List());} + T * Next(void) const {return((T *)GenericNode::Next());} + T * Prev(void) const {return((T *)GenericNode::Prev());} + bool Is_Valid(void) const {return(GenericNode::Is_Valid());} +}; + + +/* +** This is an "interface class" for a list of nodes. The rules for the class T object +** are the same as the requirements required of the node class. +*/ +template +class List : public GenericList { + public: + T * First(void) const {return((T*)GenericList::First());} + T * Last(void) const {return((T*)GenericList::Last());} +}; + + +#endif diff --git a/code/redalert/loaddlg.cpp b/code/redalert/loaddlg.cpp new file mode 100644 index 0000000..cd0e812 --- /dev/null +++ b/code/redalert/loaddlg.cpp @@ -0,0 +1,787 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOADDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.CPP * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : June 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * LoadOptionsClass::Process -- main processing routine * + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * LoadOptionsClass::Compare -- for qsort * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for unlink + + +/*********************************************************************************************** + * LoadOptionsClass::LoadOptionsClass -- class constructor * + * * + * INPUT: * + * style style for this load/save dialog (LOAD/SAVE/DELETE) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::LoadOptionsClass(LoadStyleType style) +{ + Style = style; + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::~LoadOptionsClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +LoadOptionsClass::~LoadOptionsClass() +{ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Process -- main processing routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * false = User cancelled, true = operation completed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Process(void) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 250 * RESFACTOR; // dialog width + int d_dialog_h = 156 * RESFACTOR; // dialog height + + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int d_list_w = d_dialog_w - (x_margin * 2); + int d_list_h = 104 * RESFACTOR; + int d_list_x = d_dialog_x + x_margin; + int d_list_y = d_dialog_y + d_margin + d_txt8_h + d_margin; + + int d_edit_w = d_dialog_w - (x_margin * 2); + int d_edit_h = 13 * RESFACTOR; + int d_edit_x = d_dialog_x + x_margin; + int d_edit_y = d_list_y + d_list_h - (30 * RESFACTOR) + d_margin + d_txt8_h; + +#if (GERMAN | FRENCH) + int d_button_w = 50 * RESFACTOR; +#else + int d_button_w = 40 * RESFACTOR; +#endif + int d_button_h = 13 * RESFACTOR; + int d_button_x = d_dialog_cx - d_button_w - d_margin; + int d_button_y = d_dialog_y + d_dialog_h - d_button_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 60 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_LOAD = 100, + BUTTON_SAVE, + BUTTON_DELETE, + BUTTON_CANCEL, + BUTTON_LIST, + BUTTON_EDIT, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + int list_ht = d_list_h; // adjusted list box height + + /* + ** Other Variables + */ + int btn_txt; // text on the 'OK' button + int btn_id; // ID of 'OK' button + int caption; // dialog caption + int game_idx = 0; // index of game to save/load/etc + int game_num = 0; // file number of game to load/save/etc + char game_descr[DESCRIP_MAX] = {0}; // save-game description + char fname[_MAX_NAME+_MAX_EXT]; // for generating filename to delete + int rc; // return code + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + switch (Style) { + case LOAD: + btn_txt = TXT_LOAD_BUTTON; + btn_id = BUTTON_LOAD; + caption = TXT_LOAD_MISSION; + break; + + case SAVE: + btn_txt = TXT_SAVE_BUTTON; + btn_id = BUTTON_SAVE; + caption = TXT_SAVE_MISSION; + list_ht -= 30; + break; + + default: + btn_txt = TXT_DELETE_BUTTON; + btn_id = BUTTON_DELETE; + caption = TXT_DELETE_MISSION; + break; + } + + TextButtonClass button (btn_id, btn_txt, TPF_BUTTON, d_button_x, d_button_y, d_button_w); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + + ListClass listbtn (BUTTON_LIST, d_list_x, d_list_y, d_list_w, list_ht, + TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + EditClass editbtn (BUTTON_EDIT, game_descr, sizeof(game_descr)-4, TPF_6PT_GRAD|TPF_NOSHADOW, d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + Fill_List(&listbtn); + + /* + ** Do nothing if list is empty. + */ + if ((Style == LOAD || Style == WWDELETE) && listbtn.Count()==0) { + Clear_List(&listbtn); + WWMessageBox().Process(TXT_NO_SAVES); + return(false); + } + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + listbtn.Add_Tail(*commands); + if (Style == SAVE) { + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + } + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + if (g_inMainMenu) { + Load_Title_Screen("TITLE.PCX", NULL, NULL); + } + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + cancel = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(caption, d_dialog_x, d_dialog_y, d_dialog_w); + + if (Style == SAVE) { + Fancy_Text_Print(TXT_MISSION_DESCRIPTION, d_dialog_cx, + d_edit_y - d_txt8_h, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER); + } + } + + /* + ** Redraw the buttons. + */ + { + commands->Draw_All(); // jmarshall draw all + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus if this is the save dialog. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime && Style == SAVE) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN || input == (BUTTON_EDIT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + switch (Style) { + case SAVE: + input = (KeyNumType)(BUTTON_SAVE|KN_BUTTON); + cancelbtn.Turn_Off(); +// cancelbtn.IsOn = false; + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_SAVE); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case LOAD: + input = (KeyNumType)(BUTTON_LOAD|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_LOAD); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + + case WWDELETE: + input = (KeyNumType)(BUTTON_DELETE|KN_BUTTON); +// cancelbtn.IsOn = false; + cancelbtn.Turn_Off(); + toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { + toggle->IsOn = true; + toggle->IsPressed = true; + } + break; + } + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_LOAD | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (Files[game_idx]->Valid) { + + /* + ** Start a timer before we load the game + */ + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_LOADING, TXT_NONE); + Theme.Fade_Out(); + rc = Load_Game(game_num); + + /* + ** Make sure the message says on the screen at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + if (!rc) { + WWMessageBox().Process(TXT_ERROR_LOADING_GAME); + } else { + Speak(VOX_LOAD1); + while (Is_Speaking()) { + Call_Back(); + } + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); +// Set_Palette(GamePalette); + Show_Mouse(); + process = false; + } + } else { + WWMessageBox().Process(TXT_OBSOLETE_SAVEGAME); + } + break; + + /* + ** Save: Save the game & exit the dialog + */ + case (BUTTON_EDIT | KN_BUTTON): + + case (BUTTON_SAVE | KN_BUTTON): + if (!strlen(game_descr)) { + WWMessageBox().Process(TXT_MUSTENTER_DESCRIPTION); + firsttime = true; + display = true; + break; + } + game_idx = listbtn.Current_Index(); + if (Disk_Space_Available() < SAVE_GAME_DISK_SPACE && game_idx == 0) { + WWMessageBox().Process(TXT_SPACE_CANT_SAVE); + firsttime = true; + display = true; + break; + } + +// jmarshall + if (Files.Length() > 0) + game_num = Files[game_idx]->Num; + else + game_num = 0; +// jmarshall end + if (!Save_Game(game_num, game_descr)) { + WWMessageBox().Process(TXT_ERROR_SAVING_GAME); + } else { + Speak(VOX_SAVE1); + while (Is_Speaking()) { + Call_Back(); + } + CDTimerClass timer; +// timer.Start(); + timer = TICKS_PER_SECOND*4; + + WWMessageBox().Process(TXT_GAME_WAS_SAVED, TXT_NONE, TXT_NONE); + + /* + ** Delay to let the user read the message + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + } + process = false; + break; + + /* + ** Delete: delete the file & stay in the dialog, to allow the user + ** to delete multiple files. + */ + case (BUTTON_DELETE | KN_BUTTON): + game_idx = listbtn.Current_Index(); + game_num = Files[game_idx]->Num; + if (WWMessageBox().Process(TXT_DELETE_FILE_QUERY, TXT_YES, TXT_NO)==0) { + sprintf(fname, "SAVEGAME.%03d", game_num); + unlink(fname); + Clear_List(&listbtn); + Fill_List(&listbtn); + if (listbtn.Count() == 0) { + process = false; + } else { + ToggleClass * toggle = (ToggleClass *)commands->Extract_Gadget(BUTTON_DELETE); + if (toggle != NULL) { +// toggle->IsOn = false; + toggle->Turn_Off(); + toggle->IsPressed = false; + toggle->Flag_To_Redraw(); + } + } + } + display = true; + break; + + /* + ** If the user clicks on the list, see if the there is a new current + ** item; if so, and if we're in SAVE mode, copy the list item into + ** the save-game description field. + */ + case (BUTTON_LIST | KN_BUTTON): + if (Style != SAVE) { + break; + } + + if (listbtn.Count() && listbtn.Current_Index() != game_idx) { + game_idx = listbtn.Current_Index(); + + /* + ** Copy the game's description, UNLESS it's the empty slot; if + ** it is, set the edit buffer to empty. + */ + if (game_idx != 0) { + strcpy(game_descr, listbtn.Get_Item(game_idx)); + + /* + ** Strip any leading parenthesis off of the description. + */ + if (game_descr[0] == '(') { + char * ptr = strchr(game_descr, ')'); + if (ptr != NULL) { + strcpy(game_descr, ptr+1); + strtrim(game_descr); + } + } + + } else { + game_descr[0] = 0; + } + editbtn.Set_Text(game_descr, 40); + } + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + break; + } + } + + Clear_List(&listbtn); + + if (cancel) return(false); + + return(true); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Clear_List -- clears the list box & Files arrays * + * * + * This step is essential, because it frees all the strings allocated for list items. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void LoadOptionsClass::Clear_List(ListClass * list) +{ + /* + ** For every item in the list, free its buffer & remove it from the list. + */ + int j = list->Count(); + for (int i = 0; i < j; i++) { + list->Remove_Item(list->Get_Item(0)); + } + + /* + ** Clear the array of game numbers + */ + for (int i = 0; i < Files.Count(); i++) { + delete Files[i]; + } + Files.Clear(); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Fill_List -- fills the list box & GameNum arrays * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 06/25/1995 JLB : Shows which saved games are "(old)". * + *=============================================================================================*/ +void LoadOptionsClass::Fill_List(ListClass * list) +{ + FileEntryClass * fdata; // for adding entries to 'Files' + char descr[DESCRIP_MAX+32]; + unsigned scenario; // scenario # + HousesType house; // house + WIN32_FIND_DATA ff; + int id; + + /* + ** Make sure the list is empty + */ + Clear_List(list); + + /* + ** Add the Empty Slot entry + */ + if (Style == SAVE) { + fdata = new FileEntryClass; + strcpy(fdata->Descr, Text_String(TXT_EMPTY_SLOT)); + fdata->DateTime = 0xffffffff; // will always be first + Files.Add(fdata); + } + + /* + ** Find all savegame files + */ + HANDLE rc = FindFirstFile("SAVEGAME.*", &ff); + + do { + + if (stricmp(ff.cFileName, NET_SAVE_FILE_NAME) != 0) { + + /* + ** Extract the game ID from the filename + */ + id = Num_From_Ext(ff.cFileName); + + /* + ** get the game's info; if success, add it to the list + */ + bool ok = Get_Savefile_Info(id, descr, &scenario, &house); + + fdata = new FileEntryClass; + + fdata->Descr[0] = '\0'; + if (!ok) { + strcpy(fdata->Descr, Text_String(TXT_OLD_GAME)); + } else { + if (house == HOUSE_USSR || house == HOUSE_UKRAINE) { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_SOVIET)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_SOVIET)); +#endif + } else { +#ifdef WIN32 + sprintf(fdata->Descr, "(%s) ", Text_String(TXT_ALLIES)); +#else + sprintf(fdata->Descr, "(%c) ", *Text_String(TXT_ALLIES)); +#endif + } + } + strncat(fdata->Descr, descr, (sizeof(fdata->Descr)-strlen(fdata->Descr))-1); + fdata->Valid = ok; + fdata->Scenario = scenario; + fdata->House = house; + fdata->Num = id; + fdata->DateTime = (((unsigned long)ff.ftLastWriteTime.dwHighDateTime) << 16) | (unsigned long)ff.ftLastWriteTime.dwLowDateTime; + Files.Add(fdata); + } + + } while (FindNextFile(rc, &ff)); + + /* + ** If saving a game, determine a unique file ID for the empty slot + */ + if (Style == SAVE) { + /* + ** Find an un-used number to associate with the Empty Slot by looking in + ** GameNum for each number from 0 to 'N', where 'N' is the # of entries + ** in the list; if any number isn't found, use that number; otherwise, + ** use 'N + 1'. + */ + int i; + for (i = 0; i < Files.Count(); i++) { // i = the # we're searching for + id = -1; // mark as 'not found' + for (int j = 0; j < Files.Count(); j++) { // loop through all game ID's + if (Files[j]->Num==i) { // if found, mark as found + id = j; + break; + } + } + if (id == -1) break; // if ID not found, use this one + } + + Files[0]->Num = i; // set the empty slot's ID + } + + /* + ** Now sort the list in order of Date/Time (newest first, oldest last) + */ + qsort((void *)(&Files[0]), Files.Count(), sizeof(class FileEntryClass *), LoadOptionsClass::Compare); + + /* + ** Now add every file's name to the list box + */ + for (int i = 0; i < Files.Count(); i++) { + list->Add_Item(Files[i]->Descr); + } +} + + +/*********************************************************************************************** + * LoadOptionsClass::Num_From_Ext -- clears the list box & GameNum arrays * + * * + * INPUT: * + * fname filename to parse * + * * + * OUTPUT: * + * File number for this name. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Num_From_Ext(char * fname) +{ + char ext[_MAX_EXT]; + + _splitpath(fname, NULL, NULL, NULL, ext); + int num = atoi(ext + 1); // skip the '.' + return(num); +} + + +/*********************************************************************************************** + * LoadOptionsClass::Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int LoadOptionsClass::Compare(const void * p1, const void * p2) +{ + class FileEntryClass * fe1, * fe2; + + fe1 = *((class FileEntryClass **)p1); + fe2 = *((class FileEntryClass **)p2); + + if (fe1->DateTime > fe2->DateTime) return(-1); + if (fe1->DateTime < fe2->DateTime) return(1); + return(0); +} + diff --git a/code/redalert/loaddlg.h b/code/redalert/loaddlg.h new file mode 100644 index 0000000..ea2b680 --- /dev/null +++ b/code/redalert/loaddlg.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOADDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOADDLG.H * + * * + * Programmer : Maria Legg, Joe Bostic, Bill Randolph * + * * + * Start Date : March 19, 1995 * + * * + * Last Update : March 19, 1995 * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOADDLG_H +#define LOADDLG_H + +class FileEntryClass { + public: + char Descr[80]; // save-game description + unsigned Scenario; // scenario # + HousesType House; // house + int Num; // save file number (from the extension) + unsigned long DateTime; // date/time stamp of file + bool Valid; // Is the scenario valid? +}; + +class LoadOptionsClass +{ + public: + /* + ** This defines the style of the dialog + */ + typedef enum OperationModeEnum { + NONE = 0, + LOAD, + SAVE, + WWDELETE + } LoadStyleType; + + LoadOptionsClass (LoadStyleType style = LoadOptionsClass::NONE); + ~LoadOptionsClass (); + int Process (void); + + + protected: + /* + ** Internal routines + */ + void Clear_List (ListClass *list); // clears the list & game # array + void Fill_List (ListClass *list); // fills the list & game # array + int Num_From_Ext (char *fname); // translates filename to file # + static int Compare(const void *p1, const void *p2); // for qsort() + + /* + ** This is the requested style of the dialog + */ + LoadStyleType Style; + + /* + ** This is an array of pointers to FileEntryClass objects. These objects + ** are allocated on the fly as files are found, and pointers to them are + ** added to the vector list. Thus, all the objects must be free'd before + ** the vector list is cleared. This list is used for sorting the files + ** by date/time. + */ + DynamicVectorClass Files; +}; + + +#endif + diff --git a/code/redalert/logic.cpp b/code/redalert/logic.cpp new file mode 100644 index 0000000..ed4b6fa --- /dev/null +++ b/code/redalert/logic.cpp @@ -0,0 +1,474 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOGIC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 27, 1993 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LogicClass::AI -- Handles AI logic processing for game objects. * + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "logic.h" +#include "vortex.h" + +static unsigned FramesPerSecond=0; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * LogicClass::Debug_Dump -- Displays logic class status to the mono screen. * + * * + * This is a debugging support routine. It displays the current state of the logic class * + * to the monochrome monitor. It assumes that it is being called once per second. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per second. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 01/26/1996 JLB : Prints game time value. * + *=============================================================================================*/ +void LogicClass::Debug_Dump(MonoClass * mono) const +{ + #define RECORDCOUNT 40 + #define RECORDHEIGHT 21 + static int _framecounter = 0; + + static bool first = true; + if (first) { + first = false; + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_STRESS)); + } + +//mono->Set_Cursor(0,0);mono->Printf("%d", AllowVoice); + + + _framecounter++; + mono->Set_Cursor(1, 1);mono->Printf("%ld", (long)Scen.Timer); + mono->Set_Cursor(10, 1);mono->Printf("%3d", FramesPerSecond); + mono->Set_Cursor(1, 3);mono->Printf("%02d:%02d:%02d", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND); + + mono->Set_Cursor(1, 11);mono->Printf("%3d", Units.Count()); + mono->Set_Cursor(1, 12);mono->Printf("%3d", Infantry.Count()); + mono->Set_Cursor(1, 13);mono->Printf("%3d", Aircraft.Count()); + mono->Set_Cursor(1, 14);mono->Printf("%3d", Vessels.Count()); + mono->Set_Cursor(1, 15);mono->Printf("%3d", Buildings.Count()); + mono->Set_Cursor(1, 16);mono->Printf("%3d", Terrains.Count()); + mono->Set_Cursor(1, 17);mono->Printf("%3d", Bullets.Count()); + mono->Set_Cursor(1, 18);mono->Printf("%3d", Anims.Count()); + mono->Set_Cursor(1, 19);mono->Printf("%3d", Teams.Count()); + mono->Set_Cursor(1, 20);mono->Printf("%3d", Triggers.Count()); + mono->Set_Cursor(1, 21);mono->Printf("%3d", TriggerTypes.Count()); + mono->Set_Cursor(1, 22);mono->Printf("%3d", Factories.Count()); + + SpareTicks = min((long)SpareTicks, (long)TIMER_SECOND); + + /* + ** CPU utilization record. + */ + mono->Sub_Window(15, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%3d%%", ((TIMER_SECOND-SpareTicks)*100) / TIMER_SECOND); + + /* + ** Update the frame rate log. + */ + mono->Sub_Window(22, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", FramesPerSecond); + + /* + ** Update the findpath calc record. + */ + mono->Sub_Window(50, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%4d", PathCount); + PathCount = 0; + + /* + ** Update the cell redraw record. + */ + mono->Sub_Window(29, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", CellCount); + CellCount = 0; + + /* + ** Update the target scan record. + */ + mono->Sub_Window(36, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", TargetScan); + TargetScan = 0; + + /* + ** Sidebar redraw record. + */ + mono->Sub_Window(43, 1, 6, 11); + mono->Scroll(); + mono->Set_Cursor(0, 10); + mono->Printf("%5d", SidebarRedraws); + SidebarRedraws = 0; + + /* + ** Update the CPU utilization chart. + */ + mono->Sub_Window(15, 13, 63, 10); + mono->Pan(1); + mono->Sub_Window(15, 13, 64, 10); + int graph = RECORDHEIGHT * fixed(TIMER_SECOND-SpareTicks, TIMER_SECOND); + for (int row = 1; row < RECORDHEIGHT; row += 2) { + static char _barchar[4] = {' ', 220, 0, 219}; + char str[2]; + int index = 0; + + index |= (graph >= row) ? 0x01 : 0x00; + index |= (graph >= row+1) ? 0x02: 0x00; + + str[1] = '\0'; + str[0] = _barchar[index]; + mono->Text_Print(str, 62, 9-(row/2)); + } + mono->Sub_Window(); + + + SpareTicks = 0; + FramesPerSecond = 0; +} +#endif + + +/*********************************************************************************************** + * LogicClass::AI -- Handles AI logic processing for game objects. * + * * + * This routine is used to perform the AI processing for all game objects. This includes * + * all houses, factories, objects, and teams. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1994 JLB : Created. * + * 12/17/1994 JLB : Must perform one complete pass rather than bailing early. * + * 12/23/1994 JLB : Ensures that no object gets skipped if it was deleted. * + *=============================================================================================*/ +void LogicClass::AI(void) +{ + int index; + + FramesPerSecond++; + + /* + ** Fading to B&W or color due to the chronosphere is handled here. + */ + Scen.Do_Fade_AI(); + + /* + ** Handle any general timer trigger events. + */ + for (LogicTriggerID = 0; LogicTriggerID < LogicTriggers.Count(); LogicTriggerID++) { + TriggerClass * trig = LogicTriggers[LogicTriggerID]; + + /* + ** Global changed trigger event might be triggered. + */ + if (Scen.IsGlobalChanged) { + if (trig->Spring(TEVENT_GLOBAL_SET)) continue; + if (trig->Spring(TEVENT_GLOBAL_CLEAR)) continue; + } + + /* + ** Bridge change event. + */ + if (Scen.IsBridgeChanged) { + if (trig->Spring(TEVENT_ALL_BRIDGES_DESTROYED)) continue; + } + + /* + ** General time expire trigger events can be sprung without warning. + */ + if (trig->Spring(TEVENT_TIME)) continue; + + /* + ** The mission timer expiration trigger event might spring if the timer is active + ** but at a value of zero. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + if (trig->Spring(TEVENT_MISSION_TIMER_EXPIRED)) continue; + } + } + + if (Scen.MissionTimer.Is_Active()) { + long secs = Scen.MissionTimer / TICKS_PER_SECOND; + long mins = secs / 60; + long hours = mins / 60; + secs %= 60; + mins %= 60; + + /* + ** Speak mission timer reminders. + */ + VoxType vox = VOX_NONE; + if (Scen.MissionTimer == (1 * TICKS_PER_MINUTE)) vox = VOX_TIME_1; + if (Scen.MissionTimer == (2 * TICKS_PER_MINUTE)) vox = VOX_TIME_2; + if (Scen.MissionTimer == (3 * TICKS_PER_MINUTE)) vox = VOX_TIME_3; + if (Scen.MissionTimer == (4 * TICKS_PER_MINUTE)) vox = VOX_TIME_4; + if (Scen.MissionTimer == (5 * TICKS_PER_MINUTE)) vox = VOX_TIME_5; + if (Scen.MissionTimer == (10 * TICKS_PER_MINUTE)) vox = VOX_TIME_10; + if (Scen.MissionTimer == (20 * TICKS_PER_MINUTE)) vox = VOX_TIME_20; + if (Scen.MissionTimer == (30 * TICKS_PER_MINUTE)) vox = VOX_TIME_30; + if (Scen.MissionTimer == (40 * TICKS_PER_MINUTE)) vox = VOX_TIME_40; + if (vox != VOX_NONE) { + Speak(vox); + Map.FlasherTimer = 7; + } + } + + /* + ** Clean up any status values that were maintained only for logic trigger + ** purposes. + */ + if (Scen.MissionTimer.Is_Active() && Scen.MissionTimer == 0) { + Scen.MissionTimer.Stop(); + Map.Flag_To_Redraw(true); // Used only to cause tabs to redraw in new state. + } + Scen.IsGlobalChanged = false; + Scen.IsBridgeChanged = false; + /* + ** Shadow creeping back over time is handled here. + */ + if (Special.IsShadowGrow && Rule.ShroudRate != 0 && Scen.ShroudTimer == 0) { + Scen.ShroudTimer = TICKS_PER_MINUTE * Rule.ShroudRate; + + /* + ** Do this for all players in Client/Server multiplayer. ST - 8/9/2019 10:23AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + Map.Encroach_Shadow(PlayerPtr); + } else { + for (int i=0 ; iPlayer.ID); + if (player_ptr && player_ptr->IsHuman) { + Map.Encroach_Shadow(player_ptr); + } + } + } + } + + /* + ** Team AI is processed. + */ + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->AI(); + } + + /* + ** If there's a time quake, handle it here. + */ + if (TimeQuake) { + Sound_Effect(VOC_KABOOM15); + Shake_The_Screen(8); + } + + ChronalVortex.AI(); + /* + ** AI for all sentient objects is processed. + */ + for (index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + + BStart(BENCH_AI); + obj->AI(); + BEnd(BENCH_AI); + + if (TimeQuake && obj != NULL && obj->IsActive && !obj->IsInLimbo && obj->Strength) { + int damage = (int)obj->Class_Of().MaxStrength * Rule.QuakeDamagePercent; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (TimeQuakeCenter) { + if(::Distance(obj->As_Target(),TimeQuakeCenter)/256 < MTankDistance) { + switch(obj->What_Am_I()) { + case RTTI_INFANTRY: + damage = QuakeInfantryDamage; + break; + case RTTI_BUILDING: + damage = QuakeBuildingDamage * (int)obj->Class_Of().MaxStrength; + break; + default: + damage = QuakeUnitDamage * (int)obj->Class_Of().MaxStrength; + break; + } + if (damage) { + obj->Clicked_As_Target(HOUSE_COUNT); // 2019/09/20 JAS - Added record of who clicked on the object, HOUSE_COUNT is used to mark for all houses + new AnimClass(ANIM_MINE_EXP1, obj->Center_Coord()); + } + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } else { + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } +#else + obj->Take_Damage(damage, 0, WARHEAD_AP, 0, true); +#endif + } + /* + ** If the object was destroyed in the process of performing its AI, then + ** adjust the index so that no object gets skipped. + */ + if (obj != (*this)[index]) { + index--; + } + } + HouseClass::Recalc_Attributes(); + + /* + ** Map related logic is performed. + */ + Map.Logic(); + + /* + ** Factory processing is performed. + */ + for (index = 0; index < Factories.Count(); index++) { + Factories.Ptr(index)->AI(); + } + + /* + ** House processing is performed. + */ +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + { + for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } + else + { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } + } +#else // AI() is called redundantly 12 times in multiplayer games here. ajw + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->AI(); + } + } +#endif + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + if( Session.Type != GAME_NORMAL && Scen.AutoSonarTimer == 0 ) + { + if( bAutoSonarPulse ) + { + Map.Activate_Pulse(); + Sound_Effect(VOC_SONAR); + bAutoSonarPulse = false; + } +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40; + Scen.AutoSonarTimer = AUTOSONAR_PERIOD; + } +#endif +} + + +/*********************************************************************************************** + * LogicClass::Detach -- Detatch the specified target from the logic system. * + * * + * This routine is called when the specified target object is about to be removed from the * + * game system and all references to it must be severed. The only thing that the logic * + * system looks for in this case is to see if the target refers to a trigger and if so, * + * it scans through the trigger list and removes all references to it. * + * * + * INPUT: target -- The target to remove from the sytem. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void LogicClass::Detach(TARGET target, bool ) +{ + /* + ** Remove any triggers from the logic trigger list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + if (As_Trigger(target) == LogicTriggers[index]) { + LogicTriggers.Delete(index); + index--; + } + } + } +} + + +/*********************************************************************************************** + * LogicClass::Clear_Recently_Created_Bits -- Clear out the indicators that objects were * + * recently created * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 8/19/2019 5:47PM ST : Created. * + *=============================================================================================*/ +void LogicClass::Clear_Recently_Created_Bits(void) +{ + for (int index = 0; index < Count(); index++) { + ObjectClass * obj = (*this)[index]; + obj->IsRecentlyCreated = false; + } +} \ No newline at end of file diff --git a/code/redalert/logic.h b/code/redalert/logic.h new file mode 100644 index 0000000..c3ff050 --- /dev/null +++ b/code/redalert/logic.h @@ -0,0 +1,58 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LOGIC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LOGIC.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 29, 1994 * + * * + * Last Update : May 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef LOGIC_H +#define LOGIC_H + +#include "layer.h" + +/*********************************************************************************************** +** Game logic processing is controlled by this class. The graphic and AI logic is handled +** separately so that on slower machines, the graphic display is least affected. +*/ +class LogicClass : public LayerClass +{ + public: + void AI(void); + void Detach(TARGET target, bool all=true); + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Added. ST - 8/19/2019 5:46PM + */ + void Clear_Recently_Created_Bits(void); +}; +#endif \ No newline at end of file diff --git a/code/redalert/lzo.h b/code/redalert/lzo.h new file mode 100644 index 0000000..31e29c2 --- /dev/null +++ b/code/redalert/lzo.h @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZO_H +#define _LZO_H + +#include "lzoconf.h" + +int lzo1x_1_compress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp wrkmem ); + + +int lzo1x_decompress ( const lzo_byte *in , + lzo_uint in_len, + lzo_byte *out, + lzo_uint *out_len, + lzo_voidp ); + + + + + +#endif diff --git a/code/redalert/lzo1x.h b/code/redalert/lzo1x.h new file mode 100644 index 0000000..e7b0b60 --- /dev/null +++ b/code/redalert/lzo1x.h @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x.h -- public interface of the LZO1X compression algorithm + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZO1X_H +#define __LZO1X_H + +#include "lzoconf.h" + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// +************************************************************************/ + +/* Memory required for the wrkmem parameter. + * When the required size is 0, you can also pass a NULL pointer. + */ + +#define LZO1X_MEM_COMPRESS ((lzo_uint) (16384L * sizeof(lzo_byte *))) +#define LZO1X_MEM_DECOMPRESS (0) + + +/* fast decompression */ +LZO_EXTERN(int) +lzo1x_decompress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + +/* safe decompression with overrun testing */ +LZO_EXTERN(int) +lzo1x_decompress_x ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem /* NOT USED */ ); + + +/*********************************************************************** +// +************************************************************************/ + +LZO_EXTERN(int) +lzo1x_1_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/*********************************************************************** +// better compression ratio at the cost of more memory and time +************************************************************************/ + +#define LZO1X_999_MEM_COMPRESS ((lzo_uint) (14 * 16384L * sizeof(short))) + +LZO_EXTERN(int) +lzo1x_999_compress ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/code/redalert/lzo1x_c.cpp b/code/redalert/lzo1x_c.cpp new file mode 100644 index 0000000..c541d7a --- /dev/null +++ b/code/redalert/lzo1x_c.cpp @@ -0,0 +1,348 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x_c.c -- standalone LZO1X-1 compressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#ifndef NDEBUG +#define NDEBUG +#endif +#include +#include "lzo_conf.h" + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define M1_MAX_OFFSET 0x0400 +#if defined(LZO1X) +#define M2_MAX_OFFSET 0x0800 +#elif defined(LZO1Y) +#define M2_MAX_OFFSET 0x0400 +#endif +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + + +#define _DV2(p,shift1,shift2) \ + (((( (lzo_uint)(p[2]) << shift1) ^ p[1]) << shift2) ^ p[0]) +#define DVAL_NEXT(dv,p) \ + dv ^= p[-1]; dv = (((dv) >> 5) ^ ((lzo_uint)(p[2]) << (2*5))) +#define _DV(p,shift) _DV2(p,shift,shift) +#define DVAL_FIRST(dv,p) dv = _DV((p),5) +#define _DINDEX(dv,p) ((40799u * (dv)) >> 5) +#define DINDEX(dv,p) (((_DINDEX(dv,p)) & 0x3fff) << 0) +#define UPDATE_D(dict,cycle,dv,p) dict[ DINDEX(dv,p) ] = (p) +#define UPDATE_I(dict,cycle,index,p) dict[index] = (p) + + +/*********************************************************************** +// compress a block of data. +************************************************************************/ + +static int do_compress(const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + register const lzo_byte *ip; + lzo_uint dv; + lzo_byte *op; + const lzo_byte * const in_end = in + in_len; + const lzo_byte * const ip_end = in + in_len - 9 - 4; + const lzo_byte *ii; + const lzo_bytepp const dict = (const lzo_bytepp) wrkmem; + + op = out; + ip = in; + ii = ip; + + DVAL_FIRST(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + DVAL_NEXT(dv,ip); UPDATE_D(dict,cycle,dv,ip); ip++; + + while (1) { + register const lzo_byte *m_pos; + lzo_uint m_len; + lzo_ptrdiff_t m_off; + lzo_uint lit; + + lzo_uint dindex = DINDEX(dv,ip); + m_pos = dict[dindex]; + UPDATE_I(dict,cycle,dindex,ip); + + + if (LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,M4_MAX_OFFSET)) { + } +#if defined(LZO_UNALIGNED_OK_2) + else + if (* (unsigned short *) m_pos != * (unsigned short *) ip) +#else + else + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) +#endif + { + } else { + if (m_pos[2] == ip[2]) { + lit = ip - ii; + m_pos += 3; + if (m_off <= M2_MAX_OFFSET) + goto match; + + /* better compression, but slower */ + if (lit == 3) { + assert(op - 2 > out); op[-2] |= LZO_BYTE(3); + *op++ = *ii++; *op++ = *ii++; *op++ = *ii++; + goto code_match; + } + + if (*m_pos == ip[3]) { + goto match; + } + } else { + /* still need a better way for finding M1 matches */ + } + } + + + /* a literal */ + ++ip; + if (ip >= ip_end) { + break; + } + DVAL_NEXT(dv,ip); + continue; + + + /* a match */ + +match: + + /* store current literal run */ + if (lit > 0) { + register lzo_uint t = lit; + + if (t <= 3) { + assert(op - 2 > out); + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + + do { + *op++ = *ii++; + } while (--t > 0); + } + + + /* code the match */ +code_match: + assert(ii == ip); + ip += 3; + if (*m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++ || + *m_pos++ != *ip++ || *m_pos++ != *ip++ || *m_pos++ != *ip++) + { + --ip; + m_len = ip - ii; + assert(m_len >= 3); assert(m_len <= 8); + + if (m_off <= M2_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE(m_off >> 3); + } else { + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + goto m3_m4_offset; + } + } + } else { + const lzo_byte *end; + end = in_end; + while (ip < end && *m_pos == *ip) { + m_pos++; + ip++; + } + m_len = (ip - ii); + assert(m_len >= 3); + + if (m_off <= M3_MAX_OFFSET) { + m_off -= 1; + if (m_len <= 33) { + *op++ = LZO_BYTE(M3_MARKER | (m_len - 2)); + } else { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } else { + m_off -= 0x4000; + assert(m_off > 0); assert(m_off <= 0x7fff); + if (m_len <= 9) { + *op++ = LZO_BYTE(M4_MARKER | + ((m_off & 0x4000) >> 11) | (m_len - 2)); + } else { + m_len -= 9; + *op++ = LZO_BYTE(M4_MARKER | ((m_off & 0x4000) >> 11)); +m3_m4_len: + while (m_len > 255) { + m_len -= 255; + *op++ = 0; + } + assert(m_len > 0); + *op++ = LZO_BYTE(m_len); + } + } + +m3_m4_offset: + *op++ = LZO_BYTE((m_off & 63) << 2); + *op++ = LZO_BYTE(m_off >> 6); + } + + ii = ip; + if (ip >= ip_end) { + break; + } + DVAL_FIRST(dv,ip); + } + + /* store final literal run */ + if (in_end - ii > 0) { + register lzo_uint t = in_end - ii; + + if (op == out && t <= 238) { + *op++ = LZO_BYTE(17 + t); + } else { + if (t <= 3) { + op[-2] |= LZO_BYTE(t); + } else { + if (t <= 18) { + *op++ = LZO_BYTE(t - 3); + } else { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) { + tt -= 255; + *op++ = 0; + } + assert(tt > 0); + *op++ = LZO_BYTE(tt); + } + } + } + do { + *op++ = *ii++; + } while (--t > 0); + } + + *out_len = op - out; + return LZO_E_OK; +} + + +/*********************************************************************** +// public entry point +************************************************************************/ + +int lzo1x_1_compress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp wrkmem ) +{ + lzo_byte *op = out; + int r = LZO_E_OK; + + if (in_len <= 0) + *out_len = 0; + else if (in_len <= 9 + 4) + { + *op++ = LZO_BYTE(17 + in_len); + do *op++ = *in++; while (--in_len > 0); + *out_len = op - out; + } + else + r = do_compress(in,in_len,out,out_len,wrkmem); + + if (r == LZO_E_OK) + { + op = out + *out_len; + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + *out_len += 3; + } + + return r; +} + + +/* +vi:ts=4 +*/ diff --git a/code/redalert/lzo1x_d.cpp b/code/redalert/lzo1x_d.cpp new file mode 100644 index 0000000..39dbb58 --- /dev/null +++ b/code/redalert/lzo1x_d.cpp @@ -0,0 +1,205 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo1x_d.c -- standalone LZO1X decompressor + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#include "lzo1x.h" +#ifndef NDEBUG +#define NDEBUG +#endif +#include + +#if !defined(LZO1X) && !defined(LZO1Y) +# define LZO1X +#endif + +#if 1 +# define TEST_IP 1 +#else +# define TEST_IP (ip < ip_end) +#endif + + +/*********************************************************************** +// decompress a block of data. +************************************************************************/ + +int lzo1x_decompress ( const lzo_byte *in , lzo_uint in_len, + lzo_byte *out, lzo_uint *out_len, + lzo_voidp ) +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; + register const lzo_byte *m_pos; + const lzo_byte * const ip_end = in + in_len; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + goto first_literal_run; + } + + while (TEST_IP) + { + t = *ip++; + if (t >= 16) + goto match; + /* a literal run */ + if (t == 0) + { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + /* copy literals */ + *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; +first_literal_run: + do *op++ = *ip++; while (--t > 0); + + + t = *ip++; + + if (t >= 16) + goto match; +#if defined(LZO1X) + m_pos = op - 1 - 0x800; +#elif defined(LZO1Y) + m_pos = op - 1 - 0x400; +#endif + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; *op++ = *m_pos++; + goto match_done; + + + /* handle matches */ + while (TEST_IP) + { + if (t < 16) /* a M1 match */ + { + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; *op++ = *m_pos++; + } + else + { +match: + if (t >= 64) /* a M2 match */ + { + m_pos = op - 1; +#if defined(LZO1X) + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; +#elif defined(LZO1Y) + m_pos -= (t >> 2) & 3; + m_pos -= *ip++ << 2; + t = (t >> 4) - 3; +#endif + } + else if (t >= 32) /* a M3 match */ + { + t &= 31; + if (t == 0) + { + t = 31; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } + else /* a M4 match */ + { + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) + { + t = 7; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + *op++ = *m_pos++; *op++ = *m_pos++; + do *op++ = *m_pos++; while (--t > 0); + } + +match_done: + t = ip[-2] & 3; + if (t == 0) + break; + /* copy literals */ + do *op++ = *ip++; while (--t > 0); + t = *ip++; + } + } + + /* ip == ip_end and no EOF code was found */ + + //Unreachable - ST 9/5/96 5:07PM + //*out_len = op - out; + //return (ip == ip_end ? LZO_E_EOF_NOT_FOUND : LZO_E_ERROR); + +eof_found: + assert(t == 1); + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : LZO_E_ERROR); +} + + +/* +vi:ts=4 +*/ diff --git a/code/redalert/lzo_conf.h b/code/redalert/lzo_conf.h new file mode 100644 index 0000000..cd94fe4 --- /dev/null +++ b/code/redalert/lzo_conf.h @@ -0,0 +1,283 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzo_conf.h -- main internal configuration file for the the LZO library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the library and is subject + to change. + */ + + +#ifndef __LZO_CONF_H +#define __LZO_CONF_H + +#ifndef __LZOCONF_H +# include +#endif + + +/*********************************************************************** +// compiler specific defines +************************************************************************/ + +/* need Borland C 4.0 or above because of huge-pointer bugs */ +#if defined(__LZO_MSDOS16) && defined(__TURBOC__) +# if (__TURBOC__ < 0x452) +# error You need a newer compiler version +# endif +#endif + +#if defined(__LZO_MSDOS) || defined(__i386__) || defined(__386__) +# if !defined(__LZO_i386) +# define __LZO_i386 +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#include /* ptrdiff_t, size_t */ +#include /* memcpy, memmove, memcmp, memset */ + +#if 0 && !defined(assert) +# error not included +#endif + +#if defined(__BOUNDS_CHECKING_ON) +# include +#else +# define BOUNDS_CHECKING_OFF_DURING(stmt) stmt +# define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) +#endif + +/* ptrdiff_t */ +#if (UINT_MAX >= 0xffffffffL) + typedef ptrdiff_t lzo_ptrdiff_t; +#else + typedef long lzo_ptrdiff_t; +#endif + + +#ifdef __cplusplus +# define LZO_UNUSED(parm) +#else +# define LZO_UNUSED(parm) parm +#endif + + +#if !defined(__inline__) && !defined(__GNUC__) +# if defined(__cplusplus) +# define __inline__ inline +# else +# define __inline__ /* nothing */ +# endif +#endif + + +/*********************************************************************** +// compiler and architecture specific stuff +************************************************************************/ + +/* Some defines that indicate if memory can be accessed at unaligned + * addresses. You should also test that this is actually faster if + * it is allowed by your system. + */ + +#if 1 && defined(__LZO_i386) +# if !defined(LZO_UNALIGNED_OK_2) +# define LZO_UNALIGNED_OK_2 +# endif +# if !defined(LZO_UNALIGNED_OK_4) +# define LZO_UNALIGNED_OK_4 +# endif +#endif + + +#if defined(LZO_UNALIGNED_OK_2) || defined(LZO_UNALIGNED_OK_4) +# if !defined(LZO_UNALIGNED_OK) +# define LZO_UNALIGNED_OK +# endif +#endif + + +/* Definitions for byte order, according to significance of bytes, from low + * addresses to high addresses. The value is what you get by putting '4' + * in the most significant byte, '3' in the second most significant byte, + * '2' in the second least significant byte, and '1' in the least + * significant byte. + */ + +#define LZO_LITTLE_ENDIAN 1234 +#define LZO_BIG_ENDIAN 4321 +#define LZO_PDP_ENDIAN 3412 + +/* The byte order is only needed if we use LZO_UNALIGNED_OK */ +#if !defined(LZO_BYTE_ORDER) +# if defined(__LZO_i386) +# define LZO_BYTE_ORDER LZO_LITTLE_ENDIAN +# elif defined(__mc68000__) +# define LZO_BYTE_ORDER LZO_BIG_ENDIAN +# elif defined(__BYTE_ORDER) +# define LZO_BYTE_ORDER __BYTE_ORDER +# endif +#endif + +#if defined(LZO_UNALIGNED_OK) +# if !defined(LZO_BYTE_ORDER) +# error LZO_BYTE_ORDER is not defined +# elif (LZO_BYTE_ORDER != LZO_LITTLE_ENDIAN) && \ + (LZO_BYTE_ORDER != LZO_BIG_ENDIAN) +# error invalid LZO_BYTE_ORDER +# endif +#endif + + +/*********************************************************************** +// optimization +************************************************************************/ + +/* gcc 2.6.3 and gcc 2.7.2 have a bug */ +#define LZO_OPTIMIZE_GNUC_i386_IS_BUGGY + +/* Help the optimizer with register allocation. + * Don't activate this macro for a fair comparision with other algorithms. + */ +#if 1 && defined(NDEBUG) && !defined(__BOUNDS_CHECKING_ON) +# if defined(__GNUC__) && defined(__i386__) +# if !defined(LZO_OPTIMIZE_GNUC_i386_IS_BUGGY) +# define LZO_OPTIMIZE_GNUC_i386 +# endif +# endif +#endif + + +/*********************************************************************** +// +************************************************************************/ + +#define LZO_BYTE(x) ((unsigned char) (x)) + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) + +#define lzo_sizeof(x) ((lzo_uint) (sizeof(x))) + +#define LZO_HIGH(x) ((lzo_uint) (sizeof(x)/sizeof(*(x)))) + +/* this always fits into 16 bits */ +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_LSIZE(bits) (1ul << (bits)) +#define LZO_LMASK(bits) (LZO_LSIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + + +/*********************************************************************** +// ANSI C preprocessor macros +************************************************************************/ + +#define _LZO_STRINGIZE(x) #x +#define _LZO_MEXPAND(x) _LZO_STRINGIZE(x) + +/* concatenate */ +#define _LZO_CONCAT2(a,b) a ## b +#define _LZO_CONCAT3(a,b,c) a ## b ## c +#define _LZO_CONCAT4(a,b,c,d) a ## b ## c ## d +#define _LZO_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e + +/* expand and concatenate (by using one level of indirection) */ +#define _LZO_ECONCAT2(a,b) _LZO_CONCAT2(a,b) +#define _LZO_ECONCAT3(a,b,c) _LZO_CONCAT3(a,b,c) +#define _LZO_ECONCAT4(a,b,c,d) _LZO_CONCAT4(a,b,c,d) +#define _LZO_ECONCAT5(a,b,c,d,e) _LZO_CONCAT5(a,b,c,d,e) + + +/*********************************************************************** +// +************************************************************************/ + +/* Generate compressed data in a deterministic way. + * This is fully portable, and compression can be faster as well. + * A reason NOT to be deterministic is when the block size is + * very small (e.g. 8kB) or the dictionary is big, because + * then the initialization of the dictionary becomes a relevant + * magnitude for compression speed. + */ +#define LZO_DETERMINISTIC + + +/*********************************************************************** +// +************************************************************************/ + +#if 0 +/* This line causes problems on some architectures */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_off = ip - m_pos) > max_offset )) + +#else +/* This is the safe (but slower) version */ +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = ip - m_pos) > max_offset) +#endif + + +/* m_pos may point anywhere... + * This marco is probably a good candidate for architecture specific problems. + * Try casting the pointers to lzo_ptr_t before comparing them. + */ +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (m_pos < in || (m_off = ip - m_pos) <= 0 || m_off > max_offset) )) + + + +#endif /* already included */ + +/* +vi:ts=4 +*/ + diff --git a/code/redalert/lzoconf.h b/code/redalert/lzoconf.h new file mode 100644 index 0000000..953f83f --- /dev/null +++ b/code/redalert/lzoconf.h @@ -0,0 +1,214 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* lzoconf.h -- configuration for the LZO real-time data compression library + + This file is part of the LZO real-time data compression library. + + Copyright (C) 1996 Markus Franz Xaver Johannes Oberhumer + + The LZO library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + The LZO library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the LZO library; see the file COPYING.LIB. + If not, write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + Markus F.X.J. Oberhumer + markus.oberhumer@jk.uni-linz.ac.at + */ + + +#ifndef __LZOCONF_H +#define __LZOCONF_H + +#define LZO_VERSION 0x0200 +#define LZO_VERSION_STRING "0.20" +#define LZO_VERSION_DATE "11 Aug 1996" + + +#include /* CHAR_BIT, UINT_MAX, ULONG_MAX */ +#if !defined(CHAR_BIT) || (CHAR_BIT != 8) +# error invalid CHAR_BIT +#endif + +//#ifdef __cplusplus +//extern "C" { +//#endif + + +/*********************************************************************** +// defines +************************************************************************/ + +#if defined(__MSDOS__) || defined(MSDOS) +# define __LZO_MSDOS +# if (UINT_MAX < 0xffffffffL) +# define __LZO_MSDOS16 +# endif +#endif + + +/*********************************************************************** +// integral and pointer types +************************************************************************/ + +/* Unsigned type with 32 bits or more */ +#if (UINT_MAX >= 0xffffffffL) + typedef unsigned int lzo_uint; + typedef int lzo_int; +# define LZO_UINT_MAX UINT_MAX +#elif (ULONG_MAX >= 0xffffffffL) + typedef unsigned long lzo_uint; + typedef long lzo_int; +# define LZO_UINT_MAX ULONG_MAX +#else +# error lzo_uint +#endif + + +/* Memory model that allows to access memory at offsets of lzo_uint. + * Huge pointers (16 bit MSDOS) are somewhat slow, but they work + * fine and I really don't care about 16 bit compiler + * optimizations nowadays. + */ +#if (LZO_UINT_MAX <= UINT_MAX) +# define __LZO_MMODEL +#elif defined(__LZO_MSDOS16) +# define __LZO_MMODEL huge +# define __LZO_ENTRY __cdecl +#else +# error __LZO_MMODEL +#endif + + +/* no typedef here because of const-pointer issues */ +#define lzo_byte unsigned char __LZO_MMODEL +#define lzo_voidp void __LZO_MMODEL * +#define lzo_bytep unsigned char __LZO_MMODEL * +#define lzo_uintp lzo_uint __LZO_MMODEL * +#define lzo_intp lzo_int __LZO_MMODEL * +#define lzo_voidpp lzo_voidp __LZO_MMODEL * +#define lzo_bytepp lzo_bytep __LZO_MMODEL * + + +/* Unsigned type that can store all bits of a lzo_voidp */ +typedef unsigned long lzo_ptr_t; + +/* Align a pointer on a boundary that is a multiple of 'size' */ +#define LZO_ALIGN(ptr,size) \ + ((lzo_voidp) (((lzo_ptr_t)(ptr) + (size)-1) & ~((lzo_ptr_t)((size)-1)))) + + +/*********************************************************************** +// function types +************************************************************************/ + +//#ifdef __cplusplus +//# define LZO_EXTERN_C extern "C" +//#else +# define LZO_EXTERN_C extern +//#endif + + +#if !defined(__LZO_ENTRY) /* calling convention */ +# define __LZO_ENTRY +#endif +#if !defined(__LZO_EXPORT) /* DLL export (and maybe size) information */ +# define __LZO_EXPORT +#endif + +#if !defined(LZO_EXTERN) +# define LZO_EXTERN(_rettype) LZO_EXTERN_C _rettype __LZO_ENTRY __LZO_EXPORT +#endif + + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_compress_t) ( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + +typedef int __LZO_ENTRY +(__LZO_EXPORT *lzo_decompress_t)( const lzo_byte *src, lzo_uint src_len, + lzo_byte *dst, lzo_uint *dst_len, + lzo_voidp wrkmem ); + + +/* a progress indicator callback function */ +typedef void __LZO_ENTRY +(__LZO_EXPORT *lzo_progress_callback_t)(lzo_uint,lzo_uint); + + +/*********************************************************************** +// error codes and prototypes +************************************************************************/ + +/* Error codes for the compression/decompression functions. Negative + * values are errors, positive values will be used for special but + * normal events. + */ +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_NOT_COMPRESSIBLE (-2) /* not used right now */ +#define LZO_E_EOF_NOT_FOUND (-3) +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_OUT_OF_MEMORY (-7) /* not used right now */ + + +/* this should be the first function you call. Check the return code ! */ +LZO_EXTERN(int) lzo_init(void); + +/* version functions (useful for shared libraries) */ +LZO_EXTERN(unsigned) lzo_version(void); +LZO_EXTERN(const char *) lzo_version_string(void); + +/* string functions */ +LZO_EXTERN(int) +lzo_memcmp(const lzo_voidp _s1, const lzo_voidp _s2, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memcpy(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memmove(lzo_voidp _dest, const lzo_voidp _src, lzo_uint _len); +LZO_EXTERN(lzo_voidp) +lzo_memset(lzo_voidp _s, int _c, lzo_uint _len); + +/* checksum functions */ +LZO_EXTERN(lzo_uint) +lzo_adler32(lzo_uint _adler, const lzo_byte *_buf, lzo_uint _len); + +/* misc. */ +LZO_EXTERN(int) lzo_assert(int _expr); +LZO_EXTERN(int) _lzo_config_check(void); + + +//#ifdef __cplusplus +//} /* extern "C" */ +//#endif + +#endif /* already included */ + +/* +vi:ts=4 +*/ diff --git a/code/redalert/lzopipe.cpp b/code/redalert/lzopipe.cpp new file mode 100644 index 0000000..85052d6 --- /dev/null +++ b/code/redalert/lzopipe.cpp @@ -0,0 +1,318 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZO processor pipe. * + * LZWPipe::Put -- Send some data through the LZO processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZO pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzopipe.h" +#include "lzo.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZOPipe::LZOPipe -- Constructor for the LZO processor pipe. * + * * + * This will initialize the LZOPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::LZOPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZOPipe::~LZOPipe -- Deconstructor for the LZO pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOPipe::~LZOPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOPipe::Put -- Send some data through the LZO processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZO processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + unsigned int length = sizeof (Buffer2); + lzo1x_decompress ((unsigned char*)Buffer, BlockHeader.CompCount, (unsigned char*)Buffer2, &length, NULL); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)source, BlockSize, (unsigned char*)Buffer2, &len, dictionary); + delete [] dictionary; + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZOPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZO decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + unsigned int len = sizeof (Buffer2); + char *dictionary = new char [64*1024]; + lzo1x_1_compress ((unsigned char*)Buffer, Counter, (unsigned char *)Buffer2, &len, dictionary); + delete [] dictionary; + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/code/redalert/lzopipe.h b/code/redalert/lzopipe.h new file mode 100644 index 0000000..84a0c74 --- /dev/null +++ b/code/redalert/lzopipe.h @@ -0,0 +1,101 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOPIPE_H +#define LZOPIPE_H + +#include "pipe.h" + + +/* +** Performs LZO compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZOPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOPipe(CompControl, int blocksize=1024*8); + virtual ~LZOPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOPipe(LZOPipe & rvalue); + LZOPipe & operator = (LZOPipe const & pipe); +}; + + +#endif diff --git a/code/redalert/lzostraw.cpp b/code/redalert/lzostraw.cpp new file mode 100644 index 0000000..faa78df --- /dev/null +++ b/code/redalert/lzostraw.cpp @@ -0,0 +1,181 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZOSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZOStraw::Get -- Fetch data through the LZO processor. * + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZOStraw::LZOStraw -- Constructor for LZO straw object. * + * * + * This will initialize the LZO straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::LZOStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZOStraw::~LZOStraw -- Destructor for the LZO straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZOStraw::~LZOStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZOStraw::Get -- Fetch data through the LZO processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZOStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *staging_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + unsigned int length = sizeof(Buffer); + lzo1x_decompress ((unsigned char*)staging_buffer, BlockHeader.CompCount, (unsigned char*)Buffer, &length, NULL); + delete [] staging_buffer; + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + char *dictionary = new char [64*1024]; + unsigned int length = sizeof (Buffer2) - sizeof (BlockHeader); + lzo1x_1_compress ((unsigned char*)Buffer, BlockHeader.UncompCount, (unsigned char*)(&Buffer2[sizeof(BlockHeader)]), &length, dictionary); + BlockHeader.CompCount = (unsigned short)length; + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/code/redalert/lzostraw.h b/code/redalert/lzostraw.h new file mode 100644 index 0000000..c1a5699 --- /dev/null +++ b/code/redalert/lzostraw.h @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZoSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZOSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZOSTRAW_H +#define LZOSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZO compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZOStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZOStraw(CompControl control, int blocksize=1024*8); + virtual ~LZOStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** Probably dont need this anymore as LZO decompresses into a staging buffer. + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZOStraw(LZOStraw & rvalue); + LZOStraw & operator = (LZOStraw const & pipe); +}; + + +#endif diff --git a/code/redalert/lzw.cpp b/code/redalert/lzw.cpp new file mode 100644 index 0000000..13f910e --- /dev/null +++ b/code/redalert/lzw.cpp @@ -0,0 +1,709 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Child_Node -- Find a matching dictionary entry. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include + +#include "xstraw.h" +#include "xpipe.h" +#include "buff.h" +#include "lzw.h" + + +LZWEngine::LZWEngine(void) +{ + Reset(); +} + + +void LZWEngine::Reset(void) +{ + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); + } +} + +int LZWEngine::Compress(Buffer const & input, Buffer const & output) +{ + BufferStraw instraw(input); + BufferPipe outpipe(output); + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + + string_code = 0; + if (instraw.Get(&string_code, sizeof(char)) == 0) { + string_code = END_OF_STREAM; + } + + for (;;) { + + /* + ** Fetch a character from the source data stream. If exhausted, + ** then break out of the process loop so that the final code + ** can be written out. + */ + unsigned char character; + if (instraw.Get(&character, sizeof(character)) == 0) break; + + /* + ** See if there is a match for the current code and current + ** character. A match indicates that there is already a + ** dictionary entry that fully represents the character + ** sequence. + */ + int index = Find_Child_Node(string_code, character); + + /* + ** If a code match was found, then set the current code + ** value to this code value that represents the concatenation + ** of the previous code value and the current character. + */ + if (index != -1 && dict[index].CodeValue != -1) { + string_code = dict[index].CodeValue; + } else { + + /* + ** Since no exact match was found, then create a new code + ** entry that represents the current code and character + ** value concatenated. This presumes there is room in the + ** code table. + */ + if (index != -1 && next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code, string_code, character); + next_code++; + } + + /* + ** Output the code to the compression stream and reset the + ** current code value to match the current character. This + ** has the effect of clearing out the current character + ** sequence scan in preparation for building a new one. It + ** also ensures that the character will be written out. + */ + outcount += outpipe.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + + outcount += outpipe.Put(&string_code, sizeof(string_code)); + if (string_code != END_OF_STREAM) { + string_code = END_OF_STREAM; + outcount += outpipe.Put(&string_code, sizeof(string_code)); + } + + return(outcount); +} + + +int LZWEngine::Uncompress(Buffer const & input, Buffer const & output) +{ + int outcount = 0; + BufferStraw instraw(input); + BufferPipe outpipe(output); + + CodeType old_code; + if (instraw.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + unsigned char character = (unsigned char)old_code; + outcount += outpipe.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (instraw.Get(&new_code, sizeof(new_code)) == 0) break; + + if (new_code == END_OF_STREAM) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += outpipe.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + + +int LZWEngine::Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +int LZWEngine::Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +int LZWEngine::Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Uncompress(inbuff, outbuff)); +} + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff) +{ + LZWEngine lzw; + return(lzw.Compress(inbuff, outbuff)); +} + + + + + +#ifdef NEVER + + +/* + * Constants used throughout the program. BITS defines how many bits + * will be in a code. TABLE_SIZE defines the size of the dictionary + * table. + */ +#define BITS 12 +#define MAX_CODE ( ( 1 << BITS ) - 1 ) +#define TABLE_SIZE 5021 +#define END_OF_STREAM 256 +#define FIRST_CODE 257 +#define UNUSED -1 + +typedef unsigned short CodeType; + +/* + * This data structure defines the dictionary. Each entry in the dictionary + * has a code value. This is the code emitted by the compressor. Each + * code is actually made up of two pieces: a parent_code, and a + * character. Code values of less than 256 are actually plain + * text codes. + */ +struct CodeClass +{ + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} +}; +CodeClass dict[TABLE_SIZE]; + +char decode_stack[TABLE_SIZE]; + +inline int Make_LZW_Hash(CodeType code, char character) +{ + return((((int)(unsigned char)character) << ( BITS - 8 ) ) ^ (int)code); +} + + +/*********************************************************************************************** + * Find_Child_Node -- Find a matching dictionary entry. * + * * + * This hashing routine is responsible for finding the table location * + * for a string/character combination. The table index is created * + * by using an exclusive OR combination of the prefix and character. * + * This code also has to check for collisions, and handles them by * + * jumping around in the table. * + * * + * INPUT: parent_code -- The code of the parent string sequence. * + * * + * character -- The current character. * + * * + * OUTPUT: Returns with the index for the matching dictionary entry. If no matching index * + * could be found, then it returns with the index to an unused dictionary entry. If * + * there are also no unused entries in the dictionary, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/28/1996 JLB : Created. * + *=============================================================================================*/ +static int Find_Child_Node(CodeType parent_code, char child_character) +{ + /* + ** Fetch the first try index for the code and character. + */ + int hash_index = Make_LZW_Hash(parent_code, child_character); + + /* + ** Base the hash-miss-try-again-displacement value on the current + ** index. [Shouldn't the value be some large prime number???]. + */ + int offset = 1; + if (hash_index != 0) { + offset = TABLE_SIZE - hash_index; + } + + /* + ** Keep offsetting through the dictionary until an exact match is + ** found for the code and character specified. + */ + int initial = hash_index; + while (!dict[hash_index].Is_Matching(parent_code, child_character)) { + + /* + ** Stop searching if an unused index is found since this means that + ** a match doesn't exist in the table at all. + */ + if (dict[hash_index].Is_Unused()) break; + + /* + ** Bump the hash index to another value such that sequential bumps + ** will not result in the same index value until all of the table + ** has been scanned. + */ + hash_index -= offset; + if (hash_index < 0) { + hash_index += TABLE_SIZE; + } + + /* + ** If the entire table has been scanned and no match or unused + ** entry was found, then return a special value indicating this + ** condition. + */ + if (initial == hash_index) { + hash_index = -1; + break; + } + } + return(hash_index); +} + + +/* + * This routine decodes a string from the dictionary, and stores it + * in the decode_stack data structure. It returns a count to the + * calling program of how many characters were placed in the stack. + */ +static int Decode_String(char * ptr, CodeType code) +{ + int count = 0; + while (code > 255) { + *ptr++ = dict[code].CharValue; + count++; + code = dict[code].ParentCode; + } + *ptr = (char)code; + count++; + return(count); +} + + +/* + * The compressor is short and simple. It reads in new symbols one + * at a time from the input file. It then checks to see if the + * combination of the current symbol and the current code are already + * defined in the dictionary. If they are not, they are added to the + * dictionary, and we start over with a new one symbol code. If they + * are, the code for the combination of the code and character becomes + * our new code. + */ + +int LZW_Compress(Buffer & inbuff, Buffer & outbuff) +{ + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + for (int i = 0; i < TABLE_SIZE; i++) { + dict[i].Make_Unused(); +// dict[i].code_value = UNUSED; + } + + int outcount = 0; + CodeType string_code = END_OF_STREAM; + CodeType next_code = FIRST_CODE; + for (;;) { + char character; + + if (input.Get(&character, sizeof(character)) == 0) break; + + int index = Find_Child_Node(string_code, character); + + if (index == -1) { + string_code = character; + outcount += output.Put(&string_code, sizeof(string_code)); + } else { + + if (dict[index].CodeValue != -1) { + string_code = dict[ index ].CodeValue; + } else { + if (next_code <= MAX_CODE) { + dict[index] = CodeClass(next_code++, string_code, character); + } + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = character; + } + } + } + + outcount += output.Put(&string_code, sizeof(string_code)); + string_code = END_OF_STREAM; + outcount += output.Put(&string_code, sizeof(string_code)); + + return(outcount); +} + + +/* + * The file expander operates much like the encoder. It has to + * read in codes, the convert the codes to a string of characters. + * The only catch in the whole operation occurs when the encoder + * encounters a CHAR+STRING+CHAR+STRING+CHAR sequence. When this + * occurs, the encoder outputs a code that is not presently defined + * in the table. This is handled as an exception. + */ +int LZW_Uncompress(Buffer & inbuff, Buffer & outbuff) +{ + int outcount = 0; + BufferStraw input(inbuff); + BufferPipe output(outbuff); + + CodeType old_code; + if (input.Get(&old_code, sizeof(old_code)) == 0) { + return(outcount); + } + + char character = (char)old_code; + outcount += output.Put(&character, sizeof(character)); + + unsigned int count; + CodeType new_code; + CodeType next_code = FIRST_CODE; + for (;;) { + if (input.Get(&new_code, sizeof(new_code)) == 0) break; + + /* + ** This code checks for the CHARACTER+STRING+CHARACTER+STRING+CHARACTER + ** case which generates an undefined code. It handles it by decoding + ** the last code, and adding a single character to the end of the decode string. + */ + if (new_code >= next_code) { + decode_stack[0] = character; + count = 1; + count += Decode_String(&decode_stack[1], old_code); + } else { + count = Decode_String(decode_stack, new_code); + } + + character = decode_stack[count-1]; + while (count > 0) { + --count; + outcount += output.Put(&decode_stack[count], sizeof(decode_stack[0])); + } + + /* + ** Add the new code sequence to the dictionary (presuming there is still + ** room). + */ + if (next_code <= MAX_CODE) { + dict[next_code] = CodeClass(next_code, old_code, character); + next_code++; + } + old_code = new_code; + } + + return(outcount); +} + +#endif + +// jmarshall: This function is from OmniBlade at Chronoshift +// https://github.com/TheAssemblyArmada/Chronoshift/blob/develop/src/game/common/lcw.cpp +/** + * @brief Compresses data to the proprietary LCW format used in many games developed by Westwood Studios. + * @warning Worst case can have the compressed data larger than the original. + */ +extern "C" int LCW_Comp(const void* src, void* dst, unsigned int bytes) +{ + //captainslog_assert(src != nullptr); + //captainslog_assert(dst != nullptr); + + if (!bytes) { + return 0; + } + + // Decide if we are going to do relative offsets for 3 and 5 byte commands + bool relative = bytes > UINT16_MAX; + + const uint8_t* getp = static_cast(src); + uint8_t* putp = static_cast(dst); + const uint8_t* getstart = getp; + const uint8_t* getend = getp + bytes; + uint8_t* putstart = putp; + + // captainslog_debug("LCW Compression... "); + + // relative LCW starts with 0 to flag as relative for decoder + if (relative) { + // captainslog_debug("LCW Relative compressor."); + *putp++ = 0; + } + + // Write a starting cmd1 and set bool to have cmd1 in progress + uint8_t* cmd_onep = putp; + *putp++ = 0x81; + *putp++ = *getp++; + bool cmd_one = true; + + // Compress data + while (getp < getend) { + // Is RLE encode (4bytes) worth evaluating? + if (getend - getp > 64 && *getp == *(getp + 64)) { + // RLE run length is encoded as a short so max is UINT16_MAX + const uint8_t* rlemax = (getend - getp) < UINT16_MAX ? getend : getp + UINT16_MAX; + const uint8_t* rlep; + + for (rlep = getp + 1; *rlep == *getp && rlep < rlemax; ++rlep) + ; + + uint16_t run_length = rlep - getp; + + // If run length is long enough, write the command and start loop again + if (run_length >= 0x41) { + // captainslog_debug("0b11111110 Source Pos %ld, Dest Pos %ld, Count %d", getp - getstart, putp - putstart, + // run_length); write 4byte command 0b11111110 + cmd_one = false; + *putp++ = 0xFE; + *putp++ = run_length; + *putp++ = run_length >> 8; + *putp++ = *getp; + getp = rlep; + continue; + } + } + + // current block size for an offset copy + int block_size = 0; + const uint8_t* offstart; + + // Set where we start looking for matching runs. + if (relative) { + offstart = (getp - getstart) < UINT16_MAX ? getstart : getp - UINT16_MAX; + } + else { + offstart = getstart; + } + + // Look for matching runs + const uint8_t* offchk = offstart; + const uint8_t* offsetp = getp; + while (offchk < getp) { + // Move offchk to next matching position + while (offchk < getp && *offchk != *getp) { + ++offchk; + } + + // If the checking pointer has reached current pos, break + if (offchk >= getp) { + break; + } + + // find out how long the run of matches goes for + //<= because it can consider the current pixel as part of a run + int i; + for (i = 1; &getp[i] < getend; ++i) { + if (offchk[i] != getp[i]) { + break; + } + } + + if (i >= block_size) { + block_size = i; + offsetp = offchk; + } + + ++offchk; + } + + // decide what encoding to use for current run + if (block_size <= 2) { + // short copy 0b10?????? + // check we have an existing 1 byte command and if its value is still + // small enough to handle additional bytes + // start a new command if current one doesn't have space or we don't + // have one to continue + if (cmd_one && *cmd_onep < 0xBF) { + // increment command value + ++* cmd_onep; + // captainslog_debug("0b10?????? Source Pos %ld, Dest Pos %ld, Count %d", getp - getstart, cmd_onep - putstart, + // *cmd_onep - 0x80); + *putp++ = *getp++; + } + else { + cmd_onep = putp; + // captainslog_debug("0b10?????? Source Pos %ld, Dest Pos %ld, Count %d", getp - getstart, cmd_onep - putstart, 1); + *putp++ = 0x81; + *putp++ = *getp++; + cmd_one = true; + } + } + else { + uint16_t offset; + uint16_t rel_offset = getp - offsetp; + if (block_size > 0xA || (rel_offset > 0xFFF)) { + // write 5 byte command 0b11111111 + if (block_size > 0x40) { + // captainslog_debug("0b11111111 Source Pos %ld, Dest Pos %ld, Count %d, ", getp - getstart, putp - putstart, + // block_size); + *putp++ = 0xFF; + *putp++ = block_size; + *putp++ = block_size >> 8; + // write 3 byte command 0b11?????? + } + else { + // captainslog_debug("0b11?????? Source Pos %ld, Dest Pos %ld, Count %d, ", getp - getstart, putp - putstart, + // block_size); + *putp++ = (block_size - 3) | 0xC0; + } + + offset = relative ? rel_offset : offsetp - getstart; + // captainslog_debug("Offset %d", offset); + // write 2 byte command? 0b0??????? + } + else { + // captainslog_debug("0b0??????? Source Pos %ld, Dest Pos %ld, Count %d, ", getp - getstart, putp - putstart, + // block_size); uint8_t cmd_two_val = 16 * (block_size - 3) + (rel_offset >> 8); captainslog_debug("Offset %d", + // rel_offset); + offset = rel_offset << 8 | (16 * (block_size - 3) + (rel_offset >> 8)); + } + *putp++ = offset; + *putp++ = offset >> 8; + getp += block_size; + cmd_one = false; + } + } + + // write final 0x80, this is why its also known as format80 compression + *putp++ = 0x80; + return putp - putstart; +} +// jmarshall end \ No newline at end of file diff --git a/code/redalert/lzw.h b/code/redalert/lzw.h new file mode 100644 index 0000000..2d69e26 --- /dev/null +++ b/code/redalert/lzw.h @@ -0,0 +1,86 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 08/28/96 * + * * + * Last Update : August 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _LZW_H +#define _LZW_H + +#include "buff.h" + +class LZWEngine +{ + public: + LZWEngine(void); + + int Compress(Buffer const & input, Buffer const & output); + int Uncompress(Buffer const & input, Buffer const & output); + + void Reset(void); + + private: + typedef short CodeType; + struct CodeClass { + CodeType CodeValue; + CodeType ParentCode; + char CharValue; + + CodeClass(void) {} + CodeClass(CodeType code, CodeType parent, char c) : CodeValue(code), ParentCode(parent), CharValue(c) {} + + enum {UNUSED=-1}; + void Make_Unused(void) {CodeValue = UNUSED;} + bool Is_Unused(void) const {return(CodeValue == UNUSED);} + bool Is_Matching(CodeType code, char c) const {return(ParentCode == code && CharValue == c);} + }; + + enum { + BITS=12, + MAX_CODE=((1 << BITS ) - 1), + FIRST_CODE=257, + END_OF_STREAM=256, + TABLE_SIZE=5021 + }; + CodeClass dict[TABLE_SIZE]; + + char decode_stack[TABLE_SIZE]; + + int Find_Child_Node(CodeType parent_code, char child_character); + int Decode_String(char * ptr, CodeType code); + static int Make_LZW_Hash(CodeType code, char character); +}; + + +int LZW_Compress(Buffer const & inbuff, Buffer const & outbuff); +int LZW_Uncompress(Buffer const & inbuff, Buffer const & outbuff); + +#endif diff --git a/code/redalert/lzwotraw.cpp b/code/redalert/lzwotraw.cpp new file mode 100644 index 0000000..672b00c --- /dev/null +++ b/code/redalert/lzwotraw.cpp @@ -0,0 +1,181 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWOTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzostraw.h" +#include "lzwstraw.h" +#include "lzo.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + char *stageing_buffer = new char [BlockHeader.CompCount]; + incount = Straw::Get(staging_buffer, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + lz01x_decompress (ptr, BlockHeader.CompCount, Buffer, sizeof(Buffer), NULL); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + int len = sizeof (Buffer2) - sizeof (BlockHeader); + char *dictionary = new char [64*1024]; + lzo1x_1_compress (Buffer, BlockHeader.UncompCount, &Buffer2[sizeof(BlockHeader)], &BlockHeader.CompCount, dictionary); + delete [] dictionary; + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/code/redalert/lzwpipe.cpp b/code/redalert/lzwpipe.cpp new file mode 100644 index 0000000..5573c6e --- /dev/null +++ b/code/redalert/lzwpipe.cpp @@ -0,0 +1,312 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "lzwpipe.h" +#include "lzw.h" +#include "buff.h" +#include +#include + + +/*********************************************************************************************** + * LZWPipe::LZWPipe -- Constructor for the LZW processor pipe. * + * * + * This will initialize the LZWPipe object so that it is prepared for compression or * + * decompression as indicated. * + * * + * INPUT: decrypt -- Should decompression be performed? * + * * + * blocksize-- The size of the data blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::LZWPipe(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + Buffer2 = new char[BlockSize+SafetyMargin]; + BlockHeader.CompCount = 0xFFFF; +} + + +/*********************************************************************************************** + * LZWPipe::~LZWPipe -- Deconstructor for the LZW pipe object. * + * * + * This will free any buffers it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWPipe::~LZWPipe(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWPipe::Put -- Send some data through the LZW processor pipe. * + * * + * This routine will take the data requested and process it (decompression or compression). * + * It does this by accumulating the necessary bytes to make a whole block. Then the block * + * is processed and the entire contents are flushed to the next pipe segment in the chain. * + * * + * INPUT: source -- Pointer to the data to be fed to this LZW processor. * + * * + * length -- The number of bytes received. * + * * + * OUTPUT: Returns with the actual number of bytes output at the far distant final link in * + * the pipe chain. * + * * + * WARNINGS: The compression process may be slow as well as consuming two buffers. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Put(void const * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Pipe::Put(source, slen)); + } + + assert(Buffer != NULL); + + int total = 0; + + /* + ** Copy as much as can fit into the buffer from the source data supplied. + */ + if (Control == DECOMPRESS) { + + while (slen > 0) { + + /* + ** First check to see if we are in the block header accumulation phase. + ** When a whole block header has been accumulated, only then will the regular + ** data processing begin for the block. + */ + if (BlockHeader.CompCount == 0xFFFF) { + int len = (slen < ((int)sizeof(BlockHeader)-Counter)) ? slen : (sizeof(BlockHeader)-Counter); + memmove(&Buffer[Counter], source, len); + source = ((char *)source) + len; + slen -= len; + Counter += len; + + /* + ** A whole block header has been accumulated. Store it for safekeeping. + */ + if (Counter == sizeof(BlockHeader)) { + memmove(&BlockHeader, Buffer, sizeof(BlockHeader)); + Counter = 0; + } + } + + /* + ** Fill the buffer with compressed data until there is enough to make a whole + ** data block. + */ + if (slen > 0) { + int len = (slen < (BlockHeader.CompCount-Counter)) ? slen : (BlockHeader.CompCount-Counter); + + memmove(&Buffer[Counter], source, len); + slen -= len; + source = ((char *)source) + len; + Counter += len; + + /* + ** If an entire block has been accumulated, then uncompress it and feed it + ** through the pipe. + */ + if (Counter == BlockHeader.CompCount) { + LZW_Uncompress(Buffer, Buffer2); + total += Pipe::Put(Buffer2, BlockHeader.UncompCount); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + } + } + + } else { + + /* + ** If the buffer already contains some data, then any new data must be stored + ** into the staging buffer until a full set has been accumulated. + */ + if (Counter > 0) { + int tocopy = (slen < (BlockSize-Counter)) ? slen : (BlockSize-Counter); + memmove(&Buffer[Counter], source, tocopy); + source = ((char *)source) + tocopy; + slen -= tocopy; + Counter += tocopy; + + if (Counter == BlockSize) { + int len = LZW_Compress(::Buffer(Buffer, BlockSize), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + /* + ** Process the source data in whole block chunks until there is insufficient + ** source data left for a whole data block. + */ + while (slen >= BlockSize) { + int len = LZW_Compress(::Buffer((void*)source, BlockSize), Buffer2); + + source = ((char *)source) + BlockSize; + slen -= BlockSize; + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)BlockSize; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + } + + /* + ** If there is any remaining data, then it is stored into the buffer + ** until a full data block has been accumulated. + */ + if (slen > 0) { + memmove(Buffer, source, slen); + Counter = slen; + } + } + + return(total); +} + + +/*********************************************************************************************** + * LZWPipe::Flush -- Flushes any partially accumulated block. * + * * + * This routine is called when any buffered data must be flushed out the pipe. For the * + * compression process, this will generate the sub-sized compressed block. For * + * decompression, this routine should not have any data in the buffer. In such a case, it * + * means that the data source was prematurely truncated. In such a case, just dump the * + * accumulated data through the pipe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the actual number of data bytes output to the distant final link in * + * the pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWPipe::Flush(void) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** If there is accumulated data, then it must processed. + */ + if (Counter > 0) { + if (Control == DECOMPRESS) { + + /* + ** If the accumulated data is insufficient to make a block header, then + ** this means the data has been truncated. Just dump the data through + ** as if were already decompressed. + */ + if (BlockHeader.CompCount == 0xFFFF) { + total += Pipe::Put(Buffer, Counter); + Counter = 0; + } + + /* + ** There appears to be a partial block accumulated in the buffer. It would + ** be disastrous to try to decompress the data since there wouldn't be + ** the special end of data code that LZW decompression needs. In this + ** case, dump the data out as if it were already decompressed. + */ + if (Counter > 0) { + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer, Counter); + Counter = 0; + BlockHeader.CompCount = 0xFFFF; + } + + } else { + + /* + ** A partial block in the compression process is a normal occurrence. Just + ** compress the partial block and output normally. + */ + int len = LZW_Compress(::Buffer(Buffer, Counter), Buffer2); + + BlockHeader.CompCount = (unsigned short)len; + BlockHeader.UncompCount = (unsigned short)Counter; + total += Pipe::Put(&BlockHeader, sizeof(BlockHeader)); + total += Pipe::Put(Buffer2, len); + Counter = 0; + } + } + + total += Pipe::Flush(); + return(total); +} + diff --git a/code/redalert/lzwpipe.h b/code/redalert/lzwpipe.h new file mode 100644 index 0000000..106870a --- /dev/null +++ b/code/redalert/lzwpipe.h @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWPIPE_H +#define LZWPIPE_H + +#include "pipe.h" + + +/* +** Performs LZW compression/decompression on the data stream that is piped through this +** class. The data is compressed in blocks so of small enough size to be compressed +** quickly and large enough size to get decent compression rates. +*/ +class LZWPipe : public Pipe +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWPipe(CompControl, int blocksize=1024*8); + virtual ~LZWPipe(void); + + virtual int Flush(void); + virtual int Put(void const * source, int slen); + + private: + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWPipe(LZWPipe & rvalue); + LZWPipe & operator = (LZWPipe const & pipe); +}; + + +#endif diff --git a/code/redalert/lzwstraw.cpp b/code/redalert/lzwstraw.cpp new file mode 100644 index 0000000..8a8914f --- /dev/null +++ b/code/redalert/lzwstraw.cpp @@ -0,0 +1,177 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * LZWStraw::Get -- Fetch data through the LZW processor. * + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "lzwstraw.h" +#include "lzw.h" +#include +#include + + +/*********************************************************************************************** + * LZWStraw::LZWStraw -- Constructor for LZW straw object. * + * * + * This will initialize the LZW straw object. Whether the object is to compress or * + * decompress and the block size to use is specified. The data is compressed in blocks * + * that are sized to be quick to compress and yet still yield good compression ratios. * + * * + * INPUT: decrypt -- Should the data be decompressed? * + * * + * blocksize-- The size of the blocks to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: It takes two buffers of the blocksize specified if compression is to be * + * performed. * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::LZWStraw(CompControl control, int blocksize) : + Control(control), + Counter(0), + Buffer(NULL), + Buffer2(NULL), + BlockSize(blocksize) +{ + SafetyMargin = BlockSize; +// SafetyMargin = BlockSize/128+1; + Buffer = new char[BlockSize+SafetyMargin]; + if (control == COMPRESS) { + Buffer2 = new char[BlockSize+SafetyMargin]; + } +} + + +/*********************************************************************************************** + * LZWStraw::~LZWStraw -- Destructor for the LZW straw. * + * * + * The destructor will free up the allocated buffers that it allocated in the constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +LZWStraw::~LZWStraw(void) +{ + delete [] Buffer; + Buffer = NULL; + + delete [] Buffer2; + Buffer2 = NULL; +} + + +/*********************************************************************************************** + * LZWStraw::Get -- Fetch data through the LZW processor. * + * * + * This routine will fetch the data bytes specified. It does this by first accumulating * + * a full block of data and then compressing or decompressing it as indicated. Subsequent * + * requests for data will draw from this buffer of processed data until it is exhausted * + * and another block must be fetched. * + * * + * INPUT: destbuf -- Pointer to the buffer to hold the data requested. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. If this number * + * is less than that requested, then this indicates that the data source has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + *=============================================================================================*/ +int LZWStraw::Get(void * destbuf, int slen) +{ + assert(Buffer != NULL); + + int total = 0; + + /* + ** Verify parameters for legality. + */ + if (destbuf == NULL || slen < 1) { + return(0); + } + + while (slen > 0) { + + /* + ** Copy as much data is requested and available into the desired + ** destination buffer. + */ + if (Counter) { + int len = (slen < Counter) ? slen : Counter; + if (Control == DECOMPRESS) { + memmove(destbuf, &Buffer[BlockHeader.UncompCount-Counter], len); + } else { + memmove(destbuf, &Buffer2[(BlockHeader.CompCount+sizeof(BlockHeader))-Counter], len); + } + destbuf = ((char *)destbuf) + len; + slen -= len; + Counter -= len; + total += len; + } + if (slen == 0) break; + + if (Control == DECOMPRESS) { + int incount = Straw::Get(&BlockHeader, sizeof(BlockHeader)); + if (incount != sizeof(BlockHeader)) break; + + void * ptr = &Buffer[(BlockSize+SafetyMargin) - BlockHeader.CompCount]; + incount = Straw::Get(ptr, BlockHeader.CompCount); + if (incount != BlockHeader.CompCount) break; + + LZW_Uncompress(ptr, Buffer); + Counter = BlockHeader.UncompCount; + } else { + BlockHeader.UncompCount = (unsigned short)Straw::Get(Buffer, BlockSize); + if (BlockHeader.UncompCount == 0) break; + BlockHeader.CompCount = (unsigned short)LZW_Compress(::Buffer(Buffer, BlockHeader.UncompCount), &Buffer2[sizeof(BlockHeader)]); + memmove(Buffer2, &BlockHeader, sizeof(BlockHeader)); + Counter = BlockHeader.CompCount+sizeof(BlockHeader); + } + } + + return(total); +} diff --git a/code/redalert/lzwstraw.h b/code/redalert/lzwstraw.h new file mode 100644 index 0000000..bbfda3c --- /dev/null +++ b/code/redalert/lzwstraw.h @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/LZWSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : LZWSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef LZWSTRAW_H +#define LZWSTRAW_H + + +#include "straw.h" + +/* +** This class handles LZW compression/decompression to the data stream that is drawn through +** this class. Note that for compression, two internal buffers are required. For decompression +** only one buffer is required. This changes the memory footprint of this class depending on +** the process desired. +*/ +class LZWStraw : public Straw +{ + public: + typedef enum CompControl { + COMPRESS, + DECOMPRESS + } CompControl; + + LZWStraw(CompControl control, int blocksize=1024*8); + virtual ~LZWStraw(void); + + virtual int Get(void * source, int slen); + + private: + + /* + ** This tells the pipe if it should be decompressing or compressing the data stream. + */ + CompControl Control; + + /* + ** The number of bytes accumulated into the staging buffer. + */ + int Counter; + + /* + ** Pointer to the working buffer that compression/decompression will use. + */ + char * Buffer; + char * Buffer2; + + /* + ** The working block size. Data will be compressed in chunks of this size. + */ + int BlockSize; + + /* + ** LZW compression requires a safety margin when decompressing over itself. This + ** margin is only for the worst case situation (very rare). + */ + int SafetyMargin; + + /* + ** Each block has a header of this format. + */ + struct { + unsigned short CompCount; // Size of data block (compressed). + unsigned short UncompCount; // Bytes of uncompressed data it represents. + } BlockHeader; + + LZWStraw(LZWStraw & rvalue); + LZWStraw & operator = (LZWStraw const & pipe); +}; + + +#endif diff --git a/code/redalert/map.cpp b/code/redalert/map.cpp new file mode 100644 index 0000000..43f1f5f --- /dev/null +++ b/code/redalert/map.cpp @@ -0,0 +1,2284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAP.CPP 3 3/14/97 5:15p Joe_b $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * MapClass::Detach -- Remove specified object from map references. * + * MapClass::In_Radar -- Is specified cell in the radar map? * + * MapClass::Init -- clears all cells * + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * MapClass::Logic -- Handles map related logic functions. * + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * MapClass::One_Time -- Performs special one time initializations for the map. * + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * MapClass::Pick_Up -- Removes specified object from the map. * + * MapClass::Place_Down -- Places the specified object onto the map. * + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * MapClass::Set_Map_Dimensions -- Initialize the map. * + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * MapClass::Validate -- validates every cell on the map * + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#define MCW MAP_CELL_W +int const MapClass::RadiusOffset[] = { + /* 0 */ 0, + /* 1 */ (-MCW*1)-1,(-MCW*1)+0,(-MCW*1)+1,-1,1,(MCW*1)-1,(MCW*1)+0,(MCW*1)+1, + /* 2 */ (-MCW*2)-1,(-MCW*2)+0,(-MCW*2)+1,(-MCW*1)-2,(-MCW*1)+2,-2,2,(MCW*1)-2,(MCW*1)+2,(MCW*2)-1,(MCW*2)+0,(MCW*2)+1, + /* 3 */ (-MCW*3)-1,(-MCW*3)+0,(-MCW*3)+1,(-MCW*2)-2,(-MCW*2)+2,(-MCW*1)-3,(-MCW*1)+3,-3,3,(MCW*1)-3,(MCW*1)+3,(MCW*2)-2,(MCW*2)+2,(MCW*3)-1,(MCW*3)+0,(MCW*3)+1, + /* 4 */ (-MCW*4)-1,(-MCW*4)+0,(-MCW*4)+1,(-MCW*3)-3,(-MCW*3)-2,(-MCW*3)+2,(-MCW*3)+3,(-MCW*2)-3,(-MCW*2)+3,(-MCW*1)-4,(-MCW*1)+4,-4,4,(MCW*1)-4,(MCW*1)+4,(MCW*2)-3,(MCW*2)+3,(MCW*3)-3,(MCW*3)-2,(MCW*3)+2,(MCW*3)+3,(MCW*4)-1,(MCW*4)+0,(MCW*4)+1, + /* 5 */ (-MCW*5)-1,(-MCW*5)+0,(-MCW*5)+1,(-MCW*4)-3,(-MCW*4)-2,(-MCW*4)+2,(-MCW*4)+3,(-MCW*3)-4,(-MCW*3)+4,(-MCW*2)-4,(-MCW*2)+4,(-MCW*1)-5,(-MCW*1)+5,-5,5,(MCW*1)-5,(MCW*1)+5,(MCW*2)-4,(MCW*2)+4,(MCW*3)-4,(MCW*3)+4,(MCW*4)-3,(MCW*4)-2,(MCW*4)+2,(MCW*4)+3,(MCW*5)-1,(MCW*5)+0,(MCW*5)+1, + /* 6 */ (-MCW*6)-1,(-MCW*6)+0,(-MCW*6)+1,(-MCW*5)-3,(-MCW*5)-2,(-MCW*5)+2,(-MCW*5)+3,(-MCW*4)-4,(-MCW*4)+4,(-MCW*3)-5,(-MCW*3)+5,(-MCW*2)-5,(-MCW*2)+5,(-MCW*1)-6,(-MCW*1)+6,-6,6,(MCW*1)-6,(MCW*1)+6,(MCW*2)-5,(MCW*2)+5,(MCW*3)-5,(MCW*3)+5,(MCW*4)-4,(MCW*4)+4,(MCW*5)-3,(MCW*5)-2,(MCW*5)+2,(MCW*5)+3,(MCW*6)-1,(MCW*6)+0,(MCW*6)+1, + /* 7 */ (-MCW*7)-1,(-MCW*7)+0,(-MCW*7)+1,(-MCW*6)-3,(-MCW*6)-2,(-MCW*6)+2,(-MCW*6)+3,(-MCW*5)-5,(-MCW*5)-4,(-MCW*5)+4,(-MCW*5)+5,(-MCW*4)-5,(-MCW*4)+5,(-MCW*3)-6,(-MCW*3)+6,(-MCW*2)-6,(-MCW*2)+6,(-MCW*1)-7,(-MCW*1)+7,-7,7,(MCW*1)-7,(MCW*1)+7,(MCW*2)-6,(MCW*2)+6,(MCW*3)-6,(MCW*3)+6,(MCW*4)-5,(MCW*4)+5,(MCW*5)-5,(MCW*5)-4,(MCW*5)+4,(MCW*5)+5,(MCW*6)-3,(MCW*6)-2,(MCW*6)+2,(MCW*6)+3,(MCW*7)-1,(MCW*7)+0,(MCW*7)+1, + /* 8 */ (-MCW*8)-1,(-MCW*8)+0,(-MCW*8)+1,(-MCW*7)-3,(-MCW*7)-2,(-MCW*7)+2,(-MCW*7)+3,(-MCW*6)-5,(-MCW*6)-4,(-MCW*6)+4,(-MCW*6)+5,(-MCW*5)-6,(-MCW*5)+6,(-MCW*4)-6,(-MCW*4)+6,(-MCW*3)-7,(-MCW*3)+7,(-MCW*2)-7,(-MCW*2)+7,(-MCW*1)-8,(-MCW*1)+8,-8,8,(MCW*1)-8,(MCW*1)+8,(MCW*2)-7,(MCW*2)+7,(MCW*3)-7,(MCW*3)+7,(MCW*4)-6,(MCW*4)+6,(MCW*5)-6,(MCW*5)+6,(MCW*6)-5,(MCW*6)-4,(MCW*6)+4,(MCW*6)+5,(MCW*7)-3,(MCW*7)-2,(MCW*7)+2,(MCW*7)+3,(MCW*8)-1,(MCW*8)+0,(MCW*8)+1, + /* 9 */ (-MCW*9)-1,(-MCW*9)+0,(-MCW*9)+1,(-MCW*8)-3,(-MCW*8)-2,(-MCW*8)+2,(-MCW*8)+3,(-MCW*7)-5,(-MCW*7)-4,(-MCW*7)+4,(-MCW*7)+5,(-MCW*6)-6,(-MCW*6)+6,(-MCW*5)-7,(-MCW*5)+7,(-MCW*4)-7,(-MCW*4)+7,(-MCW*3)-8,(-MCW*3)+8,(-MCW*2)-8,(-MCW*2)+8,(-MCW*1)-9,(-MCW*1)+9,-9,9,(MCW*1)-9,(MCW*1)+9,(MCW*2)-8,(MCW*2)+8,(MCW*3)-8,(MCW*3)+8,(MCW*4)-7,(MCW*4)+7,(MCW*5)-7,(MCW*5)+7,(MCW*6)-6,(MCW*6)+6,(MCW*7)-5,(MCW*7)-4,(MCW*7)+4,(MCW*7)+5,(MCW*8)-3,(MCW*8)-2,(MCW*8)+2,(MCW*8)+3,(MCW*9)-1,(MCW*9)+0,(MCW*9)+1, + /* 10 */ (-MCW*10)-1,(-MCW*10)+0,(-MCW*10)+1,(-MCW*9)-3,(-MCW*9)-2,(-MCW*9)+2,(-MCW*9)+3,(-MCW*8)-5,(-MCW*8)-4,(-MCW*8)+4,(-MCW*8)+5,(-MCW*7)-7,(-MCW*7)-6,(-MCW*7)+6,(-MCW*7)+7,(-MCW*6)-7,(-MCW*6)+7,(-MCW*5)-8,(-MCW*5)+8,(-MCW*4)-8,(-MCW*4)+8,(-MCW*3)-9,(-MCW*3)+9,(-MCW*2)-9,(-MCW*2)+9,(-MCW*1)-10,(-MCW*1)+10,-10,10,(MCW*1)-10,(MCW*1)+10,(MCW*2)-9,(MCW*2)+9,(MCW*3)-9,(MCW*3)+9,(MCW*4)-8,(MCW*4)+8,(MCW*5)-8,(MCW*5)+8,(MCW*6)-7,(MCW*6)+7,(MCW*7)-7,(MCW*7)-6,(MCW*7)+6,(MCW*7)+7,(MCW*8)-5,(MCW*8)-4, + (MCW*8)+4,(MCW*8)+5,(MCW*9)-3,(MCW*9)-2,(MCW*9)+2,(MCW*9)+3,(MCW*10)-1,(MCW*10)+0,(MCW*10)+1, +}; + +int const MapClass::RadiusCount[11] = {1,9,21,37,61,89,121,161,205,253,309}; + + +CellClass * BlubCell; + +/*********************************************************************************************** + * MapClass::One_Time -- Performs special one time initializations for the map. * + * * + * This routine is used by the game initialization function in order to perform any one * + * time initializations required for the map. This includes allocation of the map and * + * setting up its default dimensions. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine MUST be called once and only once. * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 12/01/1994 BR : Added CellTriggers initialization * + *=============================================================================================*/ +void MapClass::One_Time(void) +{ + GScreenClass::One_Time(); + + XSize = MAP_CELL_W; + YSize = MAP_CELL_H; + Size = XSize * YSize; + + /* + ** Allocate the cell array. + */ + Alloc_Cells(); +} + + +/*********************************************************************************************** + * MapClass::Init_Clear -- clears the map & buffers to a known state * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Clear(void) +{ + GScreenClass::Init_Clear(); + Init_Cells(); + TiberiumScan = 0; + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + Crates[index].Init(); + } +} + + +/*********************************************************************************************** + * MapClass::Alloc_Cells -- allocates the cell array * + * * + * This routine should be called at One_Time, and after loading the Map object from a save * + * game, but prior to loading the cell objects. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Alloc_Cells(void) +{ + /* + ** Assume that whatever the contents of the VectorClass are is garbage + ** (it may have been loaded from a save-game file), so zero it out first. + */ + new (&Array) VectorClass; + Array.Resize(Size); +} + + +/*********************************************************************************************** + * MapClass::Free_Cells -- frees the cell array * + * * + * This routine is used by the Load_Game routine to free the map's cell array before loading * + * the map object from disk; the array is then re-allocated & cleared before the cell objects * + * are loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Free_Cells(void) +{ + Array.Clear(); +} + + +/*********************************************************************************************** + * MapClass::Init_Cells -- Initializes the cell array to a fresh state. * + * * + * This routine is used by Init_Clear to set the cells to a known state; it's also used by * + * the Load_Game routine to init all cells before loading a set of cells from disk, so it * + * needs to be called separately from the other Init_xxx() routines. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/17/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Init_Cells(void) +{ + TotalValue = 0; + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + new (&Array[index]) CellClass; + } +} + + +/*********************************************************************************************** + * MapClass::Set_Map_Dimensions -- Set map dimensions. * + * * + * This routine is used to set the legal limits and position of the * + * map as it relates to the overall map array. Typically, this is * + * called by the scenario loading code. * + * * + * INPUT: x,y -- The X and Y coordinate of the "upper left" corner * + * of the map. * + * * + * w,h -- The width and height of the legal map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + MapCellX = x; + MapCellY = y; + MapCellWidth = w; + MapCellHeight = h; +} + + +/*********************************************************************************************** + * MapClass::Sight_From -- Mark as visible the cells within a specified radius. * + * * + * This routine is used to reveal the cells around a specific location. * + * Typically, as a unit moves or is deployed, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the sighting originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * incremental-- Is this an incremental sighting. In other * + * words, has this function been called before where * + * the center coordinate is no more than one cell * + * distant from the last time? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1992 JLB : Created. * + * 03/08/1994 JLB : Updated to use sight table and incremental flag. * + * 05/18/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MapClass::Sight_From(CELL cell, int sightrange, HouseClass * house, bool incremental) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > 10) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + if (incremental) { + if (sightrange > 2) { + ptr += RadiusCount[sightrange-3]; + count -= RadiusCount[sightrange-3]; + } + } + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Map the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + //if (!(*this)[newcell].IsMapped) { // ST - 8/7/2019 10:31AM + Map.Map_Cell(newcell, house, true, true); + } +} + + +/*********************************************************************************************** + * MapClass::Shroud_From -- cloak a radius of cells * + * * + * This routine is used to shroud the cells around a specific location. * + * Typically, as a gap generator refreshes (when Encroach_Shadow() is called) this routine's* + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the shrouding originates from. * + * * + * sightrange-- The distance in cells that sighting extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/10/1995 BWG : Created. * + * 08/09/2019 ST : Added house parameter * + *=============================================================================================*/ +void MapClass::Shroud_From(CELL cell, int sightrange, HouseClass *house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot sight. + */ + if (!In_Radar(cell)) return; + if (!sightrange || sightrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[sightrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > sightrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (sightrange * CELL_LEPTON_W)) continue; + + /* + ** Shroud the cell. + */ + Map.Shroud_Cell(newcell, house); + } +} + + +/*********************************************************************************************** + * MapClass::Jam_From -- Mark as jammed the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::Jam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.Jam_Cell(newcell, house/*KO, false*/); + } + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (!house->IsPlayerControl) { + Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != house) { + Map.Constrained_Look(Cell_Coord(cell), Rule.GapShroudRadius * CELL_LEPTON_W, player_house); + } + } + } + +#ifdef OBSOLETE + /* + ** The objects on the map need to perform a manual look operation if they happen + ** to have their sight range overlap the gap radius. + */ + if (!house->IsPlayerControl) { +// if (house != PlayerPtr) { + + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass * object = Map.Layer[LAYER_GROUND][index]; + if (object && object->Is_Techno()) { + TechnoClass * tech = ((TechnoClass *)object); + + if (tech->IsDiscoveredByPlayer && + (tech->Distance(As_Target(cell)) / CELL_LEPTON_W) <= tech->Techno_Type_Class()->SightRange + Rule.GapShroudRadius && + (tech->House->IsPlayerControl || + (tech->What_Am_I() == RTTI_BUILDING && Rule.IsAllyReveal && tech->House->Is_Ally(PlayerPtr)))) { + + object->Look(); + } + } + } + } +#endif + +#ifdef OBSOLETE + /* + ** Here all the player's vehicles will perform a look if they're within + ** the shadow. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass * unit = Infantry.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + for (index = 0; index < Vessels.Count(); index++) { + VesselClass * unit = Vessels.Ptr(index); + if (unit && !unit->IsInLimbo && unit->House == PlayerPtr) { + int dist = (unit->Distance(As_Target(cell))) / CELL_LEPTON_W; + if (dist <= unit->Class->SightRange + Rule.GapShroudRadius /*gap generator sightrange*/) { + unit->Look(); + } + } + } + } +#endif + +} + + +/*********************************************************************************************** + * MapClass::UnJam_From -- Remove jamming on the cells within a specified radius. * + * * + * This routine is used to jam the cells around a specific location. * + * Typically, as a gap generator structure is created, this routine will be * + * called. Since it deals with MANY cells, it needs to be extremely * + * fast. * + * * + * INPUT: cell -- The coordinate that the jamming originates from. * + * * + * jamrange -- The distance in cells that jamming extends. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +void MapClass::UnJam_From(CELL cell, int jamrange, HouseClass * house) +{ + int xx; // Center cell X coordinate (bounds checking). + int const * ptr; // Offset pointer. + int count; // Counter for number of offsets to process. + + /* + ** Units that are off-map cannot jam. + */ + if (!jamrange || jamrange > Rule.GapShroudRadius) return; + + /* + ** Determine logical cell coordinate for center scan point. + */ + xx = Cell_X(cell); + + /* + ** Incremental scans only scan the outer rings. Full scans + ** scan all internal cells as well. + */ + count = RadiusCount[jamrange]; + ptr = &RadiusOffset[0]; + + /* + ** Process all offsets required for the desired scan. + */ + while (count--) { + CELL newcell; // New cell with offset. + int xdiff; // New cell's X coordinate distance from center. + + newcell = cell + *ptr++; + + /* + ** Determine if the map edge has been wrapped. If so, + ** then don't process the cell. + */ + if ((unsigned)newcell >= MAP_CELL_TOTAL) continue; + xdiff = Cell_X(newcell) - xx; + xdiff = ABS(xdiff); + if (xdiff > jamrange) continue; + if (Distance(Cell_Coord(newcell), Cell_Coord(cell)) > (jamrange * CELL_LEPTON_W)) continue; + + /* + ** Jam the cell. For incremental scans, then update + ** adjacent cells as well. For full scans, just update + ** the cell itself. + */ + Map.UnJam_Cell(newcell, house); + } +} + + +/*********************************************************************************************** + * MapClass::In_Radar -- Is specified cell in the radar map? * + * * + * This determines if the specified cell can be within the navigable * + * bounds of the map. Technically, this means, any cell that can be * + * scanned by radar. If a cell returns false from this function, then * + * the player could never move to or pass over this cell. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: bool; Is this cell possible to be displayed on radar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/30/1994 JLB : Converted to member function. * + * 05/01/1994 JLB : Speeded up. * + *=============================================================================================*/ +bool MapClass::In_Radar(CELL cell) const +{ + /* + ** If the cell value is WAY out of range, then it obviously can't be part of the game + ** playfield. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) return(false); + + /* + ** If the cell is off the left or right edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_X(cell) - MapCellX) >= (unsigned)MapCellWidth) return(false); + + /* + ** If the cell is off the top or bottom edge of the playfield, then return the "not in + ** radar" flag. + */ + if ((unsigned)(Cell_Y(cell) - MapCellY) >= (unsigned)MapCellHeight) return(false); + + return(true); +} + + +/*********************************************************************************************** + * MapClass::Place_Down -- Places the specified object onto the map. * + * * + * This routine is used to place an object onto the map. It updates the "occupier" of the * + * cells that this object covers. The cells are determined from the Occupy_List function * + * provided by the object. Only one cell can have an occupier and this routine is the only * + * place that sets this condition. * + * * + * INPUT: cell -- The cell to base object occupation around. * + * * + * object -- The object to place onto the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Place_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Down(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Pick_Up -- Removes specified object from the map. * + * * + * The object specified is removed from the map by this routine. This will remove the * + * occupation flag for all the cells that the object covers. The cells that are covered * + * are determined from the Occupy_List function. * + * * + * INPUT: cell -- The cell that the object is centered about. * + * * + * object -- Pointer to the object that will be removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1994 JLB : Created. * + *=============================================================================================*/ +void MapClass::Pick_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Occupy_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Occupy_Up(object); + (*this)[newcell].Recalc_Attributes(); + (*this)[newcell].Redraw_Objects(); + } + } + + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Down -- computes & marks object's overlap cells * + * * + * This routine is just like Place_Down, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_DOWN, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Down(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Down(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overlap_Up -- Computes & clears object's overlap cells * + * * + * This routine is just like Pick_Up, but it doesn't mark the cell's Occupier. * + * This routine is used to implement MARK_OVERLAP_UP, which is useful for changing * + * an object's render size, but not its logical size (ie when it's selected or an * + * animation is attached to it). * + * * + * INPUT: * + * cell -- The cell to base object overlap around. * + * object -- The object to place onto the map. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/12/1995 BRR : Created. * + *=============================================================================================*/ +void MapClass::Overlap_Up(CELL cell, ObjectClass * object) +{ + if (!object) return; + + if (object->Class_Of().IsFootprint && object->In_Which_Layer() == LAYER_GROUND) { + short xlist[32]; + List_Copy(object->Overlap_List(), ARRAY_SIZE(xlist), xlist); + short const * list = xlist; + while (*list != REFRESH_EOL) { + CELL newcell = cell + *list++; + if ((unsigned)newcell < MAP_CELL_TOTAL) { + (*this)[newcell].Overlap_Up(object); + (*this)[newcell].Redraw_Objects(); + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Overpass -- Performs any final cleanup to a freshly constructed map. * + * * + * This routine will clean up anything necessary with the presumption that the map has * + * been freshly created. Such things to clean up include various tiberium concentrations. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the total credit value of the tiberium on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + * 02/13/1995 JLB : Returns total tiberium worth. * + * 02/15/1995 JLB : Optimal scan. * + *=============================================================================================*/ +long MapClass::Overpass(void) +{ + long value = 0; + + /* + ** Smooth out Tiberium. Cells that are not surrounded by other tiberium + ** will be reduced in density. + */ + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = (MapCellY+y) * MAP_CELL_W + (MapCellX+x); + value += (*this)[cell].Tiberium_Adjust(true); + (*this)[cell].Recalc_Attributes(); + } + } + return(value); +} + + +/*********************************************************************************************** + * MapClass::Write_Binary -- Pipes the map template data to the destination specified. * + * * + * This stores the template data from the map to the output pipe specified. The template * + * data consists of the template type number and template icon number for every cell on * + * the map. The output is organized in such a way so as to get maximum compression. * + * * + * INPUT: pipe -- Reference to the output pipe that will receive the map template data. * + * * + * OUTPUT: Returns with the total number of bytes output to the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Write_Binary(Pipe & pipe) +{ + int total = 0; + + LCWPipe comp(LCWPipe::COMPRESS); + comp.Put_To(&pipe); + + CellClass * cellptr = &Array[0]; + for (int i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + + cellptr = &Array[0]; + for (int i = 0; i < MAP_CELL_TOTAL; i++) { + total += comp.Put(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr++; + } + + return(total); +} + + +/*********************************************************************************************** + * MapClass::Read_Binary -- Reads the binary data from the straw specified. * + * * + * This routine will retrieve the map template data from the straw specified. * + * * + * INPUT: straw -- Reference to the straw that will supply the map template data. * + * * + * OUTPUT: bool; Was the template data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Read_Binary(Straw & straw) +{ + LCWStraw decomp(LCWStraw::DECOMPRESS); + decomp.Get_From(&straw); + + CELL cell; + CellClass * cellptr; + switch (NewINIFormat) { + default: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + cellptr++; + } + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + + case 0: + case 1: + case 2: + cellptr = &Array[0]; + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + decomp.Get(&cellptr->TType, sizeof(cellptr->TType)); + decomp.Get(&cellptr->TIcon, sizeof(cellptr->TIcon)); + cellptr->Recalc_Attributes(); + cellptr++; + } + break; + } + return(true); +} + + +/*********************************************************************************************** + * MapClass::Logic -- Handles map related logic functions. * + * * + * Manages tiberium growth and spread. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/11/1995 JLB : Created. * + * 07/09/1995 JLB : Handles two directional scan. * + * 08/01/1995 JLB : Gives stronger weight to blossom trees. * + *=============================================================================================*/ +void MapClass::Logic(void) +{ + if (Debug_Force_Crash) { *((int *)0) = 1; } + /* + ** Crate regeneration is handled here. + */ + if (Session.Type != GAME_NORMAL && Session.Options.Goodies) { + + /* + ** Find any crate that has expired and then regenerate it at a new + ** spot. + */ + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Expired()) { + Crates[index].Remove_It(); + Place_Random_Crate(); + } + } + } + + /* + ** Bail early if there is no allowed growth or spread of Tiberium. + */ + if (!Rule.IsTGrowth && !Rule.IsTSpread) return; + + /* + ** Scan another block of the map in order to accumulate the potential + ** Tiberium cells that can grow or spread. + */ + int subcount = MAP_CELL_TOTAL / (Rule.GrowthRate * TICKS_PER_MINUTE); + subcount = max(subcount, 1); + int index; + for (index = TiberiumScan; index < MAP_CELL_TOTAL; index++) { + CELL cell = index; + if (In_Radar(cell)) { + CellClass * ptr = &(*this)[cell]; + + /* + ** Tiberium cells can grow. + */ + if (ptr->Can_Tiberium_Grow()) { + + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumGrowthExcess) <= TiberiumGrowthCount) { + if (TiberiumGrowthCount < sizeof(TiberiumGrowth)/sizeof(TiberiumGrowth[0])) { + TiberiumGrowth[TiberiumGrowthCount++] = cell; + } else { + TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)] = cell; + } + } + TiberiumGrowthExcess++; + } + + /* + ** Heavy Tiberium growth can spread. + */ + if (ptr->Can_Tiberium_Spread()) { + /* + ** Either replace an existing recorded cell value or add the new cell value to + ** the list. + */ + if (Random_Pick(0, TiberiumSpreadExcess) <= TiberiumSpreadCount) { + if (TiberiumSpreadCount < ARRAY_SIZE(TiberiumSpread)) { + TiberiumSpread[TiberiumSpreadCount++] = cell; + } else { + TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)] = cell; + } + } + TiberiumSpreadExcess++; + } + } + + subcount--; + if (subcount == 0) break; + } + TiberiumScan = index; + + /* + ** When the entire map has been processed, proceed with tiberium (ore) growth + ** and spread action. + */ + if (TiberiumScan >= MAP_CELL_TOTAL) { + TiberiumScan = 0; + + /* + ** Growth logic. + */ + if (TiberiumGrowthCount) { + for (int i = 0; i < TiberiumGrowthCount; i++) { + CELL cell = TiberiumGrowth[i]; + CellClass * newcell = &(*this)[cell]; + newcell->Grow_Tiberium(); + } + } + TiberiumGrowthCount = 0; + TiberiumGrowthExcess = 0; + + /* + ** Spread logic. + */ + if (TiberiumSpreadCount) { + for (int i = 0; i < TiberiumSpreadCount; i++) { + Map[TiberiumSpread[i]].Spread_Tiberium(); + } + } + TiberiumSpreadCount = 0; + TiberiumSpreadExcess = 0; + } +} + + +/*********************************************************************************************** + * MapClass::Cell_Region -- Determines the region from a specified cell number. * + * * + * Use this routine to determine what region a particular cell lies in. * + * * + * INPUT: cell -- The cell number to examine. * + * * + * OUTPUT: Returns with the region that the specified cell occupies. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/15/1995 JLB : Created. * + *=============================================================================================*/ +int MapClass::Cell_Region(CELL cell) +{ + return((Cell_X(cell) / REGION_WIDTH) + 1) + (((Cell_Y(cell) / REGION_HEIGHT) + 1) * MAP_REGION_WIDTH); +} + + +/*************************************************************************** + * MapClass::Cell_Threat -- Gets a houses threat value for a cell * + * * + * INPUT: CELL cell - the cell number to check * + * HouseType house - the house to check * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1995 PWG : Created. * + *=========================================================================*/ +int MapClass::Cell_Threat(CELL cell, HousesType house) +{ + int threat = HouseClass::As_Pointer(house)->Regions[Map.Cell_Region(Map[cell].Cell_Number())].Threat_Value(); + //using function for IsVisible so we have different results for different players - JAS 2019/09/30 + if (!threat && Map[cell].Is_Visible(house)) { + threat = 1; + } + return(threat); +} + + +/*********************************************************************************************** + * MapClass::Place_Random_Crate -- Places a crate at random location on map. * + * * + * This routine will place a crate at a random location on the map. This routine will only * + * make a limited number of attempts to place and if unsuccessful, it will not place any. * + * * + * INPUT: none * + * * + * OUTPUT: Was a crate successfully placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Place_Random_Crate(void) +{ + /* + ** Find a crate index that is free for assignment. If there are + ** no free slots, then return with failure to place crate. + */ + int crateindex = 0; + for (crateindex = 0; crateindex < ARRAY_SIZE(Crates); crateindex++) { + if (!Crates[crateindex].Is_Valid()) break; + } + if (crateindex == ARRAY_SIZE(Crates)) { + return(false); + } + + /* + ** Give a good effort to scan for and place a crate down on the map. + */ + for (int index = 0; index < 1000; index++) { + CELL cell = Map.Pick_Random_Location(); + + if (Crates[crateindex].Create_Crate(cell)) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Remove_Crate -- Remove a crate from the specified cell. * + * * + * This will examine the cell and remove any crates there. * + * * + * INPUT: cell -- The cell to examine for crates and remove from. * + * * + * OUTPUT: bool; Was a crate found at the location specified and was it removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/26/1996 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Remove_Crate(CELL cell) +{ + if (Session.Type != GAME_NORMAL) { + for (int index = 0; index < ARRAY_SIZE(Crates); index++) { + if (Crates[index].Is_Here(cell)) { + return(Crates[index].Remove_It()); + } + } + } + +// if (Session.Type == GAME_NORMAL) { + CellClass * cellptr = &(*this)[cell]; + if (cellptr->Overlay != OVERLAY_NONE && OverlayTypeClass::As_Reference(cellptr->Overlay).IsCrate) { + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + return(true); + } +// } else { +// for (int index = 0; index < ARRAY_SIZE(Crates); index++) { +// if (Crates[index].Is_Here(cell)) { +// return(Crates[index].Remove_It()); +// } +// } +// } + return(false); +} + + +/*************************************************************************** + * MapClass::Validate -- validates every cell on the map * + * * + * This is a debugging routine, designed to detect memory trashers that * + * alter the map. This routine is slow, but thorough. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = map is OK, false = an error was found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +int MapClass::Validate(void) +{ + CELL cell; + TemplateType ttype; + unsigned char ticon; + TemplateTypeClass const *tclass; + unsigned char map[13*8]; + OverlayType overlay; + SmudgeType smudge; + ObjectClass * obj; + LandType land; + int i; + +BlubCell = &Array[797]; + +if (BlubCell->Overlapper[1]) { + obj = BlubCell->Overlapper[1]; + if (obj) { + if (obj->IsInLimbo) + obj = obj; + } +} + + /* + ** Check every cell on the map, even those that aren't displayed, + ** in the hopes of detecting a memory trasher. + */ + for (cell = 0; cell < MAP_CELL_TOTAL; cell++) { + /* + ** Validate Template & Icon data + */ + ttype = (*this)[cell].TType; + ticon = (*this)[cell].TIcon; + if (ttype >= TEMPLATE_COUNT && ttype != TEMPLATE_NONE) + return(false); + + /* + ** To validate the icon value, we have to get a copy of the template's + ** "icon map"; this map will have 0xff's in spots where there is no + ** icon. If the icon value is out of range or points to an invalid spot, + ** return an error. + */ + if (ttype != TEMPLATE_NONE) { + tclass = &TemplateTypeClass::As_Reference(ttype); + ticon = (*this)[cell].TIcon; + Mem_Copy(Get_Icon_Set_Map(tclass->Get_Image_Data()), map, tclass->Width * tclass->Height); + if (ticon < 0 || ticon >= (tclass->Width * tclass->Height) || map[ticon]==0xff) { + return (false); + } + } + + /* + ** Validate Overlay + */ + overlay = (*this)[cell].Overlay; + if (overlay < OVERLAY_NONE || overlay >= OVERLAY_COUNT) { + return(false); + } + + /* + ** Validate Smudge + */ + smudge = (*this)[cell].Smudge; + if (smudge < SMUDGE_NONE || smudge >= SMUDGE_COUNT) { + return(false); + } + + /* + ** Validate LandType + */ + land = (*this)[cell].Land_Type(); + if (land < LAND_CLEAR || land >= LAND_COUNT) { + return(false); + } + + /* + ** Validate Occupier + */ + obj = (*this)[cell].Cell_Occupier(); + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + + /* + ** Validate Overlappers + */ + for (i = 0; i < ARRAY_SIZE((*this)[cell].CellClass::Overlapper); i++) { + obj = (*this)[cell].Overlapper[i]; + if (obj) { + if ( + ((unsigned int)obj & 0xff000000) || + ((unsigned int)obj->Next & 0xff000000) || +// ((unsigned int)obj->Trigger & 0xff000000) || + obj->IsInLimbo || + ((unsigned int)Coord_Cell(obj->Coord) >= MAP_CELL_TOTAL)) { + + return (false); + } + } + } + } + + return (true); +} + + +/*********************************************************************************************** + * MapClass::Close_Object -- Finds a clickable close object to the specified coordinate. * + * * + * This routine is used by the mouse input processing code to find a clickable object * + * close to coordinate specified. This is for targeting as well as selection determination. * + * * + * INPUT: coord -- The coordinate to scan for close object from. * + * * + * OUTPUT: Returns with a pointer to an object that is nearby the specified coordinate. * + * * + * WARNINGS: There could be a cloaked object at the location, but it won't be considered * + * if it is not owned by the player. * + * * + * HISTORY: * + * 08/20/1995 JLB : Created. * + *=============================================================================================*/ +ObjectClass * MapClass::Close_Object(COORDINATE coord) const +{ + ObjectClass * object = 0; + int distance = 0; + CELL cell = Coord_Cell(coord); + + /* + ** Scan through current and adjacent cells, looking for the + ** closest object (within reason) to the specified coordinate. + */ + static int _offsets[] = {0, -1, 1, -MAP_CELL_W, MAP_CELL_W, MAP_CELL_W-1, MAP_CELL_W+1, -(MAP_CELL_W-1), -(MAP_CELL_W+1)}; + for (int index = 0; index < (sizeof(_offsets) / sizeof(_offsets[0])); index++) { + + /* + ** Examine the cell for close object. Make sure that the cell actually is a + ** legal one. + */ + CELL newcell = cell + _offsets[index]; + if (In_Radar(newcell)) { + + /* + ** Search through all objects that occupy this cell and then + ** find the closest object. Check against any previously found object + ** to ensure that it is actually closer. + */ + ObjectClass * o = Array[newcell].Cell_Occupier(); + while (o != NULL) { + + /* + ** Special case check to ignore cloaked object if not owned by the player. + */ + // Change for client/server multiplayer. ST - 8/7/2019 10:35AM + //if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) { + if (!o->Is_Techno() || ((TechnoClass *)o)->Is_Owned_By_Player() || ((TechnoClass *)o)->Cloak != CLOAKED) { + int d=-1; + if (o->What_Am_I() == RTTI_BUILDING) { + d = Distance(coord, Cell_Coord(newcell)); + if (d > 0x00C0) d = -1; + } else { + d = Distance(coord, o->Center_Coord()); + } + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = o; + } + } + o = o->Next; + } + } + } + + /* + ** Handle aircraft selection separately, since they aren't tracked in cells while flying + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + + if (aircraft->In_Which_Layer() != LAYER_GROUND) { + if (aircraft->Is_Owned_By_Player() || (aircraft->Cloak != CLOAKED)) { + int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -aircraft->Height))); + if (d >= 0 && (!object || d < distance)) { + distance = d; + object = aircraft; + } + } + } + } + + /* + ** Only return the object if it is within 1/4 cell distance from the specified + ** coordinate. + */ + if (object && distance > 0xC0) { + object = 0; + } + return(object); +} + + +/*********************************************************************************************** + * MapClass::Zone_Reset -- Resets all zone numbers to match the map. * + * * + * This routine will rescan the map and fill in the zone values for each of the cells. * + * All cells that are contiguous are given the same zone number. * + * * + * INPUT: method -- The method to recalculate the zones upon. If 1 then recalc non * + * crushable zone. If 2 then recalc crushable zone. If 3, then * + * recalc both zones. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a time consuming routine. Call it as infrequently as possible. It must * + * be called whenever something that would affect contiguousness occurs. Example: * + * when a bridge is built or destroyed. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Zone_Reset(int method) +{ + /* + ** Zero out all zones to a null state. + */ + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (method & MZONEF_NORMAL) { + Array[index].Zones[MZONE_NORMAL] = 0; + } + if (method & MZONEF_CRUSHER) { + Array[index].Zones[MZONE_CRUSHER] = 0; + } + if (method & MZONEF_DESTROYER) { + Array[index].Zones[MZONE_DESTROYER] = 0; + } + if (method & MZONEF_WATER) { + Array[index].Zones[MZONE_WATER] = 0; + } + } + + /* + ** Normal zone recalculation. + */ + if (method & MZONEF_NORMAL) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_NORMAL)) { + zone++; + } + } + } + + /* + ** Crushable wall recalculation. + */ + if (method & MZONEF_CRUSHER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_CRUSHER)) { + zone++; + } + } + } + + /* + ** Wall destroyer zone recalculation. + */ + if (method & MZONEF_DESTROYER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_DESTROYER)) { + zone++; + } + } + } + + /* + ** Water based zone recalcuation. + */ + if (method & MZONEF_WATER) { + int zone = 1; // Starting zone number. + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Zone_Span(cell, zone, MZONE_WATER)) { + zone++; + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * MapClass::Zone_Span -- Flood fills the specified zone from the cell origin. * + * * + * This routine is used to fill a zone value into the map. The map is "flood filled" from * + * the cell specified. All adjacent (8 connected) and generally passable terrain cells are * + * given the zone number specified. This routine checks for legality before filling * + * occurs. The routine is safe to call even if the legality of the cell is unknown at the * + * time of the call. * + * * + * INPUT: cell -- The cell to begin filling from. * + * * + * zone -- The zone number to assign to all adjacent cells. * + * * + * check -- The zone type to check against. * + * * + * OUTPUT: Returns with the number of cells marked by this routine. * + * * + * WARNINGS: This routine is slow and recursive. Only use when necessary. * + * * + * HISTORY: * + * 09/25/1995 JLB : Created. * + * 10/05/1996 JLB : Examines crushable walls. * + *=============================================================================================*/ +int MapClass::Zone_Span(CELL cell, int zone, MZoneType check) +{ + int filled = 0; + int xbegin = Cell_X(cell); + int xend = xbegin; + int y = Cell_Y(cell); + + /* + ** Perform some preliminary legality checks. If the cell specified + ** is illegal, then no further processing is necessary. + */ + if (y < MapCellY || y >= MapCellY+MapCellHeight || xbegin < MapCellX || xbegin >= MapCellX+MapCellWidth) { + return(0); + } + + /* + ** Find the full extent of the current span by first scanning leftward + ** until a boundary is reached. + */ + for (; xbegin >= MapCellX; xbegin--) { + CellClass * cellptr = &(*this)[XY_Cell(xbegin, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + + /* + ** Special short circuit code to bail from this entire routine if + ** it was called for a cell that is not a legal candidate for + ** zone marking. This eliminates redundant processing and allows this + ** routine to be called for illegal cells without causing an error. + */ + if (xbegin == Cell_X(cell)) return(0); + + /* + ** Otherwise break out of the left scan since a boundary was discovered. + */ + xbegin++; + break; + } + } + xbegin = max(xbegin, MapCellX); + + /* + ** Scan rightward until a boundary is reached. This will then define the + ** extent of the current span. + */ + for (; xend < MapCellX+MapCellWidth; xend++) { + CellClass * cellptr = &(*this)[XY_Cell(xend, y)]; + if (cellptr->Zones[check] != 0 || (!cellptr->Is_Clear_To_Move(check == MZONE_WATER ? SPEED_FLOAT : SPEED_TRACK, true, true, -1, check))) { + xend--; + break; + } + } + xend = min(xend, MapCellX+MapCellWidth-1); + + /* + ** At this point we know the bounds of the current span. Fill in the zone values + ** for the entire span. + */ + for (int x = xbegin; x <= xend; x++) { + (*this)[XY_Cell(x, y)].Zones[check] = zone; + filled++; + } + + /* + ** Now scan the upper and lower shadow rows. If any of these rows contain + ** candidate cells, then recursively call the span process for them. Take + ** note that the adjacent span scanning starts one cell wider on each + ** end of the scan. This is necessary because diagonals are considered + ** adjacent. + */ + for (int x = xbegin-1; x <= xend; x++) { + filled += Zone_Span(XY_Cell(x, y-1), zone, check); + filled += Zone_Span(XY_Cell(x, y+1), zone, check); + } + return(filled); +} + + +/*********************************************************************************************** + * MapClass::Nearby_Location -- Finds a generally clear location near a specified cell. * + * * + * This routine is used to find a location that probably will be ok to move to that is * + * located as close as possible to the specified cell. The computer uses this when it has * + * determined the ideal location for an object, but then needs to give a valid movement * + * destination to a unit. * + * * + * INPUT: cell -- The cell that scanning should radiate out from. * + * * + * zone -- The zone that must be matched to find a legal location (value of -1 means * + * any zone will do). * + * * + * * + * check -- The type of zone to check against. Only valid if a zone value is given. * + * * + * checkflagged -- Whether the cell's flagged status is checked (used when dropping). * + * * + * OUTPUT: Returns with the cell that is generally clear (legal to move to) that is close * + * to the specified cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Nearby_Location(CELL cell, SpeedType speed, int zone, MZoneType check, bool checkflagged) const +{ + CELL topten[10]; + int count = 0; + int xx = Cell_X(cell); + int yy = Cell_Y(cell); + + /* + ** Determine the limits of the scanning in the four directions so that + ** it won't scan past the edge of the world. + */ + int left = MapCellX; + int right = MapCellX + MapCellWidth - 1; + int top = MapCellY; + int bottom = MapCellY + MapCellHeight - 1; + + /* + ** Radiate outward from the specified location, looking for the closest + ** location that is generally clear. + */ + for (int radius = 0; radius < MAP_CELL_W; radius++) { + CELL newcell; + CellClass const * cellptr; + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = xx-radius; x <= xx+radius; x++) { + if (x >= left && x <= right) { + int y = yy-radius; + if (y >= top) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + y = yy+radius; + if (y <= bottom) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count == ARRAY_SIZE(topten)) break; + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = yy-radius; y <= yy+radius; y++) { + if (y >= top && y <= bottom) { + int x = xx-radius; + if (x >= left) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + + x = xx+radius; + if (x <= right) { + newcell = XY_Cell(x, y); + cellptr = &Map[newcell]; + if (Map.In_Radar(newcell) && cellptr->Is_Clear_To_Move(speed, false, false, zone, check) && (!checkflagged || !cellptr->IsFlagged)) { + topten[count++] = newcell; + } + } + if (count == ARRAY_SIZE(topten)) break; + } + } + + if (count > 0) break; + } + + if (count > 0) { + return(topten[Frame % count]); + } + return(0); +} + + +/*********************************************************************************************** + * MapClass::Base_Region -- Finds the owner and base zone for specified cell. * + * * + * This routine is used to determine what base the specified cell is close to and what * + * zone of that base the cell lies in. This routine is particularly useful in letting the * + * computer know when the player targets a destination near a computer's base. * + * * + * INPUT: cell -- The cell that is to be checked. * + * * + * house -- Reference to the house type number. This value will be set if a base * + * was found nearby the specified cell. * + * * + * zone -- The zone that the cell is located in IF the cell is near a base. * + * * + * * + * OUTPUT: Was a base near the specified cell found? If not, then the 'house' and 'zone' * + * reference values are left in an undefined state and the return value will be * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/05/1995 JLB : Created. * + *=============================================================================================*/ +bool MapClass::Base_Region(CELL cell, HousesType & house, ZoneType & zone) const +{ + if ((unsigned)cell < MAP_CELL_TOTAL && In_Radar(cell)) { + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * h = HouseClass::As_Pointer(house); + + if (h && h->IsActive && !h->IsDefeated && h->Center) { + zone = h->Which_Zone(cell); + if (zone != ZONE_NONE) { + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * MapClass::Destroy_Bridge_At -- Destroyes the bridge at location specified. * + * * + * This routine will destroy the bridge at the location specified. * + * * + * INPUT: cell -- A cell that can uniquely identify the bridge. * + * * + * OUTPUT: bool; Was the bridge destroyed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ + +// Need to inform the server of the cell change so it can communicate with the clients - SKY +extern void On_Update_Map_Cell(int cell_x, int cell_y, const char* template_type_name); + +struct CellUpdateStruct +{ + const TemplateTypeClass* Type; + CELL Cell; +}; + +static const int MAX_UPDATES = 8; + +static void Add_Cell_Update(CellUpdateStruct* updates, int& count, TemplateType type, CELL cell) +{ + new TemplateClass(type, cell); + + assert(count < MAX_UPDATES); + if (count < MAX_UPDATES) + { + updates[count].Type = TemplateTypes.Ptr((int)type); + updates[count].Cell = cell; + count++; + } +} + +bool MapClass::Destroy_Bridge_At(CELL cell) +{ + bool destroyed = false; + if (In_Radar(cell) && !Special.IsCaptureTheFlag) { + CellClass * cellptr = &(*this)[cell]; + TemplateType ttype = cellptr->TType; + + CellUpdateStruct cell_updates[MAX_UPDATES]; + int update_count = 0; + + if (ttype == TEMPLATE_BRIDGE1 || ttype == TEMPLATE_BRIDGE2) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + + if (ttype == TEMPLATE_BRIDGE1) { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1H, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2H, cell); + } + + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + w/2 + (h/2)*MAP_CELL_W)); + } + + if (ttype == TEMPLATE_BRIDGE1H || ttype == TEMPLATE_BRIDGE2H) { + int icon = cellptr->TIcon; + int bridge_w = TemplateTypeClass::As_Reference(ttype).Width; + int bridge_h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % bridge_w; + cell -= MAP_CELL_W * (icon / bridge_w); + + if (ttype == TEMPLATE_BRIDGE1H) { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE1D, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE2D, cell); + } + + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + new AnimClass(ANIM_NAPALM3, Cell_Coord(cell + bridge_w/2 + (bridge_h/2)*MAP_CELL_W)); + Map.Zone_Reset(MZONEF_ALL); + + /* + ** Now, loop through all the bridge cells and find anyone standing + ** on a destroyed part (which is now river), and nuke them. + */ + CELL cell2 = cell; + for (int y = 0; y < bridge_h; y++) { + for (int x = 0; x < bridge_w; x++) { + CellClass * bridge_cell = &(*this)[cell2]; + if (bridge_cell->TType == ttype) { + + /* + ** Any unit that is firing on the bridge at this location, will stop + ** firing because the bridge has been destroyed. + */ + Detach_This_From_All(As_Target(cell2), true); + + ObjectClass * obj = bridge_cell->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + } + cell2++; + } + cell2 += MAP_CELL_W - bridge_w; + } + Shake_The_Screen(3); + + destroyed = true; + } else { + /* + ** All this code is for the multi-part bridges. + */ + if (ttype >= TEMPLATE_BRIDGE_1A && ttype <= TEMPLATE_BRIDGE_3E) { + int icon = cellptr->TIcon; + int w = TemplateTypeClass::As_Reference(ttype).Width; + int h = TemplateTypeClass::As_Reference(ttype).Height; + + cell -= icon % w; + cell -= MAP_CELL_W * (icon / w); + switch (ttype) { + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + case TEMPLATE_BRIDGE_2A: + case TEMPLATE_BRIDGE_2B: + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + ttype++; + Add_Cell_Update(cell_updates, update_count, ttype, cell); + break; + } + + /* + ** If we were a middle piece that just got blown up, update the + ** adjoining pieces to make sure they're shaped properly. + */ + if (ttype == TEMPLATE_BRIDGE_3C) { + // check the template below us, at x-1, y+1 + CELL cell2 = cell + (MAP_CELL_W - 1); + CellClass * celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + // It was also destroyed. Update us and it. + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell); + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); + } + + // Now check the template above us, at x+1, y-1. + cell2 = cell - (MAP_CELL_W - 1); + celptr = &(*this)[cell2]; + if (celptr->TType == TEMPLATE_BRIDGE_3C) { + if (cellptr->TType == TEMPLATE_BRIDGE_3D) { + // if we're already one-sided, turn us to all water + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell); + } else { + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell); + } + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); + } + Map.Zone_Reset(MZONEF_ALL); + } + + /* + ** If we're an end bridge piece, update the adjoining piece to + ** be the proper shape. + */ + if (cellptr->TType == TEMPLATE_BRIDGE_1C) { + Scen.BridgeCount--; + Scen.IsBridgeChanged = true; + + // Point to the template below us, x-1, y+2 + CELL cell2 = cell + (MAP_CELL_W * 2) - 1; + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3E, cell2); + break; + case TEMPLATE_BRIDGE_3D: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); + break; + } + } else { + if (cellptr->TType == TEMPLATE_BRIDGE_2C) { + // Point to the template above us, x+2, y-1 + CELL cell2 = cell - (MAP_CELL_W - 2); + switch ((*this)[cell2].TType) { + case TEMPLATE_BRIDGE_3A: + case TEMPLATE_BRIDGE_3B: + case TEMPLATE_BRIDGE_3C: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3D, cell2); + break; + case TEMPLATE_BRIDGE_3E: + Add_Cell_Update(cell_updates, update_count, TEMPLATE_BRIDGE_3F, cell2); + break; + } + } + } + if (cellptr->TType == TEMPLATE_BRIDGE_1C || + cellptr->TType == TEMPLATE_BRIDGE_2C || + (cellptr->TType >= TEMPLATE_BRIDGE_3C && cellptr->TType <= TEMPLATE_BRIDGE_3E)) { + int x, y, tdata = 0; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + CellClass * ptr = &(*this)[(CELL)(cell + x)]; + if (ptr->TType == cellptr->TType || ptr->Land_Type() == LAND_RIVER || ptr->Land_Type() == LAND_WATER) { + Detach_This_From_All(As_Target((CELL)(cell+tdata)), true); + + ObjectClass * obj = ptr->Cell_Occupier(); + while (obj != NULL) { + ObjectClass * next = obj->Next; + if (obj->Is_Techno()) { + int damage = obj->Strength; + obj->Take_Damage(damage, 0, WARHEAD_HE, NULL, true); + } + obj = next; + } + + } + tdata++; + } + cell += MAP_CELL_W; + } + Map.Zone_Reset(MZONEF_ALL); + destroyed = true; + } + Shake_The_Screen(3); + } + } + + int cell_index = 0; + char cell_name[_MAX_PATH] = { 0 }; + char icon_number[32] = { 0 }; + int icon = 0; + void *image_data = 0; + for (int i = 0; i < update_count; i++) { + const TemplateTypeClass* type = cell_updates[i].Type; + CELL cell = cell_updates[i].Cell; + for (int y = 0; y < type->Height; y++) { + for (int x = 0; x < type->Width; x++) { + CELL newcell = cell + y * MAP_CELL_W + x; + if (Map.In_Radar(newcell)) { + CellClass * newcellptr = &Map[newcell]; + if (newcellptr->Get_Template_Info(cell_name, icon, image_data)) { + itoa(icon, icon_number, 10); + strncat(cell_name, "_i", 32); + strncat(cell_name, icon_number, 32); + strncat(cell_name, ".tga", 32); + On_Update_Map_Cell(Cell_X(newcell), Cell_Y(newcell), cell_name); + } + } + } + } + } + } + return(destroyed); +} + + +/*********************************************************************************************** + * MapClass::Detach -- Remove specified object from map references. * + * * + * This routine will take the object (represented by a target value) and remove all * + * references to it from the map. Typically, this is used to remove trigger reference. * + * * + * INPUT: target -- The target object to remove from the map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +void MapClass::Detach(TARGET target, bool) +{ + /* + ** Remove this trigger from the map zone/line tracking list. + */ + if (Is_Target_Trigger(target)) { + for (int index = 0; index < MapTriggers.Count(); index++) { + if (MapTriggers[index] == As_Trigger(target)) { + MapTriggers.Delete(index); + break; + } + } + + /* + ** Loop through all cells; remove any reference to this trigger + */ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if ((*this)[cell].Trigger == As_Trigger(target)) { + (*this)[cell].Trigger = NULL; + } + } + } +} + + +/*********************************************************************************************** + * MapClass::Intact_Bridge_Count -- Determine the number of intact bridges. * + * * + * This will examine the entire map and return the number of bridges that are intact. An * + * intact bridge is one that units can travel over. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of intact bridges on the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1996 JLB : Created. * + *=============================================================================================*/ +int MapClass::Intact_Bridge_Count(void) const +{ + /* + ** Count all non-destroyed bridges on the map. + */ + int count = 0; + CellClass const * cellptr = &(*this)[(CELL)0]; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + switch (cellptr->TType) { + case TEMPLATE_BRIDGE1: + case TEMPLATE_BRIDGE1H: + case TEMPLATE_BRIDGE2: + case TEMPLATE_BRIDGE2H: + case TEMPLATE_BRIDGE_1A: + case TEMPLATE_BRIDGE_1B: + if (cellptr->TIcon == 6) { + count++; + } + break; + + default: + break; + } + + cellptr++; + } + + return(count); +} + + +/*********************************************************************************************** + * MapClass::Pick_Random_Location -- Picks a random location on the map. * + * * + * This routine will pick a random location on the map. It performs no legality checking * + * other than forcing the cell to be on the map proper. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/25/1996 JLB : Created. * + *=============================================================================================*/ +CELL MapClass::Pick_Random_Location(void) const +{ + int x = Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1); + int y = Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1); + + return(XY_Cell(x, y)); +} + + +#if (1) + +/*********************************************************************************************** + * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * + * * + * INPUT: House to shroud * + * * + * OUTPUT: None + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 BWG : Created. * + * 08/12/2019 ST : Updated for client/server multiplayer * + *=============================================================================================*/ +void MapClass::Shroud_The_Map(HouseClass *house) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->Is_Mapped(house) || cellptr->Is_Visible(house)) { + + cellptr->Redraw_Objects(); + + /* + ** BG: remove "ring of darkness" around edge of map. + */ + int x = Cell_X(cell); + int y = Cell_Y(cell); + if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && + y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { + cellptr->Set_Mapped(house, false); + cellptr->Set_Visible(house, false); + } + } + } + for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { + ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; + if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == house) { + layer_object->Look(); + } + } + Flag_To_Redraw(true); +} + + +#else + +// +// Old code for posterity. ST - 8/12/2019 11:34AM +// + +/*********************************************************************************************** + * MapClass::Shroud_The_Map -- cover the whole map in darkness (usually from blackout crate) * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a cell that is within the map. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 BWG : Created. * + *=============================================================================================*/ +void MapClass::Shroud_The_Map(void) +{ + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + CellClass * cellptr = &Map[cell]; + if (cellptr->IsMapped || cellptr->IsVisible) { + cellptr->Redraw_Objects(); + /* + ** BG: remove "ring of darkness" around edge of map. + */ + int x = Cell_X(cell); + int y = Cell_Y(cell); + if (x >= Map.MapCellX && x < (Map.MapCellX + Map.MapCellWidth) && + y >= Map.MapCellY && y < (Map.MapCellY + Map.MapCellHeight)) { + cellptr->IsMapped = false; + cellptr->IsVisible = false; + } + } + } + for (int obj_index = 0; obj_index < DisplayClass::Layer[LAYER_GROUND].Count(); obj_index++) { + ObjectClass * layer_object = DisplayClass::Layer[LAYER_GROUND][obj_index]; + if (layer_object && layer_object->Is_Techno() && ((TechnoClass *)layer_object)->House == PlayerPtr) { + layer_object->Look(); + } + } + Flag_To_Redraw(true); +} +#endif \ No newline at end of file diff --git a/code/redalert/map.h b/code/redalert/map.h new file mode 100644 index 0000000..5820187 --- /dev/null +++ b/code/redalert/map.h @@ -0,0 +1,183 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAP_H +#define MAP_H + +#include "gscreen.h" +#include "crate.h" + +class MapClass: public GScreenClass +{ + public: + + MapClass(void) {}; + MapClass(NoInitClass const & x) : GScreenClass(x), Array(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // Theater-specific inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Alloc_Cells(void); // Allocates buffers + virtual void Free_Cells(void); // Frees buffers + virtual void Init_Cells(void); // Frees buffers + + /*-------------------------------------------------------- + ** Main functions that deal with groupings of cells within the map or deals with the cell + ** as it relates to the map - not what the cell contains. + */ + CELL Pick_Random_Location(void) const; + int Intact_Bridge_Count(void) const; + bool Base_Region(CELL cell, HousesType & house, ZoneType & zone) const; + CELL Nearby_Location(CELL cell, SpeedType speed, int zone=-1, MZoneType check=MZONE_NORMAL, bool checkflagged=false) const; + ObjectClass * Close_Object(COORDINATE coord) const; + virtual void Detach(ObjectClass * ) {}; + int Cell_Region(CELL cell); + int Cell_Threat(CELL cell, HousesType house); + bool In_Radar(CELL cell) const; + void Sight_From(CELL cell, int sightrange, HouseClass *house, bool incremental=false); + void Jam_From(CELL cell, int jamrange, HouseClass *house); + void Shroud_From(CELL cell, int sightrange, HouseClass *house); + void UnJam_From(CELL cell, int jamrange, HouseClass *house); + void Place_Down(CELL cell, ObjectClass * object); + void Pick_Up(CELL cell, ObjectClass * object); + void Overlap_Down(CELL cell, ObjectClass * object); + void Overlap_Up(CELL cell, ObjectClass * object); + bool Read_Binary(Straw & straw); + int Write_Binary(Pipe & pipe); + bool Place_Random_Crate(void); + bool Remove_Crate(CELL cell); + bool Zone_Reset(int method); + bool Zone_Cell(CELL cell, int zone); + int Zone_Span(CELL cell, int zone, MZoneType check); + bool Destroy_Bridge_At(CELL cell); + void Detach(TARGET target, bool all=true); + void Shroud_The_Map(HouseClass *house); + + long Overpass(void); + + virtual void Logic(void); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Debug routine + */ + int Validate(void); + + /* + ** This is the dimensions and position of the sub section of the global map. + ** It is this region that appears on the radar map and constrains normal + ** movement. + */ + int MapCellX; + int MapCellY; + int MapCellWidth; + int MapCellHeight; + + /* + ** This is the total value of all harvestable Tiberium on the map. + */ + long TotalValue; + + CellClass & operator [] (COORDINATE coord) {return(Array[Coord_Cell(coord)]);}; + CellClass & operator [] (CELL cell) {return(Array[cell]);}; + CellClass const & operator [] (COORDINATE coord) const {return(Array[Coord_Cell(coord)]);}; + CellClass const & operator [] (CELL cell) const {return(Array[cell]);}; + int ID(CellClass const * ptr) {return(Array.ID(ptr));}; + int ID(CellClass const & ptr) {return(Array.ID(ptr));}; + + protected: + + /* + ** This is the array of cell objects. + */ + VectorClass Array; + + /* + ** These are the size dimensions of the underlying array of cell objects. + ** This is the dimensions of the "map" that the tactical view is + ** restricted to. + */ + int XSize; + int YSize; + int Size; + + static int const RadiusCount[11]; + static int const RadiusOffset[]; + + /* + ** This specifies the information for the various crates in the game. + */ + CrateClass Crates[256]; + + private: + friend class CellClass; + + /* + ** Tiberium growth potential cells are recorded here. + */ + CELL TiberiumGrowth[MAP_CELL_W/2]; + int TiberiumGrowthCount; + int TiberiumGrowthExcess; + + /* + ** List of cells that are full enough strength that they could spread + ** Tiberium to adjacent cells. + */ + CELL TiberiumSpread[MAP_CELL_W/2]; + int TiberiumSpreadCount; + int TiberiumSpreadExcess; + + /* + ** This is the current cell number in the incremental map scan process. + */ + CELL TiberiumScan; + + enum MapEnum {SCAN_AMOUNT=MAP_CELL_TOTAL}; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[1024]; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/mapeddlg.cpp b/code/redalert/mapeddlg.cpp new file mode 100644 index 0000000..050a12c --- /dev/null +++ b/code/redalert/mapeddlg.cpp @@ -0,0 +1,2620 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : September 4, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Map Editor dialogs & main menu options * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Handle_Triggers -- processes the trigger dialogs * + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * MapEditClass::New_Scenario -- creates a new scenario * + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * MapEditClass::Select_Trigger -- lets user select a trigger * + * MapEditClass::Size_Map -- lets user set size & location of map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::New_Scenario -- creates a new scenario * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Prompts user for map size * + * - Initializes the scenario by calling Clear_Scenario(), which calls * + * everybody's Init() routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = new scenario created, -1 = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::New_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + HousesType house; + + /* + ** Force the house save value to match the player house. + */ + if (PlayerPtr) { + switch (PlayerPtr->Class->House) { + case HOUSE_SPAIN: + player = SCEN_PLAYER_SPAIN; + break; + + case HOUSE_GREECE: + player = SCEN_PLAYER_GREECE; + break; + + default: + case HOUSE_USSR: + player = SCEN_PLAYER_USSR; + break; + } + } + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("New Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + ScenarioInit++; + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Create houses + */ + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + new HouseClass(house); + } + + switch (player) { + case SCEN_PLAYER_MPLAYER: + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI1); + PlayerPtr->IsHuman = true; + LastHouse = HOUSE_MULTI1; + break; + + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Init the entire map + */ +// Init_Clear(); + Fill_In_Data(); + + /* + ** Prompt for map size + */ + Size_Map(-1, -1, 30, 30); + + /* + ** Set the Home & Reinforcement Cells to the center of the map + */ + Scen.Waypoint[WAYPT_REINF] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth / 2, MapCellY + MapCellHeight / 2); + (*this)[TacticalCoord].IsWaypoint = 1; + Flag_Cell(Coord_Cell(TacticalCoord)); + + Set_Tactical_Position(Cell_Coord(Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR) - (5 * RESFACTOR))); + ScenarioInit--; + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Load_Scenario -- loads a scenario INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Loads the INI file for that scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Load_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; + NodeNameType * who; // node to add to Players + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Load Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Read_Scenario_Ini() must be able to set PlayerPtr to the right house: + ** - Reading the INI will create the house objects + ** - PlayerPtr must be set before any Techno objects are created + ** - For GDI or NOD scenarios, PlayerPtr is set by reading the INI; + ** but for multiplayer, it's set via the Players vector; so, here we have + ** to set various multiplayer variables to fool the Assign_Houses() routine + ** into working properly. + */ + if (player == SCEN_PLAYER_MPLAYER) { + Clear_Vector(&Session.Players); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Session.NumPlayers = 1; + LastHouse = HOUSE_MULTI1; + } else { +#ifdef NEVER + if (ScenPlayer==SCEN_PLAYER_JP) { + PlayerPtr = HouseClass::As_Pointer(HOUSE_MULTI4); + PlayerPtr->IsHuman = true; + Base.House = HOUSE_MULTI4; + } else { +#endif + LastHouse = HOUSE_GOOD; + } + + /* + ** Blow away everything + */ + Clear_Scenario(); + + /* + ** Read the INI + */ + if (Read_Scenario_INI(Scen.ScenarioName) == 0) { + if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'G'){ + WWMessageBox().Process("Please insert Red Alert CD1"); + }else if(Scen.Scenario < 20 && Scen.ScenarioName[2] == 'U') + WWMessageBox().Process("Please insert Red Alert CD2"); + else + WWMessageBox().Process("Unable to read scenario!"); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + Fill_In_Data(); + GamePalette.Set(); +// Set_Palette(GamePalette); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Save_Scenario -- saves current scenario to an INI file * + * * + * - Prompts user for scenario data (house, scenario #); sets globals * + * PlayerPtr (for house) & Scenario (for scenario #) * + * - Saves the INI file for this scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = error/cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Save_Scenario(void) +{ + int scen_num; + ScenarioPlayerType player; + ScenarioDirType dir; + ScenarioVarType var; + + Disect_Scenario_Name(Scen.ScenarioName, scen_num, player, dir, var); + + int rc; +// FILE * fp; +// char fname[13]; + + /* + ** Prompt for scenario info + */ + rc = Pick_Scenario("Save Scenario", scen_num, player, dir, var); + if (rc != 0) { + return(-1); + } + + /* + ** Warning if scenario already exists + */ +// Scen.Set_Scenario_Name(scen_num, player, dir, var); +// fp = fopen(fname, "rb"); +// if (fp) { +// fclose(fp); +// rc = WWMessageBox().Process("File exists. Replace?", TXT_YES, TXT_NO); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// Render(); +// if (rc==1) { +// return(-1); +// } +// } + + /* + ** Set parameters + */ +// Scen.Scenario = scen_num; +// Scen.ScenPlayer = player; +// Scen.ScenDir = dir; +// Scen.ScenVar = var; + Scen.Set_Scenario_Name(scen_num, player, dir, var); + + /* + ** Player may have changed from GDI to NOD, so change playerptr accordingly + */ + switch (player) { + case SCEN_PLAYER_USSR: + PlayerPtr = HouseClass::As_Pointer(HOUSE_USSR); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_SPAIN; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_SPAIN: + PlayerPtr = HouseClass::As_Pointer(HOUSE_SPAIN); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + + case SCEN_PLAYER_GREECE: + PlayerPtr = HouseClass::As_Pointer(HOUSE_GREECE); + PlayerPtr->IsHuman = true; +// Base.House = HOUSE_USSR; + LastHouse = HOUSE_GOOD; + break; + } + + /* + ** Write the INI + */ + Write_Scenario_INI(Scen.ScenarioName); + + return(0); +} + +bool pickscenerio_faction[4] = { false, false, false, false }; +bool pickscenerio_sides[2] = { false, false }; +bool pickscenerio_version[4] = { false, false, false, false }; +bool pickscenerio_process = false; +bool pickscenerio_cancel = false; + +struct PickScenerioResults { + const char* caption; + char* scene_buf; + int *scen_nump; + ScenarioPlayerType *playerp; + ScenarioDirType *dirp; + ScenarioVarType *varp; +}; + +PickScenerioResults pickScenerioResults; + +/*************************************************************************** + * MapEditClass::Pick_Scenario -- dialog for choosing scenario * + * * + * Prompts user for: * + * - House (GDI, NOD) * + * - Scenario # * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Caption ³ * + * ³ ³ * + * ³ Scenario ___ ³ * + * ³ Version ___ ³ * + * ³ ³ * + * ³ [East] [West] ³ * + * ³ ³ * + * ³ [ GDI ] ³ * + * ³ [ NOD ] ³ * + * ³ [Multi-Player] ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * caption string to use as a title * + * scen_nump output: ptr to scenario # * + * playerp output: ptr to player type * + * dirp output: ptr to direction * + * varp output: ptr to variation * + * multi 1 = allow to change single/multiplayer; 0 = not * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 09/04/1996 JLB : Simplified * + *=========================================================================*/ +void Imgui_PickScenerio_Dialog(void) { + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 200, // dialog width + D_DIALOG_H = 290, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = 45, // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_SCEN_W = 45, // Scenario # width + D_SCEN_H = 9, // Scenario # height + D_SCEN_X = D_DIALOG_CX + 5, // Scenario # x + D_SCEN_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, // Scenario # y + + D_VARA_W = 13, // Version A width + D_VARA_H = 9, // Version A height + D_VARA_X = D_DIALOG_CX - (D_VARA_W * 5) / 2, // Version A x + D_VARA_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version A y + + D_VARB_W = 13, // Version B width + D_VARB_H = 9, // Version B height + D_VARB_X = D_VARA_X + D_VARA_W, // Version B x + D_VARB_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version B y + + D_VARC_W = 13, // Version C width + D_VARC_H = 9, // Version C height + D_VARC_X = D_VARB_X + D_VARB_W, // Version C x + D_VARC_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version C y + + D_VARD_W = 13, // Version D width + D_VARD_H = 9, // Version D height + D_VARD_X = D_VARC_X + D_VARC_W, // Version D x + D_VARD_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version D y + + D_VARLOSE_W = 13, // Version Lose width + D_VARLOSE_H = 9, // Version Lose height + D_VARLOSE_X = D_VARD_X + D_VARD_W, // Version Lose x + D_VARLOSE_Y = D_SCEN_Y + D_SCEN_H + D_MARGIN, // Version Lose y + + D_EAST_W = 50, // EAST width + D_EAST_H = 9, // EAST height + D_EAST_X = D_DIALOG_CX - D_EAST_W - 5, // EAST x + D_EAST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_WEST_W = 50, // WEST width + D_WEST_H = 9, // WEST height + D_WEST_X = D_DIALOG_CX + 5, // WEST x + D_WEST_Y = D_VARLOSE_Y + D_VARLOSE_H + D_MARGIN,// EAST y + + D_GDI_W = 90, // GDI width + D_GDI_H = 9, // GDI height + D_GDI_X = D_DIALOG_CX - (D_GDI_W / 2), // GDI x + D_GDI_Y = D_EAST_Y + D_EAST_H + D_MARGIN, // GDI y + + D_NOD_W = 90, // NOD width + D_NOD_H = 9, // NOD height + D_NOD_X = D_DIALOG_CX - (D_NOD_W / 2), // NOD x + D_NOD_Y = D_GDI_Y + D_GDI_H, // NOD y + + D_NEU_W = 90, // Neutral width + D_NEU_H = 9, // Neutral height + D_NEU_X = D_DIALOG_CX - (D_NOD_W / 2), // Neutral x + D_NEU_Y = D_NOD_Y + D_NOD_H, // Neutral y + + D_MPLAYER_W = 90, // Multi-Player width + D_MPLAYER_H = 9, // Multi-Player height + D_MPLAYER_X = D_DIALOG_CX - (D_MPLAYER_W / 2), // Multi-Player x + D_MPLAYER_Y = D_NEU_Y + D_NEU_H, // Multi-Player y + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_CX - D_OK_W - 5, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN + 15), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_CX + 5, // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN + 15), // Cancel y + + }; + + bool toolActive = false; + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(D_DIALOG_W, D_DIALOG_H)); + ImGui::SetNextWindowPos(ImVec2(D_DIALOG_X * 2, D_DIALOG_Y)); + ImGui::Begin(pickScenerioResults.caption, &toolActive, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Text("Scenario"); + + ImGui::InputText("Scenario", &pickScenerioResults.scene_buf[0], 10); + + //ImGui::Text("Version"); + // + //static char verionsId[512]; + //ImGui::SameLine(); + //ImGui::InputText("Version", &verionsId[0], sizeof(verionsId)); + ImGui::Text("Version"); + { + if (ImGui::Checkbox("A", &pickscenerio_version[0])) { + memset(pickscenerio_version, 0, sizeof(pickscenerio_version)); + pickscenerio_version[0] = true; + *pickScenerioResults.varp = SCEN_VAR_A; + } + ImGui::SameLine(); + if (ImGui::Checkbox("B", &pickscenerio_version[1])) { + memset(pickscenerio_version, 0, sizeof(pickscenerio_version)); + pickscenerio_version[1] = true; + *pickScenerioResults.varp = SCEN_VAR_B; + } + ImGui::SameLine(); + if (ImGui::Checkbox("C", &pickscenerio_version[2])) { + memset(pickscenerio_version, 0, sizeof(pickscenerio_version)); + pickscenerio_version[2] = true; + *pickScenerioResults.varp = SCEN_VAR_C; + } + ImGui::SameLine(); + if (ImGui::Checkbox("D", &pickscenerio_version[3])) { + memset(pickscenerio_version, 0, sizeof(pickscenerio_version)); + pickscenerio_version[3] = true; + *pickScenerioResults.varp = SCEN_VAR_D; + } + } + ImGui::Text("Side"); + { + if (ImGui::Checkbox("East", &pickscenerio_sides[0])) { + *pickScenerioResults.dirp = SCEN_DIR_EAST; + memset(pickscenerio_sides, 0, sizeof(pickscenerio_sides)); + pickscenerio_sides[0] = true; + } + ImGui::SameLine(); + if (ImGui::Checkbox("West", &pickscenerio_sides[1])) { + *pickScenerioResults.dirp = SCEN_DIR_WEST; + memset(pickscenerio_sides, 0, sizeof(pickscenerio_sides)); + pickscenerio_sides[1] = true; + } + } + + ImGui::Text("Faction"); + + + if (ImGui::Checkbox("North (Spain)", &pickscenerio_faction[0])) { + *pickScenerioResults.playerp = SCEN_PLAYER_SPAIN; + memset(pickscenerio_faction, 0, sizeof(pickscenerio_faction)); + pickscenerio_faction[0] = true; + } + if (ImGui::Checkbox("South(Greece)", &pickscenerio_faction[1])) { + *pickScenerioResults.playerp = SCEN_PLAYER_GREECE; + memset(pickscenerio_faction, 0, sizeof(pickscenerio_faction)); + pickscenerio_faction[1] = true; + } + if (ImGui::Checkbox("USSR", &pickscenerio_faction[2])) { + *pickScenerioResults.playerp = SCEN_PLAYER_USSR; + memset(pickscenerio_faction, 0, sizeof(pickscenerio_faction)); + pickscenerio_faction[2] = true; + } + if (ImGui::Checkbox("Multiplayer", &pickscenerio_faction[3])) { + *pickScenerioResults.playerp = SCEN_PLAYER_MPLAYER; + memset(pickscenerio_faction, 0, sizeof(pickscenerio_faction)); + pickscenerio_faction[3] = true; + } + ImGui::NewLine(); + + { + if (ImGui::Button("Ok")) { + pickscenerio_cancel = false; + pickscenerio_process = false; + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + pickscenerio_cancel = true; + pickscenerio_process = false; + } + } + + ImGui::End(); +} + +int MapEditClass::Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp) +{ + Imgui_Dialog_Function = Imgui_PickScenerio_Dialog; + pickscenerio_process = true; + pickscenerio_cancel = false; + + char scen_buf[10] = { 0 }; // buffer for editing scenario + + pickScenerioResults.caption = caption; + pickScenerioResults.scene_buf = &scen_buf[0]; + pickScenerioResults.scen_nump = &scen_nump; + pickScenerioResults.playerp = &playerp; + pickScenerioResults.dirp = &dirp; + pickScenerioResults.varp = &varp; + + /* + ** Main Processing Loop + */ + bool display = true; + while (pickscenerio_process) { + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + } + + Imgui_Dialog_Function = nullptr; + + bool cancel = pickscenerio_cancel; + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (cancel) { + return(-1); + } + + /* + ** Save selections & return + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (scen_buf[0] <= '9' && scen_buf[1] <= '9') { + scen_nump = atoi(scen_buf); + } else { + char first = scen_buf[0]; + char second = scen_buf[1]; + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scen_nump = (first * 36) + second; +//Mono_Printf("Converted to: %d, %d = %d\n",first, second, scen_nump);Keyboard->Get();Keyboard->Get(); + } +#else + scen_nump = atoi(scen_buf); +#endif + + return(0); +} + +static int map_x1; // map coords x1, pixel coords +static int map_x2; // map coords x2, pixel coords +static int map_y1; // map coords y1, pixel coords +static int map_y2; // map coords y2, pixel coords +bool sizemap_cancel = false; // true = user cancels +bool sizemap_process = true; +void Imgui_SizeMap(void) { + bool toolActive = false; + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(500, 100)); + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::Begin("Size Map", &toolActive, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Columns(4, "outer", true); + ImGui::Text("X"); + ImGui::PushID("map_x"); + ImGui::InputInt("", &map_x1); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Y"); + ImGui::PushID("map_y"); + ImGui::InputInt("", &map_y1); + ImGui::PopID(); + if (ImGui::Button("Ok")) { + sizemap_cancel = false; + sizemap_process = false; + } + ImGui::NextColumn(); + ImGui::Text("X2"); + ImGui::PushID("map_x2"); + ImGui::InputInt("", &map_x2); + ImGui::PopID(); + if (ImGui::Button("Cancel")) { + sizemap_cancel = true; + sizemap_process = false; + } + ImGui::NextColumn(); + ImGui::Text("Y2"); + ImGui::PushID("map_y2"); + ImGui::InputInt("", &map_y2); + ImGui::PopID(); + ImGui::Columns(1); + ImGui::End(); +} + +/*************************************************************************** + * MapEditClass::Size_Map -- lets user set size & location of map * + * * + * Lets the user select a side of the map and expand/shrink it to the * + * desired size, or move the whole map around the available map area. * + * * + * The entire available map area is displayed, but the map is limited such * + * that there's always one blank cell around the map; this lets objects * + * properly exit the screen, since they have a blank undisplayed cell to * + * exit onto. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ Clear Terrain ³ * + * ³ ³ ³ Water ³ * + * ³ ³ ³ Tiberium ³ * + * ³ ³ ³ Rock/Wall/Road ³ * + * ³ ³ (Map Area) ³ GDI Unit ³ * + * ³ ³ ³ NOD Unit ³ * + * ³ ³ ³ Neutral Unit ³ * + * ³ ³ ³ Terrain Object ³ * + * ³ ³ ³ Starting Cell ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ X Y Width Height ³ * + * ³ ## ## ## ## ³ * + * ³ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * x,y,w,h: initial size parameters (-1 = center the thing) * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Size_Map(int x, int y, int w, int h) +{ + Imgui_Dialog_Function = Imgui_SizeMap; + sizemap_cancel = false; // true = user cancels + sizemap_process = true; + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 350, // dialog width + D_DIALOG_H = 225, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_BORD_X1 = D_DIALOG_X + 45, +// D_BORD_X1 = D_DIALOG_X + (D_DIALOG_W / 2 - MAP_CELL_W) / 2, + D_BORD_Y1 = D_DIALOG_Y + 25, + D_BORD_X2 = D_BORD_X1 + MAP_CELL_W + 1, + D_BORD_Y2 = D_BORD_Y1 + MAP_CELL_H + 1, + + D_OK_W = 45, // OK width + D_OK_H = 9, // OK height + D_OK_X = D_DIALOG_X + 45, // OK x + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - (D_MARGIN + 10), // OK y + + D_CANCEL_W = 45, // Cancel width + D_CANCEL_H = 9, // Cancel height + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (35 + D_CANCEL_W), // Cancel x + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - (D_MARGIN + 10), // Cancel y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_OK=100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_MAP, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map board, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables: + */ + RedrawType display; // requested redraw level + KeyNumType input; // user input + int grabbed = 0; // 1=TLeft,2=TRight,3=BRight,4=BLeft + int delta1, delta2; // mouse-click proximity + int mx,my; // last-saved mouse coords +// char txt[40]; + int txt_x,txt_y; // for displaying text +// unsigned index; // for drawing map symbology + CELL cell; // for drawing map symbology + int color; // for drawing map symbology + ObjectClass * occupier; // cell's occupier + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass * commands = NULL; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Set up the actual map area relative to the map's border coords + */ + if (x==-1) { + map_x1 = D_BORD_X1 + (MAP_CELL_W - w) / 2 + 1; + } else { + map_x1 = D_BORD_X1 + x + 1; + } + + if (y==-1) { + map_y1 = D_BORD_Y1 + (MAP_CELL_H - h) / 2 + 1; + } else { + map_y1 = D_BORD_Y1 + y + 1; + } + + map_x2 = map_x1 + w - 1; + map_y2 = map_y1 + h - 1; + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main processing loop + */ + display = REDRAW_ALL; + + while (sizemap_process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ +// if (display) { +// Hide_Mouse(); +// +// /* +// ** Redraw the background, map border, key, and coord labels +// */ +// if (display >= REDRAW_BACKGROUND) { +// +// /* +// ** Background +// */ +// Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); +// Draw_Caption(TXT_SIZE_MAP, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); +// +// /* +// ** Draw the map border +// */ +// if (LogicPage->Lock()) { +// LogicPage->Draw_Rect(D_BORD_X1, D_BORD_Y1, D_BORD_X2, D_BORD_Y2, scheme->Shadow); +//// for (index = D_BORD_X1; index < D_BORD_X2; +//// index += (320/ICON_PIXEL_W)) { +//// LogicPage->Put_Pixel(index, D_BORD_Y1-1, scheme->Shadow); +//// LogicPage->Put_Pixel(index, D_BORD_Y2+1, scheme->Shadow); +//// } +//// for (index = D_BORD_Y1; index < D_BORD_Y2-8; +//// index += (200/ICON_PIXEL_H)) { +//// LogicPage->Put_Pixel(D_BORD_X1-1, index, scheme->Shadow); +//// LogicPage->Put_Pixel(D_BORD_X2+1, index, scheme->Shadow); +//// } +// +// /* +// ** Draw the map "key" +// */ +// txt_x = D_BORD_X2 + 15; +// txt_y = D_BORD_Y1; +// Plain_Text_Print("Clear Terrain", txt_x, txt_y, GroundColor[LAND_CLEAR], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Water", txt_x, txt_y, GroundColor[LAND_WATER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Tiberium", txt_x, txt_y, GroundColor[LAND_TIBERIUM], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Rock", txt_x, txt_y, GroundColor[LAND_ROCK], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Wall", txt_x, txt_y, GroundColor[LAND_WALL], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Beach", txt_x, txt_y, GroundColor[LAND_BEACH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Rough", txt_x, txt_y, GroundColor[LAND_ROUGH], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("River", txt_x, txt_y, GroundColor[LAND_RIVER], TBLACK, TPF_DROPSHADOW | TPF_EFNT); +//// txt_y += 8; +//// Plain_Text_Print("GDI Unit", txt_x, txt_y, YELLOW, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +//// txt_y += 8; +//// Plain_Text_Print("Nod Unit", txt_x, txt_y, RED, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +//// txt_y += 8; +//// Plain_Text_Print("Neutral Unit", txt_x, txt_y, PURPLE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Terrain Object", txt_x, txt_y, DKGREEN, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// txt_y += 8; +// Plain_Text_Print("Starting Cell", txt_x, txt_y, WHITE, TBLACK, TPF_DROPSHADOW | TPF_EFNT); +// +// /* +// ** Draw the coordinate labels +// */ +// txt_x = D_DIALOG_X + D_DIALOG_W / 8; +// txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 43; +// Fancy_Text_Print(" X", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print(" Y", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print(" Width", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print(" Height", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); +// +// LogicPage->Unlock(); +// } +// +// /* +// ** Redraw the buttons +// */ +// commands->Flag_List_To_Redraw(); +// } +// +// /* +// ** Redraw the map symbology & location +// */ +// if (display >= REDRAW_MAP) { +// +// if (LogicPage->Lock()) { +// +// /* +// ** Erase the map interior +// */ +// LogicPage->Fill_Rect(D_BORD_X1 + 1, D_BORD_Y1 + 1, D_BORD_X2 - 1, D_BORD_Y2 - 1, BLACK); +// +// /* +// ** Draw Land map symbols (use color according to Ground[] array). +// */ +// for (cell=0; cell < MAP_CELL_TOTAL; cell++) { +// occupier = (*this)[cell].Cell_Occupier(); +// if (occupier == NULL) { +// color = GroundColor[(*this)[cell].Land_Type()]; +// LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); +// } +// } +// +// /* +// ** Draw the actual map location +// */ +// LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, WHITE); +// switch (grabbed) { +// case 1: +// LogicPage->Draw_Line(map_x1, map_y1, map_x1 + 5, map_y1, BLUE); +// LogicPage->Draw_Line(map_x1, map_y1, map_x1, map_y1 + 5, BLUE); +// break; +// +// case 2: +// LogicPage->Draw_Line(map_x2, map_y1, map_x2 - 5, map_y1, BLUE); +// LogicPage->Draw_Line(map_x2, map_y1, map_x2, map_y1 + 5, BLUE); +// break; +// +// case 3: +// LogicPage->Draw_Line(map_x2, map_y2, map_x2 - 5, map_y2, BLUE); +// LogicPage->Draw_Line(map_x2, map_y2, map_x2, map_y2 - 5, BLUE); +// break; +// +// case 4: +// LogicPage->Draw_Line(map_x1, map_y2, map_x1 + 5, map_y2, BLUE); +// LogicPage->Draw_Line(map_x1, map_y2, map_x1, map_y2 - 5, BLUE); +// break; +// +// case 5: +// LogicPage->Draw_Rect(map_x1, map_y1, map_x2, map_y2, BLUE); +// break; +// +// default: +// break; +// } +// +// /* +// ** Draw Unit map symbols (Use the radar map color according to +// ** that specified in the house type class object. +// ** DKGREEN = terrain object +// */ +// for (cell=0; cell < MAP_CELL_TOTAL; cell++) { +// occupier = (*this)[cell].Cell_Occupier(); +// if (occupier) { +// color = DKGREEN; +// if (occupier && occupier->Owner() != HOUSE_NONE) { +// color = ColorRemaps[HouseClass::As_Pointer(occupier->Owner())->RemapColor].Color; +// } +// LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(cell) + 1, D_BORD_Y1 + Cell_Y(cell) + 1, color); +// } +// } +// +// /* +// ** Draw Home location +// */ +// LogicPage->Put_Pixel(D_BORD_X1 + Cell_X(Scen.Waypoint[WAYPT_HOME]) + 1, D_BORD_Y1 + Cell_Y(Scen.Waypoint[WAYPT_HOME]) + 1, WHITE); +// +// /* +// ** Erase old coordinates +// */ +//// LogicPage->Fill_Rect( D_DIALOG_X + 7, +//// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22, +//// D_DIALOG_X + D_DIALOG_W - 7, +//// D_DIALOG_Y + D_DIALOG_H - D_OK_H - 22 + 10, BLACK); +// +// /* +// ** Draw the coordinates +// */ +// txt_x = D_DIALOG_X + D_DIALOG_W / 8; +// txt_y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - 32; +// Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x1 - D_BORD_X1 - 1); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y1 - D_BORD_Y1 - 1); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_x2 - map_x1 + 1); +// +// txt_x += (D_DIALOG_W - 20) / 4; +// Fancy_Text_Print("%5d", txt_x, txt_y, GadgetClass::Get_Color_Scheme(), BLACK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, map_y2 - map_y1 + 1); +// +// LogicPage->Unlock(); +// } +// } +// +// Show_Mouse(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + // display = REDRAW_NONE; + + + /* + ** Process user input + */ + input = commands->Input(); + + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (sizemap_cancel) { + return(-1); + } + + /* + ** Save selections + */ + MapCellX = map_x1 - D_BORD_X1 - 1; + MapCellY = map_y1 - D_BORD_Y1 - 1; + MapCellWidth = map_x2 - map_x1 + 1; + MapCellHeight = map_y2 - map_y1 + 1; + + /* + ** Clip Home Cell to new map size + */ + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) < MapCellX) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_X(Scen.Waypoint[WAYPT_HOME]) > MapCellX + MapCellWidth - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(MapCellX + MapCellWidth - 1, Cell_Y(Scen.Waypoint[WAYPT_HOME])); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) < MapCellY) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY); + } + + if (Cell_Y(Scen.Waypoint[WAYPT_HOME]) > MapCellY + MapCellHeight - 1) { + Scen.Waypoint[WAYPT_HOME] = XY_Cell(Cell_X(Scen.Waypoint[WAYPT_HOME]), MapCellY + MapCellHeight - 1); + } + + return(0); +} + +std::vector scenerio_theaters; +std::vector scenerio_intros; +std::vector scenario_briefing; +std::vector scenario_action; +std::vector scenario_win; +std::vector scenario_loose; +std::vector scenario_theme; +std::vector scenario_houses; + +char description[DESCRIP_MAX] = ""; +int scenario_intromovie = 0; +bool scenerio_general_options[9] = { 0 }; + +bool scenerio_process = true; +bool scenerio_cancel = false; // true = user cancels + +HouseStaticClass hdata[HOUSE_COUNT]; +void ImGui_ScenerioDialog(void) { + bool toolActive = false; + int theater = Scen.Theater; + int briefmovie = Scen.BriefMovie + 1; + int action = Scen.ActionMovie + 1; + int win = Scen.WinMovie + 1; + int lose = Scen.LoseMovie + 1; + int theme = -1; + int base_house = Base.House; + + int house = PlayerPtr->Class->House; + HousesType newhouse = (HousesType)house; + + HouseStaticClass* hstatic = &hdata[newhouse]; + + if (Scen.TransitTheme != THEME_NONE) { + theme = Scen.TransitTheme + 1; + } + else { + theme = 0; + } + int ally = 0; + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(840, 400)); + ImGui::SetNextWindowPos(ImVec2(100, 100)); + ImGui::Begin("Scenario Editor", &toolActive, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Columns(2, "outer", true); + ImGui::Text("Theater"); + ImGui::PushID("t_text"); + ImGui::Combo("", (int *)&theater, &scenerio_theaters[0], scenerio_theaters.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Scenario Name:"); + ImGui::PushID("s_name"); + ImGui::InputText("", description, sizeof(description)); + ImGui::PopID(); + ImGui::Columns(1); + ImGui::Columns(5, "outer2", true); + ImGui::Text("Intro"); + ImGui::PushID("s_intro"); + ImGui::Combo("", (int*)&scenario_intromovie, &scenerio_intros[0], scenerio_intros.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Briefing"); + ImGui::PushID("brief_txt"); + ImGui::Combo("", (int*)&briefmovie, &scenario_briefing[0], scenario_briefing.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Action"); + ImGui::PushID("action_txt"); + ImGui::Combo("", (int*)&action, &scenario_action[0], scenario_action.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Win"); + ImGui::PushID("win_txt"); + ImGui::Combo("", (int*)&win, &scenario_win[0], scenario_win.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Lose"); + ImGui::PushID("lose_txt"); + ImGui::Combo("", (int*)&lose, &scenario_loose[0], scenario_loose.size()); + ImGui::PopID(); + ImGui::Columns(1); + ImGui::Columns(2, "outer3", true); + ImGui::Text("Prebuild base"); + ImGui::PushID("prebuild_txt"); + ImGui::Combo("", (int*)&base_house, &scenario_houses[0], scenario_houses.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Theme"); + ImGui::PushID("theme_txt"); + ImGui::Combo("", (int*)&theme, &scenario_theme[0], scenario_theme.size()); + ImGui::PopID(); + ImGui::Columns(1); + ImGui::Columns(5, "outer4", true); + ImGui::Text("Country:"); + ImGui::PushID("country_box"); + ImGui::ListBox("", (int *)&house, &scenario_houses[0], scenario_houses.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Tech Level"); + ImGui::PushID("tech_slider"); + ImGui::SliderInt("", &hstatic->TechLevel, 1, 16); + ImGui::PopID(); + ImGui::Text("Credits"); + ImGui::PushID("credits_slider"); + ImGui::SliderInt("", (int *)&hstatic->InitialCredits, 0, 20000); + ImGui::PopID(); + ImGui::Text("Unit Max"); + ImGui::PushID("unit_max_slider"); + ImGui::SliderInt("", (int*)&hstatic->MaxUnit, 0, 500); + ImGui::PopID(); + ImGui::Text("Home Edge"); + ImGui::Text("IQ"); + ImGui::NextColumn(); + ImGui::Text("Allies(broke):"); + ImGui::PushID("Allies_Control"); + ImGui::ListBox("", (int*)&ally, &scenario_houses[0], scenario_houses.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Plyr Control"); + ImGui::PushID("Plyr_Control"); + ImGui::ListBox("", (int*)&house, &scenario_houses[0], scenario_houses.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Checkbox("Inherit Stored Scenario?", &scenerio_general_options[0]); + ImGui::Checkbox("Store Scenario?", &scenerio_general_options[1]); + ImGui::Checkbox("Auto Evac. Tanya(Civilian)?", &scenerio_general_options[2]); + ImGui::Checkbox("Last Mission of Game?", &scenerio_general_options[3]); + ImGui::Checkbox("Inherit Mission Timer from last scenario?", &scenerio_general_options[4]); + ImGui::Checkbox("Disable Spyplane?", &scenerio_general_options[5]); + ImGui::Checkbox("Skip the score screen?", &scenerio_general_options[6]); + ImGui::Checkbox("No Map Selection(Force Var 'B')?", &scenerio_general_options[7]); + ImGui::Checkbox("Return to main menu after scenario finishes?", &scenerio_general_options[8]); + ImGui::Columns(1); + ImGui::NewLine(); + ImGui::NewLine(); + if (ImGui::Button("Ok")) { + scenerio_process = false; + scenerio_cancel = false; // true = user cancels + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + scenerio_process = false; + scenerio_cancel = true; // true = user cancels + } + ImGui::End(); + + Scen.Theater = (TheaterType)(theater); + Scen.BriefMovie = (VQType)(briefmovie - 1); + Scen.ActionMovie = (VQType)(action - 1); + Scen.WinMovie = (VQType)(win - 1); + Scen.LoseMovie = (VQType)(lose - 1); + Base.House = (HousesType)base_house; + PlayerPtr->Class->House = (HousesType)house; + if (Scen.TransitTheme != THEME_NONE) { + Scen.TransitTheme = (ThemeType)(theme - 1); + } +} + + +/*************************************************************************** + * MapEditClass::Scenario_Dialog -- scenario global parameters dialog * + * * + * Edits the house specific and general scenario options. * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/14/1994 BR : Created. * + * 02/13/1996 JLB : Revamped to new system. * + *=========================================================================*/ +int MapEditClass::Scenario_Dialog(void) +{ + scenerio_process = true; + scenerio_cancel = false; // true = user cancels + + HousesType house = PlayerPtr->Class->House; + HousesType newhouse = house; + + Imgui_Dialog_Function = ImGui_ScenerioDialog; + + + scenerio_theaters.clear(); + scenerio_intros.clear(); + scenario_briefing.clear(); + scenario_action.clear(); + scenario_win.clear(); + scenario_loose.clear(); + scenario_theme.clear(); + scenario_houses.clear(); + + scenerio_intros.push_back(""); + scenario_briefing.push_back(""); + scenario_action.push_back(""); + scenario_win.push_back(""); + scenario_loose.push_back(""); + scenario_theme.push_back(""); + + /* + ** Fill in the house data for each house that exists. + */ + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr) { + hdata[h] = hptr->Control; + } + } + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 320 * RESFACTOR, + D_DIALOG_H = 200 * RESFACTOR, + D_DIALOG_X = ((320 * RESFACTOR - D_DIALOG_W) / 2), + D_DIALOG_Y = ((200 * RESFACTOR - D_DIALOG_H) / 2), + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + 15 * RESFACTOR, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 15 * RESFACTOR, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+15*RESFACTOR), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - 15*RESFACTOR + }; + + /* + ** Button enumerations: + */ + enum { + LIST_THEATER=100, + BUTTON_DESCRIPTION, + BUTTON_ALLIES, + BUTTON_CONTROL, + BUTTON_SMARTIES, + BUTTON_BASE, + BUTTON_NOSPYPLANE, + BUTTON_INHERIT, + BUTTON_TIMER, + BUTTON_THEME, + BUTTON_RECORD, + BUTTON_EVAC, + BUTTON_MONEYTIB, + BUTTON_TECH, + BUTTON_TRUCKCRATE, + BUTTON_ENDOFGAME, + BUTTON_SKIPSCORE, + BUTTON_ONETIME, + BUTTON_NOMAPSEL, + BUTTON_HOUSE, + BUTTON_CREDITS, + BUTTON_SOURCE, + BUTTON_MAXUNIT, + BUTTON_INTRO, + BUTTON_BRIEFING, + BUTTON_ACTION, + BUTTON_WIN, + BUTTON_LOSE, + BUTTON_OK, + BUTTON_CANCEL, + }; + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + ControlClass * commands = NULL; // the button list + + /* + ** Theater choice drop down list. + */ + char theatertext[45] = ""; + DropListClass theaterbtn(LIST_THEATER, theatertext, sizeof(theatertext)-1, + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+30, 65, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (TheaterType t = THEATER_FIRST; t < THEATER_COUNT; t++) { + scenerio_theaters.push_back(Theaters[t].Name); + } + //theaterbtn.Set_Selected_Index(orig_theater); + + + strcpy(description, Scen.Description); + EditClass desc(BUTTON_DESCRIPTION, description, sizeof(description), TPF_EFNT|TPF_NOSHADOW, theaterbtn.X+theaterbtn.Width+15, theaterbtn.Y, 160); + + /* + ** Button that tells if this scenario should inherit buildings from the previous. + */ + CheckBoxClass inherit(BUTTON_INHERIT, theaterbtn.X+theaterbtn.Width+15+250, theaterbtn.Y); + if (Scen.IsToInherit) { + inherit.Turn_On(); + } else { + inherit.Turn_Off(); + } + + /* + ** Records scenario disposition into holding slot. + */ + CheckBoxClass record(BUTTON_RECORD, inherit.X, inherit.Y+8); + if (Scen.IsToCarryOver) { + record.Turn_On(); + } else { + record.Turn_Off(); + } + + /* + ** Should Tanya/civilian be automatically evacuated? + */ + CheckBoxClass tanya(BUTTON_EVAC, record.X, record.Y+8); + if (Scen.IsTanyaEvac) { + tanya.Turn_On(); + } else { + tanya.Turn_Off(); + } + + /* + ** End of game with with scenario? + */ + CheckBoxClass endofgame(BUTTON_ENDOFGAME, tanya.X, tanya.Y+8); + if (Scen.IsEndOfGame) { + endofgame.Turn_On(); + } else { + endofgame.Turn_Off(); + } + + /* + ** Timer inherit logic. + */ + CheckBoxClass timercarry(BUTTON_TIMER, endofgame.X, endofgame.Y+8); + if (Scen.IsInheritTimer) { + timercarry.Turn_On(); + } else { + timercarry.Turn_Off(); + } + + /* + ** Disable spy plane option? + */ + CheckBoxClass nospyplane(BUTTON_NOSPYPLANE, timercarry.X, timercarry.Y+8); + if (Scen.IsNoSpyPlane) { + nospyplane.Turn_On(); + } else { + nospyplane.Turn_Off(); + } + + /* + ** Skip the score screen? + */ + CheckBoxClass skipscore(BUTTON_SKIPSCORE, nospyplane.X, nospyplane.Y+8); + if (Scen.IsSkipScore) { + skipscore.Turn_On(); + } else { + skipscore.Turn_Off(); + } + + /* + ** Skip the map selection screen for next mission. Presume goes to + ** variation "B"? + */ + CheckBoxClass nomapsel(BUTTON_NOMAPSEL, skipscore.X, skipscore.Y+8); + if (Scen.IsNoMapSel) { + nomapsel.Turn_On(); + } else { + nomapsel.Turn_Off(); + } + + /* + ** Return to main menu after mission completes? + */ + CheckBoxClass onetime(BUTTON_ONETIME, nomapsel.X, nomapsel.Y+8); + if (Scen.IsOneTimeOnly) { + onetime.Turn_On(); + } else { + onetime.Turn_Off(); + } + + /* + ** Trucks carry a wood crate? + */ + CheckBoxClass truckcrate(BUTTON_TRUCKCRATE, onetime.X, onetime.Y+8); + if (Scen.IsTruckCrate) { + truckcrate.Turn_On(); + } else { + truckcrate.Turn_Off(); + } + + /* + ** Transfer credits into tiberium storage at scenario start? + */ + CheckBoxClass moneytib(BUTTON_MONEYTIB, truckcrate.X, truckcrate.Y+8); + if (Scen.IsMoneyTiberium) { + moneytib.Turn_On(); + } else { + moneytib.Turn_Off(); + } + + /* + ** Intro movie name. + */ + char introtext[_MAX_FNAME+_MAX_EXT]; + DropListClass intro(BUTTON_INTRO, introtext, sizeof(introtext), + TPF_EFNT|TPF_NOSHADOW, + theaterbtn.X, theaterbtn.Y+theaterbtn.Height+24, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //intro.Add_Item(""); + for (VQType v = VQ_FIRST; v < VQ_COUNT; v++) { + scenerio_intros.push_back(&VQName[v][0]); + } + //intro.Set_Selected_Index((int)Scen.IntroMovie + 1); + + /* + ** Briefing movie name. + */ + char brieftext[_MAX_FNAME+_MAX_EXT]; + DropListClass briefing(BUTTON_BRIEFING, brieftext, sizeof(brieftext), + TPF_EFNT|TPF_NOSHADOW, + intro.X+intro.Width+10, intro.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //briefing.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + //briefing.Add_Item(VQName[v]); + scenario_briefing.push_back(VQName[v]); + } + //briefing.Set_Selected_Index((int)Scen.BriefMovie + 1); + + char actiontext[_MAX_FNAME+_MAX_EXT]; + DropListClass action(BUTTON_ACTION, actiontext, sizeof(actiontext), + TPF_EFNT|TPF_NOSHADOW, + briefing.X+briefing.Width+10, briefing.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //action.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + //action.Add_Item(VQName[v]); + scenario_action.push_back(VQName[v]); + } + //action.Set_Selected_Index((int)Scen.ActionMovie + 1); + + char wintext[_MAX_FNAME+_MAX_EXT]; + DropListClass win(BUTTON_WIN, wintext, sizeof(wintext), + TPF_EFNT|TPF_NOSHADOW, + action.X+action.Width+10, action.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //win.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + //win.Add_Item(VQName[v]); + scenario_win.push_back(VQName[v]); + } + //win.Set_Selected_Index((int)Scen.WinMovie + 1); + + char losetext[_MAX_FNAME+_MAX_EXT]; + DropListClass lose(BUTTON_LOSE, losetext, sizeof(losetext), + TPF_EFNT|TPF_NOSHADOW, + win.X+win.Width+10, win.Y, 50, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //lose.Add_Item(""); + for (v = VQ_FIRST; v < VQ_COUNT; v++) { + //lose.Add_Item(VQName[v]); + scenario_loose.push_back(VQName[v]); + } + //lose.Set_Selected_Index((int)Scen.LoseMovie + 1); + + /* + ** House choice list. + */ + ListClass housebtn(BUTTON_HOUSE, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+105, 55, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + // housebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + //} + //housebtn.Set_Selected_Index(PlayerPtr->Class->House); + + /* + ** Base house choice drop down list. + */ + char basetext[35]; + DropListClass basebtn(BUTTON_BASE, basetext, sizeof(basetext), + TPF_EFNT|TPF_NOSHADOW, + D_DIALOG_X+15*RESFACTOR, D_DIALOG_Y+80, 65, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + //basebtn.Add_Item(HouseTypeClass::As_Reference(h).IniName); + scenario_houses.push_back(HouseTypeClass::As_Reference(h).IniName); + } + if (Base.House != HOUSE_NONE) { + basebtn.Set_Selected_Index(Base.House); + } + + /* + ** Opening scenario theme. + */ + char themetext[65]; + DropListClass themebtn(BUTTON_THEME, themetext, sizeof(themetext), + TPF_EFNT|TPF_NOSHADOW, + basebtn.X+basebtn.Width+15*RESFACTOR, basebtn.Y, 85, 7*10, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + //themebtn.Add_Item(""); + for (ThemeType th = THEME_FIRST; th < THEME_COUNT; th++) { + //themebtn.Add_Item(Theme.Full_Name(th)); + scenario_theme.push_back(Theme.Full_Name(th)); + } + //if (Scen.TransitTheme != THEME_NONE) { + // themebtn.Set_Selected_Index(Scen.TransitTheme+1); + //} else { + // themebtn.Set_Selected_Index(0); + //} + + /* + ** Build level (technology). + */ + SliderClass techlevel(BUTTON_TECH, housebtn.X+housebtn.Width+15, housebtn.Y, 100, 8); + techlevel.Set_Maximum(16); + + char statictechbuff[15]; + StaticButtonClass techstatic(0, "999", TPF_EFNT|TPF_NOSHADOW, techlevel.X+techlevel.Width-20, techlevel.Y-7); + + /* + ** Starting credits. + */ + SliderClass creditbtn(BUTTON_CREDITS, housebtn.X+housebtn.Width+15, techlevel.Y+20, 100, 8); + creditbtn.Set_Maximum(201); + + char staticcreditbuff[15]; + StaticButtonClass creditstatic(0, "999999999", TPF_EFNT|TPF_NOSHADOW, creditbtn.X+creditbtn.Width-50, creditbtn.Y-7); + + /* + ** Maximum unit/infantry slider. + */ + SliderClass maxunit(BUTTON_MAXUNIT, housebtn.X+housebtn.Width+15, creditbtn.Y+20, 100, 8); + maxunit.Set_Maximum(501); + + char staticmaxunitbuff[15]; + StaticButtonClass maxunitstatic(0, "999999", TPF_EFNT|TPF_NOSHADOW, maxunit.X+maxunit.Width-30, maxunit.Y-7); + + /* + ** Source of ground delivery reinforcements. + */ + char sourcetext[25] = ""; + ListClass sourcebtn(BUTTON_SOURCE, + housebtn.X+housebtn.Width+15, maxunit.Y+20, 100, 7*4, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SourceType source = SOURCE_FIRST; source <= SOURCE_WEST; source++) { + sourcebtn.Add_Item(SourceName[source]); + } + + /* + ** Smartness lider. + */ + SliderClass smarties(BUTTON_SMARTIES, sourcebtn.X, sourcebtn.Y+sourcebtn.Height+15, 35, 8); + smarties.Set_Maximum(Rule.MaxIQ+1); + + char staticsmartiesbuff[15]; + StaticButtonClass smartiesstatic(0, "9999", TPF_EFNT|TPF_NOSHADOW, smarties.X+smarties.Width-20, smarties.Y-7); + + /* + ** List box of who is allied with whom. + */ + CheckListClass allies(BUTTON_ALLIES, + techlevel.X+techlevel.Width+5, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (hdata[house].Allies & (1L << h)) { + allies.Check_Item(h, true); + } + } + allies.Set_Selected_Index(0); + + /* + ** List box of who the player can control. + */ + CheckListClass control(BUTTON_CONTROL, + allies.X+allies.Width+10, housebtn.Y, 65, 7*10, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + control.Add_Item(HouseTypeClass::As_Reference(h).IniName); + if (HouseClass::As_Pointer(h)->IsPlayerControl) { + control.Check_Item(h, true); + } + } + control.Set_Selected_Index(0); + + /* + ** Create the ubiquitous "ok" and "cancel" buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Create the list + */ + //commands = &okbtn; + //cancelbtn.Add_Tail(*commands); + //theaterbtn.Add_Tail(*commands); + //themebtn.Add_Tail(*commands); + //housebtn.Add_Tail(*commands); + //techlevel.Add_Tail(*commands); + //techstatic.Add_Tail(*commands); + //sourcebtn.Add_Tail(*commands); + //creditbtn.Add_Tail(*commands); + //creditstatic.Add_Tail(*commands); + //maxunitstatic.Add_Tail(*commands); + //moneytib.Add_Tail(*commands); + //smartiesstatic.Add_Tail(*commands); + //allies.Add_Tail(*commands); + //control.Add_Tail(*commands); + //maxunit.Add_Tail(*commands); + //nospyplane.Add_Tail(*commands); + //skipscore.Add_Tail(*commands); + //nomapsel.Add_Tail(*commands); + //onetime.Add_Tail(*commands); + //inherit.Add_Tail(*commands); + //timercarry.Add_Tail(*commands); + //tanya.Add_Tail(*commands); + //record.Add_Tail(*commands); + //truckcrate.Add_Tail(*commands); + //endofgame.Add_Tail(*commands); + //briefing.Add_Tail(*commands); + //intro.Add_Tail(*commands); + //action.Add_Tail(*commands); + //win.Add_Tail(*commands); + //lose.Add_Tail(*commands); + //basebtn.Add_Tail(*commands); + //smarties.Add_Tail(*commands); + //desc.Add_Tail(*commands); + + TheaterType orig_theater = Scen.Theater; + + /* + ** Main Processing Loop + */ + bool housechange = true; + bool display = true; + bool dotext = true; // display the text. + bool fetch = false; // Fetch data from dialog into tracking structure. + //Set_Logic_Page(SeenBuff); + while (scenerio_process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + if (housechange) { + HouseStaticClass * hstatic = &hdata[newhouse]; + creditbtn.Set_Value(hstatic->InitialCredits/100); + techlevel.Set_Value(hstatic->TechLevel); + sourcebtn.Set_Selected_Index(hstatic->Edge); + maxunit.Set_Value(hstatic->MaxUnit + hstatic->MaxInfantry); + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + allies.Check_Item(h, hstatic->Allies & (1L << h)); + } + smarties.Set_Value(hstatic->IQ); + + house = newhouse; + housechange = false; + display = true; + } + + /* + ** Refresh display if needed + */ + //if (display) { + // Hide_Mouse(); + // + // /* + // ** Draw the background + // */ + // Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + // Draw_Caption(TXT_SCENARIO_OPTIONS, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + // + // /* + // ** Display the text that doesn't need drawing except when the entire dialog + // ** needs to be redrawn. + // */ + // Fancy_Text_Print("Tech Level =", techlevel.X, techlevel.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Credits =", creditbtn.X, creditbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Unit Max =", maxunit.X, maxunit.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("IQ =", smarties.X, smarties.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Prebuild Base:", basebtn.X, basebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Theater:", theaterbtn.X, theaterbtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Scenario Name:", desc.X, desc.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Country:", housebtn.X, housebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Home Edge:", sourcebtn.X, sourcebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Allies:", allies.X, allies.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Plyr Control:", control.X, control.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Theme:", themebtn.X, themebtn.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Intro:", intro.X, intro.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Briefing:", briefing.X, briefing.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Action:", action.X, action.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Win:", win.X, win.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Lose:", lose.X, lose.Y-7, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Store scenario?", record.X+10, record.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Inherit stored scenario?", inherit.X+10, inherit.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Auto evac. Tanya (civilian)?", tanya.X+10, tanya.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Last mission of game?", endofgame.X+10, endofgame.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Inherit mission timer from last scenario?", timercarry.X+10, timercarry.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Disable spy plane?", nospyplane.X+10, nospyplane.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Skip the score screen?", skipscore.X+10, skipscore.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("No map selection (force var 'B')?", nomapsel.X+10, nomapsel.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Return to main menu after scenario finishes?", onetime.X+10, onetime.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Truck carries wood crate?", truckcrate.X+10, truckcrate.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // Fancy_Text_Print("Initial money is transferred to silos?", moneytib.X+10, moneytib.Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_EFNT|TPF_NOSHADOW); + // + // theaterbtn.Collapse(); + // themebtn.Collapse(); + // intro.Collapse(); + // briefing.Collapse(); + // action.Collapse(); + // win.Collapse(); + // lose.Collapse(); + // basebtn.Collapse(); + // //commands->Draw_All(true); + // Show_Mouse(); + // display = false; + // dotext = true; + //} + + /* + ** Display the text of the buttons that could change their text as a + ** result of slider interaction. + */ + if (dotext) { + dotext = false; + Hide_Mouse(); + + sprintf(statictechbuff, "%2d", techlevel.Get_Value()); + techstatic.Set_Text(statictechbuff); + techstatic.Draw_Me(); + + sprintf(staticcreditbuff, "$%-7d", creditbtn.Get_Value() * 100); + creditstatic.Set_Text(staticcreditbuff); + creditstatic.Draw_Me(); + + sprintf(staticmaxunitbuff, "%4d", maxunit.Get_Value()); + maxunitstatic.Set_Text(staticmaxunitbuff); + maxunitstatic.Draw_Me(); + + sprintf(staticsmartiesbuff, "%2d", smarties.Get_Value()); + smartiesstatic.Set_Text(staticsmartiesbuff); + smartiesstatic.Draw_Me(); + + Show_Mouse(); + } + + /* + ** Get user input + */ + KeyNumType input = KN_NONE; //commands->Input(); + + //g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + + UserInput.Process_Input(input); + + /* + ** Process input + */ + switch (input) { + case BUTTON_ALLIES|KN_BUTTON: + allies.Check_Item(house, true); + break; + + case BUTTON_CONTROL|KN_BUTTON: + control.Check_Item(house, true); + break; + + case BUTTON_THEME|KN_BUTTON: + case BUTTON_INTRO|KN_BUTTON: + case BUTTON_BRIEFING|KN_BUTTON: + case BUTTON_ACTION|KN_BUTTON: + case BUTTON_WIN|KN_BUTTON: + case BUTTON_LOSE|KN_BUTTON: + case BUTTON_BASE|KN_BUTTON: + case LIST_THEATER|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + themebtn.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + display = true; + break; + + case BUTTON_SMARTIES|KN_BUTTON: + case BUTTON_MAXUNIT|KN_BUTTON: + case BUTTON_CREDITS|KN_BUTTON: + case BUTTON_TECH|KN_BUTTON: + briefing.Collapse(); + action.Collapse(); + win.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + themebtn.Collapse(); + theaterbtn.Collapse(); + dotext = true; + break; + + case BUTTON_HOUSE|KN_BUTTON: + newhouse = HousesType(housebtn.Current_Index()); + housechange = true; + briefing.Collapse(); + action.Collapse(); + themebtn.Collapse(); + win.Collapse(); + intro.Collapse(); + lose.Collapse(); + basebtn.Collapse(); + theaterbtn.Collapse(); + fetch = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + //cancel = false; + //process = false; + fetch = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //cancel = true; + //process = false; + break; + + default: + break; + } + + + /* + ** If the house changes, then all the gadgets that reflect the settings of the + ** house should change as well. + */ + //if (fetch) { + // fetch = false; + // HouseStaticClass * hstatic = &hdata[house]; + // + // Base.House = HousesType(basebtn.Current_Index()); + // hstatic->InitialCredits = creditbtn.Get_Value() * 100; + // hstatic->Edge = SourceType(sourcebtn.Current_Index()); + // hstatic->TechLevel = techlevel.Get_Value(); + // hstatic->MaxUnit = maxunit.Get_Value()/2; + // hstatic->MaxInfantry = maxunit.Get_Value()/2; + // hstatic->IQ = smarties.Get_Value(); + // for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + // if (allies.Is_Checked(h)) { + // hstatic->Allies |= (1L << h); + // } else { + // hstatic->Allies &= ~(1L << h); + // } + // } + //} + } + + /* + ** Redraw the map + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** If cancel, just return + */ + if (scenerio_cancel) { + return(-1); + } + + /* + ** Copy the dialog data back into the appropriate game data locations. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + HouseClass * hptr = HouseClass::As_Pointer(h); + if (hptr != NULL) { + hptr->Control = hdata[h]; + hptr->Allies = hdata[h].Allies; + + if (control.Is_Checked(h)) { + hptr->IsPlayerControl = true; + } else { + hptr->IsPlayerControl = false; + } + } + } + PlayerPtr->IsPlayerControl = true; + strncpy(Scen.Description, desc.Get_Text(), sizeof(Scen.Description)); + Scen.Description[sizeof(Scen.Description)-1] = '\0'; + Scen.IntroMovie = VQType(scenario_intromovie); + Scen.BriefMovie = VQType(briefing.Current_Index()-1); + Scen.ActionMovie = VQType(action.Current_Index()-1); + Scen.WinMovie = VQType(win.Current_Index()-1); + Scen.LoseMovie = VQType(lose.Current_Index()-1); + Scen.IsToInherit = inherit.IsOn; + Scen.IsToCarryOver = record.IsOn; + Scen.IsTanyaEvac = tanya.IsOn; + Scen.IsEndOfGame = endofgame.IsOn; + Scen.IsInheritTimer = timercarry.IsOn; + Scen.IsNoSpyPlane = nospyplane.IsOn; + Scen.IsSkipScore = skipscore.IsOn; + Scen.IsNoMapSel = nomapsel.IsOn; + Scen.IsOneTimeOnly = onetime.IsOn; + Scen.IsTruckCrate = truckcrate.IsOn; + Scen.IsMoneyTiberium = moneytib.IsOn; + Scen.TransitTheme = ThemeType(themebtn.Current_Index()-1); + + /* + ** Change the theater: + ** - 1st set the Theater global + ** - scan all cells to check their TType for compatibility with the new + ** theater; if not compatible, set TType to TEMPLATE_NONE & TIcon to 0 + ** - Then, re-initialize the TypeClasses for the new Theater + */ + TheaterType theater = Scen.Theater; + if (theater != orig_theater) { + + unsigned char theater_mask; // template/terrain mask + TerrainClass * terrain; // cell's terrain pointer + + /* + ** Loop through all cells + */ + for (CELL i = 0; i < MAP_CELL_TOTAL; i++) { + + /* + ** If this cell has a template icon & that template isn't compatible + ** with this theater, set the icon to NONE + */ + if ((*this)[i].TType != TEMPLATE_NONE) { + theater_mask = TemplateTypeClass::As_Reference((*this)[i].TType).Theater; + if ( (theater_mask & (1 << theater))==0) { + (*this)[i].TType = TEMPLATE_NONE; + (*this)[i].TIcon = 0; + } + } + + /* + ** If this cell has terrain in it, and that terrain isn't compatible + ** with this theater, delete the terrain object. + */ + terrain = (*this)[i].Cell_Terrain(); + if (terrain != NULL) { + theater_mask = terrain->Class->Theater; + if ( (theater_mask & (1<Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + + /* + ** 'New' + */ + if (rc==2) { + + /* + ** Create a new trigger + */ + CurTrigger = new TriggerTypeClass(); + if (CurTrigger) { + + /* + ** delete it if user cancels + */ + if (!CurTrigger->Edit()) { + delete CurTrigger; + CurTrigger = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more triggers available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTrigger) { + Detach_This_From_All(CurTrigger->As_Target(), true); + delete CurTrigger; + //CurTrigger->Remove(); + CurTrigger = NULL; + Changed = 1; + } + } + } + + /* + ** Let the CurTrigger global exist if the trigger can be placed on the + ** ground or on a game object. + */ + if (CurTrigger && !(CurTrigger->Attaches_To() & (ATTACH_OBJECT|ATTACH_CELL))) { + CurTrigger = NULL; + } +} + +int select_trigger_seleceted = 0; +enum SelectTriggerButtonState_t { + SELECT_TRIGGER_BUTTON_NONE = 0, + SELECT_TRIGGER_BUTTON_EDIT, + SELECT_TRIGGER_BUTTON_DELETE, + SELECT_TRIGGER_BUTTON_OK, + SELECT_TRIGGER_BUTTON_NEW +}; + +SelectTriggerButtonState_t selectTriggerButtonState = SELECT_TRIGGER_BUTTON_NONE; + +void ImGui_SelectTrigger(void) { + bool toolActive = false; + + std::vector triggerlist; + + for (int i = 0; i < TriggerTypes.Count(); i++) { + //triggerlist.Add_Item(TriggerTypes.Ptr(i)); + triggerlist.push_back(TriggerTypes.Ptr(i)->Description()); + } + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(450, 230)); + ImGui::SetNextWindowPos(ImVec2(100, 100)); + ImGui::Begin("Trigger Editor", &toolActive, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::PushItemWidth(450); + bool forceClear = false; + if (triggerlist.size() == 0) { + forceClear = true; + triggerlist.push_back(""); + } + ImGui::ListBox("", &select_trigger_seleceted, &triggerlist[0], triggerlist.size()); + if (forceClear) { + triggerlist.clear(); + } + ImGui::PopItemWidth(); + ImGui::NewLine(); + ImGui::Columns(4, "outert", true); + ImGui::PushID("st_edit"); + if (ImGui::Button("Edit")) { + selectTriggerButtonState = SELECT_TRIGGER_BUTTON_EDIT; + } + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::PushID("st_new"); + if (ImGui::Button("New")) { + selectTriggerButtonState = SELECT_TRIGGER_BUTTON_NEW; + } + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::PushID("st_delete"); + if (ImGui::Button("Delete")) { + selectTriggerButtonState = SELECT_TRIGGER_BUTTON_DELETE; + } + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::PushID("st_ok"); + if (ImGui::Button("Ok")) { + selectTriggerButtonState = SELECT_TRIGGER_BUTTON_OK; + } + ImGui::PopID(); + ImGui::Columns(1); + ImGui::End(); +} + +/*************************************************************************** + * MapEditClass::Select_Trigger -- lets user select a trigger * + * * + * CurTrigger can be NULL when this function is called. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Triggers ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name Event Action House Team ³³ ³ * + * ³ ³ Name Event Action House Team ÃÄ´ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ Name Event Action House Team ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sort trigger list. * + *=========================================================================*/ +int MapEditClass::Select_Trigger(void) +{ + selectTriggerButtonState = SELECT_TRIGGER_BUTTON_NONE; + Imgui_Dialog_Function = ImGui_SelectTrigger; + select_trigger_seleceted = 0; + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_TXT8_H = 11, + D_MARGIN = 35, + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-10, + D_LIST_H = D_DIALOG_H-70, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 25, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + }; + + /* + ** Button enumerations: + */ + enum { + TRIGGER_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables: + */ + //bool edit_trig = false; // true = user wants to edit + //bool new_trig = false; // true = user wants to new + //bool del_trig = false; // true = user wants to new + int i; // loop counter + + /* + ** Buttons + */ + //ControlClass * commands = NULL; // the button list + + //TListClass triggerlist(TRIGGER_LIST, D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + // TPF_EFNT|TPF_NOSHADOW, + // MFCD::Retrieve("EBTN-UP.SHP"), + // MFCD::Retrieve("EBTN-DN.SHP")); + // + //TextButtonClass editbtn(BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W, D_EDIT_H); + //TextButtonClass newbtn(BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W, D_NEW_H); + //TextButtonClass deletebtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W, D_DELETE_H); + //TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in the list box + */ + //for (i = 0; i < TriggerTypes.Count(); i++) { + // triggerlist.Add_Item(TriggerTypes.Ptr(i)); + //} + // + //PNBubble_Sort(&triggerlist[0], triggerlist.Count()); + // + //if (CurTrigger) { + // triggerlist.Set_Selected_Index(CurTrigger); + //} else { + // triggerlist.Set_Selected_Index(0); + //} + + /* + ** Set CurTrigger if it isn't + */ + // if (TriggerTypes.Count()==0) { + // CurTrigger = NULL; + // } else { + // CurTrigger = (TriggerTypeClass *)triggerlist.Current_Item(); + //// if (!CurTrigger) { + //// CurTrigger = &*triggerlist.Current_Item(); + //// } + // } + + /* + ** Create the list + */ + //commands = &triggerlist; + //editbtn.Add_Tail(*commands); + //newbtn.Add_Tail(*commands); + //deletebtn.Add_Tail(*commands); + //okbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + while (selectTriggerButtonState == SELECT_TRIGGER_BUTTON_NONE) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if requested. + */ +// if (display /*&& LogicPage->Lock()*/) { +// Hide_Mouse(); +// Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); +// Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); +// //commands->Flag_List_To_Redraw(); +// //commands->Draw_All(); +// Show_Mouse(); +// display = false; +//// LogicPage->Unlock(); +// } + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + /* + ** Get user input + */ + //KeyNumType input = commands->Input(); + // + ///* + //** Process input + //*/ + //switch (input) { + // case (TRIGGER_LIST | KN_BUTTON): + // CurTrigger = (TriggerTypeClass *)&*triggerlist.Current_Item(); +// // CurTrigger = (TriggerTypeClass *)&*triggerlist.Current_Item(); + // break; + // + // case (BUTTON_EDIT | KN_BUTTON): + // if (CurTrigger) { // only allow if there's one selected + // process = false; + // edit_trig = true; + // } + // break; + // + // case (BUTTON_NEW | KN_BUTTON): + // process = false; + // new_trig = true; + // break; + // + // case (BUTTON_DELETE | KN_BUTTON): + // process = false; + // del_trig = true; + // break; + // + // case (KN_RETURN): + // case (BUTTON_OK | KN_BUTTON): + // process = false; + // break; + //} + } + + CurTrigger = TriggerTypes.Ptr(select_trigger_seleceted); + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + Imgui_Dialog_Function = nullptr; + + if (selectTriggerButtonState == SELECT_TRIGGER_BUTTON_EDIT) return(1); + if (selectTriggerButtonState == SELECT_TRIGGER_BUTTON_NEW) return(2); + if (selectTriggerButtonState == SELECT_TRIGGER_BUTTON_DELETE) return(3); + return(0); +} + + +#endif diff --git a/code/redalert/mapedit.cpp b/code/redalert/mapedit.cpp new file mode 100644 index 0000000..ab87f0e --- /dev/null +++ b/code/redalert/mapedit.cpp @@ -0,0 +1,2223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDIT.CPP 2 3/13/97 2:05p Steve_tall $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : February 2, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * Map Editor overloaded routines & utility routines * + *-------------------------------------------------------------------------* + * Map Editor modules: * + * (Yes, they're all one huge class.) * + * mapedit.cpp: overloaded routines, utility routines * + * mapeddlg.cpp: map editor dialogs, most of the main menu options * + * mapedplc.cpp: object-placing routines * + * mapedsel.cpp: object-selection & manipulation routines * + * mapedtm.cpp: team-editing routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::AI -- The map editor's main logic * + * MapEditClass::Read_INI -- overloaded Read_INI function * + * MapEditClass::AI_Menu -- menu of AI options * + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * MapEditClass::Clear_List -- clears the internal chooseable object list* + * MapEditClass::Cycle_House -- finds next valid house for object type * + * MapEditClass::Draw_It -- overloaded Redraw routine * + * MapEditClass::Fatal -- exits with error message * + * MapEditClass::Main_Menu -- main menu processor for map editor * + * MapEditClass::MapEditClass -- class constructor * + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * MapEditClass::One_Time -- one-time initialization * + * MapEditClass::Verify_House -- sees if given house can own given obj * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "function.h" + +#ifdef SCENARIO_EDITOR + +/* +** Array of all missions supported by the map editor +*/ +MissionType MapEditClass::MapEditMissions[] = { + MISSION_GUARD, + MISSION_STICKY, + MISSION_HARMLESS, + MISSION_HARVEST, + MISSION_GUARD_AREA, + MISSION_RETURN, + MISSION_AMBUSH, + MISSION_HUNT, + MISSION_SLEEP, +}; +const int NUM_EDIT_MISSIONS = (sizeof(MapEditClass::MapEditMissions) / sizeof(MapEditClass::MapEditMissions[0])); + + +/* +** For menu processing +*/ +extern int UnknownKey; // in menus.cpp + +char MapEditClass::HealthBuf[20]; + +/*************************************************************************** + * MapEditClass::MapEditClass -- class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +MapEditClass::MapEditClass(void) +{ + /* + ** Init data members. + */ +// ScenVar = SCEN_VAR_A; + LastHouse = HOUSE_GOOD; + GrabbedObject = 0; + Scen.Waypoint[WAYPT_HOME] = 0; + CurrentCell = 0; + CurTeam = NULL; + CurTrigger = NULL; + Changed = 0; + LMouseDown = 0; + BaseBuilding = false; +// BasePercent = 100; +} + + +/*************************************************************************** + * MapEditClass::One_Time -- one-time initialization * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/02/1995 BR : Created. * + *=========================================================================*/ +void MapEditClass::One_Time(void) +{ + MouseClass::One_Time(); + + /* + ** The map: a single large "button" + */ +#ifdef WIN32 + MapArea = new ControlClass(MAP_AREA, 0, 8, ScreenWidth-8, ScreenHeight-8, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#else + MapArea = new ControlClass(MAP_AREA, 0, 8, 312, 192, GadgetClass::LEFTPRESS | GadgetClass::LEFTRELEASE, false); +#endif + + /* + ** House buttons + */ + HouseList = new ListClass(POPUP_HOUSELIST, POPUP_HOUSE_X, POPUP_HOUSE_Y, POPUP_HOUSE_W, POPUP_HOUSE_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseList->Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + + /* + ** The mission list box + */ + MissionList = new ListClass(POPUP_MISSIONLIST, + POPUP_MISSION_X, POPUP_MISSION_Y, POPUP_MISSION_W, POPUP_MISSION_H, + TPF_EFNT|TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int i = 0; i < NUM_EDIT_MISSIONS; i++) { + MissionList->Add_Item(MissionClass::Mission_Name(MapEditMissions[i])); + } + + /* + ** The health bar + */ + HealthGauge = new TriColorGaugeClass(POPUP_HEALTHGAUGE, + POPUP_HEALTH_X, POPUP_HEALTH_Y, POPUP_HEALTH_W, POPUP_HEALTH_H); + HealthGauge->Use_Thumb(true); + HealthGauge->Set_Maximum(0x100); + HealthGauge->Set_Red_Limit(0x3f - 1); + HealthGauge->Set_Yellow_Limit(0x7f - 1); + + /* + ** The health text label + */ + HealthBuf[0] = 0; + HealthText = new TextLabelClass(HealthBuf, + POPUP_HEALTH_X + POPUP_HEALTH_W / 2, + POPUP_HEALTH_Y + POPUP_HEALTH_H + 1, + GadgetClass::Get_Color_Scheme(), + TPF_CENTER | TPF_FULLSHADOW | TPF_EFNT); + + /* + ** Building attribute buttons. + */ + Sellable = new TextButtonClass(POPUP_SELLABLE, TXT_SELLABLE, TPF_EBUTTON, 320-65, 200-25, 60); + Rebuildable = new TextButtonClass(POPUP_REBUILDABLE, TXT_REBUILD, TPF_EBUTTON, 320-65, 200-15, 60); + + /* + ** The facing dial + */ + FacingDial = new Dial8Class(POPUP_FACINGDIAL, POPUP_FACEBOX_X, + POPUP_FACEBOX_Y, POPUP_FACEBOX_W, POPUP_FACEBOX_H, (DirType)0); + + /* + ** The base percent-built slider & its label + */ + BaseGauge = new GaugeClass(POPUP_BASEPERCENT, POPUP_BASE_X, POPUP_BASE_Y, POPUP_BASE_W, POPUP_BASE_H); + BaseLabel = new TextLabelClass ("Base:", POPUP_BASE_X - 3, POPUP_BASE_Y, GadgetClass::Get_Color_Scheme(), + TPF_RIGHT | TPF_NOSHADOW | TPF_EFNT); + BaseGauge->Set_Maximum(100); + BaseGauge->Set_Value(Scen.Percent); +} + + +/*********************************************************************************************** + * MapeditClass::Init_IO -- Reinitializes the radar map at scenario start. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Init_IO(void) +{ + /* + ** For normal game mode, jump to the parent's Init routine. + */ + if (!Debug_Map) { + + MouseClass::Init_IO(); + + } else { + + /* + ** For editor mode, add the map area to the button input list + */ + Buttons = 0; + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + } +} + + +/*************************************************************************** + * MapEditClass::Clear_List -- clears the internal chooseable object list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Clear_List(void) +{ + /* + ** Set # object type ptrs to 0, set NumType for each type to 0 + */ + for (int i = 0; i < NUM_EDIT_CLASSES; i++) { + Objects[i].clear(); + } +} + + +/*************************************************************************** + * MapEditClass::Add_To_List -- adds a TypeClass to the chooseable list * + * * + * Use this routine to add an object to the game object selection list. * + * This list is used by the Add_Object function. All items located in the * + * list will appear and be chooseable by that function. Make sure to * + * clear the list before adding a sequence of items to it. Clearing * + * the list is accomplished by the Clear_List() function. * + * * + * INPUT: * + * object ptr to ObjectTypeClass to add * + * * + * OUTPUT: * + * bool: was the object added to the list? A failure could occur if * + * NULL were passed in or the list is full. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/04/1994 JLB : Created. * + *=========================================================================*/ +bool MapEditClass::Add_To_List(ObjectTypeClass const * object) +{ + /* + ** Add the object if there's room. + */ + if (object /*&& ObjCount < MAX_EDIT_OBJECTS*/) { + //Objects[ObjCount++] = object; + /* + ** Update type counters. + */ + switch (object->What_Am_I()) { + case RTTI_TEMPLATETYPE: + Objects[0].push_back(object); + break; + + case RTTI_OVERLAYTYPE: + Objects[1].push_back(object); + break; + + case RTTI_SMUDGETYPE: + Objects[2].push_back(object); + break; + + case RTTI_TERRAINTYPE: + Objects[3].push_back(object); + break; + + case RTTI_UNITTYPE: + Objects[4].push_back(object); + break; + + case RTTI_INFANTRYTYPE: + Objects[5].push_back(object); + break; + + case RTTI_VESSELTYPE: + Objects[6].push_back(object); + break; + + case RTTI_BUILDINGTYPE: + Objects[7].push_back(object); + break; + + case RTTI_AIRCRAFTTYPE: + Objects[8].push_back(object); + break; + } + + return(true); + } + + return(false); +} + + +/*************************************************************************** + * MapEditClass::AI -- The map editor's main logic * + * * + * This routine overloads the parent's (DisplayClass) AI function. * + * It checks for any input specific to map editing, and calls the parent * + * AI routine to handle scrolling and other mainstream map stuff. * + * * + * If this detects one of its special input keys, it sets 'input' to 0 * + * before calling the parent AI routine; this prevents input conflict. * + * * + * SUPPORTED INPUT: * + * General: * + * F2/RMOUSE: main menu * + * F6: toggles show-passable mode * + * HOME: go to the Home Cell (scenario's start position)* + * SHIFT-HOME: set the Home Cell to the current TacticalCell* + * ESC: exits to DOS * + * Object Placement: * + * INSERT: go into placement mode * + * ESC: exit placement mode * + * LEFT/RIGHT: prev/next placement object * + * PGUP/PGDN: prev/next placement category * + * HOME: 1st placement object (clear template) * + * h/H: toggle house of placement object * + * LMOUSE: place the placement object * + * MOUSE MOTION: "paint" with the placement object * + * Object selection: * + * LMOUSE: select & "grab" current object * + * If no object is present where the mouse is * + * clicked, the current object is de-selected * + * If the same object is clicked on, it stays * + * selected. Also displays the object-editing * + * gadgets. * + * LMOUSE RLSE: release currently-grabbed object * + * MOUSE MOTION: if an object is grabbed, moves the object * + * SHIFT|ALT|ARROW: moves object in that direction * + * DELETE deletes currently-selected object * + * Object-editing controls: * + * POPUP_GDI: makes GDI the owner of this object * + * POPUP_NOD: makes NOD the owner of this object * + * POPUP_MISSIONLIST: sets that mission for this object * + * POPUP_HEALTHGAUGE: sets that health value for this object * + * POPUP_FACINGDIAL: sets the object's facing * + * * + * Changed is set when you: * + * - place an object * + * - move a grabbed object * + * - delete an object * + * - size the map * + * - create a new scenario * + * Changed is cleared when you: * + * - Save the scenario * + * - Load a scenario * + * - Play the scenario * + * * + * INPUT: * + * input KN_ value, 0 if none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI(KeyNumType & input, int x, int y) +{ + int rc; + MissionType mission; + int strength; + CELL cell; + int i; + int found; // for removing a waypoint label + int waypt_idx; // for labelling a waypoint + BaseNodeClass * node; // for removing from an AI Base + HousesType house; + char wayname[4]; + + /* + ** Trap 'F2' regardless of whether we're in game or editor mode + */ + if (Debug_Flag) { + if ((input == KN_F2 && Session.Type == GAME_NORMAL) || input == (KN_F2 | KN_CTRL_BIT)) { + ScenarioInit = 0; + + /* + ** If we're in editor mode & Changed is set, prompt for saving changes + */ + if (Debug_Map && Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort game + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + } else { + Changed = 0; + Go_Editor(!Debug_Map); + } + } else { + + /* + ** User doesn't want to save + */ + Go_Editor(!Debug_Map); + } + } else { + /* + ** If we're in game mode, set Changed to 0 (so if we didn't save our + ** changes above, they won't keep coming back to haunt us with continual + ** Save Changes? prompts!) + */ + if (!Debug_Map) { + Changed = 0; + } + BaseGauge->Set_Value(Scen.Percent); + Go_Editor(!Debug_Map); + } + } + } + + /* + ** For normal game mode, jump to the parent's AI routine. + */ + if (!Debug_Map) { + MouseClass::AI(input, x, y); + return; + } + + ::Frame++; + + /* + ** Do special mouse processing if the mouse is over the map + */ + if (Get_Mouse_X() > TacPixelX && Get_Mouse_X() < + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() > TacPixelY && Get_Mouse_Y() < + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + /* + ** When the mouse moves over a scrolling edge, ScrollClass changes its + ** shape to the appropriate arrow or NO symbol; it's our job to change it + ** back to normal (or whatever the shape is set to by Set_Default_Mouse()) + ** when it re-enters the map area. + */ + if (CurTrigger) { + Override_Mouse_Shape(MOUSE_CAN_MOVE); + } else { + Override_Mouse_Shape(MOUSE_NORMAL); + } + } + + /* + ** Set 'ZoneCell' to track the mouse cursor around over the map. Do this + ** even if the map is scrolling. + */ + if (Get_Mouse_X() >= TacPixelX && Get_Mouse_X() <= + TacPixelX + Lepton_To_Pixel(TacLeptonWidth) && + Get_Mouse_Y() >= TacPixelY && Get_Mouse_Y() <= + TacPixelY + Lepton_To_Pixel(TacLeptonHeight)) { + + cell = Click_Cell_Calc(Get_Mouse_X(), Get_Mouse_Y()); + if (cell != -1) { + Set_Cursor_Pos(cell); + if (PendingObject) { + Flag_To_Redraw(true); + } + } + } + + /* + ** Check for mouse motion while left button is down. + */ + rc = Mouse_Moved(); + if (LMouseDown && rc) { + + /* + ** "Paint" mode: place current object, and restart placement + */ + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Move the currently-grabbed object + */ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } + } + } + } + + /* + ** Trap special editing keys; if one is detected, set 'input' to 0 to + ** prevent a conflict with parent's AI(). + */ + switch (input) { + /* + ** F2/RMOUSE = pop up main menu + */ + case KN_RMOUSE: + + /* + ** Turn off placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + } + + /* + ** Turn off trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + } + + /* + ** Unselect object & hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + Main_Menu(); + input = KN_NONE; + break; + + /* + ** F6 = toggle passable/impassable display + */ + case KN_F6: + Debug_Passable = (Debug_Passable == false); + HidPage.Clear(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + + /* + ** INSERT = go into object-placement mode + */ + case KN_INSERT: + if (!PendingObject) { + /* + ** Unselect current object, hide popup controls + */ + if (CurrentObject.Count()) { + CurrentObject[0]->Unselect(); + Popup_Controls(); + } + + /* + ** Go into placement mode + */ + Start_Placement(); + } + input = KN_NONE; + break; + + /* + ** ESC = exit placement mode, or exit to DOS + */ + case KN_ESC: + + /* + ** Exit object placement mode + */ + if (PendingObject) { + if (BaseBuilding) { + Cancel_Base_Building(); + } else { + Cancel_Placement(); + } + input = KN_NONE; + break; + } else { + + /* + ** Exit trigger placement mode + */ + if (CurTrigger) { + Stop_Trigger_Placement(); + input = KN_NONE; + break; + } else { + rc = WWMessageBox().Process("Exit Scenario Editor?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User doesn't want to exit; return to editor + */ + if (rc==1) { + input = KN_NONE; + break; + } + + /* + ** If changed, prompt for saving + */ + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + /* + ** User wants to save + */ + if (rc == 0) { + + /* + ** If save cancelled, abort exit + */ + if (Save_Scenario()!=0) { + input = KN_NONE; + break; + } else { + Changed = 0; + } + } + } + } + } + //Prog_End(); + Emergency_Exit(0); + break; + + /* + ** LEFT = go to previous placement object + */ + case KN_LEFT: + if (PendingObject) { + Place_Prev(); + } + input = KN_NONE; + break; + + /* + ** RIGHT = go to next placement object + */ + case KN_RIGHT: + if (PendingObject) { + Place_Next(); + } + input = KN_NONE; + break; + + /* + ** PGUP = go to previous placement category + */ + case KN_PGUP: + if (PendingObject) { + Place_Prev_Category(); + } + input = KN_NONE; + break; + + /* + ** PGDN = go to next placement category + */ + case KN_PGDN: + if (PendingObject) { + Place_Next_Category(); + } + input = KN_NONE; + break; + + /* + ** HOME = jump to first placement object, or go to Home Cell + */ + case KN_HOME: + if (PendingObject) { + Place_Home(); + } else { + + /* + ** Set map position + */ + ScenarioInit++; + Set_Tactical_Position(Scen.Waypoint[WAYPT_HOME]); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + input = KN_NONE; + break; + + /* + ** SHIFT-HOME: set new Home Cell position + */ + case ((int)KN_HOME | (int)KN_SHIFT_BIT): + if (CurrentCell != 0) { + + /* + ** Unflag the old Home Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_HOME]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_HOME && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + + /* + ** Now set the new Home cell + */ +// Scen.Waypoint[WAYPT_HOME] = Coord_Cell(TacticalCoord); +// (*this)[TacticalCoord].IsWaypoint = 1; +// Flag_Cell(Coord_Cell(TacticalCoord)); + Scen.Waypoint[WAYPT_HOME] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + + Changed = 1; + input = KN_NONE; +} + break; + + /* + ** SHIFT-R: set new Reinforcement Cell position. Don't allow setting + ** the Reinf. Cell to the same as the Home Cell (for display purposes.) + */ + case ((int)KN_R | (int)KN_SHIFT_BIT): + if (CurrentCell==0 || CurrentCell==Scen.Waypoint[WAYPT_HOME]) { + break; + } + + /* + ** Unflag the old Reinforcement Cell, if there are no other waypoints + ** pointing to it + */ + cell = Scen.Waypoint[WAYPT_REINF]; + + if (cell != -1) { + found = 0; + for (i = 0; i < WAYPT_COUNT; i++) { + if (i != WAYPT_REINF && Scen.Waypoint[i]==cell) { + found = 1; + } + } + + if (found==0) { + (*this)[cell].IsWaypoint = 0; + Flag_Cell(cell); + } + + } + /* + ** Now set the new Reinforcement cell + */ + Scen.Waypoint[WAYPT_REINF] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Flag_Cell(CurrentCell); + Changed = 1; + input = KN_NONE; + break; + + /* + ** ALT-Letter: Label a waypoint cell + */ + case ((int)KN_A | (int)KN_ALT_BIT): + case ((int)KN_B | (int)KN_ALT_BIT): + case ((int)KN_C | (int)KN_ALT_BIT): + case ((int)KN_D | (int)KN_ALT_BIT): + case ((int)KN_E | (int)KN_ALT_BIT): + case ((int)KN_F | (int)KN_ALT_BIT): + case ((int)KN_G | (int)KN_ALT_BIT): + case ((int)KN_H | (int)KN_ALT_BIT): + case ((int)KN_I | (int)KN_ALT_BIT): + case ((int)KN_J | (int)KN_ALT_BIT): + case ((int)KN_K | (int)KN_ALT_BIT): + case ((int)KN_L | (int)KN_ALT_BIT): + case ((int)KN_M | (int)KN_ALT_BIT): + case ((int)KN_N | (int)KN_ALT_BIT): + case ((int)KN_O | (int)KN_ALT_BIT): + case ((int)KN_P | (int)KN_ALT_BIT): + case ((int)KN_Q | (int)KN_ALT_BIT): + case ((int)KN_R | (int)KN_ALT_BIT): + case ((int)KN_S | (int)KN_ALT_BIT): + case ((int)KN_T | (int)KN_ALT_BIT): + case ((int)KN_U | (int)KN_ALT_BIT): + case ((int)KN_V | (int)KN_ALT_BIT): + case ((int)KN_W | (int)KN_ALT_BIT): + case ((int)KN_X | (int)KN_ALT_BIT): + case ((int)KN_Y | (int)KN_ALT_BIT): + case ((int)KN_Z | (int)KN_ALT_BIT): + if (CurrentCell != 0) { +#ifdef WIN32 + waypt_idx = (input & ~KN_ALT_BIT) - KN_A; +#else + waypt_idx = KN_To_KA(input & 0x00ff) - KA_a; +#endif + Update_Waypoint(waypt_idx); + } + input = KN_NONE; + break; + + /* + ** ALT-. : Designate an extended (2-letter) waypoint name + */ + case KN_PERIOD: + case ((int)KN_PERIOD | (int)KN_ALT_BIT): + if (CurrentCell != 0 && Get_Waypoint_Name(wayname)) { + int waynm = 0; + if (strlen(wayname)) { + wayname[0] = toupper(wayname[0]); + wayname[1] = toupper(wayname[1]); + if (wayname[0] >= 'A' && wayname[0] <= 'Z') { + waynm = wayname[0] - 'A'; + if (wayname[1] >= 'A' && wayname[1] <= 'Z') { + waynm = (waynm+1)*26 + (wayname[1] - 'A'); + } + if (waynm < WAYPT_HOME) Update_Waypoint(waynm); + } + } + } + input = KN_NONE; + break; + +#ifdef OBSOLETE + /* + ** ALT-1-4: Designate a cell as a capture-the-flag cell. + */ + case ((int)KN_1 | (int)KN_ALT_BIT): + case ((int)KN_2 | (int)KN_ALT_BIT): + case ((int)KN_3 | (int)KN_ALT_BIT): + case ((int)KN_4 | (int)KN_ALT_BIT): + + /* + ** If there's a current cell, place the flag & waypoint there. + */ + if (CurrentCell != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); +// waypt_idx = (KN_To_KA(input & 0xff) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house)) { + HouseClass::As_Pointer(house)->Flag_Attach(CurrentCell, true); + } + } else { + + /* + ** If there's a current object, attach the flag to it and clear the + ** waypoint. + */ + if (CurrentObject[0] != 0) { + waypt_idx = (Keyboard->To_ASCII((KeyNumType)(input & 0xff)) - KA_1); + house = (HousesType)(HOUSE_MULTI1 + waypt_idx); + if (HouseClass::As_Pointer(house) && CurrentObject[0]->What_Am_I() == RTTI_UNIT) { + HouseClass::As_Pointer(house)->Flag_Attach((UnitClass *)CurrentObject[0], true); + } + } + } + input = KN_NONE; + break; +#endif + + /* + ** ALT-Space: Remove a waypoint designation + */ + case ((int)KN_SPACE | (int)KN_ALT_BIT): + if (CurrentCell != 0) { + + /* + ** Loop through letter waypoints; if this cell is one of them, + ** clear that waypoint. + */ + for (i = 0 ; i < WAYPT_HOME; i++) { + if (Scen.Waypoint[i]==CurrentCell) + Scen.Waypoint[i] = -1; + } + + /* + ** Loop through flag home values; if this cell is one of them, clear + ** that waypoint. + */ + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(HOUSE_MULTI1 + i); + if (HouseClass::As_Pointer(house) && + CurrentCell == HouseClass::As_Pointer(house)->FlagHome) + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(CurrentCell), true); + } + + /* + ** If there are no more waypoints on this cell, clear the cell's + ** waypoint designation. + */ + if (Scen.Waypoint[WAYPT_HOME]!=CurrentCell && + Scen.Waypoint[WAYPT_REINF]!=CurrentCell) + (*this)[CurrentCell].IsWaypoint = 0; + Changed = 1; + Flag_Cell(CurrentCell); + } + input = KN_NONE; + break; + + /* + ** 'H' = toggle current placement object's house + */ + case KN_H: + case ((int)KN_H | (int)KN_SHIFT_BIT): + if (PendingObject) { + Toggle_House(); + } + input = KN_NONE; + break; + + /* + ** Left-mouse click: + ** Button DOWN: + ** - Toggle LMouseDown + ** - If we're in placement mode, try to place the current object + ** - If success, re-enter placement mode + ** - Otherwise, try to select an object, and "grab" it if there is one + ** - If no object, then select that cell as the "current" cell + ** Button UP: + ** - Toggle LMouseDown + ** - release any grabbed object + */ + case ((int)MAP_AREA | (int)KN_BUTTON): + + /* + ** Left Button DOWN + */ + if (Keyboard->Down(KN_LMOUSE)) { + LMouseDown = 1; + + /* + ** Placement mode: place an object + */ + if (PendingObject) { + if (Place_Object()==0) { + Changed = 1; + Start_Placement(); + } + } else { + + /* + ** Place a trigger + */ + if (CurTrigger) { + Place_Trigger(); + Changed = 1; + } else { + + /* + ** Select an object or a cell + ** Check for double-click + */ + if (CurrentObject.Count() && + ((TickCount - LastClickTime) < 15)) { + ; // stub + + } else { + /* + ** Single-click: select object + */ + if (Select_Object()==0) { + CurrentCell = 0; + Grab_Object(); + } else { + + /* + ** No object: select the cell + */ + CurrentCell = Click_Cell_Calc(Keyboard->MouseQX, Keyboard->MouseQY); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } + } + } + LastClickTime = TickCount(); + input = KN_NONE; + } else { + + /* + ** Left Button UP + */ + LMouseDown = 0; + GrabbedObject = 0; + input = KN_NONE; + } + break; + + /* + ** SHIFT-ALT-Arrow: move the current object + */ + case (int)KN_UP | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_DOWN | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_LEFT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + case (int)KN_RIGHT | (int)KN_ALT_BIT | (int)KN_SHIFT_BIT: + if (CurrentObject.Count()) { + CurrentObject[0]->Move(KN_To_Facing(input)); + Changed = 1; + } + input = KN_NONE; + break; + + /* + ** DELETE: delete currently-selected object + */ + case KN_DELETE: + + /* + ** Delete currently-selected object's trigger, or the object + */ + if (CurrentObject.Count()) { + + /* + ** Delete trigger + */ + if (CurrentObject[0]->Trigger.Is_Valid()) { + CurrentObject[0]->Trigger = NULL; + } else { + /* + ** If the current object is part of the AI's Base, remove it + ** from the Base's Node list. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && + Base.Is_Node((BuildingClass *)CurrentObject[0])) { + node = Base.Get_Node((BuildingClass *)CurrentObject[0]); + Base.Nodes.Delete(*node); + } + + /* + ** Delete current object + */ + delete CurrentObject[0]; + + /* + ** Hide the popup controls + */ + Popup_Controls(); + } + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } else { + + /* + ** Remove trigger from current cell + */ + if (CurrentCell) { + if ((*this)[CurrentCell].Trigger.Is_Valid()) { + (*this)[CurrentCell].Trigger = NULL; +// CellTriggers[CurrentCell] = NULL; + + /* + ** Force a redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + } + input = KN_NONE; + break; + + /* + ** TAB: select next object on the map + */ + case KN_TAB: + Select_Next(); + input = KN_NONE; + break; + + /* + ** Object-Editing button: House Button + */ + case POPUP_HOUSELIST|KN_BUTTON: + /* + ** Determine the house desired by examining the currently + ** selected index in the house list gadget. + */ + house = HousesType(((ListClass *)Buttons->Extract_Gadget(POPUP_HOUSELIST))->Current_Index()); + + /* + ** If that house doesn't own this object, try to transfer it + */ + if (CurrentObject[0]->Owner() != house) { + if (Change_House(house)) { + Changed = 1; + } + } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); + HidPage.Clear(); + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + input = KN_NONE; + break; + +// case (POPUP_GDI | KN_BUTTON): +// case (POPUP_NOD | KN_BUTTON): +// case (POPUP_NEUTRAL | KN_BUTTON): +// case (POPUP_MULTI1 | KN_BUTTON): +// case (POPUP_MULTI2 | KN_BUTTON): +// case (POPUP_MULTI3 | KN_BUTTON): +// case (POPUP_MULTI4 | KN_BUTTON): +// +// /* +// ** Convert input value into a house value; assume HOUSE_GOOD is 0 +// */ +// house = (HousesType)( (input & (~KN_BUTTON)) - POPUP_FIRST); +// +// /* +// ** If that house doesn't own this object, try to transfer it +// */ +// if (CurrentObject[0]->Owner()!=house) { +// if (Change_House(house)) { +// Changed = 1; +// } +// } +// Set_House_Buttons(CurrentObject[0]->Owner(), Buttons, POPUP_FIRST); +// HidPage.Clear(); +// Flag_To_Redraw(true); +// input = KN_NONE; +// break; + + case POPUP_SELLABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsAllowedToSell = (building->IsAllowedToSell == false); + building->Mark(MARK_CHANGE); + } + if (building->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + } + break; + + case POPUP_REBUILDABLE|KN_BUTTON: + if (CurrentObject[0]->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)CurrentObject[0]; + + if (building->Class->Level != -1) { +// if (building->Class->IsBuildable) { + building->IsToRebuild = (building->IsToRebuild == false); + building->Mark(MARK_CHANGE); + } + if (building->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + } + break; + + /* + ** Object-Editing button: Mission + */ + case (POPUP_MISSIONLIST | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + /* + ** Set new mission + */ + mission = MapEditMissions[MissionList->Current_Index()]; + if (CurrentObject[0]->Get_Mission() != mission) { + ((TechnoClass *)CurrentObject[0])->Set_Mission(mission); + Changed = 1; + Buttons->Flag_List_To_Redraw(); + Flag_To_Redraw(true); + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Health + */ + case (POPUP_HEALTHGAUGE | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Derive strength from current gauge reading + */ + int val = fixed(HealthGauge->Get_Value(), 256); + strength = CurrentObject[0]->Class_Of().MaxStrength * val; +// strength = Fixed_To_Cardinal((unsigned)CurrentObject[0]->Class_Of().MaxStrength, (unsigned)HealthGauge->Get_Value()); + + /* + ** Clip to 1 + */ + if (strength <= 0) { + strength = 1; + } + + /* + ** Set new strength + */ + if (strength != CurrentObject[0]->Strength) { + CurrentObject[0]->Strength = strength; + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + + /* + ** Update text label + */ + sprintf(HealthBuf, "%d", strength); + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_FACINGDIAL | KN_BUTTON): + if (CurrentObject[0]->Is_Techno()) { + + /* + ** Set new facing + */ + if (FacingDial->Get_Direction() != + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Get()) { + + /* + ** Set body's facing + */ + ((TechnoClass *)CurrentObject[0])->PrimaryFacing.Set(FacingDial->Get_Direction()); + + /* + ** Set turret facing, if there is one + */ + if (CurrentObject[0]->What_Am_I()==RTTI_UNIT) { + ((UnitClass *)CurrentObject[0])->SecondaryFacing.Set(FacingDial->Get_Direction()); + } + + HidPage.Clear(); + Flag_To_Redraw(true); + Changed = 1; + } + } + input = KN_NONE; + break; + + /* + ** Object-Editing button: Facing + */ + case (POPUP_BASEPERCENT | KN_BUTTON): + if (BaseGauge->Get_Value() != Scen.Percent) { + Scen.Percent = BaseGauge->Get_Value(); + Build_Base_To(Scen.Percent); + HidPage.Clear(); + Flag_To_Redraw(true); + } + input = KN_NONE; + break; + + default: + break; + } + + /* + ** Call parent's AI routine + */ + MouseClass::AI(input, x, y); +} + + +/*************************************************************************** + * MapEditClass::Draw_It -- overloaded Redraw routine * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Draw_It(bool forced) +{ + char const * label; + char buf[40]; + char const * tptr; + + MouseClass::Draw_It(forced); + + if (!Debug_Map) { + return; + } + + /* + ** Display the total value of all Tiberium on the map. + */ + Fancy_Text_Print("Tiberium=%ld ", 0, 0, GadgetClass::Get_Color_Scheme(), + BLACK, TPF_EFNT | TPF_NOSHADOW, TotalValue); + + /* + ** If there are no object controls displayed, just invoke parent's Redraw + ** and return. + */ + if (!Buttons) { + return; + } + + /* + ** Otherwise, if 'display' is set, invoke the parent's Redraw to refresh + ** the HIDPAGE; then, update the buttons & text labels onto HIDPAGE; + ** then invoke the parent's Redraw to blit the HIDPAGE to SEENPAGE. + */ + if (forced) { + + /* + ** Update the text labels + */ + if (CurrentObject.Count()) { + /* + ** Display the object's name & ID + */ + label = Text_String(CurrentObject[0]->Full_Name()); + tptr = label; + sprintf(buf, "%s (%d)", tptr, CurrentObject[0]->As_Target()); + + /* + ** print the label + */ + Fancy_Text_Print (buf, 160, 0, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_EFNT); + } + } +} + + +/*************************************************************************** + * MapEditClass::Mouse_Moved -- checks for mouse motion * + * * + * Reports whether the mouse has moved or not. This varies based on the * + * type of object currently selected. If there's an infantry object * + * selected, mouse motion counts even within a cell; for all other types,* + * mouse motion counts only if the mouse changes cells. * + * * + * The reason this routine is needed is to prevent Paint-Mode from putting* + * gobs of trees and such into the same cell if the mouse moves just * + * a little bit. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Mouse_Moved(void) +{ + static int old_mx = 0; + static int old_my = 0; + static CELL old_zonecell = 0; + const ObjectTypeClass * objtype = NULL; + bool retcode = false; + + /* + ** Return if no motion + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { + return(false); + } + + /* + ** Get a ptr to ObjectTypeClass + */ + if (PendingObject) { + objtype = PendingObject; + } else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } + + /* + ** Infantry: mouse moved if any motion at all + */ + if (objtype->What_Am_I() == RTTI_INFANTRYTYPE) { + retcode = true; + } else { + /* + ** Others: mouse moved only if cell changed + */ + if (old_zonecell!=ZoneCell) { + retcode = true; + } else { + retcode = false; + } + } + + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(retcode); +} + +int mainmenu_selection = -1; +int mainmenu_x = -1; +int mainmenu_y = -1; + +void Imgui_MapEdit_MainMenu(void) { + bool toolActive = false; + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(150, 200)); + ImGui::SetNextWindowPos(ImVec2(mainmenu_x, mainmenu_y)); + ImGui::Begin("MainMenu", &toolActive, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar); + if (ImGui::Button("New Scenario")) { + mainmenu_selection = 0; + } + if(ImGui::Button("Load Scenario")) { + mainmenu_selection = 1; + } + if (ImGui::Button("Save Scenario")) { + mainmenu_selection = 2; + } + if (ImGui::Button("Size Map")) { + mainmenu_selection = 3; + } + if (ImGui::Button("Add Game Object")) { + mainmenu_selection = 4; + } + if (ImGui::Button("Scenario Options")) { + mainmenu_selection = 5; + } + if (ImGui::Button("AI Options")) { + mainmenu_selection = 6; + } + if (ImGui::Button("Play Scenario")) { + mainmenu_selection = 7; + } + ImGui::End(); +} + + +/*************************************************************************** + * MapEditClass::Main_Menu -- main menu processor for map editor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/20/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Main_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + int rc; + + Imgui_Dialog_Function = Imgui_MapEdit_MainMenu; + mainmenu_selection = -1; + mainmenu_x = UserInput.Mouse.X; + mainmenu_y = UserInput.Mouse.Y; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + selection = mainmenu_selection; + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + Show_Mouse(); + + if (UnknownKey==KN_ESC || g_globalKeyNumType == KN_RMOUSE) { + break; + } + + if (selection == -1) + continue; + + /* + ** Process selection + */ + switch (selection) { + + /* + ** New scenario + */ + case 0: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (New_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 1; + } + process = false; + break; + + /* + ** Load scenario + */ + case 1: + if (Changed) { + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + if (Load_Scenario()==0) { + Scen.CarryOverMoney = 0; + Changed = 0; + } + process = false; + break; + + /* + ** Save scenario + */ + case 2: + if (Save_Scenario() == 0) { + Changed = 0; + } + process = false; + break; + + /* + ** Edit map size + */ + case 3: + if (Size_Map(MapCellX, MapCellY, MapCellWidth, MapCellHeight)==0) { + process = false; + Changed = 1; + } + break; + + /* + ** Add an object + */ + case 4: + if (Placement_Dialog() == 0) { + Start_Placement(); + process = false; + } + break; + + /* + ** Scenario options + */ + case 5: + if (Scenario_Dialog() == 0) { + Changed = 1; + process = false; + } + break; + + /* + ** Other options + */ + case 6: + AI_Menu(); + process = false; + break; + + /* + ** Test-drive this scenario + */ + case 7: + if (Changed) { + Imgui_Dialog_Function = nullptr; + + UserInput.Process_Input(); + + rc = WWMessageBox().Process("Save Changes?", TXT_YES, TXT_NO, TXT_CANCEL); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + if (rc == 2) return; + if (rc==0) { + if (Save_Scenario()!=0) { + break; + } else { + Changed = 0; + } + } + } + Changed = 0; + Debug_Map = false; + Imgui_Dialog_Function = nullptr; + Start_Scenario(Scen.ScenarioName); + return; + } + + mainmenu_selection = selection = -1; + break; + } + + Imgui_Dialog_Function = nullptr; + + /* + ** Restore the display: + ** - Clear HIDPAGE to erase any spurious drawing done by the menu system + ** - Invoke Flag_To_Redraw to tell DisplayClass to re-render the whole screen + ** - Invoke Redraw() to update the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::AI_Menu -- menu of AI options * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::AI_Menu(void) +{ + int selection; // option the user picks + bool process; // menu stays up while true + char const * _menus[MAX_AI_MENU_NUM + 1]; + + /* + ** Fill in menu strings + */ + _menus[0] = "Pre-Build a Base"; + _menus[1] = "Edit Triggers"; + _menus[2] = "Edit Teams"; + _menus[3] = NULL; + + /* + ** Main Menu loop + */ + Override_Mouse_Shape(MOUSE_NORMAL); // display default mouse cursor + process = true; + while (process) { + + /* + ** Invoke game callback, to update music + */ + Call_Back(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + /* + ** Invoke menu + */ + Hide_Mouse(); // Do_Menu assumes the mouse is already hidden + + selection = Do_Menu(&_menus[0], true); + Show_Mouse(); + + if (UnknownKey==KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) { + break; + } + + /* + ** Process selection + */ + switch (selection) { + /* + ** Pre-Build a Base + */ + case 0: + Start_Base_Building(); + process = false; + break; + + /* + ** Trigger Editing + */ + case 1: + Handle_Triggers(); + /* + ** Go into trigger placement mode + */ + if (CurTrigger) { + Start_Trigger_Placement(); + } + process = false; + break; + + /* + ** Team Editing + */ + case 2: + Handle_Teams("Teams"); + process = false; + break; + } + } +} + + +/*************************************************************************** + * MapEditClass::Verify_House -- is this objtype ownable by this house? * + * * + * INPUT: * + * house house to check * + * objtype ObjectTypeClass to check * + * * + * OUTPUT: * + * 0 = isn't ownable, 1 = it is * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Verify_House(HousesType house, ObjectTypeClass const * objtype) +{ + /* + ** Verify that new house can own this object + */ + return((objtype->Get_Ownable() & (1 << house)) != 0); +} + + +/*************************************************************************** + * MapEditClass::Cycle_House -- finds next valid house for object type * + * * + * INPUT: * + * objtype ObjectTypeClass ptr to get house for * + * curhouse current house value to start with * + * * + * OUTPUT: * + * HousesType that's valid for this object type * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + *=========================================================================*/ +HousesType MapEditClass::Cycle_House(HousesType curhouse, ObjectTypeClass const *) +{ + HousesType count; // prevents an infinite loop + + /* + ** Loop through all house types, starting with the one after 'curhouse'; + ** return the first one that's valid + */ + count = HOUSE_NONE; + while (1) { + + /* + ** Go to next house + */ + curhouse++; + if (curhouse == HOUSE_COUNT) { + curhouse = HOUSE_FIRST; + } + + /* + ** Count # iterations; don't go forever + */ + count++; + if (count == HOUSE_COUNT) { + curhouse = HOUSE_NONE; + break; + } + + /* + ** Break if this is a valid house + */ +// if (HouseClass::As_Pointer(curhouse) && Verify_House(curhouse, objtype)) { + break; +// } + } + + return(curhouse); +} + + +/*************************************************************************** + * MapEditClass::Fatal -- exits with error message * + * * + * INPUT: * + * code tells which message to display; this minimizes the * + * use of character strings in the code. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Fatal(int txt) +{ + //Prog_End(); + printf("%s\n", txt); + Emergency_Exit(EXIT_FAILURE); +} + + +bool MapEditClass::Scroll_Map(DirType facing, int & distance, bool really) +{ + if (Debug_Map) { + /* + ** The popup gadgets require the entire map to be redrawn if we scroll. + */ + if (really) { + Flag_To_Redraw(true); + } + } + return(MouseClass::Scroll_Map(facing, distance, really)); +} + + +#ifdef OBSOLETE +void MapEditClass::Flag_To_Redraw(bool complete) +{ + MouseClass::Flag_To_Redraw(complete); +} +#endif + + +void MapEditClass::Detach(ObjectClass * object) +{ + if (GrabbedObject == object) { + GrabbedObject = 0; + } +} + +bool MapEditClass::Get_Waypoint_Name(char wayptname[]) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 100, // dialog width + D_DIALOG_H = 56, // dialog height + D_DIALOG_X = ((320 - D_DIALOG_W) / 2), // centered x-coord + D_DIALOG_Y = ((200 - D_DIALOG_H) / 2), // centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 7, // margin width/height + + D_EDIT_W = D_DIALOG_W - (D_MARGIN * 2), + D_EDIT_H = 13, + D_EDIT_X = D_DIALOG_X + D_MARGIN, + D_EDIT_Y = D_DIALOG_Y + 20, + + D_BUTTON_X = D_DIALOG_X + D_MARGIN, + D_BUTTON_Y = D_DIALOG_Y + 40, + D_BUTTON_W = 40, + D_BUTTON_H = 13, + + D_CANCEL_X = D_DIALOG_X + 53, + D_CANCEL_Y = D_DIALOG_Y + 40, + D_CANCEL_W = 40, + D_CANCEL_H = 13, + + }; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_EDIT, + }; + + /* + ** Dialog variables + */ + bool cancel = false; // true = user cancels + wayptname[0] = 0; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TextButtonClass button (BUTTON_OK, TXT_OK, TPF_EBUTTON, D_BUTTON_X, D_BUTTON_Y, D_BUTTON_W); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W); + EditClass editbtn (BUTTON_EDIT, wayptname, 3, TPF_EFNT|TPF_NOSHADOW, D_EDIT_X, D_EDIT_Y, D_EDIT_W, -1, EditClass::ALPHANUMERIC); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &button; + cancelbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + editbtn.Set_Focus(); + + /* + ** Main Processing Loop. + */ + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else if (Main_Loop()) { + process = false; + cancel = true; + } + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); +// Draw_Caption(caption, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + } + + /* + ** Redraw the buttons. + */ + if (display) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime) { + firsttime = false; + editbtn.Set_Focus(); + editbtn.Flag_To_Redraw(); + } + + /* + ** If the key was pressed, then default to the appropriate + ** action button according to the style of this dialog box. + */ + if (input == KN_RETURN) { + input = (KeyNumType)(BUTTON_OK|KN_BUTTON); + } + + /* + ** Process input. + */ + switch (input) { + /* + ** Load: if load fails, present a message, and stay in the dialog + ** to allow the user to try another game + */ + case (BUTTON_OK | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + process = false; + cancel = false; + break; + + /* + ** ESC/Cancel: break + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + Hide_Mouse(); + SeenPage.Clear(); + GamePalette.Set(); + Show_Mouse(); + cancel = true; + process = false; + break; + + default: + break; + } + } + + Map.Flag_To_Redraw(true); + if (cancel) return(false); + + return(true); +} + +void MapEditClass::Update_Waypoint(int waypt_idx) +{ + CELL cell; + + /* + ** Unflag cell for this waypoint if there is one + */ + cell = Scen.Waypoint[waypt_idx]; + if (cell != -1) { + if (Scen.Waypoint[WAYPT_HOME] != cell && Scen.Waypoint[WAYPT_REINF] != cell) { + (*this)[cell].IsWaypoint = 0; + } + Flag_Cell(cell); + } + Scen.Waypoint[waypt_idx] = CurrentCell; + (*this)[CurrentCell].IsWaypoint = 1; + Changed = 1; + Flag_Cell(CurrentCell); +} + + +/*************************************************************************** + * MapEditClass::Read_INI -- overloaded Read_INI function * + * * + * Overloading this function gives the map editor a chance to initialize * + * certain values every time a new INI is read. * + * * + * INPUT: * + * buffer INI staging area * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/16/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Read_INI(CCINIClass & ini) +{ + /* + ** Invoke parent's Read_INI + */ + Mono_Printf("We are in Read_INI\n"); + + MouseClass::Read_INI(ini); + BaseGauge->Set_Value(Scen.Percent); + + Mono_Clear_Screen(); + Mono_Printf("Scen.Percent = %d", Scen.Percent); + +// BaseGauge->Set_Value(Scen.Percent); +} + + +void MapEditClass::Write_INI(CCINIClass & ini) +{ + MouseClass::Write_INI(ini); +// ini.Put_Int("Basic", "Percent", Scen.Percent); +} + +#endif + +#include "mapedsel.cpp" + diff --git a/code/redalert/mapedit.h b/code/redalert/mapedit.h new file mode 100644 index 0000000..caa2715 --- /dev/null +++ b/code/redalert/mapedit.h @@ -0,0 +1,319 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDIT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 14, 1994 * + * * + * Last Update : May 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * This class is derived from the normal display map class. It exists * + * only to allow editing and adding items to the map. * + *---------------------------------------------------------------------------------------------* + * House-setting functions: The editor contains several house maintenance routines: * + * Verify_House: tells if the given ObjectType can be owned by the given HousesType * + * Cycle_House: Finds the next valid house for the given ObjectType; used when a new object * + * can't be owned by the current editor HousesType. * + * Change_House: attempts to change the owner of the currently-selected object * + * Toggle_House: cycles the HousesType of a pending placement object * + * Set_House_Buttons: sets house buttons in accordance with the given HousesType * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MAPEDIT_H +#define MAPEDIT_H + +#include "function.h" +#include + +/* +** This is the maximum # of ObjectTypeClasses the editor has to deal with. +*/ +enum MapEdit1Enum { + MAX_EDIT_OBJECTS = // max # of ObjectTypeClasses allowed + (int)TEMPLATE_COUNT + + (int)OVERLAY_COUNT + + (int)SMUDGE_COUNT + + (int)TERRAIN_COUNT + + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)VESSEL_COUNT + + (int)STRUCT_COUNT, + + MAX_TEAM_CLASSES = // max # ObjectTypeClasses for a team + (int)UNIT_COUNT + + (int)INFANTRY_COUNT + + (int)AIRCRAFT_COUNT, + +// NUM_EDIT_MISSIONS = 6, // # missions that can be assigned an object + + NUM_EDIT_CLASSES = 9, // # different classes (templates, terrain, etc) + + MAX_MAIN_MENU_NUM = 8, + MAX_MAIN_MENU_LEN = 20, + + MAX_AI_MENU_NUM = 6, + MAX_AI_MENU_LEN = 20, + + POPUP_HOUSE_X=10, + POPUP_HOUSE_Y=100, + POPUP_HOUSE_W=60, + POPUP_HOUSE_H=(190-100), + +// POPUP_GDI_W = 50, +// POPUP_GDI_H = 9, +// POPUP_GDI_X = 10, +// POPUP_GDI_Y = 160, + +// POPUP_NOD_W = 50, +// POPUP_NOD_H = 9, +// POPUP_NOD_X = 10, +// POPUP_NOD_Y = 169, + +// POPUP_NEUTRAL_W = 50, +// POPUP_NEUTRAL_H = 9, +// POPUP_NEUTRAL_X = 10, +// POPUP_NEUTRAL_Y = 178, + +// POPUP_MULTI1_W = 25, +// POPUP_MULTI1_H = 9, +// POPUP_MULTI1_X = 10, +// POPUP_MULTI1_Y = 160, + +// POPUP_MULTI2_W = 25, +// POPUP_MULTI2_H = 9, +// POPUP_MULTI2_X = 35, +// POPUP_MULTI2_Y = 160, + +// POPUP_MULTI3_W = 25, +// POPUP_MULTI3_H = 9, +// POPUP_MULTI3_X = 10, +// POPUP_MULTI3_Y = 169, + +// POPUP_MULTI4_W = 25, +// POPUP_MULTI4_H = 9, +// POPUP_MULTI4_X = 35, +// POPUP_MULTI4_Y = 169, + + POPUP_MISSION_W = 80, + POPUP_MISSION_H = 40, + POPUP_MISSION_X = 70, + POPUP_MISSION_Y = 150, + + POPUP_FACEBOX_W = 30, + POPUP_FACEBOX_H = 30, + POPUP_FACEBOX_X = 160, + POPUP_FACEBOX_Y = 160, + + POPUP_HEALTH_W = 50, + POPUP_HEALTH_H = 10, + POPUP_HEALTH_X = 200, + POPUP_HEALTH_Y = 170, + + POPUP_BASE_W = 50, + POPUP_BASE_H = 8, + POPUP_BASE_X = 300 - 50, + POPUP_BASE_Y = 0 +}; + +/* +** These are the button ID's for the pop-up object-editing gizmos. +** The house button ID's must be sequential, with a 1-to-1 correspondence to +** the HousesType values. +*/ +enum MapEditButtonIDEnum{ + POPUP_SPAIN=500, + POPUP_FIRST=POPUP_SPAIN, + POPUP_GREECE, + POPUP_USSR, + POPUP_ENGLAND, + POPUP_ITALY, + POPUP_GERMANY, + POPUP_FRANCE, + POPUP_TURKEY, + POPUP_HOUSELIST, // House selection list. + POPUP_SELLABLE, // Allowed to sell. + POPUP_REBUILDABLE, // Allowed to rebuild. + POPUP_MISSIONLIST, // list box for missions + POPUP_HEALTHGAUGE, // health of object + POPUP_FACINGDIAL, // object's facing + POPUP_BASEPERCENT, // Base's percent-built slider + MAP_AREA, // map as a click-able thingy + BUTTON_FLAG=0x8000 +}; + + +class TeamTypeClass; + +class MapEditClass : public MouseClass +{ + /* + ** Public Interface + */ + public: + + /* + ** mapedit.cpp + */ + MapEditClass(void); + MapEditClass(NoInitClass const & x) : MouseClass(x) {}; + bool Get_Waypoint_Name(char wayptname[]); + void Update_Waypoint(int waypt_index); + + virtual void One_Time(void); // One-time init + virtual void Init_IO(void); // Inits button list + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool forced = true); + virtual bool Scroll_Map(DirType facing, int & distance, bool really=true); + virtual void Read_INI(CCINIClass & ini); + virtual void Write_INI(CCINIClass & ini); + virtual void Detach(ObjectClass * object); + void Detach(TARGET target, bool all=true) {MouseClass::Detach(target, all);} + void Clear_List(void); + bool Add_To_List(ObjectTypeClass const *object); + void Main_Menu(void); + void AI_Menu(void); + bool Mouse_Moved(void); + bool Verify_House(HousesType house, ObjectTypeClass const * objtype); + HousesType Cycle_House(HousesType curhouse, ObjectTypeClass const * objtype); +// int Trigger_Needs_Team(TriggerClass *trigger); + void Fatal(int txt); + + /* + ** mapeddlg.cpp + */ + int New_Scenario(void); + int Load_Scenario(void); + int Save_Scenario(void); + int Pick_Scenario(char const * caption, int & scen_nump, ScenarioPlayerType & playerp, ScenarioDirType & dirp, ScenarioVarType & varp); + int Size_Map(int x, int y, int w, int h); + int Scenario_Dialog(void); + void Handle_Triggers(void); + int Select_Trigger(void); + + /* + ** mapedplc.cpp + */ + int Placement_Dialog(void); + void Start_Placement(void); + int Place_Object(void); + void Cancel_Placement(void); + void Place_Next(void); + void Place_Prev(void); + void Place_Next_Category(void); + void Place_Prev_Category(void); + void Place_Home(void); + void Toggle_House(void); + void Set_House_Buttons(HousesType house, GadgetClass *btnlist, int base_id); + void Start_Trigger_Placement(void); + void Stop_Trigger_Placement(void); + void Place_Trigger(void); + void Start_Base_Building(void); + void Cancel_Base_Building(void); + void Build_Base_To(int percent); + + /* + ** mapedsel.cpp + */ + int Select_Object(void); + void Select_Next(void); + void Popup_Controls(void); + void Grab_Object(void); + int Move_Grabbed_Object(void); + bool Change_House(HousesType newhouse); + + /* + ** mapedtm.cpp + */ + void Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house); + void Handle_Teams(char const * caption); + int Select_Team(char const * caption); + int Team_Members(HousesType house); + + /* + ** Private Interface + */ + private: + /* + ** This is the last-requested variation of a loaded/saved/new scenario. + */ +// ScenarioVarType ScenVar; + + std::vector Objects[NUM_EDIT_CLASSES]; + + /* + ** Last-selected object to place, and last-selected house of object + */ + //int LastChoice; // index of item user picked last + HousesType LastHouse; // house of last item picked + + /* + ** Variables for grabbing/moving objects + */ + ObjectClass * GrabbedObject; // object "grabbed" with mouse + CELL GrabOffset; // offset to grabbed obj's upper-left + unsigned long LastClickTime; // time of last LMOUSE click + + /* + ** The "current" trigger for point-and-click trigger setting + */ + TriggerTypeClass * CurTrigger; // current trigger + + /* + ** The "current" team type for editing & associating with a trigger + */ + TeamTypeClass * CurTeam; // current team + + /* + ** Bitfields for flags & such + */ + unsigned Changed : 1; // 1 = changes are unsaved + unsigned LMouseDown : 1; // 1 = left mouse is held down + unsigned BaseBuilding : 1; // 1 = we're in base-building mode + + /* + ** Variables for pre-building a base + */ +// int BasePercent; // Percentage the base will be built + + /* + ** Variables for supporting the object-editing controls at screen bottom + */ + ListClass * HouseList; + ListClass * MissionList; + TriColorGaugeClass *HealthGauge; + Dial8Class *FacingDial; + ControlClass *MapArea; + TextLabelClass *HealthText; + TextButtonClass * Sellable; + TextButtonClass * Rebuildable; + static char HealthBuf[20]; + GaugeClass *BaseGauge; + TextLabelClass *BaseLabel; +public: + static MissionType MapEditMissions[]; +}; + +#endif diff --git a/code/redalert/mapedplc.cpp b/code/redalert/mapedplc.cpp new file mode 100644 index 0000000..6016eae --- /dev/null +++ b/code/redalert/mapedplc.cpp @@ -0,0 +1,1952 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDPLC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDPLC.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-placement routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Build_Base_To -- builds the AI base to the given percent* + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * MapEditClass::Cancel_Placement -- cancels placement mode * + * MapEditClass::Place_Home -- homes the placement object * + * MapEditClass::Place_Next -- while placing object, goes to next * + * MapEditClass::Place_Next_Category -- places next object category * + * MapEditClass::Place_Object -- attempts to place the current object * + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * MapEditClass::Place_Prev_Category -- places previous object category * + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * MapEditClass::Start_Base_Building -- starts base-building mode * + * MapEditClass::Start_Placement -- enters placement mode * + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode* + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * MapEditClass::Toggle_House -- toggles current placement object's house* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "function.h" + +#ifdef SCENARIO_EDITOR +int placementeditor_faction = 0; +char* placementeditor_housenames[256]; +int placementeditor_numhouses = 0; +bool placementeditor_catagory[9] = { false, false, false, false, false, false, false, false, false }; +int placementeditor_ObjCount = 0; +bool placementeditor_process = true; +bool placementeditor_cancel = false; // true = user cancels +int LastChoice = 0; +static int typeindex = 0; // index of class type + +void ImGui_Placement_Editor(void) { + bool toolActive; + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(480, 220)); + ImGui::SetNextWindowPos(ImVec2(0, 150)); + ImGui::Begin("Place Object", &toolActive, ImGuiWindowFlags_AlwaysAutoResize /*| ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground*/); + ImGui::Columns(3, "outer", true); + ImGui::LabelText("Test", "Object Name"); + ImGui::NewLine(); + ImGui::SameLine(45); + if (ImGui::Button("Prev")) { + LastChoice--; + if (LastChoice < 0) { + LastChoice = placementeditor_ObjCount -1; + } + } + ImGui::SameLine(); + if (ImGui::Button("Next")) { + LastChoice++; + if (LastChoice == placementeditor_ObjCount) { + LastChoice = 0; + } + } + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::NewLine(); + ImGui::SameLine(45); + if (ImGui::Button("Ok")) { + placementeditor_process = false; + placementeditor_cancel = false; + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + placementeditor_process = false; + placementeditor_cancel = true; + } + ImGui::NextColumn(); + ImGui::ListBox("", &placementeditor_faction, &placementeditor_housenames[0], placementeditor_numhouses); + ImGui::NextColumn(); + //ImGui::NewLine(); + if (ImGui::Checkbox("Template", &placementeditor_catagory[0])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[0] = true; + } + if (ImGui::Checkbox("Overlay", &placementeditor_catagory[1])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[1] = true; + } + if (ImGui::Checkbox("Smudge", &placementeditor_catagory[2])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[2] = true; + } + if (ImGui::Checkbox("Terrain", &placementeditor_catagory[3])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[3] = true; + } + if (ImGui::Checkbox("Units", &placementeditor_catagory[4])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[4] = true; + } + if (ImGui::Checkbox("Infantry", &placementeditor_catagory[5])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[5] = true; + } + if (ImGui::Checkbox("Ships", &placementeditor_catagory[6])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[6] = true; + } + if (ImGui::Checkbox("Building", &placementeditor_catagory[7])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[7] = true; + } + if (ImGui::Checkbox("Aircraft", &placementeditor_catagory[8])) { + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[8] = true; + } + ImGui::Columns(1); + ImGui::End(); +} + +/*************************************************************************** + * MapEditClass::Placement_Dialog -- adds an object to the scenario * + * * + * This function sets LastChoice & LastHouse to the values chosen * + * by the user. It's up to the caller to call Start_Placement to enter * + * placement mode. * + * This routine does not modify PendingObject or PendingHouse. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ [GDI] [NOD] [Neutral] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ [Template] ³ * + * ³ ³ ³ [Overlay ] ³ * + * ³ ³ ³ [Smudge ] ³ * + * ³ ³ ³ [Terrain ] ³ * + * ³ ³ (Object picture) ³ [Unit ] ³ * + * ³ ³ ³ [Infantry] ³ * + * ³ ³ ³ [Aircraft] ³ * + * ³ ³ ³ [Building] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÚÄÄÄÄÄÄ¿ ³ * + * ³ [<-] [->] ³(Grid)³ ³ * + * ³ ³ ³ ³ * + * ³ [OK] [Cancel] ÀÄÄÄÄÄÄÙ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/21/1994 BR : Created. * + * 12/13/1995 JLB : Fixed house buttons to handle expanded house list. * + * 05/12/1996 JLB : Handles hi-res. * + *=========================================================================*/ +int MapEditClass::Placement_Dialog(void) +{ + HousesType house; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + placementeditor_faction = 0; + placementeditor_numhouses = 0; + placementeditor_process = 0; + //placementeditor_ObjCount = ObjCount; + memset(&placementeditor_catagory[0], 0, sizeof(placementeditor_catagory)); + placementeditor_catagory[0] = true; + placementeditor_cancel = false; + placementeditor_process = true; + + Imgui_Dialog_Function = ImGui_Placement_Editor; + + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 200, + D_DIALOG_H = 150, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT8_H = 11, + D_MARGIN = 7, + + D_PICTURE_W = 152, // must be divisible by 8! + D_PICTURE_H = 105, + D_PICTURE_X = -20, // must start on a byte boundary! + D_PICTURE_Y = D_DIALOG_Y + D_MARGIN + D_TXT8_H + D_MARGIN, + D_PICTURE_CX = D_PICTURE_X + D_PICTURE_W / 2, + + D_GDI_W = 65, + D_GDI_H = 9, + D_GDI_X = D_PICTURE_X+D_PICTURE_W+5, + D_GDI_Y = D_PICTURE_Y, + + D_LEFT_W = 45, + D_LEFT_H = 9, + D_LEFT_X = D_PICTURE_CX - 5 - D_LEFT_W, + D_LEFT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_RIGHT_W = 45, + D_RIGHT_H = 9, + D_RIGHT_X = D_PICTURE_CX + 5, + D_RIGHT_Y = D_PICTURE_Y + D_PICTURE_H + D_MARGIN, + + D_TEMPLATE_W = 70, + D_TEMPLATE_H = 9, + D_TEMPLATE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TEMPLATE_W - 30, + D_TEMPLATE_Y = D_PICTURE_Y, + + D_OVERLAY_W = 70, + D_OVERLAY_H = 9, + D_OVERLAY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_OVERLAY_W - 30, + D_OVERLAY_Y = D_TEMPLATE_Y + D_TEMPLATE_H, + + D_SMUDGE_W = 70, + D_SMUDGE_H = 9, + D_SMUDGE_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_SMUDGE_W - 30, + D_SMUDGE_Y = D_OVERLAY_Y + D_OVERLAY_H, + + D_TERRAIN_W = 70, + D_TERRAIN_H = 9, + D_TERRAIN_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_TERRAIN_W - 30, + D_TERRAIN_Y = D_SMUDGE_Y + D_SMUDGE_H, + + D_UNIT_W = 70, + D_UNIT_H = 9, + D_UNIT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_UNIT_W - 30, + D_UNIT_Y = D_TERRAIN_Y + D_TERRAIN_H, + + D_INFANTRY_W = 70, + D_INFANTRY_H = 9, + D_INFANTRY_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_INFANTRY_W - 30, + D_INFANTRY_Y = D_UNIT_Y + D_UNIT_H, + + D_AIRCRAFT_W = 70, + D_AIRCRAFT_H = 9, + D_AIRCRAFT_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIRCRAFT_W - 30, + D_AIRCRAFT_Y = D_INFANTRY_Y + D_INFANTRY_H, + + D_BUILDING_W = 70, + D_BUILDING_H = 9, + D_BUILDING_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_BUILDING_W - 30, + D_BUILDING_Y = D_AIRCRAFT_Y + D_AIRCRAFT_H, + + D_AIR_W = 70, + D_AIR_H = 9, + D_AIR_X = D_DIALOG_X + D_DIALOG_W - D_MARGIN - D_AIR_W - 30, + D_AIR_Y = D_BUILDING_Y + D_BUILDING_H, + + D_OK_W = 45, + D_OK_H = 9, + D_OK_X = D_PICTURE_CX - D_OK_W - 5, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN - 15, + + D_CANCEL_W = 45, + D_CANCEL_H = 9, + D_CANCEL_X = D_PICTURE_CX + 5, + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN - 15, + + GRIDSIZE = 10, + GRIDBLOCK_W = 3, + GRIDBLOCK_H = 3, + D_GRID_X = D_DIALOG_X + D_DIALOG_W - (GRIDSIZE * GRIDBLOCK_W) - D_MARGIN - 35, + D_GRID_Y = D_DIALOG_Y + D_DIALOG_H - (GRIDSIZE * GRIDBLOCK_H) - D_MARGIN - 35, + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_GDI=100, + BUTTON_HOUSE, + BUTTON_NEXT, + BUTTON_PREV, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_TEMPLATE, + BUTTON_OVERLAY, + BUTTON_SMUDGE, + BUTTON_TERRAIN, + BUTTON_UNIT, + BUTTON_INFANTRY, + BUTTON_AIRCRAFT, + BUTTON_BUILDING, + BUTTON_AIR, + }; + + /* + ** Dialog variables + */ + const ObjectTypeClass * curobj; // Working object pointer. + int x,y; // for drawing the grid + KeyNumType input; // user input + short const * occupy; // ptr into object's OccupyList + int cell; // cell index for parsing OccupyList + int i; + + /* + ** Buttons + */ + ControlClass * commands; + + ListClass housebtn(BUTTON_HOUSE, + D_GDI_X, D_GDI_Y, 60, 8*16, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + //housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + placementeditor_housenames[house] = HouseTypeClass::As_Reference(house).IniName; + placementeditor_numhouses++; + } + house = HOUSE_FIRST; + + //TextButtonClass nextbtn(BUTTON_NEXT, TXT_RIGHT, TPF_EBUTTON, D_RIGHT_X, D_RIGHT_Y, D_RIGHT_W, D_RIGHT_H); + //TextButtonClass prevbtn(BUTTON_PREV, TXT_LEFT, TPF_EBUTTON, D_LEFT_X, D_LEFT_Y, D_LEFT_W, D_LEFT_H); + //TextButtonClass okbtn(BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + //TextButtonClass cancelbtn(BUTTON_CANCEL, "Cancel", TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + //TextButtonClass templatebtn(BUTTON_TEMPLATE, "Template", TPF_EBUTTON, D_TEMPLATE_X, D_TEMPLATE_Y, D_TEMPLATE_W, D_TEMPLATE_H); + //TextButtonClass overlaybtn(BUTTON_OVERLAY, "Overlay", TPF_EBUTTON, D_OVERLAY_X, D_OVERLAY_Y, D_OVERLAY_W, D_OVERLAY_H); + //TextButtonClass smudgebtn(BUTTON_SMUDGE, "Smudge", TPF_EBUTTON, D_SMUDGE_X, D_SMUDGE_Y, D_SMUDGE_W, D_SMUDGE_H); + //TextButtonClass terrainbtn(BUTTON_TERRAIN, "Terrain", TPF_EBUTTON, D_TERRAIN_X, D_TERRAIN_Y, D_TERRAIN_W, D_TERRAIN_H); + //TextButtonClass unitbtn(BUTTON_UNIT, "Unit", TPF_EBUTTON, D_UNIT_X, D_UNIT_Y, D_UNIT_W, D_UNIT_H); + //TextButtonClass infantrybtn(BUTTON_INFANTRY, "Infantry", TPF_EBUTTON, D_INFANTRY_X, D_INFANTRY_Y, D_INFANTRY_W, D_INFANTRY_H); + //TextButtonClass aircraftbtn(BUTTON_AIRCRAFT, "Ships", TPF_EBUTTON, D_AIRCRAFT_X, D_AIRCRAFT_Y, D_AIRCRAFT_W, D_AIRCRAFT_H); + //TextButtonClass buildingbtn(BUTTON_BUILDING, "Building", TPF_EBUTTON, D_BUILDING_X, D_BUILDING_Y, D_BUILDING_W, D_BUILDING_H); + //TextButtonClass airbtn(BUTTON_AIR, "Aircraft", TPF_EBUTTON, D_AIR_X, D_AIR_Y, D_AIR_W, D_AIR_H); + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. (Skip aircraft, since they won't be used in the editor.) + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + if (Objects[0].size() <= 0) + return -1; + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + LastChoice = 0; + curobj = Objects[0][0]; // current object to choose + + //commands = &nextbtn; + //housebtn.Add_Tail(*commands); + //prevbtn.Add_Tail(*commands); + //okbtn.Add_Tail(*commands); + //cancelbtn.Add_Tail(*commands); + //templatebtn.Add_Tail(*commands); + //overlaybtn.Add_Tail(*commands); + //smudgebtn.Add_Tail(*commands); + //terrainbtn.Add_Tail(*commands); + //unitbtn.Add_Tail(*commands); + //infantrybtn.Add_Tail(*commands); + //aircraftbtn.Add_Tail(*commands); + //buildingbtn.Add_Tail(*commands); + //airbtn.Add_Tail(*commands); + + /* + ** Make sure the recorded house selection matches the house list + ** box selection. + */ + //LastHouse = HousesType(housebtn.Current_Index()); + + /* + ** Main processing loop + */ + bool display = true; + while (placementeditor_process) { + placementeditor_ObjCount = Objects[typeindex].size(); + + /* + ** Invoke game callback + */ + Call_Back(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + /* + ** Refresh display if needed + */ + //if (display) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + // Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + // Draw_Caption(TXT_PLACE_OBJECT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); +// jmarshall - temp render a black background here so the tool works. + LogicPage->Fill_Rect(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_X + D_DIALOG_W, D_DIALOG_Y + D_DIALOG_H, 0); +// jamrshall end + + /* + ** Display the current object: + ** - save the current window dimensions + ** - adjust the window size to the actual drawable area + ** - draw the shape + ** - reset the window dimensions + */ + WindowList[WINDOW_EDITOR][WINDOWX] = D_PICTURE_X; + WindowList[WINDOW_EDITOR][WINDOWY] = D_PICTURE_Y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + // Draw_Box(D_PICTURE_X, D_PICTURE_Y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, false); + curobj->Display(WinW/2, WinH>>1, WINDOW_EDITOR, LastHouse); +// curobj->Display(WinW<<2, WinH>>1, WINDOW_EDITOR, LastHouse); + + /* + ** Erase the grid + */ + LogicPage->Fill_Rect(D_GRID_X - GRIDBLOCK_W * 2, D_GRID_Y, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, BLACK); + + /* + ** Draw a box for every cell occupied + */ + occupy = curobj->Occupy_List(); + while ( (*occupy) != REFRESH_EOL) { + cell = (*occupy); + occupy++; + x = D_GRID_X + ((cell % MAP_CELL_W) * GRIDBLOCK_W); + y = D_GRID_Y + ((cell / MAP_CELL_W) * GRIDBLOCK_H); + LogicPage->Fill_Rect(x, y, x + GRIDBLOCK_W - 1, y + GRIDBLOCK_H - 1, scheme->Bright); + } + + /* + ** Draw the grid itself + */ + for (y = 0; y <= GRIDSIZE; y++) { + for (x = 0; x <= GRIDSIZE; x++) { + LogicPage->Draw_Line(D_GRID_X + x * GRIDBLOCK_W, D_GRID_Y, + D_GRID_X + x * GRIDBLOCK_W, + D_GRID_Y + GRIDSIZE * GRIDBLOCK_H, scheme->Shadow); + } + LogicPage->Draw_Line(D_GRID_X, D_GRID_Y + y * GRIDBLOCK_H, + D_GRID_X + GRIDSIZE * GRIDBLOCK_W, D_GRID_Y + y * GRIDBLOCK_H, + scheme->Shadow); + } + + /* + ** Print the object's label from the class's Full_Name(). + ** Warning: Text_String returns an EMS pointer, so standard string + ** functions won't work! + */ + Fancy_Text_Print (curobj->Full_Name(), + D_PICTURE_CX, D_PICTURE_Y + D_MARGIN, scheme, TBLACK, + TPF_CENTER | TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw buttons + ** Figure out which class category we're in & highlight that button + ** This updates 'typeindex', which is used below, and it also updates + ** the category button states. + */ + i = 0; + //for (typeindex = 0; typeindex < NUM_EDIT_CLASSES; typeindex++) { + // i += NumType[typeindex]; + // if (LastChoice < i) break; + //} + //templatebtn.Turn_Off(); + //overlaybtn.Turn_Off(); + //smudgebtn.Turn_Off(); + //terrainbtn.Turn_Off(); + //unitbtn.Turn_Off(); + //infantrybtn.Turn_Off(); + //aircraftbtn.Turn_Off(); + //airbtn.Turn_Off(); + //buildingbtn.Turn_Off(); + //switch (typeindex + BUTTON_TEMPLATE) { + // case BUTTON_TEMPLATE: + // templatebtn.Turn_On(); + // break; + // + // case BUTTON_OVERLAY: + // overlaybtn.Turn_On(); + // break; + // + // case BUTTON_SMUDGE: + // smudgebtn.Turn_On(); + // break; + // + // case BUTTON_TERRAIN: + // terrainbtn.Turn_On(); + // break; + // + // case BUTTON_UNIT: + // unitbtn.Turn_On(); + // break; + // + // case BUTTON_INFANTRY: + // infantrybtn.Turn_On(); + // break; + // + // case BUTTON_AIRCRAFT: + // aircraftbtn.Turn_On(); + // break; + // + // case BUTTON_AIR: + // airbtn.Turn_On(); + // break; + // + // case BUTTON_BUILDING: + // buildingbtn.Turn_On(); + // break; + //} + + /* + ** Redraw buttons + */ + //commands->Draw_All(); + Show_Mouse(); + display = false; + + // } + + display = true; + + if (placementeditor_catagory[0]) { + typeindex = 0; + } + else if (placementeditor_catagory[1]) { + typeindex = 1; + } + else if (placementeditor_catagory[2]) { + typeindex = 2; + } + else if (placementeditor_catagory[3]) { + typeindex = 3; + } + else if (placementeditor_catagory[4]) { + typeindex = 4; + } + else if (placementeditor_catagory[5]) { + typeindex = 5; + } + else if (placementeditor_catagory[6]) { + typeindex = 6; + } + else if (placementeditor_catagory[7]) { + typeindex = 7; + } + else if (placementeditor_catagory[8]) { + typeindex = 8; + } + + LastHouse = (HousesType)placementeditor_faction; + curobj = Objects[typeindex][LastChoice]; + + /* + ** Get user input + */ + //input = commands->Input(); + // + ///* + //** Process user input + //*/ + //switch (input) { + // + // /* + // ** GDI House + // */ + // case (BUTTON_HOUSE | KN_BUTTON): + // house = HousesType(housebtn.Current_Index()); + // + // /* + // ** Set flags & buttons + // */ + // LastHouse = house; + // display = true; + // break; + // + // /* + // ** Next in list + // */ + // case (KN_RIGHT): + // case (BUTTON_NEXT | KN_BUTTON): + // /* + // ** Increment to next obj + // */ + // LastChoice++; + // if (LastChoice == ObjCount) { + // LastChoice = 0; + // } + // curobj = Objects[LastChoice]; + // + // nextbtn.Turn_Off(); + // display = true; + // break; + // + // /* + // ** Previous in list + // */ + // case (KN_LEFT): + // case (BUTTON_PREV | KN_BUTTON): + // + // /* + // ** Decrement to prev obj + // */ + // LastChoice--; + // if (LastChoice < 0) { + // LastChoice = ObjCount-1; + // } + // curobj = Objects[LastChoice]; + // prevbtn.Turn_Off(); + // display = true; + // break; + // + // /* + // ** Select a class type + // */ + // case (BUTTON_TEMPLATE | KN_BUTTON): + // case (BUTTON_OVERLAY | KN_BUTTON): + // case (BUTTON_SMUDGE | KN_BUTTON): + // case (BUTTON_TERRAIN | KN_BUTTON): + // case (BUTTON_UNIT | KN_BUTTON): + // case (BUTTON_INFANTRY | KN_BUTTON): + // case (BUTTON_AIRCRAFT | KN_BUTTON): + // case (BUTTON_BUILDING | KN_BUTTON): + // case (BUTTON_AIR | KN_BUTTON): + // + // /* + // ** Find index of class + // */ + // typeindex = input - (BUTTON_TEMPLATE | KN_BUTTON); + // + // /* + // ** If no objects of that type, do nothing + // */ + // if (NumType[typeindex]==0) { + // display = true; + // break; + // } + // + // /* + // ** Set current object + // */ + // LastChoice = TypeOffset[typeindex]; + // curobj = Objects[LastChoice]; + // display = true; + // break; + // + // /* + // ** Next category + // */ + // case KN_PGDN: + // typeindex++; + // if (typeindex==NUM_EDIT_CLASSES) { + // typeindex = 0; + // } + // + // /* + // ** Set current object + // */ + // LastChoice = TypeOffset[typeindex]; + // curobj = Objects[LastChoice]; + // display = true; + // break; + // + // /* + // ** Previous category + // */ + // case KN_PGUP: + // typeindex--; + // if (typeindex < 0) { + // typeindex = NUM_EDIT_CLASSES - 1; + // } + // + // /* + // ** Set current object + // */ + // LastChoice = TypeOffset[typeindex]; + // curobj = Objects[LastChoice]; + // display = true; + // break; + // + // /* + // ** Jump to 1st choice + // */ + // case KN_HOME: + // LastChoice = 0; + // + // /* + // ** Set current object + // */ + // curobj = Objects[LastChoice]; + // display = true; + // break; + // + // /* + // ** OK + // */ + // case (KN_RETURN): + // case (BUTTON_OK | KN_BUTTON): + // cancel = false; + // process = false; + // break; + // + // /* + // ** Cancel + // */ + // case (KN_ESC): + // case (BUTTON_CANCEL | KN_BUTTON): + // cancel = true; + // process = false; + // break; + // + // default: + // break; + //} + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (placementeditor_cancel) { + return(-1); + } + + return(0); +} + + +/*************************************************************************** + * MapEditClass::Start_Placement -- enters placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Placement(void) +{ + + /* + ** Initialize addable objects list; we must do this every time in case one + ** of the object pools has become exhausted; that object won't be available + ** for adding. These must be added in the same order expected by the + ** object selection dialog (same as button order). + */ + Clear_List(); + TemplateTypeClass::Prep_For_Add(); + OverlayTypeClass::Prep_For_Add(); + SmudgeTypeClass::Prep_For_Add(); + TerrainTypeClass::Prep_For_Add(); + UnitTypeClass::Prep_For_Add(); + InfantryTypeClass::Prep_For_Add(); + VesselTypeClass::Prep_For_Add(); + BuildingTypeClass::Prep_For_Add(); + AircraftTypeClass::Prep_For_Add(); + + /* + ** Create the placement object: + ** - For normal placement mode, use the last-used index into Objects + ** (LastChoice), and the last-used house (LastHouse). + ** - For base-building mode, force the object to be a building, and use the + ** House specified in the Base object + */ + if (!BaseBuilding) { + if (LastChoice >= Objects[typeindex].size()) { + LastChoice = Objects[typeindex].size() - 1; + } + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } else { + //if (LastChoice < TypeOffset[7]) { + // LastChoice = TypeOffset[7]; + //} + if (LastChoice >= Objects[typeindex].size()) { + LastChoice = Objects[typeindex].size() - 1; + } + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse = Base.House; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(LastHouse)); + } + + /* + ** Error if no more objects available + */ + if (!PendingObjectPtr) { + WWMessageBox().Process("No more objects of this type available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + PendingObject = NULL; + if (BaseBuilding) { + Cancel_Base_Building(); + } + return; + } + + /* + ** Set the placement cursor + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(PendingObject->Occupy_List()); +} + + +/*************************************************************************** + * MapEditClass::Place_Object -- attempts to place the current object * + * * + * Placement of "real" objects is simply checked via their Unlimbo routine.* + * Placement of templates is more complex: * + * - for every cell in the template's OccupyList, check for objects * + * already in that cell by looking at the cell's OccupyList & * + * OverlapList * + * - "lift" all the objects in the cell by Mark'ing them * + * - temporarily place the template in that cell * + * - try to Unlimbo all the objects that were in the cell. If any * + * objects fail to Unlimbo onto that template, the template cannot * + * be placed here * + * * + * It is assumed that the object being placed is a "new" object; the * + * object's strength & mission are not set during Unlimbo. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, -1 = unable to place * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Place_Object(void) +{ + CELL template_cell; // cell being checked for template + COORDINATE obj_coord; // coord of occupier object + int okflag; // OK to place a template? + short const * occupy; // ptr into template's OccupyList + ObjectClass * occupier; // occupying object + TemplateType save_ttype; // for saving cell's TType + unsigned char save_ticon; // for saving cell's TIcon +// BaseNodeClass node; // for adding to an AI Base + + /* + ** Placing a template: + ** - first lift up any objects in the cell + ** - place the template, and try to replace the objects; if they won't go + ** back, the template can't go there + */ + //ScenarioInit++; + if (PendingObject->What_Am_I() == RTTI_TEMPLATETYPE) { + + /* + ** Loop through all cells this template will occupy + */ + okflag = true; + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Check this cell for an occupier + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + if ((*this)[template_cell].Cell_Occupier()) { + occupier = (*this)[template_cell].Cell_Occupier(); + + /* + ** Save object's coordinates + */ + obj_coord = occupier->Coord; + + /* + ** Place the object in limbo + */ + occupier->Mark(MARK_UP); + + /* + ** Set the cell's template values + */ + save_ttype = (*this)[template_cell].TType; + save_ticon = (*this)[template_cell].TIcon; + (*this)[template_cell].TType = + ((TemplateTypeClass *)PendingObject)->Type; + (*this)[template_cell].TIcon = Cell_X(*occupy) + Cell_Y(*occupy) * + ((TemplateTypeClass *)PendingObject)->Width; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Try to put the object back down + */ + if (occupier->Can_Enter_Cell(Coord_Cell(obj_coord)) != MOVE_OK) { + okflag = false; + } + + /* + ** Put everything back the way it was + */ + (*this)[template_cell].TType = save_ttype; + (*this)[template_cell].TIcon = save_ticon; + (*this)[template_cell].Recalc_Attributes(); + + /* + ** Major error if can't replace the object now + */ + occupier->Mark(MARK_DOWN); + } + occupy++; + } + + /* + ** If it's still OK after ALL THAT, place the template + */ + if (okflag) { + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Loop through all cells occupied by this template, and clear the + ** smudge & overlay. + */ + occupy = PendingObject->Occupy_List(); + while ((*occupy) != REFRESH_EOL) { + + /* + ** Get cell for this occupy item + */ + template_cell = (ZoneCell+ZoneOffset) + (*occupy); + + /* + ** Clear smudge & overlay + */ + (*this)[template_cell].Overlay = OVERLAY_NONE; + (*this)[template_cell].OverlayData = 0; + (*this)[template_cell].Smudge = SMUDGE_NONE; + + /* + ** make adjacent cells recalc attrib's + */ + (*this)[template_cell].Recalc_Attributes(); + (*this)[template_cell].Wall_Update(); + (*this)[template_cell].Concrete_Calc(); + + occupy++; + } + + /* + ** Set flags etc + */ + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + TotalValue = Overpass(); + Flag_To_Redraw(false); + return(0); + } + + /* + ** Failure to deploy results in a returned failure code. + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Not OK; return error + */ + //ScenarioInit--; + return(-1); + } + + /* + ** Placing infantry: Infantry can go into cell sub-positions, so find the + ** sub-position closest to the mouse & put him there + */ + if (PendingObject->What_Am_I() == RTTI_INFANTRYTYPE) { + + /* + ** Find cell sub-position + */ + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + obj_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + } else { + obj_coord = NULL; + } + + /* + ** No free spots; don't place the object + */ + if (obj_coord == NULL) { + //ScenarioInit--; + return(-1); + } + + /* + ** Unlimbo the object + */ + if (PendingObjectPtr->Unlimbo(obj_coord)) { + ((InfantryClass *)PendingObjectPtr)->Set_Occupy_Bit(obj_coord); +// Map[obj_coord].Flag.Composite |= +// (1 << CellClass::Spot_Index(obj_coord)); + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + //ScenarioInit--; + return(-1); + } + + /* + ** Placing an object + */ + if (PendingObjectPtr->Unlimbo(Cell_Coord(ZoneCell + ZoneOffset))) { + + /* + ** Update the Tiberium computation if we're placing an overlay + */ + if (PendingObject->What_Am_I() == RTTI_OVERLAYTYPE && + ((OverlayTypeClass *)PendingObject)->IsTiberium) { + TotalValue = Overpass(); + Flag_To_Redraw(false); + } + + /* + ** If we're building a base, add this building to the base's Node list. + */ + if (BaseBuilding && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE) { +// node.Type = ((BuildingTypeClass *)PendingObject)->Type; +// node.Cell = Coord_Cell(PendingObjectPtr->Coord); + Base.Nodes.Add(BaseNodeClass(((BuildingTypeClass *)PendingObject)->Type, Coord_Cell(PendingObjectPtr->Coord))); + } + + PendingObjectPtr = 0; + PendingObject = 0; + PendingHouse = HOUSE_NONE; + Set_Cursor_Shape(0); + //ScenarioInit--; + return(0); + } + + return(-1); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Placement -- cancels placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Placement(void) +{ + /* + ** Delete the placement object + */ + delete PendingObjectPtr; + PendingObject = 0; + PendingObjectPtr = 0; + PendingHouse = HOUSE_NONE; + + /* + ** Restore cursor shape + */ + Set_Cursor_Shape(0); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next -- while placing object, goes to next * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Increments LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * incrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + /* + ** Go to next object in Objects list + */ + LastChoice++; + if (LastChoice == Objects[typeindex].size()) { + + /* + ** If we're in normal placement mode, wrap to the 1st object; + ** if we're in base-building mode, wrap to the 1st building + */ + if (!BaseBuilding) { + LastChoice = 0; + } else { + //LastChoice = TypeOffset[7]; + typeindex = 7; + LastChoice = Objects[typeindex].size() - 1; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev -- while placing object, goes to previous * + * * + * - Deletes the current 'PendingObjectPtr' * + * - Decrements LastChoice * + * - Tries to create a new 'PendingObjectPtr'; if fails, keeps * + * decrementing until it gets it * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Go to prev object in Objects list + */ + LastChoice--; + + /* + ** If we're in normal placement mode, wrap at the 1st object. + ** If we're building a base, wrap at the 1st building. + */ + if (!BaseBuilding) { + if (LastChoice < 0) { + LastChoice = Objects[typeindex].size() - 1; + } + } else { + //if (LastChoice < TypeOffset[7]) { + // LastChoice = ObjCount - 1; + //} + if (LastChoice <= 0 && typeindex > 0) { + typeindex--; + LastChoice = Objects[typeindex].size() - 1; + } + } + + /* + ** Create placement object + */ + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + if (!PendingObjectPtr) { + PendingObject = NULL; + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Next_Category -- places next category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Next_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to next category in Objects list + */ + i = LastChoice; + while (Objects[typeindex][i]->What_Am_I() == Objects[typeindex][LastChoice]->What_Am_I()) { + i++; + if (i == Objects[typeindex].size()) { + i = 0; + } + } + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == Objects[typeindex].size()) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Prev_Category -- places previous category of object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Prev_Category(void) +{ + int i; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Go to prev category in Objects list + */ + i = LastChoice; + + /* + ** Scan for start of this category + */ + while (Objects[typeindex][i]->What_Am_I() == Objects[typeindex][LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = Objects[typeindex].size() - 1; + } + } + + i--; + if (i < 0) i = Objects[typeindex].size() -1; + LastChoice = i; + + /* + ** Scan for the previous category + */ + while (Objects[typeindex][i]->What_Am_I() == Objects[typeindex][LastChoice]->What_Am_I()) { + i--; + if (i < 0) { + i = Objects[typeindex].size() - 1; + } + } + + i++; + if (i >= Objects[typeindex].size()) i = 0; + LastChoice = i; + + /* + ** Loop until we create a valid object + */ + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ +// if (!Verify_House(LastHouse, Objects[LastChoice])) { +// LastHouse = Cycle_House(LastHouse, Objects[LastChoice]); +// } + + /* + ** Create placement object + */ + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice--; + if (LastChoice < 0) { + LastChoice = Objects[typeindex].size() - 1; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Place_Home -- homes the placement object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/03/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Home(void) +{ + delete PendingObjectPtr; + PendingObjectPtr = NULL; + PendingObject = NULL; + + /* + ** Don't allow this command if we're building a base; the only valid + ** category for base-building is buildings. + */ + if (BaseBuilding) { + return; + } + + /* + ** Loop until we create a valid object + */ + LastChoice = 0; + while (!PendingObjectPtr) { + + /* + ** Get house for this object type + */ + if (!Verify_House(LastHouse, Objects[typeindex][LastChoice])) { + LastHouse = Cycle_House(LastHouse, Objects[typeindex][LastChoice]); + } + + /* + ** Create placement object + */ + PendingObject = Objects[typeindex][LastChoice]; + PendingHouse = LastHouse; + PendingObjectPtr = PendingObject->Create_One_Of(HouseClass::As_Pointer(PendingHouse)); + + /* + ** If this one failed, try the next + */ + if (!PendingObjectPtr) { + PendingObject = NULL; + LastChoice++; + if (LastChoice == Objects[typeindex].size()) { + LastChoice = 0; + } + } + } + + /* + ** Set the new cursor shape + */ + Set_Cursor_Pos(); + Set_Cursor_Shape(0); + Set_Cursor_Shape(PendingObject->Occupy_List()); + + /* + ** Redraw the map to erase old leftovers + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); +} + + +/*************************************************************************** + * MapEditClass::Toggle_House -- toggles current placement object's house * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Toggle_House(void) +{ + TechnoClass *tp; + + /* + ** Don't allow this command if we're building a base; the only valid + ** house for base-building is the one assigned to the base. + */ + if (BaseBuilding) { + return; + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!PendingObjectPtr->Is_Techno()) { + return; + } + + /* + ** Select the house that will own this object + */ + LastHouse = Cycle_House(PendingObjectPtr->Owner(), PendingObject); + + /* + ** Change the house + */ + tp = (TechnoClass *)PendingObjectPtr; + tp->House = HouseClass::As_Pointer(LastHouse); + + /* + ** Set house variables to new house + */ + PendingHouse = LastHouse; +} + + +/*************************************************************************** + * MapEditClass::Set_House_Buttons -- toggles house buttons for btn list * + * * + * Looks in the given button list for the given GDI, NOD & Neutral button * + * id's. Sets the On/Off state of the buttons based on the given house, * + * only if that button is found in the list. * + * * + * INPUT: * + * house house to set buttons to * + * btnlist ptr to button list to search * + * base_id button ID for GDI; assumes other id's are sequential* + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/23/1994 BR : Created. * + * 01/26/1996 JLB : Uses new house selection list method. * + *=========================================================================*/ +void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass *, int ) +//void MapEditClass::Set_House_Buttons(HousesType house, GadgetClass * btnlist, int base_id) +{ + HouseList->Set_Selected_Index(house); + +#ifdef NEVER + HousesType h; + int id; + TextButtonClass * btn; + + /* + ** Loop through all houses, searching the button list for each one. + */ + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + + /* + ** Compute the desired button ID; get a pointer to the button + */ + id = (int)h + base_id; + btn = (TextButtonClass *)btnlist->Extract_Gadget(id); + if (btn) { + + /* + ** If this house value is the desired one, turn the button on; + ** otherwise, turn it off. + */ + if (h == house) { + btn->Turn_On(); + } else { + btn->Turn_Off(); + } + } + } +#endif +} + + +/*************************************************************************** + * MapEditClass::Start_Trigger_Placement -- enters trigger placement mode * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Trigger_Placement(void) +{ + Set_Default_Mouse(MOUSE_CAN_MOVE); + Override_Mouse_Shape(MOUSE_CAN_MOVE); +} + + +/*************************************************************************** + * MapEditClass::Stop_Trigger_Placement -- exits trigger placement mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Stop_Trigger_Placement(void) +{ + CurTrigger = NULL; + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); +} + + +/*************************************************************************** + * MapEditClass::Place_Trigger -- assigns trigger to object or cell * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Place_Trigger(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** Assign trigger to an object + */ + AttachType a1 = CurTrigger->Attaches_To(); + if (object && (a1 & ATTACH_OBJECT) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + if (tt) { + object->Trigger = tt; + } + } + } else { + + /* + ** Assign trigger to a cell + */ + if ((a1 & ATTACH_CELL) != 0) { + if (CurTrigger) { + TriggerClass * tt = Find_Or_Make(CurTrigger); + Map[cell].Trigger = tt; + } +// CellTriggers[cell] = CurTrigger; + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Start_Base_Building -- starts base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Start_Base_Building(void) +{ + /* + ** Fully build the base so the user can edit it + */ + Build_Base_To(100); + + /* + ** Start placement mode + */ + BaseBuilding = true; + Start_Placement(); + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Cancel_Base_Building -- stops base-building mode * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Cancel_Base_Building(void) +{ + /* + ** Build the base to the proper amount + */ + Build_Base_To(Scen.Percent); + + /* + ** Cancel placement mode + */ + Cancel_Placement(); + BaseBuilding = false; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Build_Base_To -- builds the AI base to the given percent * + * * + * INPUT: * + * percent percentage to build base to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Build_Base_To(int percent) +{ + int i; + int num_buildings; + BuildingTypeClass const * objtype; + BuildingClass * obj; + + //ScenarioInit++; + + /* + ** Completely dismantle the base, so we start at a known point + */ + for (i = 0; i < Base.Nodes.Count(); i++) { + if (Base.Is_Built(i)) { + obj = Base.Get_Building(i); + delete obj; + } + } + + /* + ** Compute number of buildings to build + */ + num_buildings = (Base.Nodes.Count() * percent) / 100; + + /* + ** Build the base to the desired amount + */ + for (i = 0; i < num_buildings; i++) { + /* + ** Get a ptr to the type of building to build, create one, and unlimbo it. + */ + objtype = &BuildingTypeClass::As_Reference(Base.Nodes[i].Type); + obj = (BuildingClass *)objtype->Create_One_Of(HouseClass::As_Pointer(Base.House)); + + /* + ** If unlimbo fails, error out + */ + ScenarioInit++; + if (!obj->Unlimbo(Cell_Coord(Base.Nodes[i].Cell))) { + delete obj; + WWMessageBox().Process("Unable to build base!"); + ScenarioInit--; + return; + } + ScenarioInit--; + } + + //ScenarioInit--; +} + + +#endif diff --git a/code/redalert/mapedsel.cpp b/code/redalert/mapedsel.cpp new file mode 100644 index 0000000..78532a6 --- /dev/null +++ b/code/redalert/mapedsel.cpp @@ -0,0 +1,585 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDSEL.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : November 18, 1994 * + * * + * Last Update : April 30, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Object-selection & manipulation routines * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Change_House -- changes CurrentObject's house * + * MapEditClass::Grab_Object -- grabs the current object * + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls* + * MapEditClass::Select_Next -- selects next object on the map * + * MapEditClass::Select_Object -- selects an object for processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * Select_Object -- selects an object for processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object selected, -1 = none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/04/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Select_Object(void) +{ + ObjectClass * object=NULL; // Generic object clicked on. + int x,y; + CELL cell; // Cell that was selected. + int rc=0; + + /* + ** See if an object was clicked on + */ + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + + /* + ** Get cell for x,y + */ + cell = Click_Cell_Calc(x, y); + + /* + ** Convert x,y to offset from cell upper-left + */ + x = (x-TacPixelX) % ICON_PIXEL_W; + y = (y-TacPixelY) % ICON_PIXEL_H; + + /* + ** Get object at that x,y + */ + object = Cell_Object(cell, x, y); + + /* + ** If no object, unselect the current one + */ + if (!object) { + if (CurrentObject.Count()) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + + /* + ** Turn off object controls + */ + Popup_Controls(); + } + rc = -1; + } else { + + /* + ** Select object only if it's different + */ + if (!CurrentObject.Count() || (CurrentObject.Count() && object != CurrentObject[0])) { + + /* + ** Unselect all current objects + */ + Unselect_All(); + object->Select(); + + /* + ** Set mouse shape back to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show the popup controls + */ + Popup_Controls(); + } + } + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); + + return(rc); +} + + +/*************************************************************************** + * MapEditClass::Select_Next -- selects next object on the map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Select_Next(void) +{ + ObjectClass * obj; + CELL obj_cell; + int smap_w; // screen map width in icons + int smap_h; // screen map height in icons + int cell_x; // cell-x of next object + int cell_y; // cell-y of next object + int tcell_x; // cell-x of TacticalCell + int tcell_y; // cell-y of TacticalCell + + /* + ** Get next object on the map + */ + obj = Map.Next_Object(CurrentObject[0]); + + if (obj) { + /* + ** Unselect current object if there is one + */ + Unselect_All(); + + /* + ** Select this object + */ + obj->Select(); + } + + /* + ** Restore mouse shape to normal + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + /* + ** Show pop-up controls + */ + Popup_Controls(); + + /* + ** Make sure object is shown on the screen + */ + /* + ** compute screen map dimensions + */ + smap_w = Lepton_To_Cell(TacLeptonWidth); + smap_h = Lepton_To_Cell(TacLeptonHeight); + + /* + ** compute x,y of object's cell + */ + obj_cell = Coord_Cell(CurrentObject[0]->Coord); + cell_x = Cell_X(obj_cell); + cell_y = Cell_Y(obj_cell); + tcell_x = Coord_XCell(TacticalCoord); + tcell_y = Coord_YCell(TacticalCoord); + + /* + ** If object is off-screen, move map + */ + if (cell_x < tcell_x) { + tcell_x = cell_x; + } else { + if (cell_x >= tcell_x + smap_w) { + tcell_x = cell_x - smap_w + 1; + } + } + + if (cell_y < tcell_y) { + tcell_y = cell_y; + } else { + if (cell_y >= tcell_y + smap_h) { + tcell_y = cell_y - smap_h + 1; + } + } + + ScenarioInit++; + Set_Tactical_Position(XY_Coord(Cell_To_Lepton(tcell_x), Cell_To_Lepton(tcell_y))); + ScenarioInit--; + + /* + ** Force map to redraw + */ + HidPage.Clear(); + Flag_To_Redraw(true); +} + + +/*************************************************************************** + * MapEditClass::Popup_Controls -- shows/hides the pop-up object controls * + * * + * Call this routine whenever the CurrentObject changes. The routine will * + * selectively enable or disable the popup controls based on whether * + * CurrentObject is NULL, or if it's a Techno object, or what type of * + * Techno object it is. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1994 BR : Created. * + * 04/30/1996 JLB : Revamped for new buttons and stuff. * + *=========================================================================*/ +void MapEditClass::Popup_Controls(void) +{ + const TechnoTypeClass * objtype = NULL; + HousesType owner; // object's current owner + int mission_index; // object's current mission + int strength; // object's 0-255 strength value + int i; + + /* + ** Remove all buttons from GScreen's button list (so none of them provide + ** input any more); then, destroy the list by Zapping each button. Then, + ** we'll have to add at least the MapArea button back to the Input button + ** list before we return, plus any other buttons to process input for. We + ** always must add MapArea LAST in the list, so it doesn't intercept the + ** other buttons' input. + */ + Remove_A_Button(*HouseList); + Remove_A_Button(*MissionList); + Remove_A_Button(*HealthGauge); + Remove_A_Button(*HealthText); + Remove_A_Button(*FacingDial); + Remove_A_Button(*BaseGauge); + Remove_A_Button(*BaseLabel); + Remove_A_Button(*MapArea); + + Remove_A_Button(*Sellable); + Remove_A_Button(*Rebuildable); + + /* + ** If no current object, hide the list + */ + if (!CurrentObject.Count()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + /* + ** If not Techno, no need for editing buttons + */ + if (!CurrentObject[0]->Is_Techno()) { + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); + return; + } + + objtype = (TechnoTypeClass const *)&CurrentObject[0]->Class_Of(); + + /* + ** Get object's current values + */ + owner = CurrentObject[0]->Owner(); + mission_index = 0; + for (i = 0; i < NUM_EDIT_MISSIONS; i++) { + if (CurrentObject[0]->Get_Mission() == MapEditMissions[i]) { + mission_index = i; + } + } + strength = CurrentObject[0]->Health_Ratio()*256; + + switch (objtype->What_Am_I()) { + case RTTI_VESSELTYPE: + case RTTI_UNITTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_AIRCRAFTTYPE: + MissionList->Set_Selected_Index(mission_index); + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + FacingDial->Set_Direction(((TechnoClass *)CurrentObject[0])->PrimaryFacing); + + /* + ** Make the list. + */ + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*MissionList); + Add_A_Button(*HealthText); + Add_A_Button(*FacingDial); + break; + + case RTTI_BUILDINGTYPE: + HealthGauge->Set_Value(strength); + sprintf(HealthBuf, "%d", CurrentObject[0]->Strength); + Add_A_Button(*HealthGauge); + Add_A_Button(*HouseList); + HouseList->Set_Selected_Index(owner); + Add_A_Button(*HealthText); + + Add_A_Button(*Sellable); + if (((BuildingClass*)CurrentObject[0])->IsAllowedToSell) { + Sellable->Turn_On(); + } else { + Sellable->Turn_Off(); + } + Add_A_Button(*Rebuildable); + if (((BuildingClass*)CurrentObject[0])->IsToRebuild) { + Rebuildable->Turn_On(); + } else { + Rebuildable->Turn_Off(); + } + + if (objtype->IsTurretEquipped) { + FacingDial->Set_Direction(((TechnoClass *) CurrentObject[0])->PrimaryFacing); + Add_A_Button(*FacingDial); + } + break; + } + + /* + ** Add the map area last, so it's "underneath" the other buttons, and won't + ** intercept input for those buttons. + */ + Add_A_Button(*BaseGauge); + Add_A_Button(*BaseLabel); + Add_A_Button(*MapArea); +} + + +/*************************************************************************** + * MapEditClass::Grab_Object -- grabs the current object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Grab_Object(void) +{ + CELL cell; + + if (CurrentObject.Count()) { + GrabbedObject = CurrentObject[0]; + + /* + ** Find out which cell 'ZoneCell' is in relation to the object's current cell + */ + cell = Coord_Cell(GrabbedObject->Coord); + GrabOffset = cell - ZoneCell; + } +} + + +/*************************************************************************** + * MapEditClass::Move_Grabbed_Object -- moves the grabbed object * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = object moved, -1 = it didn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1994 BR : Created. * + *=========================================================================*/ +int MapEditClass::Move_Grabbed_Object(void) +{ + COORDINATE new_coord = 0; + int retval = -1; + + /* + ** Lift up the object + */ + GrabbedObject->Mark(MARK_UP); + + /* + ** If infantry, use a free spot in this cell + */ + if (GrabbedObject->Is_Infantry()) { + + if (Is_Spot_Free(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()))) { + new_coord = Closest_Free_Spot(Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y())); + + /* + ** Clear the occupied bit in this infantry's cell. + */ + ((InfantryClass *)GrabbedObject)->Clear_Occupy_Bit(GrabbedObject->Coord); + } else { + new_coord = NULL; + } + + } else { + + /* + ** Non-infantry: use cell's center coordinate + */ + new_coord = Cell_Coord(ZoneCell + GrabOffset); + + if (GrabbedObject->What_Am_I() == RTTI_BUILDING || + GrabbedObject->What_Am_I() == RTTI_TERRAIN) { + + new_coord = Coord_Whole(new_coord); + } + + /* + ** Try to place object at new coordinate + */ + if (GrabbedObject->Can_Enter_Cell(Coord_Cell(new_coord)) != MOVE_OK) { + new_coord = NULL; + } + } + if (new_coord != NULL) { + + /* + ** If this object is part of the AI's Base list, change the coordinate + ** in the Base's Node list. + */ + if (GrabbedObject->What_Am_I()==RTTI_BUILDING && + Base.Get_Node((BuildingClass *)GrabbedObject)) + Base.Get_Node((BuildingClass *)GrabbedObject)->Cell = Coord_Cell(new_coord); + + GrabbedObject->Coord = new_coord; + retval = 0; + } + GrabbedObject->Mark(MARK_DOWN); + + /* + ** For infantry, set the bit in its new cell marking that spot as occupied. + */ + if (GrabbedObject->Is_Infantry()) { + ((InfantryClass *)GrabbedObject)->Set_Occupy_Bit(new_coord); + } + + /* + ** Re-select the object, and reset the mouse pointer + */ + Set_Default_Mouse(MOUSE_NORMAL); + Override_Mouse_Shape(MOUSE_NORMAL); + + Flag_To_Redraw(true); + + return(retval); +} + + +/*************************************************************************** + * MapEditClass::Change_House -- changes CurrentObject's house * + * * + * INPUT: * + * newhouse house to change to * + * * + * OUTPUT: * + * 1 = house was changed, 0 = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/17/1994 BR : Created. * + *=========================================================================*/ +bool MapEditClass::Change_House(HousesType newhouse) +{ + TechnoClass *tp; + + /* + ** Return if no current object + */ + if (!CurrentObject.Count()) { + return(false); + } + + /* + ** Only techno objects can be owned by a house; return if not a techno + */ + if (!CurrentObject[0]->Is_Techno()) { + return(false); + } + + /* + ** You can't change the house if the object is part of the AI's Base. + */ + if (CurrentObject[0]->What_Am_I()==RTTI_BUILDING && Base.Is_Node((BuildingClass *)CurrentObject[0])) { + return(false); + } + + /* + ** Verify that the target house exists + */ + if (HouseClass::As_Pointer(newhouse)==NULL) { + return(false); + } + + /* + ** Verify that this is a valid owner + */ +// if (!Verify_House(newhouse, &CurrentObject[0]->Class_Of())) { +// return(false); +// } + + /* + ** Change the house + */ + tp = (TechnoClass *)CurrentObject[0]; + tp->House = HouseClass::As_Pointer(newhouse); + + tp->IsOwnedByPlayer = false; + if (tp->House == PlayerPtr) { + tp->IsOwnedByPlayer = true; + } + + return(true); +} + + +#endif diff --git a/code/redalert/mapedtm.cpp b/code/redalert/mapedtm.cpp new file mode 100644 index 0000000..32a7366 --- /dev/null +++ b/code/redalert/mapedtm.cpp @@ -0,0 +1,948 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPEDTM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPEDTM.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 7, 1994 * + * * + * Last Update : May 7, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * MapEditClass::Select_Team -- user selects a team from a list * + * MapEditClass::Team_Members -- user picks makeup of a team * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef SCENARIO_EDITOR + + +/*************************************************************************** + * MapEditClass::Handle_Teams -- main team-dialog-handling function * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + *=========================================================================*/ +void MapEditClass::Handle_Teams(char const * caption) +{ + int rc; + + /* + ** Team dialog processing loop: + ** - Invoke the team selection dialog. If a team's selected, break + ** & return + ** - If user wants to edit the current team, do so + ** - If user wants to create new team, new a TeamTypeClass & edit it + ** - If user wants to delete team, delete the current team + ** - Keep looping until 'OK' + */ + for (;;) { + + /* + ** Select team + */ + rc = Select_Team(caption); + + /* + ** 'OK'; break + */ + if (rc == 0) { + break; + } else { + + /* + ** 'Edit' + */ + if (rc == 1 && CurTeam) { + if (CurTeam->Edit()) { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** 'New' + */ + if (rc == 2) { + + /* + ** Create a new team + */ + CurTeam = new TeamTypeClass(); + if (CurTeam) { + /* + ** delete it if user cancels + */ + if (!CurTeam->Edit()) { + delete CurTeam; + CurTeam = NULL; + } else { + Changed = 1; + } + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } else { + + /* + ** Unable to create; issue warning + */ + WWMessageBox().Process("No more teams available."); + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + } + } else { + + /* + ** 'Delete' + */ + if (rc==3) { + if (CurTeam) { + Detach_This_From_All(CurTeam->As_Target(), true); + delete CurTeam; + //CurTeam->Remove(); + CurTeam = NULL; + } + } + } + } + } + } +} + + +/*************************************************************************** + * MapEditClass::Select_Team -- user selects a team from a list * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Teams ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Name House Class:Count,Class:Count ³³ ³ * + * ³ ³ Name House Class:Count,Class:Count ÃÄ´ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ Name House Class:Count,Class:Count ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ * + * ³ ³ ÃÄ´ ³ * + * ³ ³ ³³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ ³ * + * ³ [Edit] [New] [Delete] [OK] ³ * + * ³ ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = OK, 1 = Edit, 2 = New, 3 = Delete * + * * + * WARNINGS: * + * Uses HIDBUFF. * + * * + * HISTORY: * + * 12/08/1994 BR : Created. * + * 05/07/1996 JLB : Streamlined and sorted team list. * + *=========================================================================*/ +int MapEditClass::Select_Team(char const * ) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, // dialog width + D_DIALOG_H = 250, // dialog height + D_DIALOG_X = 0, // centered x-coord + D_DIALOG_Y = 0, // centered y-coord +// D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT8_H = 11, // ht of 8-pt text + D_MARGIN = 25, // margin width/height + + D_LIST_W = (D_DIALOG_W-(D_MARGIN*2))-20, + D_LIST_X = D_DIALOG_X + (D_DIALOG_W-D_LIST_W)/2, + D_LIST_Y = D_DIALOG_Y + 20, + D_LIST_H = (D_DIALOG_H-50)-D_LIST_Y, + + BUTTON_W = 45, + BUTTON_H = 9, + + D_EDIT_W = BUTTON_W, + D_EDIT_H = BUTTON_H, + D_EDIT_X = D_DIALOG_X + D_DIALOG_W - (((D_EDIT_W+10)*4)+25), + D_EDIT_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_EDIT_H, + + D_NEW_W = BUTTON_W, + D_NEW_H = BUTTON_H, + D_NEW_X = D_EDIT_X + D_EDIT_W + 10, + D_NEW_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_NEW_H, + + D_DELETE_W = BUTTON_W, + D_DELETE_H = BUTTON_H, + D_DELETE_X = D_NEW_X + D_NEW_W + 10, + D_DELETE_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_DELETE_H, + + D_OK_W = BUTTON_W, + D_OK_H = BUTTON_H, + D_OK_X = D_DELETE_X + D_DELETE_W + 10, + D_OK_Y = D_DIALOG_Y + D_DIALOG_H - 20 - D_OK_H, + + TEAMTXT_LEN = 43, // max length of a team entry + }; + + /* + ** Button enumerations: + */ + enum { + TEAM_LIST=100, + BUTTON_EDIT, + BUTTON_NEW, + BUTTON_DELETE, + BUTTON_OK, + }; + + /* + ** Dialog variables + */ + bool edit_team = false; // true = user wants to edit + bool new_team = false; // true = user wants to new + bool del_team = false; // true = user wants to new + static int tabs[] = {35, 60, 80, 100}; // list box tab stops + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass * commands = NULL; // the button list + + TListClass teamlist (TEAM_LIST, + D_LIST_X, D_LIST_Y, D_LIST_W, D_LIST_H, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + TextButtonClass editbtn (BUTTON_EDIT, "Edit", TPF_EBUTTON, D_EDIT_X, D_EDIT_Y, D_EDIT_W); + TextButtonClass newbtn (BUTTON_NEW, "New", TPF_EBUTTON, D_NEW_X, D_NEW_Y, D_NEW_W); + TextButtonClass deletebtn (BUTTON_DELETE, "Delete", TPF_EBUTTON, D_DELETE_X, D_DELETE_Y, D_DELETE_W); + TextButtonClass okbtn (BUTTON_OK, "OK", TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Fill in team names + */ + for (int index = 0; index < TeamTypes.Count(); index++) { + teamlist.Add_Item(TeamTypes.Ptr(index)); + } + + PNBubble_Sort(&teamlist[0], teamlist.Count()); + + if (!CurTeam || !CurTeam->IsActive) CurTeam = NULL; + + if (CurTeam) { + teamlist.Set_Selected_Index(CurTeam); + CurTeam = (TeamTypeClass *)teamlist.Current_Item(); + } else { + teamlist.Set_Selected_Index(0); + if (TeamTypes.Count()) { + CurTeam = (TeamTypeClass*)teamlist.Current_Item(); + } + } + + /* + ** Create the list + */ + commands = &teamlist; + editbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + + /* + ** Init tab stops for list + */ + teamlist.Set_Tabs(tabs); + + /* + ** Main Processing Loop + */ + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + commands->Draw_All(); + Show_Mouse(); + display = false; +// LogicPage->Unlock(); + } + + /* + ** Get user input + */ + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + //Device_Present(); + + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (TEAM_LIST | KN_BUTTON): + CurTeam = (TeamTypeClass*)teamlist.Current_Item(); + break; + + case (BUTTON_EDIT | KN_BUTTON): + if (teamlist.Count()) { + process = false; + edit_team = true; + } + break; + + case (BUTTON_NEW | KN_BUTTON): + process = false; + new_team = true; + break; + + case (BUTTON_DELETE | KN_BUTTON): + process = false; + del_team = true; + break; + + case (KN_RETURN): + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + /* + ** Redraw the display + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (edit_team) return(1); + if (new_team) return(2); + if (del_team) return(3); + return(0); +} + + +/*************************************************************************** + * MapEditClass::Team_Members -- user picks makeup of a team * + * * + * Team members are rendered in a 24 x 24 area; the Window coordinates * + * have to be set to this area when the object's 'Display()' routine is * + * called. Thus, the dialog's window coords have to be divisible by * + * 24. The height of the dialog is computed based on how many objects * + * there are in it. * + * * + * 10 pixels are left between rows of objects, so the # of that type of * + * object can be displayed underneath the object. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Team Members ³ * + * ³ ³ * + * ³ ÚÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄÂÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÃÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄÅÄÄÄ´ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÁÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * house house to display objects for * + * * + * OUTPUT: * + * 0 = OK, -1 = cancel * + * * + * WARNINGS: * + * CurTeam must NOT be NULL when this function is called. * + * This routine uses HIDBUFF for data storage. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +//#define TEENSY_WEENSY +/* +** Dialog & button dimensions +*/ +enum { + D_DIALOG_W = 640, + D_DIALOG_X = 0, + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), + + D_TXT6_H = 7, + D_MARGIN = 7, + +#ifdef TEENSY_WEENSY + D_PICTURE_W = 32, + D_PICTURE_H = 24, +#else + D_PICTURE_W = 64, + D_PICTURE_H = 48, +#endif + D_ROW_H = (D_PICTURE_H + 3), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_CX - 5 - D_OK_W, + D_OK_Y = 0, + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_CX + 5, + D_CANCEL_Y = 0, + +}; + +int MapEditClass::Team_Members(HousesType house) +{ + /* + ** Button enumerations: + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + ** (highest enum is the lowest layer). Each section of the map checks + ** the requested redraw level to see if it's supposed to draw; if it's + ** >= its level, it redraws. + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + RedrawType display; // requested redraw level + bool process; // loop while true + + /* + ** Dialog variables + */ + KeyNumType input; // user input + bool cancel = false; // true = user cancels + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team display variables + */ + const TechnoTypeClass **teamclass; // array of team classes + int *teamcount; // array of class counts + int numcols; // # units displayed horizontally + int numrows; // # units displayed vertically + + /* + ** Dialog dimensions. + */ + int dlg_y; + int dlg_h; // dialog height + int msg_y; // y-coord for object names + + int curclass = -1; // current index into 'teamclass'; can be invalid! + // (is based on current mouse position) + int numclasses; // current # classes in the team (limited to <=5) + int maxclasses; // max # classes available + int i,j; + + /* + ** Values for timing when mouse held down. + */ + int lheld = 0; + int rheld = 0; + long tdelay[3] = {5, 20, 0}; + int tindex = 0; + long heldtime; + + /* + ** Buttons. + */ + ControlClass * commands; + + TextButtonClass okbtn (BUTTON_OK, TXT_OK, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_EFNT | TPF_NOSHADOW, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Set up the team data arrays (ObjectTypeClass pointers & count) + */ +#ifdef WIN32 + teamclass = (const TechnoTypeClass **)SysMemPage.Get_Buffer(); + teamcount = (int *)SysMemPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#else + teamclass = (const TechnoTypeClass **)HidPage.Get_Buffer(); + teamcount = (int *)HidPage.Get_Buffer() + MAX_TEAM_CLASSES * sizeof (ObjectTypeClass *); +#endif + + /* + ** Fill in the ObjectTypeClass array with all available object type ptrs, + ** checking to be sure this house can own the object + */ + i = 0; + for (InfantryType i_id = INFANTRY_FIRST; i_id < INFANTRY_COUNT; i_id++) { + teamclass[i] = &InfantryTypeClass::As_Reference(i_id); + i++; + } + + for (AircraftType a_id = AIRCRAFT_FIRST; a_id < AIRCRAFT_COUNT; a_id++) { + teamclass[i] = &AircraftTypeClass::As_Reference(a_id); + i++; + } + + for (UnitType u_id = UNIT_FIRST; u_id < UNIT_COUNT; u_id++) { + teamclass[i] = &UnitTypeClass::As_Reference(u_id); + i++; + } + + for (VesselType v_id = VESSEL_FIRST; v_id < VESSEL_COUNT; v_id++) { + teamclass[i] = &VesselTypeClass::As_Reference(v_id); + i++; + } + + /* + ** Save max # classes. + */ + maxclasses = i; + + /* + ** Fill in the 'count' array with data from the current team: + ** - For every class in the current team, find that class type in the + ** 'teamclass' array & set its count value + */ + for (j = 0; j < maxclasses; j++) { + teamcount[j] = 0; + } + + /* + ** Loop through all classes in the team. + */ + for (i = 0; i < CurTeam->ClassCount; i++) { + + /* + ** Find this class in our array. + */ + for (j = 0; j < maxclasses; j++) { + + /* + ** Set the count; detect a match between the team's class & the + ** 'teamclass' array entry by comparing the actual pointers; typeid + ** won't work because E1 & E2 are the same type class. + */ + if (CurTeam->Members[i].Class == teamclass[j]) { + teamcount[j] = CurTeam->Members[i].Quantity; + break; + } + } + } + numclasses = CurTeam->ClassCount; + + /* + ** Set up the dialog dimensions based on number of classes we have to draw + ** + ** Compute picture rows & cols. + */ + numcols = (D_DIALOG_W - 16) / D_PICTURE_W; + numrows = (maxclasses + numcols - 1) / numcols; + + /* + ** Dialog's height = top margin + label + picture rows + margin + label + margin + btn + */ + dlg_h = 400; + dlg_y = 0; + msg_y = dlg_y+dlg_h - 26 - 15; + + okbtn.Y = dlg_y + dlg_h - D_MARGIN - D_OK_H - 15; + cancelbtn.Y = dlg_y + dlg_h - D_MARGIN - D_CANCEL_H - 15; + + /* + ** Draw to SeenPage. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Make sure 'house' is valid. + */ +// if (house!=HOUSE_GOOD && house!=HOUSE_BAD && house != HOUSE_MULTI1 && +// house != HOUSE_MULTI2 && house != HOUSE_MULTI3 && house != HOUSE_MULTI4 ) { +// if (Scen.ScenPlayer == SCEN_PLAYER_MPLAYER) { +// house = HOUSE_MULTI1; +// } else { +// house = HOUSE_GOOD; +// } +// } + + /* + ** Create the list. + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + display = REDRAW_ALL; + process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + + /* + ** Display the constant background of this dialog. + */ + Dialog_Box(D_DIALOG_X, dlg_y, D_DIALOG_W, dlg_h); + Draw_Caption(TXT_TEAM_MEMBERS, D_DIALOG_X, dlg_y, D_DIALOG_W); + + /* + ** Draw the objects. + */ + for (i = 0; i < maxclasses; i++) { + + /* + ** Display the object along with any count value for it. + */ + Draw_Member(teamclass[i], i, teamcount[i], house); + } + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, + &ColorRemaps[PCOLOR_BROWN], TBLACK, + TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + } + + /* + ** Redraw the buttons. + */ + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input. + */ + input = commands->Input(); + + /* + ** Process input. + */ + switch (input) { + + /* + ** Mouse buttons set or clear 'held' values + */ + case (KN_LMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + lheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case (KN_RMOUSE): + if (curclass >= 0 && curclass < maxclasses) { + rheld = 1; + tindex = 2; + heldtime = 0; + } + break; + + case ((int)KN_LMOUSE | (int)KN_RLSE_BIT): + lheld = 0; + break; + + case ((int)KN_RMOUSE | (int)KN_RLSE_BIT): + rheld = 0; + break; + + /* + ** OK: save values & return. + */ + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + + /* + ** Cancel: abort & return. + */ + case (BUTTON_CANCEL | KN_BUTTON): + cancel = true; + process = false; + break; + + default: + /* + ** Compute new 'curclass' based on mouse position. + */ + i = (Get_Mouse_X() - 32 - D_DIALOG_X) / D_PICTURE_W + + ((Get_Mouse_Y() - (dlg_y+8+11)) / D_ROW_H) * numcols; + + /* + ** If it's changed, update class label. + */ + if (i != curclass) { + + curclass = i; + + /* + ** Clear out the previously printed name of the item. + */ + Hide_Mouse(); + LogicPage->Fill_Rect(D_DIALOG_X + 32, msg_y, D_DIALOG_X + D_DIALOG_W - 64, msg_y + D_TXT6_H, BLACK); + + if ((unsigned)curclass < maxclasses) { + Fancy_Text_Print(teamclass[curclass]->Full_Name(), + D_DIALOG_X + D_DIALOG_W / 2, msg_y, scheme, TBLACK, TPF_CENTER|TPF_EFNT|TPF_NOSHADOW); + } + + /* + ** Force buttons to not be held. + */ + lheld = 0; + rheld = 0; + Show_Mouse(); + } + break; + } + + /* + ** Check for a 'held' mouse button; if it's down, and the correct + ** amount of time has gone by, increment/decrement the count for the + ** current class. + */ + if (lheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + heldtime = TickCount; + if (tindex) { + tindex--; + } + + /* + ** Detect addition of a new class. + */ + if (teamcount[curclass]==0) { + + /* + ** Don't allow more classes than we can handle. + */ + if (numclasses == TeamTypeClass::MAX_TEAM_CLASSCOUNT) { + continue; + } + numclasses++; + } + teamcount[curclass]++; + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + + } else { + + if (rheld) { + + /* + ** The first time in, TickCount - heldtime will be larger than + ** tdelay[2], so we increment the count immediately; then, we decrement + ** tindex to go to the next time delay, which is longer; then, decr. + ** again to go to the 1st time delay which is the shortest. + */ + if (TickCount - heldtime > tdelay[tindex]) { + if (tindex) { + tindex--; + } + heldtime = TickCount; + + if (teamcount[curclass] > 0) { + teamcount[curclass]--; + + /* + ** Detect removal of a class. + */ + if (teamcount[curclass] == 0) { + numclasses--; + } + } + + /* + ** Update number label. + */ + Draw_Member(teamclass[curclass], curclass, teamcount[curclass], house); + } + } + } + } + + /* + ** Copy data into team. + */ + if (!cancel) { + CurTeam->ClassCount = numclasses; + i = 0; // current team class index + for (j = 0; j < maxclasses; j++) { + if (teamcount[j] > 0) { + CurTeam->Members[i].Quantity = teamcount[j]; + CurTeam->Members[i].Class = teamclass[j]; + i++; + } + } + } + + /* + ** Redraw the display. + */ + HidPage.Clear(); + Flag_To_Redraw(true); + Render(); + + if (cancel) return(-1); + return(0); +} + + +/*********************************************************************************************** + * MapEditClass::Draw_Member -- Draws a member of the team dialog box. * + * * + * This routine will display the cameo image of the potential team member. In the corner, * + * it will show the current quantity of this member for the current team being edited. * + * * + * INPUT: ptr -- Pointer to the member object type. * + * * + * index -- The index into the team dialog box array of selectable objects. This is * + * used to determine the correct X and Y offsets to draw. * + * * + * quant -- The quantity number to display in the corner of the image. * + * * + * house -- The owner of this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1995 JLB : Created. * + *=============================================================================================*/ +void MapEditClass::Draw_Member(TechnoTypeClass const * ptr, int index, int quant, HousesType house) +{ + int numcols = (D_DIALOG_W - 64) / D_PICTURE_W; + int col = index % numcols; + int row = index / numcols; + int dlg_y = 0; + int x = D_DIALOG_X + 32 + col * D_PICTURE_W; + int y = dlg_y + 8 + 13 + row * D_ROW_H; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Change the window to this box. + */ + WindowList[WINDOW_EDITOR][WINDOWX] = x; + WindowList[WINDOW_EDITOR][WINDOWY] = y; + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = D_PICTURE_W; + WindowList[WINDOW_EDITOR][WINDOWHEIGHT] = D_PICTURE_H; + Change_Window((int)WINDOW_EDITOR); + + Hide_Mouse(); + Draw_Box(x, y, D_PICTURE_W, D_PICTURE_H, BOXSTYLE_DOWN, true); + ptr->Display(WinW/2, WinH>>1, WINDOW_EDITOR, house); + if (quant > 0) { + Fancy_Text_Print("%d", x+1, y+1, scheme, TBLACK, TPF_8POINT|TPF_DROPSHADOW, quant); +// Fancy_Text_Print("%d", x+1, y+D_PICTURE_H-8, scheme, TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_DROPSHADOW, quant); + } + Show_Mouse(); +} + + +#endif diff --git a/code/redalert/mapscript.cpp b/code/redalert/mapscript.cpp new file mode 100644 index 0000000..4c74838 --- /dev/null +++ b/code/redalert/mapscript.cpp @@ -0,0 +1,4286 @@ +// MapScript.cpp +// + +#include "FUNCTION.H" +#include "MapScript.h" + +/********************************************************************************************** +* Red Alert Vanilla Actions * +*=============================================================================================*/ + + /*********************************************************************************************** + * Script_Win - The specified player wins * + * * + * SCRIPT INPUT: houseType (int) - The player (house that wins) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_Win(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + + if (this_house->ID == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } + else { + PlayerPtr->Flag_To_Lose(); + } + + bool success = this_house->Fire_Sale(); + lua_pushboolean(L, int(success)); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_Lose - The specified player loses * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_Lose(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + + if (this_house->ID == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Lose(); + } + else { + PlayerPtr->Flag_To_Win(); + } + + lua_pushboolean(L, 1); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_BeginProduction - This will enable production to begin for the house specified * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_BeginProduction(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Begin_Production(); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_CreateTeam - Attempts to create team (as defined in map) * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to create * + * * + * SCRIPT OUTPUT: success (bool) - Did a team get created? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreateTeam(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + ScenarioInit++; + + bool success = false; + + TeamClass* new_team = teamPtr->Create_One_Of(); + + if (new_team != NULL) { + success = true; + } + + lua_pushboolean(L, int(success)); + + ScenarioInit--; + } + + return 1; + } + + /*********************************************************************************************** + * Script_DestroyTeam - Destroy all teams of the type specified * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to destroy * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DestroyTeam(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + + teamPtr->Destroy_All_Of(); + + } + + return 1; + } + + /*********************************************************************************************** + * Script_AllHunt - Force all units of specified house to go into hunt mode * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllHunt(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Do_All_To_Hunt(); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_Reinforcements - Attempts to create reinforcements as defined by team * + * * + * SCRIPT INPUT: teamType (int) - The team index to attempt to create * + * * + * SCRIPT OUTPUT: success (bool) - Did Do_Reinforcements return true? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_Reinforcements(lua_State* L) { + + const char* teamType = lua_tostring(L, 1); + + TeamTypeClass* teamPtr = TeamTypeClass::From_Name(teamType); + + if (teamPtr != NULL) { + bool success = Do_Reinforcements(teamPtr); + lua_pushboolean(L, int(success)); + } + else { + lua_pushboolean(L, 0); + } + + return 1; + } + + /*********************************************************************************************** + * Script_DropZoneFlare - Places drop down smoke at specified waypoint location * + * * + * SCRIPT INPUT: in_x (int) - The Cell/X location to drop the smoke * + * in_y (int) - The Cell/Y location to drop the smoke * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DropZoneFlare(lua_State* L) { + + int in_x = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(XY_Cell(in_x,in_y))); + + return 1; + } + + /*********************************************************************************************** + * Script_FireSale - Make AI house give up, selling everything and going all in on attack * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: success (bool) - Did the action get performed? * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_FireSale(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + bool success = this_house->Fire_Sale(); + lua_pushboolean(L, int(success)); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_PlayMovie - Plays the given movie * + * * + * SCRIPT INPUT: ret (int) - The movie index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_PlayMovie(lua_State* L) { + int ret = lua_tointeger(L, -1); + Play_Movie((VQType)ret); + return 1; + } + + /*********************************************************************************************** + * Script_TriggerText - Triggers text to display in-game * + * * + * SCRIPT INPUT: textIndex (int) - The text index to display (from tutorial.ini) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_TriggerText(lua_State* L) { + int textIndex = lua_tointeger(L, -1); + + // TODO: Implement colors / flags + + // JJQUESTION: This appears right but doesn't do anything. Is text broken at the moment? + if (textIndex > 0 && textIndex < sizeof(TutorialText)) { + + Session.Messages.Add_Message(NULL, textIndex, (char*)TutorialText[textIndex], PCOLOR_GREEN, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return 1; + } + + /*********************************************************************************************** + * Script_DestroyTrigger - Destroys a given trigger * + * * + * SCRIPT INPUT: ret (int) - The index/id of the trigger of which to destroy * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DestroyTrigger(lua_State* L) { + + int triggerIndex = lua_tointeger(L, -1); + + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); + + if (this_trigger != NULL) { + Detach_This_From_All(this_trigger->As_Target()); + delete Triggers.Ptr(triggerIndex); + } + + return 1; + } + + /*********************************************************************************************** + * Script_AutoCreate - Allows house AI to begin creating autocreate teams * + * * + * Autocreation means that instead of a teamtype being * + * manually created by a Create Team trigger action, that * + * it will be subject to the autocreate function. Each * + * computer country can have its autocreation function * + * turned on through a trigger action, after which the * + * creation of the teamtypes that have autocreation turned * + * on will be subject to being created, at the whim of the * + * autocreate function. See [TeamTypes] Section for more * + * details. * + * - The Red Alert Single Player Mission Creation Guide * + * * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow autocreate for * + * createEnables (bool, default yes) - Allow autocreate on/off * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AutoCreate(lua_State* L) { + + int houseType = 0; + bool autoCreateEnabled = true; + + houseType = lua_tointeger(L, 1); + + // If there is a second argument given, process it + if (lua_gettop(L) == 2) { + autoCreateEnabled = lua_toboolean(L, 2); + } + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + + // If we are enabling auto creation by the house, clear alert + if (autoCreateEnabled) { + this_house->AlertTime = 0; // TODO - don't assume successful pointer + } + + // Now set the parameter + this_house->IsAlerted = autoCreateEnabled; + + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_AllowWin - Allows a win * + * * + * Used when you want a specific objective * + * to be met before the mission is accomplished. In other words, if the * + * trigger with this action assigned to it hasn't been fired, but the * + * actual WIN trigger has been fired, the mission will not end until this * + * "allow win" trigger is fired. * + * * + * SCRIPT INPUT: houseType (int) - The AI player (house) index to allow winning for * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllowWin(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + if (houseType != HOUSE_NONE) { + + HouseClass* this_house = HouseClass::As_Pointer((HousesType)houseType); + + if (this_house != NULL) { + this_house->Blockage++; + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_RevealAll - Reveals the entire map TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_RevealAll(lua_State* L) { + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_RevealCell - Reveals map around given waypoint TODO: input player * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_RevealCell(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[ret], Rule.GapShroudRadius, PlayerPtr, false); + } + + return 1; + } + + /*********************************************************************************************** + * Script_RevealZone - Reveals area of map around given waypoint TODO: input player * + * * + * Reveal all cells of the zone that the specified waypoint is located in. This can be * + * used to reveal whole islands or bodies of water * + * * + * SCRIPT INPUT: ret (int) - The waypoint index of which to reveal * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_RevealZone(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[ret]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_PlaySound - Plays the given Sound * + * * + * SCRIPT INPUT: ret (int) - The sound index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_PlaySound(lua_State* L) { + int ret = lua_tointeger(L, -1); + + Sound_Effect((VocType)ret); + + return 1; + } + + /*********************************************************************************************** + * Script_PlayMusic - Plays the given Music score. * + * * + * Plays music score at given index. Negative numbers stop any music playing. * + * * + * SCRIPT INPUT: ret (int) - The music index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_PlayMusic(lua_State* L) { + int ret = lua_tointeger(L, -1); + + if (ret < 0) { + Theme.Stop(); + } + else { + Theme.Play_Song((ThemeType)ret); + } + + return 1; + } + + /*********************************************************************************************** + * Script_PlaySpeech - Plays the given Speech audio * + * * + * SCRIPT INPUT: ret (int) - The speech index of which to play * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_PlaySpeech(lua_State* L) { + int ret = lua_tointeger(L, -1); + Speak((VoxType)ret); + return 1; + } + + /*********************************************************************************************** + * Script_StartMissionTimer - Starts the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_StartMissionTimer(lua_State* L) { + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + return 1; + } + + /*********************************************************************************************** + * Script_StopMissionTimer - Stops the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_StopMissionTimer(lua_State* L) { + Scen.MissionTimer.Stop(); + return 1; + } + + /*********************************************************************************************** + * Script_IncreaseMissionTimer - Increases the mission timer by the given amount * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to increase by * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_IncreaseMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); + Map.Redraw_Tab(); + return 1; + } + + /*********************************************************************************************** + * Script_DecreaseMissionTimer - Decreases the mission timer by the given amount * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute to decrease by * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DecreaseMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer - (ret * (TICKS_PER_MINUTE / 10)); + if (Scen.MissionTimer < 0) { Scen.MissionTimer = 0; } + Map.Redraw_Tab(); + return 1; + } + + /*********************************************************************************************** + * Script_SetMissionTimer - Sets and starts the in-game mission timer * + * * + * SCRIPT INPUT: ret (int) - amount of time in tenths of a minute * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetMissionTimer(lua_State* L) { + int ret = lua_tointeger(L, -1); + Scen.MissionTimer = Scen.MissionTimer + (ret * (TICKS_PER_MINUTE / 10)); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + return 1; + } + + /*********************************************************************************************** + * Script_SetGlobalValue - Sets the global value to "set" position (on) * + * * + * SCRIPT INPUT: global_value (int) - The global to set to "set" * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetGlobalValue(lua_State* L) { + int global_value = lua_tointeger(L, -1); + Scen.Set_Global_To(global_value, true); + return 1; + } + + /*********************************************************************************************** + * Script_ClearGlobalValue - Sets the global value to "clear" position (off) * + * * + * SCRIPT INPUT: global_value (int) - The global to set to "off" * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_ClearGlobalValue(lua_State* L) { + int global_value = lua_tointeger(L, -1); + Scen.Set_Global_To(global_value, false); + return 1; + } + + /*********************************************************************************************** + * Script_AutoBaseBuilding - Sets AI basebuilding on/off for given house * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index of the AI * + * on_off (bool) - Enable / disable auto base building * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AutoBaseBuilding(lua_State* L) { + int houseType = lua_tointeger(L, 1); + bool on_off = lua_toboolean(L, 2); + + HouseClass* this_house = Houses.Ptr(houseType); + if (this_house != NULL) { + this_house->IsBaseBuilding = on_off; + } + + return 1; + } + + /*********************************************************************************************** + * Script_StopMissionTimer - Stops the in-game mission timer * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreepShadow(lua_State* L) { + Map.Encroach_Shadow(PlayerPtr); + return 1; + } + + /*********************************************************************************************** + * Script_DestroyTriggerBuilding - Stops the in-game mission timer * + * * + * SCRIPT INPUT: triggerIndex (int) - The index of the trigger previously created * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DestroyTriggerBuilding(lua_State* L) { + int triggerIndex = lua_tointeger(L, -1); + + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); + + if(this_trigger != NULL){ + for (int index = 0; index < Buildings.Count(); index++) { + + BuildingClass* this_building = Buildings.Ptr(index); + + if (this_building->Trigger == this_trigger && this_building->Strength > 0) { + int damage = this_building->Strength; + this_building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + + } + } + + return 1; + } + + /*********************************************************************************************** + * Script_GiveOneTimeSpecialWeapon - Gives specified house a one time special weapon * + * * + * SCRIPT INPUT: weaponType (int) - The special weapon type to give * + * houseType (int) - The player (house) index to give this to (-1 for all) * + * immediate(bool) - should the weapon be fully charged? * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GiveOneTimeSpecialWeapon(lua_State* L) { + + int weaponType = lua_tointeger(L, 1); + + int houseType = -1; + bool immediate = -1; + + // Optional houseType parameter + if (lua_gettop(L) >= 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional immediate parameter + if (lua_gettop(L) >= 2) { + immediate = lua_toboolean(L, 3); + } + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->SuperWeapon[weaponType].Enable(TACTION_1_SPECIAL, true); + + if (immediate) { + house->SuperWeapon[weaponType].Forced_Charge(true); + } + + if (PlayerPtr == house) { + Map.Add(RTTI_SPECIAL, weaponType); + Map.Column[1].Flag_To_Redraw(); + } + + if (houseType >= 0) { + break; + } + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_GiveSpecialWeapon - Gives specified house a repeating special weapon * + * * + * SCRIPT INPUT: weaponType (int) - The special weapon type to give * + * houseType (int) - The player (house) index to give this to (-1 for all) * + * immediate(bool) - should the weapon be fully charged? * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GiveSpecialWeapon(lua_State* L) { + + int weaponType = lua_tointeger(L, 1); + + int houseType = -1; + bool immediate = -1; + + // Optional houseType parameter + if (lua_gettop(L) >= 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional immediate parameter + if (lua_gettop(L) ==3) { + immediate = lua_toboolean(L, 3); + } + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->SuperWeapon[weaponType].Enable(TACTION_FULL_SPECIAL, true); + + if (immediate) { + house->SuperWeapon[weaponType].Forced_Charge(true); + } + + if (PlayerPtr == house) { + Map.Add(RTTI_SPECIAL, weaponType); + Map.Column[1].Flag_To_Redraw(); + } + + if (houseType >= 0) { + break; + } + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_DesignatePreferredTarget - Gives specified house a repeating special weapon * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to set targetting for (or -1) * + * targetType (int) - The target type index to set to * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_DesignatePreferredTarget(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + unsigned char targetType = lua_tointeger(L, 2); + + if(targetType >= 1 && targetType <= 11){ + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + house->PreferredTarget = (QuarryType)targetType; + + // Discontinue giving out free cash if a specific house was specified + if (houseType >= 0) { + break; + } + + } + + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_LaunchFakeNukes - All nuke silos begin launching duds * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_LaunchFakeNukes(lua_State* L) { + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass* bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_MSLO) { + bldg->Assign_Mission(MISSION_MISSILE); + } + } + + return 1; + } + +/********************************************************************************************** +* Red Alert Vanilla Events * +*=============================================================================================*/ + + /*********************************************************************************************** + * Script_CreateCellCallback - Creates an ENTERED_BY trigger and attaches a callback * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index to test for entry * + * in_x (int) - The cell/X location of the cell to test * + * in_y (int) - The cell/Y location of the cell to test * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CellCallback(lua_State* L) { + + // TODO: option 6 arguments to allow x2,y2 (for a rect) + + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); + const char* in_function = lua_tostring(L, 4); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_PLAYER_ENTERED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass * this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + // Get the cell and set the trigger + this_trigger->Cell = XY_Cell(in_x, in_y); + Map[XY_Cell(in_x, in_y)].Trigger = this_trigger; + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_CreateSpiedByCallback - Creates a SPIED_BY trigger and attaches a callback * + * * + * SCRIPT INPUT: objectID (int) - The building ID being spied * + * houseType (int) - The house (player) index doing the spying * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SpiedByCallback(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + int houseType = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + BuildingClass* this_building = Buildings.Ptr(objectID); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count() && this_building != NULL) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)this_building->House->ID; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_SPIED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + // Set the building + this_building->Trigger = this_trigger; + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_CreateDiscoveryCallback - Creates a TEVENT_DISCOVERED trigger and attaches a callback* + * * + * Trigger is sprung when the given object is discovered * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_ObjectDiscoveryCallback(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (objectID >= 0 ) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_DISCOVERED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + Script_SetObjectTrigger(objectID, this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_HouseDiscoveredCallback - Initiates when given house is discovered * + * * + * Trigger is sprung when an object belonging to the given house is discovered * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_HouseDiscoveredCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; + + this_trigger_type->Event1 = TEVENT_HOUSE_DISCOVERED; + this_trigger_type->EventControl = MULTI_ONLY; + + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_ObjectAttackedCallback - Trigger that springs when object is attacked by anyone * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_ObjectAttackedCallback(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // An object must be specified + if (objectID >= 0) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ATTACKED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + Script_SetObjectTrigger(objectID, this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_ObjectDestroyedCallback - Trigger that springs when object is destroyed by anyone * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_ObjectDestroyedCallback(lua_State* L) { + + int objectID = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // An object must be specified + if (objectID >= 0) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + Script_SetObjectTrigger(objectID, this_trigger); + + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_AnyEventCallback - * + * * + * SCRIPT INPUT: objectID (int) - The object's ID being discovered * + * houseType (int) - The house (player) index * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AnyEventCallback(lua_State* L) { + + const char* in_function = lua_tostring(L, 1); + + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ANY; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + /*********************************************************************************************** + * Script_AllUnitsDestroyedCallback - Called when all of a house's units are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllUnitsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_UNITS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_AllBuildingsDestroyedCallback - Called when all of a house's buildings are destroyed* + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllBuildingsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILDINGS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_AllDestroyedCallback - Called when everything belonging to a house is destyroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ALL_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_CreditsReachedCallback - Called when given house meets given amount of credits * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index to test against * + * in_credits (int) - The amount of credits * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CreditsReachedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_credits = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CREDITS; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value= in_credits; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_TimeReachedCallback - Called when the given amount of time has elapsed * + * * + * SCRIPT INPUT: in_time (int) - The time (in tenths of a second) that was reached * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_TimeReachedCallback(lua_State* L) { + + int in_time = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_TIME; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_time; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + /*********************************************************************************************** + * Script_MissionTimerCallback - Called when the mission timer has expired * + * * + * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_MissionTimerCallback(lua_State* L) { + + const char* in_function = lua_tostring(L, 1); + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_MISSION_TIMER_EXPIRED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + /*********************************************************************************************** + * Script_UnitsDestroyedCallback - Called when given number of a house's units are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said units * + * in_number (int) - The number of units that have must be destroyed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_UnitsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_number = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NUNITS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_number; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /************************************************************************************************ + * Script_BuildingsDestroyedCallback - Called when number of a house's buildings are destroyed * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index owner of said buildings * + * in_number (int) - The number of buildings that have must be destroyed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *==============================================================================================*/ + static int Script_BuildingsDestroyedCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_number = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NBUILDINGS_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_number; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_NoFactoriesCallback - Called when no more factories exist for a house * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that got pwned * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_NoFactoriesCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_NOFACTORIES; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = -1; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_CivilianEscapeCallback - Called when civilians get evacuated from map * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner of civilians escaped * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CivilianEscapeCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_EVAC_CIVILIAN; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = -1; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_BuildingBuiltCallback - Called when a specified house builds a specified building * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the building * + * objectType (int) - The index / building type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_BuildingBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_UnitBuiltCallback - Called when a specified house builds a specified unit * + * * + * Does not trigger for units that spawn with buildings (ie. ore truck) * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * objectType (int) - The index / unit type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_UnitBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_UNIT; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_InfantryBuiltCallback - Called when a specified house builds a specified infantry * + * * + * Does not trigger for infantry gained by selling a building * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the infantry * + * objectType (int) - The index / infantry type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_InfantryBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_INFANTRY; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_AircraftBuiltCallback - Called when a specified house builds a specified aircraft * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * objectType (int) - The index / unit type to check for * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AircraftBuiltCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int objectType = lua_tointeger(L, 2); + const char* in_function = lua_tostring(L, 3); + + if (objectType >= 0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_BUILD_AIRCRAFT; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = objectType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_TeamLeavesMapCallback - Called when the specified team leaves the map * + * * + * SCRIPT INPUT: in_team (int) - The team index that has left the map * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_TeamLeavesMapCallback(lua_State* L) { + + int in_team = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + TeamTypeClass* this_team = TeamTypes.Ptr(in_team); + + if (this_team != NULL) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = this_team->House; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_LEAVES_MAP; + this_trigger_type->EventControl = MULTI_ONLY; + + + this_trigger_type->Event1.Team = this_team; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + // Output the new trigger's ID + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_ZoneEntryCallback - Called when a specified house enters zone of given position * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that built the unit * + * in_x (int) - The cell/X location of the cell to test * + * in_y (int) - The cell/Y location of the cell to test * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_ZoneEntryCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); + + const char* in_function = lua_tostring(L, 4); + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ENTERS_ZONE; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + // Get the cell and set the trigger + this_trigger->Cell = XY_Cell(in_x, in_y); + MapTriggers.Add(this_trigger); + + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_HorizontalCrossCallback - Called when a specified house's units cross the given X * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * + * in_x (int) - The cell/X location that must be crossed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_HorizontalCrossCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_x = lua_tointeger(L,2); + + const char* in_function = lua_tostring(L, 3); + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CROSS_VERTICAL; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + // Find a free spot and set the trigger + int addY = 0; + for (addY = 0; addY < Map.MapCellHeight; addY++) { + TriggerClass* trigger = Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger; + if (trigger == NULL) { + this_trigger->Cell = XY_Cell(in_x, Map.MapCellY + addY); + Map[XY_Cell(in_x, Map.MapCellY + addY)].Trigger = this_trigger; + break; + } + } + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_VerticalCrossCallback - Called when a specified house's units cross the given Y * + * * + * Does not trigger for units that spawn with buildings * + * * + * SCRIPT INPUT: houseType (int) - The house (player) owner that owns the unit * + * in_y (int) - The cell/Y location that must be crossed * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_VerticalCrossCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + const char* in_function = lua_tostring(L, 3); + + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_CROSS_HORIZONTAL; // <- Their description doesn't make sense, not mine + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + // Find a free spot and set the trigger + int addX = 0; + for (addX = 0; addX < Map.MapCellWidth; addX++) { + TriggerClass* trigger = Map[XY_Cell(Map.MapCellX + addX, in_y)].Trigger; + if (trigger == NULL) { + this_trigger->Cell = XY_Cell(Map.MapCellX+ addX, in_y); + Map[XY_Cell(Map.MapCellX+ addX, in_y)].Trigger = this_trigger; + break; + } + } + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + + } + + /*********************************************************************************************** + * Script_GlobalSetCallback - activated when the global value is in the "set" poisition * + * * + * SCRIPT INPUT: in_global - The global value to set * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GlobalSetCallback(lua_State* L) { + + int in_global = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_GLOBAL_SET; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_global; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + } + + /*********************************************************************************************** + * Script_GlobalSetCallback - activated when the global value is in the "cleared" poisition * + * * + * SCRIPT INPUT: in_global - The global value to set to "cleared" * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GlobalClearedCallback(lua_State* L) { + + int in_global = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_GLOBAL_CLEAR; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = in_global; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + } + + /*********************************************************************************************** + * Script_LowPowerCallback - Initiates when given house becomes low on power * + * * + * SCRIPT INPUT: houseType (int) - The house (player) index that has been discovered * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_LowPowerCallback(lua_State* L) { + + int houseType = lua_tointeger(L, 1); + const char* in_function = lua_tostring(L, 2); + + // A house must be specified + if (houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; + + this_trigger_type->Event1 = TEVENT_LOW_POWER; + this_trigger_type->EventControl = MULTI_ONLY; + + this_trigger_type->Event1.Data.House = (HousesType)houseType; + + // Create instance of trigger type + + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[PlayerPtr->Class->House].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + + /*********************************************************************************************** + * Script_AllBridgesDestroyedCallback - Called when all map's bridges have been destroyed * + * * + * SCRIPT INPUT: in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_AllBridgesDestroyedCallback(lua_State* L) { + + const char* in_function = lua_tostring(L, 1); + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = HousesType::HOUSE_NONE; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::PERSISTANT; + + this_trigger_type->Event1 = TEVENT_ALL_BRIDGES_DESTROYED; + this_trigger_type->EventControl = MULTI_ONLY; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + LogicTriggers.Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + /*********************************************************************************************** + * Script_BuildingExistsCallback - Called if a given building exists for a given house * + * * + * SCRIPT INPUT: buildingType (int) - The building type to check for * + * houseType (int) - The house (player) index owner of said building * + * in_function (string) - The callback function to execute upon trigger * + * * + * SCRIPT OUTPUT: triggerID - The ID of the trigger created for use with Trigger Functions * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_BuildingExistsCallback(lua_State* L) { + + int buildingType = lua_tointeger(L, 1); + int houseType = lua_tointeger(L,2); + const char* in_function = lua_tostring(L,3); + + // A house must be specified + if (buildingType>=0 && houseType >= 0 && houseType < HouseTypes.Count()) { + + // Create trigger type + TriggerTypeClass* this_trigger_type = new TriggerTypeClass(); + this_trigger_type->House = (HousesType)houseType; + this_trigger_type->IsActive = 1; + this_trigger_type->IsPersistant = TriggerTypeClass::VOLATILE; + this_trigger_type->Event1 = TEVENT_BUILDING_EXISTS; + this_trigger_type->EventControl = MULTI_ONLY; + this_trigger_type->Event1.Data.Value = buildingType; + + // Create instance of trigger type + TriggerClass* this_trigger = Find_Or_Make(this_trigger_type); + + // Copy the callback function into the trigger + strncpy(this_trigger->MapScriptCallback, in_function, sizeof(this_trigger->MapScriptCallback) - 1); + + HouseTriggers[(HousesType)houseType].Add(this_trigger); + + // Output the new trigger's ID + lua_pushnumber(L, this_trigger->ID); + + return 1; + + } + + lua_pushnumber(L, -1); + + return 1; + } + +/********************************************************************************************** +* Trigger Utility Functions * +*=============================================================================================*/ + + /*********************************************************************************************** + * Script_Script_TriggerAddCell - Adds a cell to any cell based callback * + * * + * The map editor will always be the fastest way to do this, but here in case it's needed * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which to add a cell * + * in_x (int) - The Cell/X location of the new cell * + * in_y (int) - The Cell/Y location of the new cell * + * in_x2 (int) (opt. gr. 2) - The second Cell/X location (for bounding box) * + * in_y2 (int) (opt. gr. 2) - The second Cell/Y location (for bounding box) * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_TriggerAddCell(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + + int in_x = lua_tointeger(L, 2); + int in_y = lua_tointeger(L, 3); + + int in_x2=in_x; + int in_y2=in_y; + + // Optional secondary group bounding box parameter + if (lua_gettop(L) == 5) { + in_x2 = lua_tointeger(L, 4); + in_y2 = lua_tointeger(L, 5); + } + + TriggerClass* this_trigger = Triggers.Ptr(triggerIndex); + + if(this_trigger != NULL){ + for (int _x = in_x; _x <= in_x2; _x++) { + for (int _y = in_y; _y <= in_y2; _y++) { + Map[XY_Cell(_x, _y)].Trigger = this_trigger; + } + } + } + + + + return 1; + } + + /*********************************************************************************************** + * Script_SetTriggerCallback - Adds a lua callback to an existing trigger * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger of which to get * + * * + * callbackName (string) - The function name to be called when the * + * actions have been met * + * * + * actionIndex (int) (opt.) - 0: always callback on any of the given paths * + * of the trigger * + * 1: only callback on first action * + * 2: only callback on second action * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetTriggerCallback(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + + const char* callbackName = lua_tostring(L, 2); + char actionIndex = 0; + + // Optional actionIndex parameter + if (lua_gettop(L) == 3) { + actionIndex = lua_tointeger(L, 2); + } + + TriggerClass* trigger = Triggers.Ptr(triggerIndex); + + if (trigger != NULL) { + + strncpy(trigger->MapScriptCallback, callbackName, sizeof(trigger->MapScriptCallback) - 1); + trigger->MapScriptActionIndex = actionIndex; + + } + + return 1; + } + + /*********************************************************************************************** + * Script_GetCellObject - Gets an object (if any) on a cell and returns the ID * + * * + * SCRIPT INPUT: in_x (int) - The cell/X location of the cell to pick from * + * in_y (int) - The cell/Y location of the cell to pick from * + * * + * SCRIPT OUTPUT: result (number) - The ID of the building on the cell, or -1 if none * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetCellObject(lua_State* L) { + + int in_x = lua_tointeger(L, 1); + int in_y = lua_tointeger(L, 2); + + long result = -1; + + ObjectClass* this_object = Map[XY_Cell(in_x, in_y)].Cell_Object(); + + if (this_object == NULL) { + + lua_pushnumber(L, -1); + return 1; + } + + MapScriptObject* CacheObject = new MapScriptObject; + + CacheObject->ID = this_object->ID; + CacheObject->RTTI = this_object->RTTI; + CacheObject->classIndex = -1; + + switch (this_object->RTTI) { + case RTTI_BUILDING: { + CacheObject->classIndex = Script_BuildingIndexFromID(this_object->ID); + }break; + case RTTI_UNIT: { + CacheObject->classIndex = Script_UnitIndexFromID(this_object->ID); + }break; + case RTTI_AIRCRAFT: { + CacheObject->classIndex = Script_AircraftIndexFromID(this_object->ID); + }break; + case RTTI_INFANTRY: { + CacheObject->classIndex = Script_InfantryIndexFromID(this_object->ID); + }break; + case RTTI_VESSEL: { + CacheObject->classIndex = Script_VesselIndexFromID(this_object->ID); + }break; + } + + if (CacheObject->classIndex == -1 || CacheObject->ID < 0) { + delete CacheObject; + lua_pushnumber(L, -1); + } + else { + Scen.mapScript->ObjectCache.push_back(CacheObject); + lua_pushnumber(L, CacheObject->ID); // Return index for ultrafast lookup + } + + return 1; + } + + /*********************************************************************************************** + * Script_Merge_Triggers - Adds the event from trigger 2 to trigger 1 and discards trigger 2 * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which will be the result * + * mergeTriggerIndex (int)- The trigger ID of which is being merged * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_MergeTriggers(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + int mergeTriggerIndex = lua_tointeger(L, 2); + + TriggerClass* resulting_trigger = Triggers.Ptr(triggerIndex); + TriggerTypeClass* resulting_trigger_type = resulting_trigger->Class; + TriggerClass* merge_trigger = Triggers.Ptr(mergeTriggerIndex); + + if (resulting_trigger != NULL && merge_trigger != NULL) { + + // Set event 2 to event 1 of the merge trigger + resulting_trigger_type->Event2.Event = (TEventType)merge_trigger->Class->Event1.Event; + resulting_trigger_type->Event2.Data.Value = (long)merge_trigger->Class->Event1.Data.Value; + resulting_trigger_type->Event2.Team = (TeamTypeClass*)merge_trigger->Class->Event1.Team; + + // Make this an "AND" coniditional trigger (event 1 AND event 2) + resulting_trigger->Class->EventControl = MULTI_AND; + resulting_trigger->Class->ActionControl = MULTI_ONLY; + + // TODO: only do these individual cleanups for actions that they represent (for efficiency) + + // Now switch over any LogicTrigger references (we're getting rid of the second trigger) + for (int t_index = 0; t_index < LogicTriggers.Count(); t_index++) { + + TriggerClass* _trigger = LogicTriggers[t_index]; + + if (_trigger == merge_trigger) { + + LogicTriggers.Delete(t_index); + + bool find_existing_result; + for (int t2_index = 0; t2_index < LogicTriggers.Count(); t2_index++) { + if (LogicTriggers[t2_index] == resulting_trigger) { + find_existing_result = true; + break; + } + } + if (!find_existing_result) { + LogicTriggers.Add(resulting_trigger); + } + + break; + } + + } + + // Now switch over any MapTrigger references (we're getting rid of the second trigger) + for (int t_index = 0; t_index < MapTriggers.Count(); t_index++) { + TriggerClass* _trigger = MapTriggers[t_index]; + + if (_trigger != NULL) { + if (_trigger == merge_trigger) { + + MapTriggers.Delete(t_index); + + bool find_existing_result; + for (int t2_index = 0; t2_index < MapTriggers.Count(); t2_index++) { + if (MapTriggers[t2_index] == resulting_trigger) { + find_existing_result = true; + break; + } + } + if (!find_existing_result) { + MapTriggers.Add(resulting_trigger); + } + + break; + } + } + + } + + // Now switch over any HouseTrigger references (we're getting rid of the second trigger) + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + for (int t_index = 0; t_index < HouseTriggers[h_index].Count(); t_index++) { + + TriggerClass* _trigger = HouseTriggers[h_index][t_index]; + + if(_trigger != NULL){ + if (_trigger == merge_trigger) { + + HouseTriggers[h_index].Delete(t_index); + + bool find_existing_result; + for (int t2_index = 0; t2_index < HouseTriggers[h_index].Count(); t2_index++) { + if (HouseTriggers[h_index][t2_index] == resulting_trigger) { + find_existing_result = true; + break; + } + } + if (!find_existing_result) { + HouseTriggers[h_index].Add(resulting_trigger); + } + + break; + } + } + + } + } + + // Go through the map and replace any celltriggers + for (int _x = Map.MapCellX; _x <= Map.MapCellX + Map.MapCellWidth; _x++) { + for (int _y = Map.MapCellY; _y <= Map.MapCellY + Map.MapCellHeight; _y++) { + if (Map[XY_Cell(_x, _y)].Trigger == merge_trigger) { + Map[XY_Cell(_x, _y)].Trigger = resulting_trigger; + } + } + } + + // Now that we've merged the contents, and it's no longer being referred to, we can safely delete the merge trigger + Detach_This_From_All(merge_trigger->As_Target()); + delete merge_trigger; + } + + return 1; + } + + /*********************************************************************************************** + * Script_GetTriggerByName - Gets the trigger with a given name * + * * + * SCRIPT INPUT: triggerName (string); The input trigger name * + * * + * SCRIPT OUTPUT: triggerID (int) - The index of the trigger with this name * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetTriggerByName(lua_State* L) { + + const char* triggerName= lua_tostring(L, 1); + + // Find the trigger and set up callback + for (int t_index = 0; t_index < Triggers.Count(); t_index++) { + TriggerClass* trigger = Triggers.Ptr(t_index); + + if (trigger != NULL) { + + if (strcmp(trigger->Name(), triggerName) == 0) { + + TriggerTypeClass* this_type = trigger->Class; + lua_pushnumber(L, t_index); + + return 1; + + break; + } + + } + + } + + lua_pushnumber(L, -1); + + return -1; + } + + /*********************************************************************************************** + * Script_TriggerSetPersistence - Sets whether a trigger runs once or indefinitely * + * * + * SCRIPT INPUT: triggerIndex (int) - The trigger ID of which this will apply * + * isPersistent (int) - The persistence value * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_TriggerSetPersistence(lua_State* L) { + + int triggerIndex = lua_tointeger(L, 1); + unsigned char isPersistent = lua_tointeger(L, 2); + + TriggerClass* trigger = Triggers.Ptr(triggerIndex); + + if (trigger != NULL) { + + TriggerTypeClass* trigger_type = trigger->Class; + + trigger_type->IsPersistant = (TriggerTypeClass::PersistantType)isPersistent; + + + } + + return -1; + } + + + +/********************************************************************************************** +* Mission Utility Functions * +*=============================================================================================*/ + + /*********************************************************************************************** + * Script_SetBriefingText - Sets the text on the mission briefing screen * + * * + * SCRIPT INPUT: (char*); The input text * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_SetBriefingText(lua_State* L) { + strcpy(Scen.BriefingText, lua_tostring(L, -1)); + return -1; + } + +/********************************************************************************************** +* Status / Location Utility Functions * +*=============================================================================================*/ + + /*********************************************************************************************** + * Script_GiveCredits - Gives or takes a given amount of credits to / from a player * + * * + * SCRIPT INPUT: cashToGive (int) - The amount of cold hard credits to give the player. * + * Negative numbers may be used to *take* money * + * * + * houseType (int) - The player (house) index to complete this transaction * + * for, or -1 to give to all players * + * * + * SCRIPT OUTPUT: void * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + * HISTORY: * + * 6/11/2020 - JM Created * + * 6/12/2020 - JJ Added player (house) index input * + *=============================================================================================*/ + static int Script_GiveCredits(lua_State* L) { + + int cashToGive = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + + if (cashToGive < 0) { + house->Spend_Money(max(0, cashToGive * -1)); + } + else { + house->Refund_Money(cashToGive); + } + + // Discontinue giving out free cash if a specific house was specified + if (houseType >= 0) { + break; + } + } + + } + + return 1; + } + + /*********************************************************************************************** + * Script_GetCredits - Gets how many credits the given player has * + * * + * SCRIPT INPUT: houseType (int) - The player (house) index to get credits for * + * * + * SCRIPT OUTPUT: result (number) - The amount of credits the player has * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetCredits(lua_State* L) { + + int houseType = lua_tointeger(L, -1); + + long result = -1; + + for (int h_index = 0; h_index < Houses.Count(); h_index++) { + + HouseClass* house = Houses.Ptr(h_index); + + if (house->ID == houseType || houseType == -1) { + result = house->Credits; + + break; + } + + } + + lua_pushnumber(L, result); + + return 1; + } + + /*********************************************************************************************** + * Script_CountBuildings - Returns the amount of specific buildings for a player * + * * + * Optionally, you can return only building at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: structType (int) - The structure index to count buildings for * + * or -1 for all structures * + * * + * houseType (int) - The player (house) index to count buildings for * + * or -1 to for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include buildings at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include buildings at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include buildings at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include buildings at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching buildings for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CountBuildings(lua_State* L) { + int structType = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + + int result = 0; + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass* building = Buildings.Ptr(b_index); + + if (building->Owner() == houseType || houseType == -1) { + if (building->Class->Type == structType || structType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area + } + else { + + int this_x = Cell_X(Coord_Cell(building->Coord)); + int this_y = Cell_Y(Coord_Cell(building->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + + } + } + } + + lua_pushnumber(L, result); + return 1; + } + + /*********************************************************************************************** + * Script_CountAircraft - Returns the amount of specific aircraft for a player * + * * + * Optionally, you can return only aircraft at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: aircraftType (int) - The aircraft index to count aircraft for * + * or -1 for all aircraft * + * * + * houseType (int) - The player (house) index to count aircraft for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include aircraft at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include aircraft at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include aircraft at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include aircraft at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching aircraft for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CountAircraft(lua_State* L) { + int aircraftType = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + + int result = 0; + for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { + AircraftClass* aircraft = Aircraft.Ptr(a_index); + + if (aircraft->Owner() == houseType || houseType == -1) { + if (aircraft->Class->Type == aircraftType || aircraftType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area + } + else { + + int this_x = Cell_X(Coord_Cell(aircraft->Coord)); + int this_y = Cell_Y(Coord_Cell(aircraft->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + + } + } + } + + lua_pushnumber(L, result); + return 1; + } + + /*********************************************************************************************** + * Script_CountUnits - Returns the amount of specific units for a player * + * * + * Optionally, you can return only units at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: unitType (int) - The unit index to count units for * + * or -1 for all units * + * * + * houseType (int) - The player (house) index to count units for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include units at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include units at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include units at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include units at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching units for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CountUnits(lua_State* L) { + int unitType = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + + int result = 0; + for (int u_index = 0; u_index < Units.Count(); u_index++) { + UnitClass* unit = Units.Ptr(u_index); + + if (unit->Owner() == houseType || houseType == -1) { + if (unit->Class->Type == unitType || unitType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area + } + else { + + int this_x = Cell_X(Coord_Cell(unit->Coord)); + int this_y = Cell_Y(Coord_Cell(unit->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + + } + } + } + + lua_pushnumber(L, result); + return 1; + } + + /*********************************************************************************************** + * Script_CountInfantry - Returns the amount of specific infantry for a player * + * * + * Optionally, you can return only infantry at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: infantryType (int) - The infantry index to count infantry for * + * or -1 for all infantry * + * * + * houseType (int) - The player (house) index to count infantry for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include infantry at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include infantry at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include infantry at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include infantry at or before this Cell/Y * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching infantry for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CountInfantry(lua_State* L) { + int infantryType = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + + int result = 0; + for (int u_index = 0; u_index < Infantry.Count(); u_index++) { + InfantryClass* infantry = Infantry.Ptr(u_index); + + if (infantry->Owner() == houseType || houseType == -1) { + if (infantry->Class->Type == infantryType || infantryType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area + } + else { + + int this_x = Cell_X(Coord_Cell(infantry->Coord)); + int this_y = Cell_Y(Coord_Cell(infantry->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + + } + } + } + + lua_pushnumber(L, result); + return 1; + } + + /*********************************************************************************************** + * Script_CountVessels - Returns the amount of specific vessels for a player * + * * + * Optionally, you can return only vessels at a particular cell or in a rectangular area * + * using the two groups of optional coordinate inputs * + * * + * SCRIPT INPUT: vesselType (int) - The vessel index to count vessels for * + * or -1 for all vessels * + * * + * houseType (int) - The player (house) index to count vessels for * + * or -1 for all players * + * * + * NOTE: -1 for either index input acts as ALL * + * * + * in_x1 (int) (optional A) - Only include vessels at or beginning at this * + * Cell/X location * + * * + * in_y1 (int) (optional A) - Only include vessels at or beginning at this * + * Cell/Y location * + * * + * in_x2 (int) (optional B) - Only include vessels at or before this Cell/X * + * location * + * * + * in_y2 (int) (optional B) - Only include vessels at or before this Cell/X * + * location * + * * + * SCRIPT OUTPUT: result (number) - The amount of matching vessels for that player * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_CountVessels(lua_State* L) { + int vesselType = lua_tointeger(L, 1); + + int houseType = -1; + + // Optional houseType parameter + if (lua_gettop(L) == 2) { + houseType = lua_tointeger(L, 2); + } + + // Optional search area + bool within_area = false; + int in_x1, in_x2, in_y1, in_y2; + if (lua_gettop(L) == 4) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = in_x1; + in_y2 = in_y1; + within_area = true; + } + else if (lua_gettop(L) == 6) { + in_x1 = lua_tointeger(L, 3); + in_y1 = lua_tointeger(L, 4); + in_x2 = lua_tointeger(L, 5); + in_y2 = lua_tointeger(L, 6); + within_area = true; + } + + int result = 0; + for (int u_index = 0; u_index < Vessels.Count(); u_index++) { + VesselClass* vessel = Vessels.Ptr(u_index); + + if (vessel->Owner() == houseType || houseType == -1) { + if (vessel->Class->Type == vesselType || vesselType == -1) { + + // Count, in general + if (within_area == false) { + result++; + + // Within search area + } + else { + + int this_x = Cell_X(Coord_Cell(vessel->Coord)); + int this_y = Cell_Y(Coord_Cell(vessel->Coord)); + + if (this_x >= in_x1 && this_x <= in_x2 && this_y >= in_y1 && this_y <= in_y2) { + result++; + } + } + + } + } + } + + lua_pushnumber(L, result); + return 1; + } + + /*********************************************************************************************** + * Script_GetWaypointX - Gets the Cell/X Coordinate of Waypoint * + * * + * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the X coordinate for * + * * + * SCRIPT OUTPUT: outX (int) - The Cell/X coordinate of the waypoint * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetWaypointX(lua_State* L) { + + int ret = lua_tointeger(L, -1); + + if (ret > 0 && ret < sizeof(Scen.Waypoint)) { + + lua_pushnumber(L, Cell_X(Scen.Waypoint[ret])); + + return 1; + + } + + lua_pushnumber(L, -1); + + return -1; + } + + /*********************************************************************************************** + * Script_GetWaypointY - Gets the Cell/Y Coordinate of Waypoint * + * * + * SCRIPT INPUT: waypointIndex (int) - The waypoint's index to grab the Y coordinate for * + * * + * SCRIPT OUTPUT: outX (int) - The Cell/Y coordinate of the waypoint * + * * + * INPUT: lua_State - The current Lua state * + * * + * OUTPUT: int; Did the function run successfully? Return 1 * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + static int Script_GetWaypointY(lua_State* L) { + + int ret = lua_tointeger(L, -1); + + if (ret > 0 && ret < sizeof(Scen.Waypoint)) { + + lua_pushnumber(L, Cell_Y(Scen.Waypoint[ret])); + + return 1; + + } + + lua_pushnumber(L, -1); + + return -1; + } + +/********************************************************************************************** +* MapScript Internal Functions * +*=============================================================================================*/ + + /*********************************************************************************************** + * MapScript::GetCacheObject -- Returns ObjectClass from cache with given ID * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: this_object (ObjectClass*) - The object with the ID or NULL * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + ObjectClass* Script_GetCacheObject(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + // Look through cache for object with ID, and return it + for (std::vector::iterator it = Scen.mapScript->ObjectCache.begin(); it != Scen.mapScript->ObjectCache.end(); ++it) {//Error 2-4 + + MapScriptObject* thisCacheObject = (MapScriptObject*)(*it); + + // Here it is + if (thisCacheObject->ID == input_object_id) { + + // Determine type of object and return it from cache. If it doesn't exist any longer, remove it from cache + switch (thisCacheObject->RTTI) { + case RTTI_BUILDING: { + BuildingClass* this_building = Buildings.Ptr(thisCacheObject->classIndex); + if (this_building != NULL) { + return this_building; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_UNIT: { + UnitClass* this_unit = Units.Ptr(thisCacheObject->classIndex); + if (this_unit != NULL) { + return this_unit; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_AIRCRAFT: { + AircraftClass* this_aircraft = Aircraft.Ptr(thisCacheObject->classIndex); + if (this_aircraft != NULL) { + return this_aircraft; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_INFANTRY: { + InfantryClass* this_infantry = Infantry.Ptr(thisCacheObject->classIndex); + if (this_infantry != NULL) { + return this_infantry; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + case RTTI_VESSEL: { + VesselClass* this_vessel = Vessels.Ptr(thisCacheObject->classIndex); + if (this_vessel != NULL) { + return this_vessel; + } + else { + it = Scen.mapScript->ObjectCache.erase(it); // Cache item exists but is invalidated; remove from cache + return NULL; + } + }break; + } + + } + + } + + // Not found in cache; let's find the object by ID the hard way... If scripting is done well, this doesn't get hit + int _objectIndex = -1; + MapScriptObject* CacheObject = new MapScriptObject; + CacheObject->ID = -1; + + _objectIndex = Script_BuildingIndexFromID(input_object_id); + BuildingClass* this_building = Buildings.Ptr(_objectIndex); + if (this_building != NULL) { + + CacheObject->ID = this_building->ID; + CacheObject->RTTI = this_building->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_building; + + } + else { + _objectIndex = Script_UnitIndexFromID(input_object_id); + UnitClass* this_unit = Units.Ptr(_objectIndex); + if (this_unit != NULL) { + + CacheObject->ID = this_unit->ID; + CacheObject->RTTI = this_unit->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_unit; + + } + else { + _objectIndex = Script_AircraftIndexFromID(input_object_id); + AircraftClass* this_aircraft = Aircraft.Ptr(_objectIndex); + if (this_aircraft != NULL) { + + CacheObject->ID = this_aircraft->ID; + CacheObject->RTTI = this_aircraft->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_aircraft; + + } + else { + _objectIndex = Script_VesselIndexFromID(input_object_id); + VesselClass* this_vessel = Vessels.Ptr(_objectIndex); + if (this_vessel != NULL) { + + CacheObject->ID = this_vessel->ID; + CacheObject->RTTI = this_vessel->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_vessel; + + } + else { + _objectIndex = Script_InfantryIndexFromID(input_object_id); + InfantryClass* this_infantry = Infantry.Ptr(_objectIndex); + if (this_infantry != NULL) { + + CacheObject->ID = this_infantry->ID; + CacheObject->RTTI = this_infantry->RTTI; + CacheObject->classIndex = _objectIndex; + Scen.mapScript->ObjectCache.push_back(CacheObject); + + return this_infantry; + + } + } + } + } + } + + // Looks like we didn't find anything. They probably shouldn't have gotten this far in the first place so it was worth a try. + delete CacheObject; + + return NULL; + } + + /*********************************************************************************************** + * Script_SetObjectTrigger -- Figures out what kind of object is given and sets up trigger * + * * + * INPUT: input_object (ObjectClass*) - Input object * + * input_trigger (TriggerClass*) - Input Trigger * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger) { + + if (input_object_id < 0) { + return false; + } + + if (input_trigger == NULL) { + return false; + } + + ObjectClass* this_object = Script_GetCacheObject(input_object_id); + + if (this_object != NULL) { + + switch (this_object->RTTI) { + case RTTI_BUILDING:{ + input_trigger->Class->Event1.Data.Structure = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_UNIT:{ + input_trigger->Class->Event1.Data.Unit = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_AIRCRAFT:{ + input_trigger->Class->Event1.Data.Aircraft = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + case RTTI_INFANTRY:{ + input_trigger->Class->Event1.Data.Infantry = dynamic_cast(this_object)->Class->Type; + dynamic_cast(this_object)->Trigger = input_trigger; + }break; + } + + } + + return false; + } + + /*********************************************************************************************** + * Script_BuildingIndexFromID -- Returns BuildingClass or NULL depending on the input * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + int Script_BuildingIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Buildings.Count(); _index++) { + BuildingClass* _object = Buildings.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; + } + + /*********************************************************************************************** + * Script_UnitIndexFromID -- Returns UnitClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input Unit ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + int Script_UnitIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Units.Count(); _index++) { + UnitClass* _object = Units.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; + } + + /*********************************************************************************************** + * Script_AircraftIndexFromID -- Returns AircraftClass* or NULL depending on the input * + * * + * INPUT: input_object_id (int) - Input object ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + int Script_AircraftIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Aircraft.Count(); _index++) { + AircraftClass* _object = Aircraft.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; + } + + /*********************************************************************************************** + * Script_InfantryIndexFromID -- Returns InfantryClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input Infantry ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + int Script_InfantryIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Infantry.Count(); _index++) { + InfantryClass* _object = Infantry.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; + } + + /*********************************************************************************************** + * Script_VesselIndexFromID -- Returns VesselClass* or NULL depending on the input * + * * + * INPUT: input_unit_id (int) - Input vessel ID * + * * + * OUTPUT: bool; Was the object found? Does the trigger exist? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + int Script_VesselIndexFromID(int input_object_id) { + + if (input_object_id < 0) { + return false; + } + + for (int _index = 0; _index < Vessels.Count(); _index++) { + VesselClass* _object = Vessels.Ptr(_index); + + if (_object->ID == input_object_id) { + return _index; + } + } + + return -1; + } + + /*********************************************************************************************** + * MapScript::CallFunction - Calls a given function within the current script * + * * + * INPUT: functionName - The name of the function to call * + * * + * OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + void MapScript::CallFunction(const char* functionName) { + + lua_getglobal(L, functionName); + lua_pcall(L, 0, 0, 0); + + } + + /*********************************************************************************************** + * MapScript::setLuaPath - Sets Lua's path / extensions to search for scripts * + * * + * This helps Lua know where/how to look for files when using require(), otherwise we could * + * hard code path/extension as we did previously * + * * + * INPUT: pathvalue - See lua documentation RE: LUA_PATH to know how to deal with this * + * * + * OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + void MapScript::SetLuaPath( const char* input_path) + { + + // Needs space to keep the initial paths + char new_path[1024]; + + // Set new GLOBALS.package.path + lua_getglobal(L, "package"); + lua_getfield(L, -1, "path"); // get field "path" from table at top of stack (-1) + const char* cur_path = lua_tostring(L, -1); // grab path string from top of stack + + sprintf(new_path, "%s;%s", cur_path, input_path); + + lua_pop(L, 1); + lua_pushstring(L, new_path); + lua_setfield(L, -2, "path"); + lua_pop(L, 1); + } + + /*********************************************************************************************** + * MapScript::Deinit - Deinitialize/free any data used by MapScript (used within destructor) * + * * + * SCRIPT INPUT: none * + * * + * SCRIPT OUTPUT: void * + * * + * WARNINGS: ? * + * * + *=============================================================================================*/ + void MapScript::Deinit() { + + for (int i = 0; i < ObjectCache.size(); ++i) + { + delete ObjectCache[i]; + } + + ObjectCache.clear(); + + } + + /*********************************************************************************************** + * MapScript::Init -- Loads and initializes script for a given map * + * * + * This routine is called at the beginning of a mission * + * * + * INPUT: mapName - The script path * + * * + * OUTPUT: bool; did the map script load and initialize? * + * * + * WARNINGS: none * + * * + *=============================================================================================*/ + bool MapScript::Init(const char* mapName) { + char scriptName[256]; + + // Build map path + sprintf(scriptName, "scripts/%s.script", mapName); + L = luaL_newstate(); + + luaL_openlibs(L); + + // Load file + if (luaL_loadfile(L, scriptName)){ + L = NULL; + return false; + } + + // TODO: I've tried several combinations of paths (including "scripts/?"), + // but I can only get "require 'basefilename'" to work within a + // script, not "require 'basefilename.script'". This may be completely + // normal within lua, but it seems odd. + + // This allows require() to work + SetLuaPath(";scripts/?.script;"); + + if (lua_pcall(L, 0, 0, 0)) { + L = NULL; + return false; + } + + // Reserve object cache memory + ObjectCache.reserve(2000); + + /********************************************************************************************** + * Red Alert Vanilla Actions * + *=============================================================================================*/ + + lua_register(L, "Win", Script_Win); // player wins! + lua_register(L, "Lose", Script_Lose); // player loses. + lua_register(L, "BeginProduction", Script_BeginProduction); // computer begins factory production. + lua_register(L, "CreateTeam", Script_CreateTeam); // computer creates a certain type of team + lua_register(L, "DestroyTeam", Script_DestroyTeam); // destroy team (comment was redacted. was something offensive originally written?) + lua_register(L, "AllHunt", Script_AllHunt); // all enemy units go into hunt mode (teams destroyed). + lua_register(L, "Reinforcements", Script_Reinforcements); // player gets reinforcements (house that gets them is determined by the Reinforcement instance) + lua_register(L, "DropZoneFlare", Script_DropZoneFlare); // Deploy drop zone smoke. + lua_register(L, "FireSale", Script_FireSale); // Sell all buildings and go on rampage. + lua_register(L, "PlayMovie", Script_PlayMovie); // Play movie (temporarily suspend game). + lua_register(L, "TriggerText", Script_TriggerText); // Triggers a text message display. + lua_register(L, "DestroyTrigger", Script_DestroyTrigger); // Destroy specified trigger. + lua_register(L, "AutoCreate", Script_AutoCreate); // Computer to autocreate teams. + // Win if captured, lose if destroyed // function does not exist + lua_register(L, "AllowWin", Script_AllowWin); // Allows winning if triggered. + lua_register(L, "RevealAll", Script_RevealAll); // Reveal the entire map. + lua_register(L, "RevealCell", Script_RevealCell); // Reveal map around cell #. + lua_register(L, "RevealZone", Script_RevealZone); // Reveal all of specified zone. + lua_register(L, "PlaySound", Script_PlaySound); // Play sound effect. + lua_register(L, "PlayMusic", Script_PlayMusic); // Play musical score. + lua_register(L, "PlaySpeech", Script_PlaySpeech); // Play EVA speech. + // TODO: Force trigger to activate. + lua_register(L, "StartMissionTimer", Script_StartMissionTimer); // Start mission timer. + lua_register(L, "StopMissionTimer", Script_StopMissionTimer); // Stop mission timer. + lua_register(L, "IncreaseMissionTimer", Script_IncreaseMissionTimer); // Increase mission timer time. + lua_register(L, "DecreaseMissionTimer", Script_DecreaseMissionTimer); // Decrease mission timer time. + lua_register(L, "SetMissionTimer", Script_SetMissionTimer); // Set and start the mission timer. + lua_register(L, "SetGlobalValue", Script_SetGlobalValue); // Set global variable. + lua_register(L, "ClearGlobalValue", Script_ClearGlobalValue); // Clear global variable. + lua_register(L, "AutoBaseBuilding", Script_AutoBaseBuilding); // Automated base building. + lua_register(L, "CreepShadow", Script_CreepShadow); // Shadow grows back one 'step'. + lua_register(L, "DestroyTriggerBuilding", Script_DestroyTriggerBuilding); // Destroys the building this trigger is attached to. (in the case of scripging, we can supply the trigger) + lua_register(L, "GiveOneTimeSpecialWeapon", Script_GiveOneTimeSpecialWeapon); // Add a one-time special weapon ability to house. + lua_register(L, "GiveSpecialWeapon", Script_GiveSpecialWeapon); // Add a repeating special weapon ability to house. + lua_register(L, "DesignatePreferredTarget", Script_DesignatePreferredTarget); // Designates preferred target for house. + lua_register(L, "LaunchFakeNukes", Script_LaunchFakeNukes); // Launch fake nuclear missiles from all silos + + /********************************************************************************************** + * Red Alert Vanilla Events * + *=============================================================================================*/ + + lua_register(L, "CellCallback", Script_CellCallback); // player enters this square (or group of squares) + lua_register(L, "SpiedByCallback", Script_SpiedByCallback); // Spied by. + // Thieved by (raided or stolen vehicle). // This doesn't appear to be implemented in the engine (?) + lua_register(L, "ObjectDiscoveryCallback", Script_ObjectDiscoveryCallback); // player discovers this object + lua_register(L, "HouseDiscoveredCallback", Script_HouseDiscoveredCallback); // House has been discovered. + lua_register(L, "ObjectAttackedCallback", Script_ObjectAttackedCallback); // player attacks this object + lua_register(L, "ObjectDestroyedCallback", Script_ObjectDestroyedCallback); // player destroys this object + lua_register(L, "AnyEventCallback", Script_AnyEventCallback); // Any object event will cause the trigger. + lua_register(L, "AllUnitsDestroyedCallback", Script_AllUnitsDestroyedCallback); // all house's units destroyed + lua_register(L, "AllBuildingsDestroyedCallback", Script_AllBuildingsDestroyedCallback); // all house's buildings destroyed + lua_register(L, "AllDestroyedCallback", Script_AllDestroyedCallback); // all house's units & buildings destroyed + lua_register(L, "CreditsReachedCallback", Script_CreditsReachedCallback); // house reaches this many credits + lua_register(L, "TimeReachedCallback", Script_TimeReachedCallback); // Scenario elapsed time from start. + lua_register(L, "MissionTimerCallback", Script_MissionTimerCallback); // Pre expired mission timer. + lua_register(L, "BuildingsDestroyedCallback", Script_BuildingsDestroyedCallback); // Number of buildings destroyed. + lua_register(L, "UnitsDestroyedCallback", Script_UnitsDestroyedCallback); // Number of units destroyed. + lua_register(L, "NoFactoriesCallback", Script_NoFactoriesCallback); // No factories left. + lua_register(L, "CivilianEscapeCallback", Script_CivilianEscapeCallback); // Civilian has been evacuated. + lua_register(L, "BuildingBuiltCallback", Script_BuildingBuiltCallback); // Specified building has been built. + lua_register(L, "UnitBuiltCallback", Script_UnitBuiltCallback); // Specified unit has been built. + lua_register(L, "InfantryBuiltCallback", Script_InfantryBuiltCallback); // Specified infantry has been built. + lua_register(L, "AircraftBuiltCallback", Script_AircraftBuiltCallback); // Specified aircraft has been built. + lua_register(L, "TeamLeavesMapCallback", Script_TeamLeavesMapCallback); // Specified team member leaves map. + lua_register(L, "ZoneEntryCallback", Script_ZoneEntryCallback); // Enters same zone as waypoint 'x'. + lua_register(L, "HorizontalCrossCallback", Script_HorizontalCrossCallback); // Crosses horizontal trigger line. + lua_register(L, "VerticalCrossCallback", Script_VerticalCrossCallback); // Crosses vertical trigger line. + lua_register(L, "GlobalSetCallback", Script_GlobalSetCallback); // If specified global has been set. (somewhat useless with Scripting) + lua_register(L, "GlobalClearedCallback", Script_GlobalClearedCallback); // If specified global has been cleared. (somewhat useless with Scripting) + // If all fake structures are gone. // This doesn't appear to be implemented in the engine (?) + lua_register(L, "LowPowerCallback", Script_LowPowerCallback); // When power drops below 100%. + lua_register(L, "AllBridgesDestroyedCallback", Script_AllBridgesDestroyedCallback); // All bridges destroyed. + lua_register(L, "BuildingExistsCallback", Script_BuildingExistsCallback); // Check for building existing. + + /********************************************************************************************** + * Trigger Utility Functions * + *=============================================================================================*/ + + lua_register(L, "SetTriggerCallback", Script_SetTriggerCallback); // Initiates a given [callback] on an existing [trigger] // TODO: Make this work via name OR ID + lua_register(L, "TriggerAddCell", Script_TriggerAddCell); // Initiates a given [callback] on an existing [trigger] + lua_register(L, "GetCellObject", Script_GetCellObject); // Gets an object (ID - building/vehicle/aircraft/infantry/vessel), if any, at [Cell/X],[Cell/Y] + lua_register(L, "MergeTriggers", Script_MergeTriggers); // Combines the events of two triggers (for multi-event triggers :) + lua_register(L, "GetTriggerByName", Script_GetTriggerByName); // Gets the ID of the trigger with a given name + lua_register(L, "TriggerSetPersistence", Script_TriggerSetPersistence); // Does a trigger go on and on or just run once? + + /********************************************************************************************** + * Mission Utility Functions * + *=============================================================================================*/ + + lua_register(L, "SetBriefingText", Script_SetBriefingText); // Sets the [text] on the mission briefing screen + + /********************************************************************************************** + * Status / Location Utility Functions * + *=============================================================================================*/ + + lua_register(L, "GiveCredits", Script_GiveCredits); // Give [credits] to [player] + lua_register(L, "GetCredits", Script_GetCredits); // Get [player]'s [credits] + + lua_register(L, "CountBuildings", Script_CountBuildings); // Number of buildings of [type] for given [player] + lua_register(L, "CountAircraft", Script_CountAircraft); // Number of units of [type] for given [player] + lua_register(L, "CountUnits", Script_CountUnits); // Number of units of [type] for given [player] + lua_register(L, "CountInfantry", Script_CountInfantry); // Number of infantry of [type] for given [player] + lua_register(L, "CountVessels", Script_CountVessels); // Number of vessels of [type] for given [player] + + lua_register(L, "GetWaypointX", Script_GetWaypointX); // Get Cell/X of waypoint + lua_register(L, "GetWaypointY", Script_GetWaypointY); // Get Cell/Y of waypoint + + /********************************************************************************************** + * Script Globals * + *=============================================================================================*/ + + lua_pushnumber(L, PlayerPtr->ID); // Local player (house) index + lua_setglobal(L, "_localPlayer"); + + + return true; + } \ No newline at end of file diff --git a/code/redalert/mapscript.h b/code/redalert/mapscript.h new file mode 100644 index 0000000..b3bf2d4 --- /dev/null +++ b/code/redalert/mapscript.h @@ -0,0 +1,56 @@ +// MapScript.h +// + +#ifndef MAPSCRIPT_H +#define MAPSCRIPT_H + +// Lua is written in C, so compiler needs to know how to link its libraries +extern "C" { +# include "lua.h" +# include "lauxlib.h" +# include "lualib.h" +} + + +// Object Cache Item +// Stores an id lookup table +struct MapScriptObject { + int ID=-1; // The object's "Global ID" + int classIndex=-1; // The object's "Global ID" + RTTIType RTTI; // The type of object +}; + + +// +// MapScript Class +// +class MapScript { +public: + + + ~MapScript() { Deinit(); }; + + + bool Init(const char* mapName); + void Deinit(); + + void CallFunction(const char* functionName); + void SetLuaPath(const char* input_path); + + std::vector ObjectCache; + +private: + lua_State* L; +}; + +// Non-Class-Functions +bool Script_SetObjectTrigger(int input_object_id, TriggerClass* input_trigger); +ObjectClass* Script_GetCacheObject(int input_object_id); + +int Script_BuildingIndexFromID(int input_object_id); +int Script_UnitIndexFromID(int input_object_id); +int Script_AircraftIndexFromID(int input_object_id); +int Script_InfantryIndexFromID(int input_object_id); +int Script_VesselIndexFromID(int input_object_id); + +#endif \ No newline at end of file diff --git a/code/redalert/mapsel.cpp b/code/redalert/mapsel.cpp new file mode 100644 index 0000000..023382b --- /dev/null +++ b/code/redalert/mapsel.cpp @@ -0,0 +1,403 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MAPSEL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MAPSEL.CPP * + * * + * Programmer : Barry W. Green * + * * + * Start Date : April 17, 1995 * + * * + * Last Update : April 27, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Bit_It_In -- Pixel fade graphic copy. * + * Map_Selection -- Starts the whole process of selecting next map to go to * + * Print_Statistics -- Prints statistics on country selected * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +void Cycle_Call_Back_Delay(int time, PaletteClass &pal); +extern int ControlQ; + +int Mouse_Over_Spot(int house, int scenario); +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot); +//VG for ant mission progression +const char* antmission[] = {NULL, "SCA01EA.INI", "SCA02EA.INI", "SCA03EA.INI", "SCA04EA.INI"}; + +struct point { + int x; + int y; +} const MapCoords[2][14][3] = { + { + {{185,123},{ -1, -1},{ -1, -1}}, + {{173,112},{ -1, -1},{ -1, -1}}, + {{196,100},{200,112},{ -1, -1}}, + {{175,113},{ -1, -1},{ -1, -1}}, + {{187, 91},{202, 93},{206,105}}, + {{207,161},{212,172},{ -1, -1}}, + {{172, 92},{ -1, -1},{ -1, -1}}, + {{132,119},{146,125},{ -1, -1}}, + {{199, 73},{205, 86},{ -1, -1}}, + {{236,114},{ -1, -1},{ -1, -1}}, + {{219, 64},{225, 76},{ -1, -1}}, + {{256, 69},{ -1, -1},{ -1, -1}}, + {{262, 77},{ -1, -1},{ -1, -1}}, + {{249, 97},{ -1, -1},{ -1, -1}} + }, +// Soviet coords + { + {{178,105},{ -1, -1},{ -1, -1}}, + {{163,101},{163,113},{ -1, -1}}, + {{160, 89},{ -1, -1},{ -1, -1}}, + {{142,101},{142,117},{ -1, -1}}, + {{212,163},{ -1, -1},{ -1, -1}}, + {{155,133},{171,144},{ -1, -1}}, + {{216,103},{ -1, -1},{ -1, -1}}, + {{132,145},{154,154},{ -1, -1}}, + {{122,117},{ -1, -1},{ -1, -1}}, + {{117,130},{ -1, -1},{ -1, -1}}, + {{ 99,107},{109,146},{ -1, -1}}, + {{134,125},{ -1, -1},{ -1, -1}}, + {{ 32,156},{ 46,171},{ -1, -1}}, + {{108, 97},{ -1, -1},{ -1, -1}} + } +}; + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/*********************************************************************************************** + * Map_Selection -- Starts the whole process of selecting next map to go to * + * * + * * + * INPUT: * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1996 BWG : Created. * + *=============================================================================================*/ +extern int CopyType; +#ifndef WIN32 +extern short StreamLowImpact; +#endif +char const * Map_Selection(void) +{ + static char scenarioname[_MAX_FNAME+_MAX_EXT]; + +#ifdef FIXIT_ANTS + if (AntsEnabled) { + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + return(scenarioname); + } + +#endif + char _filename[]="MSAA.WSA"; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); + + _filename[2] = house ? 'S' : 'A'; + _filename[3] = Scen.Scenario + 'A'; + PaletteClass mappalette; + + int scenario = Scen.Scenario; + int selection; + static CDTimerClass timer; + int start = 0; + int count = 0; + int delay = 0; + int xspot = 0; + int yspot = 0; + + void const * appear1 = MFCD::Retrieve("MAPWIPE2.AUD"); + void const * bleep11 = MFCD::Retrieve("BLEEP11.AUD"); + void const * country4 = MFCD::Retrieve("MAPWIPE5.AUD"); + void const * toney7 = MFCD::Retrieve("TONEY7.AUD"); + void const * bleep17 = MFCD::Retrieve("BLEEP17.AUD"); + + void const * scold1 = MFCD::Retrieve("TONEY4.AUD"); + void const * country1 = MFCD::Retrieve("TONEY10.AUD"); + +#ifdef WIN32 + GraphicBufferClass* pseudoseenbuff = &VisiblePage; // GraphicBufferClass(320, 200, (void*)NULL); +#endif + +// fixed oldvolume = Options.ScoreVolume; +// Options.Set_Score_Volume(fixed(4, 10)); + Theme.Queue_Song(THEME_MAP); + + void *anim = Open_Animation(_filename, NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), (unsigned char *)mappalette.Get_Data()); + + Keyboard->Clear(); + SeenPage.Clear(); + mappalette.Set(FADE_PALETTE_FAST, Call_Back); + +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); + for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + HidPage.Clear(); + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + + + int frame = 1; + //StreamLowImpact = true; +#ifdef WIN32 + Play_Sample(appear1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(appear1, 255, Options.Normalize_Volume(55)); +#endif + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + CopyType = 1; + Animate_Frame(anim, *pseudoseenbuff, frame++); + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + switch(frame) { + case 16: +#ifdef WIN32 + Play_Sample(bleep11, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep11, 255, Options.Normalize_Volume(55)); +#endif + break; + case 30: +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(55)); +#endif + break; + case 51: +#ifdef WIN32 + Play_Sample(toney7, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(toney7, 255, Options.Normalize_Volume(55)); +#endif + break; + case 61: +#ifdef WIN32 + Play_Sample(bleep17, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(bleep17, 255, Options.Normalize_Volume(55)); +#endif + break; + } + } + //StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); + Show_Mouse(); + Keyboard->Clear(); + + bool done = 0; + MouseType shape = MOUSE_NORMAL; + while (!done) { + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + + UserInput.Process_Input(); + + Call_Back(); + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; + } +#endif + Cycle_Call_Back_Delay(1, mappalette); + int choice = Mouse_Over_Spot(house, scenario); + if (choice == -1) { + shape = MOUSE_NORMAL; + } else { + shape = MOUSE_CAN_ATTACK; + } + + Set_Mouse(shape, start, count, delay, xspot, yspot); + if (timer == 0) { + frame++; + frame %= count; + timer = delay; + Set_Mouse_Cursor(xspot, yspot, start + frame); + } + if (g_globalKeyFlags == KN_LMOUSE) + { + choice = 0; + if (choice != -1) { + done = 1; + selection = choice; +#ifdef WIN32 + Play_Sample(country1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(country1, 255, Options.Normalize_Volume(50)); +#endif + } else { +#ifdef WIN32 + Play_Sample(scold1, 255, Options.Normalize_Volume(170)); +#else + Play_Sample(scold1, 255, Options.Normalize_Volume(50)); +#endif + } + } + } + + Hide_Mouse(); + + /* + ** Restore the mouse to normal shape before leaving this routine. + */ + Set_Mouse(MOUSE_NORMAL, start, count, delay, xspot, yspot); + Set_Mouse_Cursor(xspot, yspot, start); + + Keyboard->Clear(); +// BlackPalette.Set(FADE_PALETTE_SLOW, Call_Back); +// SeenPage.Clear(); + + Fancy_Text_Print(TXT_STAND_BY, 160 * RESFACTOR, 190 * RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Create the new scenario filename from the selection. The filename is + ** derived from the previous filename but it has the scenario number + ** incremented and the chosen variation set. + */ + + //V.G. added so Ant Missions would progress + if(Scen.ScenarioName[2] == 'A'){ + int antnum = Scen.Scenario++; + if(antnum > 4) antnum = 1; + strcpy(scenarioname, antmission[antnum]); + } + else{ + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + sprintf(buf, "%02d", Scen.Scenario+1); + memcpy(&scenarioname[3], buf, 2); + scenarioname[6] = 'A' + selection; + } + Theme.Fade_Out(); +// Options.Set_Score_Volume(oldvolume); + +// Scen.ScenVar = (ScenarioVarType)selection; +//Mono_Printf("Chose variant %d \n", selection); + return(scenarioname); +} + +int Mouse_Over_Spot(int house, int scenario) +{ + int retval = -1; + for (int selection = 0; selection < 3 && MapCoords[house][scenario][selection].x != -1; selection++) { + int mousex = UserInput.Mouse.X / RESFACTOR; + int mousey = Get_Mouse_Y() / RESFACTOR; + if (UserInput.Mouse.X >= MapCoords[house][scenario][selection].x && + UserInput.Mouse.Y >= MapCoords[house][scenario][selection].y && + UserInput.Mouse.X <= MapCoords[house][scenario][selection].x+11 && + UserInput.Mouse.Y <= MapCoords[house][scenario][selection].y+9) { + + retval = selection; + break; + } + } + return(retval); +} +void Cycle_Call_Back_Delay(int time, PaletteClass &pal) +{ + static CDTimerClass _ftimer; + static bool _up = false; + static int val = 255; + + while(time--) { + /* + ** Process the fading white color. + */ + if (!_ftimer) { + _ftimer = TIMER_SECOND/6; + + #define STEP_RATE 20 + if (_up) { + val += STEP_RATE; + if (val > 150) { + val = 150; + _up = false; + } + } else { + val -= STEP_RATE; + if (val < 0x20) { + val = 0x20; + _up = true; + } + } + + /* + ** Set the pulse color as the proportional value between white and the + ** minimum value for pulsing. + */ + pal[254] = GamePalette[WHITE]; + pal[254].Adjust(val, BlackColor); + + pal.Set(); + } + Call_Back_Delay(1); + } +} + +void Set_Mouse(MouseType shape, int &start, int &count, int &delay, int &xspot, int &yspot) +{ + switch(shape) { + case MOUSE_NORMAL: + start = 0; + count = 1; + delay = 0; + xspot = 0; + yspot = 0; + break; + default: + start = 21; + count = 8; + delay = 4; + xspot = 14; + yspot = 11; + break; + } +} diff --git a/code/redalert/mci.cpp b/code/redalert/mci.cpp new file mode 100644 index 0000000..c743c9e --- /dev/null +++ b/code/redalert/mci.cpp @@ -0,0 +1,362 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* FILE +* MCI.cpp +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include "mci.h" + +/**************************************************************************** +* +* NAME +* GetDeviceCount() +* +* DESCRIPTION +* +* INPUTS +* NONE +* +* RESULT +* Count - Number of MCI device entries +* +****************************************************************************/ + +unsigned int MCI::GetDeviceCount(void) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + unsigned int count; + + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)&count; + sysInfo.dwRetSize = sizeof(count); + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_QUANTITY, (DWORD)&sysInfo); + + if (rc) + return 0; + + return count; + } + + +/**************************************************************************** +* +* NAME +* GetDeviceName(entry, name) +* +* DESCRIPTION +* +* INPUTS +* Entry - Entry number to get name for. +* Name - On return; device entry name +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::GetDeviceName(unsigned int item, char* buffer) + { + MCIERROR rc; + MCI_SYSINFO_PARMS sysInfo; + + // Get device name + memset(&sysInfo, 0, sizeof(sysInfo)); + sysInfo.lpstrReturn = (LPSTR)buffer; + sysInfo.dwRetSize = 63; + sysInfo.dwNumber = item; + + rc = mciSendCommand(MCI_ALL_DEVICE_ID, MCI_SYSINFO, + MCI_WAIT | MCI_SYSINFO_NAME, (DWORD)&sysInfo); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetProductName(MCIDEVICEID id, char* buffer) + { + MCIERROR rc; + MCI_INFO_PARMS info; + + // Get device product name + memset(&info, 0, sizeof(info)); + info.lpstrReturn = (LPSTR)buffer; + info.dwRetSize = 63; + + rc = mciSendCommand(id, MCI_INFO, MCI_WAIT | MCI_INFO_PRODUCT, + (DWORD)&info); + + if (rc) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* OpenDevice(name) +* +* DESCRIPTION +* +* INPUTS +* Name - Device name to open +* +* RESULT +* DeviceID - ID of opened device, 0 if error. +* +****************************************************************************/ + +MCIDEVICEID MCI::OpenDevice(const char* name) + { + MCIERROR rc; + MCI_OPEN_PARMS open; + + memset(&open, 0, sizeof(open)); + open.lpstrDeviceType = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE, (DWORD)&open); + + if (rc) + return 0; + + return (open.wDeviceID); + } + + +void MCI::CloseDevice(MCIDEVICEID id) + { + MCI_GENERIC_PARMS close; + + close.dwCallback = (DWORD)NULL; + + if (id) + mciSendCommand(id, MCI_CLOSE, MCI_WAIT, (DWORD)&close); + } + + +/**************************************************************************** +* +* NAME +* GetDeviceDescription +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetDeviceDescription(const char* name, MCIDevice* caps) + { + MCIDEVICEID id; + unsigned long result; + + // Copy the name + strncpy(caps->name, name, 63); + + if ((id = OpenDevice(name)) == 0) + return false; + + // Get device product name + GetProductName(id, caps->description); + + // Get device type + if (GetCapability(id, MCI_GETDEVCAPS_DEVICE_TYPE, &result)) + caps->type = result; + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_EJECT, &result)) + caps->canEject = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_PLAY, &result)) + caps->canPlay = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_RECORD, &result)) + caps->canRecord = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_CAN_SAVE, &result)) + caps->canSave = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_COMPOUND_DEVICE, &result)) + caps->usesDevElem = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_AUDIO, &result)) + caps->hasAudio = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_HAS_VIDEO, &result)) + caps->hasVideo = ((result) ? true : false); + + if (GetCapability(id, MCI_GETDEVCAPS_USES_FILES, &result)) + caps->reqElemFile = ((result) ? true : false); + + CloseDevice(id); + + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCI::GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result) + { + MCIERROR rc; + MCI_GETDEVCAPS_PARMS devCaps; + + memset(&devCaps, 0, sizeof(devCaps)); + devCaps.dwItem = capItem; + rc = mciSendCommand(id, MCI_GETDEVCAPS, MCI_WAIT|MCI_GETDEVCAPS_ITEM, + (DWORD)&devCaps); + + if (rc) + return false; + + *result = devCaps.dwReturn; + return true; + } + + +/**************************************************************************** +* +* NAME +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +const char* MCI::GetDeviceTypeName(unsigned long type) + { + static struct _DeviceType {unsigned long typeID; const char* typeName;} + _deviceTypeNames[] = + { + {MCI_DEVTYPE_ANIMATION, "Animation"}, + {MCI_DEVTYPE_CD_AUDIO, "CD Audio"}, + {MCI_DEVTYPE_DAT, "DAT"}, + {MCI_DEVTYPE_DIGITAL_VIDEO, "Digital Video"}, + {MCI_DEVTYPE_OTHER, "Other"}, + {MCI_DEVTYPE_OVERLAY, "Overlay"}, + {MCI_DEVTYPE_SCANNER, "Scanner"}, + {MCI_DEVTYPE_SEQUENCER, "MIDI Sequencer"}, + {MCI_DEVTYPE_VCR, "VCR"}, + {MCI_DEVTYPE_VIDEODISC, "VideoDisc"}, + {MCI_DEVTYPE_WAVEFORM_AUDIO, "Wave Audio"}, + {0, NULL}, + }; + + int i = 0; + + while (_deviceTypeNames[i].typeID != 0) + { + if (_deviceTypeNames[i].typeID == type) + return _deviceTypeNames[i].typeName; + + i++; + } + + return NULL; + } + + +/**************************************************************************** +* +* NAME +* MCIEnumerate(callack, context) +* +* DESCRIPTION +* +* INPUTS +* Callback - +* Context - +* +* RESULT +* Success - Success / Failure flag +* +****************************************************************************/ + +bool MCI::EnumerateDevices(MCIEnumCB* callback, void* context) + { + DWORD count; + DWORD i; + char name[64]; + MCIDevice device; + + // Get the number of devices + count = GetDeviceCount(); + + // Do for each device + for (i = 1; i <= count; i++) + { + GetDeviceName(i, name); + memset(&device, 0, sizeof(device)); + + if (GetDeviceDescription(name, &device)) + { + if (!callback(&device, context)) + break; + } + } + + return true; + } +#endif // MCIMPEG + diff --git a/code/redalert/mci.h b/code/redalert/mci.h new file mode 100644 index 0000000..c936b02 --- /dev/null +++ b/code/redalert/mci.h @@ -0,0 +1,105 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MCI_H_ +#define _MCI_H_ +/**************************************************************************** +* +* FILE +* MCI.H +* +* DESCRIPTION +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* 6/22/98 +* +****************************************************************************/ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include "watcom.h" + +/* MCIDevice - MCI device capabilities and description + * + * name - Name used to open device. + * description - Product description + * type - Device type + * canEject - Can eject media flag + * canPlay - Can playback media + * canRecord - Can record media + * canSave - Can save media + * usesDevElem - Uses device element + * hasAudio - Media supports audio + * hasVideo - Media supports video + * reqElemFile - Requires element file + */ +typedef struct _MCIDevice + { + char name[64]; + char description[64]; + unsigned long type; + bool canEject; + bool canPlay; + bool canRecord; + bool canSave; + bool usesDevElem; + bool hasAudio; + bool hasVideo; + bool reqElemFile; + } MCIDevice; + +/* MCI enumeration callback definition */ +typedef bool (MCIEnumCB)(MCIDevice* desc, void*); + +class MCI + { + public: + // Open MCI device + MCIDEVICEID OpenDevice(const char* name); + void CloseDevice(MCIDEVICEID id); + + // Enumerate devices + bool EnumerateDevices(MCIEnumCB* callback, void* context); + + // Get number of MCI devices name in registry or [MCI] section + // of system.ini + unsigned int GetDeviceCount(void); + + // Get device name from registry or [MCI] section of system.ini + bool GetDeviceName(unsigned int item, char* buffer); + + // Get general device description + bool GetDeviceDescription(const char* name, MCIDevice* caps); + + // Get type name (IE: Digital Video) from type ID (IE: MCI_DEVTYPE_DIGITAL_VIDEO) + const char* GetDeviceTypeName(unsigned long type); + + // Get device product name + bool GetProductName(MCIDEVICEID id, char* buffer); + + // Get device capability + bool GetCapability(MCIDEVICEID id, unsigned long capItem, + unsigned long* result); + }; + +#endif // MCIMPEG +#endif // _MCI_H_ diff --git a/code/redalert/mcimovie.cpp b/code/redalert/mcimovie.cpp new file mode 100644 index 0000000..f5f9c8a --- /dev/null +++ b/code/redalert/mcimovie.cpp @@ -0,0 +1,349 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "function.h" + +#ifdef MCIMPEG +#include "mcimovie.h" +#include + +/**************************************************************************** +* +* NAME +* MCIMovie - Constructor +* +* DESCRIPTION +* +* INPUTS +* HInstance - Application instance handle +* +* RESULT +* NONE +* +****************************************************************************/ + +MCIMovie::MCIMovie(HWND mainWindow) + : mMainWindow(mainWindow), mMCIWindow(NULL), mName(NULL), mDeviceID(0) + { + mWidth = mHeight = 0; + } + + +/**************************************************************************** +* +* NAME +* ~MCIMovie - Destructor +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +MCIMovie::~MCIMovie() + { + // Stop any playing movie + Close(); + + // Free name + if (mName != NULL) + free(mName); + } + + +/**************************************************************************** +* +* NAME +* Open() +* +* DESCRIPTION +* Open the media file in preparation for playback. +* +* INPUTS +* NONE +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Open(const char* name, const char* device) + { + MCIERROR rc; + MCI_DGV_RECT_PARMS rectParm; + MCI_BREAK_PARMS breakParm; + + // Stop any currently playing movie + Close(); + + // Copy the movie name for our use + if (mName != NULL) + free(mName); + + mName = strdup(name); + + if (device == NULL) + device = "mpegvideo"; + + // Setup open parameters + memset((void*)&mOpenParm, 0, sizeof(mOpenParm)); + mOpenParm.dwCallback = NULL; + mOpenParm.lpstrDeviceType = device; + mOpenParm.lpstrElementName = name; + + rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE | + MCI_OPEN_ELEMENT, (DWORD)&mOpenParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + // Set device ID + mDeviceID = mOpenParm.wDeviceID; + + // Retrieve movie dimensions + rectParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_WHERE, MCI_WAIT | MCI_DGV_WHERE_SOURCE, + (DWORD)&rectParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + mWidth = rectParm.rc.right - rectParm.rc.left; + mHeight = rectParm.rc.bottom - rectParm.rc.top; + + // Set break key to escape + breakParm.dwCallback = NULL; + breakParm.nVirtKey = VK_ESCAPE; + breakParm.hwndBreak = mMainWindow; + + rc = mciSendCommand(mDeviceID, MCI_BREAK, MCI_WAIT | MCI_BREAK_HWND | + MCI_BREAK_KEY, (DWORD)&breakParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Play - Play the specified movie. +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Play(HWND window) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + // Provide window for playback + if (AttachWindow(window)) + { + // Size the video area + if (SizeDestination()) + { + // Start playing + memset((void*)&mPlayParm, 0, sizeof(mPlayParm)); + mPlayParm.dwCallback = NULL; + + rc = mciSendCommand(mDeviceID, MCI_PLAY, MCI_WAIT, (DWORD)&mPlayParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + Close(); + } + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* Pause +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Pause(void) + { + if (mDeviceID == 0) + return false; + + if (mciSendCommand(mDeviceID, MCI_PAUSE, 0, (DWORD)NULL)) + return false; + + return true; + } + + +/**************************************************************************** +* +* NAME +* Stop +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* Success - Success/Failure flag +* +****************************************************************************/ + +bool MCIMovie::Close(void) + { + MCIERROR rc; + + if (mDeviceID == 0) + return false; + + rc = mciSendCommand(mDeviceID, MCI_CLOSE, 0, (DWORD)NULL); + mDeviceID = 0; + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* SizeDestination +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::SizeDestination(void) + { + MCIERROR rc; + MCI_DGV_PUT_PARMS putParm; + RECT rect; + + if (mMCIWindow == NULL) + return false; + + GetClientRect(mMCIWindow, &rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect); + ClientToScreen(mMCIWindow, (LPPOINT)&rect + 1); + + putParm.dwCallback = NULL; + putParm.rc.left = rect.left; + putParm.rc.top = rect.top; + putParm.rc.right = rect.right; + putParm.rc.bottom = rect.bottom; + + rc = mciSendCommand(mDeviceID, MCI_PUT, MCI_WAIT | MCI_DGV_RECT | + MCI_DGV_PUT_DESTINATION, (DWORD)&putParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + + +/**************************************************************************** +* +* NAME +* AttachWindow +* +* DESCRIPTION +* +* INPUTS +* +* RESULT +* +****************************************************************************/ + +bool MCIMovie::AttachWindow(HWND window) + { + MCIERROR rc; + MCI_DGV_WINDOW_PARMS winParm; + + mMCIWindow = window; + + memset((void*)&winParm, 0, sizeof(winParm)); + winParm.dwCallback = NULL; + winParm.hWnd = window; + winParm.nCmdShow = SW_SHOW; + + rc = mciSendCommand(mDeviceID, MCI_WINDOW, MCI_WAIT| MCI_DGV_WINDOW_HWND | + MCI_DGV_WINDOW_STATE, (DWORD)&winParm); + + if (rc) + { + char buffer[512]; + mciGetErrorString(rc, buffer, 512); + return false; + } + + return true; + } + +#endif // MCIMPEG + diff --git a/code/redalert/mcimovie.h b/code/redalert/mcimovie.h new file mode 100644 index 0000000..71e8a12 --- /dev/null +++ b/code/redalert/mcimovie.h @@ -0,0 +1,66 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MCIMOVIE_H_ +#define _MCIMOVIE_H_ + +#include "function.h" + +#ifdef MCIMPEG +#include +#include +#include +#include +#include "watcom.h" + +class MCIMovie + { + public: + MCIMovie(HWND mainWindow); + ~MCIMovie(); + + bool Open(const char* name, const char* device); + bool Play(HWND window); + bool Pause(void); + bool Close(void); + + LONG GetWidth(void) + {return ((mDeviceID) ? mWidth : 0);} + + LONG GetHeight(void) + {return ((mDeviceID) ? mHeight : 0);} + + protected: + HWND mMainWindow; // Application window + HWND mMCIWindow; // Callback window + char *mName; + UINT mDeviceID; + MCI_OPEN_PARMS mOpenParm; + MCI_PLAY_PARMS mPlayParm; + + // Video stream dimension + LONG mWidth, mHeight; + + private: + bool SizeDestination(void); + bool AttachWindow(HWND window); + + static int mRegistered; + static WNDCLASS mWndClass; + static HINSTANCE mInstance; + }; + +#endif // MCIMPEG +#endif // _MCIMOVIE_H_ diff --git a/code/redalert/memcheck.h b/code/redalert/memcheck.h new file mode 100644 index 0000000..5a1b537 --- /dev/null +++ b/code/redalert/memcheck.h @@ -0,0 +1,2602 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# + + MemCheck 3.0 Professional for DOS + + Copyright (c) 1990-1994, StratosWare Corporation. + All rights reserved. + + 1-800-WE-DEBUG + + Note to Developers: + -------------------- + This file should be #included AFTER any other #includes in + each source file which is to be memory checked, and BEFORE + any code that performs any operations on allocated pointers. + If it isn't, MemCheck will not pick up source file and line + information for intercepted functions. + + The MCCONFIG.EXE utility distributed with MemCheck 3.0 + will do this safely and quickly for you. + + Most specifically, this header file MUST NOT come before + any prototypes of routines that MemCheck intercepts, like + malloc(), free(), strcpy(), and so on. + + The Final Cut: + --------------- + To ENTIRELY remove MemCheck from your code, just #define + the constant "NOMEMCHECK", or equivalently, "NOMC". + + This header file will then automatically 'evaporate' all + MemCheck 3.0 calls. This is MUCH PREFERABLE to placing + #if-#endif's around the header file's inclusion, as in + + #ifdef DEBUG // DON'T DO THIS! + #include + #endif + + Using the "#ifdef DEBUG" as above doesn't allow the + MemCheck header file to evaporate the MemCheck 3.0 API + calls you may have placed in your code, like mc_startcheck() + and mc_endcheck(). You would then have to surround + each MemCheck API call with additional #if-#endif's. + + Modification History + + WHO WHEN WHAT + KWB 07/28/93 Gussy for beta + KWB 09/11/93 Add new placement overload, NEW() C++ stuff + KWB 11/08/93 Final QA/QC for initial release + KWB 12/02/93 LINT -save added + KWB 02/19/94 Fixed function inlining holes, added macros + for underscore func variants under MSC 7+ + KWB 07/08/94 Added defines for BC extender (_CC_POWERPACK_) + KWB 07/09/94 Added special case for STACKTOP, END under PowerPack + KWB 08/04/94 Added cdecl modifier to stklen, atopsp etc. decls + KWB 08/11/94 Associated _CC32_ with _PROTECTED_ in ccdefs section; + Changed method of determining compiler model, + e.g. _CC_MSC6_ from if == to if >= + Associated DOSX286 with _PROTECTED_ for Phar Lap + Added MC_SET_EXCEPTF, mc_set/get_exceptf() + KWB 08/17/94 Added new[] support filtering (_CPP_ANSI20_) + KWB 08/18/94 Changed _MCFARCALL to _MCFARGLUE + KWB 09/13/94 Added erf_printf as alias for erf_stdout + KWB 09/14/94 Added endf_summary + KWB 09/21/94 Added MCCRITF and mc_set_critf() & related + KWB 10/10/94 Added #if !defined(setmem) etc. for BC DPMI32 + KWB 10/11/94 Added _CC_BORLANDx_ comp def, 'x' = major ver + +*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + + +/* Avoid multiple inclusions */ +#ifndef _MEMCHECK_H_ +#define _MEMCHECK_H_ + +/* Prevent inclusion in Windows(tm) compilations */ +#if defined(_Windows) || defined(WINDOWS) || defined(_WINDOWS) +# if !defined (__DPMI16__) && !defined (__DPMI32__) && !defined (DOSX286) +# error DOS version of MemCheck header file #included! +# endif +#endif + +/* Shorthand equivalence, V2.0 backwards compatibility... */ +#if defined (NOMC) || defined (NOMEMCHK) +# define NOMEMCHECK +#endif + +/* C++ new interception -- see note later and + read the MemCheck 3.0 User's Manual, section + "Integration With C++." Uncommenting the next line + and following simple instructions allows seamless + transmission of the exact file and line location + of new's in your source code. +*/ +/* #define NEW_OVERLOADED */ + + +/* *** Backwards compatibility with V2.0 *** */ + +#define mc_isactive mc_is_active /* standardize naming... */ +#define mc_getmode mc_get_mode +#define mc_errorstatus mc_error_flags +#define mc_verify_memory mc_check_buffers + +#define MC_USEDISK MC_USING_DISK +#define MC_USEMEM MC_USING_MEMORY + + +/* *** Backwards compatibility with V2.1 *** */ + +#define MCLINENO MCSL /* "MemCheck Source Line" */ +#define MFUNC ERF /* error reporting function */ + +#define mc_set_msgfunc mc_set_erf /* "Message funcs" are now */ +#define mc_get_msgfunc mc_get_erf /* universally referred to as */ +#define mc_error_status mc_error_flags /* "error reporting functions"*/ + +/* Maintain source code compatibility with version 2.1. + Buffer registration is simplified to + just calling "mc_register", regardless of model. + Same with buffer checking, e.g. "mc_check_far" + and "mc_check_near" are rolled into "mc_check". +*/ +#define mc_register_near(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_register_far(p,s) mc_register((void _MCFAR *)(p),s) +#define mc_unregister_near(p) mc_unregister((void _MCFAR *)(p)) +#define mc_unregister_far(p) mc_unregister((void _MCFAR *)(p)) +#define mc_check_near(p) mc_check((void _MCFAR *)(p)) +#define mc_check_far(p) mc_check((void _MCFAR *)(p)) + +/* Error Number Definitions + Returned by mc_endcheck() and mc_error_flags(). + In MemCheck 3.0, there's now a global error number much + like the "errno" variable in standard C. +*/ +#define MCE_NO_ERROR 0 /* it's debugging time & all well */ +#define MCE_NULL_SOURCE 1 /* null source ptr on copy */ +#define MCE_NULL_DEST 2 /* null dest ptr on copy */ +#define MCE_UNDERWRITE 3 /* allocated buf underwritten (front) */ +#define MCE_OVERWRITE 4 /* allocated buf overwritten (end) */ +#define MCE_LEAK 5 /* forgot to free alloc'd memory */ +#define MCE_LEAKAGE MCE_LEAK +#define MCE_UNFREED_MEMORY MCE_LEAK +#define MCE_NULL_PTR_ASSIGN 6 /* assigned data through null ptr */ +#define MCE_BAD_STACK_PTR 7 /* bad stack pointer */ +#define MCE_STACK_OVERWRITE 8 /* copy trashes stack frame */ +#define MCE_INTERNAL 9 /* internal error msg */ +#define MCE_OVERLAPPING_COPY 10 /* source overlaps dest on copy */ +#define MCE_INVALID_PTR 11 /* bad ptr on free, realloc */ +#define MCE_DEST_OVERWRITE 12 /* copy too big for dest buffer */ +#define MCE_OUT_OF_MEMORY 13 /* out of memory */ +#define MCE_OOM MCE_OUT_OF_MEMORY +#define MCE_GPF_PTR 14 /* ptr caused GPF */ + + +/* MemCheck Error Flags + + The MemCheck error flag is just an unsigned long + (specifically, a MCEFLAGS typedef) with "sticky" bits + corresponding to the above MCE_... error numbers. + You can use the error flags macro MC_EFLAG(e) to + test the global MC_Errno double flag word. +*/ +/* Returns a long word with the e-th bit set */ +#define MC_EFLAG(e) ( (e) ? ((MCEFLAGS)1 << (e-1)) : (MCEFLAGS)0) + +#define EFLAG_NULL_PTR MC_EFLAG(MCE_NULL_DEST) +#define EFLAG_BAD_PTR MC_EFLAG(MCE_UNALLOCED_PTR) +#define EFLAG_FRONT_MAGIC MC_EFLAG(MCE_UNDERWRITE) +#define EFLAG_BACK_MAGIC MC_EFLAG(MCE_OVERWRITE) +#define EFLAG_PTRS_NOT_FREED MC_EFLAG(MCE_LEAK) +#define EFLAG_TRACK_FILE 0 /*obsolete in 3.0+*/ +#define EFLAG_NULL_ASSIGN MCE_FLAG(MCE_NULL_PTR_ASSIGN) + +/* *** End Compatibility Section *** */ + + +/* *** MemCheck Compiler Constant Definitions *** */ + +/* + Compiler Defines + -------- ------- + Microsoft _CC_MSC_, _CC_MSC_COMPATIBLE_ + V8.x _CC_MSC8_ + V7.x _CC_MSC7_ + V6.x _CC_MSC6_ + V5.x _CC_MSC5_ + Borland* _CC_BORLAND_, _CC_BCC_ + V3.x _CC_BORLAND3_ + V4.x _CC_BORLAND4_ + PowerPack/16 _CC_POWERPACK_, _CC_POWERPACK16_ + PowerPack/32 _CC_POWERPACK_, _CC_POWERPACK32_, _CC32_ + WATCOM _CC_WATCOM_, _CC_MSC_COMPATIBLE_ + WAT/386 _CC_WATCOM32_, _CC32_ + + * Borland has no good way of determining compiler + version. __BORLANDC__ returns some truly funky + hex constant that "will increase in future versions." + + Define Meaning + ------ -------- + _CC32_ * 32-bit compile + _PROTECTED_ 16- or 32-bit protected mode compile + LCODE Defined if large code model + LDATA Defined if large data model + STACKTOP Highest stack address (top) + STACKEND Lowest stack address + STACKLEN Stack length (top - end) + _CPP_ANSI20_ Compiler supports C++ 2.0, e.g. new[] + + * If _CC32_ is defined, _PROTECTED_ is also defined +*/ + + +#ifndef _CCDEFS_H_ +#define _CCDEFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* NOTE: Microsoft C 5.x users have to "#define _MSC_VER 500" + at the top of this file. +*/ +#if defined(_MSC_VER) +/* + _MSC_VER Microsoft C version; currently defined as 700. + M_I86 _M_I86 Member of the I86 processor family. + M_I86mM _M_I86mM Memory model type: + = T Tiny + S Small (default) + C Compact model + M Medium model + L Large model + H Huge model + Identifiers defined by /AT, /AS, /AC, /AM, + /AL, and /AH, respectively. + _MSDOS MS-DOS operating system. + _QC Microsoft QuickC Compiler. + _WINDLL Windows protected-mode dynamic-link library + is selected with /GD. + _WINDOWS Windows protected-mode is selected with /GA, + /Gn, /GW, /Mq, or /GD. +*/ +# define _CC_MSC_ +# define _CC_MSC_COMPATIBLE_ + +# if (_MSC_VER >= 800) +# define _CC_MSC8_ +# elif (_MSC_VER >= 700) +# define _CC_MSC7_ +# elif (_MSC_VER >= 600) +# define _CC_MSC6_ +# elif (_MSC_VER >= 500) /* see note above */ +# define _CC_MSC5_ +# else +# error MemCheck.h: unrecognized version of Microsoft compiler! +# endif + +#elif defined(__BORLANDC__) +# define _CC_BORLAND_ /* Borland product */ +# define _CC_BCC_ /* Borland C compiler */ + + /* Major compiler rev */ +# if (__BORLANDC__ >= 0x0450) +# define _CC_BORLAND4_ +# elif (__BORLANDC__ >= 0x400) +# define _CC_BORLAND3_ +# else + /* not needed */ +# endif + + /* Borland 4.0 PowerPack BCC.EXE defines (-WX): + __DPMI16__ _Windows + + With -WXD, -WXDE: + __DLL__ __DPMI16__ _Windows + + Borland 4.0 PowerPack BCC21.EXE defines (-WX): + __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + + With -WXD: + __DLL__ __CONSOLE__ __DPMI32__ __FLAT__ __WIN32__ _Windows + */ +# if defined(__DPMI16__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK16_ +# define _PROTECTED_ +# endif +# if defined(__DPMI32__) +# define _CC_POWERPACK_ +# define _CC_POWERPACK32_ +# define _CC32_ /* flat model */ +# endif + +/* Turbo C++ */ +#elif defined(MCTCP) /* homebrew */ +# define _CC_BORLAND_ /* Borland product */ +# define _CC_TCP_ /* Turbo C++ */ + +#elif defined(__TURBOC__) +/* + __TURBOC__ Gives the current Turbo C version + number, a hexadecimal number. Version + 1.0 id 0x1000; version 1.2 is 0x0102, etc. + __TINY__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__, __HUGE__ + Model defintions + __MSDOS__ Signals that we're not yet in the + twenty-first century +*/ +# define _CC_BORLAND_ /* Borland C product */ +# define _CC_TCC_ /* Turbo C/C++ compiler */ + + +#elif defined(_INTELC32_) +/* + _INTELC32_ has the value 1. + _ARCHITECTURE_ is 386 if the nomod486 pragma is ineffect, + 486 otherwise. +*/ +# define _CC_INTEL_ /* Intel Code Builder */ +# define _CC_MSC_COMPATIBLE_ +# define _CC32_ /* flat model */ + +#elif defined(__WATCOMC__) +/* + __WATCOMC__ Used to determine if the WATCOM C + or C/386 compiler is compiling + __386__ identifies the target machine as an + Intel 80386 under the WATCOM C/386 compiler + MSDOS Signals that we're not yet in the + twenty-first century + __FLAT__, __SMALL__, __MEDIUM__, + __COMPACT__, __LARGE__ + Model defintions (flat is default) + Also id's MSC-compatible model defines + + 8.5 and later: + __WINDOWS__ Identifies 16-bit Windows ("zw" or "zW" opts) + __WINDOWS_386__ 32-bit Microsoft Windows "zW" opt + __NETWARE_386__ Novell Netware 386, defined by the + Novell/Watcom C + __OS2__ IBM OS/2-hosted version of Watcom +*/ +# define _CC_WATCOM_ /* Watcom C product */ +# define _CC_MSC_COMPATIBLE_ +# ifdef __386__ +# define _CC32_ /* flat model */ +# define _CC_WATCOM32_ +# endif + + +#elif defined(__STDC__) /* Standard ANSI C */ +# define _CC_ANSI_ +# define _CC32_ /* no segmentation via far/near */ +# define far +# define near +# define huge +# define cdecl + +/* Avoids parameter mismatches from _far, etc. */ +# define _far +# define _near +# define _huge +# define _cdecl + +#else /* UNSUPPORTED / UNRECOGNIZED COMPILER */ + +#error MemCheck 3.0: unrecognized compiler +/* + If you're using Microsoft C5.1, you must + define the constant _MSC_VER, e.g. + + #define _MSC_VER 500 + + Place this statement at the top of this + header file or in your project header file + BEFORE the MemCheck header file is included. + + Microsoft C 5.0 is NOT supported (it's non-ANSI). +*/ + +#endif /* compiler constant definitions */ + +/* END of _CC..._ name setups; ripple & alias */ + +/* Sheer utility & avoidance of !_CC32_ */ +#ifndef _CC32_ +# define _CC16_ +#endif + +/* 32-bit compilers are always protected mode */ +#ifdef _CC32_ +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* CC32 */ + +/* Phar Lap support */ +#ifdef DOSX286 +#ifndef _PROTECTED_ +# define _PROTECTED_ +#endif +#endif /* DOSX286 */ + +/* C++ 2.0 compliance: _CPP_ANSI20_ */ +#if defined(_CC_BORLAND_) + /* Only BC++ 4.x and later, but Borland has + seriously mangled version info (__BORLANDC__) */ +# if defined (__BCPLUSPLUS__) +# if (__BCPLUSPLUS__ > 0x0310) /* after 3.1 */ +# define _CPP_ANSI20_ +# endif +# elif defined(_CC_POWERPACK_) +# define _CPP_ANSI20_ +# endif +#elif defined (_CC_MSC_) + /* Current release through 8.x doesn't support new[] */ +#elif defined (_CC_WATCOM_) +# if (__WATCOMC__ >= 1000) /* 10.x */ +# define _CPP_ANSI20_ +# endif +#endif + + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Code and Data Size Constants *** + LCODE not used by this header file. + LDATA *is* used, however. + + May be commented out if these constants are already defined. +*/ + +/* #ifndef LCODE */ +#if defined(M_I86MM) || defined(M_I86LM) || defined(M_I86HM) +# define LCODE 1 +#elif defined(__MEDIUM__) || defined(__LARGE__) || defined(__HUGE__) +# define LCODE 1 +#endif +/* #endif */ + +#if defined(__COMPACT__) || defined(__LARGE__) || defined(__HUGE__) +# define LDATA 1 +#elif defined(M_I86CM) || defined(M_I86LM) || defined(M_I86HM) +# define LDATA 1 +#endif + +/* Macro "aliases" */ + +#ifndef _LCODE +# ifdef LCODE +# define _LCODE LCODE +# endif +#endif + +#ifndef _LDATA +# ifdef LDATA +# define _LDATA LDATA +# endif +#endif + +/*******************************************************************/ +/*******************************************************************/ + +/* *** Physical Stack Address *** */ + +#if defined(_CC_BORLAND_) /* -------------------------- */ + + /* + BORLAND RTL Notes: + ;---------------------------------------------------------------------- + ; In large data models, the stack is in its own segment. The stack + ; starts at SS:__stklen and grows down to SS:0. + ; + ; In small data models, the stack is in the DGROUP, and grows down + ; to meet the heap. The end of the heap is marked by ___brklvl. + (KWB: Note that the brklvl is adjusted upwards until it meets + _stklen...) + ;---------------------------------------------------------------------- + */ + +# define STACKSLOP 256 + extern unsigned cdecl _stklen; + +# if defined(_CC_POWERPACK_) +# define STACKTOP (mc_stacktop()) +# define STACKEND (mc_stackend()) +# else /* not P-Pack */ +# ifdef LDATA + /* Compact, Large, Huge Models ... + + The stack starts at SS:_stklen and + grows downward to SS:0 + */ +# define STACKTOP ((unsigned) _stklen) +# define STACKEND ((unsigned) 0 + STACKSLOP) + +# else + /* Small, Medium Models ... + + The stack starts at SS:0xFFFE and grows + downwards _stklen bytes. + */ +# define STACKTOP ((unsigned) 0xFFFE) +# define STACKEND (STACKTOP - _stklen + STACKSLOP) +# endif +# endif /* not PowerPack */ + +#elif defined (_CC_MSC_) /* ------------------------------- */ + + extern char cdecl end; /* end of stack */ + extern unsigned cdecl _atopsp; /* top of stack */ + +# define STACKTOP _atopsp +# define STACKSLOP 256 + +# ifdef LDATA + /* If in far data, stack could be in its own + seg. tho not usually. see /FARSTACK */ +# define STACKEND ((unsigned) ((unsigned long)&end) + STACKSLOP) +# else + /* If near data, must be in DS; use near ptr */ +# define STACKEND ((unsigned)&end + STACKSLOP) +# endif + +#elif defined (_CC_WATCOM_) /* ------------------------------- */ + + extern unsigned _STACKLOW; /* end of stack */ + extern unsigned _STACKTOP; /* top of stack */ + +# define STACKTOP (_STACKTOP) +# define STACKSLOP 256 + +# ifdef LDATA +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# else +# define STACKEND ((unsigned) (_STACKLOW + STACKSLOP)) +# endif + +#else /* Unknown compiler ---------------- */ + +#error Unknown compiler at __FILE__(__LINE__) + +#endif /* defining stack top, end */ + +/*******************************************************************/ +/*******************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* _ccdefs already #included */ + +/* End CCDEFS */ + + +#if !defined (NULL) /* pull in stdio.h if not already */ +# include +#endif + +/* Backup... sometimes NULL defined in other headers */ +#if !defined (_IOFBF) /* pull in stdio.h if not already */ +# include +#endif + + +/* *** MemCheck Constants *** */ + +/* Standard from MemCheck 3.0 onwards. + Access major version and revision + via mc_version() and mc_revision() macros. +*/ +#define _MC_VERSION 0x0300 /* welcome 3.0 the powerful */ + +#define mc_version() ((int)((_MC_VERSION & 0xFF00) >> 8)) +#define mc_revision() ((int)(_MC_VERSION & 0x00FF)) + +#if defined (_CC32_) /* 32-bit Intel target */ +#define PRT_FP "0x%p" +#else +#define PRT_FP "%Fp" +#endif + +/* *** MCID Macro *** */ + +/* Allows later flexibility in assigning mapping... + Also makes MCIDs formulaic +*/ +#define _MCID(f) (MCID)(__paste(MCID_,f)) + +/* + MemCheck Function ID's (MCID's) + + These are the indices used to retrieve information + about specific runtime library calls. +*/ +/* --- MEMCHECK INTERNAL FUNCTIONS --- */ + +#define _MCID_FIRST_INTERNAL 0 /* index of first internal func */ + +#define MCID_mc_startcheck 0 +#define MCID_mc_endcheck 1 +#define MCID_mc_check_buffers 2 +#define MCID_mc_check 3 +#define MCID_mc_register 4 +#define MCID_mc_unregister 5 +#define MCID_mc_set_alignsize 6 +#define MCID_mc_set_checkbytes 7 +#define MCID_mc_nullcheck 8 +#define MCID_mc_breakpoint 9 +#define MCID_mc_debug 10 +#define MCID_mc_set_location 11 +#define MCID_mc_stack_trace 12 +#define MCID_mc_report 13 + +#define _MCID_LAST_INTERNAL 14 /* Set = to last internal ID */ + + +/* *************** STANDARD C ROUTINES ******************* */ + +#define _MCID_FIRST_ANSI (_MCID_LAST_INTERNAL+1) + +#define MCID_calloc (_MCID_FIRST_ANSI + 0) +#define MCID_free (_MCID_FIRST_ANSI + 1) +#define MCID_malloc (_MCID_FIRST_ANSI + 2) +#define MCID_memcpy (_MCID_FIRST_ANSI + 3) +#define MCID_memmove (_MCID_FIRST_ANSI + 4) +#define MCID_memset (_MCID_FIRST_ANSI + 5) +#define MCID_realloc (_MCID_FIRST_ANSI + 6) +#define MCID_sprintf (_MCID_FIRST_ANSI + 7) +#define MCID_strcat (_MCID_FIRST_ANSI + 8) +#define MCID_strcpy (_MCID_FIRST_ANSI + 9) +#define MCID_strncat (_MCID_FIRST_ANSI + 10) +#define MCID_strncpy (_MCID_FIRST_ANSI + 11) +#define MCID_vsprintf (_MCID_FIRST_ANSI + 12) + +#define _MCID_LAST_ANSI MCID_vsprintf /* define to last ANSI */ + + +/* *************** MICROSOFT C ******************* */ + +#if defined(_CC_MSC_COMPATIBLE_) + +#define _MCID_FIRST_MSC (_MCID_LAST_ANSI + 1) + +# define MCID__expand (_MCID_FIRST_MSC + 0) +# define MCID__ffree (_MCID_FIRST_MSC + 1) +# define MCID__fcalloc (_MCID_FIRST_MSC + 2) +# define MCID__fmalloc (_MCID_FIRST_MSC + 3) +# define MCID__fmsize (_MCID_FIRST_MSC + 4) +# define MCID__frealloc (_MCID_FIRST_MSC + 5) +# define MCID__fexpand (_MCID_FIRST_MSC + 6) +# define MCID__msize (_MCID_FIRST_MSC + 7) + +# define MCID__fmemmove (_MCID_FIRST_MSC + 8) +# define MCID__fmemcpy (_MCID_FIRST_MSC + 9) +# define MCID__fmemset (_MCID_FIRST_MSC + 10) +# define MCID__fmemccpy (_MCID_FIRST_MSC + 11) +# define MCID__fstrcat (_MCID_FIRST_MSC + 12) +# define MCID__fstrncat (_MCID_FIRST_MSC + 13) +# define MCID__fstrcpy (_MCID_FIRST_MSC + 14) +# define MCID__fstrncpy (_MCID_FIRST_MSC + 15) +# define MCID__fstrdup (_MCID_FIRST_MSC + 16) +# define MCID__fstrset (_MCID_FIRST_MSC + 17) +# define MCID__fstrnset (_MCID_FIRST_MSC + 18) + +# define MCID__nfree (_MCID_FIRST_MSC + 19) +# define MCID__nmalloc (_MCID_FIRST_MSC + 20) +# define MCID__ncalloc (_MCID_FIRST_MSC + 21) +# define MCID__nrealloc (_MCID_FIRST_MSC + 22) +# define MCID__nexpand (_MCID_FIRST_MSC + 23) +# define MCID__nmsize (_MCID_FIRST_MSC + 24) +# define MCID__nstrdup (_MCID_FIRST_MSC + 25) + +# define MCID__dos_setvect (_MCID_FIRST_MSC + 26) +# define MCID__getdcwd (_MCID_FIRST_MSC + 27) + +/* Here starts the Great ANSI Divide. + MSC6 and earlier have no underscores; + MSC7 and later *have* underscores to emphasize + departure from ANSI... +*/ +#if defined(_CC_MSC_) && !defined (MSC6) /* not MSC6-compatible */ + +# define MCID__getcwd (_MCID_FIRST_MSC + 28) +# define MCID__cgets (_MCID_FIRST_MSC + 29) +# define MCID__halloc (_MCID_FIRST_MSC + 30) +# define MCID__hfree (_MCID_FIRST_MSC + 31) +# define MCID__memccpy (_MCID_FIRST_MSC + 32) +# define MCID__strdup (_MCID_FIRST_MSC + 33) +# define MCID__strnset (_MCID_FIRST_MSC + 34) +# define MCID__strset (_MCID_FIRST_MSC + 35) +# define MCID__swab (_MCID_FIRST_MSC + 36) +# define MCID__tempnam (_MCID_FIRST_MSC + 37) + +#else /*** MSC6 and before; WATCOM ***/ + +/* No leading underscores */ + +# define MCID_getcwd (_MCID_FIRST_MSC + 28) +# define MCID_cgets (_MCID_FIRST_MSC + 29) +# define MCID_halloc (_MCID_FIRST_MSC + 30) +# define MCID_hfree (_MCID_FIRST_MSC + 31) +# define MCID_memccpy (_MCID_FIRST_MSC + 32) +# define MCID_strdup (_MCID_FIRST_MSC + 33) +# define MCID_strnset (_MCID_FIRST_MSC + 34) +# define MCID_strset (_MCID_FIRST_MSC + 35) +# define MCID_swab (_MCID_FIRST_MSC + 36) +# define MCID_tempnam (_MCID_FIRST_MSC + 37) + +#endif /* MSC6 ANSI calls */ + +# define MCID_new (_MCID_FIRST_MSC + 38) +# define MCID_delete (_MCID_FIRST_MSC + 39) +# define MCID__fullpath (_MCID_FIRST_MSC + 40) + +#endif /* Microsoft C-compatible calls */ + + +/* *************** BORLAND C ******************* */ + +#if defined (_CC_BORLAND_) + +#define _MCID_FIRST_BC (_MCID_LAST_ANSI + 1) + +# define MCID__fmemmove (_MCID_FIRST_BC + 0) +# define MCID__fmemcpy (_MCID_FIRST_BC + 1) +# define MCID__fmemset (_MCID_FIRST_BC + 2) +# define MCID__fmemccpy (_MCID_FIRST_BC + 3) +# define MCID__fstrcat (_MCID_FIRST_BC + 4) +# define MCID__fstrncat (_MCID_FIRST_BC + 5) +# define MCID__fstrcpy (_MCID_FIRST_BC + 6) +# define MCID__fstrncpy (_MCID_FIRST_BC + 7) +# define MCID__fstrdup (_MCID_FIRST_BC + 8) +# define MCID__fstrset (_MCID_FIRST_BC + 9) +# define MCID__fstrnset (_MCID_FIRST_BC + 10) + +# define MCID__dos_setvect (_MCID_FIRST_BC + 11) +# define MCID__getdcwd (_MCID_FIRST_BC + 12) + +# define MCID_getcwd (_MCID_FIRST_BC + 13) +# define MCID_cgets (_MCID_FIRST_BC + 14) +# define MCID_memccpy (_MCID_FIRST_BC + 15) +# define MCID_strdup (_MCID_FIRST_BC + 16) +# define MCID_strnset (_MCID_FIRST_BC + 17) +# define MCID_strset (_MCID_FIRST_BC + 18) +# define MCID_swab (_MCID_FIRST_BC + 19) +# define MCID_tempnam (_MCID_FIRST_BC + 20) + +# define MCID_farmalloc (_MCID_FIRST_BC + 21) +# define MCID_farrealloc (_MCID_FIRST_BC + 22) +# define MCID_farfree (_MCID_FIRST_BC + 23) +# define MCID_farcalloc (_MCID_FIRST_BC + 24) +# define MCID_movmem (_MCID_FIRST_BC + 25) +# define MCID_setmem (_MCID_FIRST_BC + 26) +# define MCID_setvect (_MCID_FIRST_BC + 27) +# define MCID_stpcpy (_MCID_FIRST_BC + 28) +# define MCID__fmovmem (_MCID_FIRST_BC + 29) +# define MCID__fsetmem (_MCID_FIRST_BC + 30) +# define MCID_new (_MCID_FIRST_BC + 31) +# define MCID_delete (_MCID_FIRST_BC + 32) +# define MCID__fullpath (_MCID_FIRST_BC + 33) + +#endif + + +/* + 'TOUCH' macro so high warning levels don't generate + 'unreferenced variable' warnings, especially when + making Production libraries... All MemCheck code + compiles without a whymper. +*/ +#if defined (_CC_WATCOM_) +# define TOUCH(var) var = var +#elif defined (_CC_BORLAND4_) +# define TOUCH(var) var = var +#else +# define TOUCH(var) if (var) +#endif + + +/* Default log name used by stock erf_logfile() and variants... */ +#define MEMCHECK_LOG "MEMCHECK.LOG" + +#define MAX_MEMORY 1000 /* 1000K is more than ever possible */ + +/* User-Modifiable Defaults */ + +#define D_CheckByteCt sizeof(int) /* word size is default */ +#define D_AlignSize sizeof(int) /* align returned memory ptrs */ + +/* Number of bytes to copy from null segment (to determine null + pointer assignments) +*/ +#define D_NULLCHECK_BYTES_FAR 16 /* at 0000:0000 (far NULL) */ +#define D_NULLCHECK_BYTES_NEAR 16 /* at DS:0000 (near NULL) */ +#define MAX_NULLCHECK_BYTES_FAR 1024 /* extent of irupt vect tbl */ +#define MAX_NULLCHECK_BYTES_NEAR 66 /* reserved in DS */ + +/* Unroll the double-negative */ +/* + Debugging code specific to MemCheck can be + conditionally compiled by placing it within + #if-#endif sections: (NOTE that this is NOT + required when just using API functions) + + #ifdef MEMCHECK + + void _MCCALLBACK trackf_special (int op, MEMRECP memrecp) + { + (... your custom callback code ...) + } + + #endif + + instead of the more arcane + + #ifndef NOMEMCHECK + : + #endif + + (Both approaches work equally well, however...) +*/ +#ifndef NOMEMCHECK /* MemCheck active */ +#define MEMCHECK +#endif + + +/* *** Calling Conventions *** */ + +#if !defined (_CC_ANSI_) +#define _MCAPI pascal /* MemCheck API functions */ +#define _FASTAPI pascal /* speed-critical functions */ +#define _MCCDECL cdecl /* MemCheck varargs API */ +#define _MCCALLBACK cdecl /* callback functions */ +#define _MCVAR cdecl /* MemCheck global variable */ +#else +#define _MCAPI /* MemCheck API functions */ +#define _FASTAPI /* speed-critical functions */ +#define _MCCDECL /* MemCheck varargs API */ +#define _MCCALLBACK /* callback functions */ +#define _MCVAR /* MemCheck global variable */ +#endif + +#if !defined(_CC_WATCOM_) +# define _RTL _MCCDECL /* RTL calling conv */ +#else +# define _RTL /* RTL calling conv */ + +/* WATCOM C++ does not currently (2/17/94) + accept "cdecl" as a modifier on variables... +*/ +# undef _MCVAR +# define _MCVAR +#endif /* _CC_WATCOM_ */ + +/* 32-bit compiler-independent stuff */ +#if !defined(_CC32_) +#define _MCFAR far +#define _MCFARCALL far +#define _MCNEAR near +#define _MCNEARCALL near +#define _MCHUGE huge +#else +#define _MCFAR +#define _MCFARCALL +#define _MCNEAR +#define _MCNEARCALL +#define _MCHUGE +#endif /* _CC32_ */ + +/* + MSC declares the following routines as "far"... + So does Borland. WATCOM does not; define glue. + + _fstrset _fstrnset _fstrcpy + _fstrncpy _fstrcat _fstrncat + _fmemset _fmemmove _fmemccpy +*/ +#if !defined(_CC_WATCOM_) +# define _MCFARGLUE far +#else +# define _MCFARGLUE +#endif + + +/* Microsoft C7 and later will not have + have a malloc_mc, only _fmalloc_mc; likewise + with free_mc. + The RTLMALLOC and RTLFREE macros are used + to refer to a generically present back-end malloc + and free. +*/ +#if defined (_CC_MSC_) +# if defined (LDATA) +# define RTLMALLOC RTL(_fmalloc) +# define RTLFREE RTL(_ffree) +# else +# define RTLMALLOC RTL(_nmalloc) +# define RTLFREE RTL(_nfree) +# endif +#else /* non-MSC */ +# define RTLMALLOC RTL(malloc) +# define RTLFREE RTL(free) +#endif + + +/* WATCOM defines its atexit funcs as a "register", + which causes a param type mismatch. + _ATEXITFUNC calling convention smooths this out. +*/ +#if defined (_CC_WATCOM_) +# define _ATEXITFUNC +#else +# define _ATEXITFUNC _MCCDECL +#endif + + +/* MemCheck Tracking Mode + + Returned by mc_get_mode(). + Indicates whether information on each allocation + is being stored in memory or on disk. +*/ +#define MC_USING_MEMORY 1 +#define MC_USING_DISK 2 + + +/* Min, max orders for each node in the B-tree */ + +#define BT_ORDER_MIN 5 +#define BT_ORDER_MAX 255 /* maximum tree order */ +#define BT_ORDER_DEFAULT 19 /* default tree order */ + +/* + Returned by mc_get_speed(). + Pass as params to mc_set_speed(). +*/ +#define MC_RUN_NORMAL 1 +#define MC_RUN_FAST 2 + +/* For mc_report(): + "Flags" field of the MEMREC structure + is set to REPORT_START or REPORT_END + to indicate begin and end of report. + + NOTE: If REPORT_START or REPORT_END conflicts + with defines in your project, just comment + them out and use the MC_... variants instead. +*/ +#define REPORT_START (MRFLAGS)0xFE +#define REPORT_END (MRFLAGS)0xFD + +#define MC_REPORT_START (MRFLAGS)0xFE /* alternates in case of conflict */ +#define MC_REPORT_END (MRFLAGS)0xFD + + +/* + Maximum number of breakpoints that + can be set via mc_breakpoint(). +*/ +#define MC_MAX_BREAKS 50 + + +/* "Optype" parameter on Tracking function callback. */ +#define TRACKF_ADD 1 /* record being added to tree */ +#define TRACKF_DEL 2 /* record being deleted from tree */ + +/* Used for the mcflags field of MEMREC to indicate + whether file & line are exact or approximate +*/ +#define MRFLAG_EXACT_LOC ( (MRFLAGS) 0x01) + +/* + Set if the values for a MEMREC are already converted + to "user" values. +*/ +#define MRFLAG_USER_SPECS ( (MRFLAGS) 0x02) +#define MRFLAG_NO_CHECKBYTES ( (MRFLAGS) 0x04) + +/* Alternate name */ +#define mc_message mc_debug + +/* + Parameter to mc_check_transfer() that + specifies that the given data transfer function cannot + have overlapping source & destination. + (MCID's are unsigned bytes.) +*/ +#define MCF_NO_OVERLAP ((unsigned)0x8000) +#define NO_OVERLAP(mcid) ((mcid) | MCF_NO_OVERLAP) + +/* Parameter to mc_check_transfer indicating that + the found memory record is not needed */ +#define NO_MEMREC ((MEMRECP)NULL) +#define NOMEMREC NO_MEMREC + +/* Parameter to mc_check_transfer indicating that + there is no source pointer operand associated + with the data transfer being checked: e.g. memset. */ +#define NO_SOURCE ((void _MCFAR *)0xFFFFFFFA) + + +/* *** TYPEDEFS *** */ + +typedef char * MCSF; /* MemCheck source file */ +typedef unsigned int MCSL; /* MemCheck source line */ +typedef unsigned char MCID; /* MemCheck function ID */ + +typedef unsigned long MCEFLAGS; /* MemCheck error flags */ +typedef void _MCFAR * MCPTR; /* type of ptr stored in tree */ +typedef unsigned char MRFLAGS; /* flags in MEMRECORD */ +typedef unsigned long MCFLAGS; /* MemCheck settings flags */ + +/* MemCheck Rocket allocator prototypes */ +typedef void _MCFAR * (_MCFAR *ROCKETALLOCF) (size_t); +typedef void (_MCFAR *ROCKETFREEF) (void _MCFAR *); + +#pragma pack(1) +/* + Memory Tracking Structure (MEMREC) + + This is the data structure for buffers being + tracked by MemCheck. +*/ +typedef struct MemRecord + { + MCPTR ptr; /* heap/registered ptr */ + MCID mcid; /* MemCheck function ID */ + MRFLAGS flags; /* internal MC flags */ + unsigned long allocno; /* cardinality of allocation */ + unsigned long size; /* size of block */ + MCSF file; /* source file */ + MCSL line; /* source line */ + + } MEMREC, _MCFAR *MEMRECP; + + +/* *** SETTINGS *** */ +/* These are values that describe the life of a MemCheck run. */ + +typedef struct MCSETTINGS { + /* + Bit Flag What + --- --------------- ----------------------------------- + 0 MCF_ACTIVE MemCheck active or off + 1 MCF_FAST_MODE Fast mode or normal + 2 MCF_PROTECTED_MODE Protected mode or real + 3 MCF_FAR_NULL_CHECK Check for far NULL ptr assigns * + 4 MCF_NEAR_NULL_CHECK Check for far NULL ptr assigns * + 5 MCF_STANDARD_STACK Standard stack frame * + 6 MCF_AUTOINIT Start up automatically * + 7 MCF_CLEAR_ON_FREE Clear buffers when freed + 8 MCF_DISK_ROCKET Use DiskRocket options + 9 MCF_IDX_IN_MEMORY Use memory only for Rocket indexes * + (only if DiskRocket linked) + 10 MCF_SOURCE_ONLY Intercept in source code only + + 11 - 31 Reserved + */ + MCFLAGS Flags; /* Main settings flags */ + + unsigned short MaxMem; /* Max mem for tree usage, in K */ + unsigned short NearNullBytes; /* bytes to check in near null */ + unsigned short FarNullBytes; /* " " " " far " */ + unsigned char CheckByteCt; /* check byte count */ + unsigned char AlignSize; /* alignment boundary size */ + char TrackingDir[36]; /* Rocket stores temp files here */ + + } MCSETTINGS, *MCSETTINGSP; + + +/* Random 32-bit .CFG file sentinel */ +#define MC_CFG_FILE_SENTINEL ( (unsigned long) 0x10F23BC4 ) + +typedef struct MCCfgInfo { + + unsigned long sentinel; /* always MC_CFG_FILE_SENTINEL */ + MCSETTINGS MemCheckSettings; /* saved by user */ + + } MCCFGINFO, *MCCFGINFOP; + + +#ifndef _CC32_ + +/* 16-bit exception stack */ +typedef struct { + + unsigned xRetIP; + unsigned xRetCS; + unsigned xErr; + unsigned xIP; + unsigned xCS; + unsigned xFlags; + unsigned xSP; + unsigned xSS; + + } MCEXCEPTINFO; + +#else + +/* 32-bit exception stack */ +typedef struct { + + unsigned long xRetEIP; + unsigned short xRsvd1; + unsigned short xRetCS; + unsigned long xErr; + unsigned long xEIP; + unsigned short xRsvd2; + unsigned short xCS; + unsigned long xFlags; + unsigned long xESP; + unsigned short xRsvd3; + unsigned short xSS; + + } MCEXCEPTINFO; + +#endif /* _CC32_ */ + +/* Values for MCCRITSECT.action */ +#define MCCS_ENTER_SECTION 0 +#define MCCS_LEAVE_SECTION 1 + +#define MCCS_ACTION(pMCCS) (pMCCS->nAction) +#define MCCS_ENTER(pMCCS) ((*(pMCCS->pLocked))++) /* inc flag */ +#define MCCS_LEAVE(pMCCS) ((*(pMCCS->pLocked))--) /* dec flag */ + +/* + Critical section object - ptr to this passed to crit sect callback + WARNING: access these fields ONLY via the MCCS_...() macros. + To do otherwise subjects you to changes in implementation + of the underlying coordination of critical section locking. +*/ +typedef struct { + int nAction; /* MCCS_ENTER/LEAVE_SECTION */ + int * pLocked; /* # times entered */ + unsigned long ulRsvd; /* internal use */ + } MCCRITSECT; + +#pragma pack() + + +#define MC_FEXIT ( (MCID) 0xFF ) + + +/* Error Reporting Function typedef */ +#ifndef _ERF_DEFINED +#define _ERF_DEFINED +typedef void (_MCCALLBACK *ERF) (char *); +#endif + + +/* *** Callback Functions *** */ + +/* Interception callback (on every interception) */ +typedef void (_MCCALLBACK * GLOBALF) (void); + +/* Called whenever nodes added to or deleted from MC database */ +typedef void (_MCCALLBACK *TRACKF) (int, MEMRECP); + +/* User-definable check function to add to transfer checking */ +typedef void (_MCCALLBACK * CHECKF) ( + int , /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); + +/* Funcs called at startup or shutdown */ +typedef void (_MCCALLBACK *STARTF) (void); +typedef void (_MCCALLBACK *ENDF) (void); + +/* Report function type passed to mc_report() */ +typedef void (_MCCALLBACK *REPORTF) (MEMRECP); + +/* Additional heap pointer verification (troubleshoot only) */ +typedef int (_MCCALLBACK *VERIFYF) (void _MCFAR *); + +typedef void (*MCVOIDFP) (void); + +/* Exception handler */ +typedef void (_MCCALLBACK _MCFAR *MCEXCEPTF) (void); + +/* Multitasking enter/exit critical section callback. + Specify as param to mc_set_critf() at beginning of program; + callback function will be called with MCCS_ENTER_SECTION + on entry to MemCheck, or MCCS_LEAVE_SECTION on exit. + + NOT TO BE CONFUSED WITH A MEMCHECK "GLOBAL" FUNCTION. + Global functions (GLOBALF's) are used to perform any + actions on the interception of a runtime function; + CRITF's must be used ONLY to serialize access to MemCheck. +*/ +typedef void (_MCCALLBACK * MCCRITF) (MCCRITSECT *); + + +/* Stack Frame Handler + + You can define your own function to + record, analyze, or inspect each stack frame + when mc_stack_trace() is called. + + You *cannot* modify ANY of the values passed + in, as the "const" typing indicates. If you need to + modify a value, make a copy. See the MemCheck 3.0 + documentation for more details on stack frame handlers. +*/ + +typedef void (_MCFAR _MCCDECL *_SSFRAMEHANDLER ) ( + short const , /* AX: near/far/error flag */ + unsigned short const , /* CX: near (default) rtn CS */ + unsigned short const , /* ES: far rtn CS */ + unsigned const , /* DI: rtn offset from stack */ + short const /* DX: frame count */ + ); + +/* Values for "flag" constant parameter to a + stack frame handler. +*/ +#define TRACE_BAD_FRAME 0x00 /* couldn't recognize frame */ +#define TRACE_FAR_CALL 0x01 /* frame represents a far call */ +#define TRACE_NEAR_CALL 0x02 /* " " " near " */ +#define TRACE_BAD_CHAIN 0x03 /* frame BP chewed up */ +#define TRACE_BEGIN 0x80 /* signals begin walk */ +#define TRACE_END 0x81 /* signals end of walk */ + + +/* MC Settings Structure, "flags" member: */ +#define MCF_ACTIVE (MCFLAGS)(0x01) +#define MCF_FAST_MODE (MCFLAGS)(0x02) +#define MCF_PROTECTED_MODE (MCFLAGS)(0x04) +#define MCF_FAR_NULL_CHECK (MCFLAGS)(0x08) +#define MCF_NEAR_NULL_CHECK (MCFLAGS)(0x10) +#define MCF_STANDARD_STACK (MCFLAGS)(0x20) +#define MCF_AUTOINIT (MCFLAGS)(0x40) +#define MCF_CLEAR_ON_FREE (MCFLAGS)(0x80) +#define MCF_DISK_ROCKET (MCFLAGS)(0x100) +#define MCF_IDX_IN_MEMORY (MCFLAGS)(0x200) +#define MCF_SOURCE_ONLY (MCFLAGS)(0x400) + + +/* *** Conditional Compilation *** */ + +/* -------------------------------------------------------------- + If MEMCHECK is not being `compiled out' (via definition + of the constant NOMEMCHECK), include this section... +-------------------------------------------------------------- */ + +#if !defined(MEMCHECK) + +/* Include Section for `MemCheck Not Active' */ + +/* ***************************** + MemCheck Not Active Section + ***************************** + + This section completely removes or + "evaporates" all MemCheck function references + from your projects when you compile with + NOMEMCHECK #defined. + + There's no need to remove any MemCheck + headers or statements from your code + to produce a full production version + of your application. + + o + ooo + ooooooo + ooooooooo + ooooooooooo + ooo + ooo + ooo + ooo + */ + +#ifndef MEMCHECK_MODULE + +/* Evaporate all MemCheck 3.0 API + statements to do nothing, safely... */ + +# define mc_alloc_count() 0L +# define mc_blocks_allocated() 0L +# define mc_blocks_freed() 0L +# define mc_breakpoint(fi) 0 +# define mc_bytes_allocated() 0L +# define mc_bytes_freed() 0L +# define mc_check(p) 0 +# define mc_check_buffers() 0 +# define mc_cur_file() "No file" +# define mc_cur_line() 0 +# define mc_debug(s) +# define mc_debugf(_args) +# define mc_debug_on() +# define mc_debug_off() +# define mc_endcheck() (MCEFLAGS)0 +# define mc_errno() MCE_NO_ERROR +# define mc_error_flags() (MCEFLAGS)0 +# define mc_error_text(e) "MemCheck not active" +# define mc_except_text(e) "MemCheck not active" +# define mc_file() "No file" +# define mc_find_buffer(p,mr) 0 +# define mc_func() (MCID)0 +# define mc_func_name(mcid) ("") +# define mc_get_erf() (ERF)NULL +# define mc_get_mode() 0 +# define mc_get_speed() 0 +# define mc_in_source() 0 +# define mc_is_active() 0 +# define mc_line() 0 +# define mc_load_debugger() +# define mc_location_text() "MemCheck not active" +# define mc_memory_leaked() 0L +# define mc_memrec() (MEMRECP)NULL +# define mc_memrec_text() "MemCheck not active" +# define mc_msg_continued() 0 +# define mc_nullcheck() 0 +# define mc_null_snapshot() +# define mc_register(p,s) +# define mc_report(_f) +# define mc_set_alignsize(_s) +# define mc_set_breakfile(_f) +# define mc_set_checkbytes(_cb) +# define mc_set_checkf(_f) (CHECKF)NULL +# define mc_set_critf(_f) +# define mc_set_endf(erf) (ENDF)NULL +# define mc_set_erf(erf) (ERF)NULL +# define mc_set_globalf(_f) (GLOBALF)NULL +# define mc_set_globalexitf(_f) (GLOBALF)NULL +# define mc_set_speed(_s) +# define mc_set_location() +# define mc_set_trackf(_f) (TRACKF)NULL +# define mc_set_tracefile(_f) +# define mc_set_tree_order(_q) +# define mc_set_track_dir(_dir) +# define mc_source_ptr() (MCPTR)NULL +# define mc_stack_trace(_memo) 0 +# define mc_startcheck(_erf) +# define mc_unregister(p) +# define mc_set_exceptf(f) +# define mc_get_exceptf() ((MCEXCEPTF)NULL) + +/* *** Stock error reporting functions *** */ +# define erf_default(_msg) +# define erf_standard(_msg) +# define erf_logfile(_msg) +# define erf_log_only(_msg) +# define erf_trace(_msg) + +/* Internal Helpers */ +# define _direct_output(_s) +# define _mcsl(_f,_l) +# define _mcsl_delete(_f,_l) +# define _mcsl_new(_f,_l) +# define _mcslx(_f,_l,_s) +# define _mc_set_delflag() +# define _mc_set_location(_f,_l) +# define _mc_set_newflag() + +/* Link-time compileouts */ +# define MC_SET_AUTOINIT(_toggle) +# define MC_SET_CHECK_FREQ(_freq) +# define MC_SET_CHECKF(_f) +# define MC_SET_CRITF(_f) +# define MC_SET_ENDF(_f) +# define MC_SET_ENV_VAR(_envvarname) +# define MC_SET_ERF(_f) +# define MC_SET_GLOBALF(_f) +# define MC_SET_GLOBALEXITF(_f) +# define MC_SET_LOGFILE(_logfilename) +# define MC_SET_PANIC_BUFFERS(_q) +# define MC_SET_SSFH(_f) +# define MC_SET_STARTF(_f) +# define MC_SET_TRACKF(_f) +# define MC_SET_VERIFYF(_f) + +/* Back-end direct */ +#define RTL(_f) _f + +/* *** C++ *** */ +#ifdef __cplusplus + +#define NEW(_object) new _object +#define DELETE(_object) delete _object +#define DELETE_ARR(_arr) delete[] _arr + +#define cpp_malloc(_s) malloc(_s) +#define cpp_calloc(_n,_s) calloc(_n,_s) +#define cpp_free(_p) free(_p) + +/* Borland C++ */ +#define cpp_farmalloc(_s) farmalloc(_s) +#define cpp_farfree(_fp) farfree(_fp) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) _fmalloc(_s) +#define cpp__ffree(_fp) _ffree(_fp) + +#endif /* C++ */ + +#endif /* not MEMCHECK_MODULE */ + + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#else /* MEMCHECK is defined */ + +/* #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# */ + +#pragma message ("MemCheck V3.0") + +/* + ************************* + MemCheck Active Section + ************************* + + The rest of this header file deals with + MemCheck's being compiled into an application. + + */ + +/* Specify that vars and funcs are straight C.. */ +#ifdef __cplusplus +extern "C" { +#endif + + +/* *** ANSI Location Defines *** */ + +#define _MC_NO_FILE ((MCSF) 0) /* just in case... */ +#define _MC_NO_LINE ((MCSL) 0) + + +/* Allow for the possibility of _MCSF_ being + defined to reference a single, static module + filename. This prevents a multiplicity of + static filenames getting added to the DGROUP, e.g. + + static char *_thisfile = __FILE__; + #define _MCSF_ ((MCSF)thisfile) + #include + + This is only needed under MSC pre-VC++. + Borland has "-d" Merge duplicate strings. + VC++ has "/Gf" Elim duplicate strings. +*/ +#if !defined (_MCSF_) +# ifdef __FILE__ +# define _MCSF_ (MCSF)__FILE__ +# else +# define _MCSF_ _MC_NO_FILE +# endif +#endif + +#ifdef __LINE__ +# define _MCSL_ (MCSL)__LINE__ +#else +# define _MCSL_ _MC_NO_LINE +#endif + + +/* *** Standard ANSI C Includes *** + + For va_list function call args + Inclusion of this header won't change + the behavior of host code. +*/ +#if !defined (va_start) /* avoid multiple inclusion... */ +# include +#endif + + +/* *** Compiler-specific includes *** */ + +/*lint -save -e537 Repeated include files (if necessary) */ +#if defined(_CC_MSC_COMPATIBLE_) + +# if !defined (_INC_MALLOC) /* C7.x and later optimization */ +# include +# endif + +# if !defined (_INC_STRING) /* C7.x and later optimization */ +# include +# endif + +#elif defined(_CC_BORLAND_) + +# if !defined (__ALLOC_H) +# include +# endif + +/* String functions must be proto'd before pragmas */ +# if !defined (__STRING_H) +# include +# endif + +#endif /* Compiler-specific includes */ +/*lint -restore */ + +#if defined (_CC_POWERPACK32_) +extern void cdecl mcinitfp_startcheck (void); +extern void cdecl mcexitfp_endcheck (void); +#pragma startup mcinitfp_startcheck 16 +#pragma exit mcexitfp_endcheck 16 +#endif + +/***************************************/ +/* *** MemCheck 3.0 API Prototypes *** */ +/***************************************/ + +/* Internal helper macro - proto shorthand */ +#define _LOCP MCSF,MCSL + +extern unsigned long _MCAPI mc_alloc_count (void); +extern unsigned long _MCAPI mc_blocks_allocated (void); +extern unsigned long _MCAPI mc_blocks_freed (void); +extern unsigned long _MCAPI mc_bytes_allocated (void); +extern unsigned long _MCAPI mc_bytes_freed (void); +extern int _MCAPI mc_check (void _MCFAR *); +extern int _MCAPI mc_check_buffers (void); +extern MCSF _MCAPI mc_cur_file (void); +extern MCSL _MCAPI mc_cur_line (void); +extern void _MCCDECL mc_debugv (const char *, ...); +extern void _MCAPI mc_debug (const char *); +extern MCEFLAGS _MCAPI mc_endcheck (void); +extern MCEFLAGS _MCAPI mc_error_flags (void); +extern char * _MCAPI mc_error_text (int); +extern int _MCAPI mc_errno (void); +extern char * _MCAPI mc_except_text (unsigned); +extern MCSF _MCAPI mc_file (void); +extern int _MCAPI mc_find_buffer(void _MCFAR *realptr,MEMRECP memrecp); +extern MCID _MCAPI mc_func (void); +extern char * _MCAPI mc_func_name(MCID); +extern ERF _MCAPI mc_get_erf (void); +extern MCEXCEPTF _MCAPI mc_get_exceptf (void); +extern int _MCAPI mc_get_mode (void); +extern int _MCAPI mc_get_speed (void); +extern char * _MCAPI mc_get_tracefile (void); +extern int _MCAPI mc_in_source (void); +extern int _MCAPI mc_is_active (void); +extern MCSL _MCAPI mc_line (void); +extern char * _MCAPI mc_location_text (void); +#define mc_load_debugger() _asm int 3 +extern unsigned long _MCAPI mc_memory_leaked (void); +extern char * _MCAPI mc_memrec_text (MEMRECP); +extern MEMRECP _MCAPI mc_memrec (void); +extern int _MCAPI mc_msg_continued (void); +extern int _MCAPI mc_nullcheck (void); +extern void _MCAPI mc_null_snapshot (void); +extern void _MCAPI mc_register (void _MCFAR *, unsigned long); +extern void _MCAPI mc_report (REPORTF); +extern void _MCAPI mc_set_alignsize (unsigned int); +extern void _MCAPI mc_set_breakfile (char *); +extern void _MCAPI mc_set_checkbytes (unsigned int); +extern CHECKF _MCAPI mc_set_checkf (CHECKF); +extern void _MCAPI mc_set_critf (MCCRITF); +extern ENDF _MCAPI mc_set_endf (ENDF); +extern ERF _MCAPI mc_set_erf (ERF); +extern MCEXCEPTF _MCAPI mc_set_exceptf (MCEXCEPTF); +extern GLOBALF _MCAPI mc_set_globalf (GLOBALF); +extern GLOBALF _MCAPI mc_set_globalexitf (GLOBALF); +#define mc_set_location() _mc_set_location(_MCSF_,_MCSL_) +extern MCPTR _MCAPI mc_source_ptr (void); +extern void _MCAPI mc_set_speed (int); +extern void _MCAPI mc_set_tracefile (char *); +extern void _MCAPI mc_set_track_dir (char *); +extern TRACKF _MCAPI mc_set_trackf (TRACKF); +extern void _MCAPI mc_set_tree_order (int); +extern int _MCAPI mc_stack_trace (char *); +extern void _MCAPI mc_startcheck (_LOCP, ERF); +extern void _ATEXITFUNC mc_endcheck_at_exit (void); +extern void _MCAPI mc_unregister (void _MCFAR *); + +/* Debugging versions of the MemCheck library only */ +extern void _MCAPI mc_debug_on (void); +extern void _MCAPI mc_debug_off (void); +extern int _MCCALLBACK mc_search_heap (void _MCFAR *); + +/* *** INTERNAL API HELPERS *** */ +extern void _MCAPI _mc_set_location (_LOCP); +extern void _FASTAPI _mcsl (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcslx (MCSF,MCSL,size_t); /* location run-ahead */ +extern void _FASTAPI _mcsl_new (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mcsl_delete (MCSF,MCSL); /* location run-ahead */ +extern void _FASTAPI _mc_set_newflag (void); /* new's a'comin' */ +extern void _FASTAPI _mc_set_delflag (void); /* delete's a'comin' */ + +/* Misc - uses INT 9 to output directly to screen */ +#if !defined (_CC_WATCOM32_) +extern void _MCCDECL _MCFAR _direct_output (char _MCFAR *); +#else +#define _direct_output(s) printf ("%s\n", s) /* ALERT */ +#endif + +/* + mc_breakpoint() is now a MemCheck Tracking function (TRACKF). + Tracking functions get called every time + MemCheck adds or deletes from its database. +*/ +#define mc_breakpoint(_f) \ + mc_set_trackf( (mc_set_breakfile (_f), trackf_breakpoint) ) +#define mc_breakpoint_trace(_f) \ + mc_set_trackf( (mc_set_tracefile (_f), trackf_breakpoint_trace) ) + + +/* *** Advanced-user API extenders *** */ + +/* extern int _MCAPI mc_find_buffer(void _MCFAR *, MEMRECP); */ +extern int _MCAPI mc_check_transfer( + void _MCFAR *, + void _MCFAR *, + unsigned long, + unsigned, + unsigned, + MEMRECP); + +/* mc_get_settings + + Write your own "get settings" routine + to override the one shipped with MemCheck. + You can hard-wire any settings you like, e.g. + always ON for versions of your app shipped to + testers/QA stations, etc. +*/ +extern void _MCCALLBACK mc_get_settings (MCSETTINGS *); + + +/* *** Callbacks / Functionality Extenders *** + + Function Type Called... + -------------- ------------------------------ + Error reporting To handle each MemCheck error message + Global Interception On each MemCheck interception + Checking On every data transfer check + Tracking On every allocation/deallocation + Start On mc_startcheck or AutoInit + End At mc_endcheck or MemCheck shutdown + + Refer to your MemCheck 3.0 manual for further details. + + *** STOCK FUNCTIONS *** + These functions are available in the MemCheck + libraries as "ready-made" for your programming + pleasure in the categories above. +*/ + +/* *** Stock error reporting functions *** */ + +extern void _MCCALLBACK erf_default (char *); +extern void _MCCALLBACK erf_standard (char *); +extern void _MCCALLBACK erf_logfile (char *); +extern void _MCCALLBACK erf_log_only (char *); +extern void _MCCALLBACK erf_trace (char *); +extern void _MCCALLBACK erf_trace_all (char *); +extern void _MCCALLBACK erf_trace_obj (char *); +extern void _MCCALLBACK erf_stdout (char *); +extern void _MCCALLBACK erf_stderr (char *); +extern void _MCCALLBACK erf_find_leaks (char *); + +#define erf_printf erf_stdout /* alias*/ + +/* *** Stock Tracking Functions *** */ + +extern void _MCCALLBACK trackf_default (int, MEMRECP); +extern void _MCCALLBACK trackf_all (int, MEMRECP); +extern void _MCCALLBACK trackf_all_2 (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint (int, MEMRECP); +extern void _MCCALLBACK trackf_breakpoint_trace (int, MEMRECP); +extern void _MCCALLBACK trackf_big_alloc (int, MEMRECP); + +/* *** Stock End Functions *** */ + +extern void _MCCALLBACK endf_default (void); /* does nothing */ +extern void _MCCALLBACK endf_info (void); /* write run info to log */ +extern void _MCCALLBACK endf_alert (void); /* warn if run errs */ +extern void _MCCALLBACK endf_summary (void); /* warn if run errs */ + +/* *** Stock Start functions *** */ + +extern void _MCCALLBACK startf_default (void); /* does nothing */ +extern void _MCCALLBACK startf_info (void); /* write options to log */ + +/* *** Stock Check Functions *** */ + +extern void _MCCALLBACK checkf_default (int,void _MCFAR *,long); +extern void _MCCALLBACK checkf_dataseg ( + int, /* 0 or MCE_... error val for this xfer op */ + void _MCFAR *, /* user ptr dest */ + long /* bytes to copy to dest */ + ); +extern void _MCCALLBACK checkf_verify_heap (int,void _MCFAR *,long); + +/* *** Stock Global Interception Functions *** */ + +extern void _MCCALLBACK globalf_default (void); /* does nothing */ +extern void _MCCALLBACK globalf_supercheck (void); +extern void _MCCALLBACK globalf_check_buffers (void); +extern void _MCCALLBACK globalf_heapcheck (void); + +/* *** Stock Report Functions *** */ +extern void _MCCALLBACK reportf_default (MEMRECP); + +/* *** Stock Exception Handlers *** */ +extern void _MCCALLBACK _MCFAR exceptf_default (void); + +/* *** Stock Stack Frame Handlers *** */ +extern void _MCFAR _MCCDECL ssfh_info ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_fast ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + /* int const _flag, */ + +extern void _MCFAR _MCCDECL ssfh_standard ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +extern void _MCFAR _MCCDECL ssfh_debug ( + short const, unsigned short const, unsigned short const, unsigned const, short const); + +/* */ +extern unsigned int _MCAPI mc_stacktop (void); /* high address */ +extern unsigned int _MCAPI mc_stackend (void); /* low address */ + + +/* Function external variables. + + These are used effectively with MemCheck 3.0's AutoInit + setting. Under AutoInit, MemCheck fires itself up automatically + on its first interception. Under these circumstances, + there's no chance to have changed any defaults (like the + ERF or error reporting function). These variables provide + a link-level method of setting these functions: + + #include + : + // Sets custom erf at link-time + MC_SET_ERF (erf_custom_startup); + : +*/ +/* *** GLOBALS *** */ + +extern ERF _MCVAR MC_ERF; /* error reporting func ptr */ +extern CHECKF _MCVAR MC_CheckF; /* transfer check func */ +extern MCCRITF _MCVAR MC_CritF; /* crit section enter/exit */ +extern GLOBALF _MCVAR MC_GlobalF; /* global interception callback */ +extern GLOBALF _MCVAR MC_GlobalExitF; /* called on exit interception */ +extern TRACKF _MCVAR MC_TrackF; /* alloc/dealloc callback */ +extern STARTF _MCVAR MC_StartF; /* startup callback */ +extern ENDF _MCVAR MC_EndF; /* shutdown callback */ + +extern VERIFYF _MCVAR MC_VerifyF; /* troubleshooting */ + +extern char * _MCVAR MC_LogFile; /* log file name used */ +extern char _MCVAR MC_UserAutoInit; +extern int _MCVAR MC_CheckFreq; /* for globalf_supercheck() et al */ +extern char * _MCVAR MC_EnvVar; /* Env var to detect 'active' */ +extern unsigned short _MCVAR MC_DataSeg; /* DS value */ + +extern int _MCVAR MC_MaxTraceDepth; +extern char * _MCVAR MCST_Desc; /* trace descrip to mc_..trc() */ + +extern MCSETTINGS _MCVAR MC_DefaultSettings; /* default settings */ +extern MCSETTINGS _MCVAR MC_Settings; /* real settings-- + USE WITH CARE!!! */ + +extern MCVOIDFP _MCVAR MC_PMMap1; /* p-mode func in map seg 1 */ + +/* Protected mode exception handling */ +extern unsigned char _MCVAR MC_ExceptList[]; /* exceptions to handle */ +extern MCEXCEPTINFO _MCVAR MC_ExceptInfo; /* in exception */ +extern MCEXCEPTF _MCVAR MC_ExceptF; /* installed hdler */ + +/* Rocket Guidance Systems */ +extern ROCKETALLOCF _MCVAR MC_RocketAllocF; +extern ROCKETFREEF _MCVAR MC_RocketFreeF; +extern unsigned char _MCVAR MC_PanicBufCount; /* anti-tree failure */ + +/* This char is used to fill freed buffers + if the "ClearOnFree" option in effect. + Default buffer clear char is 0. +*/ +extern unsigned char _MCVAR MC_FreedBufferFillChar; + +/* Link-time defaults. + + These macros are "covers" to insulate you, the developer, + from the underlying implementation, as well as to provide + such bennies as compiling clean out of your code when + NOMEMCHECK or NOMC is defined. + + Use instead of accessing vars directly! + + To use, place the following in ONE MODULE e.g. your main module + (any *one* module will work fine) after the MemCheck + header file has been included: + + #include + MC_SET_...(params); + + For example, to change the default log file that MemCheck + uses at runtime to C:\MYDEV\MYPROG.LOG: + + #include + MC_SET_LOGFILE ("C:\\MYDEV\\MPROG.LOG"); + + Most of these macros have runtime function equivalents, + such as mc_set_erf() for MC_SET_ERF(), etc. Notable + exceptions to this are the following values that + must generally have link-time initializations: + + MC_SET_LOGFILE() + MC_SET_AUTOINIT() + MC_SET_STARTF() +*/ +#define MC_SET_AUTOINIT(_toggle) \ + char _MCVAR MC_UserAutoInit = (_toggle); +#define MC_SET_CHECKF(_f) \ + CHECKF _MCVAR MC_CheckF = (_f) +#define MC_SET_CHECK_FREQ(_freq) \ + int _MCVAR MC_CheckFreq = (_freq) +#define MC_SET_CRITF(_f) \ + MCCRITF _MCVAR MC_CritF = (_f) +#define MC_SET_ENDF(_f) \ + ENDF _MCVAR MC_EndF = (_f) +#define MC_SET_ENV_VAR(_envvarname) \ + char * _MCVAR MC_EnvVar = (_envvarname) +#define MC_SET_ERF(_f) \ + ERF _MCVAR MC_ERF = (_f) +#define MC_SET_EXCEPTF(_f) \ + MCEXCEPTF _MCVAR MC_ExceptF = (_f) +#define MC_SET_GLOBALF(_f) \ + GLOBALF _MCVAR MC_GlobalF = (_f) +#define MC_SET_GLOBALEXITF(_f) \ + GLOBALF _MCVAR MC_GlobalExitF = (_f) +#define MC_SET_LOGFILE(_f) \ + char * _MCVAR MC_LogFile = (_f) +#define MC_SET_PANIC_BUFFERS(_q) \ + unsigned char _MCVAR MC_PanicBufCount = (_q) +#define MC_SET_SSFH(_f) \ + _SSFRAMEHANDLER _MCVAR near MC_SFrameHandler = (_f) +#define MC_SET_STARTF(_f) \ + STARTF _MCVAR MC_StartF = (_f) +#define MC_SET_TRACKF(_f) \ + TRACKF _MCVAR MC_TrackF = (_f) +#define MC_SET_VERIFYF(_f) \ + VERIFYF _MCVAR MC_VerifyF = (_f) + +/* Use the MC_BEGIN_EXCEPTLIST, MC_HANDLE_EXCEPTION, + and MC_END_EXCEPTLIST macros to change the exceptions + MemCheck handles in protected mode by default. + + Usage (exactly as typed): + #include + : + MC_BEGIN_EXCEPTLIST + MC_HANDLE_EXCEPTION (0x0) + MC_HANDLE_EXCEPTION (0xD) + MC_END_EXCEPTLIST + + NOTE: + To turn off MemCheck's exception handling completely, use + + MC_SET_EXCEPTF(NULL); + + instead of trying to define an empty EXCEPTLIST... +*/ +#define MC_BEGIN_EXCEPTLIST \ + unsigned char _MCVAR MC_ExceptList[] = { +#define MC_HANDLE_EXCEPTION(e) \ + (unsigned char)(e), +#define MC_END_EXCEPTLIST \ + (unsigned char)0xFF }; /* 0xFF MUST end list */ + +/* ------------- End MemCheck 3.0 Library Calls --------------- */ + +/* Formulaic rogue varargs interceptions; + most host-code-compatible method... + "Are you experienced?" + + "It is better to be mugged than + to live in fear." - Anon. +*/ +#define _VA_DEF(f,r,p) \ + typedef r (_RTL *p_##f) p; \ + extern p_##f _MCAPI _loc_##f (_LOCP); + +/* Declare sprintf helper function */ +_VA_DEF(sprintf,int,(char *, const char *, ...)) + + /* * * * * * * * * * * * * * * * * * * * * * * + ************************* + Back-End RTL + ************************* +*/ + +/* *** Back-end functions *** */ + +/* Macro to access true back-end RTL. + Used internally by the MemCheck API functions. +*/ +#define __paste(x,y) x ## y +#define RTL(func) __paste(func,_mc) + +/* Macro to encapsulate the declaration of + the renamed (zapped) back-end rtl +*/ +#define _RTLDECL(f,rctype,params) \ + extern rctype _RTL RTL(f) params + + +/* For the conversion that MSC underwent + from C 6 to 7, where non-ANSI calls + have underbars +*/ +#if defined (_CC_MSC_) && !defined (MSC6) +#if (_MSC_VER >= 700) +# define _C7A +#endif +#endif + +#ifdef _C7A +#define C7ANSI(func) _##func +#else +#define C7ANSI(func) func +#endif + +#undef _C7A + + +/* ---------------------------------------------- */ +/* These are the renamed ("zapped") RTL functions */ +/* ---------------------------------------------- */ + +/* *** ANSI *** */ + +_RTLDECL(malloc, void *, (size_t)); +_RTLDECL(calloc, void *, (size_t, size_t)); +_RTLDECL(realloc, void *, (void *, size_t)); +_RTLDECL(free, void, (void *)); +_RTLDECL(memcpy, void *, (void *,const void *,size_t)); +_RTLDECL(memmove, void *, (void *,const void *,size_t)); +_RTLDECL(memset, void *, (void *,int,size_t)); +_RTLDECL(strcpy, char *, (char *,const char *)); +_RTLDECL(strncpy, char *, (char *,const char *,size_t)); +_RTLDECL(strcat, char *, (char *,const char *)); +_RTLDECL(strncat, char *, (char *,const char *,size_t)); +_RTLDECL(vsprintf, int, (char *,const char *,va_list)); +_RTLDECL(sprintf, int, (char *,const char *,...)); + +#if !defined (_CC_ANSI_) +/* *** MSC *** */ + +/* WATCOM doesn't support these... */ +#if !defined(_CC32_) +_RTLDECL(_fmalloc, void far *, (size_t)); +_RTLDECL(_fcalloc, void far *, (size_t, size_t)); +_RTLDECL(_ffree, void, (void far *)); +_RTLDECL(_fmsize, size_t, (void far *)); +#endif + +_RTLDECL(_nmalloc, void _MCNEAR *,(size_t)); +_RTLDECL(_nfree, void, (void _MCNEAR *)); + +/* *** Borland *** */ + +#if !defined(_CC_POWERPACK32_) +_RTLDECL(farmalloc, void _MCFAR *, (unsigned long)); +_RTLDECL(farcalloc, void _MCFAR *, (unsigned long, unsigned long)); +_RTLDECL(farfree, void, (void _MCFAR *)); + +/* *** General Porpoise *** */ + +_RTLDECL(_fmemset, void far * _MCFARGLUE, (void far *,int,size_t)); +_RTLDECL(_fmemcpy, void far * _MCFARGLUE, (void far *,const void far *,size_t )); +_RTLDECL(_fstrcpy, char far * _MCFARGLUE, (char far *,const void far *)); +#endif /* not _CC_POWERPACK32_ */ + +#endif /* not STDC/ANSI */ + +/***************************************************************** + * -------- Function Call Interception Definitions --------- * + *****************************************************************/ + +#ifndef MEMCHECK_MODULE + +/* + This section targets user's code only +*/ + +/* Func interceptors... */ +#define _INTERCEPT(_f) (_mcsl(_MCSF_,_MCSL_),_f) +#define _VA_INTERCEPT(_f) (*_loc_##_f(_MCSF_,_MCSL_)) +#define _SETLOC(_f) (mc_set_location(),_f) + +/* NOTE near _mcsl with #if (_MCC_NEAR_INTERCEPT == 0) */ + +/* + MC_NO_TRANSFER_SIZE is used to eliminate errors or warnings + like "sizeof returns 0" or "Not allowed type in sizeof ". + These occur for unsized variables declared like + + extern unsigned char gHelpString[]; + + The optimal solution is to "size" the extern, e.g. + + extern unsigned char gHelpString[80]; + + but where this may not be practical, MC_NO_TRANSFER_SIZE may + be defined on a module-by-module OR project-wide basis. +*/ +#ifdef MC_NO_XFER_SIZE /* beta compat */ +# define MC_NO_TRANSFER_SIZE +#endif +#ifdef NO_TRANSFER_SIZE /* alternate */ +# define MC_NO_TRANSFER_SIZE +#endif + +#if defined (MC_NO_TRANSFER_SIZE) +# define _INTERCEPTX(_f,_d) _INTERCEPT(_f) +#else /* standard; transmit sizeof dest */ +# define _INTERCEPTX(_f,_d) (_mcslx(_MCSF_,_MCSL_,sizeof(_d)),_f) +#endif + + +/* Intrinsic Function Disabling + + It's important to disable function inlining for + all intercepted functions. +*/ + +#if defined(_CC_MSC_) + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros... +*/ +#pragma function(strcat) +#pragma function(strcpy) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(strset) + +#if defined(_MSC_VER) +#if (_MSC_VER >= 700) +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) +#pragma function(_fstrset) +#pragma function(_strset) +#endif +#endif /* defined _MSC_VER */ + +#elif defined(_CC_BORLAND_) + +/* Turbo C not like pragmae */ +#if !defined (_CC_TCC_) + +/* Eliminate duplicate strings. + This can save a bit of space in large + programs particularly, since each call to + MemCheck references an otherwise separate + copy of the current filename. +*/ +#pragma option -d + +/* Intrinsics (== in-line functions) not permissible + since they're implemented as macros, for one... +*/ +#pragma intrinsic -strcat +#pragma intrinsic -strncat +#pragma intrinsic -strcpy +#pragma intrinsic -strncpy +#pragma intrinsic -stpcpy +#pragma intrinsic -strset +#pragma intrinsic -strnset +#pragma intrinsic -memcpy +#pragma intrinsic -memset + +#endif /* not Turbo C */ + +/* end Borland compiler intrinsics */ + +#elif defined (_CC_WATCOM_) + +/* NOTE: unfortunately, WATCOM C/C++ compilers + force inlining of the strcpy() function regardless + of whether you want it inlined or not, all the time. + So this pragma, while it should ensure that + strcpy() is a function call, does not... :{ + + So we take other measures below: see _mcwatcom_strcpy() +*/ +#pragma function(strcpy) + +#pragma function(strcat) +#pragma function(memcpy) +#pragma function(memset) + +#pragma function(_fmemcpy) +#pragma function(_fmemset) +#pragma function(_fstrcat) +#pragma function(_fstrcpy) + +#endif + +/* End disable function inlining */ + + +/*lint -save -e652 Define of symbol declared previously */ +#if defined (MC_NO_INTERCEPT) +#define NO_INTERCEPT +#endif + +#if !defined (NO_INTERCEPT) + +/* *** ANSI Standard C *** */ + +#define calloc(n,_sz) _INTERCEPT(calloc(n,_sz)) +#define malloc(_sz) _INTERCEPT(malloc(_sz)) +#define realloc(p,s) _INTERCEPT(realloc(p,s)) +#define free(p) _INTERCEPT(free(p)) + +#define memcpy(d,s,n) _INTERCEPTX(memcpy(d,s,n),d) +#define memmove(d,s,n) _INTERCEPTX(memmove(d,s,n),d) +#define memset(p,c,n) _INTERCEPTX(memset(p,c,n),p) +#define strcat(s1,s2) _INTERCEPTX(strcat(s1,s2),s1) +#if defined(_CC_WATCOM_) + /* WATCOM forces inlining of strcpy()... see note above */ +# define strcpy(d,s) _INTERCEPTX(_mcwatcom_strcpy(d,s),d) + extern char * _RTL _mcwatcom_strcpy (char *, const char *); +#else +# define strcpy(d,s) _INTERCEPTX(strcpy(d,s),d) +#endif +#define strncat(s1,s2,n) _INTERCEPTX(strncat(s1,s2,n),s1) +#define strncpy(d,s,n) _INTERCEPTX(strncpy(d,s,n),d) +#define vsprintf(s,f,a) _INTERCEPTX(vsprintf(s,f,a),s) + +/* #define sprintf _VA_INTERCEPT(sprintf) */ +#ifndef _lint +#define sprintf _INTERCEPT(sprintf) +#endif + +#if defined(_CC_MSC_COMPATIBLE_) /* *** Microsoft C *** */ + +#define _expand(_p,_s) _INTERCEPT(_expand(_p,_s)) +#define _fcalloc(n,_sz) _INTERCEPT(_fcalloc(n,_sz)) +#define _fexpand(_p,_s) _INTERCEPT(_fexpand(_p,_s)) +#define _ffree(p) _INTERCEPT(_ffree(p)) +#define _fmalloc(_sz) _INTERCEPT(_fmalloc(_sz)) +#define _frealloc(p,s) _INTERCEPT(_frealloc(p,s)) +#define _fmsize(p) _INTERCEPT(_fmsize(p)) +#define _msize(p) _INTERCEPT(_msize(p)) +#define _nfree(p) _INTERCEPT(_nfree(p)) +#define _nmalloc(_sz) _INTERCEPT(_nmalloc(_sz)) +#define _nrealloc(p,s) _INTERCEPT(_nrealloc(p,s)) +#define _ncalloc(n,_sz) _INTERCEPT(_ncalloc(n,_sz)) +#define _nexpand(_p,_s) _INTERCEPT(_nexpand(_p,_s)) +#define _nmsize(p) _INTERCEPT(_nmsize(p)) +#define _nstrdup(s) _INTERCEPT(_nstrdup(s)) +/* #define halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define _halloc(n,_sz) _INTERCEPT(halloc(n,_sz)) */ +/* #define hfree(p) _INTERCEPT(hfree(p)) */ +/* #define _hfree(p) _INTERCEPT(hfree(p)) */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define _cgets(s) _INTERCEPTX(_cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#define _memccpy(d,s,c,n) _INTERCEPTX(_memccpy(d,s,c,n),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define _strdup(s) _INTERCEPT(_strdup(s)) +#define _strnset(s,c,n) _INTERCEPTX(_strnset(s,c,n),s) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define _strset(s,c) _INTERCEPTX(_strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define _swab(s,d,n) _INTERCEPTX(_swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) +#define _tempnam(d,pfx) _INTERCEPT(_tempnam(d,pfx)) + +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) +#define _fullpath(b,p,n) _INTERCEPTX(_fullpath(b,p,n),b) + +/* ----- END Microsoft C/C++ interceptions ----- */ + +#elif defined (_CC_BORLAND_) /* *** Borland C/C++ *** */ + +#ifndef _CC_POWERPACK32_ +#define farfree(p) _INTERCEPT(farfree(p)) +#define farmalloc(s) _INTERCEPT(farmalloc(s)) +#define farcalloc(n,s) _INTERCEPT(farcalloc(n,s)) +#define farrealloc(p,s) _INTERCEPT(farrealloc(p,s)) +#endif /* not _CC_POWERPACK32_ */ + +#define cgets(s) _INTERCEPTX(cgets(s),s) +#define memccpy(d,s,c,n) _INTERCEPTX(memccpy(d,s,c,n),d) +#if !defined(movmem) +#define movmem(s,d,l) _INTERCEPTX(movmem(s,d,l),d) +#endif +#if !defined(setmem) +#define setmem(d,c,v) _INTERCEPTX(setmem(d,c,v),d) +#endif +#define setvect(i,v) _INTERCEPT(setvect(i,v)) +#define stpcpy(d,s) _INTERCEPTX(stpcpy(d,s),d) +#define _stpcpy(d,s) _INTERCEPTX(_stpcpy(d,s),d) +#define strdup(s) _INTERCEPT(strdup(s)) +#define strnset(s,c,n) _INTERCEPTX(strnset(s,c,n),s) +#define strset(s,c) _INTERCEPTX(strset(s,c),s) +#define swab(s,d,n) _INTERCEPTX(swab(s,d,n),d) +#define tempnam(d,pfx) _INTERCEPT(tempnam(d,pfx)) + +#define getcwd(d,n) _INTERCEPTX(getcwd(d,n),d) +#define _getdcwd(r,d,n) _INTERCEPTX(_getdcwd(r,d,n),d) +#define _dos_setvect(_i,_h) _INTERCEPT(_dos_setvect(_i,_h)) + +#ifndef _CC_POWERPACK32_ +#define _fmemcpy(d,s,n) _INTERCEPTX(_fmemcpy(d,s,n),d) +#define _fmemmove(d,s,n) _INTERCEPTX(_fmemmove(d,s,n),d) +#define _fmemset(d,c,n) _INTERCEPTX(_fmemset(d,c,n),d) +#define _fmemccpy(d,s,c,n) _INTERCEPTX(_fmemccpy(d,s,c,n),d) +#define _fmovmem(s,d,l) _INTERCEPTX(_fmovmem(s,d,l),s) +#define _fsetmem(d,c,v) _INTERCEPTX(_fsetmem(d,c,v),d) +#define _fstrcat(s1,s2) _INTERCEPTX(_fstrcat(s1,s2),s1) +#define _fstrcpy(d,s) _INTERCEPTX(_fstrcpy(d,s),d) +#define _fstrncat(s1,s2,n) _INTERCEPTX(_fstrncat(s1,s2,n),s1) +#define _fstrncpy(d,s,n) _INTERCEPTX(_fstrncpy(d,s,n),d) +#define _fstrdup(s) _INTERCEPT(_fstrdup(s)) +#define _fstrnset(d,c,n) _INTERCEPTX(_fstrnset(d,c,n),d) +#define _fstrset(d,c) _INTERCEPTX(_fstrset(d,c),d) +#endif /* not _CC_POWERPACK32_ */ + +/* +#define freemem(g) _INTERCEPT(freemem(g)) +#define vsscanf(d,f,a) _INTERCEPTX(vsscanf(d,f,a),d) +*/ + +/* ----- END Borland C/C++ interceptions ----- */ + +#else + +#error Unknown compiler in MemCheck.h + +#endif /* Compiler-specific Function Mapping Section */ + +/* Location Transmitters + + You can add any non-intercepted functions to + this bunch... Just updates MemCheck's file and line + information via mc_set_location(), which is thousands + of times faster than anything that does I/O. + The only time this section could be a problem is + if the header file is included before any other header + files which prototype these routines. + + Borland's TD (Turbo Debugger) also has problems here (see note). +*/ +#ifndef _lint /* LINT not like */ + +/* Borland's Turbo Debugger gets confoosed and executes + a `Run' instead of a `Step' when _SETLOC macro is used... +*/ +#if !defined (_CC_BORLAND_) +#if 1 /* Change this to '0' to omit this section */ + +#define printf _SETLOC(printf) + +#define fopen _SETLOC(fopen) +#define fprintf _SETLOC(fprintf) +#define fread _SETLOC(fread) +#define fwrite _SETLOC(fwrite) +#define fclose _SETLOC(fclose) + +#define system _SETLOC(system) +#define exec _SETLOC(exec) +#define spawnl _SETLOC(spawnl) +#define spawnlp _SETLOC(spawnlp) +#define spawnle _SETLOC(spawnle) +#define spawnlpe _SETLOC(spawnlpe) +#define spawnv _SETLOC(spawnv) +#define spawnvp _SETLOC(spawnvp) +#define spawnve _SETLOC(spawnve) +#define spawnvpe _SETLOC(spawnvpe) + +#endif /* end location transmission section */ +#endif /* not Borland C++ */ +#endif /* not def _lint */ + + +/* **** THIRD-PARTY MAPPINGS **** */ + +/* Vermont Views V3.xx + + The following code will transmit the exact file + and line of any mem_get() and mem_free() calls to + MemCheck, so that it can report on the location where + these functions are called, instead of the location of + the calloc() or free(). + + If you've used MCCONFIG to configure the Vermont Views source + code, you *must* either NOT include the MemCheck header file + in the MEM_GET.C and MEM_FREE.C modules, or, if you do, then + #define NO_INTERCEPT beforehand, e.g. + + Module MEM_GET.C ... + : + #define NO_INTERCEPT + #include + : + + MCCONFIG may be used to configure even the shrouded + Vermont Views source code. + + See also: TechNote "Using MemCheck 3.0 Professional + With Vermont Views", available on the StratosWare + BBS (313) 996-2993 as VIEWS.TXT, or by fax. +*/ +#if defined (VV_SYS) /* should do the trick */ +# define mem_get(s) _INTERCEPT(mem_get(s)) +# define mem_free(p) _INTERCEPT(mem_free(p)) +#endif + + +/* **** APPLICATION-SPECIFIC MAPPINGS **** */ + +/* + If your application uses allocation "cover" routines, + MemCheck will by default report errors and leaks by + the file and line of the malloc or free within the + cover module. To get MemCheck to report by file and + line where the cover function is actually called, follow + the instructtions in MemCheck TechNote "Transmitting File + and Line to MemCheck 3.0 Professional Through Cover Functions." + + This is where you can place the cover function macros. +*/ + + +/* end APPLICATION-SPECIFIC MAPPINGS */ + +#endif /* not NO_INTERCEPT */ + +/* Calls that xmit source file, line number if called in source */ +/* *** MemCheck API file & line transmittal *** */ +#define mc_startcheck(erf) mc_startcheck(_MCSF_,_MCSL_,erf) +#define mc_stack_trace(_memo) _INTERCEPT(mc_stack_trace(_memo)) +#define mc_debug(s) _INTERCEPT(mc_debug(s)) +#define mc_debugf(arglist) mc_debugv arglist +#define mc_debugv _mcsl(_MCSF_,_MCSL_),mc_debugv +#define mc_endcheck() _INTERCEPT(mc_endcheck()) +#define mc_check_buffers() _INTERCEPT(mc_check_buffers()) +#define mc_check(p) _INTERCEPT(mc_check(p)) +#define mc_register(p,s) _INTERCEPT(mc_register(p,s)) +#define mc_unregister(p) _INTERCEPT(mc_unregister(p)) +#define mc_nullcheck() _INTERCEPT(mc_nullcheck()) +#define mc_report(f) _INTERCEPT(mc_report(f)) + +/*lint -restore 652 Define of symbol declared prev */ + +#endif /* not MEMCHECK_MODULE, function interceptions */ + + +/* End "C" call wrapper */ +#ifdef __cplusplus +} + + +/* C++ MemCheck Class + + This class can be used as an alternative to + AutoInit, or to placing the mc_startcheck() and + mc_endcheck() calls in your main() program. + Just declaring an object of class 'MemCheck' + will start MemCheck up; usually you will place + this 'above' any other global or statically declared + C++ objects in your main module. + + Here are some examples of starting MemCheck up + via object mechanics: + + MemCheck On; + MemCheck Active; + MemCheck Rules; + + Use your imagination! Note that if AutoInit is ON, + any calls to mc_startcheck() and mc_endcheck() are + ignored. +*/ +#if !defined (NO_INTERCEPT) /* must not have this def'd */ + +/* This class def causes a warning under MSC if not used */ +#if !defined (_CC_MSC_) + +class MemCheck { +public: + MemCheck () { mc_startcheck (NULL); } + ~MemCheck () { mc_endcheck (); } +}; + +#endif + +#endif /* NO_INTERCEPT */ + + +/* *** For use in new and delete modules only *** */ +/* + Replace 'mallocs' with 'cpp_mallocs', etc. + In new and delete modules, NO_INTERCEPT should be #defined, e.g. + + #define NO_INTERCEPT + #include + : + void * operator new ( size_t size ) + { + if (!size) size = 1; + return (cpp_malloc (size)); + } + etc. +*/ +#define cpp_malloc(_s) (_mc_set_newflag(), malloc(_s)) +#define cpp_calloc(_n,_s) (_mc_set_newflag(), calloc(_n,_s)) +#define cpp_free(_p) (_mc_set_delflag(), free(_p)) + +/* Borland C++ */ +#define cpp_farmalloc(_s) (_mc_set_newflag(), farmalloc(_s)) +#define cpp_farfree(_fp) (_mc_set_delflag(), farfree(_fp)) + +/* Microsoft C++-compatibles */ +#define cpp__fmalloc(_s) (_mc_set_newflag(), _fmalloc(_s)) +#define cpp__ffree(_fp) (_mc_set_delflag(), _ffree(_fp)) + + +/* C++ */ +#if !defined (NO_INTERCEPT) +#if !defined (NO_CPP) + +/* + This method is off by default, because it + requires definition of a new operator like: + + void * new (size_t size, char *file, int lineno); + + Such a new operator is included in your SOURCE\CPP + directory. To have this method used for all modules, + #define NEW_OVERLOADED at the top of this header file + or in your project #include file, BEFORE the MemCheck + header file is #included. + + The substitutions for the new operator + may not work in all situations. To disable + MemCheck's interception of new on a module-by- + module basis by undefining NEW_OVERLOADED. +*/ +#if defined (NEW_OVERLOADED) + +/* Method 1: Placement Operators + + Use placement operators to trap file and line location transparently + on calls to new. + + Thanks for this tip to Dan Saks, + C and C++ writer, author, teacher, and columnist--buy his books. + He came through when no one else had a clue! + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with an overloaded new placement operator. +*/ + +/* Declare overloaded new with placement operators */ +void *operator new (size_t _sz, char *file, int lineno); +#if defined (_CPP_ANSI20_) +/* Array version; only under supporting compilers + COMMENT LINE OUT if it causes a compile error. +*/ +void *operator new[] (size_t _sz, char *file, int lineno); +#endif + +#if !defined (_CC_MSC_) +#define new new((char *)__FILE__,(int)__LINE__) +#else +#define new new(__FILE__,__LINE__) +#endif + +/* NOTE: + This placement operator interception syntax has been + known to cause syntax errors (in VC++ 1.0) for no apparent reason + on statements like + + Domain *d = new Domain (); + + Workaround is to change line (sorry!) to equivalent + + Domain *d = new Domain; +*/ + +/* Backwards compatibility with the V2.1 C++ macros */ +#ifndef NEW +#define NEW(_object) new _object +#endif +#ifdef DELETE +#define DELETE(_object) delete _object +#endif +#define DELETE_ARR(_arr) delete[] _arr + +#else /* !NEW_OVERLOADED - end of Placement Operator intercept */ + +/* New and Delete Interception, Method 2: NEW() and DELETE() + + The NEW() and DELETE() macros may be used to transmit file + and line of new and delete. These macros, which require + modification of source code, i.e. "NEW(object)" for "new object", + should probably be used only if the above overloaded new does + not work for your code base. + + Please consult your manual, MemCheck technotes, + or StratosWare Technical Support (1-800-WE-DEBUG) + for details on how to configure your project for + use with NEW() and DELETE(). + + If calling, please have your MemCheck serial number handy. +*/ +#ifndef NEW +#define NEW(_object) (_mcsl_new(_MCSF_,_MCSL_), new _object) +#endif +#ifndef DELETE /* WINNT.H under BC DPMI32 defines DELETE */ +#define DELETE(_object) (_mcsl_delete(_MCSF_,_MCSL_), delete _object) +#endif +#define DELETE_ARR(_arr) (_mcsl_delete(_MCSF_,_MCSL_), delete[] _arr) + +#endif /* !NEW_OVERLOADED */ + +#define delete _mcsl_delete(_MCSF_,_MCSL_), delete + + +/* *** FAILURES *** */ + +/* These macros failed in the purpose of + intercepting new transparently in some + situation or other. +*/ + +/* Failed on " * new expr " (TV) */ +/* #define new (mc_set_location(),0) ? NULL : new */ + +/* Failed on " x = new Object " (TV) */ +/* #define new ((mc_set_location(),0) ? NULL : new) */ +/* #define new new (mc_set_location(),0) ? NULL : */ + +#endif /* !NO_CPP */ +#endif /* NO_INTERCEPT */ +#endif /* cplusplus */ +/******** End C++ ************/ + + +#endif /* End of Section for MEMCHECK Defined */ + +/* -------------------------------------------------------------------------- */ + +#endif /* _MEMCHECK_H_ */ + + +/******************************** + * End of MemCheck 3.0 Header * + ********************************/ + diff --git a/code/redalert/menus.cpp b/code/redalert/menus.cpp new file mode 100644 index 0000000..7b534d0 --- /dev/null +++ b/code/redalert/menus.cpp @@ -0,0 +1,808 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MENUS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MENUS.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : Oct. 24, 1996 Victor Grippi * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Main_Menu -- Menu processing * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "function.h" +#ifdef WIN32 +#include "ccdde.h" +#else //WIN32 +#include +#endif + +#include + +// this makes buttons larger on a larger screen +// necessary?? + +//#undef RESFACTOR +//#define RESFACTOR (2 + (ScreenWidth / 640)) + +/***************************** +** Function prototypes +******************************/ + +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2); +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index); +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc); + +int UnknownKey; + +PRIVATE int MenuUpdate=1; +PRIVATE int MenuSkip; + +#ifdef FIXIT_VERSION_3 +#include "WolStrng.h" +#endif + +/*=========================================================================*/ +/* SELECT_TO_ENTRY: */ +/* */ +/* This routine converts a selection to the correct string entry. It */ +/* does this by search through a long bitfield starting at position index */ +/* until it finds the correct conversion to entries. */ +/* */ +/* INPUTS: int selection from menu, long the bit field to search, int */ +/* the starting index within the bit field. */ +/* RETURNS: int the index into the table of entries */ +/*=========================================================================*/ +PRIVATE int Select_To_Entry(int select, unsigned long bitfield, int index) +{ + int placement; + + if (bitfield==0xFFFFFFFFL) /* if all bits are set */ + return(select); /* then it as is */ + + placement=0; /* current pos zero */ + while (select) { /* while still ones */ + if (bitfield & (1L<<(placement+index))) /* if this flagged then */ + select--; /* decrement counter */ + placement++; /* and we moved a place */ + } + while (!(bitfield & (1L<<(placement+index)))) { + placement++; + } + + return(placement); /* return the position */ +} + + +/*=========================================================================*/ +/* FLASH_LINE: */ +/* */ +/* This routine will flash the line at the desired location for the */ +/* menu routine. It is way cool awesome! */ +/* */ +/* INPUTS: char *text, int x position on line, int y position, char */ +/* normal foreground color, char hilight foreground color, char */ +/* background color */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE void Flash_Line(char const *text, int xpix, int ypix, unsigned nfgc, unsigned hfgc, unsigned bgc) +{ + int loop; + + for (loop=0;loop<3;loop++) { + Hide_Mouse(); + Plain_Text_Print(text, xpix, ypix, hfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Delay(2); + Plain_Text_Print(text, xpix, ypix, nfgc, bgc, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); + Delay(2); + } +} + +/*=========================================================================*/ +/* COORDINATES_IN_REGION: */ +/* */ +/* Test to see if a given pair of coordinates are within the given */ +/* rectangular region. */ +/* */ +/* INPUTS: int x to be tested, int y to be tested, int left x pos, */ +/* int top y pos, int right x pos, int bottom y pos */ +/* RETURNS: none */ +/*=========================================================================*/ +PRIVATE int Coordinates_In_Region(int x, int y, int inx1, int iny1, int inx2, int iny2) +{ + return((x>=inx1)&&(x<=inx2)&&(y>=iny1)&&(y<=iny2)); +} + +#ifdef NEVER +/*=========================================================================*/ +/* FIND_MENU_ITEMS: */ +/* */ +/* This routine finds the real total items in a menu when certain items */ +/* may be disabled by bit fields and the like. This is done by looping */ +/* through the fields, starting at the position passed in index and */ +/* counting the number of bits that are set. */ +/* */ +/* INPUTS: int the maximum number of items possible on the menu, long */ +/* the bit field of enabled and disabled items, char the index */ +/* point to start at within the list. */ +/* RETURNS: int the total number of items in the menu */ +/*=========================================================================*/ + int Find_Menu_Items(int maxitems, unsigned long field, char index) + { + int loop,ctr; + + if (field==0xFFFFFFFFL) /* if all bits are set */ + return(maxitems); /* then maxitems set */ + + for (loop=ctr=0;loopClear(); +} + + +/*=========================================================================*/ +/* CHECK_MENU: */ +/* */ +/* */ +/* */ +/* INPUTS: */ +/* RETURNS: */ +/*=========================================================================*/ +int Check_Menu(int menu, char const * text[], char *, long field, int index, KeyNumType _key) +{ + int maxitem,select,key,menuy,menux; + int mx1,mx2,my1,my2,tempy; + int drawy,menuskip,halfskip; + int normcol,litcol,item,newitem,idx; + int * menuptr; + + //selection++; /* get rid of warning */ + + menuptr = &MenuList[menu][0]; /* get pointer to menu */ + maxitem = menuptr[ITEMSHIGH]-1; /* find max items */ + newitem = item = menuptr[MSELECTED]%(maxitem+1); /* find selected */ + select = -1; /* no selection made */ + menuskip = FontHeight+MenuSkip; /* calc new font height */ + halfskip = MenuSkip>>1; /* adjustment for menus */ + + menuy = WinY+menuptr[MENUY]; /* get the absolute */ + menux = (WinX+menuptr[MENUX]); /* coords of menu */ + normcol = menuptr[NORMCOL]; + litcol = menuptr[HILITE]; + + /* + ** Fetch a pending keystroke from the buffer if there is a keystroke + ** present. If no keystroke is pending then simple mouse tracking will + ** be done. + */ + if (_key == KN_NONE) + { + key = 0; + UnknownKey = 0; + if (Keyboard->Check()) { + key = (Keyboard->Get() & ~(WWKEY_SHIFT_BIT | WWKEY_ALT_BIT | WWKEY_CTRL_BIT)); /* mask off all but release bit */ + } + } + else { + key = _key; + } + + /* + ** if we are using the mouse and it is installed, then find the mouse + ** coordinates of the menu and if we are not somewhere on the menu get + ** the heck outta here. If we are somewhere on the menu, then figure + ** out the new selected item, and continue forward. + */ + mx1=(WinX)+(menuptr[MENUX]*FontWidth); /* get menu coords */ + my1=(WinY)+(menuptr[MENUY])-halfskip; /* from the menu */ + mx2=mx1+(menuptr[ITEMWIDTH]*FontWidth)-1; /* structure as */ + my2=my1+(menuptr[ITEMSHIGH]*menuskip)-1; /* necessary */ + + tempy=UserInput.Mouse.Y; + if (Coordinates_In_Region(UserInput.Mouse.X, tempy, mx1, my1, mx2, my2)&& MenuUpdate) { + newitem=(tempy-my1)/menuskip; + } + + switch (key) { + + case KN_UP: /* if the key moves up */ + newitem--; /* new item up one */ + if (newitem<0) /* if invalid new item */ + newitem=maxitem; /* put at list bottom */ + break; + case KN_DOWN: /* if key moves down */ + newitem++; /* new item down one */ + if (newitem>maxitem) /* if new item past */ + newitem=0; /* list end, clear */ + break; + case KN_HOME: /* if top of list key */ + case KN_PGUP: /* is selected then */ + newitem=0; /* new item = top */ + break; + case KN_END: /* if bottom of list is */ + case KN_PGDN: /* selected then */ + newitem=maxitem; /* new item = bottom */ + break; + + /* + ** Handle mouse button press. Set selection and then fall into the + ** normal menu item select logic. + */ + case KN_RMOUSE: + case KN_LMOUSE: + if (Coordinates_In_Region(UserInput.Mouse.X, UserInput.Mouse.Y, mx1, my1, mx2, my2)) { + newitem = (UserInput.Mouse.Y - my1) / menuskip; + } else { + UnknownKey = key; // Pass the unprocessed button click back. + break; + } + + /* + ** Normal menu item select logic. Will flash line and exit with menu + ** selection number. + */ + case KN_RETURN: /* if a selection is */ + case KN_SPACE: /* made with key */ + case KN_CENTER: + select=newitem; /* flag it made. */ + break; + + case 0: + break; + + /* + ** When no key was pressed or an unknown key was pressed, set the + ** global record of the key and exit normally. + ** EXCEPTION: If the key matches the first letter of any of the + ** menu entries, then presume it as a selection of + ** that entry. + */ + default: + for (idx = 0; idx < menuptr[ITEMSHIGH]; idx++) { + if (toupper(*(text[Select_To_Entry(idx, field, index)])) == toupper(Keyboard->To_ASCII((KeyNumType)(key&0x0FF)))) { + newitem = select = idx; + break; + } + } + UnknownKey = key; + break; + } + + if (newitem!=item) { + Hide_Mouse(); + idx=Select_To_Entry(item, field, index); + drawy=menuy+(item*menuskip); + Plain_Text_Print(text[idx], menux, drawy, normcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + idx=Select_To_Entry(newitem, field, index); + drawy=menuy+(newitem*menuskip); + Plain_Text_Print(text[idx], menux, drawy, litcol, TBLACK, TPF_8POINT|TPF_DROPSHADOW); + Show_Mouse(); /* resurrect the mouse */ + } + + if (select!=-1) { + idx=Select_To_Entry(select, field, index); + Hide_Mouse(); /* get rid of the mouse */ + drawy=menuy+(newitem*menuskip); + Flash_Line(text[idx], menux, drawy, normcol, litcol, TBLACK); + Show_Mouse(); + select=idx; + } + + menuptr[MSELECTED]=newitem; /* update menu select */ + + return(select); +} + + +/*************************************************************************** + * Do_Menu -- Generic menu processor. * + * * + * This helper function displays a menu of specified entries and waits * + * for the player to make a selection. If a selection is made, then * + * a whole number (starting at 0) is returned matching the entry * + * selected. If ESC is pressed, then -1 is returned. * + * * + * INPUT: strings -- A pointer to an array of pointers to text strings. * + * Each entry in the list will be a menu entry that * + * can be selected. * + * * + * blue -- Should the special blue color be used to display * + * the menu? * + * * + * OUTPUT: Returns with the cardinal number of the selected menu entry. * + * If ESC was pressed, then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=========================================================================*/ +int Do_Menu(char const ** strings, bool ) +{ + int count; // Number of entries in this menu. + int length; // The width of the menu (in pixels). + char const ** ptr; // Working menu text pointer. + int selection; // Selection from user. + + if (!strings) return(-1); + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + + /* + ** Determine the number of entries in this string. + */ + ptr = strings; + count = 0; + while (*ptr++) { + count++; + } + MenuList[0][ITEMSHIGH] = count; + + /* + ** Determine the width of the menu by finding the length of the + ** longest menu entry. + */ + Plain_Text_Print(TXT_NONE, 0, 0, 0, 0, TPF_8POINT|TPF_DROPSHADOW); + length = 0; + ptr = strings; + while (*ptr) { + length = max(length, (int)String_Pixel_Width(*ptr)); + ptr++; + } + length += 7; + MenuList[0][ITEMWIDTH] = length >> 3; + + /* + ** Adjust the window values to match the size of the + ** specified menu. + */ + WindowList[WINDOW_MENU][WINDOWWIDTH] = (MenuList[0][ITEMWIDTH] + 2) * 8; + WindowList[WINDOW_MENU][WINDOWX] = ScreenWidth - ((19 - (length >> 4)) * 8); + WindowList[WINDOW_MENU][WINDOWY] = 174 - (unsigned)(MenuList[0][ITEMSHIGH] * (FontHeight+FontYSpacing)); + WindowList[WINDOW_MENU][WINDOWHEIGHT] = MenuList[0][ITEMSHIGH] * FontHeight + 5 /*11*/; + + /* + ** Display the menu. + */ + Change_Window((int)WINDOW_MENU); + Show_Mouse(); + Window_Box(WINDOW_MENU, BOXSTYLE_RAISED); + Setup_Menu(0, strings, 0xFFFFL, 0, 0); + + Keyboard->Clear(); + selection = -1; + UnknownKey = 0; + + // AI options main selection loop + while (selection == -1) { + Call_Back(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + + Device_Present(); + + Show_Mouse(true); + + selection = Check_Menu(0, strings, NULL, 0xFFL, 0, (KeyNumType)g_globalKeyFlags); + if ( UnknownKey != 0 || UnknownKey == KN_ESC || UnknownKey==KN_LMOUSE || UnknownKey==KN_RMOUSE) break; + } + + Keyboard->Clear(); + Hide_Mouse(); + + HidPage.Blit(SeenPage); +//WindowList[WINDOW_MAIN][2] = SeenBuff.Get_Width();//BG + Change_Window((int)WINDOW_MAIN); + Map.Flag_To_Redraw(true); + return(selection); +} + + +/*************************************************************************** + * Main_Menu -- Menu processing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * index of item selected, -1 if time out * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/17/1995 BRR : Created. * + *=========================================================================*/ +int Main_Menu(unsigned long ) +{ + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 152 * RESFACTOR; + int d_dialog_h = 100 * RESFACTOR; + + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); + + int d_button_w = 140 * RESFACTOR; + int d_button_h = 11 * RESFACTOR; + int d_button_dy = d_button_h + 4 * RESFACTOR; // spacing between top of two buttons + int d_button_x = d_dialog_x + ( ( d_dialog_w - d_button_w ) / 2 ); + int d_button_y = d_dialog_y + (24 * RESFACTOR); + + static int max_buttons = 7; + + /* + ** Button enumerations: + */ + // Enums in Select_Game() must match order of buttons in Main_Menu(). + enum { + BUTTON_EXPAND=100, // (CS) + BUTTON_EXPAND_AM, + BUTTON_START, + BUTTON_LOAD, + BUTTON_MULTI, + BUTTON_INTRO, + BUTTON_EXIT, + }; + + /* + ** Dialog variables: + */ + bool bExpansionCS = Expansion_CS_Present(); + bool bExpansionAM = Expansion_AM_Present(); + + KeyNumType input; // input from user + int retval; // return value + int curbutton; + TextButtonClass * buttons[7]; + unsigned long starttime; + + /* + ** Buttons + */ + ControlClass * commands = NULL; // the button list + + TextButtonClass expandbtnCS( BUTTON_EXPAND, TXT_WOL_CS_MISSIONS, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h ); + + if( bExpansionCS ) + d_button_y += d_button_dy; + TextButtonClass expandbtnAM( BUTTON_EXPAND_AM, TXT_WOL_AM_MISSIONS, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + + if( bExpansionAM ) + d_button_y += d_button_dy; + + TextButtonClass startbtn(BUTTON_START, TXT_START_NEW_GAME, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + d_button_y += d_button_dy; + + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_MISSION, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + d_button_y += d_button_dy; + + TextButtonClass multibtn(BUTTON_MULTI, TXT_MULTIPLAYER_GAME, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + d_button_y += d_button_dy; + + TextButtonClass introbtn(BUTTON_INTRO, TXT_INTRO, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + d_button_y += d_button_dy; + + TextButtonClass exitbtn(BUTTON_EXIT, TXT_EXIT_GAME, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + d_button_y += d_button_dy; + + int version_x = d_button_x + d_button_w; + int version_y = d_button_y + ( 5 * RESFACTOR ); + + /* + ** Initialize + */ + if (RequiredCD != -2) { + RequiredCD = -1; + Force_CD_Available(RequiredCD); + } + Set_Logic_Page(SeenBuff); + Keyboard->Clear(); + starttime = TickCount; + + /* + ** Create the list + */ + commands = &startbtn; + + if( bExpansionCS ) + expandbtnCS.Add_Tail(*commands); + if( bExpansionAM ) + expandbtnAM.Add_Tail(*commands); + + loadbtn.Add_Tail(*commands); + multibtn.Add_Tail(*commands); + introbtn.Add_Tail(*commands); + exitbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ + curbutton = bExpansionCS ? 0 : ( bExpansionAM ? 1 : 2 ); + + buttons[0] = &expandbtnCS; + buttons[1] = &expandbtnAM; + buttons[2] = &startbtn; + buttons[3] = &loadbtn; + buttons[4] = &multibtn; + buttons[5] = &introbtn; + buttons[6] = &exitbtn; + + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + fixed oldvolume = Options.ScoreVolume; + if (oldvolume == 0) { + Options.Set_Score_Volume(fixed(4, 10), false); + } + Theme.Play_Song(THEME_INTRO); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + while (process) { + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Load the background picture. + */ + Load_Title_Page(); + CCPalette.Set(); + + /* + ** Display the title and text overlay for the menu. + */ + Set_Logic_Page(HidPage); + commands->Draw_All(); + + Fancy_Text_Print("V%s", + version_x, + version_y, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_EFNT|TPF_NOSHADOW|TPF_RIGHT, + Version_Name()); + + /* + ** Copy the menu to the visible page. + */ + Hide_Mouse(); + HidPage.Blit(SeenPage); + Show_Mouse(); + + Set_Logic_Page(SeenBuff); + display = false; + + /* + ** Get and process player input. + */ + input = (KeyNumType)0; + input = commands->Input(); + + /* + ** If there is input, then take this opportunity to seed some bits + ** to the cryptographic random number generator. + */ + if (input != 0) { + SYSTEMTIME t; + GetSystemTime(&t); + CryptRandom.Seed_Byte(t.wMilliseconds); + } + + /* + ** Dispatch the input to be processed. + */ + switch (input) { + case (BUTTON_EXPAND | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_EXPAND_AM | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_START | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_LOAD | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_MULTI | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_INTRO | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case (BUTTON_EXIT | KN_BUTTON): + retval = (input & 0x7FFF) - BUTTON_EXPAND; + process = false; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + + switch( curbutton ) + { + case -1: + curbutton = max_buttons - 1; + break; + case 0: + if( !bExpansionCS ) + curbutton = max_buttons - 1; + break; + case 1: + if( !bExpansionAM ) + { + if( bExpansionCS ) + curbutton = 0; + else + curbutton = max_buttons - 1; + } + break; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + + if( curbutton == max_buttons ) + { + if( bExpansionCS ) + curbutton = 0; + else if( bExpansionAM ) + curbutton = 1; + else + curbutton = 2; + } + else if (curbutton == 1 && !bExpansionAM) + { + curbutton = 2; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + retval = curbutton; + process = false; + break; + + + + case KN_LMOUSE: + if (Coordinates_In_Region(UserInput.Mouse.X, UserInput.Mouse.Y, + + 9*RESFACTOR, 10*RESFACTOR, + 79*RESFACTOR, 24*RESFACTOR)){ + Show_Who_Was_Responsible(); + display = true; + Theme.Play_Song(THEME_INTRO); + + break; + } + + // secret ANT missions + if (Is_Counterstrike_Installed() == true) + { + if ((Keyboard->Down(KN_LSHIFT) || Keyboard->Down(KN_RSHIFT)) && Coordinates_In_Region(UserInput.Mouse.X, UserInput.Mouse.Y, 260*RESFACTOR, 0, 320*RESFACTOR, 50*RESFACTOR)) { + AntsEnabled = true; + process = false; + retval = 2; // To match SEL_START_NEW_GAME + } + } + + default: + break; + } + + //Device_Present(); + } + + Options.Set_Score_Volume(oldvolume, false); + + return(retval); +} diff --git a/code/redalert/message.h b/code/redalert/message.h new file mode 100644 index 0000000..f498db9 --- /dev/null +++ b/code/redalert/message.h @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#define MESSAGE_NONE 0 // +#define MESSAGE_BUILD_WINDTRAP 1 // You must build a Windtrap +#define MESSAGE_STRUCT_CONCRETE 2 // Concrete: Use concrete to +#define MESSAGE_STRUCT_PALACE 3 // Palace: This is your +#define MESSAGE_STRUCT_LIGHT 4 // Light Factory: The Light +#define MESSAGE_STRUCT_HEAVY 5 // Heavy Factory: The Heavy +#define MESSAGE_STRUCT_HITECH 6 // Hi-Tech Factory: The +#define MESSAGE_STRUCT_IX 7 // House IX: The IX Research +#define MESSAGE_STRUCT_WOR 8 // WOR: Wor is used to train +#define MESSAGE_STRUCT_CONST 9 // Construction Facility: All +#define MESSAGE_STRUCT_WINDTRAP 10 // Windtrap: The windtrap +#define MESSAGE_STRUCT_BARRACKS 11 // Barracks: The Barracks is +#define MESSAGE_STRUCT_STARPORT 12 // Startport: The Starport is +#define MESSAGE_STRUCT_REFINERY 13 // Spice Refinery: The +#define MESSAGE_STRUCT_REPAIR 14 // Repair Facility: The Repair +#define MESSAGE_STRUCT_WALL 15 // Wall: The wall is used for +#define MESSAGE_STRUCT_TURRET 16 // Gun Turret: The cannon +#define MESSAGE_STRUCT_RTURRET 17 // Rocket Turret: The +#define MESSAGE_STRUCT_SILO 18 // Spice Silo: The Spice silo +#define MESSAGE_STRUCT_OUTPOST 19 // Outpost: The Outpost +#define MESSAGE_NEED_CONCRETE 20 // There isn't enough open +#define MESSAGE_SAND 21 // Sand: This is sand terrain. +#define MESSAGE_DUNE 22 // Sand Dunes: These are an +#define MESSAGE_ROCK 23 // Rock: This is rock terrain. +#define MESSAGE_MOUNT 24 // Mountain: Mountains on +#define MESSAGE_SPICE 25 // Spice Field: This is the +#define MESSAGE_ADJACENT 26 // Structures must be placed +#define MESSAGE_SEARCH4SPICE 27 // Search for spice fields to +#define MESSAGE_SANDWORM 28 // Warning: Sandworms diff --git a/code/redalert/miscasm.cpp b/code/redalert/miscasm.cpp new file mode 100644 index 0000000..cd8e639 --- /dev/null +++ b/code/redalert/miscasm.cpp @@ -0,0 +1,1572 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* +** +** Misc. assembly code moved from headers +** +** +** +** +** +*/ + +#include "FUNCTION.H" + + + +extern "C" void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy) +{ + memcpy(dest, source, bytes_to_copy); +} + + +/*********************************************************************************************** + * Distance -- Determines the lepton distance between two coordinates. * + * * + * This routine is used to determine the distance between two coordinates. It uses the * + * Dragon Strike method of distance determination and thus it is very fast. * + * * + * INPUT: coord1 -- First coordinate. * + * * + * coord2 -- Second coordinate. * + * * + * OUTPUT: Returns the lepton distance between the two coordinates. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +int Distance_Coord(COORDINATE coord1, COORDINATE coord2) +{ + __asm { + mov eax,[coord1] + mov ebx,[coord2] + mov dx,ax + sub dx,bx + jg okx + neg dx + okx: + shr eax,16 + shr ebx,16 + sub ax,bx + jg oky + neg ax +oky: + cmp ax,dx + jg ok + xchg ax,dx +ok: + shr dx,1 + add ax,dx + } +} + + + + +/* +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ + +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + static const char _new_facing16[] = { + 3, 2, 4,-1, 1, 2,0,-1, + 13,14,12,-1,15,14,0,-1, + 5, 6, 4,-1, 7, 6,8,-1, + 11,10,12,-1, 9,10,8,-1 + }; + + + __asm { + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[_new_facing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;* Desired_Facing256 -- Desired facing algorithm 0..255 resolution. * +;* * +;* This is a desired facing algorithm that has a resolution of 0 * +;* through 255. * +;* * +;* INPUT: srcx,srcy -- Source coordinate. * +;* * +;* dstx,dsty -- Destination coordinate. * +;* * +;* OUTPUT: Returns with the desired facing to face the destination * +;* coordinate from the position of the source coordinate. North * +;* is 0, East is 64, etc. * +;* * +;* WARNINGS: This routine is slower than the other forms of desired * +;* facing calculation. Use this routine when accuracy is * +;* required. * +;* * +;* HISTORY: * +;* 12/24/1991 JLB : Adapted. * +;*=========================================================================*/ + +int __cdecl Desired_Facing256(LONG srcx, LONG srcy, LONG dstx, LONG dsty) +{ + + __asm { + xor ebx,ebx //; Facing number. + + ////; Determine absolute X delta and left/right direction. + mov ecx,[dstx] + sub ecx,[srcx] + jge short xnotneg + neg ecx + mov ebx,11000000b //; Set bit 7 and 6 for leftward. +xnotneg: + + //; Determine absolute Y delta and top/bottom direction. + mov eax,[srcy] + sub eax,[dsty] + jge short ynotneg + xor ebx,01000000b //; Complement bit 6 for downward. + neg eax +ynotneg: + + //; Set DX=64 for quadrants 0 and 2. + mov edx,ebx + and edx,01000000b + xor edx,01000000b + + //; Determine if the direction is closer to the Y axis and make sure that + //; CX holds the larger of the two deltas. This is in preparation for the + //; divide. + cmp eax,ecx + jb short gotaxis + xchg eax,ecx + xor edx,01000000b //; Closer to Y axis so make DX=64 for quad 0 and 2. +gotaxis: + + //; If closer to the X axis then add 64 for quadrants 0 and 2. If + //; closer to the Y axis then add 64 for quadrants 1 and 3. Determined + //; add value is in DX and save on stack. + push edx + + //; Make sure that the division won't overflow. Reduce precision until + //; the larger number is less than 256 if it appears that an overflow + //; will occur. If the high byte of the divisor is not zero, then this + //; guarantees no overflow, so just abort shift operation. + test eax,0FFFFFF00h + jnz short nooverflow +again: + test ecx,0FFFFFF00h + jz short nooverflow + shr ecx,1 + shr eax,1 + jmp short again +nooverflow: + + //; Make sure that the division won't underflow (divide by zero). If + //; this would occur, then set the quotient to $FF and skip divide. + or ecx,ecx + jnz short nounderflow + mov eax,0FFFFFFFFh + jmp short divcomplete + + //; Derive a pseudo angle number for the octant. The angle is based + //; on $00 = angle matches long axis, $00 = angle matches $FF degrees. +nounderflow: + xor edx,edx + shld edx,eax,8 //; shift high byte of eax into dl + shl eax,8 + div ecx +divcomplete: + + //; Integrate the 5 most significant bits into the angle index. If DX + //; is not zero, then it is 64. This means that the dividend must be negated + //; before it is added into the final angle value. + shr eax,3 + pop edx + or edx,edx + je short noneg + dec edx + neg eax +noneg: + add eax,edx + add eax,ebx + and eax,0FFH +// ret + } +} + + + + + + + + + + + + +/* + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING8.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing8 -- Determines facing to reach a position. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing8 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 8 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 3 = Is y2 < y1? +; bit 2 = Is x2 < x1? +; bit 1 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 0 = Is the facing closer to a major axis? +//NewFacing8 DB 1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4 + +// CODESEG +*/ + +/* +;*************************************************************************** +;* DESIRED_FACING8 -- Determines facing to reach a position. * +;* * +;* This routine will return with the most desirable facing to reach * +;* one position from another. It is accurate to a resolution of 0 to * +;* 7. * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0..255 with an * +;* accuracy of 32 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 07/15/1991 JLB : Documented. * +;* 08/08/1991 JLB : Same position check. * +;* 08/14/1991 JLB : New algorithm * +;* 02/06/1995 BWG : Convert to 32-bit * +;*=========================================================================* +*/ +int __cdecl Desired_Facing8(long x1, long y1, long x2, long y2) +{ + + static const char _new_facing8[] = {1,2,1,0,7,6,7,0,3,2,3,4,5,6,5,4}; + + __asm { + + xor ebx,ebx //; Index byte (built). + + //; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + + cmp ecx,eax + rcl ebx,1 //; Close to major axis bit. + + xor eax,eax + mov al,[_new_facing8+ebx] + + //; Normalize to 0..FF range. + shl eax,5 + +// ret + + } + +} + + + +#if (0) + +/* + ; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : FACING16.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 8, 1991 * +;* * +;* Last Update : February 6, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Desired_Facing16 -- Converts coordinates into a facing number. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Desired_Facing16 :NEAR +; INCLUDE "wwlib.i" + + DATASEG + +; 16 direction desired facing lookup table. Build the index according +; to the following bits: +; +; bit 4 = Is y2 < y1? +; bit 3 = Is x2 < x1? +; bit 2 = Is the ABS(x2-x1) < ABS(y2-y1)? +; bit 1 = Is the lesser absolute difference very close to zero? +; bit 0 = Is the lesser absolute difference very close to the greater dist? +NewFacing16 DB 3, 2, 4,-1, 1, 2,0,-1 + DB 13,14,12,-1,15,14,0,-1 + DB 5, 6, 4,-1, 7, 6,8,-1 + DB 11,10,12,-1, 9,10,8,-1 + + CODESEG + +;*************************************************************************** +;* DESIRED_FACING16 -- Converts coordinates into a facing number. * +;* * +;* This converts coordinates into a desired facing number that ranges * +;* from 0 to 15 (0 equals North and going clockwise). * +;* * +;* INPUT: x1,y1 -- Position of origin point. * +;* * +;* x2,y2 -- Position of target. * +;* * +;* OUTPUT: Returns desired facing as a number from 0 to 255 but * +;* accurate to 22.5 degree increments. * +;* * +;* WARNINGS: If the two coordinates are the same, then -1 will be * +;* returned. It is up to you to handle this case. * +;* * +;* HISTORY: * +;* 08/14/1991 JLB : Created. * +;*=========================================================================* +*/ +long __cdecl Desired_Facing16(long x1, long y1, long x2, long y2) +{ + + __asm { + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx //; DX = Y axis (signed). + jns short absy + inc ebx //; Set the signed bit. + neg edx //; ABS(y) +absy: + + //; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax //; CX = X axis (signed). + jns short absx + inc ebx //; Set the signed bit. + neg ecx //; ABS(x) +absx: + + //; Determine the greater axis. + cmp ecx,edx + jb short dxisbig + xchg ecx,edx +dxisbig: + rcl ebx,1 //; Y > X flag bit. + + //; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax //; Round up. + shr eax,1 + inc eax //; Round up. + shr eax,1 //; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 //; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 //; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + //; Normalize to 0..FF range. + shl eax,4 + +// ret + } +} + + + + +#if (0) + PROC Desired_Facing16 C near + USES ebx, ecx, edx + + ARG x1:DWORD + ARG y1:DWORD + ARG x2:DWORD + ARG y2:DWORD + + xor ebx,ebx ; Index byte (built). + + ; Determine Y axis difference. + mov edx,[y1] + mov ecx,[y2] + sub edx,ecx ; DX = Y axis (signed). + jns short ??absy + inc ebx ; Set the signed bit. + neg edx ; ABS(y) +??absy: + + ; Determine X axis difference. + shl ebx,1 + mov eax,[x1] + mov ecx,[x2] + sub ecx,eax ; CX = X axis (signed). + jns short ??absx + inc ebx ; Set the signed bit. + neg ecx ; ABS(x) +??absx: + + ; Determine the greater axis. + cmp ecx,edx + jb short ??dxisbig + xchg ecx,edx +??dxisbig: + rcl ebx,1 ; Y > X flag bit. + + ; Determine the closeness or farness of lesser axis. + mov eax,edx + inc eax ; Round up. + shr eax,1 + inc eax ; Round up. + shr eax,1 ; 1/4 of greater axis. + + cmp ecx,eax + rcl ebx,1 ; Very close to major axis bit. + + sub edx,eax + cmp edx,ecx + rcl ebx,1 ; Very far from major axis bit. + + xor eax,eax + mov al,[NewFacing16+ebx] + + ; Normalize to 0..FF range. + shl eax,4 + + ret + + ENDP Desired_Facing16 + + END +#endif +#endif + + + + + + + + + + + + + + + + +/* +;*********************************************************************************************** +;* Cardinal_To_Fixed -- Converts cardinal numbers into a fixed point number. * +;* * +;* This utility function will convert cardinal numbers into a fixed point fraction. The * +;* use of fixed point numbers occurs throughout the product -- since it is a convenient * +;* tool. The fixed point number is based on the formula: * +;* * +;* result = cardinal / base * +;* * +;* The accuracy of the fixed point number is limited to 1/65536 as the lowest and up to * +;* 65536 as the largest. * +;* * +;* INPUT: base -- The key number to base the fraction about. * +;* * +;* cardinal -- The other number (hey -- what do you call it?) * +;* * +;* OUTPUT: Returns with the fixed point number of the "cardinal" parameter as it relates * +;* to the "base" parameter. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Cardinal_To_Fixed(unsigned base, unsigned cardinal) +{ + __asm { + + mov eax, 0FFFFFFFFh //; establish default return value + + mov ebx,[base] + or ebx, ebx + jz retneg1 //; if base==0, return 4294967295 + + mov eax,[cardinal] //; otherwise, return (cardinal*65536)/base + shl eax,16 + xor edx,edx + div ebx + +retneg1: + //ret + + + } +} + +#if (0) + PROC Cardinal_To_Fixed C near + USES ebx, edx + + ARG base:DWORD + ARG cardinal:DWORD + + mov eax,0FFFFh ; establish default return value + + mov ebx,[base] + or ebx,ebx + jz near ??retneg1 ; if base==0, return 65535 + + mov eax,[cardinal] ; otherwise, return (cardinal*256)/base + shl eax,8 + xor edx,edx + div ebx + +??retneg1: + ret + + ENDP Cardinal_To_Fixed +#endif + +/* +;*********************************************************************************************** +;* Fixed_To_Cardinal -- Converts a fixed point number into a cardinal number. * +;* * +;* Use this routine to convert a fixed point number into a cardinal number. * +;* * +;* INPUT: base -- The base number that the original fixed point number was created from. * +;* * +;* fixed -- The fixed point number to convert. * +;* * +;* OUTPUT: Returns with the reconverted number. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 02/17/1995 BWG : Created. * +;*=============================================================================================*/ + +unsigned int __cdecl Fixed_To_Cardinal(unsigned base, unsigned fixed) +{ +// PROC Fixed_To_Cardinal C near +// USES edx + +// ARG base:DWORD +// ARG fixed:DWORD + + __asm { + mov eax,[base] + mul [fixed] + add eax,08000h //; eax = (base * fixed) + 0x8000 + + shr eax,16 //; return eax/65536 + //ret + } + + +#if (0) + mov eax,[base] + mul [fixed] + add eax,080h ; eax = (base * fixed) + 0x80 + + test eax,0FF000000h ; if high byte set, return FFFF + jnz ??rneg1 + shr eax,8 ; else, return eax/256 + ret +??rneg1 : + mov eax,0FFFFh ; establish default return value + ret + + ENDP Fixed_To_Cardinal + + END +#endif + + +} + + + + + + + + + + + + + + + +void __cdecl Set_Bit(void * array, int bit, int value) +{ + __asm { + mov ecx, [bit] + mov eax, [value] + mov esi, [array] + mov ebx,ecx + shr ebx,5 + and ecx,01Fh + btr [esi+ebx*4],ecx + or eax,eax + jz ok + bts [esi+ebx*4],ecx +ok: + } +} + + +int __cdecl Get_Bit(void const * array, int bit) +{ + __asm { + mov eax, [bit] + mov esi, [array] + mov ebx,eax + shr ebx,5 + and eax,01Fh + bt [esi+ebx*4],eax + setc al + } +} + +int __cdecl First_True_Bit(void const * array) +{ + __asm { + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + + +int __cdecl First_False_Bit(void const * array) +{ + __asm { + + mov esi, [array] + mov eax,-32 +again: + add eax,32 + mov ebx,[esi] + not ebx + add esi,4 + bsf ebx,ebx + jz again + add eax,ebx + } +} + +int __cdecl Bound(int original, int min, int max) +{ + __asm { + mov eax,[original] + mov ebx,[min] + mov ecx,[max] + cmp ebx,ecx + jl okorder + xchg ebx,ecx +okorder: cmp eax,ebx + jg okmin + mov eax,ebx +okmin: cmp eax,ecx + jl okmax + mov eax,ecx +okmax: + } +} + + + + + + + +/* + +CELL __cdecl Coord_Cell(COORDINATE coord) +{ + __asm { + mov eax, coord + mov ebx,eax + shr eax,010h + xor al,al + shr eax,2 + or al,bh + } + +} + + + +*/ + + + + + +/* +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* +*/ +void __cdecl Shake_Screen(int shakes) +{ + // PG_TO_FIX + // Need a different solution for shaking the screen + shakes; +} + + + +#if (0) +GLOBAL C Shake_Screen :NEAR + + CODESEG + +;*********************************************************** +; SHAKE_SCREEN +; +; VOID Shake_Screen(int shakes); +; +; This routine shakes the screen the number of times indicated. +; +; Bounds Checking: None +; +;* + PROC Shake_Screen C near + USES ecx, edx + + ARG shakes:DWORD + ret + + mov ecx,[shakes] + +;;; push es +;;; mov ax,40h +;;; mov es,ax +;;; mov dx,[es:63h] +;;; pop es + mov eax,[0463h] ; get CRTC I/O port + mov dx,ax + add dl,6 ; video status port + +??top_loop: + +??start_retrace: + in al,dx + test al,8 + jz ??start_retrace + +??end_retrace: + in al,dx + test al,8 + jnz ??end_retrace + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,01 ; top word of start address + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,040h ; bottom word = 40 (140h) + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + +??start_retrace2: + in al,dx + test al,8 + jz ??start_retrace2 + +??end_retrace2: + in al,dx + test al,8 + jnz ??end_retrace2 + +??start_retrace3: + in al,dx + test al,8 + jz ??start_retrace3 + +??end_retrace3: + in al,dx + test al,8 + jnz ??end_retrace3 + + cli + sub dl,6 ; dx = 3B4H or 3D4H + + mov ah,0 + mov al,0Ch + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + dec dx + + mov ah,0 + inc al + out dx,al + xchg ah,al + inc dx + out dx,al + xchg ah,al + + sti + add dl,5 + + loop ??top_loop + + ret + + ENDP Shake_Screen + +;*********************************************************** + + END + +#endif + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* Conquer_Build_Fading_Table -- Builds custom shadow/light fading table. * +;* * +;* This routine is used to build a special fading table for C&C. There * +;* are certain colors that get faded to and cannot be faded again. * +;* With this rule, it is possible to draw a shadow multiple times and * +;* not have it get any lighter or darker. * +;* * +;* INPUT: palette -- Pointer to the 768 byte IBM palette to build from. * +;* * +;* dest -- Pointer to the 256 byte remap table. * +;* * +;* color -- Color index of the color to "fade to". * +;* * +;* frac -- The fraction to fade to the specified color * +;* * +;* OUTPUT: Returns with pointer to the remap table. * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================*/ + +void * __cdecl Conquer_Build_Fading_Table(void const *palette, void *dest, int color, int frac) +{ + /* + global C Conquer_Build_Fading_Table : NEAR + PROC Conquer_Build_Fading_Table C near + USES ebx, ecx, edi, esi + + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + +ALLOWED_COUNT EQU 16 +ALLOWED_START EQU 256-ALLOWED_COUNT + */ + +#define ALLOWED_COUNT 16 +#define ALLOWED_START 256-ALLOWED_COUNT + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini1 + cmp [dest],0 + je fini1 + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl eax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through a limited set of existing colors to find the closest + ; matching color. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,ALLOWED_COUNT + + mov esi,[palette] ; Pointer to original palette. + add esi,(ALLOWED_START)*3 + + ; BH = color index. + mov bh,ALLOWED_START + innerloop: + + xor edx,edx ; Comparison value starts null. + + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + jae short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,ALLOWED_START-1 + jne mainloop + + ; Fill the remainder of the remap table with values + ; that will remap the color to itself. + mov ecx,ALLOWED_COUNT + fillerloop: + inc bl + mov al,bl + stosb + loop fillerloop + + fini1: + mov esi,[dest] + mov eax,esi + + //ret + } +} + + + + + + +extern "C" long __cdecl Reverse_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + xchg al,ah + ror eax,16 + xchg al,ah + } +} + + +extern "C" short __cdecl Reverse_Short(short number) +{ + __asm { + mov ax,[number] + xchg ah,al + } +} + + + +extern "C" long __cdecl Swap_Long(long number) +{ + __asm { + mov eax,dword ptr [number] + ror eax,16 + } +} + + + + + + + + +/* + + + +;*************************************************************************** +;* strtrim -- Remove the trailing white space from a string. * +;* * +;* Use this routine to remove white space characters from the beginning * +;* and end of the string. The string is modified in place by * +;* this routine. * +;* * +;* INPUT: buffer -- Pointer to the string to modify. * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 10/07/1992 JLB : Created. * +;*=========================================================================* +; VOID cdecl strtrim(BYTE *buffer); + global C strtrim :NEAR + PROC strtrim C near + USES ax, edi, esi + + ARG buffer:DWORD ; Pointer to string to modify. +*/ +void __cdecl strtrim(char *buffer) +{ + __asm { + cmp [buffer],0 + je short fini + + ; Prepare for string scanning by loading pointers. + cld + mov esi,[buffer] + mov edi,esi + + ; Strip white space from the start of the string. + looper: + lodsb + cmp al,20h ; Space + je short looper + cmp al,9 ; TAB + je short looper + stosb + + ; Copy the rest of the string. + gruntloop: + lodsb + stosb + or al,al + jnz short gruntloop + dec edi + ; Strip the white space from the end of the string. + looper2: + mov [edi],al + dec edi + mov ah,[edi] + cmp ah,20h + je short looper2 + cmp ah,9 + je short looper2 + + fini: + //ret + } +} + + +/* +;*************************************************************************** +;* Fat_Put_Pixel -- Draws a fat pixel. * +;* * +;* Use this routine to draw a "pixel" that is bigger than 1 pixel * +;* across. This routine is faster than drawing a similar small shape * +;* and faster than calling Fill_Rect. * +;* * +;* INPUT: x,y -- Screen coordinates to draw the pixel's upper * +;* left corner. * +;* * +;* color -- The color to render the pixel in. * +;* * +;* size -- The number of pixels width of the big "pixel". * +;* * +;* page -- The pointer to a GraphicBuffer class or something * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: none * +;* * +;* HISTORY: * +;* 03/17/1994 JLB : Created. * +;*=========================================================================* +; VOID cdecl Fat_Put_Pixel(long x, long y, long color, long size, void *page) + global C Fat_Put_Pixel:NEAR + PROC Fat_Put_Pixel C near + USES eax, ebx, ecx, edx, edi, esi + + ARG x:DWORD ; X coordinate of upper left pixel corner. + ARG y:DWORD ; Y coordinate of upper left pixel corner. + ARG color:DWORD ; Color to use for the "pixel". + ARG siz:DWORD ; Size of "pixel" to plot (square). + ARG gpage:DWORD ; graphic page address to plot onto +*/ + +void __cdecl Fat_Put_Pixel(int x, int y, int color, int siz, GraphicViewPortClass &gpage) +{ + __asm { + + cmp [siz],0 + je short exit_label + + ; Set EDI to point to start of logical page memory. + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[gpage] ; get a pointer to viewport + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + + ; Verify the the Y pixel offset is legal. + mov eax,[y] + cmp eax,[ebx]GraphicViewPortClass.Height ;YPIXEL_MAX + jae short exit_label + mov ecx,[ebx]GraphicViewPortClass.Width + add ecx,[ebx]GraphicViewPortClass.XAdd + add ecx,[ebx]GraphicViewPortClass.Pitch + mul ecx + add edi,eax + + ; Verify the the X pixel offset is legal. + + mov edx,[ebx]GraphicViewPortClass.Width + cmp edx,[x] + mov edx,ecx + jbe short exit_label + add edi,[x] + + ; Write the pixel to the screen. + mov ebx,[siz] ; Copy of pixel size. + sub edx,ebx ; Modulo to reach start of next row. + mov eax,[color] + again: + mov ecx,ebx + rep stosb + add edi,edx ; EDI points to start of next row. + dec [siz] + jnz short again + + exit_label: + //ret + } +} + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : CRC.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : June 12, 1992 * +;* * +;* Last Update : February 10, 1995 [BWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Calculate_CRC :NEAR + + CODESEG +*/ +/* +extern "C" long __cdecl Calculate_CRC(void *buffer, long length) +{ + unsigned long crc; + + unsigned long local_length = (unsigned long) length; + + __asm { + ; Load pointer to data block. + mov [crc],0 + pushad + mov esi,[buffer] + cld + + ; Clear CRC to default (NULL) value. + xor ebx,ebx + + //; Fetch the length of the data block to CRC. + + mov ecx,[local_length] + + jecxz short fini + + ; Prepare the length counters. + mov edx,ecx + and dl,011b + shr ecx,2 + + ; Perform the bulk of the CRC scanning. + jecxz short remainder2 + accumloop: + lodsd + rol ebx,1 + add ebx,eax + loop accumloop + + ; Handle the remainder bytes. + remainder2: + or dl,dl + jz short fini + mov ecx,edx + xor eax,eax + + and ecx,0FFFFh + push ecx + nextbyte: + lodsb + ror eax,8 + loop nextbyte + pop ecx + neg ecx + add ecx,4 + shl ecx,3 + ror eax,cl + + ;nextbyte: + ; shl eax,8 + ; lodsb + ; loop nextbyte + rol ebx,1 + add ebx,eax + + fini: + mov [crc],ebx + popad + mov eax,[crc] + //ret + } +} + + +*/ + + + +extern "C" void __cdecl Set_Palette_Range(void *palette) +{ + if (palette == NULL) { + return; + } + + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); +} \ No newline at end of file diff --git a/code/redalert/mission.cpp b/code/redalert/mission.cpp new file mode 100644 index 0000000..0ea8e18 --- /dev/null +++ b/code/redalert/mission.cpp @@ -0,0 +1,570 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MISSION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : September 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MissionClass::AI -- Processes order script. * + * MissionClass::Assign_Mission -- Give an order to a unit. * + * MissionClass::Commence -- Start script with new order. * + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * MissionClass::Restore_Mission -- Restores overridden mission * + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a te* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * MissionClass::MissionClass -- Default constructor for the mission object type. * + * * + * This is the default constructor for the mission class object. It sets the mission * + * handler into a default -- do nothing -- state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + * 03/01/1996 JLB : Uses initializer lists. * + *=============================================================================================*/ +MissionClass::MissionClass(RTTIType rtti, int id) : + ObjectClass(rtti, id), + Mission(MISSION_NONE), + SuspendedMission(MISSION_NONE), + MissionQueue(MISSION_NONE), + Status(0), + Timer(0) +{ +} + + +/*********************************************************************************************** + * MissionClass::Mission_??? -- Stub mission functions that do nothing. * + * * + * These are the stub routines that handle the mission logic. They do nothing at this * + * level. Derived classes will override these routine as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before calling this mission * + * handler again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +int MissionClass::Mission_Sleep(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Ambush(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Attack(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Capture(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Guard_Area(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Harvest(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Hunt(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Move(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Retreat(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Return(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Stop(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Unload(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Enter(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Construction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Deconstruction(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Repair(void) {return TICKS_PER_SECOND*30;}; +int MissionClass::Mission_Missile(void) {return TICKS_PER_SECOND*30;}; + + +/*********************************************************************************************** + * MissionClass::Set_Mission -- Sets the mission to the specified value. * + * * + * Use this routine to set the current mission for this object. This routine will blast * + * over the current mission, bypassing the queue method. Call it when the mission needs * + * to be changed immediately. * + * * + * INPUT: mission -- The mission to set to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Set_Mission(MissionType mission) +{ + assert(IsActive); + + Mission = mission; + MissionQueue = MISSION_NONE; +} + + +/*********************************************************************************************** + * MissionClass::Get_Mission -- Fetches the mission that this object is acting under. * + * * + * Use this routine to fetch the mission that this object is CURRENTLY acting under. The * + * mission queue may be filled with a imminent mission change, but this routine does not * + * consider that. It only returns the CURRENT mission. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the mission that this unit is currently following. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +MissionType MissionClass::Get_Mission(void) const +{ + assert(IsActive); + + return(Mission == MISSION_NONE ? MissionQueue : Mission); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * MissionClass::Debug_Dump -- Dumps status values to mono screen. * + * * + * This is a debugging function that dumps this class' status to the monochrome screen * + * for review. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +void MissionClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(1, 9);mono->Printf("%-14s", MissionClass::Mission_Name(Mission)); + mono->Set_Cursor(16, 9);mono->Printf("%-12s", MissionClass::Mission_Name(MissionQueue)); + mono->Set_Cursor(1, 7);mono->Printf("%3d", (long)Timer); + mono->Set_Cursor(6, 7);mono->Printf("%2d", Status); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * MissionClass::AI -- Processes order script. * + * * + * This routine will process the order script for as much time as * + * possible or until a script delay is detected. This routine should * + * be called for every unit once per game loop (if possible). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 06/25/1995 JLB : Added new missions. * + *=============================================================================================*/ +void MissionClass::AI(void) +{ + assert(IsActive); + + ObjectClass::AI(); + + /* + ** If this is the kind of object that is "paralyzed with fear" while it is above + ** ground level (such as when be paradropped), it will perform no mission AI + ** processing. + */ + if ((What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL) && Height > 0) { + return; + } + + /* + ** This is the script AI equivalent processing. + */ + BStart(BENCH_MISSION); + if (Timer == 0 && Strength > 0) { + switch (Mission) { + default: + Timer = Mission_Sleep(); + break; + + case MISSION_HARMLESS: + case MISSION_SLEEP: + Timer = Mission_Sleep(); + break; + + case MISSION_STICKY: + case MISSION_GUARD: + Timer = Mission_Guard(); + break; + + case MISSION_ENTER: + Timer = Mission_Enter(); + break; + + case MISSION_CONSTRUCTION: + Timer = Mission_Construction(); + break; + + case MISSION_DECONSTRUCTION: + Timer = Mission_Deconstruction(); + break; + + case MISSION_CAPTURE: + case MISSION_SABOTAGE: + Timer = Mission_Capture(); + break; + + case MISSION_QMOVE: + case MISSION_MOVE: + Timer = Mission_Move(); + break; + + case MISSION_ATTACK: + Timer = Mission_Attack(); + break; + + case MISSION_RETREAT: + Timer = Mission_Retreat(); + break; + + case MISSION_HARVEST: + Timer = Mission_Harvest(); + break; + + case MISSION_GUARD_AREA: + Timer = Mission_Guard_Area(); + break; + + case MISSION_RETURN: + Timer = Mission_Return(); + break; + + case MISSION_STOP: + Timer = Mission_Stop(); + break; + + case MISSION_AMBUSH: + Timer = Mission_Ambush(); + break; + + case MISSION_HUNT: + case MISSION_RESCUE: + Timer = Mission_Hunt(); + break; + +// case MISSION_TIMED_HUNT: +// Timer = Mission_Timed_Hunt(); +// break; + + case MISSION_UNLOAD: + Timer = Mission_Unload(); + break; + + case MISSION_REPAIR: + Timer = Mission_Repair(); + break; + + case MISSION_MISSILE: + Timer = Mission_Missile(); + break; + } + } + BEnd(BENCH_MISSION); +} + + +/*********************************************************************************************** + * MissionClass::Commence -- Start script with new order. * + * * + * This routine will start script processing according to any queued * + * order it may have. If there is no queued order, then this routine * + * does nothing. Call this routine whenever the unit is in a good * + * position to change its order (such as when it is stopped). * + * * + * INPUT: none * + * * + * OUTPUT: Did the mission actually change? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/23/1994 JLB : Created. * + * 07/14/1994 JLB : Simplified. * + * 06/17/1995 JLB : Returns success flag. * + *=============================================================================================*/ +bool MissionClass::Commence(void) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + Mission = MissionQueue; + MissionQueue = MISSION_NONE; + + /* + ** Force immediate state machine processing at the first state machine state value. + */ + Timer = 0; + Status = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Assign_Mission -- Give an order to a unit. * + * * + * This routine will assign the specified mission to the mission queue for this object. * + * The actual mission logic will then be performed at the first available and legal * + * opportunity. * + * * + * INPUT: order -- Mission to give the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 04/15/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void MissionClass::Assign_Mission(MissionType order) +{ + assert(IsActive); + + /* + ** Ensure that a MISSION_QMOVE is translated into a MISSION_MOVE. + */ + if (order == MISSION_QMOVE) order = MISSION_MOVE; + + if (order != MISSION_NONE && Mission != order) { + MissionQueue = order; + } +} + + +/*********************************************************************************************** + * MissionClass::Mission_From_Name -- Fetch order pointer from its name. * + * * + * This routine is used to convert an ASCII order name into the actual * + * order number it represents. Typically, this is used when processing * + * a scenario INI file. * + * * + * INPUT: name -- The ASCII order name to process. * + * * + * OUTPUT: Returns with the actual order number that the ASCII name * + * represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 04/22/1994 JLB : Converted to static member function. * + *=============================================================================================*/ +MissionType MissionClass::Mission_From_Name(char const * name) +{ + MissionType order; + + if (name) { + for (order = MISSION_FIRST; order < MISSION_COUNT; order++) { + if (stricmp(Missions[order], name) == 0) { + return(order); + } + } + } + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * MissionClass::Mission_Name -- Converts a mission number into an ASCII string. * + * * + * Use this routine to convert a mission number into the ASCII string that represents * + * it. Typical use of this is when generating an INI file. * + * * + * INPUT: mission -- The mission number to convert. * + * * + * OUTPUT: Returns with a pointer to the ASCII string that represents the mission type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +char const * MissionClass::Mission_Name(MissionType mission) +{ + if (mission != MISSION_NONE) { + return(Missions[mission]); + } + return("None"); +} + + +/*********************************************************************************************** + * MissionClass::Override_Mission -- temporarily overrides the units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override * + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +void MissionClass::Override_Mission(MissionType mission, TARGET, TARGET) +{ + assert(IsActive); + + if (MissionQueue != MISSION_NONE) { + SuspendedMission = MissionQueue; + } else { + SuspendedMission = Mission; + } + + Assign_Mission(mission); +} + + +/*********************************************************************************************** + * MissionClass::Restore_Mission -- Restores overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=============================================================================================*/ +bool MissionClass::Restore_Mission(void) +{ + assert(IsActive); + + if (SuspendedMission != MISSION_NONE) { + Assign_Mission(SuspendedMission); + SuspendedMission= MISSION_NONE; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MissionClass::Is_Recruitable_Mission -- Determines if this mission is recruitable for a tea * + * * + * Some missions preclude recruitment into a team. This routine will examine the mission * + * specified and if not allowed for a team, it will return false. * + * * + * INPUT: mission -- The mission type to examine. * + * * + * OUTPUT: bool; Is an object following this mission allowed to be recruited into a team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/14/1996 JLB : Created. * + *=============================================================================================*/ +bool MissionClass::Is_Recruitable_Mission(MissionType mission) +{ + if (mission == MISSION_NONE) { + return(true); + } + return(MissionControl[mission].IsRecruitable); +} + + + +MissionControlClass::MissionControlClass(void) : + Mission(MISSION_NONE), + IsNoThreat(false), + IsZombie(false), + IsRecruitable(true), + IsParalyzed(false), + IsRetaliate(true), + IsScatter(true), + Rate(".016"), + AARate(".016") +{ +} + + +char const * MissionControlClass::Name(void) const +{ + if (Mission == MISSION_NONE) { + return(""); + } + return(Missions[Mission]); +} + + + +bool MissionControlClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsNoThreat = ini.Get_Bool(Name(), "NoThreat", IsNoThreat); + IsZombie = ini.Get_Bool(Name(), "Zombie", IsZombie); + IsRecruitable = ini.Get_Bool(Name(), "Recruitable", IsRecruitable); + IsParalyzed = ini.Get_Bool(Name(), "Paralyzed", IsParalyzed); + IsRetaliate = ini.Get_Bool(Name(), "Retaliate", IsRetaliate); + IsScatter = ini.Get_Bool(Name(), "Scatter", IsScatter); + Rate = ini.Get_Fixed(Name(), "Rate", Rate); + AARate = ini.Get_Fixed(Name(), "AARate", 0); + if (AARate == 0) { + AARate = Rate; + } + return(true); + } + return(false); +} diff --git a/code/redalert/mission.h b/code/redalert/mission.h new file mode 100644 index 0000000..845cb52 --- /dev/null +++ b/code/redalert/mission.h @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MISSION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MISSION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISSION_H +#define MISSION_H + +#include "object.h" +#include "monoc.h" + +/**************************************************************************** +** This handles order assignment and tracking. The order is used to guide +** overall AI processing. +*/ +class MissionClass : public ObjectClass +{ + public: + + /* + ** This the tactical strategy to use. It is used by the unit script. This + ** is a general guide for unit AI processing. + */ + MissionType Mission; + MissionType SuspendedMission; + + /* + ** The order queue is used for orders that should take effect when the vehicle + ** has reached the center point of a cell. The queued order number is +1 when stored here + ** so that 0 will indicated there is no queued order. + */ + MissionType MissionQueue; + + int Status; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + MissionClass(RTTIType rtti, int id); + MissionClass(NoInitClass const & x) : ObjectClass(x), Timer(x) {}; + virtual ~MissionClass(void) {}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + #ifdef CHEAT_KEYS + void Debug_Dump(MonoClass *mono) const; + #endif + + void Shorten_Mission_Timer(void) {Timer = 0;} + virtual MissionType Get_Mission(void) const; + virtual void Assign_Mission(MissionType mission); + virtual bool Commence(void); + virtual void AI(void); + + /* + ** Support functions. + */ + virtual int Mission_Sleep(void); + virtual int Mission_Ambush(void); + virtual int Mission_Attack(void); + virtual int Mission_Capture(void); + virtual int Mission_Guard(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); +// virtual int Mission_Timed_Hunt(void); + virtual int Mission_Move(void); + virtual int Mission_Retreat(void); + virtual int Mission_Return(void); + virtual int Mission_Stop(void); + virtual int Mission_Unload(void); + virtual int Mission_Enter(void); + virtual int Mission_Construction(void); + virtual int Mission_Deconstruction(void); + virtual int Mission_Repair(void); + virtual int Mission_Missile(void); + virtual void Set_Mission(MissionType mission); + static bool Is_Recruitable_Mission(MissionType mission); + + static char const * Mission_Name(MissionType order); + static MissionType Mission_From_Name(char const *name); + virtual void Override_Mission(MissionType mission, TARGET, TARGET); + virtual bool Restore_Mission(void); + + private: + + /* + ** This the thread processing timer. When this value counts down to zero, then + ** more script processing may occur. + */ + CDTimerClass Timer; +}; + + +/**************************************************************************** +** This is the mission control (pun) that controls how each mission behaves +** when it comes to interacting with the game world. Example; some +** missions allow the object to scatter from threats, while others require +** the object to remain in place. This kind of characteristics are specfied +** by this class. +*/ +class MissionControlClass +{ + public: + MissionControlClass(void); + + bool Read_INI(CCINIClass & ini); + int Normal_Delay(void) const {return(TICKS_PER_MINUTE * Rate);} + int AA_Delay(void) const {return(TICKS_PER_MINUTE * AARate);} + + /* + ** This is the mission identifier that this mission represents. + */ + MissionType Mission; + + char const * Name(void) const; + + /* + ** If the object should not be considered a threat when it + ** comes to target scanning, then this will be true. + */ + unsigned IsNoThreat:1; + + /* + ** If objects in this mission should avoid targeting the enemy and + ** also avoid responding to the enemy, then this will be true. + */ + unsigned IsZombie:1; + + /* + ** An ojbect that can be recruited into a team must be on a mission + ** of this type. + */ + unsigned IsRecruitable:1; + + /* + ** If the object can behave normally except that it cannot + ** move to another location, then this flag will be true. + */ + unsigned IsParalyzed:1; + + /* + ** If an object on this mission is damaged, it is allowed to + ** retaliate? + */ + unsigned IsRetaliate:1; + + /* + ** Is the object allowed to scatter from immediate threats? + */ + unsigned IsScatter:1; + + /* + ** This specifies the time to delay between calls to the mission handler for those cases + ** where the delay could be indefinate. The exception would be when timing is critical. + ** Typical use of this would be to regulate the delay between mundane mission processing + ** in order to achieve less game overhead. + */ + fixed Rate; + + /* + ** Anti-Aircraft buildings (and units) in guard or guard area mode will use this override + ** delay interval instead of the normal "Rate" value. + */ + fixed AARate; +}; + + +#endif diff --git a/code/redalert/mixfile.cpp b/code/redalert/mixfile.cpp new file mode 100644 index 0000000..c3302ec --- /dev/null +++ b/code/redalert/mixfile.cpp @@ -0,0 +1,596 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MIXFILE.CPP 2 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MIXFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + * * + * Modified by Vic Grippi for WwXlat Tool 10/14/96 * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * MixFileClass::Free -- Uncaches a cached mixfile. * + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * MixFileClass::Offset -- Searches in mixfile for matching file and returns offset if found.* + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "buff.h" +#include "function.h" +#include +#include +#include +#include +#include +#include +#include "mixfile.h" + +#include "cdfile.h" +extern MFCD temp; + +//template int Compare(T const *obj1, T const *obj2) { +// if (*obj1 < *obj2) return(-1); +// if (*obj1 > *obj2) return(1); +// return(0); +//}; + + +/* +** This is the pointer to the first mixfile in the list of mixfiles registered +** with the mixfile system. +*/ +template +List > MixFileClass::List; + +template class MixFileClass; + +/*********************************************************************************************** + * MixFileClass::Free -- Uncaches a cached mixfile. * + * * + * Use this routine to uncache a mixfile that has been cached. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile that is to be uncached. * + * * + * OUTPUT: bool; Was the mixfile found and freed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Free(char const * filename) +{ + MixFileClass * ptr = Finder(filename); + + if (ptr) { + ptr->Free(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::~MixFileClass -- Destructor for the mixfile object. * + * * + * This destructor will free all memory allocated by this mixfile and will remove it from * + * the system. A mixfile removed in this fashion must be created anew in order to be * + * subsequent used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 01/06/1995 JLB : Puts mixfile header table into EMS. * + *=============================================================================================*/ +template +MixFileClass::~MixFileClass(void) +{ + /* + ** Deallocate any allocated memory. + */ + if (Filename) { + free((char *)Filename); + } + if (Data != NULL && IsAllocated) { + delete [] Data; + IsAllocated = false; + } + Data = NULL; + + if (HeaderBuffer != NULL) { + delete [] HeaderBuffer; + HeaderBuffer = NULL; + } + + /* + ** Unlink this mixfile object from the chain. + */ + Unlink(); +} + + +/*********************************************************************************************** + * MixFileClass::MixFileClass -- Constructor for mixfile object. * + * * + * This is the constructor for the mixfile object. It takes a filename and a memory * + * handler object and registers the mixfile object with the system. The index block is * + * allocated and loaded from disk by this routine. * + * * + * INPUT: filename -- Pointer to the filename of the mixfile object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles compressed file header. * + *=============================================================================================*/ +template +MixFileClass::MixFileClass(char const * filename, PKey const * key) : + IsDigest(false), + IsEncrypted(false), + IsAllocated(false), + Filename(0), + Count(0), + DataSize(0), + DataStart(0), + HeaderBuffer(0), + Data(0) +{ + if (filename == NULL) return; // ST - 5/9/2019 + + /* + ** Check to see if the file is available. If it isn't, then + ** no further processing is needed or possible. + */ + if (!Force_CD_Available(RequiredCD)) { + Prog_End("MixFileClass Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + + T file(filename); // Working file object. + Filename = strdup(file.File_Name()); + FileStraw fstraw(file); + PKStraw pstraw(PKStraw::DECRYPT, CryptRandom); + Straw * straw = &fstraw; + + if (!file.Is_Available()) return; + + /* + ** Stuctures used to hold the various file headers. + */ + FileHeader fileheader; + struct { + short First; // Always zero for extended mixfile format. + short Second; // Bitfield of extensions to this mixfile. + } alternate; + + /* + ** Fetch the first bit of the file. From this bit, it is possible to detect + ** whether this is an extended mixfile format or the plain format. An + ** extended format may have extra options or data layout. + */ + int got = straw->Get(&alternate, sizeof(alternate)); + + /* + ** Detect if this is an extended mixfile. If so, then see if it is encrypted + ** and/or has a message digest attached. Otherwise, just retrieve the + ** plain mixfile header. + */ + if (alternate.First == 0) { + IsDigest = ((alternate.Second & 0x01) != 0); + IsEncrypted = ((alternate.Second & 0x02) != 0); + + if (IsEncrypted) { + pstraw.Key(key); + pstraw.Get_From(&fstraw); + straw = &pstraw; + } + straw->Get(&fileheader, sizeof(fileheader)); + + } else { + memmove(&fileheader, &alternate, sizeof(alternate)); + straw->Get(((char*)&fileheader)+sizeof(alternate), sizeof(fileheader)-sizeof(alternate)); + } + + Count = fileheader.count; + DataSize = fileheader.size; +//BGMono_Printf("Mixfileclass %s DataSize: %08x \n",filename,DataSize);Get_Key(); + /* + ** Load up the offset control array. If RAM is exhausted, then the mixfile is invalid. + */ + HeaderBuffer = new SubBlock [Count]; + if (HeaderBuffer == NULL) return; + straw->Get(HeaderBuffer, Count * sizeof(SubBlock)); + + /* + ** The start of the embedded mixfile data will be at the current file offset. + ** This should be true even if the file header has been encrypted because the file + ** header was cleverly written with just the sufficient number of padding bytes so + ** that this condition would be true. + */ + DataStart = file.Seek(0, SEEK_CUR) + file.BiasStart; +// DataStart = file.Seek(0, SEEK_CUR); + + /* + ** Attach to list of mixfiles. + */ + List.Add_Tail(this); +} + + +/*********************************************************************************************** + * MixFileClass::Retrieve -- Retrieves a pointer to the specified data file. * + * * + * This routine will return with a pointer to the specified data file if the file resides * + * in memory. Otherwise, this routine returns NULL. Use this routine to access a resident * + * file directly rather than going through the process of pseudo disk access. This will * + * save both time and RAM. * + * * + * INPUT: filename -- Pointer to the filename of the data file to retrieve a pointer to. * + * * + * OUTPUT: Returns with a pointer to the data file's data. If the file is not in RAM, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1994 JLB : Created. * + *=============================================================================================*/ +template +void const * MixFileClass::Retrieve(char const * filename) +{ + void * ptr = 0; + Offset(filename, &ptr); + return(ptr); +}; + + +/*********************************************************************************************** + * MixFileClass::Finder -- Finds the mixfile object that matches the name specified. * + * * + * This routine will scan through all registered mixfiles and return with a pointer to * + * the matching mixfile. If no mixfile could be found that matches the name specified, * + * then NULL is returned. * + * * + * INPUT: filename -- Pointer to the filename to search for. * + * * + * OUTPUT: Returns with a pointer to the matching mixfile -- if found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 06/08/1996 JLB : Only compares filename and extension. * + *=============================================================================================*/ +template +MixFileClass * MixFileClass::Finder(char const * filename) +{ + MixFileClass * ptr = List.First(); + while (ptr->Is_Valid()) { + char path[_MAX_PATH]; + char name[_MAX_FNAME]; + char ext[_MAX_EXT]; + + /* + ** Strip the drive and path (if present) off of the filename + ** in the mixfile list. This enables a simple comparison to the + ** filename specified. The filename specified won't have a path attached and + ** the full pathname in the mixfile list WILL have a path attached. Hence, this + ** stripping of the path is necessary. + */ + _splitpath(ptr->Filename, NULL, NULL, name, ext); + _makepath(path, NULL, NULL, name, ext); + + if (stricmp(path, filename) == 0) { + return(ptr); + } + ptr = ptr->Next(); + } + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Caches the named mixfile into RAM. * + * * + * This routine will cache the mixfile, specified by name, into RAM. * + * * + * INPUT: filename -- The name of the mixfile that should be cached. * + * * + * OUTPUT: bool; Was the cache successful? * + * * + * WARNINGS: This routine could go to disk for a very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(char const * filename, Buffer const * buffer) +{ + MixFileClass * mixer = Finder(filename); + + if (mixer != NULL) { + return(mixer->Cache(buffer)); + } + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Cache -- Loads this particular mixfile's data into RAM. * + * * + * This load the mixfile data into ram for this mixfile object. This is the counterpart * + * to the Free() function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file load successful? It could fail if there wasn't enough room * + * to allocate the raw data block. * + * * + * WARNINGS: This routine goes to disk for a potentially very long time. * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + * 07/12/1996 JLB : Handles attached message digest. * + *=============================================================================================*/ +template +bool MixFileClass::Cache(Buffer const * buffer) +{ + /* + ** If the mixfile is already cached, then no action needs to be performed. + */ + if (Data != NULL) return(true); + + /* + ** If a buffer was supplied (and it is big enough), then use it as the data block + ** pointer. Otherwise, the data block must be allocated. + */ + if (buffer != NULL) { + if (buffer->Get_Size() == 0 || buffer->Get_Size() >= DataSize) { + Data = buffer->Get_Buffer(); + } + } else { + Data = new char [DataSize]; + IsAllocated = true; + } + + /* + ** If there is a data buffer to fill, then fill it now. + */ + if (Data != NULL) { + T file(Filename); + + FileStraw fstraw(file); + Straw * straw = &fstraw; + + /* + ** If a message digest is attached, then link a SHA straw segment to the data + ** stream so that the actual SHA can be compared with the attached one. + */ + SHAStraw sha; + if (IsDigest) { + sha.Get_From(fstraw); + straw = &sha; + } + + /* + ** Bias the file to the actual start of the data. This is necessary because the + ** real data starts some distance (not so easily determined) from the beginning of + ** the real file. + */ + file.Open(READ); + file.Bias(0); + file.Bias(DataStart); + + /* + ** Fetch the whole mixfile data in one step. If the number of bytes retrieved + ** does not equal that requested, then this indicates a serious error. + */ + long actual = straw->Get(Data, DataSize); + if (actual != DataSize) { + delete [] Data; + Data = NULL; + file.Error(EIO); + return(false); + } + + /* + ** If there is a digest attached to this mixfile, then read it in and + ** compare it to the generated digest. If they don't match, then + ** return with the "failure to cache" error code. + */ + if (IsDigest) { + char digest1[20]; + char digest2[20]; + sha.Result(digest2); + fstraw.Get(digest1, sizeof(digest1)); + if (memcmp(digest1, digest2, sizeof(digest1)) != 0) { + delete [] Data; + Data = NULL; + return(false); + } + } + + return(true); + } + IsAllocated = false; + return(false); +} + + +/*********************************************************************************************** + * MixFileClass::Free -- Frees the allocated raw data block (not the index block). * + * * + * This routine will free the (presumably large) raw data block, but leave the index * + * block intact. By using this in conjunction with the Cache() function, one can maintain * + * tight control of memory usage. If the index block is desired to be freed, then the * + * mixfile object must be deleted. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1994 JLB : Created. * + *=============================================================================================*/ +template +void MixFileClass::Free(void) +{ + if (Data != NULL && IsAllocated) { + delete [] Data; + } + Data = NULL; + IsAllocated = false; +} + + +int compfunc(void const * ptr1, void const * ptr2) +{ + if (*(long const *)ptr1 < *(long const *)ptr2) return(-1); + if (*(long const *)ptr1 > *(long const *)ptr2) return(1); + return(0); +} + + +/*********************************************************************************************** + * MixFileClass::Offset -- Determines the offset of the requested file from the mixfile system.* + * * + * This routine will take the filename specified and search through the mixfile system * + * looking for it. If the file was found, then the mixfile it was found in, the offset * + * from the start of the mixfile, and the size of the embedded file will be returned. * + * Using this method it is possible for the CCFileClass system to process it as a normal * + * file. * + * * + * INPUT: filename -- The filename to search for. * + * * + * realptr -- Stores a pointer to the start of the file in memory here. If the * + * file is not in memory, then NULL is stored here. * + * * + * mixfile -- The pointer to the corresponding mixfile is placed here. If no * + * mixfile was found that contains the file, then NULL is stored here. * + * * + * offset -- The starting offset from the beginning of the parent mixfile is * + * stored here. * + * * + * size -- The size of the embedded file is stored here. * + * * + * OUTPUT: bool; Was the file found? The file may or may not be resident, but it does exist * + * and can be opened. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +template +bool MixFileClass::Offset(char const * filename, void ** realptr, MixFileClass ** mixfile, long * offset, long * size) +{ + MixFileClass * ptr; + + if (filename == NULL) { +assert(filename != NULL);//BG + return(false); + } + + /* + ** Create the key block that will be used to binary search for the file. + */ + // Can't call strupr on a const string. ST - 5/20/2019 + //long crc = Calculate_CRC(strupr((char *)filename), strlen(filename)); + char filename_upper[_MAX_PATH]; + strcpy(filename_upper, filename); + strupr(filename_upper); + long crc = Calculate_CRC(strupr(filename_upper), strlen(filename_upper)); + SubBlock key; + key.CRC = crc; + + /* + ** Sweep through all registered mixfiles, trying to find the file in question. + */ + ptr = List.First(); + while (ptr->Is_Valid()) { + SubBlock * block; + + /* + ** Binary search for the file in this mixfile. If it is found, then extract the + ** appropriate information and store it in the locations provided and then return. + */ + block = (SubBlock *)bsearch(&key, ptr->HeaderBuffer, ptr->Count, sizeof(SubBlock), compfunc); + if (block != NULL) { + if (mixfile != NULL) *mixfile = ptr; + if (size != NULL) *size = block->Size; + if (realptr != NULL) *realptr = NULL; + if (offset != NULL) *offset = block->Offset; + if (realptr != NULL && ptr->Data != NULL) { + *realptr = (char *)ptr->Data + block->Offset; + } + if (ptr->Data == NULL && offset != NULL) { + *offset += ptr->DataStart; + } + return(true); + } + + /* + ** Advance to next mixfile. + */ + ptr = ptr->Next(); + } + + /* + ** All the mixfiles have been examined but no match was found. Return with the non success flag. + */ +assert(1);//BG + return(false); +} + + + +// ST - 12/18/2019 11:36AM +template +void MixFileClass::Free_All(void) +{ + MixFileClass * ptr = List.First(); + while (ptr->Is_Valid()) { + delete ptr; + ptr = List.First(); + } +} diff --git a/code/redalert/mixfile.h b/code/redalert/mixfile.h new file mode 100644 index 0000000..3e8a88e --- /dev/null +++ b/code/redalert/mixfile.h @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MIXFILE.H 1 3/03/97 10:25a Joe_bostic $ */ + +#ifndef MIXFILE_H +#define MIXFILE_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include +#include "listnode.h" +#include "pk.h" +#include "buff.h" + +template +class MixFileClass : public Node> +{ + public: + char const * Filename; // Filename of mixfile. + + + MixFileClass(char const *filename, PKey const * key); + ~MixFileClass(void); + + static bool Free(char const *filename); + void Free(void); + static void Free_All(void); // ST - 12/18/2019 11:35AM + bool Cache(Buffer const * buffer = NULL); + static bool Cache(char const *filename, Buffer const * buffer=NULL); + static bool Offset(char const *filename, void ** realptr = 0, MixFileClass ** mixfile = 0, long * offset = 0, long * size = 0); + static void const * Retrieve(char const *filename); + + struct SubBlock { + long CRC; // CRC code for embedded file. + long Offset; // Offset from start of data section. + long Size; // Size of data subfile. + + int operator < (SubBlock & two) const {return (CRC < two.CRC);}; + int operator > (SubBlock & two) const {return (CRC > two.CRC);}; + int operator == (SubBlock & two) const {return (CRC == two.CRC);}; + }; + + private: + static MixFileClass * Finder(char const * filename); + //long Offset(long crc, long * size = 0) const; // ST - 5/10/2019 + + /* + ** If this mixfile has an attached message digest, then this flag + ** will be true. The digest is checked only when the mixfile is + ** cached. + */ + unsigned IsDigest:1; + + /* + ** If the header to this mixfile has been encrypted, then this flag + ** will be true. Although the header of the mixfile may be encrypted, + ** the attached data files are not. + */ + unsigned IsEncrypted:1; + + /* + ** If the cached memory block was allocated by this routine, then this + ** flag will be true. + */ + unsigned IsAllocated:1; + + /* + ** This is the initial file header. It tells how many files are embedded + ** within this mixfile and the total size of all embedded files. + */ + typedef struct { + short count; + long size; + } FileHeader; + + /* + ** The number of files within the mixfile. + */ + int Count; + + /* + ** This is the total size of all the data file embedded within the mixfile. + ** It does not include the header or digest bytes. + */ + long DataSize; + + /* + ** Start of raw data in within the mixfile. + */ + long DataStart; + + /* + ** Points to the file header control block array. Each file in the mixfile will + ** have an entry in this table. The entries are sorted by their (signed) CRC value. + */ + SubBlock * HeaderBuffer; + + /* + ** If the mixfile has been cached, then this points to the cached data. + */ + void * Data; // Pointer to raw data. + + static List List; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/monoc.cpp b/code/redalert/monoc.cpp new file mode 100644 index 0000000..36cf97f --- /dev/null +++ b/code/redalert/monoc.cpp @@ -0,0 +1,1132 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MONOC.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MonoClass::Clear -- Clears the monochrome screen object. * + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * MonoClass::Pan -- Scroll the window right or left. * + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * MonoClass::Print -- Simple print of text number. * + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * MonoClass::Printf -- Prints formatted text using text string number. * + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * MonoClass::Text_Print -- Simple text printing from text number. * + * MonoClass::View -- Brings the mono object to the main display. * + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * Mono_Print -- Prints simple text to monochrome screen. * + * Mono_Printf -- Prints formatted text to visible page. * + * Mono_Text_Print -- Prints text to location specified. * + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#if (0)//PG +//#include "watcom.h" +#include "monoc.h" + +#include "function.h" + +#include +#include +#include +//#include +#include +#include + + +//PG +//extern void output(short port, short data); +//#pragma aux output parm [dx] [ax] = \ +// "out dx,al" \ +// "inc dx" \ +// "mov al,ah" \ +// "out dx,al" + + +bool MonoClass::Enabled = 0; +MonoClass * MonoClass::PageUsage[MonoClass::MAX_MONO_PAGES]; +//MonoClass::MonoPageType * MonoClass::MonoRAM = (MonoClass::MonoPageType *) 0xB0000; + +/* +** These are the IBM linedraw characters. +*/ +MonoClass::BoxDataType const MonoClass::CharData[MonoClass::COUNT] = { + {0xDA,0xC4,0xBF,0xB3,0xD9,0xC4,0xC0,0xB3}, // Single line + {0xD5,0xCD,0xB8,0xB3,0xBE,0xCD,0xD4,0xB3}, // Double horz. + {0xD6,0xC4,0xB7,0xBA,0xBD,0xC4,0xD3,0xBA}, // Double vert. + {0xC9,0xCD,0xBB,0xBA,0xBC,0xCD,0xC8,0xBA} // Double horz and vert. +}; + + +#ifdef NEVER +template +T min(T a, T b) { + if (a < b) return(a); + return(b); +} + +template +T max(T a, T b) { + if (a > b) return(a); + return(b); +} +#endif + + +/*********************************************************************************************** + * MonoClass::MonoClass -- The default constructor for monochrome screen object. * + * * + * This is the constructor for monochrome screen objects. It handles allocating a free * + * monochrome page. If there are no more pages available, then this is a big error. The * + * page allocated may not be the visible one. Call the View function in order to bring * + * it to the displayed page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::MonoClass(void) : + X(0), + Y(0), + Attrib(NORMAL), + Page(0), + SubX(0), + SubY(0), + SubW(COLUMNS), + SubH(LINES) +{ + int index; + + for (index = 0; index < MAX_MONO_PAGES; index++) { + if (!PageUsage[index]) { + PageUsage[index] = this; + Page = (char)index; + break; + } + } + if (index == MAX_MONO_PAGES) { + // Major error message should pop up here! + delete this; + } +} + + +/*********************************************************************************************** + * MonoClass::~MonoClass -- The default destructor for a monochrome screen object. * + * * + * This is the default destructor for a monochrome screen object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass::~MonoClass(void) +{ + PageUsage[Page] = 0; +} + + +/*********************************************************************************************** + * MonoClass::Pan -- Scroll the window right or left. * + * * + * This routine will scroll the window to the right or left as indicated by the number of * + * rows. * + * * + * INPUT: cols -- The number of columns to pan the window. Positive numbers pan to the left * + * while negative numbers pan to the right. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Pan(int cols) +{ + if (cols == 0) return; + + if (abs(cols) >= SubW) { + Clear(); + return; + } + + CellType cell; + cell.Character = ' '; + cell.Attribute = Attrib; + + if (cols > 0) { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX], &Page_Ptr()->Data[index][SubX+cols], sizeof(CellType)*(SubW-cols)); + for (int cc = SubX+SubW-cols; cc < SubX+SubW; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } else { + for (int index = SubY; index < SubY+SubH; index++) { + memmove(&Page_Ptr()->Data[index][SubX-cols], &Page_Ptr()->Data[index][SubX], sizeof(CellType)*(SubW+cols)); + for (int cc = SubX; cc < SubX-cols; cc++) { + Page_Ptr()->Data[index][cc] = cell; + } + } + } + Set_Cursor(X-cols, Y); +} + + +/*********************************************************************************************** + * MonoClass::Sub_Window -- Partitions the mono screen into a sub-window. * + * * + * This routine is used to partition the monochrome screen so that only a sub-window will * + * be processed. By using this, a separate rectangle of the screen can be cleared, * + * scrolled, panned, or printed into. * + * * + * INPUT: x,y -- The upper left corner of the new sub-window. * + * * + * w,h -- Dimensions of the sub-window specified in characters. * + * * + * OUTPUT: none * + * * + * WARNINGS: The parameters are clipped as necessary. * + * * + * HISTORY: * + * 06/05/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Sub_Window(int x, int y, int w, int h) +{ + /* + ** Undo any sub window adjustments to the cursor position. + */ + X += SubX; + Y += SubY; + + /* + ** Ensure parameters are legal. + */ + x = min(x, COLUMNS-1); + x = max(x, 0); + y = min(y, LINES-1); + y = max(y, 0); + if (w == -1) w = COLUMNS-x; + if (h == -1) h = LINES-y; + + /* + ** Assign the new sub-region. + */ + SubX = x; + SubY = y; + SubW = w; + SubH = h; + + /* + ** Move the cursor (if necessary) so that it falls within that region. + */ + int xx = X; + int yy = Y; + xx = min(xx, SubX+SubW-1); + xx = max(xx, SubX); + yy = min(yy, SubY+SubH-1); + yy = max(yy, SubY); + Set_Cursor(xx-SubX, yy-SubY); +} + + +/*********************************************************************************************** + * MonoClass::Draw_Box -- Draws a box using the IBM linedraw characters. * + * * + * Use this routine to draw a box to the monochrome screen. The IBM line draw characters * + * are used to give the it a fancy appearance. There are several line draw modes supported. * + * * + * INPUT: x,y -- The coordinates of the upper left corner of the box. * + * * + * w,y -- The width and height (respectively) to make the box. * + * * + * attrib -- The text attribute to use when drawing the box outline characters. * + * * + * thick -- The thickness style to use. Examine the BoxStyleType enum for * + * elaboration on the supported styles. * + * * + * OUTPUT: none * + * * + * WARNINGS: The interior of the box is NOT cleared by this routine. It is advised that this * + * area be cleared before the box is drawn. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Draw_Box(int x, int y, int w, int h, MonoAttribute attrib, BoxStyleType thick) +{ + CellType cell; + MonoAttribute oldattrib = Attrib; + + if (!Enabled || !w || !h) return; + + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + w = min(w, SubW-x); + w = max(w, 1); + h = min(h, SubH-y); + h = max(h, 1); + + x += SubX; + y += SubY; + + cell.Attribute = attrib; + + /* + ** Draw the horizontal lines. + */ + for (int xpos = 0; xpos < w-2; xpos++) { + cell.Character = CharData[thick].TopEdge; + Page_Ptr()->Data[y][x+xpos+1] = cell; + cell.Character = CharData[thick].BottomEdge; + Page_Ptr()->Data[y+h-1][x+xpos+1] = cell; + } + + /* + ** Draw the vertical lines. + */ + for (int ypos = 0; ypos < h-2; ypos++) { + cell.Character = CharData[thick].LeftEdge; + Page_Ptr()->Data[y+ypos+1][x] = cell; + cell.Character = CharData[thick].RightEdge; + Page_Ptr()->Data[y+ypos+1][x+w-1] = cell; + } + + /* + ** Draw the four corners. + */ + if (w > 1 && h > 1) { + cell.Character = CharData[thick].UpperLeft; + Page_Ptr()->Data[y][x] = cell; + cell.Character = CharData[thick].UpperRight; + Page_Ptr()->Data[y][x+w-1] = cell; + cell.Character = CharData[thick].BottomRight; + Page_Ptr()->Data[y+h-1][x+w-1] = cell; + cell.Character = CharData[thick].BottomLeft; + Page_Ptr()->Data[y+h-1][x] = cell; + } + + Attrib = oldattrib; +} + + +/*********************************************************************************************** + * MonoClass::Set_Cursor -- Sets the monochrome cursor to the coordinates specified. * + * * + * Use this routine to set the monochrome's cursor position to the coordinates specified. * + * This is the normal way of controlling where the next Print or Printf will output the * + * text to. * + * * + * INPUT: x,y -- The coordinate to position the monochrome cursor. 0,0 is the upper left * + * corner. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Set_Cursor(int x, int y) +{ + x = min(x, SubW); + x = max(x, 0); + y = min(y, SubH); + y = max(y, 0); + + X = x; + Y = y; +#if (0)//PG + if (!Enabled) return; + + /* + ** Update the visible cursor position only if the this mono page is the currently + ** visible one. + */ + int pos = ((y+SubY)*COLUMNS)+(x+SubX); + if (Page == 0) { + output(CONTROL_PORT, (short)(0x0E|(pos&0xFF00))); + output(CONTROL_PORT, (short)(0x0F|(pos<<8))); + } +#endif +} + + +/*********************************************************************************************** + * MonoClass::Clear -- Clears the monochrome screen object. * + * * + * This routine will fill the monochrome screen object with spaces. It is clearing the * + * screen of data, making it free for output. The cursor is positioned at the upper left * + * corner of the screen by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Clear(void) +{ + if (!Enabled) return; + + Set_Cursor(0, 0); + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + for (int rows = 0; rows < SubH; rows++) { + for (int cols = 0; cols < SubW; cols++) { + Page_Ptr()->Data[rows+SubX][cols+SubY] = cell; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Fill_Attrib -- Fill a block with specified attribute. * + * * + * This routine will give the specified attribute to the characters within the block * + * but will not change the characters themselves. You can use this routine to change the * + * underline, blink, or inverse characteristics of text. * + * * + * INPUT: x,y -- The upper left coordinate of the region to change. * + * * + * w,h -- The dimensions of the region to change (in characters). * + * * + * attrib -- The attribute to fill into the region specified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib) +{ + if (!w || !h || x >= SubW || h >= SubH || x+w > SubW || y+h > SubH) return; + + for (int rows = y; rows < y+h; rows++) { + for (int cols = x; cols < x+w; cols++) { + Page_Ptr()->Data[rows+SubY][cols+SubX].Attribute = attrib; + } + } +} + + +/*********************************************************************************************** + * MonoClass::Scroll -- Scroll the monochrome screen up by the specified lines. * + * * + * Use this routine to scroll the monochrome screen up by the number of lines specified. * + * This routine is typically called by the printing functions so that the monochrome screen * + * behaves in the expected manner -- printing at the bottom of the screen scrolls it up * + * to make room for new text. * + * * + * INPUT: lines -- The number of lines to scroll the monochrome screen. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Scroll(int lines) +{ + if (!Enabled || lines <= 0) return; + + if (abs(lines) >= SubH) { + Clear(); + return; + } + + CellType cell; + cell.Attribute = Attrib; + cell.Character = ' '; + + if (lines > 0) { + for (int row = 0; row < SubH-lines; row++) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row+1][SubX], SubW*sizeof(CellType)); + } + for (int frow = SubH-lines; frow < SubH; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } else { + for (int row = SubH-1; row >= -lines; row--) { + memmove(&Page_Ptr()->Data[SubY+row][SubX], &Page_Ptr()->Data[SubY+row-1][SubX], SubW*sizeof(CellType)); + } + for (int frow = 0; frow < -lines; frow++) { + for (int cc = 0; cc < SubW; cc++) { + Page_Ptr()->Data[SubY+frow][SubX+cc] = cell; + } + } + } + + Set_Cursor(X, Y-lines); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints a formatted string to the monochrome screen. * + * * + * Use this routine to output a formatted string, using the standard formatting options, * + * to the monochrome screen object's current cursor position. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * ... -- Any optional parameters to supply in formatting the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: The total formatted text length must not exceed 255 characters. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(char const *text, ...) +{ + va_list va; + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, text, va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Printf -- Prints formatted text using text string number. * + * * + * This routine will take the given text string number and print the formatted text to * + * the monochrome screen. * + * * + * INPUT: text -- The text number to convert into real text (by way of external function). * + * * + * ... -- Additional parameters as needed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Printf(int text, ...) +{ + va_list va; + + /* + ** The buffer object is placed at the end of the local variable list + ** so that if the sprintf happens to spill past the end, it isn't likely + ** to trash anything (important). The buffer is then manually truncated + ** to maximum allowed size before being printed. + */ + char buffer[256]; + + if (!Enabled) return; + + va_start(va, text); + vsprintf(buffer, Text_String(text), va); + buffer[sizeof(buffer)-1] = '\0'; + + Print(buffer); + va_end(va); +} + + +/*********************************************************************************************** + * MonoClass::Print -- Prints the text string at the current cursor coordinates. * + * * + * Use this routine to output the specified text string at the monochrome object's current * + * text coordinate position. * + * * + * INPUT: ptr -- Pointer to the string to print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(char const * ptr) +{ + int startcol = X; + char const * text; + CellType cell; + + if (!ptr || !Enabled) return; + + text = ptr; + cell.Attribute = Attrib; + while (*text) { + + cell.Character = *text; + + /* + ** Sometimes the character string is used for cursor control instead + ** of plain text output. Check for this case. + */ + switch (cell.Character) { + + /* + ** The "return" code behaves as it did in the old C library + ** mono system. That is, it returns the cursor position to + ** the next line but at the starting column of the print. + */ + case '\r': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(startcol, Y+1); + break; + + /* + ** The TAB character is not currently handled. Convert it to + ** a space instead. + */ + case '\t': + cell.Character = ' '; + // fall into normal print case. + + /* + ** All other characters are output directly and the cursor moves + ** rightward to match. If the cursor wraps past the right + ** edge it is moved to the next now down at left margin. If the + ** cursor goes off the bottom of the display, the display is scrolled + ** upward a line. + */ + default: + Page_Ptr()->Data[SubY+Y][SubX+X] = cell; + + if (X < SubW-1) { + Set_Cursor(X+1, Y); + break; + } + // Fall into newline case. + + /* + ** The "newline" code behaves like the console newline character. + ** That is, it moves the cursor down one line and at the first + ** column. + */ + case '\n': + if (Y == SubH-1) { + Scroll(1); + } + Set_Cursor(0, Y+1); + break; + } + text++; + } +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Prints text to the monochrome object at coordinates indicated. * + * * + * Use this routine to output text to the monochrome object at the X and Y coordinates * + * specified. * + * * + * INPUT: text -- Pointer to the text string to display. * + * * + * x,y -- The X and Y character coordinates to start the printing at. * + * * + * attrib-- Optional parameter that specifies what text attribute code to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(char const *text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(text); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); +} + + +/*********************************************************************************************** + * MonoClass::Text_Print -- Simple text printing from text number. * + * * + * This will print the text (represented by the text number) to the location on the * + * monochrome screen specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * x,y -- The coordinates to begin the printing at. * + * * + * attrib-- The character attribute to use while printing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Text_Print(int text, int x, int y, MonoAttribute attrib) +{ + int oldx = X; + int oldy = Y; + MonoAttribute oldattrib = Attrib; + + if (text != 0) { + X = (char)x; + Y = (char)y; + Attrib = attrib; + Print(Text_String(text)); + Attrib = oldattrib; + Set_Cursor(oldx, oldy); + } +} + + +/*********************************************************************************************** + * MonoClass::Print -- Simple print of text number. * + * * + * Prints text represented by the text number specified. * + * * + * INPUT: text -- The text number to print (converted to real text by external routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void MonoClass::Print(int text) +{ + Print(Text_String(text)); +} + + +/*********************************************************************************************** + * MonoClass::operator = -- Handles making one mono object have the same imagery as another. * + * * + * The assignment operator will handle copying the imagery from one monochrome object to * + * another. Use this routine in to make two monochrome class objects visually identical. * + * * + * INPUT: src -- A reference to the source (right side) monochrome object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +MonoClass & MonoClass::operator = (MonoClass const & src) +{ + memmove(Page_Ptr(), src.Page_Ptr(), sizeof(MonoPageType)); + Set_Cursor(src.X, src.Y); + return(*this); +} + + +/*********************************************************************************************** + * MonoClass::View -- Brings the mono object to the main display. * + * * + * Use this routine to display the mono object on the monochrome screen. It is possible * + * that the mono object exists on some background screen memory. Calling this routine will * + * perform the necessary memory swapping to bring the data to the front. The mono object * + * that was currently being viewed is not destroyed by this function. It is merely moved * + * off to some background page. It can be treated normally, except that is just isn't * + * visible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void MonoClass::View(void) +{ + if (Get_Current() == this) return; + + /* + ** If the visible page is already assigned to a real monochrome page + ** object, then it must be swapped with the new one. + */ + MonoClass * displace = Get_Current(); + if (displace) { + for (int line = 0; line < LINES; line++) { + for (int col = 0; col < COLUMNS; col++) { + CellType temp = Page_Ptr()->Data[line][col]; + Page_Ptr()->Data[line][col] = Raw_Ptr(0)->Data[line][col]; + Raw_Ptr(0)->Data[line][col] = temp; + } + } + displace->Page = Page; + + } else { + + /* + ** Just copy the new page over since the display page is not assigned + ** to a real monochrome page object. + */ + memmove(Raw_Ptr(0), Page_Ptr(), sizeof(MonoPageType)); + } + PageUsage[Page] = displace; + PageUsage[0] = this; + Page = 0; + + Set_Cursor(X, Y); +} + + + +/************************************************************************************ +** This is the set of C wrapper functions that access the MonoClass support routines. +** Since the C interface doesn't have the ability to write to non-visible pages, it +** will just blast the output to whichever mono page is currently visible. If there is +** no mono class object that is visible, then one will be created -- BUT NOT FREED. +** Typically, this is ok, since the C interface will create only one MonoClass object +** and the system supports up to 8. +*/ +void Mono_Set_Cursor(int x, int y) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Set_Cursor(x, y); + } +} + + +/*********************************************************************************************** + * Mono_Printf -- Prints formatted text to visible page. * + * * + * This routine will print formatted text (with parameters) to the visible monochrome page * + * at whatever the current cursor location is. * + * * + * INPUT: string -- The string to use as the main text and formatting control string. * + * * + * ... -- Any additional parameters required by the formatting string. * + * * + * OUTPUT: Returns with the number of characters written to the display. * + * * + * WARNINGS: The total size of the formatted text must not exceed 256 characters. * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Printf(char const * string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, string, va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + + +/*********************************************************************************************** + * Mono_Clear_Screen -- Clear the currently visible monochrome page. * + * * + * This routine will clear the currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Clear_Screen(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Clear(); + } +} + + +/*********************************************************************************************** + * Mono_Text_Print -- Prints text to location specified. * + * * + * This routine will print the specified text to the location indicated. * + * * + * INPUT: text -- Pointer to the text to print. * + * * + * x,y -- The coordinate to print the text at. * + * * + * attrib-- The attribute to use when printing the text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Text_Print(void const *text, int x, int y, int attrib) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Text_Print((const char*)text, x, y, (MonoClass::MonoAttribute)attrib); + } +} + + +/*********************************************************************************************** + * Mono_Draw_Rect -- Draws rectangle to monochrome screen. * + * * + * Use this routine to draw a rectangle to the monochrome screen. The dimensions, attribute,* + * and line style are controlled by parameters. * + * * + * INPUT: x,y -- Coordinate of upper left corner of the box to draw. * + * * + * w,h -- The width and height of the box to draw. * + * * + * attrib-- The attribute to use when drawing the box. * + * * + * thick -- The line drawing style to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Draw_Box(x, y, w, h, (MonoClass::MonoAttribute)attrib, (MonoClass::BoxStyleType)thick); + } +} + + +/*********************************************************************************************** + * Mono_Print -- Prints simple text to monochrome screen. * + * * + * This is the non-formatting print to the monochrome screen. * + * * + * INPUT: text -- Pointer to the text that will be printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +void Mono_Print(void const *text) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + mono->Print((const char*)text); + } +} + + +/*********************************************************************************************** + * Mono_X -- Fetches the X cursor position for current visible mono page. * + * * + * Use this routine to get the current cursor X position. This only applies to the * + * currently visible monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with X position of cursor. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_X(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +/*********************************************************************************************** + * Mono_Y -- Fetches the Y cursor position for current mono page. * + * * + * This routine will fetch the current Y cursor position for the monochrome page. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Y position of the monochrome page. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/04/1996 JLB : Created. * + *=============================================================================================*/ +int Mono_Y(void) +{ + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + return(short)mono->Get_X(); + } + return(0); +} + + +void Mono_Put_Char(char , int ) +{ +} + +void Mono_Scroll(int ) +{ +} + +void Mono_View_Page(int ) +{ +} + +int Mono_Printf(int string, ...) +{ + va_list va; + char buffer[256]; + + buffer[0] = '\0'; + if (MonoClass::Is_Enabled()) { + MonoClass * mono = MonoClass::Get_Current(); + if (!mono) { + mono = new MonoClass(); + mono->View(); + } + + va_start(va, string); + vsprintf(buffer, Text_String(string), va); + + mono->Print(buffer); + + va_end(va); + } + return((short)strlen(buffer)); +} + +//#endif \ No newline at end of file diff --git a/code/redalert/monoc.h b/code/redalert/monoc.h new file mode 100644 index 0000000..e2115ca --- /dev/null +++ b/code/redalert/monoc.h @@ -0,0 +1,244 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MONOC.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_Hx +#define MONOC_Hx + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class MonoClass { + public: + enum MonoClassPageEnums { + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + MAX_MONO_PAGES=16 // Maximum RAM pages on mono card. + }; + + typedef enum MonoAttribute { + INVISIBLE=0x00, // Black on black. + UNDERLINE=0x01, // Underline. + BLINKING=0x90, // Blinking white on black. + NORMAL=0x02, // White on black. + INVERSE=0x70, // Black on white. + } MonoAttribute; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = true;}; + static void Disable(void) {Enabled = false;}; + static bool Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Sub_Window(int x=0, int y=0, int w=-1, int h=-1); + void Fill_Attrib(int x, int y, int w, int h, MonoAttribute attrib); + void Draw_Box(int x, int y, int w, int h, MonoAttribute attrib=NORMAL, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(MonoAttribute attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, MonoAttribute attrib=NORMAL); + void Text_Print(int text, int x, int y, MonoAttribute attrib=NORMAL); + void View(void); + void Scroll(int lines=1); + void Pan(int cols=1); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + int Get_Width(void) const {return(SubW);}; + int Get_Height(void) const {return(SubH);}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + + /* + ** Cursor coordinate (relative to sub-window). + */ + int X; + int Y; + + /* + ** Default attribute to use when printing text. + */ + MonoAttribute Attrib; + + /* + ** The current physical page that this mono class object refers to. + */ + int Page; + + /* + ** Sub window coordinates. + */ + int SubX; + int SubY; + int SubW; + int SubH; + + /* + ** Pointer to the monochrome RAM. + */ +// static MonoPageType * MonoRAM; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + struct BoxDataType { + unsigned char UpperLeft; + unsigned char TopEdge; + unsigned char UpperRight; + unsigned char RightEdge; + unsigned char BottomRight; + unsigned char BottomEdge; + unsigned char BottomLeft; + unsigned char LeftEdge; + }; + static BoxDataType const CharData[4]; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + struct CellType { + unsigned char Character; // Character to display. + unsigned char Attribute; // Attribute. + }; + + struct MonoPageType { + CellType Data[LINES][COLUMNS]; + }; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + SIZE_OF_PAGE=(int)LINES*(int)COLUMNS*sizeof(CellType) // Entire page size. + }; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** Fetches pointers to the appropriate mono RAM. + */ + MonoPageType * Raw_Ptr(int page) const { +// jmarshall + //return &((MonoPageType *)0xB0000)[page]; + static char* fakeMonoPage = nullptr; + if (fakeMonoPage == nullptr) { + fakeMonoPage = new char[0x40000000]; + } + return (MonoPageType * )fakeMonoPage; +// jmarshall end + } + MonoPageType * Page_Ptr(void) const { + return(Raw_Ptr(Page)); + } + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static bool Enabled; +}; + +#ifndef WIN32 +int Mono_Printf(int string, ...); +#else +extern void Mono_Set_Cursor(int x, int y); +extern int Mono_Printf(int string, ...); +extern int Mono_Printf(char const * string, ...); +extern void Mono_Clear_Screen(void); +extern void Mono_Text_Print(void const *text, int x, int y, int attrib); +extern void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +extern void Mono_Print(void const *text); +extern int Mono_X(void); +extern int Mono_Y(void); +#endif + +#endif + diff --git a/code/redalert/mouse.cpp b/code/redalert/mouse.cpp new file mode 100644 index 0000000..cf9c0ab --- /dev/null +++ b/code/redalert/mouse.cpp @@ -0,0 +1,389 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MOUSE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MouseClass::AI -- Process player input as it relates to the mouse * + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** This points to the loaded mouse shapes. +*/ +void const * MouseClass::MouseShapes; + +/* +** This is the timer that controls the mouse animation. It is always at a fixed +** rate so it uses the constant system timer. +*/ +CDTimerClass MouseClass::Timer = 0; + + +/*********************************************************************************************** + * MouseClass::MouseClass -- Default constructor for the mouse handler class. * + * * + * This is the default constructor for the mouse handling class. It merely sets up the * + * mouse system to its default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +MouseClass::MouseClass(void) : + IsSmall(false), + CurrentMouseShape(MOUSE_NORMAL), + NormalMouseShape(MOUSE_NORMAL), + Frame(0) +{ +} + + +/*********************************************************************************************** + * MouseClass::Set_Default_Mouse -- Sets the mouse to match the shape specified. * + * * + * This routine is used to inform the display system as to which mouse shape is desired. * + * * + * INPUT: mouse -- The mouse shape number to set the mouse to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Set_Default_Mouse(MouseType mouse, bool size) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + NormalMouseShape = mouse; + Override_Mouse_Shape(mouse, size); +} + + +/*********************************************************************************************** + * MouseClass::Revert_Mouse_Shape -- Reverts the mouse shape to the non overridden shape. * + * * + * Use this routine to cancel the effects of Override_Mouse_Shape(). It will revert the * + * mouse back to the original shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/27/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Revert_Mouse_Shape(void) +{ + Override_Mouse_Shape(NormalMouseShape, false); +} + + +/*********************************************************************************************** + * MouseClass::Mouse_Small -- Controls the sizing of the mouse. * + * * + * This routine is called to change the mouse sizing override. If the mouse can change * + * size to that specified, then the mouse imagery will be changed. If a change of imagery * + * cannot occur (due to lack of appropriate artwork), then no action will be performed. * + * * + * INPUT: small -- Should the mouse be made small? If not, then it will be made large. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Mouse_Small(bool wsmall) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (IsSmall == wsmall) { + return; + } + + IsSmall = wsmall; + + if (wsmall) { + if (control->SmallFrame != -1) { + Set_Mouse_Cursor(control->X, control->Y, control->SmallFrame + Frame / 4); + } else { + Set_Mouse_Cursor(MouseControl[MOUSE_NORMAL].X, MouseControl[MOUSE_NORMAL].Y, MOUSE_NORMAL); + } + } else { + Set_Mouse_Cursor(control->X, control->Y, control->StartFrame + Frame / 4); + } +} + + +/*********************************************************************************************** + * MouseClass::Override_Mouse_Shape -- Alters the shape of the mouse. * + * * + * This routine is used to alter the shape of the mouse as needed. * + * Typical mouse shape change occurs when scrolling the map or * + * selecting targets. * + * * + * INPUT: mouse -- The mouse shape number to use. * + * * + * OUTPUT: bool; Was the mouse shape changed? * + * * + * WARNINGS: This is not intended to be used as a means to hide the * + * mouse. Nor will it work correctly if the mouse shape * + * file hasn't been loaded. * + * * + * HISTORY: * + * 03/10/1994 JLB : Created. * + * 06/03/1994 JLB : Made into member function. * + * 12/24/1994 JLB : Added small control parameter. * + *=============================================================================================*/ +#ifdef WIN32 +void Block_Mouse(GraphicBufferClass *buffer); +void Unblock_Mouse(GraphicBufferClass *buffer); +#endif + +bool MouseClass::Override_Mouse_Shape(MouseType mouse, bool wsmall) +{ + assert((unsigned)mouse < MOUSE_COUNT); + + MouseStruct const * control = &MouseControl[mouse]; + static bool startup = false; + int baseshp; + + /* + ** Only certain mouse shapes have a small counterpart. If the requested mouse + ** shape is not one of these, then force the small size override flag to false. + */ + if (control->SmallFrame == -1) { + wsmall = false; + } + + /* + ** If the mouse shape is going to change, then inform the mouse driver of the + ** change. + */ + if (!startup || (MouseShapes && ((mouse != CurrentMouseShape) || (wsmall != IsSmall)))) { + startup = true; + + Timer = control->FrameRate; + Frame = 0; + + baseshp = (wsmall) ? control->SmallFrame : control->StartFrame; + if (baseshp == -1) { + baseshp = control->StartFrame; + } + + Set_Mouse_Cursor(control->X, control->Y, baseshp); + CurrentMouseShape = mouse; + IsSmall = wsmall; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * MouseClass::AI -- Process player input as it relates to the mouse * + * * + * This routine will is to be called once per game tick and is passed the player keyboard * + * or mouse input code. It processes this code and updates the mouse shape as appropriate. * + * * + * INPUT: input -- The player input code as returned from Keyboard->Get(). * + * * + * x,y -- The mouse coordinate values to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 03/27/1995 JLB : New animation control. * + * 05/28/1995 JLB : Moderates animation so is more steady regardless of speed. * + * 06/30/1995 JLB : Uses constant timer system. * + *=============================================================================================*/ +void MouseClass::AI(KeyNumType &input, int x, int y) +{ + MouseStruct const * control = &MouseControl[CurrentMouseShape]; + + if (control->FrameRate && Timer == 0) { + + Frame++; + Frame %= control->FrameCount; + Timer = control->FrameRate; + + if (!IsSmall || control->SmallFrame != -1) { + int baseframe = (IsSmall) ? control->SmallFrame : control->StartFrame; + if (baseframe == -1) baseframe = control->StartFrame; + Set_Mouse_Cursor(control->X, control->Y, baseframe + Frame); + } + } + + ScrollClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * MouseClass::One_Time -- Performs the one time initialization of the mouse system. * + * * + * Use this routine to load the mouse data file and perform any other necessary one time * + * preparations for the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine ONCE. * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::One_Time(void) +{ + ScrollClass::One_Time(); + + /* + ** Override the mouse shape file with the one in the current directory, but only if there + ** is an override file available. + */ + #ifndef NDEBUG + RawFileClass file("MOUSE.SHP"); + if (file.Is_Available()) { + MouseShapes = Load_Alloc_Data(file); + } else { + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + } + #else + MouseShapes = MFCD::Retrieve("MOUSE.SHP"); + #endif +} + + +/*********************************************************************************************** + * MouseClass::Init_Clear -- Sets the mouse system to a known state * + * * + * This routine will reset the mouse handling system. Typically, this routine is called * + * when preparing for the beginning of a new scenario. * + * * + * INPUT: theater -- The theater that the scenario will take place. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void MouseClass::Init_Clear(void) +{ + ScrollClass::Init_Clear(); + IsSmall = false; + NormalMouseShape = MOUSE_NORMAL; +} + + +/* +** This array of structures is used to control the mouse animation +** sequences. +*/ +//#ifdef WIN32 +//#define WD 45 +//#define HT 36 +//#else +#define WD 29 +#define HT 23 +//#endif + +MouseClass::MouseStruct MouseClass::MouseControl[MOUSE_COUNT] = { + {0, 1, 0, 80, 0, 0}, // MOUSE_NORMAL + {1, 1, 0, -1, WD/2, 0}, // MOUSE_N + {2, 1, 0, -1, WD, 0}, // MOUSE_NE + {3, 1, 0, -1, WD, HT/2}, // MOUSE_E + {4, 1, 0, -1, WD, HT}, // MOUSE_SE + {5, 1, 0, -1, WD/2, HT}, // MOUSE_S + {6, 1, 0, -1, 0, HT}, // MOUSE_SW + {7, 1, 0, -1, 0, HT/2}, // MOUSE_W + {8, 1, 0, -1, 0, 0}, // MOUSE_NW + + {124, 1, 0, -1, WD/2, 0}, // MOUSE_NO_N + {125, 1, 0, -1, WD, 0}, // MOUSE_NO_NE + {126, 1, 0, -1, WD, HT/2}, // MOUSE_NO_E + {127, 1, 0, -1, WD, HT}, // MOUSE_NO_SE + {128, 1, 0, -1, WD/2, HT}, // MOUSE_NO_S + {129, 1, 0, -1, 0, HT}, // MOUSE_NO_SW + {130, 1, 0, -1, 0, HT/2}, // MOUSE_NO_W + {131, 1, 0, -1, 0, 0}, // MOUSE_NO_NW + + {14, 1, 0, 33, WD/2, HT/2}, // MOUSE_NO_MOVE + {10, 4, 4, 29, WD/2, HT/2}, // MOUSE_CAN_MOVE + {113, 3, 4, 142, WD/2, HT/2}, // MOUSE_ENTER + {59, 9, 4, -1, WD/2, HT/2}, // MOUSE_DEPLOY + {15, 6, 4, -1, WD/2, HT/2}, // MOUSE_CAN_SELECT + {21, 8, 4, 134, WD/2, HT/2}, // MOUSE_CAN_ATTACK + {68, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_BACK + {148, 12, 2, -1, WD/2, HT/2}, // MOUSE_SELL_UNIT + {35, 24, 2, -1, WD/2, HT/2}, // MOUSE_REPAIR + {120, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + {119, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_SELL_BACK + {81, 1, 0, 145, WD/2, HT/2}, // MOUSE_RADAR_CURSOR + {90, 7, 4, -1, WD/2, HT/2}, // MOUSE_NUCLEAR_BOMB + {82, 8, 2, 213, WD/2, HT/2}, // MOUSE_AIR_STRIKE + {116, 3, 4, 121, WD/2, HT/2}, // MOUSE_DEMOLITIONS + {147, 1, 0, 146, WD/2, HT/2}, // MOUSE_AREA_GUARD + {160, 4, 4, 194, WD/2, HT/2}, // MOUSE_HEAL + {164, 3, 4, 167, WD/2, HT/2}, // MOUSE_DAMAGE + {170, 24, 2, -1, WD/2, HT/2}, // MOUSE_GREPAIR + {195, 8, 4, 203, WD/2, HT/2}, // MOUSE_STAY_ATTACK + {211, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_DEPLOY + {212, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_ENTER + {213, 1, 0, -1, WD/2, HT/2}, // MOUSE_NO_REPAIR + + {97, 8, 3, -1, WD/2, HT/2}, // MOUSE_CHRONO_SELECT + {105, 8, 2, -1, WD/2, HT/2}, // MOUSE_CHRONO_DEST + +}; diff --git a/code/redalert/mouse.h b/code/redalert/mouse.h new file mode 100644 index 0000000..ab2bffb --- /dev/null +++ b/code/redalert/mouse.h @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MOUSE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MOUSE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MOUSE_H +#define MOUSE_H + +#include "stage.h" +#include "scroll.h" + +class MouseClass: public ScrollClass +{ + public: + MouseClass(void); + MouseClass(NoInitClass const & x) : ScrollClass(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void AI(KeyNumType &input, int x, int y); + virtual bool Override_Mouse_Shape(MouseType mouse, bool wsmall=false); + virtual void Revert_Mouse_Shape(void); + virtual MouseType Get_Mouse_Shape(void) const {return NormalMouseShape;}; + virtual void Mouse_Small(bool wsmall); + + /* + ** File I/O. + */ + virtual bool Load(Straw & file); + virtual bool Save(Pipe & file) const; + + virtual void Set_Default_Mouse(MouseType mouse, bool wsmall = false); + + /* + ** This allows the tactical map input gadget access to change the + ** mouse shapes. + */ + friend class TacticalClass; + + /* + ** This points to the loaded mouse shapes. + */ + static void const * MouseShapes; + + private: + + /* + ** This type is used to control the frames and rates of the mouse + ** pointer. Some mouse pointers are actually looping animations. + */ + typedef struct MouseStruct + { + int StartFrame; // Starting frame number. + int FrameCount; // Number of animation frames. + int FrameRate; // Frame delay between changing frames. + int SmallFrame; // Start frame number for small version (if any). + int X,Y; // Hotspot X and Y offset. + } MouseStruct; + + /* + ** The control frames and rates for the various mouse pointers are stored + ** in this static array. + */ + static MouseStruct MouseControl[MOUSE_COUNT]; + + public: + /* + ** If the small representation of the mouse is active, then this flag is true. + */ + unsigned IsSmall:1; + + + private: + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseType CurrentMouseShape; + MouseType NormalMouseShape; + + /* + ** For animating mouse shapes, this controls the frame and animation rate. + */ + static CDTimerClass Timer; + int Frame; +}; + + +#endif diff --git a/code/redalert/movie.h b/code/redalert/movie.h new file mode 100644 index 0000000..9cbc39c --- /dev/null +++ b/code/redalert/movie.h @@ -0,0 +1,70 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MPGMOVIE_H_ +#define _MPGMOVIE_H_ +/**************************************************************************** +* +* FILE +* MpgMovie.h +* +* DESCRIPTION +* Movie playback using DirectShow Multimedia streaming and DirectDraw +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* May 27, 1998 +* +****************************************************************************/ + +#include +//#include + +#ifdef MPGEXPORT +#define DLLCALL __declspec(dllexport) +#else +#define DLLCALL __declspec(dllimport) +#endif + +typedef enum + { + MPGCMD_ERROR = -1, + MPGCMD_INIT = 0, + MPGCMD_CLEANUP, + MPGCMD_PALETTE, + MPGCMD_UPDATE + } MPG_CMD; + +typedef enum + { + MPGRES_QUIT = -1, + MPGRES_CONTINUE = 0, + MPGRES_LOSTFOCUS, + } MPG_RESPONSE; + +typedef MPG_RESPONSE (far __stdcall *LPMPGCALLBACK)(MPG_CMD cmd, LPVOID data, LPVOID user); + +extern "C" + { + DLLCALL void __stdcall MpgPlay(const char* name, IDirectDraw* dd, + IDirectDrawSurface* surface, RECT* dstRect); + DLLCALL void __stdcall MpgPause(void); + DLLCALL void __stdcall MpgResume(void); + DLLCALL void __stdcall MpgSetCallback(LPMPGCALLBACK callback, LPVOID user); + } + +#endif // _MPGMOVIE_H_ diff --git a/code/redalert/mp.cpp b/code/redalert/mp.cpp new file mode 100644 index 0000000..663e092 --- /dev/null +++ b/code/redalert/mp.cpp @@ -0,0 +1,2729 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * memrev -- Reverse the byte order of the buffer specified. * + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * XMP_Add -- Add two MP numbers with a carry option. * + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * XMP_Compare -- Compare one MP number with another. * + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * XMP_Dec -- Decrement the MP number by one. * + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * XMP_Double_Mul -- Double precision MP multiply. * + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * XMP_Inc -- Increment an MP number by one. * + * XMP_Init -- Initialize an MP number to a starting value. * + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * XMP_Move -- Assign one MP number to another. * + * XMP_Neg -- Negate the specified MP number. * + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * XMP_Randomize -- Generate a random MP number. * + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include "mp.h" + + +#ifndef __BORLANDC__ +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define _USERENTRY +#endif + + +/*********************************************************************************************** + * _Byte_Precision -- Determines the number of bytes significant in long integer. * + * * + * This utility routine will determine the number of precision bytes exist in the long * + * integer specified. There are some optimizations that can occur if the byte precision * + * is known. * + * * + * INPUT: value -- The value of the long integer that the byte precision will be calculated * + * for. * + * * + * OUTPUT: Returns with the number of bytes that the long integer requires (at a minimum) * + * to cover the precision of the integer. The minimum value will be 1, the maximum * + * will be 4. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +static int _Byte_Precision(unsigned long value) +{ + int byte_count; + for (byte_count = sizeof(value); byte_count; byte_count--) { + if (value >> ((byte_count-1)*8)) break; + } + return(byte_count); +} + + +/*********************************************************************************************** + * XMP_DER_Length_Encode -- Output the length of a DER block. * + * * + * This routine will output the length of the block using Distinguished Encoding Rules. * + * The rest of the block must be filled in as appropriate. For data blocks that are less * + * than 128 bytes long, the header consists of only one byte. Longer buffers lengths * + * can consume up to 5 bytes (depends on magnitude of the length value). * + * * + * INPUT: length -- The length of the data block to be output. * + * * + * output -- Pointer to the memory block that will be set up. * + * * + * OUTPUT: Returns with the number of bytes (header) that was used to store the length * + * value. Subsequent data must be placed after the header. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output) +{ + assert(output != NULL); + + int header_length = 0; + + if (length <= SCHAR_MAX) { + output[header_length++] = (unsigned char)length; + } else { + output[header_length++] = (unsigned char)(_Byte_Precision(length) | 0x80); + for (int byte_counter = _Byte_Precision(length); byte_counter; --byte_counter) { + output[header_length++] = (unsigned char)(length >> ((byte_counter-1)*8)); + } + } + return(header_length); +} + + +/*********************************************************************************************** + * XMP_DER_Encode -- Encode a number into a buffer using DER. * + * * + * This routine is used to encode a number into a buffer using Distinguished Encoding * + * Rules. The number of bytes used will be, typically, two bytes more than the number of * + * precision bytes in the number. * + * * + * INPUT: from -- Pointer to the multi-precision number. * + * * + * output -- Pointer to the buffer that will hold the DER encoded number. * + * * + * precision-- The precision of the multi-precision number. * + * * + * OUTPUT: Returns with the number of bytes used in the output buffer. * + * * + * WARNINGS: Make sure the buffer is big enough to hold the DER encoded number. For safety * + * make sure it is precision+6 bytes long. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision) +{ + assert(from != NULL); + assert(output != NULL); + assert(precision > 0); + + unsigned char buffer[MAX_UNIT_PRECISION*sizeof(digit)+1]; + int header_count = 0; + + unsigned number_count = XMP_Encode(buffer, from, precision); + + output[header_count++] = 0x02; + header_count += XMP_DER_Length_Encode(number_count, &output[header_count]); + memcpy(&output[header_count], buffer, number_count); + + return(header_count+number_count); +} + + +/*********************************************************************************************** + * XMP_DER_Decode -- Decode a DER number into an MP number. * + * * + * Use this routine to decode a Distinguished Encoding Rules number into a multi-precision * + * number. This is the counterpart function to the XMP_DER_Encode() function. * + * * + * INPUT: result -- The buffer the hold the result MP number. * + * * + * input -- Pointer to the DER encoded number. * + * * + * precision -- The precision of the MP number. This is the maximum precision the * + * DER number can be. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision) +{ + assert(result != NULL); + assert(input != NULL); + assert(precision > 0); + + if (*input++ == 0x02) { + unsigned byte_count; + + if ((*input & 0x80) == 0) { + byte_count = *input++; + } else { + int length = *input++ & 0x7f; + if (length > 2) return; + byte_count = *input++; + if (length > 1) byte_count = (byte_count << 8) | *input++; + } + if (byte_count <= (precision * sizeof(digit))) { + XMP_Signed_Decode(result, input, byte_count, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer. * + * * + * This routine will encode an multi-precision number into a buffer of specified length. * + * The number of stored in "big endian" format with appropriate sign extension. * + * * + * INPUT: to -- Pointer to the buffer to place the number. * + * * + * tobytes -- The number of bytes to use in the destination buffer. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes placed into the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(tobytes > 0); + assert(precision > 0); + + unsigned frombytes = precision * sizeof(digit); + unsigned char filler = (unsigned char)(XMP_Is_Negative(from, precision) ? 0xff : 0); + + int index; + for (index = 0; index < (int)(tobytes-frombytes); index++) { + *to++ = filler; + } + + const unsigned char * fptr = ((const unsigned char *)from) + min(tobytes, frombytes); + for (index = 0; index < (int)min(tobytes, frombytes); index++) { + *to++ = *--fptr; + } + + return(tobytes); +} + + +/*********************************************************************************************** + * XMP_Encode -- Encode MP number into buffer as compactly as possible. * + * * + * This routine will encode the MP number into the specified buffer. The number will be * + * encoded using the least number of bytes possible. * + * * + * INPUT: to -- The buffer to encode the MP number into. * + * * + * from -- Pointer to the MP number to be encoded. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes used in the destination buffer to hold the * + * encoded number. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the encoded MP number. * + * A safe size would be the precision plus one. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +//#pragma warning 364 9 +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision) +{ + assert(to != NULL); + assert(from != NULL); + assert(precision > 0); + + bool is_negative = XMP_Is_Negative(from, precision); + unsigned char filler = (unsigned char)(is_negative ? 0xff : 0); + unsigned char * number_ptr; + + unsigned char * const end = (unsigned char *)from; + for (number_ptr = (unsigned char *)end + precision - 1; number_ptr > (unsigned char *)end; number_ptr--) { + if (*number_ptr != filler) break; + } + + unsigned index = 0; + if (((*number_ptr & 0x80) && !is_negative) || (!(*number_ptr & 0x80) && is_negative)) { + to[index++] = filler; + } + + to[index++] = *number_ptr; + + while (number_ptr != end) { + to[index++] = *--number_ptr; + } + return(index); +} + + +/*********************************************************************************************** + * XMP_Signed_Decode -- Decode a number as if it were signed. * + * * + * Use this routine to convert a coded number back into an MP number. The coded number * + * is presumed to be signed. * + * * + * INPUT: result -- Pointer to the buffer that will hold the decoded MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes consumed by the encoded MP number. * + * * + * precision -- The precision of the MP number (maximum) of the result. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the precision is sufficient to hold the decoded MP number. * + * Otherwise, the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + unsigned char filler = (unsigned char)((*from & 0x80) ? 0xff : 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = filler; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Unsigned_Decode -- Decode a number as if it were unsigned. * + * * + * Use this routine to decode a MP number and treat it as if it were unsigned. * + * * + * INPUT: result -- Pointer to the buffer to hold the result MP number. * + * * + * from -- Pointer to the encoded MP number. * + * * + * frombytes-- The number of bytes in the encoded number. * + * * + * precision-- The precision of the result MP number -- maximum precision. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the result MP precision is sufficient to hold the decoded number or * + * else the result is undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision) +{ + assert(result != NULL); + assert(from != NULL); + assert(frombytes > 0); + assert(precision > 0); + + int fillcount = precision * sizeof(digit) - frombytes; + unsigned char * dest = (unsigned char *)&result[precision]; + + /* + ** Fill in any excess significant bytes. + */ + int index; + for (index = 0; index < fillcount; index++) { + *--dest = '\0'; + } + + /* + ** Move in the remaining bytes. + */ + for (index = 0; index < frombytes; index++) { + *--dest = *from++; + } +} + + +/*********************************************************************************************** + * XMP_Significance -- Fetch the precision (bytes) of the MP number. * + * * + * This routine will return with the precision of the MP number expressed as bytes. The * + * MP number is presumed unsigned. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The precision of the MP number to examine. * + * * + * OUTPUT: Returns with the minimum number of bytes consumed by this MP number. * + * * + * WARNINGS: Passing a signed MP number to this routine will return an artificially greater * + * precision than it really is. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Significance(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + number += precision; + do { + if (*(--number)) break; + } while (--precision); + return(precision); +} + + +/*********************************************************************************************** + * XMP_Inc -- Increment an MP number by one. * + * * + * This will increment the MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to increment. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps around the maximum precision, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inc(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + if (++(*number)) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Dec -- Decrement the MP number by one. * + * * + * Use this routine to decrement the specified MP number by one. * + * * + * INPUT: number -- Pointer to the MP number to decrement. * + * * + * precision-- The precision of the MP number to decrement. * + * * + * OUTPUT: none * + * * + * WARNINGS: If the number wraps below zero, the results are undefined. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Dec(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + do { + *number -= 1; + if ((*number) != ~(digit)0) break; + number++; + } while (--precision); +} + + +/*********************************************************************************************** + * XMP_Neg -- Negate the specified MP number. * + * * + * This routine will negate (reverse sign) of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to negate. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Neg(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + XMP_Not(number, precision); + XMP_Inc(number, precision); +} + + +/*********************************************************************************************** + * XMP_Abs -- Perform an absolute value on the specified MP number. * + * * + * This will perform the absolute value function on the specified MP number. That is, if * + * the MP number is negative, it will be transformed into a positive number. If the number * + * is already positive, then it will be left alone. * + * * + * INPUT: number -- Pointer to the MP number to ABS. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Abs(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + if (XMP_Is_Negative(number, precision)) { + XMP_Neg(number, precision); + } +} + + +/*********************************************************************************************** + * XMP_Shift_Right_Bits -- Shift the MP number right by specified bit count. * + * * + * Use this routine to perform a right shift of the MP number by the number of bits * + * specified. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift upon. * + * * + * bits -- The number of bits to shift. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is an unsigned shift. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Right_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + number += precision; + digit carry = 0; + while (precision--) { + number--; + digit temp = *number; + *number = carry; + carry = temp; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + number += precision; + digit carry = 0; + digit bitmask = (1L << bits) - 1; + int unbits = UNITSIZE - bits; + + while (precision--) { + number--; + digit temp = *number & bitmask; + *number >>= bits; + *number |= carry << unbits; + carry = temp; + } + return; + } + + /* + ** General purpose slow right. + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number + digits_to_shift) >> bits_to_shift) | (*(number + (digits_to_shift + 1)) << (UNITSIZE - bits_to_shift)); + number++; + } + + if (digits_to_shift < precision) { + *number = (*(number + digits_to_shift) >> bits_to_shift); + number++; + } + + for (index= 0; index < min(digits_to_shift, precision); index++) { + *number++ = 0; + } +} + + +/*********************************************************************************************** + * XMP_Shift_Left_Bits -- Shifts the MP number left by the specified bit count. * + * * + * Use this routine to perform a left shift of the specified MP number. * + * * + * INPUT: number -- Pointer to the MP number to perform the shift operation on. * + * * + * bits -- The number of bits to shift the MP number leftward. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Shift_Left_Bits(digit * number, int bits, int precision) +{ + assert(number != NULL); + assert(bits >= 0); + assert(precision > 0); + + if (bits == 0) return; /* shift zero bits is a no-op */ + + /* + ** If the shift is by whole bytes, then the shift operation can + ** be performed very quickly. + */ + if (bits == UNITSIZE) { + digit carry = 0; + while (precision--) { + digit temp = *number; + *number = carry; + carry = temp; + number++; + } + return; + } + + /* + ** If the number of bits to shift is less than one byte, then the + ** shift operation is a relatively simple "ripple" effect through + ** the MP number buffer. + */ + if (bits < UNITSIZE) { + digit carry = 0; + digit bitmask = ~(((digit)-1) >> bits); + int unbits = UNITSIZE - bits; /* shift bits must be <= UNITSIZE */ + + while (precision--) { + digit temp = *number & bitmask; + *number = (*number << bits) | (carry >> unbits); + carry = temp; + number++; + } + return; + } + + /* + ** General purpose slow left; + */ + int digits_to_shift = bits / UNITSIZE; + int bits_to_shift = bits % UNITSIZE; + + int index; + number += precision-1; + for (index = digits_to_shift; index < (precision-1); index++) { + *number = (*(number - digits_to_shift) << bits_to_shift) | (*(number - (digits_to_shift + 1)) >> (UNITSIZE - bits_to_shift)); + number--; + } + + if (digits_to_shift < precision) { + *number = (*(number - digits_to_shift) << bits_to_shift); + number--; + } + + for (index = 0; index < min(digits_to_shift, precision); index++) { + *number-- = 0; + } +} + + +/*********************************************************************************************** + * XMP_Rotate_Left -- Rotate specified MP number leftward. * + * * + * This routine will rotate the MP number to the left by one bit. The rotation passes bits * + * through a "carry" bit position. The initial value of this "carry" bit is passed to the * + * routine and the final value is returned as the result. * + * * + * INPUT: number -- Pointer to the MP number to perform the left rotate upon. * + * * + * carry -- The initial value of the "carry" bit. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: Returns with the final value of the carry bit. This is the the bit value of the * + * upper most bit of the MP number prior to the rotate operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rotate_Left(digit * number, bool carry, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + while (precision--) { + bool temp = ((*number & UPPER_MOST_BIT) != 0); + *number = (*number << 1); + if (carry) *number = *number + 1; + carry = temp; + number++; + } + return carry; +} + + +/*********************************************************************************************** + * XMP_Not -- Perform bitwise NOT operation on MP number. * + * * + * Perform a bitwise NOT operation. * + * * + * INPUT: number -- Pointer to the MP number to operate on. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Not(digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + for (int index = 0; index < precision; index++) { + *number = ~(*number); + number++; + } +} + + +/*********************************************************************************************** + * XMP_Init -- Initialize an MP number to a starting value. * + * * + * This will initialize (assign) a number to an MP number. The initial value is limited * + * to the precision allowed by a DIGIT type. * + * * + * INPUT: number -- Pointer to the MP number to initialize. * + * * + * value -- Initial integer value to assign to the MP number. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Init(digit * number, digit value, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + memset(number, '\0', precision * sizeof(digit)); + *number = value; +} + + +/*********************************************************************************************** + * XMP_Count_Bits -- Count the total number of bits (precision) in MP number. * + * * + * This routine will count the maximum number of bits used by this MP number. The result * + * could be referred to as the "bit precision" of the MP number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- The (digit) precision of the MP number. * + * * + * OUTPUT: Returns with the number of significant bits in the MP number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +unsigned XMP_Count_Bits(const digit * number, int precision) +{ + assert(number != NULL); + assert(precision > 0); + + int sub_precision = XMP_Significance(number, precision); + if (!sub_precision) return(0); + int total_bit_count = XMP_Digits_To_Bits(sub_precision); + number += sub_precision-1; + digit high_bit_mask = UPPER_MOST_BIT; + + while (!((*number) & high_bit_mask)) { + high_bit_mask >>= 1; + total_bit_count--; + } + + return(total_bit_count); +} + + +/*********************************************************************************************** + * XMP_Count_Bytes -- Counts the number of precision bytes in MP number. * + * * + * This routine will scan the MP number and determine the number of bytes needed to * + * represent the MP number. Consider the result the "byte precision" of the number. * + * * + * INPUT: number -- Pointer to the MP number to examine. * + * * + * precision-- Precision of the MP number. * + * * + * OUTPUT: Returns with the number of bytes required to represent the precision of the number.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Count_Bytes(const digit * number, int precision) +{ + unsigned char * ptr = (unsigned char *)number; + int count = 0; + for (unsigned index = 0; index < precision*sizeof(digit); index++) { + if (!*ptr) break; + count++; + ptr++; + } + return(count); +} + + +/*********************************************************************************************** + * XMP_Move -- Assign one MP number to another. * + * * + * This will move one MP number over the top of another. * + * * + * INPUT: dest -- Destination MP number (will get clobbered). * + * * + * source -- Source MP number. * + * * + * precision-- Precision of both MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Both MP numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Move(digit * dest, digit const * source, int precision) +{ + memcpy(dest, source, precision * sizeof(digit)); +} + + +/*********************************************************************************************** + * XMP_Compare -- Compare one MP number with another. * + * * + * This routine will compare two MP numbers. It will return a value indicating which MP * + * number is greater or if they are equal. * + * * + * INPUT: left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns -1 if the left_number is less than the right_number. * + * Returns 1 if the left_number is greater than the right number. * + * Returns 0 if both numbers are identical. * + * * + * WARNINGS: Both numbers must have the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Compare(const digit * left_number, const digit * right_number, int precision) +{ + left_number += precision-1; + right_number += precision-1; + do { + if (*left_number < *right_number) return -1; + if (*left_number > *right_number) return 1; + left_number--; + right_number--; + } while (--precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Add -- Add two MP numbers with a carry option. * + * * + * Use this routine to add one MP number to another. There is an optional "carry" value * + * that (when true) will add an additional 1 to the result. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. This can be the * + * same value as the left_number or right_number pointers. * + * * + * left_number -- The left hand MP number. * + * * + * right_number-- The right hand MP number. * + * * + * carry -- Optional carry flag (typically this will be false). * + * * + * precision -- The precision of the numbers involved. * + * * + * OUTPUT: Returns with the carry flag after the addition. If the value is true then an * + * overflow occurred. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add(digit * result, const digit * left_number, const digit * right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + *right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number++; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Add_Int -- Add an integer to an MP number (with carry). * + * * + * This routine will add an integer number to an MP number. There is an optional carry * + * parameter. If the carry flag is true, and additional 1 will be added to the result. * + * This routine is much faster than adding two MP numbers together. * + * * + * INPUT: result -- Pointer to the result MP number. This pointer can be the same as * + * the left_number parameter. * + * * + * left_number -- Pointer to the left hand MP number. * + * * + * right_number-- The integer number to add to the left hand number. * + * * + * carry -- Input carry flag. If this is true, then an additional one will be * + * added to the result. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: Returns with the result carry flag. A true value means the addition overflowed. * + * * + * WARNINGS: All MP numbers must share the same precision. Negative numbers are not * + * supported. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Add_Int(digit * result, const digit * left_number, digit right_number, bool carry, int precision) +{ + while (precision--) { + digit term = *left_number + right_number; + digit final = term + carry; + carry = (term < *left_number || (carry && final == 0)); + + right_number = 0; + left_number++; + *result++ = final; + } + return(carry); +} + + +/*********************************************************************************************** + * XMP_Sub -- Subtract one MP number from another (with borrow). * + * * + * This routine will subtract one MP number from another. There is an optional borrow * + * flag that can be specified. * + * * + * INPUT: result -- Pointer to the MP number that will hold the result. This pointer * + * can be the same as the left_number or right_number parameters. * + * * + * left_number -- The left hand number (value will be subtracted from this). * + * * + * right_number-- The right hand number (the value to subtract from the left number) * + * * + * borrow -- The optional borrow flag. If this flag is true, the an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow result flag. If the value is true, then an underflow * + * occurred during subtraction. * + * * + * WARNINGS: All MP numbers must share the same precision. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub(digit * result, const digit * left_number, const digit * right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + const unsigned short * right_number_ptr = (const unsigned short *)right_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - (digit) *right_number_ptr - (digit) borrow; + right_number_ptr++; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Sub_Int -- Subtract an integer from an MP number (with borrow). * + * * + * This will subtract an integer from the specified MP number. There is an optional borrow * + * flag available. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * left_number -- Pointer to the MP number that will be subtracted FROM. * + * * + * right_number-- The integer to subtract from the left hand number. * + * * + * borrow -- The optional borrow flag. If this value is true, then an extra one * + * will be subtracted from the result. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: Returns with the borrow flag of the result. If this value is true, then an * + * underflow occurred during subtraction. * + * * + * WARNINGS: The precision must be identical between the MP numbers involved. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Sub_Int(digit * result, const digit * left_number, unsigned short right_number, bool borrow, int precision) +{ + const unsigned short * left_number_ptr = (const unsigned short *)left_number; + unsigned short * result_ptr = (unsigned short *)result; + + precision *= 2; + while (precision--) { + digit x = (digit) *left_number_ptr - right_number - borrow; + left_number_ptr++; + *result_ptr++ = (unsigned short)x; + borrow = (((1L << 16) & x) != 0L); + + right_number = 0; + } + return (borrow); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult -- Multiply two unsigned MP numbers together. * + * * + * This routine will multiply two MP numbers together. The result will have the sum of the * + * significance of the two. * + * * + * INPUT: prod -- Pointer to the product MP buffer that will hold the result. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product will fit within the precision of the result. * + * * + * HISTORY: * + * 07/01/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Init(prod, 0, precision); + + /* + ** Multiplying by zero is always a zero product. + */ + if (XMP_Test_Eq_Int(multiplicand, 0, precision) || XMP_Test_Eq_Int(multiplier, 0, precision)) { + return 0; + } + + int total_bit_count = XMP_Count_Bits(multiplier, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + multiplier += sub_precision; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(prod, 1, precision); + + if ((*(multiplier-1)) & high_bit_mask) { + XMP_Add(prod, prod, multiplicand, 0, precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + multiplier--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Unsigned_Mult_Int -- Multiply an MP number by a simple integer. * + * * + * This is a very fast multiply since the multiplier is just an integer integral. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that is the multiplicand. * + * * + * multiplier -- The integral integer that is the multiplier. * + * * + * precision -- The precision of the MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fit in a signed integer (although it isn't a signed value). * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision) +{ + const unsigned short * m2 = (const unsigned short *)multiplicand; + unsigned short * pr = (unsigned short *)prod; + unsigned long carry = 0; + for (int i = 0; i < precision*2; ++i) { + unsigned long p = (((unsigned long)multiplier) * *m2) + carry;; + *pr = (unsigned short) p; + carry = p >> 16; + m2++; + pr++; + } + + /* Add carry to the next higher word of product / dividend */ +// *pr += (unsigned short)carry; + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult_Int -- Multiply an MP number by a signed simple integer. * + * * + * This will multiply the specified integer with the MP number. It is a much faster * + * multiply than when multiplying two MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number. * + * * + * multiplicand-- Pointer to the MP number that serves as the multiplicand. * + * * + * multiplier -- The simple integral integer used as the multiplier. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The multiplier must fist within a signed short integer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + } else { + XMP_Unsigned_Mult_Int(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (multiplier < 0) { + multiplier = (signed short)-multiplier; + + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult_Int(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Signed_Mult -- A signed multiply between two MP numbers. * + * * + * This routine will perform a multiply between two signed MP numbers. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is not a very fast routine. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + if (XMP_Is_Negative(multiplicand, precision)) { + digit abs_multiplicand[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplicand, multiplicand, precision); + XMP_Neg(abs_multiplicand, precision); + + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, abs_multiplicand, abs_multiplier, precision); + } else { + XMP_Unsigned_Mult(prod, abs_multiplicand, multiplier, precision); + XMP_Neg(prod, precision); + } + } else { + if (XMP_Is_Negative(multiplier, precision)) { + digit abs_multiplier[MAX_UNIT_PRECISION]; + XMP_Move(abs_multiplier, multiplier, precision); + XMP_Neg(abs_multiplier, precision); + + XMP_Unsigned_Mult(prod, multiplicand, abs_multiplier, precision); + XMP_Neg(prod, precision); + } else { + XMP_Unsigned_Mult(prod, multiplicand, multiplier, precision); + } + } + return(0); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div_Int -- Perform a short integer divide into an MP number. * + * * + * This routine performs a fast divide of the specified MP dividend by a simple * + * short integer. The division is an UNSIGNED division however. * + * * + * INPUT: quotient -- Pointer to the MP number buffer where the quotient will go. * + * * + * dividend -- Pointer to the MP number that serves as the dividend. * + * * + * divisor -- The simple signed short integer that serves as the divisor. * + * * + * precision -- The precision that is used by the MP numbers involved. * + * * + * OUTPUT: Returns with the remainder of the division. * + * * + * WARNINGS: This is an UNSIGNED divide even. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision) +{ + if (!divisor) return 0; /* zero divisor means divide error */ + + unsigned short remainder = 0; + + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + digit const * dividend_ptr = dividend + (digit_precision-1); + + if (!digit_precision) return(0); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + digit * quotient_ptr = quotient + (digit_precision-1); + + while (total_bit_count--) { + remainder <<= 1; + + if ((*dividend_ptr) & high_bit_mask) remainder++; + + if (remainder >= divisor) { + remainder -= divisor; + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + --dividend_ptr; + --quotient_ptr; + } + } + + return(remainder); +} + + +/*********************************************************************************************** + * XMP_Unsigned_Div -- Unsigned divide of one MP number into another. * + * * + * This will perform the (dog slow) divide of one MP number into another. Because of the * + * slowness of this routine, both the quotient and the remainder are available as a * + * result of the operation. * + * * + * INPUT: remainder -- Pointer to the MP buffer that will hold the remainder of the divide.* + * * + * quotient -- Pointer to the MP buffer that will hold the quotient of the divide. * + * * + * dividend -- The MP dividend (numerator) number. * + * * + * divisor -- The MP divisor (denominator) number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + // check for divide by zero. + if (XMP_Test_Eq_Int(divisor, 0, precision)) return(-1); + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + int total_bit_count = XMP_Count_Bits(dividend, precision); + int digit_precision = XMP_Bits_To_Digits(total_bit_count); + if (!digit_precision) return(0); + + digit const * dividend_ptr = dividend + (digit_precision-1); + digit * quotient_ptr = quotient + (digit_precision-1); + + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + + if (((*dividend_ptr) & high_bit_mask) != 0) { + XMP_Inc(remainder, precision); + } + + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient_ptr |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + dividend_ptr--; + quotient_ptr--; + } + + } + return 0; +} + + +/*********************************************************************************************** + * XMP_Signed_Div -- Signed divide of one MP number into another. * + * * + * This will perform a signed divide (very very slow) of one MP number into another. * + * Because of the slow nature of this routine, both the quotient and the remainder are * + * available as results. * + * * + * INPUT: remainder -- Pointer to the buffer that will hold the remainder of the divide. * + * * + * quotient -- Pointer to the buffer that will hold the quotient of the divide. * + * * + * dividend -- The dividend (numerator) MP number. * + * * + * divisor -- The divisor (denominator) MP number. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is very very slow. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision) +{ + bool negative = false; + + digit scratch_dividend[MAX_UNIT_PRECISION]; + XMP_Move(scratch_dividend, dividend, precision); + + digit scratch_divisor[MAX_UNIT_PRECISION]; + XMP_Move(scratch_divisor, divisor, precision); + + if (XMP_Is_Negative(scratch_dividend, precision)) { + XMP_Neg(scratch_dividend, precision); + negative = !negative; + } + + if (XMP_Is_Negative(scratch_divisor, precision)) { + XMP_Neg(scratch_divisor, precision); + negative = !negative; + } + + XMP_Unsigned_Div(remainder, quotient, scratch_dividend, scratch_divisor, precision); + + if (negative) { + XMP_Neg(quotient, precision); + if (!XMP_Test_Eq_Int(remainder, 0, precision)) { + XMP_Dec(quotient, precision); + XMP_Neg(remainder, precision); + XMP_Add(remainder, remainder, scratch_divisor, 0, precision); + } + } +} + + +/*********************************************************************************************** + * XMP_Inverse_A_Mod_B -- Inverts and performs modulus on an MP number. * + * * + * This is a utility routine that will perform an inverse on the MP number and then * + * perform a modulus of that number by another MP number. There are some algorithms that * + * require this process. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * number -- The MP number that will be inverted then modulo-ized. * + * * + * modulus -- The MP number to modulus the first number by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision) +{ + digit g[3][MAX_UNIT_PRECISION]; + XMP_Move(g[0], modulus, precision); + XMP_Move(g[1], number, precision); + + digit v[3][MAX_UNIT_PRECISION]; + XMP_Init(v[0], 0, precision); + XMP_Init(v[1], 1, precision); + + digit y[MAX_UNIT_PRECISION]; + + int i; + for (i = 1; !XMP_Test_Eq_Int(g[i%3], 0, precision); i++) { + XMP_Unsigned_Div(g[(i+1)%3], y, g[(i-1)%3], g[i%3], precision); + + XMP_Unsigned_Mult(result, v[i%3], y, precision); + XMP_Sub(v[(i+1)%3], v[(i-1)%3], result, 0, precision); + } + + if (XMP_Is_Negative(v[(i-1)%3], precision)) { + XMP_Add(v[(i-1)%3], v[(i-1)%3], modulus, 0, precision); + } + + XMP_Move(result, v[(i-1)%3], precision); +} + + +/*********************************************************************************************** + * XMP_Reciprocal -- Compute the reciprocal (inverse) of the MP number. * + * * + * Use this routine to determine the inverse of the specified MP number. The inverse is * + * defined as 1/number. * + * * + * INPUT: result -- Pointer to the result MP number buffer. * + * * + * number -- The number to be inverted. * + * * + * precision-- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision) +{ + digit remainder[MAX_UNIT_PRECISION]; + + if (XMP_Test_Eq_Int(divisor, 0, precision)) return -1; /* zero divisor means divide error */ + + XMP_Init(remainder, 0, precision); + XMP_Init(quotient, 0, precision); + + /* normalize and compute number of bits in quotient first */ + unsigned total_bit_count = XMP_Count_Bits(divisor, precision); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count + 1); /* bitmask within a single digit */ + int sub_precision = XMP_Bits_To_Digits(total_bit_count + 1); + + XMP_Set_Bit(remainder, total_bit_count - 1); + + /* rescale quotient to precision of divisor bits */ + quotient += sub_precision-1; + + while (total_bit_count--) { + XMP_Shift_Left_Bits(remainder, 1, precision); + if (XMP_Compare(remainder, divisor, precision) >= 0) { + XMP_Sub(remainder, remainder, divisor, 0, precision); + *quotient |= high_bit_mask; + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + quotient--; + } + } + + XMP_Init(remainder, 0, precision); + return 0; +} + + +/*********************************************************************************************** + * XMP_Decode_ASCII -- Convert ASCII into an MP number. * + * * + * This routine will convert a supplied ASCII string into an MP number. * + * * + * INPUT: str -- Pointer to the ASCII string that will be converted. * + * * + * mpn -- Pointer to the MP number buffer that will be initialized. * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision) +{ + /* + ** Initialize the multiprecision number to zero. From this point + ** onward, this object can be manipulated as a regular number. + ** This is, in fact, what is done as the ascii string is parsed + ** into a working number. + */ + XMP_Init(mpn, 0, precision); + + /* + ** No string or zero length is considered '0'. + */ + if (!str) return; + int i = strlen(str); + if (i == 0) return; + + unsigned short radix; /* base 2-16 */ + switch (toupper(str[i-1])) { /* classify radix select suffix character */ + case '.': + radix = 10; + break; + + case 'H': + radix = 16; + break; + + case 'O': + radix = 8; + break; + + case 'B': /* caution! 'b' is a hex digit! */ + radix = 2; + break; + + default: + radix = 10; + break; + } + + bool minus = (*str == '-'); + if (minus) str++; + + digit c; + while ((c = (unsigned char)*str++) != 0) { + if (c == ',') continue; /* allow commas in number */ + + /* + ** If not a hexadecimal (highest base) digit then it is + ** clearly the end of the processable string. Bail out + ** of the scan loop. + */ + if (!isxdigit((char)c)) break; + + /* + ** Convert the character into an integer number 0 through 15. + */ + if (isdigit((char)c)) { + c -= '0'; + } else { + c = (unsigned char)(toupper((char)c) - 'A') + 10; + } + + /* + ** If the integer digit is greater than the radix, then we + ** know that further processing should stop. This is the + ** end of the number string. + */ + if (c >= radix) break; /* scan terminated by any non-digit */ + + + XMP_Unsigned_Mult_Int(mpn, mpn, radix, precision); + XMP_Add_Int(mpn, mpn, c, 0, precision); + } + if (minus) { + XMP_Neg(mpn, precision); + } +} + + +/*********************************************************************************************** + * XMP_Hybrid_Mul -- Special hybrid short multiply (with carry). * + * * + * Multiply the single-word multiplier times the multiprecision integer * + * in multiplicand, accumulating result in prod. The resulting * + * multiprecision prod will be 1 word longer than the multiplicand. * + * multiplicand is double precision words long. We add into prod, so caller * + * should zero it out first. For best results, this time-critical * + * function should be implemented in assembly. * + * NOTE: Unlike other functions in the multiprecision arithmetic * + * library, both multiplicand and prod are pointing at the LSB, * + * regardless of byte order of the machine. On an 80x86, this makes * + * no difference. But if this assembly function is implemented * + * on a 680x0, it becomes important. * + * * + * Note that this has been modified from the previous version to allow * + * better support for Smith's modmult: * + * The final carry bit is added to the existing product * + * array, rather than simply stored. * + * * + * INPUT: prod -- Pointer to the product MP number buffer. * + * * + * multiplicand -- Pointer to the multiplicand MP number. * + * * + * multiplier -- The short integer used as the multiplier. * + * * + * precision -- The precision of the MP number used. * + * * + * OUTPUT: none * + * * + * WARNINGS: The carry (if any) is added into the integer one beyond the end of the * + * product buffer. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Hybrid_Mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision) +{ + unsigned long carry = 0; + for (int i = 0; i < precision; ++i) { + unsigned long p = (unsigned long)multiplier * *multiplicand++; + p += *prod + carry; + *prod++ = (unsigned short) p; + carry = p >> 16; + } + + /* Add carry to the next higher word of product / dividend */ + *prod += (unsigned short) carry; +} + + +/*********************************************************************************************** + * XMP_Double_Mul -- Double precision MP multiply. * + * * + * This will perform a double precision multiply of MP numbers. This means that the product * + * will be twice the precision of the components. * + * * + * INPUT: prod -- Pointer to the result buffer. This buffer must be able to hold * + * double the precision specified. * + * * + * multiplicand-- Pointer to the multiplicand MP number. * + * * + * multiplier -- Pointer to the multiplier number. * + * * + * precision -- The precision of the two component MP numbers. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the product buffer can hold a double precision number. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + /* + ** Clear out the double precision product buffer. + */ + XMP_Init(prod, 0, precision*2); + + const unsigned short * multiplier_ptr = (const unsigned short *) multiplier; + unsigned short * product_ptr = (unsigned short *) prod; + + // Multiply multiplicand by each word in multiplier, accumulating prod. + for (int i = 0; i < precision*2; ++i) { + XMP_Hybrid_Mul(product_ptr++, (unsigned short *)multiplicand, *multiplier_ptr++, precision*2); + } +} + + + +static int _modulus_shift; // number of bits for recip scaling +static unsigned short _reciprical_high_digit; // MSdigit of scaled recip +static unsigned short _reciprical_low_digit; // LSdigit of scaled recip + +static int _modulus_sub_precision; // length of modulus in MULTUNITs +static int _modulus_bit_count; // number of modulus significant bits +static digit _scratch_modulus[MAX_UNIT_PRECISION]; // modulus + +// The double precision modulus staging buffer. +static digit _double_staging_number[MAX_UNIT_PRECISION * 2 + 2]; + +// most significant digits of modulus. +static digit _mod_quotient[4]; +static digit _mod_divisor[4]; + + +/*********************************************************************************************** + * XMP_Prepare_Modulus -- Prepare globals for modulus operation. * + * * + * Calculate the reciprocal of modulus with a precision of two MULTUNITs. * + * Assumes that precision has already been adjusted to the * + * size of the modulus, plus SLOP_BITS. * + * * + * Note: This routine was designed to work with large values and * + * doesn't have the necessary testing or handling to work with a * + * modulus having less than three significant digits. For such cases, * + * the separate multiply and modulus routines can be used. * + * * + * INPUT: modulus -- Pointer to the modulus number. * + * * + * precision-- The precision of the modulus number. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Prepare_Modulus(const digit * n_modulus, int precision) +{ + XMP_Move(_scratch_modulus, n_modulus, precision); + + _modulus_bit_count = XMP_Count_Bits(_scratch_modulus, precision); + _modulus_sub_precision = (_modulus_bit_count + 16 - 1) / 16; + + /* + ** Keep 2*16 bits in _mod_divisor. + ** This will (normally) result in a reciprocal of 2*16+1 bits. + */ + int sub_precision = XMP_Significance(_scratch_modulus, precision); // significant digits in modulus + XMP_Move(_mod_divisor, &_scratch_modulus[sub_precision-2], 2); + _modulus_shift = XMP_Count_Bits(_mod_divisor, 2) - 2 * 16; + XMP_Shift_Right_Bits(_mod_divisor, _modulus_shift, 2); + + XMP_Reciprocal(_mod_quotient, _mod_divisor, 2); + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + + /* Reduce to: 0 < _modulus_shift <= 16 */ + _modulus_shift = ((_modulus_shift + (16 - 1)) % 16) + 1; + + /* round up */ + XMP_Inc(_mod_quotient, 2); + if (XMP_Count_Bits(_mod_quotient, 2) > 2 * 16) { + XMP_Shift_Right_Bits(_mod_quotient, 1, 2); + _modulus_shift--; /* now 0 <= _modulus_shift <= 16 */ + } + unsigned short * mpm = (unsigned short *) _mod_quotient; + _reciprical_low_digit = *mpm++; + _reciprical_high_digit = *mpm; + + return 0; +} + + +/*********************************************************************************************** + * XMP_Mod_Mult -- Perform a multiply - modulus operation. * + * * + * This routine will combine a multiply and a modulus operation. This takes advantage of * + * a tremendous speed advantage possible if these two processes are combined rather than * + * being performed separately. * + * * + * INPUT: prod -- Pointer to the MP buffer that will hold the result. * + * * + * multiplicand-- The number to multiply. * + * * + * multiplier -- The number to multiply by. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: The modulus must already have been prepared by the routine XMP_Prepare_Modulus. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision) +{ + XMP_Double_Mul(_double_staging_number, multiplicand, multiplier, precision); + + int double_precision = precision * 2 + 1; + + _double_staging_number[double_precision - 1] = 0; /* leading 0 digit */ + + /* + ** We now start working with MULTUNITs. + ** Determine the most significant MULTUNIT of the product so we don't + ** have to process leading zeros in our divide loop. + */ + int dmi = XMP_Significance(_double_staging_number, double_precision) * 2; // number of significant MULTUNITs in product + + if (dmi >= _modulus_sub_precision) { + + /* Make dividend negative. This allows the use of mp_single_mul to + ** "subtract" the product of the modulus and the trial divisor + ** by actually adding to a negative dividend. + ** The one's complement of the dividend is used, since it causes + ** a zero value to be represented as all ones. This facilitates + ** testing the result for possible overflow, since a sign bit + ** indicates that no adjustment is necessary, and we should not + ** attempt to adjust if the result of the addition is zero. + */ + XMP_Inc(_double_staging_number, double_precision); + XMP_Neg(_double_staging_number, double_precision); + + int nqd = dmi + 1 - _modulus_sub_precision; // number of quotient digits remaining to be generated + + /* Set msb, lsb, and normal ptrs of dividend */ + unsigned short * dmph = ((unsigned short *)_double_staging_number) + dmi + 1; // points to one higher than precision would indicate + unsigned short * dmpl = dmph - _modulus_sub_precision; + + /* + ** Divide loop. + ** Each iteration computes the next quotient MULTUNIT digit, then + ** multiplies the divisor (modulus) by the quotient digit and adds + ** it to the one's complement of the dividend (equivalent to + ** subtracting). If the product was greater than the remaining dividend, + ** we get a non-negative result, in which case we subtract off the + ** modulus to get the proper negative remainder. + */ + for (; nqd; nqd--) { + --dmph; + --dmpl; + + unsigned short q = mp_quo_digit(dmph); // trial quotient digit + if (q > 0) { + XMP_Hybrid_Mul(dmpl, (unsigned short *)_scratch_modulus, q, precision*2); + + /* Perform correction if q too large. + ** This rarely occurs. + */ + if (!(*dmph & SEMI_UPPER_MOST_BIT)) { + unsigned short * dmp = dmpl; + if (XMP_Sub((unsigned long *)dmp, (unsigned long *)dmp, _scratch_modulus, false, precision)) { + (*dmph)--; + } + } + } + } + + /* d contains the one's complement of the remainder. */ + XMP_Neg(_double_staging_number, precision); + XMP_Dec(_double_staging_number, precision); + } + + XMP_Move(prod, _double_staging_number, precision); + return (0); +} + + +/*********************************************************************************************** + * XMP_Mod_Mult_Clear -- Remove temporary values from memory. * + * * + * Smith's mp_modmult function leaves some internal arrays in memory, * + * so we have to call modmult_burn() at the end of mp_exponent_mod. * + * This is so that no cryptographically sensitive data is left in memory * + * after the program exits. * + * * + * INPUT: precision -- The precision of the numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Mod_Mult_Clear(int precision) +{ + XMP_Init(_scratch_modulus, 0, precision); + XMP_Init(_double_staging_number, 0, precision); + XMP_Init(_mod_quotient, 0, ARRAY_SIZE(_mod_quotient)); + XMP_Init(_mod_divisor, 0, ARRAY_SIZE(_mod_divisor)); + _modulus_shift = _modulus_bit_count = 0; + _reciprical_high_digit = _reciprical_low_digit = 0; + _modulus_sub_precision = /*mutemp =*/ 0; +} + + +/* +** The function mp_quo_digit is the heart of Smith's modulo reduction, +** which uses a form of long division. It computes a trial quotient +** "digit" (MULTUNIT-sized digit) by multiplying the three most +** significant MULTUNITs of the dividend by the two most significant +** MULTUNITs of the reciprocal of the modulus. Note that this function +** requires that 16 * 2 <= sizeof(unsigned long). +** +** An important part of this technique is that the quotient never be +** too small, although it may occasionally be too large. This was +** done to eliminate the need to check and correct for a remainder +** exceeding the divisor. It is easier to check for a negative +** remainder. The following technique rarely needs correction for +** MULTUNITs of at least 16 bits. +** +** The following routine has two implementations: +** +** Parameter: dividend - points to the most significant MULTUNIT +** of the dividend. Note that dividend actually contains the +** one's complement of the actual dividend value (see comments for +** XMP_Mod_Mult). +** +** Return: the trial quotient digit resulting from dividing the first +** three MULTUNITs at dividend by the upper two MULTUNITs of the +** modulus. +*/ +unsigned short mp_quo_digit(unsigned short * dividend) +{ + unsigned long q, q0, q1, q2; + + /* + * Compute the least significant product group. + * The last terms of q1 and q2 perform upward rounding, which is + * needed to guarantee that the result not be too small. + */ + q1 = (dividend[-2] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit + _reciprical_high_digit; + q2 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit + (1L << 16); + q0 = (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the middle significant product group. */ + q1 = (dividend[-1] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit; + q2 = (dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_low_digit; + q = (q0 >> 16) + (q1 >> 1) + (q2 >> 1) + 1; + + /* Compute the most significant term and add in the others */ + q = (q >> (16 - 2)) + (((dividend[0] ^ SEMI_MASK) * (unsigned long) _reciprical_high_digit) << 1); + q >>= _modulus_shift; + + /* Prevent overflow and then wipe out the intermediate results. */ + return (unsigned short) min(q, (unsigned long)(1L << 16) - 1); +} + + +/* +** Russian peasant combined exponentiation/modulo algorithm. +** Calls modmult instead of mult. +** Computes: expout = (expin**exponent) mod modulus +** WARNING: All the arguments must be less than the modulus! +*/ +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision) +{ + digit product[MAX_UNIT_PRECISION]; + + XMP_Init(expout, 1, precision); + if (XMP_Test_Eq_Int(exponent_ptr, 0, precision)) { + if (XMP_Test_Eq_Int(expin, 0, precision)) { + return -1; /* 0 to the 0th power means return error */ + } + return 0; /* otherwise, zero exponent means expout is 1 */ + } + + if (XMP_Test_Eq_Int(modulus, 0, precision)) { + return -2; /* zero modulus means error */ + } + + if (XMP_Compare(expin, modulus, precision) >= 0) { + return -3; /* if expin >= modulus, return error */ + } + + if (XMP_Compare(exponent_ptr, modulus, precision) >= 0) { + return -4; /* if exponent >= modulus, return error */ + } + + /* set smallest optimum precision for this modulus */ + int limited_precision = XMP_Significance(modulus, precision); + + if (XMP_Prepare_Modulus(modulus, limited_precision)) { + return -5; /* unstageable modulus (STEWART algorithm) */ + } + + /* normalize and compute number of bits in exponent first */ +// int exp_precision = XMP_Significance(exponent_ptr, limited_precision); +// if (!exp_precision) return(0); +// int bits = XMP_Digits_To_Bits(exp_precision); +// exponent_ptr += (exp_precision-1); +// digit high_bit_mask = UPPER_MOST_BIT; +// while (! ((*exponent_ptr) & high_bit_mask)) { +// high_bit_mask >>= 1; +// bits--; +// } + + int total_bit_count = XMP_Count_Bits(exponent_ptr, limited_precision); + int sub_precision = XMP_Bits_To_Digits(total_bit_count); + if (!sub_precision) return(0); + digit high_bit_mask = XMP_Bits_To_Mask(total_bit_count); + exponent_ptr += (sub_precision-1); + + /* We can "optimize out" the first modsquare and modmult: */ + total_bit_count--; /* We know for sure at this point that bits>0 */ + + XMP_Move(expout, expin, limited_precision); + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + while (total_bit_count--) { + XMP_Mod_Mult(product, expout, expout, limited_precision); + + if (((*exponent_ptr) & high_bit_mask)) { + XMP_Mod_Mult(expout, product, expin, limited_precision); + } else { + XMP_Move(expout, product, limited_precision); + } + + high_bit_mask >>= 1; + if (!high_bit_mask) { + high_bit_mask = UPPER_MOST_BIT; + exponent_ptr--; + } + + } + + XMP_Init(product, 0, limited_precision); + XMP_Mod_Mult_Clear(limited_precision); /* ask mp_modmult to also burn its own evidence */ + + return 0; +} + + +/*********************************************************************************************** + * memrev -- Reverse the byte order of the buffer specified. * + * * + * This routine will reverse the byte order in the buffer specified. * + * * + * INPUT: buffer -- Pointer to the buffer that will be reversed. * + * * + * length -- The length of the buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void memrev(char * buffer, size_t length) +{ + char * r2 = &(buffer[length - 1]); + while (buffer < r2) { + char b = *buffer; + *buffer++ = *r2; + *r2-- = b; + } +} + + +int _USERENTRY pfunc(const void * pkey, const void * base) +{ + if (*(unsigned short *)pkey < *(unsigned short *)base) return(-1); + if (*(unsigned short *)pkey > *(unsigned short *)base) return(1); + return(0); +} + + +/*********************************************************************************************** + * XMP_Is_Small_Prime -- Determine if MP number is a small prime. * + * * + * This routine will compare the MP number against all known small prime numbers. It will * + * return true if a match was found. * + * * + * INPUT: candidate -- Pointer to MP number that is to be tested. * + * * + * precision -- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the MP number a member of the small prime community? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Small_Prime(const digit * candidate, int precision) +{ + /* + ** If the number is too large for comparison to the known small primes table, then + ** bail immediately. + */ + if (XMP_Significance(candidate, precision) > 1) return(false); + if (*candidate > primeTable[ARRAY_SIZE(primeTable)-1]) return false; + + unsigned long * ptr = (unsigned long *)bsearch(&candidate, &primeTable[0], ARRAY_SIZE(primeTable), sizeof(primeTable[0]), pfunc); + return(ptr != NULL); +} + + +/*********************************************************************************************** + * XMP_Small_Divisors_Test -- Perform the small divisors test on an MP number. * + * * + * This test for primality will divide an MP number by the set of small primes. If any of * + * these numbers divides evenly into the candidate number, then it is known that the * + * candidate is NOT prime. * + * * + * INPUT: candidate -- Pointer to the MP number that is to be tested. * + * * + * precision -- The precision of the MP number/ * + * * + * OUTPUT: bool; Did the MP number pass the small divisors test? * + * * + * WARNINGS: If the MP number passes, it doesn't mean that it is prime, just that is hasn't * + * yet been proven to be not prime. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Small_Divisors_Test(const digit * candidate, int precision) +{ + digit quotient[MAX_UNIT_PRECISION]; + + for (unsigned i = 0; i < ARRAY_SIZE(primeTable); i++) { + if (XMP_Unsigned_Div_Int(quotient, candidate, primeTable[i], precision) == 0) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Fermat_Test -- Performs Fermat's Little Theorem on an MP number. * + * * + * This is a more expensive but thorough test for primality. The aggressiveness of this * + * test can be controlled by the number of rounds specified. Four rounds is usually * + * sufficient. * + * * + * INPUT: candidate -- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of rounds to test the MP number (keep it small). * + * * + * precision -- The precision of the MP number. * + * * + * OUTPUT: bool; Was the number not proven to be not prime. A FALSE means that it is not * + * prime. A TRUE means that it might be prime. * + * * + * WARNINGS: This takes a bit of time. The time it takes is directly controlled by the * + * number of rounds specified. Keep the number of rounds as small as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision) +{ + assert(rounds < ARRAY_SIZE(primeTable)); + + digit term[MAX_UNIT_PRECISION]; + XMP_Move(term, candidate_prime, precision); + XMP_Dec(term, precision); + + for (unsigned i = 0; i < rounds; i++) { + // if ((x**(p-1)) mod p) != 1, then p is not prime + digit result[MAX_UNIT_PRECISION]; + + digit small_prime[MAX_UNIT_PRECISION]; + XMP_Init(small_prime, primeTable[i], precision); + + xmp_exponent_mod(result, small_prime, term, candidate_prime, precision); + + if (!XMP_Test_Eq_Int(result, 1, precision)) return(false); + } + return(true); +} + + +/*********************************************************************************************** + * XMP_Rabin_Miller_Test -- Performs the Rabin Miller test for primality. * + * * + * This test for primality is even more expensive the Fermat's Little Theorem. It doesn't * + * prove that a number is prime, but it can prove that it is not prime. * + * * + * INPUT: rng -- Reference to to a random number generator. * + * * + * candidate-- Pointer to the candidate MP number that is to be tested. * + * * + * rounds -- The number of test rounds to perform. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? A FALSE means that the number is * + * not prime. A TRUE means that it might be. * + * * + * WARNINGS: This routine takes a long time. Use as few rounds as possible. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision) +{ + digit wminus1[MAX_UNIT_PRECISION]; + XMP_Sub_Int(wminus1, w, 1, 0, precision); + + unsigned maxbitprecision = precision * sizeof(digit) * 8; + unsigned a; + for (a = 0; a < maxbitprecision; a++) { + if (XMP_Test_Bit(wminus1, a)) { + break; + } + } + + digit m[MAX_UNIT_PRECISION]; + XMP_Move(m, wminus1, precision); + XMP_Shift_Right_Bits(wminus1, a, precision); + + for (int i = 0; i < rounds; i++) { + digit b[MAX_UNIT_PRECISION]; + digit temp[MAX_UNIT_PRECISION]; + XMP_Init(temp, 2, precision); + XMP_Randomize(b, rng, temp, wminus1, precision); + + digit z[MAX_UNIT_PRECISION]; + xmp_exponent_mod(z, b, m, w, precision); + + if (XMP_Test_Eq_Int(z, 1, precision) || XMP_Compare(z, wminus1, precision) == 0) { + continue; // passes this round + } + + int j; + for (j = 1; j < (int)a; j++) { + digit t2[MAX_UNIT_PRECISION]; + xmp_exponent_mod(t2, z, temp, w, precision); + + if (XMP_Compare(t2, wminus1, precision) == 0) { + break; // passed this round + } + if (XMP_Test_Eq_Int(z, 1, precision)) { + return false; + } + } + if (j == a) { + return false; + } + } + return true; +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number. * + * * + * This routine will generate a random MP number with the number of bits precision * + * specified. This is the starting point for generating large random prime numbers. It is * + * very important that the random number generated is truly random. * + * * + * INPUT: result -- Pointer to the buffer that will hold the MP number. * + * * + * rng -- Reference to a random number generator. * + * * + * total_bits-- The number of bits precision that the MP number must have. * + * * + * precision-- The precision of the MP number to be generated (maximum) * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, int total_bits, int precision) +{ + assert(XMP_Bits_To_Digits(total_bits) <= MAX_UNIT_PRECISION); + + total_bits = min(total_bits, precision * 32); + + unsigned nbytes = total_bits/8 + 1; + + XMP_Init(result, 0, precision); + rng.Get(result, nbytes); + + ((unsigned char *)result)[nbytes-1] &= (unsigned char)(~((~0) << (total_bits % 8))); +} + + +/*********************************************************************************************** + * XMP_Randomize -- Generate a random MP number between the boundaries specified. * + * * + * This routine will generate a random MP number but it will be bounded by the minimum * + * and maximum MP numbers specified. * + * * + * INPUT: result -- Pointer to the MP buffer that will hold the result. * + * * + * rng -- Reference to a random number generator to use. * + * * + * minval -- Minimum value allowed. * + * * + * maxval -- Maximum value allowed. * + * * + * precision -- The precision of the MP numbers involved. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +void XMP_Randomize(digit * result, Straw & rng, digit const * minval, digit const * maxval, int precision) +{ + digit range[MAX_UNIT_PRECISION]; + XMP_Sub(range, maxval, minval, 0, precision); + unsigned int bit_count = XMP_Count_Bits(range, precision); + do { + XMP_Randomize(result, rng, bit_count, precision); + } while (XMP_Compare(result, range, precision) > 0); + + XMP_Add(result, result, minval, 0, precision); +} + + +/*********************************************************************************************** + * XMP_Is_Prime -- Determine if the specified MP number is prime. * + * * + * This routine will perform some checks to try and determine if the specified MP number * + * is a prime number. The result of this test is not 100% conclusive, but it is pretty * + * darn close. * + * * + * INPUT: prime -- Pointer to a candidate number to test for primality. * + * * + * precision-- The precision of the MP number specified. * + * * + * OUTPUT: bool; Was the number not proven to be not prime? If FALSE, then the number is * + * not prime. If TRUE, then it might be. * + * * + * WARNINGS: This can take a very very very very very long time. Especially for the larger * + * numbers. * + * * + * HISTORY: * + * 07/02/1996 JLB : Created. * + *=============================================================================================*/ +bool XMP_Is_Prime(digit const * prime, int precision) +{ + /* + ** Even numbers are ALWAYS not prime. + */ + if (!(*prime & 0x01)) return(false); + + /* + ** Compare the prime number against the exhaustive list of prime + ** numbers below 14 bits in size. If it finds a match, then + ** the number is a known prime. + */ + if (XMP_Is_Small_Prime(prime, precision)) return(true); + + /* + ** Perform the small divisors test. This is not exhaustive, but + ** will weed out a large percentage of non-prime numbers. + */ + if (!XMP_Small_Divisors_Test(prime, precision)) return(false); + + /* + ** Perform Fermat's Little Theorum on the candidate prime. Run + ** the theorum for several rounds to ensure a high degree of + ** confidence. + */ + if (!XMP_Fermat_Test(prime, 2, precision)) return(false); + + /* + ** If all of the above tests have not confirmed primality nor + ** confirmed non-primality, presume that the number must be prime. + */ + return(true); +} + + +/* +** Complete list of all prime numbers that are less than 32719 (inclusive). +*/ +unsigned short primeTable[3511] = { + 0x0002,0x0003,0x0005,0x0007,0x000B,0x000D,0x0011,0x0013,0x0017,0x001D,0x001F,0x0025,0x0029,0x002B,0x002F,0x0035, + 0x003B,0x003D,0x0043,0x0047,0x0049,0x004F,0x0053,0x0059,0x0061,0x0065,0x0067,0x006B,0x006D,0x0071,0x007F,0x0083, + 0x0089,0x008B,0x0095,0x0097,0x009D,0x00A3,0x00A7,0x00AD,0x00B3,0x00B5,0x00BF,0x00C1,0x00C5,0x00C7,0x00D3,0x00DF, + 0x00E3,0x00E5,0x00E9,0x00EF,0x00F1,0x00FB,0x0101,0x0107,0x010D,0x010F,0x0115,0x0119,0x011B,0x0125,0x0133,0x0137, + 0x0139,0x013D,0x014B,0x0151,0x015B,0x015D,0x0161,0x0167,0x016F,0x0175,0x017B,0x017F,0x0185,0x018D,0x0191,0x0199, + 0x01A3,0x01A5,0x01AF,0x01B1,0x01B7,0x01BB,0x01C1,0x01C9,0x01CD,0x01CF,0x01D3,0x01DF,0x01E7,0x01EB,0x01F3,0x01F7, + 0x01FD,0x0209,0x020B,0x021D,0x0223,0x022D,0x0233,0x0239,0x023B,0x0241,0x024B,0x0251,0x0257,0x0259,0x025F,0x0265, + 0x0269,0x026B,0x0277,0x0281,0x0283,0x0287,0x028D,0x0293,0x0295,0x02A1,0x02A5,0x02AB,0x02B3,0x02BD,0x02C5,0x02CF, + 0x02D7,0x02DD,0x02E3,0x02E7,0x02EF,0x02F5,0x02F9,0x0301,0x0305,0x0313,0x031D,0x0329,0x032B,0x0335,0x0337,0x033B, + 0x033D,0x0347,0x0355,0x0359,0x035B,0x035F,0x036D,0x0371,0x0373,0x0377,0x038B,0x038F,0x0397,0x03A1,0x03A9,0x03AD, + 0x03B3,0x03B9,0x03C7,0x03CB,0x03D1,0x03D7,0x03DF,0x03E5,0x03F1,0x03F5,0x03FB,0x03FD,0x0407,0x0409,0x040F,0x0419, + 0x041B,0x0425,0x0427,0x042D,0x043F,0x0443,0x0445,0x0449,0x044F,0x0455,0x045D,0x0463,0x0469,0x047F,0x0481,0x048B, + 0x0493,0x049D,0x04A3,0x04A9,0x04B1,0x04BD,0x04C1,0x04C7,0x04CD,0x04CF,0x04D5,0x04E1,0x04EB,0x04FD,0x04FF,0x0503, + 0x0509,0x050B,0x0511,0x0515,0x0517,0x051B,0x0527,0x0529,0x052F,0x0551,0x0557,0x055D,0x0565,0x0577,0x0581,0x058F, + 0x0593,0x0595,0x0599,0x059F,0x05A7,0x05AB,0x05AD,0x05B3,0x05BF,0x05C9,0x05CB,0x05CF,0x05D1,0x05D5,0x05DB,0x05E7, + 0x05F3,0x05FB,0x0607,0x060D,0x0611,0x0617,0x061F,0x0623,0x062B,0x062F,0x063D,0x0641,0x0647,0x0649,0x064D,0x0653, + 0x0655,0x065B,0x0665,0x0679,0x067F,0x0683,0x0685,0x069D,0x06A1,0x06A3,0x06AD,0x06B9,0x06BB,0x06C5,0x06CD,0x06D3, + 0x06D9,0x06DF,0x06F1,0x06F7,0x06FB,0x06FD,0x0709,0x0713,0x071F,0x0727,0x0737,0x0745,0x074B,0x074F,0x0751,0x0755, + 0x0757,0x0761,0x076D,0x0773,0x0779,0x078B,0x078D,0x079D,0x079F,0x07B5,0x07BB,0x07C3,0x07C9,0x07CD,0x07CF,0x07D3, + 0x07DB,0x07E1,0x07EB,0x07ED,0x07F7,0x0805,0x080F,0x0815,0x0821,0x0823,0x0827,0x0829,0x0833,0x083F,0x0841,0x0851, + 0x0853,0x0859,0x085D,0x085F,0x0869,0x0871,0x0883,0x089B,0x089F,0x08A5,0x08AD,0x08BD,0x08BF,0x08C3,0x08CB,0x08DB, + 0x08DD,0x08E1,0x08E9,0x08EF,0x08F5,0x08F9,0x0905,0x0907,0x091D,0x0923,0x0925,0x092B,0x092F,0x0935,0x0943,0x0949, + 0x094D,0x094F,0x0955,0x0959,0x095F,0x096B,0x0971,0x0977,0x0985,0x0989,0x098F,0x099B,0x09A3,0x09A9,0x09AD,0x09C7, + 0x09D9,0x09E3,0x09EB,0x09EF,0x09F5,0x09F7,0x09FD,0x0A13,0x0A1F,0x0A21,0x0A31,0x0A39,0x0A3D,0x0A49,0x0A57,0x0A61, + 0x0A63,0x0A67,0x0A6F,0x0A75,0x0A7B,0x0A7F,0x0A81,0x0A85,0x0A8B,0x0A93,0x0A97,0x0A99,0x0A9F,0x0AA9,0x0AAB,0x0AB5, + 0x0ABD,0x0AC1,0x0ACF,0x0AD9,0x0AE5,0x0AE7,0x0AED,0x0AF1,0x0AF3,0x0B03,0x0B11,0x0B15,0x0B1B,0x0B23,0x0B29,0x0B2D, + 0x0B3F,0x0B47,0x0B51,0x0B57,0x0B5D,0x0B65,0x0B6F,0x0B7B,0x0B89,0x0B8D,0x0B93,0x0B99,0x0B9B,0x0BB7,0x0BB9,0x0BC3, + 0x0BCB,0x0BCF,0x0BDD,0x0BE1,0x0BE9,0x0BF5,0x0BFB,0x0C07,0x0C0B,0x0C11,0x0C25,0x0C2F,0x0C31,0x0C41,0x0C5B,0x0C5F, + 0x0C61,0x0C6D,0x0C73,0x0C77,0x0C83,0x0C89,0x0C91,0x0C95,0x0C9D,0x0CB3,0x0CB5,0x0CB9,0x0CBB,0x0CC7,0x0CE3,0x0CE5, + 0x0CEB,0x0CF1,0x0CF7,0x0CFB,0x0D01,0x0D03,0x0D0F,0x0D13,0x0D1F,0x0D21,0x0D2B,0x0D2D,0x0D3D,0x0D3F,0x0D4F,0x0D55, + 0x0D69,0x0D79,0x0D81,0x0D85,0x0D87,0x0D8B,0x0D8D,0x0DA3,0x0DAB,0x0DB7,0x0DBD,0x0DC7,0x0DC9,0x0DCD,0x0DD3,0x0DD5, + 0x0DDB,0x0DE5,0x0DE7,0x0DF3,0x0DFD,0x0DFF,0x0E09,0x0E17,0x0E1D,0x0E21,0x0E27,0x0E2F,0x0E35,0x0E3B,0x0E4B,0x0E57, + 0x0E59,0x0E5D,0x0E6B,0x0E71,0x0E75,0x0E7D,0x0E87,0x0E8F,0x0E95,0x0E9B,0x0EB1,0x0EB7,0x0EB9,0x0EC3,0x0ED1,0x0ED5, + 0x0EDB,0x0EED,0x0EEF,0x0EF9,0x0F07,0x0F0B,0x0F0D,0x0F17,0x0F25,0x0F29,0x0F31,0x0F43,0x0F47,0x0F4D,0x0F4F,0x0F53, + 0x0F59,0x0F5B,0x0F67,0x0F6B,0x0F7F,0x0F95,0x0FA1,0x0FA3,0x0FA7,0x0FAD,0x0FB3,0x0FB5,0x0FBB,0x0FD1,0x0FD3,0x0FD9, + 0x0FE9,0x0FEF,0x0FFB,0x0FFD,0x1003,0x100F,0x101F,0x1021,0x1025,0x102B,0x1039,0x103D,0x103F,0x1051,0x1069,0x1073, + 0x1079,0x107B,0x1085,0x1087,0x1091,0x1093,0x109D,0x10A3,0x10A5,0x10AF,0x10B1,0x10BB,0x10C1,0x10C9,0x10E7,0x10F1, + 0x10F3,0x10FD,0x1105,0x110B,0x1115,0x1127,0x112D,0x1139,0x1145,0x1147,0x1159,0x115F,0x1163,0x1169,0x116F,0x1181, + 0x1183,0x118D,0x119B,0x11A1,0x11A5,0x11A7,0x11AB,0x11C3,0x11C5,0x11D1,0x11D7,0x11E7,0x11EF,0x11F5,0x11FB,0x120D, + 0x121D,0x121F,0x1223,0x1229,0x122B,0x1231,0x1237,0x1241,0x1247,0x1253,0x125F,0x1271,0x1273,0x1279,0x127D,0x128F, + 0x1297,0x12AF,0x12B3,0x12B5,0x12B9,0x12BF,0x12C1,0x12CD,0x12D1,0x12DF,0x12FD,0x1307,0x130D,0x1319,0x1327,0x132D, + 0x1337,0x1343,0x1345,0x1349,0x134F,0x1357,0x135D,0x1367,0x1369,0x136D,0x137B,0x1381,0x1387,0x138B,0x1391,0x1393, + 0x139D,0x139F,0x13AF,0x13BB,0x13C3,0x13D5,0x13D9,0x13DF,0x13EB,0x13ED,0x13F3,0x13F9,0x13FF,0x141B,0x1421,0x142F, + 0x1433,0x143B,0x1445,0x144D,0x1459,0x146B,0x146F,0x1471,0x1475,0x148D,0x1499,0x149F,0x14A1,0x14B1,0x14B7,0x14BD, + 0x14CB,0x14D5,0x14E3,0x14E7,0x1505,0x150B,0x1511,0x1517,0x151F,0x1525,0x1529,0x152B,0x1537,0x153D,0x1541,0x1543, + 0x1549,0x155F,0x1565,0x1567,0x156B,0x157D,0x157F,0x1583,0x158F,0x1591,0x1597,0x159B,0x15B5,0x15BB,0x15C1,0x15C5, + 0x15CD,0x15D7,0x15F7,0x1607,0x1609,0x160F,0x1613,0x1615,0x1619,0x161B,0x1625,0x1633,0x1639,0x163D,0x1645,0x164F, + 0x1655,0x1669,0x166D,0x166F,0x1675,0x1693,0x1697,0x169F,0x16A9,0x16AF,0x16B5,0x16BD,0x16C3,0x16CF,0x16D3,0x16D9, + 0x16DB,0x16E1,0x16E5,0x16EB,0x16ED,0x16F7,0x16F9,0x1709,0x170F,0x1723,0x1727,0x1733,0x1741,0x175D,0x1763,0x1777, + 0x177B,0x178D,0x1795,0x179B,0x179F,0x17A5,0x17B3,0x17B9,0x17BF,0x17C9,0x17CB,0x17D5,0x17E1,0x17E9,0x17F3,0x17F5, + 0x17FF,0x1807,0x1813,0x181D,0x1835,0x1837,0x183B,0x1843,0x1849,0x184D,0x1855,0x1867,0x1871,0x1877,0x187D,0x187F, + 0x1885,0x188F,0x189B,0x189D,0x18A7,0x18AD,0x18B3,0x18B9,0x18C1,0x18C7,0x18D1,0x18D7,0x18D9,0x18DF,0x18E5,0x18EB, + 0x18F5,0x18FD,0x1915,0x191B,0x1931,0x1933,0x1945,0x1949,0x1951,0x195B,0x1979,0x1981,0x1993,0x1997,0x1999,0x19A3, + 0x19A9,0x19AB,0x19B1,0x19B5,0x19C7,0x19CF,0x19DB,0x19ED,0x19FD,0x1A03,0x1A05,0x1A11,0x1A17,0x1A21,0x1A23,0x1A2D, + 0x1A2F,0x1A35,0x1A3F,0x1A4D,0x1A51,0x1A69,0x1A6B,0x1A7B,0x1A7D,0x1A87,0x1A89,0x1A93,0x1AA7,0x1AAB,0x1AAD,0x1AB1, + 0x1AB9,0x1AC9,0x1ACF,0x1AD5,0x1AD7,0x1AE3,0x1AF3,0x1AFB,0x1AFF,0x1B05,0x1B23,0x1B25,0x1B2F,0x1B31,0x1B37,0x1B3B, + 0x1B41,0x1B47,0x1B4F,0x1B55,0x1B59,0x1B65,0x1B6B,0x1B73,0x1B7F,0x1B83,0x1B91,0x1B9D,0x1BA7,0x1BBF,0x1BC5,0x1BD1, + 0x1BD7,0x1BD9,0x1BEF,0x1BF7,0x1C09,0x1C13,0x1C19,0x1C27,0x1C2B,0x1C2D,0x1C33,0x1C3D,0x1C45,0x1C4B,0x1C4F,0x1C55, + 0x1C73,0x1C81,0x1C8B,0x1C8D,0x1C99,0x1CA3,0x1CA5,0x1CB5,0x1CB7,0x1CC9,0x1CE1,0x1CF3,0x1CF9,0x1D09,0x1D1B,0x1D21, + 0x1D23,0x1D35,0x1D39,0x1D3F,0x1D41,0x1D4B,0x1D53,0x1D5D,0x1D63,0x1D69,0x1D71,0x1D75,0x1D7B,0x1D7D,0x1D87,0x1D89, + 0x1D95,0x1D99,0x1D9F,0x1DA5,0x1DA7,0x1DB3,0x1DB7,0x1DC5,0x1DD7,0x1DDB,0x1DE1,0x1DF5,0x1DF9,0x1E01,0x1E07,0x1E0B, + 0x1E13,0x1E17,0x1E25,0x1E2B,0x1E2F,0x1E3D,0x1E49,0x1E4D,0x1E4F,0x1E6D,0x1E71,0x1E89,0x1E8F,0x1E95,0x1EA1,0x1EAD, + 0x1EBB,0x1EC1,0x1EC5,0x1EC7,0x1ECB,0x1EDD,0x1EE3,0x1EEF,0x1EF7,0x1EFD,0x1F01,0x1F0D,0x1F0F,0x1F1B,0x1F39,0x1F49, + 0x1F4B,0x1F51,0x1F67,0x1F75,0x1F7B,0x1F85,0x1F91,0x1F97,0x1F99,0x1F9D,0x1FA5,0x1FAF,0x1FB5,0x1FBB,0x1FD3,0x1FE1, + 0x1FE7,0x1FEB,0x1FF3,0x1FFF,0x2011,0x201B,0x201D,0x2027,0x2029,0x202D,0x2033,0x2047,0x204D,0x2051,0x205F,0x2063, + 0x2065,0x2069,0x2077,0x207D,0x2089,0x20A1,0x20AB,0x20B1,0x20B9,0x20C3,0x20C5,0x20E3,0x20E7,0x20ED,0x20EF,0x20FB, + 0x20FF,0x210D,0x2113,0x2135,0x2141,0x2149,0x214F,0x2159,0x215B,0x215F,0x2173,0x217D,0x2185,0x2195,0x2197,0x21A1, + 0x21AF,0x21B3,0x21B5,0x21C1,0x21C7,0x21D7,0x21DD,0x21E5,0x21E9,0x21F1,0x21F5,0x21FB,0x2203,0x2209,0x220F,0x221B, + 0x2221,0x2225,0x222B,0x2231,0x2239,0x224B,0x224F,0x2263,0x2267,0x2273,0x2275,0x227F,0x2285,0x2287,0x2291,0x229D, + 0x229F,0x22A3,0x22B7,0x22BD,0x22DB,0x22E1,0x22E5,0x22ED,0x22F7,0x2303,0x2309,0x230B,0x2327,0x2329,0x232F,0x2333, + 0x2335,0x2345,0x2351,0x2353,0x2359,0x2363,0x236B,0x2383,0x238F,0x2395,0x23A7,0x23AD,0x23B1,0x23BF,0x23C5,0x23C9, + 0x23D5,0x23DD,0x23E3,0x23EF,0x23F3,0x23F9,0x2405,0x240B,0x2417,0x2419,0x2429,0x243D,0x2441,0x2443,0x244D,0x245F, + 0x2467,0x246B,0x2479,0x247D,0x247F,0x2485,0x249B,0x24A1,0x24AF,0x24B5,0x24BB,0x24C5,0x24CB,0x24CD,0x24D7,0x24D9, + 0x24DD,0x24DF,0x24F5,0x24F7,0x24FB,0x2501,0x2507,0x2513,0x2519,0x2527,0x2531,0x253D,0x2543,0x254B,0x254F,0x2573, + 0x2581,0x258D,0x2593,0x2597,0x259D,0x259F,0x25AB,0x25B1,0x25BD,0x25CD,0x25CF,0x25D9,0x25E1,0x25F7,0x25F9,0x2605, + 0x260B,0x260F,0x2615,0x2627,0x2629,0x2635,0x263B,0x263F,0x264B,0x2653,0x2659,0x2665,0x2669,0x266F,0x267B,0x2681, + 0x2683,0x268F,0x269B,0x269F,0x26AD,0x26B3,0x26C3,0x26C9,0x26CB,0x26D5,0x26DD,0x26EF,0x26F5,0x2717,0x2719,0x2735, + 0x2737,0x274D,0x2753,0x2755,0x275F,0x276B,0x276D,0x2773,0x2777,0x277F,0x2795,0x279B,0x279D,0x27A7,0x27AF,0x27B3, + 0x27B9,0x27C1,0x27C5,0x27D1,0x27E3,0x27EF,0x2803,0x2807,0x280D,0x2813,0x281B,0x281F,0x2821,0x2831,0x283D,0x283F, + 0x2849,0x2851,0x285B,0x285D,0x2861,0x2867,0x2875,0x2881,0x2897,0x289F,0x28BB,0x28BD,0x28C1,0x28D5,0x28D9,0x28DB, + 0x28DF,0x28ED,0x28F7,0x2903,0x2905,0x2911,0x2921,0x2923,0x293F,0x2947,0x295D,0x2965,0x2969,0x296F,0x2975,0x2983, + 0x2987,0x298F,0x299B,0x29A1,0x29A7,0x29AB,0x29BF,0x29C3,0x29D5,0x29D7,0x29E3,0x29E9,0x29ED,0x29F3,0x2A01,0x2A13, + 0x2A1D,0x2A25,0x2A2F,0x2A4F,0x2A55,0x2A5F,0x2A65,0x2A6B,0x2A6D,0x2A73,0x2A83,0x2A89,0x2A8B,0x2A97,0x2A9D,0x2AB9, + 0x2ABB,0x2AC5,0x2ACD,0x2ADD,0x2AE3,0x2AEB,0x2AF1,0x2AFB,0x2B13,0x2B27,0x2B31,0x2B33,0x2B3D,0x2B3F,0x2B4B,0x2B4F, + 0x2B55,0x2B69,0x2B6D,0x2B6F,0x2B7B,0x2B8D,0x2B97,0x2B99,0x2BA3,0x2BA5,0x2BA9,0x2BBD,0x2BCD,0x2BE7,0x2BEB,0x2BF3, + 0x2BF9,0x2BFD,0x2C09,0x2C0F,0x2C17,0x2C23,0x2C2F,0x2C35,0x2C39,0x2C41,0x2C57,0x2C59,0x2C69,0x2C77,0x2C81,0x2C87, + 0x2C93,0x2C9F,0x2CAD,0x2CB3,0x2CB7,0x2CCB,0x2CCF,0x2CDB,0x2CE1,0x2CE3,0x2CE9,0x2CEF,0x2CFF,0x2D07,0x2D1D,0x2D1F, + 0x2D3B,0x2D43,0x2D49,0x2D4D,0x2D61,0x2D65,0x2D71,0x2D89,0x2D9D,0x2DA1,0x2DA9,0x2DB3,0x2DB5,0x2DC5,0x2DC7,0x2DD3, + 0x2DDF,0x2E01,0x2E03,0x2E07,0x2E0D,0x2E19,0x2E1F,0x2E25,0x2E2D,0x2E33,0x2E37,0x2E39,0x2E3F,0x2E57,0x2E5B,0x2E6F, + 0x2E79,0x2E7F,0x2E85,0x2E93,0x2E97,0x2E9D,0x2EA3,0x2EA5,0x2EB1,0x2EB7,0x2EC1,0x2EC3,0x2ECD,0x2ED3,0x2EE7,0x2EEB, + 0x2F05,0x2F09,0x2F0B,0x2F11,0x2F27,0x2F29,0x2F41,0x2F45,0x2F4B,0x2F4D,0x2F51,0x2F57,0x2F6F,0x2F75,0x2F7D,0x2F81, + 0x2F83,0x2FA5,0x2FAB,0x2FB3,0x2FC3,0x2FCF,0x2FD1,0x2FDB,0x2FDD,0x2FE7,0x2FED,0x2FF5,0x2FF9,0x3001,0x300D,0x3023, + 0x3029,0x3037,0x303B,0x3055,0x3059,0x305B,0x3067,0x3071,0x3079,0x307D,0x3085,0x3091,0x3095,0x30A3,0x30A9,0x30B9, + 0x30BF,0x30C7,0x30CB,0x30D1,0x30D7,0x30DF,0x30E5,0x30EF,0x30FB,0x30FD,0x3103,0x3109,0x3119,0x3121,0x3127,0x312D, + 0x3139,0x3143,0x3145,0x314B,0x315D,0x3161,0x3167,0x316D,0x3173,0x317F,0x3191,0x3199,0x319F,0x31A9,0x31B1,0x31C3, + 0x31C7,0x31D5,0x31DB,0x31ED,0x31F7,0x31FF,0x3209,0x3215,0x3217,0x321D,0x3229,0x3235,0x3259,0x325D,0x3263,0x326B, + 0x326F,0x3275,0x3277,0x327B,0x328D,0x3299,0x329F,0x32A7,0x32AD,0x32B3,0x32B7,0x32C9,0x32CB,0x32CF,0x32D1,0x32E9, + 0x32ED,0x32F3,0x32F9,0x3307,0x3325,0x332B,0x332F,0x3335,0x3341,0x3347,0x335B,0x335F,0x3367,0x336B,0x3373,0x3379, + 0x337F,0x3383,0x33A1,0x33A3,0x33AD,0x33B9,0x33C1,0x33CB,0x33D3,0x33EB,0x33F1,0x33FD,0x3401,0x340F,0x3413,0x3419, + 0x341B,0x3437,0x3445,0x3455,0x3457,0x3463,0x3469,0x346D,0x3481,0x348B,0x3491,0x3497,0x349D,0x34A5,0x34AF,0x34BB, + 0x34C9,0x34D3,0x34E1,0x34F1,0x34FF,0x3509,0x3517,0x351D,0x352D,0x3533,0x353B,0x3541,0x3551,0x3565,0x356F,0x3571, + 0x3577,0x357B,0x357D,0x3581,0x358D,0x358F,0x3599,0x359B,0x35A1,0x35B7,0x35BD,0x35BF,0x35C3,0x35D5,0x35DD,0x35E7, + 0x35EF,0x3605,0x3607,0x3611,0x3623,0x3631,0x3635,0x3637,0x363B,0x364D,0x364F,0x3653,0x3659,0x3661,0x366B,0x366D, + 0x368B,0x368F,0x36AD,0x36AF,0x36B9,0x36BB,0x36CD,0x36D1,0x36E3,0x36E9,0x36F7,0x3701,0x3703,0x3707,0x371B,0x373F, + 0x3745,0x3749,0x374F,0x375D,0x3761,0x3775,0x377F,0x378D,0x37A3,0x37A9,0x37AB,0x37C9,0x37D5,0x37DF,0x37F1,0x37F3, + 0x37F7,0x3805,0x380B,0x3821,0x3833,0x3835,0x3841,0x3847,0x384B,0x3853,0x3857,0x385F,0x3865,0x386F,0x3871,0x387D, + 0x388F,0x3899,0x38A7,0x38B7,0x38C5,0x38C9,0x38CF,0x38D5,0x38D7,0x38DD,0x38E1,0x38E3,0x38FF,0x3901,0x391D,0x3923, + 0x3925,0x3929,0x392F,0x393D,0x3941,0x394D,0x395B,0x396B,0x3979,0x397D,0x3983,0x398B,0x3991,0x3995,0x399B,0x39A1, + 0x39A7,0x39AF,0x39B3,0x39BB,0x39BF,0x39CD,0x39DD,0x39E5,0x39EB,0x39EF,0x39FB,0x3A03,0x3A13,0x3A15,0x3A1F,0x3A27, + 0x3A2B,0x3A31,0x3A4B,0x3A51,0x3A5B,0x3A63,0x3A67,0x3A6D,0x3A79,0x3A87,0x3AA5,0x3AA9,0x3AB7,0x3ACD,0x3AD5,0x3AE1, + 0x3AE5,0x3AEB,0x3AF3,0x3AFD,0x3B03,0x3B11,0x3B1B,0x3B21,0x3B23,0x3B2D,0x3B39,0x3B45,0x3B53,0x3B59,0x3B5F,0x3B71, + 0x3B7B,0x3B81,0x3B89,0x3B9B,0x3B9F,0x3BA5,0x3BA7,0x3BAD,0x3BB7,0x3BB9,0x3BC3,0x3BCB,0x3BD1,0x3BD7,0x3BE1,0x3BE3, + 0x3BF5,0x3BFF,0x3C01,0x3C0D,0x3C11,0x3C17,0x3C1F,0x3C29,0x3C35,0x3C43,0x3C4F,0x3C53,0x3C5B,0x3C65,0x3C6B,0x3C71, + 0x3C85,0x3C89,0x3C97,0x3CA7,0x3CB5,0x3CBF,0x3CC7,0x3CD1,0x3CDD,0x3CDF,0x3CF1,0x3CF7,0x3D03,0x3D0D,0x3D19,0x3D1B, + 0x3D1F,0x3D21,0x3D2D,0x3D33,0x3D37,0x3D3F,0x3D43,0x3D6F,0x3D73,0x3D75,0x3D79,0x3D7B,0x3D85,0x3D91,0x3D97,0x3D9D, + 0x3DAB,0x3DAF,0x3DB5,0x3DBB,0x3DC1,0x3DC9,0x3DCF,0x3DF3,0x3E05,0x3E09,0x3E0F,0x3E11,0x3E1D,0x3E23,0x3E29,0x3E2F, + 0x3E33,0x3E41,0x3E57,0x3E63,0x3E65,0x3E77,0x3E81,0x3E87,0x3EA1,0x3EB9,0x3EBD,0x3EBF,0x3EC3,0x3EC5,0x3EC9,0x3ED7, + 0x3EDB,0x3EE1,0x3EE7,0x3EEF,0x3EFF,0x3F0B,0x3F0D,0x3F37,0x3F3B,0x3F3D,0x3F41,0x3F59,0x3F5F,0x3F65,0x3F67,0x3F79, + 0x3F7D,0x3F8B,0x3F91,0x3FAD,0x3FBF,0x3FCD,0x3FD3,0x3FDD,0x3FE9,0x3FEB,0x3FF1,0x3FFD,0x401B,0x4021,0x4025,0x402B, + 0x4031,0x403F,0x4043,0x4045,0x405D,0x4061,0x4067,0x406D,0x4087,0x4091,0x40A3,0x40A9,0x40B1,0x40B7,0x40BD,0x40DB, + 0x40DF,0x40EB,0x40F7,0x40F9,0x4109,0x410B,0x4111,0x4115,0x4121,0x4133,0x4135,0x413B,0x413F,0x4159,0x4165,0x416B, + 0x4177,0x417B,0x4193,0x41AB,0x41B7,0x41BD,0x41BF,0x41CB,0x41E7,0x41EF,0x41F3,0x41F9,0x4205,0x4207,0x4219,0x421F, + 0x4223,0x4229,0x422F,0x4243,0x4253,0x4255,0x425B,0x4261,0x4273,0x427D,0x4283,0x4285,0x4289,0x4291,0x4297,0x429D, + 0x42B5,0x42C5,0x42CB,0x42D3,0x42DD,0x42E3,0x42F1,0x4307,0x430F,0x431F,0x4325,0x4327,0x4333,0x4337,0x4339,0x434F, + 0x4357,0x4369,0x438B,0x438D,0x4393,0x43A5,0x43A9,0x43AF,0x43B5,0x43BD,0x43C7,0x43CF,0x43E1,0x43E7,0x43EB,0x43ED, + 0x43F1,0x43F9,0x4409,0x440B,0x4417,0x4423,0x4429,0x443B,0x443F,0x4445,0x444B,0x4451,0x4453,0x4459,0x4465,0x446F, + 0x4483,0x448F,0x44A1,0x44A5,0x44AB,0x44AD,0x44BD,0x44BF,0x44C9,0x44D7,0x44DB,0x44F9,0x44FB,0x4505,0x4511,0x4513, + 0x452B,0x4531,0x4541,0x4549,0x4553,0x4555,0x4561,0x4577,0x457D,0x457F,0x458F,0x45A3,0x45AD,0x45AF,0x45BB,0x45C7, + 0x45D9,0x45E3,0x45EF,0x45F5,0x45F7,0x4601,0x4603,0x4609,0x4613,0x4625,0x4627,0x4633,0x4639,0x463D,0x4643,0x4645, + 0x465D,0x4679,0x467B,0x467F,0x4681,0x468B,0x468D,0x469D,0x46A9,0x46B1,0x46C7,0x46C9,0x46CF,0x46D3,0x46D5,0x46DF, + 0x46E5,0x46F9,0x4705,0x470F,0x4717,0x4723,0x4729,0x472F,0x4735,0x4739,0x474B,0x474D,0x4751,0x475D,0x476F,0x4771, + 0x477D,0x4783,0x4787,0x4789,0x4799,0x47A5,0x47B1,0x47BF,0x47C3,0x47CB,0x47DD,0x47E1,0x47ED,0x47FB,0x4801,0x4807, + 0x480B,0x4813,0x4819,0x481D,0x4831,0x483D,0x4847,0x4855,0x4859,0x485B,0x486B,0x486D,0x4879,0x4897,0x489B,0x48A1, + 0x48B9,0x48CD,0x48E5,0x48EF,0x48F7,0x4903,0x490D,0x4919,0x491F,0x492B,0x4937,0x493D,0x4945,0x4955,0x4963,0x4969, + 0x496D,0x4973,0x4997,0x49AB,0x49B5,0x49D3,0x49DF,0x49E1,0x49E5,0x49E7,0x4A03,0x4A0F,0x4A1D,0x4A23,0x4A39,0x4A41, + 0x4A45,0x4A57,0x4A5D,0x4A6B,0x4A7D,0x4A81,0x4A87,0x4A89,0x4A8F,0x4AB1,0x4AC3,0x4AC5,0x4AD5,0x4ADB,0x4AED,0x4AEF, + 0x4B07,0x4B0B,0x4B0D,0x4B13,0x4B1F,0x4B25,0x4B31,0x4B3B,0x4B43,0x4B49,0x4B59,0x4B65,0x4B6D,0x4B77,0x4B85,0x4BAD, + 0x4BB3,0x4BB5,0x4BBB,0x4BBF,0x4BCB,0x4BD9,0x4BDD,0x4BDF,0x4BE3,0x4BE5,0x4BE9,0x4BF1,0x4BF7,0x4C01,0x4C07,0x4C0D, + 0x4C0F,0x4C15,0x4C1B,0x4C21,0x4C2D,0x4C33,0x4C4B,0x4C55,0x4C57,0x4C61,0x4C67,0x4C73,0x4C79,0x4C7F,0x4C8D,0x4C93, + 0x4C99,0x4CCD,0x4CE1,0x4CE7,0x4CF1,0x4CF3,0x4CFD,0x4D05,0x4D0F,0x4D1B,0x4D27,0x4D29,0x4D2F,0x4D33,0x4D41,0x4D51, + 0x4D59,0x4D65,0x4D6B,0x4D81,0x4D83,0x4D8D,0x4D95,0x4D9B,0x4DB1,0x4DB3,0x4DC9,0x4DCF,0x4DD7,0x4DE1,0x4DED,0x4DF9, + 0x4DFB,0x4E05,0x4E0B,0x4E17,0x4E19,0x4E1D,0x4E2B,0x4E35,0x4E37,0x4E3D,0x4E4F,0x4E53,0x4E5F,0x4E67,0x4E79,0x4E85, + 0x4E8B,0x4E91,0x4E95,0x4E9B,0x4EA1,0x4EAF,0x4EB3,0x4EB5,0x4EC1,0x4ECD,0x4ED1,0x4ED7,0x4EE9,0x4EFB,0x4F07,0x4F09, + 0x4F19,0x4F25,0x4F2D,0x4F3F,0x4F49,0x4F63,0x4F67,0x4F6D,0x4F75,0x4F7B,0x4F81,0x4F85,0x4F87,0x4F91,0x4FA5,0x4FA9, + 0x4FAF,0x4FB7,0x4FBB,0x4FCF,0x4FD9,0x4FDB,0x4FFD,0x4FFF,0x5003,0x501B,0x501D,0x5029,0x5035,0x503F,0x5045,0x5047, + 0x5053,0x5071,0x5077,0x5083,0x5093,0x509F,0x50A1,0x50B7,0x50C9,0x50D5,0x50E3,0x50ED,0x50EF,0x50FB,0x5107,0x510B, + 0x510D,0x5111,0x5117,0x5123,0x5125,0x5135,0x5147,0x5149,0x5171,0x5179,0x5189,0x518F,0x5197,0x51A1,0x51A3,0x51A7, + 0x51B9,0x51C1,0x51CB,0x51D3,0x51DF,0x51E3,0x51F5,0x51F7,0x5209,0x5213,0x5215,0x5219,0x521B,0x521F,0x5227,0x5243, + 0x5245,0x524B,0x5261,0x526D,0x5273,0x5281,0x5293,0x5297,0x529D,0x52A5,0x52AB,0x52B1,0x52BB,0x52C3,0x52C7,0x52C9, + 0x52DB,0x52E5,0x52EB,0x52FF,0x5315,0x531D,0x5323,0x5341,0x5345,0x5347,0x534B,0x535D,0x5363,0x5381,0x5383,0x5387, + 0x538F,0x5395,0x5399,0x539F,0x53AB,0x53B9,0x53DB,0x53E9,0x53EF,0x53F3,0x53F5,0x53FB,0x53FF,0x540D,0x5411,0x5413, + 0x5419,0x5435,0x5437,0x543B,0x5441,0x5449,0x5453,0x5455,0x545F,0x5461,0x546B,0x546D,0x5471,0x548F,0x5491,0x549D, + 0x54A9,0x54B3,0x54C5,0x54D1,0x54DF,0x54E9,0x54EB,0x54F7,0x54FD,0x5507,0x550D,0x551B,0x5527,0x552B,0x5539,0x553D, + 0x554F,0x5551,0x555B,0x5563,0x5567,0x556F,0x5579,0x5585,0x5597,0x55A9,0x55B1,0x55B7,0x55C9,0x55D9,0x55E7,0x55ED, + 0x55F3,0x55FD,0x560B,0x560F,0x5615,0x5617,0x5623,0x562F,0x5633,0x5639,0x563F,0x564B,0x564D,0x565D,0x565F,0x566B, + 0x5671,0x5675,0x5683,0x5689,0x568D,0x568F,0x569B,0x56AD,0x56B1,0x56D5,0x56E7,0x56F3,0x56FF,0x5701,0x5705,0x5707, + 0x570B,0x5713,0x571F,0x5723,0x5747,0x574D,0x575F,0x5761,0x576D,0x5777,0x577D,0x5789,0x57A1,0x57A9,0x57AF,0x57B5, + 0x57C5,0x57D1,0x57D3,0x57E5,0x57EF,0x5803,0x580D,0x580F,0x5815,0x5827,0x582B,0x582D,0x5855,0x585B,0x585D,0x586D, + 0x586F,0x5873,0x587B,0x588D,0x5897,0x58A3,0x58A9,0x58AB,0x58B5,0x58BD,0x58C1,0x58C7,0x58D3,0x58D5,0x58DF,0x58F1, + 0x58F9,0x58FF,0x5903,0x5917,0x591B,0x5921,0x5945,0x594B,0x594D,0x5957,0x595D,0x5975,0x597B,0x5989,0x5999,0x599F, + 0x59B1,0x59B3,0x59BD,0x59D1,0x59DB,0x59E3,0x59E9,0x59ED,0x59F3,0x59F5,0x59FF,0x5A01,0x5A0D,0x5A11,0x5A13,0x5A17, + 0x5A1F,0x5A29,0x5A2F,0x5A3B,0x5A4D,0x5A5B,0x5A67,0x5A77,0x5A7F,0x5A85,0x5A95,0x5A9D,0x5AA1,0x5AA3,0x5AA9,0x5ABB, + 0x5AD3,0x5AE5,0x5AEF,0x5AFB,0x5AFD,0x5B01,0x5B0F,0x5B19,0x5B1F,0x5B25,0x5B2B,0x5B3D,0x5B49,0x5B4B,0x5B67,0x5B79, + 0x5B87,0x5B97,0x5BA3,0x5BB1,0x5BC9,0x5BD5,0x5BEB,0x5BF1,0x5BF3,0x5BFD,0x5C05,0x5C09,0x5C0B,0x5C0F,0x5C1D,0x5C29, + 0x5C2F,0x5C33,0x5C39,0x5C47,0x5C4B,0x5C4D,0x5C51,0x5C6F,0x5C75,0x5C77,0x5C7D,0x5C87,0x5C89,0x5CA7,0x5CBD,0x5CBF, + 0x5CC3,0x5CC9,0x5CD1,0x5CD7,0x5CDD,0x5CED,0x5CF9,0x5D05,0x5D0B,0x5D13,0x5D17,0x5D19,0x5D31,0x5D3D,0x5D41,0x5D47, + 0x5D4F,0x5D55,0x5D5B,0x5D65,0x5D67,0x5D6D,0x5D79,0x5D95,0x5DA3,0x5DA9,0x5DAD,0x5DB9,0x5DC1,0x5DC7,0x5DD3,0x5DD7, + 0x5DDD,0x5DEB,0x5DF1,0x5DFD,0x5E07,0x5E0D,0x5E13,0x5E1B,0x5E21,0x5E27,0x5E2B,0x5E2D,0x5E31,0x5E39,0x5E45,0x5E49, + 0x5E57,0x5E69,0x5E73,0x5E75,0x5E85,0x5E8B,0x5E9F,0x5EA5,0x5EAF,0x5EB7,0x5EBB,0x5ED9,0x5EFD,0x5F09,0x5F11,0x5F27, + 0x5F33,0x5F35,0x5F3B,0x5F47,0x5F57,0x5F5D,0x5F63,0x5F65,0x5F77,0x5F7B,0x5F95,0x5F99,0x5FA1,0x5FB3,0x5FBD,0x5FC5, + 0x5FCF,0x5FD5,0x5FE3,0x5FE7,0x5FFB,0x6011,0x6023,0x602F,0x6037,0x6053,0x605F,0x6065,0x606B,0x6073,0x6079,0x6085, + 0x609D,0x60AD,0x60BB,0x60BF,0x60CD,0x60D9,0x60DF,0x60E9,0x60F5,0x6109,0x610F,0x6113,0x611B,0x612D,0x6139,0x614B, + 0x6155,0x6157,0x615B,0x616F,0x6179,0x6187,0x618B,0x6191,0x6193,0x619D,0x61B5,0x61C7,0x61C9,0x61CD,0x61E1,0x61F1, + 0x61FF,0x6209,0x6217,0x621D,0x6221,0x6227,0x623B,0x6241,0x624B,0x6251,0x6253,0x625F,0x6265,0x6283,0x628D,0x6295, + 0x629B,0x629F,0x62A5,0x62AD,0x62D5,0x62D7,0x62DB,0x62DD,0x62E9,0x62FB,0x62FF,0x6305,0x630D,0x6317,0x631D,0x632F, + 0x6341,0x6343,0x634F,0x635F,0x6367,0x636D,0x6371,0x6377,0x637D,0x637F,0x63B3,0x63C1,0x63C5,0x63D9,0x63E9,0x63EB, + 0x63EF,0x63F5,0x6401,0x6403,0x6409,0x6415,0x6421,0x6427,0x642B,0x6439,0x6443,0x6449,0x644F,0x645D,0x6467,0x6475, + 0x6485,0x648D,0x6493,0x649F,0x64A3,0x64AB,0x64C1,0x64C7,0x64C9,0x64DB,0x64F1,0x64F7,0x64F9,0x650B,0x6511,0x6521, + 0x652F,0x6539,0x653F,0x654B,0x654D,0x6553,0x6557,0x655F,0x6571,0x657D,0x658D,0x658F,0x6593,0x65A1,0x65A5,0x65AD, + 0x65B9,0x65C5,0x65E3,0x65F3,0x65FB,0x65FF,0x6601,0x6607,0x661D,0x6629,0x6631,0x663B,0x6641,0x6647,0x664D,0x665B, + 0x6661,0x6673,0x667D,0x6689,0x668B,0x6695,0x6697,0x669B,0x66B5,0x66B9,0x66C5,0x66CD,0x66D1,0x66E3,0x66EB,0x66F5, + 0x6703,0x6713,0x6719,0x671F,0x6727,0x6731,0x6737,0x673F,0x6745,0x6751,0x675B,0x676F,0x6779,0x6781,0x6785,0x6791, + 0x67AB,0x67BD,0x67C1,0x67CD,0x67DF,0x67E5,0x6803,0x6809,0x6811,0x6817,0x682D,0x6839,0x683B,0x683F,0x6845,0x684B, + 0x684D,0x6857,0x6859,0x685D,0x6863,0x6869,0x686B,0x6871,0x6887,0x6899,0x689F,0x68B1,0x68BD,0x68C5,0x68D1,0x68D7, + 0x68E1,0x68ED,0x68EF,0x68FF,0x6901,0x690B,0x690D,0x6917,0x6929,0x692F,0x6943,0x6947,0x6949,0x694F,0x6965,0x696B, + 0x6971,0x6983,0x6989,0x6997,0x69A3,0x69B3,0x69B5,0x69BB,0x69C1,0x69C5,0x69D3,0x69DF,0x69E3,0x69E5,0x69F7,0x6A07, + 0x6A2B,0x6A37,0x6A3D,0x6A4B,0x6A67,0x6A69,0x6A75,0x6A7B,0x6A87,0x6A8D,0x6A91,0x6A93,0x6AA3,0x6AC1,0x6AC9,0x6AE1, + 0x6AE7,0x6B05,0x6B0F,0x6B11,0x6B23,0x6B27,0x6B2D,0x6B39,0x6B41,0x6B57,0x6B59,0x6B5F,0x6B75,0x6B87,0x6B89,0x6B93, + 0x6B95,0x6B9F,0x6BBD,0x6BBF,0x6BDB,0x6BE1,0x6BEF,0x6BFF,0x6C05,0x6C19,0x6C29,0x6C2B,0x6C31,0x6C35,0x6C55,0x6C59, + 0x6C5B,0x6C5F,0x6C65,0x6C67,0x6C73,0x6C77,0x6C7D,0x6C83,0x6C8F,0x6C91,0x6C97,0x6C9B,0x6CA1,0x6CA9,0x6CAF,0x6CB3, + 0x6CC7,0x6CCB,0x6CEB,0x6CF5,0x6CFD,0x6D0D,0x6D0F,0x6D25,0x6D27,0x6D2B,0x6D31,0x6D39,0x6D3F,0x6D4F,0x6D5D,0x6D61, + 0x6D73,0x6D7B,0x6D7F,0x6D93,0x6D99,0x6DA5,0x6DB1,0x6DB7,0x6DC1,0x6DC3,0x6DCD,0x6DCF,0x6DDB,0x6DF7,0x6E03,0x6E15, + 0x6E17,0x6E29,0x6E33,0x6E3B,0x6E45,0x6E75,0x6E77,0x6E7B,0x6E81,0x6E89,0x6E93,0x6E95,0x6E9F,0x6EBD,0x6EBF,0x6EE3, + 0x6EE9,0x6EF3,0x6EF9,0x6EFB,0x6F0D,0x6F11,0x6F17,0x6F1F,0x6F2F,0x6F3D,0x6F4D,0x6F53,0x6F61,0x6F65,0x6F79,0x6F7D, + 0x6F83,0x6F85,0x6F8F,0x6F9B,0x6F9D,0x6FA3,0x6FAF,0x6FB5,0x6FBB,0x6FBF,0x6FCB,0x6FCD,0x6FD3,0x6FD7,0x6FE3,0x6FE9, + 0x6FF1,0x6FF5,0x6FF7,0x6FFD,0x700F,0x7019,0x701F,0x7027,0x7033,0x7039,0x704F,0x7051,0x7057,0x7063,0x7075,0x7079, + 0x7087,0x708D,0x7091,0x70A5,0x70AB,0x70BB,0x70C3,0x70C7,0x70CF,0x70E5,0x70ED,0x70F9,0x70FF,0x7105,0x7115,0x7121, + 0x7133,0x7151,0x7159,0x715D,0x715F,0x7163,0x7169,0x7183,0x7187,0x7195,0x71AD,0x71C3,0x71C9,0x71CB,0x71D1,0x71DB, + 0x71E1,0x71EF,0x71F5,0x71FB,0x7207,0x7211,0x7217,0x7219,0x7225,0x722F,0x723B,0x7243,0x7255,0x7267,0x7271,0x7277, + 0x727F,0x728F,0x7295,0x729B,0x72A3,0x72B3,0x72C7,0x72CB,0x72CD,0x72D7,0x72D9,0x72E3,0x72EF,0x72F5,0x72FD,0x7303, + 0x730D,0x7321,0x732B,0x733D,0x7357,0x735B,0x7361,0x737F,0x7381,0x7385,0x738D,0x7393,0x739F,0x73AB,0x73BD,0x73C1, + 0x73C9,0x73DF,0x73E5,0x73E7,0x73F3,0x7415,0x741B,0x742D,0x7439,0x743F,0x7441,0x745D,0x746B,0x747B,0x7489,0x748D, + 0x749B,0x74A7,0x74AB,0x74B1,0x74B7,0x74B9,0x74DD,0x74E1,0x74E7,0x74FB,0x7507,0x751F,0x7525,0x753B,0x753D,0x754D, + 0x755F,0x756B,0x7577,0x7589,0x758B,0x7591,0x7597,0x759D,0x75A1,0x75A7,0x75B5,0x75B9,0x75BB,0x75D1,0x75D9,0x75E5, + 0x75EB,0x75F5,0x75FB,0x7603,0x760F,0x7621,0x762D,0x7633,0x763D,0x763F,0x7655,0x7663,0x7669,0x766F,0x7673,0x7685, + 0x768B,0x769F,0x76B5,0x76B7,0x76C3,0x76DB,0x76DF,0x76F1,0x7703,0x7705,0x771B,0x771D,0x7721,0x772D,0x7735,0x7741, + 0x774B,0x7759,0x775D,0x775F,0x7771,0x7781,0x77A7,0x77AD,0x77B3,0x77B9,0x77C5,0x77CF,0x77D5,0x77E1,0x77E9,0x77EF, + 0x77F3,0x77F9,0x7807,0x7825,0x782B,0x7835,0x783D,0x7853,0x7859,0x7861,0x786D,0x7877,0x7879,0x7883,0x7885,0x788B, + 0x7895,0x7897,0x78A1,0x78AD,0x78BF,0x78D3,0x78D9,0x78DD,0x78E5,0x78FB,0x7901,0x7907,0x7925,0x792B,0x7939,0x793F, + 0x794B,0x7957,0x795D,0x7967,0x7969,0x7973,0x7991,0x7993,0x79A3,0x79AB,0x79AF,0x79B1,0x79B7,0x79C9,0x79CD,0x79CF, + 0x79D5,0x79D9,0x79F3,0x79F7,0x79FF,0x7A05,0x7A0F,0x7A11,0x7A15,0x7A1B,0x7A23,0x7A27,0x7A2D,0x7A4B,0x7A57,0x7A59, + 0x7A5F,0x7A65,0x7A69,0x7A7D,0x7A93,0x7A9B,0x7A9F,0x7AA1,0x7AA5,0x7AED,0x7AF5,0x7AF9,0x7B01,0x7B17,0x7B19,0x7B1D, + 0x7B2B,0x7B35,0x7B37,0x7B3B,0x7B4F,0x7B55,0x7B5F,0x7B71,0x7B77,0x7B8B,0x7B9B,0x7BA1,0x7BA9,0x7BAF,0x7BB3,0x7BC7, + 0x7BD3,0x7BE9,0x7BEB,0x7BEF,0x7BF1,0x7BFD,0x7C07,0x7C19,0x7C1B,0x7C31,0x7C37,0x7C49,0x7C67,0x7C69,0x7C73,0x7C81, + 0x7C8B,0x7C93,0x7CA3,0x7CD5,0x7CDB,0x7CE5,0x7CED,0x7CF7,0x7D03,0x7D09,0x7D1B,0x7D1D,0x7D33,0x7D39,0x7D3B,0x7D3F, + 0x7D45,0x7D4D,0x7D53,0x7D59,0x7D63,0x7D75,0x7D77,0x7D8D,0x7D8F,0x7D9F,0x7DAD,0x7DB7,0x7DBD,0x7DBF,0x7DCB,0x7DD5, + 0x7DE9,0x7DED,0x7DFB,0x7E01,0x7E05,0x7E29,0x7E2B,0x7E2F,0x7E35,0x7E41,0x7E43,0x7E47,0x7E55,0x7E61,0x7E67,0x7E6B, + 0x7E71,0x7E73,0x7E79,0x7E7D,0x7E91,0x7E9B,0x7E9D,0x7EA7,0x7EAD,0x7EB9,0x7EBB,0x7ED3,0x7EDF,0x7EEB,0x7EF1,0x7EF7, + 0x7EFB,0x7F13,0x7F15,0x7F19,0x7F31,0x7F33,0x7F39,0x7F3D,0x7F43,0x7F4B,0x7F5B,0x7F61,0x7F63,0x7F6D,0x7F79,0x7F87, + 0x7F8D,0x7FAF,0x7FB5,0x7FC3,0x7FC9,0x7FCD,0x7FCF +}; diff --git a/code/redalert/mp.h b/code/redalert/mp.h new file mode 100644 index 0000000..675f758 --- /dev/null +++ b/code/redalert/mp.h @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MP.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MP.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MP_H +#define MP_H + +//lint -e740 -e534 -e537 -e760 + +//lint -d_LINT=1 +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include "straw.h" +#include + +extern unsigned short primeTable[3511]; + + +#define digit unsigned long +#define signeddigit signed long +#define LOG_UNITSIZE 5 +#define UNITSIZE 32 +#define UPPER_MOST_BIT 0x80000000L +#define SEMI_UPPER_MOST_BIT 0x8000 +#define SEMI_MASK ((unsigned short)~0) +#define MAX_BIT_PRECISION 2048 +#define MAX_UNIT_PRECISION (MAX_BIT_PRECISION/UNITSIZE) +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) +#endif + + +int XMP_Significance(const digit * r, int precision); +void XMP_Inc(digit * r, int precision); +void XMP_Dec(digit * r, int precision); +void XMP_Neg(digit * r, int precision); +void XMP_Abs(digit * r, int precision); +void XMP_Shift_Right_Bits(digit * r1, int bits, int precision); +void XMP_Shift_Left_Bits(digit * r1, int bits, int precision); +bool XMP_Rotate_Left(digit * r1, bool carry, int precision); +void XMP_Not(digit * digit_ptr, int precision); +void XMP_Init(digit * r, digit value, int precision); +unsigned XMP_Count_Bits(const digit * r, int precision); +int XMP_Count_Bytes(const digit * r, int precision); +void XMP_Move(digit * dest, digit const * source, int precision); +int XMP_Compare(const digit * r1, const digit * r2, int precision); +bool XMP_Add(digit * result, const digit * r1, const digit * r2, bool carry, int precision); +bool XMP_Add_Int(digit * result, const digit * r1, digit r2, bool carry, int precision); +bool XMP_Sub(digit * result, const digit * r1, const digit * r2, bool borrow, int precision); +bool XMP_Sub_Int(digit * result, const digit * r1, unsigned short r2, bool borrow, int precision); +int XMP_Unsigned_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int XMP_Unsigned_Mult_Int(digit * prod, const digit * multiplicand, short multiplier, int precision); +int XMP_Signed_Mult_Int(digit * prod, const digit * multiplicand, signed short multiplier, int precision); +int XMP_Signed_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +unsigned short XMP_Unsigned_Div_Int(digit * quotient, digit const * dividend, unsigned short divisor, int precision); +int XMP_Unsigned_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +void XMP_Signed_Div(digit * remainder, digit * quotient, digit const * dividend, digit const * divisor, int precision); +int XMP_Reciprocal(digit * quotient, const digit * divisor, int precision); +void XMP_Decode_ASCII(char const * str, digit * mpn, int precision); +void xmp_single_mul(unsigned short * prod, unsigned short * multiplicand, unsigned short multiplier, int precision); +void XMP_Double_Mul(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +int xmp_stage_modulus(const digit * n_modulus, int precision); +int XMP_Mod_Mult(digit * prod, const digit * multiplicand, const digit * multiplier, int precision); +void XMP_Mod_Mult_Clear(int precision); +unsigned short mp_quo_digit(unsigned short * dividend); +int xmp_exponent_mod(digit * expout, const digit * expin, const digit * exponent_ptr, const digit * modulus, int precision); +bool XMP_Is_Small_Prime(const digit * candidate, int precision); +bool XMP_Small_Divisors_Test(const digit * candidate, int precision); +bool XMP_Fermat_Test(const digit * candidate_prime, unsigned rounds, int precision); +void XMP_Inverse_A_Mod_B(digit * result, digit const * number, digit const * modulus, int precision); +void XMP_Signed_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +void XMP_Unsigned_Decode(digit * result, const unsigned char * from, int frombytes, int precision); +unsigned XMP_Encode(unsigned char * to, digit const * from, int precision); +unsigned XMP_Encode(unsigned char * to, unsigned tobytes, digit const * from, int precision); +void XMP_Randomize(digit * result, Straw & rng, int nbits, int precision); +void XMP_Randomize(digit * result, Straw & rng, digit const * min, digit const * max, int precision); +bool XMP_Is_Prime(digit const * prime, int precision); +bool XMP_Rabin_Miller_Test(Straw & rng, digit const * w, int rounds, int precision); +int XMP_DER_Length_Encode(unsigned long length, unsigned char * output); +int XMP_DER_Encode(digit const * from, unsigned char * output, int precision); +void XMP_DER_Decode(digit * result, unsigned char const * input, int precision); + + + +inline int XMP_Digits_To_Bits(int digits) +{ + return(digits << LOG_UNITSIZE); +} + + +inline int XMP_Bits_To_Digits(int bits) +{ + return ((bits + (UNITSIZE-1)) / UNITSIZE); +} + + +inline digit XMP_Bits_To_Mask(int bits) +{ + if (!bits) return(0); + return(1 << ((bits-1) % UNITSIZE)); +} + + +inline bool XMP_Is_Negative(const digit * r, int precision) +{ + return((signeddigit) *(r + (precision-1)) < 0); +} + + +inline bool XMP_Test_Eq_Int(digit const * r, int i, int p) +{ + return( (*r == i ) && XMP_Significance(r,p) <= 1 ); +} + + +inline void XMP_Set_Bit(digit * r, unsigned bit) +{ + r[bit >> LOG_UNITSIZE] |= ((digit)1 << (bit & (UNITSIZE-1))); +} + +inline bool XMP_Test_Bit(const digit * r, unsigned bit) +{ + return (r[bit >> LOG_UNITSIZE] & ((digit)1 << (bit & (UNITSIZE-1)))); +} + + + +// Misc functions. +void memrev(char * buffer, size_t length); + +#endif diff --git a/code/redalert/mpgset.cpp b/code/redalert/mpgset.cpp new file mode 100644 index 0000000..38b24a7 --- /dev/null +++ b/code/redalert/mpgset.cpp @@ -0,0 +1,606 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* FILE +* MpgSet.cpp +* +* DESCRIPTION +* Mpeg movie settings manager +* +* PROGRAMMER +* Denzil E. Long, Jr. +* +* DATE +* June 30, 1998 +* +****************************************************************************/ + +#include "function.h" + +#ifdef DVD +#include "mpgset.h" + +#ifdef MCIMPEG +bool EnumMCI(MCIDevice* desc, void* context); +#endif + +/**************************************************************************** +* +* NAME +* MPGSettings(DeviceName) +* +* DESCRIPTION +* Default constructor +* +* INPUTS +* DeviceName - Initial device to use for MPG playback (NULL = DXMedia) +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::MPGSettings(const char* deviceName) + : mDeviceName(NULL) + { + SetDeviceName(deviceName); + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + #endif + } + + +MPGSettings::MPGSettings(FileClass& file) + : mDeviceName(NULL) + { + INIClass ini; + char buffer[256]; + char* device = NULL; + + #ifdef MCIMPEG + int count = mMCI.GetDeviceCount(); + + mMCIDevices = NULL; + mCount = 0; + + // Enumerate all the MCI devices that can play a movie + if (count) + { + mMCIDevices = new MCIDevice[count]; + + if (mMCIDevices) + { + mMCI.EnumerateDevices(EnumMCI, this); + } + } + + #endif + + // Retrieve the user specified device from the config file + buffer[0] = '\0'; + + if (ini.Load(file)) + { + ini.Get_String("MovieSettings", "Device", "Default", buffer, sizeof(buffer)); + } + + // If there is a specification in the config and it isn't the default + if ((strlen(buffer) != 0) && (stricmp(buffer, "Default") != 0)) + { + #ifdef MCIMPEG + // Search for selection + for (int i = 0; i < mCount; i++) + { + if (stricmp(buffer, mMCIDevices[i].name) == 0) + { + device = mMCIDevices[i].name; + break; + } + } + #endif + } + + SetDeviceName(device); + } + + +/**************************************************************************** +* +* NAME +* ~MPGSettings +* +* DESCRIPTION +* Destructor +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +MPGSettings::~MPGSettings(void) + { + if (mDeviceName) + free(mDeviceName); + + #ifdef MCIMPEG + if (mMCIDevices) + delete[] mMCIDevices; + #endif + } + + +/**************************************************************************** +* +* NAME +* SetDeviceName(DeviceName) +* +* DESCRIPTION +* Change current device used for mpeg playback +* +* INPUTS +* DeviceName - Device name type (IE: mpegvideo) +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::SetDeviceName(const char* deviceName) + { + if (mDeviceName) + free(mDeviceName); + + mDeviceName = NULL; + + if (deviceName) + mDeviceName = strdup(deviceName); + } + + +bool MPGSettings::Save(FileClass& file) + { + INIClass ini; + + if (ini.Load(file)) + { + const char* device = GetDeviceName(); + + if (device) + { + ini.Put_String("MovieSettings", "Device", device); + } + else + { + ini.Put_String("MovieSettings", "Device", "Default"); + } + + ini.Save(file); + return true; + } + + return false; + } + + +/**************************************************************************** +* +* NAME +* Dialog() +* +* DESCRIPTION +* Mpeg playback settings dialog +* +* INPUTS +* NONE +* +* RESULT +* NONE +* +****************************************************************************/ + +void MPGSettings::Dialog(void) + { + // Dialog & button dimensions + int d_dialog_w = 200 *RESFACTOR; + int d_dialog_h = 100 *RESFACTOR; + int d_dialog_x = (((320*RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = 70 * RESFACTOR; + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); + + int d_txt6_h = 7 *RESFACTOR; + int d_margin = 7 *RESFACTOR; + + int d_okay_w = 40 *RESFACTOR; + int d_okay_h = 9 *RESFACTOR; + int d_okay_x = d_dialog_x + d_margin + 20; + int d_okay_y = ((d_dialog_y + d_dialog_h) - (d_okay_h + 20)); + + int d_test_w = 40 *RESFACTOR; + int d_test_h = 9 *RESFACTOR; + int d_test_x = (d_dialog_cx - (d_test_w / 2)); + int d_test_y = ((d_dialog_y + d_dialog_h) - (d_test_h + 20)); + + int d_cancel_w = 40 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = ((d_dialog_x + d_dialog_w) - (d_cancel_w + d_margin + 20)); + int d_cancel_y = ((d_dialog_y + d_dialog_h) - (d_cancel_h + 20)); + + int d_method_w = 165 * RESFACTOR; + int d_method_h = 50 * RESFACTOR; + int d_method_x = (d_dialog_cx - (d_method_w / 2)); + int d_method_y = (d_dialog_y + 40); + + // Button enumerations: + enum + { + BUTTON_OKAY = 100, + BUTTON_TEST, + BUTTON_CANCEL, + BUTTON_METHOD, + NUM_OF_BUTTONS = 4, + }; + + int num_of_buttons = NUM_OF_BUTTONS; + + // Redraw values: in order from "top" to "bottom" layer of the dialog + typedef enum + { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + // Dialog variables: + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + int i; + char* origDevice = NULL; + + // Buttons + ControlClass * commands = NULL; // the button list + + TextButtonClass okaybtn(BUTTON_OKAY, TXT_OK, TPF_BUTTON, + d_okay_x, d_okay_y, d_okay_w, d_okay_h); + + TextButtonClass testbtn(BUTTON_TEST, "Test", TPF_BUTTON, + d_test_x, d_test_y, d_test_w, d_test_h); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ListClass method(BUTTON_METHOD, d_method_x, d_method_y, d_method_w, + d_method_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + // Initialize + Set_Logic_Page(SeenBuff); + + // Create the list + commands = &okaybtn; + testbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + method.Add_Tail(*commands); + + // Fill array of button ptrs + curbutton = 0; + buttons[0] = &okaybtn; + buttons[1] = &testbtn; + buttons[2] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + // Add device to device control + method.Add_Item("DirectX Media (Default)"); + + #ifdef MCIMPEG + for (i = 0; i < mCount; i++) + { + if (mMCIDevices[i].description != NULL) + { + method.Add_Item(mMCIDevices[i].description); + } + } + #endif + + method.Set_Selected_Index(0); + + #ifdef MCIMPEG + // Search for current selection + if (GetDeviceName()) + { + for (i = 0; i < mCount; i++) + { + if (stricmp(GetDeviceName(), mMCIDevices[i].name) == 0) + { + method.Set_Selected_Index(i + 1); + break; + } + } + } + #endif + + // Save original device selection + if (GetDeviceName()) + origDevice = strdup(GetDeviceName()); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + + while (process) + { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) + { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + // Invoke game callback + Call_Back(); + + // Refresh display if needed + if (display) + { + Hide_Mouse(); + + if (display >= REDRAW_BACKGROUND) + { + // Refresh the backdrop + Load_Title_Page(true); + CCPalette.Set(); + + // Draw the background + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption("Movie Settings", d_dialog_x, d_dialog_y, d_dialog_w); + } + + // Redraw buttons + if (display >= REDRAW_BUTTONS) + commands->Flag_List_To_Redraw(); + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Get user input + input = commands->Input(); + + // Process input + switch (input) + { + case (BUTTON_OKAY | KN_BUTTON): + selection = BUTTON_OKAY; + pressed = true; + break; + + case (BUTTON_TEST | KN_BUTTON): + selection = BUTTON_TEST; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + + if (curbutton > (num_of_buttons - 1)) + curbutton = 0; + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_OKAY; + pressed = true; + break; + + default: + break; + } + + if (pressed) + { + // to make sure the selection is correct in case they used the mouse + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton = selection - BUTTON_OKAY; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) + { + case (BUTTON_TEST): + buttons[curbutton]->IsPressed = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + Theme.Fade_Out(); + Hide_Mouse(); + VisiblePage.Clear(); + //PlayMpegMovie("acrop"); //PG + Keyboard->Clear(); + Show_Mouse(); + Theme.Queue_Song(THEME_CRUS); + display = REDRAW_ALL; + + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + buttons[0]->Turn_On(); + curbutton = 0; + break; + + case (BUTTON_OKAY): + process = false; + + if (method.Current_Index() == 0) + { + SetDeviceName(NULL); + } + else + { + i = method.Current_Index(); + #ifdef MCIMPEG + SetDeviceName(mMCIDevices[i - 1].name); + #endif + } + + { + RawFileClass file(CONFIG_FILE_NAME); + Save(file); + } + break; + + case (BUTTON_CANCEL): + process = false; + SetDeviceName(origDevice); + break; + } + + pressed = false; + } + } + + if (origDevice) + free(origDevice); + } + + +#ifdef MCIMPEG +/**************************************************************************** +* +* NAME +* EnumMCI(DeviceDesc, Context) +* +* DESCRIPTION +* MCI device enumeration callback +* +* INPUTS +* DeviceDesc - MCI device description +* Context - User defined context variable +* +* RESULT +* Continue - Continue with next device flag +* +****************************************************************************/ + +bool EnumMCI(MCIDevice* desc, void* context) + { + MPGSettings* mpgset = (MPGSettings*)context; + + // Digital video device type? + if (desc->type == MCI_DEVTYPE_DIGITAL_VIDEO) + { + if (MciMovie) + { + CCFileClass file; + const char* filename; + + filename = file.Set_Name("movies\\acrop.mpg"); + + if (!file.Is_Available()) + { + char buffer[256]; + sprintf(buffer, "Couldn't test MCI device %s\n", desc->name); + VisiblePage.Clear(); + GamePalette.Set(); + Show_Mouse(); + WWMessageBox().Process(buffer); + Hide_Mouse(); + VisiblePage.Clear(); + return true; + } + + if (MciMovie->Open(filename, desc->name)) + { + MciMovie->Close(); + memcpy((void*)&mpgset->mMCIDevices[mpgset->mCount], (void*)desc, + sizeof(MCIDevice)); + mpgset->mCount++; + } + } + } + + return true; + } +#endif +#endif diff --git a/code/redalert/mpgset.h b/code/redalert/mpgset.h new file mode 100644 index 0000000..aae36bb --- /dev/null +++ b/code/redalert/mpgset.h @@ -0,0 +1,48 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef _MPGSETTINGS_H_ +#define _MPGSETTINGS_H_ + +#include "function.h" + +#ifdef DVD +#include "mci.h" +#include "rawfile.h" + +class MPGSettings + { + public: + MPGSettings(const char* deviceName); + MPGSettings(FileClass& file); + virtual ~MPGSettings(void); + + void SetDeviceName(const char* device); + const char* GetDeviceName(void) const + {return mDeviceName;} + bool Save(FileClass& file); + void Dialog(void); + + char* mDeviceName; + + #ifdef MCIMPEG + MCI mMCI; + unsigned int mCount; + MCIDevice* mMCIDevices; + #endif + }; + +#endif // DVD +#endif // _MPGSETTINGS_H_ diff --git a/code/redalert/mplayer.cpp b/code/redalert/mplayer.cpp new file mode 100644 index 0000000..88bebf3 --- /dev/null +++ b/code/redalert/mplayer.cpp @@ -0,0 +1,1174 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPLAYER.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPLAYER.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 14, 1995 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * Clear_Listbox -- clears the given list box * + * Clear_Vector -- clears the given NodeNameType vector * + * Computer_Message -- "sends" a message from the computer * + * Garble_Message -- "garbles" a message * + * Surrender_Dialog -- Prompts user for surrendering * + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +extern bool Is_Mission_Counterstrike (char *file_name); + +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#endif + +/*********************************************************************************************** + * Select_MPlayer_Game -- prompts user for NULL-Modem, Modem, or Network game * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_NORMAL, GAME_MODEM, etc. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +GameType Select_MPlayer_Game (void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 190 *RESFACTOR; + int d_dialog_h = 78 * RESFACTOR; + + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); + + int d_button_w = 80 * RESFACTOR; + int d_button_h = 11 * RESFACTOR; + int d_button_x = d_dialog_x + ((d_dialog_w - d_button_w) / 2); + int d_button_dy = d_button_h + 4 * RESFACTOR; // spacing between top of two buttons + int d_button_y = d_dialog_y + ( 24 * RESFACTOR ); + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + //------------------------------------------------------------------------ + // Button enumerations: + //------------------------------------------------------------------------ + enum { + BUTTON_MODEMSERIAL = 100, + BUTTON_SKIRMISH, + BUTTON_IPX, +#ifdef WOLAPI_INTEGRATION + BUTTON_WOL, // ajw +#endif + BUTTON_CANCEL, + +#ifdef WOLAPI_INTEGRATION + NUM_OF_BUTTONS = 5, // ajw +#else + NUM_OF_BUTTONS = 4, +#endif + }; + + int num_of_buttons = NUM_OF_BUTTONS - (Ipx.Is_IPX() ? 0 : 1); + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, // includes map interior & coord values + REDRAW_BACKGROUND, // includes box, map bord, key, coord labels, btns + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables: + //------------------------------------------------------------------------ + KeyNumType input; // input from user + bool process; // loop while true + RedrawType display; // true = re-draw everything + GameType retval; // return value + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + + //------------------------------------------------------------------------ + // If IPX not active then do only the modem serial dialog + //------------------------------------------------------------------------ +// if ( !Ipx.Is_IPX() ) { +// return( Select_Serial_Dialog() ); +// } + + TextButtonClass modemserialbtn (BUTTON_MODEMSERIAL, TXT_MODEM_SERIAL, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + commands = &modemserialbtn; + d_button_y += d_button_dy; + + TextButtonClass skirmishbtn (BUTTON_SKIRMISH, TXT_SKIRMISH, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + skirmishbtn.Add_Tail(*commands); + d_button_y += d_button_dy; + + TextButtonClass ipxbtn(BUTTON_IPX, TXT_NETWORK, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + + if(Ipx.Is_IPX()) { + ipxbtn.Add_Tail(*commands); + d_button_y += d_button_dy; + } + + TextButtonClass cancelbtn (BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_button_x, d_button_y, d_button_w, d_button_h); + cancelbtn.Add_Tail(*commands); + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + //VisiblePage.Blit(seen_buff_save); + + //------------------------------------------------------------------------ + // Fill array of button ptrs + //------------------------------------------------------------------------ + curbutton = 0; + buttons[0] = &modemserialbtn; + buttons[1] = &skirmishbtn; + + if(Ipx.Is_IPX()) { + buttons[2] = &ipxbtn; + buttons[3] = &cancelbtn; + } else { + buttons[2] = &cancelbtn; + } + + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + seen_buff_save.Blit(VisiblePage); + display = REDRAW_ALL; + } + #endif + + //..................................................................... + // Invoke game callback + //..................................................................... + Call_Back(); + + //..................................................................... + // Refresh display if needed + //..................................................................... + { + Hide_Mouse(); + { + + //............................................................... + // Refresh the backdrop + //............................................................... + Load_Title_Page(true); + CCPalette.Set(); + + //............................................................... + // Draw the background + //............................................................... + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_SELECT_MPLAYER_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + { + commands->Draw_All(); // jmarshall: force redraw everything. + } + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_MODEMSERIAL | KN_BUTTON): + selection = BUTTON_MODEMSERIAL; + pressed = true; + break; + + case (BUTTON_SKIRMISH | KN_BUTTON): + selection = BUTTON_SKIRMISH; + pressed = true; + break; + + case (BUTTON_IPX | KN_BUTTON): + selection = BUTTON_IPX; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case KN_UP: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (num_of_buttons - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_DOWN: + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (num_of_buttons - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case KN_RETURN: + selection = curbutton + BUTTON_MODEMSERIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + + //.................................................................. + // to make sure the selection is correct in case they used the mouse + //.................................................................. + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_MODEMSERIAL; + if(selection == BUTTON_CANCEL && !Ipx.Is_IPX()) curbutton--; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_MODEMSERIAL): + + //............................................................ + // Pop up the modem/serial/com port dialog + //............................................................ + retval = Select_Serial_Dialog(); + + if (retval != GAME_NORMAL) { + process = false; + } else { + buttons[curbutton]->IsPressed = false; + display = REDRAW_ALL; + } + break; + + case (BUTTON_SKIRMISH): + Session.Type = GAME_SKIRMISH; + + if (Com_Scenario_Dialog(true)) { + retval = GAME_SKIRMISH; + process = false; + bAftermathMultiplayer = Is_Aftermath_Installed(); + Session.ScenarioIsOfficial = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + } else { + buttons[curbutton]->IsPressed = false; + Session.Type = GAME_NORMAL; + display = REDRAW_ALL; + } + break; + + case (BUTTON_IPX): + retval = GAME_IPX; + process = false; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } + return(retval); + +} /* end of Select_MPlayer_Game */ + + +/*************************************************************************** + * Clear_Listbox -- clears the given list box * + * * + * This routine assumes the items in the given list box are character * + * buffers; it deletes each item in the list, then clears the list. * + * * + * INPUT: * + * list ptr to listbox * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Listbox(ListClass * list) +{ + char * item; + + //------------------------------------------------------------------------ + // Clear the list box + //------------------------------------------------------------------------ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + list->Flag_To_Redraw(); + +} // end of Clear_Listbox + + +/*************************************************************************** + * Clear_Vector -- clears the given NodeNameType vector * + * * + * INPUT: * + * vector ptr to vector to clear * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +void Clear_Vector(DynamicVectorClass * vector) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the 'Players' Vector + //------------------------------------------------------------------------ + for (i = 0; i < vector->Count(); i++) { + delete (*vector)[i]; + } + vector->Clear(); + +} // end of Clear_Vector + + +/*************************************************************************** + * Computer_Message -- "sends" a message from the computer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +void Computer_Message(void) +{ +#ifdef NEVER + int color; + HousesType house; + HouseClass * ptr; + + //------------------------------------------------------------------------ + // Find the computer house that the message will be from + //------------------------------------------------------------------------ + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr || ptr->IsHuman || ptr->IsDefeated) { + continue; + } + + //..................................................................... + // Decode this house's color + //..................................................................... + color = ptr->RemapColor; + + //..................................................................... + // We now have a 1/4 chance of echoing one of the human players' + // messages back. + //..................................................................... + if (Percent_Chance(25)) { + + //.................................................................. + // Now we have a 1/3 chance of garbling the human message. + //.................................................................. + if (Percent_Chance(33)) { + Garble_Message(Session.LastMessage); + } + + //.................................................................. + // Only add the message if there is one to add. + //.................................................................. + if (strlen(Session.LastMessage)) { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Session.LastMessage, + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + } + else { + Session.Messages.Add_Message(Text_String(TXT_COMPUTER), 0, + Text_String(TXT_COMP_MSG1 + Random_Pick(0, 12)), + color, TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + } + + return; + } +#endif +} /* end of Computer_Message */ + + +#ifdef NEVER +/*************************************************************************** + * Garble_Message -- "garbles" a message * + * * + * INPUT: * + * buf buffer to garble; stores output message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/06/1995 BRR : Created. * + *=========================================================================*/ +static void Garble_Message(char * buf) +{ + char txt[80]; + char punct[20]; // for punctuation + char * p; // working ptr + int numwords; // # words in the phrase + char * words[40]; // ptrs to various words in the phrase + int i,j; + + //------------------------------------------------------------------------ + // Pull off any trailing punctuation + //------------------------------------------------------------------------ + p = buf + strlen(buf) - 1; + while (1) { + if (p < buf) + break; + if (p[0]=='!' || p[0]=='.' || p[0]=='?') { + p--; + } + else { + p++; + break; + } + if (strlen(p) >= (sizeof(punct) - 1) ) { + break; + } + } + strcpy (punct, p); + p[0] = 0; + + for (i = 0; i < 40; i++) { + words[i] = NULL; + } + + //------------------------------------------------------------------------ + // Copy the original buffer + //------------------------------------------------------------------------ + strcpy(txt, buf); + + //------------------------------------------------------------------------ + // Split it up into words + //------------------------------------------------------------------------ + p = strtok (txt, " "); + numwords = 0; + while (p) { + words[numwords] = p; + numwords++; + p = strtok (NULL, " "); + } + + //------------------------------------------------------------------------ + // Now randomly put the words back. Don't use the real random-number + // generator, since different machines will have different LastMessage's, + // and will go out of sync. + //------------------------------------------------------------------------ + buf[0] = 0; + for (i = 0; i < numwords; i++) { + j = Sim_IRandom(0, numwords); + if (words[j] == NULL) { // this word has been used already + i--; + continue; + } + strcat(buf, words[j]); + words[j] = NULL; + if (i < numwords-1) + strcat(buf, " "); + } + strcat(buf, punct); + +} /* end of Garble_Message */ +#endif + + +/*************************************************************************** + * Surrender_Dialog -- Prompts user for surrendering * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = user cancels, 1 = user wants to surrender. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(int text) +{ + return Surrender_Dialog( Text_String( text ) ); +} +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. +int Surrender_Dialog(const char* text) +#else +int Surrender_Dialog(int text) +#endif +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 240*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_OK_W = 45*RESFACTOR, // OK width + D_OK_H = 9*RESFACTOR, // OK height + + D_CANCEL_W = 45*RESFACTOR, // Cancel width + D_CANCEL_H = 9*RESFACTOR, // Cancel height + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + }; + + int D_DIALOG_X = (SeenBuff.Get_Width() - D_DIALOG_W) / 2; // centered x-coord + int D_DIALOG_Y = (SeenBuff.Get_Height() - D_DIALOG_H) / 2; // centered y-coord + int D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2); // coord of x-center + + int D_OK_X = D_DIALOG_CX - D_OK_W - 5 * RESFACTOR; // OK x + int D_OK_Y = D_DIALOG_Y + D_DIALOG_H - D_OK_H - D_MARGIN * 2; // OK y + + int D_CANCEL_X = D_DIALOG_CX + 5 * RESFACTOR; // Cancel x + int D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - D_CANCEL_H - D_MARGIN * 2; // Cancel y + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + + buttons[0] = &okbtn; + buttons[1] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... +#ifdef FIXIT_VERSION_3 // Stalemate games. + Fancy_Text_Print(text, + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(Text_String(text), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); +// jmarshall + Call_Back(); +// jmarshall end + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_OK | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_CANCEL | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +/*************************************************************************** + * Abort_Dialog -- Prompts user for confirmation on aborting the mission * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = user confirms abort, 0 = user cancels * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/05/1995 BRR : Created. * + *=========================================================================*/ +int Abort_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + enum { + D_DIALOG_W = 170*RESFACTOR, // dialog width + D_DIALOG_H = 63*RESFACTOR, // dialog height + D_DIALOG_X = ((320*RESFACTOR - D_DIALOG_W) / 2),// centered x-coord + D_DIALOG_Y = ((200*RESFACTOR - D_DIALOG_H) / 2),// centered y-coord + D_DIALOG_CX = D_DIALOG_X + (D_DIALOG_W / 2), // coord of x-center + + D_TXT6_H = 7*RESFACTOR, // ht of 6-pt text + D_MARGIN = 5*RESFACTOR, // margin width/height + D_TOPMARGIN = 20*RESFACTOR, // top margin + + D_YES_W = 45*RESFACTOR, // YES width + D_YES_H = 9*RESFACTOR, // YES height + D_YES_X = D_DIALOG_CX - D_YES_W - 5*RESFACTOR, // YES x + D_YES_Y = D_DIALOG_Y + D_DIALOG_H - D_YES_H - D_MARGIN*2, // YES y + + D_NO_W = 45*RESFACTOR, // Cancel width + D_NO_H = 9*RESFACTOR, // Cancel height + D_NO_X = D_DIALOG_CX + 5*RESFACTOR, // Cancel x + D_NO_Y = D_DIALOG_Y + D_DIALOG_H - D_NO_H - D_MARGIN*2, // Cancel y + }; + + //------------------------------------------------------------------------ + // Button enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_YES = 100, + BUTTON_NO, + }; + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + ControlClass * commands = NULL; // the button list + + TextButtonClass yesbtn(BUTTON_YES, TXT_YES, TPF_BUTTON, D_YES_X, D_YES_Y, D_YES_W, D_YES_H); + + TextButtonClass nobtn(BUTTON_NO, TXT_NO, TPF_BUTTON, D_NO_X, D_NO_Y, D_NO_W, D_NO_H); + + int curbutton; + TextButtonClass * buttons[2]; + curbutton = 0; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + Set_Logic_Page(SeenBuff); + + //------------------------------------------------------------------------ + // Create the button list + //------------------------------------------------------------------------ + commands = &yesbtn; + nobtn.Add_Tail(*commands); + + buttons[0] = &yesbtn; + buttons[1] = &nobtn; + buttons[curbutton]->Turn_On(); + + //------------------------------------------------------------------------ + // Main Processing Loop + //------------------------------------------------------------------------ + int retcode = 0; + bool display = true; + bool process = true; + while (process) { + + //..................................................................... + // Invoke game callback + //..................................................................... + if (Session.Type != GAME_SKIRMISH) { + if (Main_Loop()) { + retcode = 0; + process = false; + } + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + display = false; + + //.................................................................. + // Display the dialog box + //.................................................................. + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_NONE, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + //............................................................... + // Draw the captions + //............................................................... + Fancy_Text_Print(Text_String(TXT_CONFIRM_EXIT), + D_DIALOG_CX, D_DIALOG_Y + D_TOPMARGIN, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_CENTER | TPF_TEXT); + + //.................................................................. + // Redraw the buttons + //.................................................................. + commands->Flag_List_To_Redraw(); + Show_Mouse(); + } + + //..................................................................... + // Get user input + //..................................................................... + KeyNumType input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + case (BUTTON_YES | KN_BUTTON): + retcode = 1; + process = false; + break; + + case (BUTTON_NO | KN_BUTTON): + retcode = 0; + process = false; + break; + + case (KN_RETURN): + if (curbutton == 0) { + retcode = 1; + } else { + retcode = 0; + } + process = false; + break; + + case (KN_ESC): + retcode = 0; + process = false; + break; + + case (KN_RIGHT): + buttons[curbutton]->Turn_Off(); + curbutton++; + if (curbutton > 1) { + curbutton = 0; + } + buttons[curbutton]->Turn_On(); + break; + + case (KN_LEFT): + buttons[curbutton]->Turn_Off(); + curbutton--; + if (curbutton < 0) { + curbutton = 1; + } + buttons[curbutton]->Turn_On(); + break; + + default: + break; + } + } + + //------------------------------------------------------------------------ + // Redraw the display + //------------------------------------------------------------------------ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + + return (retcode); +} + + +#if(TEN) +/*************************************************************************** + * Read_TEN_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_TEN_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + if (Session.TenPlayerID == -1) { + return (0); + } + Session.ColorIdx = (PlayerColorType)Session.TenPlayerID; + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + Session.NumPlayers = ini.Get_Int("Options", "Players", 2); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * Read_MPATH_Game_Options -- reads multiplayer game options from disk * + * * + * This routine is used for multiplayer games which read the game options * + * from disk, rather than through a connection dialog. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/11/1996 BRR : Created. * + *=========================================================================*/ +int Read_MPATH_Game_Options(void) +{ + INIClass ini; + if (!ini.Load(RawFileClass(Session.OptionsFile))) { + return (0); + } + + ini.Get_String("Options", "Handle", "Noname", Session.Handle, + sizeof(Session.Handle)); + Session.ColorIdx = (PlayerColorType)ini.Get_Int("Options", "Color", 0); + Session.House = (HousesType) + ((int)HOUSE_USSR + ini.Get_Int("Options", "Side", 0)); + Session.Options.Credits = ini.Get_Int("Options", "Credits", 3000); + Session.Options.Bases = ini.Get_Int("Options", "Bases", 1); + Session.Options.Tiberium = ini.Get_Int("Options", "Tiberium", 1); + Session.Options.Goodies = ini.Get_Int("Options", "Crates", 1); + Special.IsShadowGrow = ini.Get_Int ("Options", "Shadow", 0); + BuildLevel = ini.Get_Int("Options", "BuildLevel", 3); + Session.Options.UnitCount = ini.Get_Int("Options", "UnitCount", 5); + Seed = ini.Get_Int("Options", "Seed", 0); + Special.IsCaptureTheFlag = ini.Get_Int("Options", "CapFlag", 0); + Session.Options.AIPlayers = ini.Get_Int("Options", "AI", 0); + + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ + ini.Get_String("Options", "Scenario", "Black Acres", + Session.Options.ScenarioDescription, + sizeof (Session.Options.ScenarioDescription)); + + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), + Session.Options.ScenarioDescription) ) { + Session.Options.ScenarioIndex = i; + strcpy(Session.ScenarioFileName, Session.Scenarios[i]->Get_Filename()); + strcpy(Scen.ScenarioName, Session.Scenarios[i]->Get_Filename()); + break; + } + } + + if (Session.Options.ScenarioIndex == -1) { + WWMessageBox().Process("Scenario not found!"); + //Prog_End(); + Emergency_Exit(0); + } + + Options.GameSpeed = 0; + + Session.MaxAhead = ini.Get_Int("Timing", "MaxAhead", 9); + Session.FrameSendRate = ini.Get_Int("Timing", "SendRate", 3); + Session.NetResponseTime = ini.Get_Int("Timing","Latency",600); + + return (1); +} +#endif // MPATH + +/************************** end of mplayer.cpp *****************************/ diff --git a/code/redalert/mplib.cpp b/code/redalert/mplib.cpp new file mode 100644 index 0000000..f46128b --- /dev/null +++ b/code/redalert/mplib.cpp @@ -0,0 +1,127 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "types.h" +#include "mgenord.h" +#include "magic.h" +#include "rtq.h" +#include +#include +#include +#include "mplib.h" + +#define CHUNNEL_INT 0x48 + +typedef union REGS REGISTERS; + +void +Yield(void) +{ + REGISTERS regs; + + regs.w.ax = 0x1680; + int386(0x2f, ®s, ®s); +} + +void +PostWindowsMessage(void) +{ + REGISTERS regs; + + regs.x.eax = DPMIAPI_POST_WINDOWS_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = 0; + regs.x.ecx = 0; + int386(CHUNNEL_INT, ®s, ®s); +} + +int MGenGetQueueCtr(int qNo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETQUEUECTR_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qNo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +RTQ_NODE *MGenMoveTo(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MOVENODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetNode(int q) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_GETNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = q; + int386(CHUNNEL_INT, ®s, ®s); + + return (RTQ_NODE *) regs.x.eax; +} + +RTQ_NODE *MGenGetMasterNode(unsigned *size) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MASTERNODE_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + *size = regs.x.ecx; + + return (RTQ_NODE *) regs.x.eax; +} + +int MGenFlushNodes(int qFrom, int qTo) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_FLUSHNODE_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = qFrom; + regs.x.ecx = qTo; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenMCount(unsigned lowerOrderBits, unsigned upperOrderBits) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_MCOUNT_ORD << 16 | MGENVXD_DEVICE_ID; + regs.x.ebx = lowerOrderBits; + regs.x.ecx = upperOrderBits; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + +int MGenSanityCheck(void) +{ + REGISTERS regs; + + regs.x.eax = MGENVXD_SANITYCHECK_ORD << 16 | MGENVXD_DEVICE_ID; + int386(CHUNNEL_INT, ®s, ®s); + + return regs.x.eax; +} + \ No newline at end of file diff --git a/code/redalert/mpmgrd.cpp b/code/redalert/mpmgrd.cpp new file mode 100644 index 0000000..33254e2 --- /dev/null +++ b/code/redalert/mpmgrd.cpp @@ -0,0 +1,363 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include "mpmgrd.h" + +extern "C" { +#include +#include +#include +#include +#include "types.h" +#include "rtq.h" +#include "services.h" +} +#include "mplib.h" +#include "mplpc.h" + + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +typedef struct { + DWORD address; + char Data[1]; +} packet; + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +#define FREEQUEUE 0 +#define DOSWORKQUEUE 1 +#define WINWORKQUEUE 2 +#define WINSENDQUEUE 3 +#define DOSPENDINGQUEUE 4 +#define WINWORKQUEUE2 5 + +// 6, 7, 8, taken up by LPC services + +#define GDOSWORKQUEUE 14 +#define GWINWORKQUEUE 15 +#define GWINSENDQUEUE 16 +#define GDOSPENDINGQUEUE 17 +#define GWINWORKQUEUE2 18 + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + unsigned size; + + MGenGetMasterNode(&size); + if (size != sizeof(RTQ_NODE)) { + exit(-234); + } + _myAddr = LPCGetMPAddr(); + _nConnections = 0; + + for (int i = 0; i < CONNECT_MAX; i++) { + _Connections[i] = 0; + strcpy(_Names[i], ""); + } +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int /* ack_req */, + int conn_id) +{ + RTQ_NODE *n; + int idx = Connection_Index(conn_id); + + if (_nConnections == 0) { + return (STATUS_OK); + } + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (conn_id == CONNECTION_NONE) { + p->address = BROADCAST_ADDR; + } else { + p->address = _Connections[idx]; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, WINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + RTQ_NODE *n; + int i; + + if ((n = MGenMoveTo(DOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + *conn_id = CONNECTION_NONE; + for (i = 0; i < _nConnections; i++) { + if (p->address == _Connections[i]) { + (*conn_id) = _ID[i]; + break; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int /*ack_req*/, + int address) +{ + RTQ_NODE *n; + + while ((n = MGenMoveTo(FREEQUEUE, DOSWORKQUEUE)) == 0); + + packet *p = (packet *) n->rtqDatum; + + if (address == 0) { + p->address = BROADCAST_ADDR; + } else { + p->address = address; + } + + memcpy(p->Data, buf, buflen); + n->rtqUpCtr = (WORD)(buflen + sizeof(DWORD)); + + MGenMoveTo(DOSWORKQUEUE, GWINSENDQUEUE); + PostWindowsMessage(); + Yield(); + + return STATUS_OK; +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + RTQ_NODE *n; + + if ((n = MGenMoveTo(GDOSPENDINGQUEUE, DOSWORKQUEUE)) == 0) { + *buflen = 0; + return 0; + } + + packet *p = (packet *) n->rtqDatum; + + int lentocpy = n->rtqUpCtr - sizeof(DWORD); + + if (address) { + if (p->address == BROADCAST_ADDR) { + *address = 0; + } else { + *address = p->address; + } + } + + memcpy(buf, p->Data, lentocpy); + + *buflen = lentocpy; + + MGenMoveTo(DOSWORKQUEUE, FREEQUEUE); + + return STATUS_OK; +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + _Connections[_nConnections] = address; + _ID[_nConnections] = id; + strcpy(_Names[_nConnections], name); + _nConnections++; + return STATUS_OK; +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == -1) + return 0; + + for (i = idx; i < _nConnections - 1; i++) { + _Connections[i] = _Connections[i+1]; + _ID[i] = _ID[i+1]; + strcpy (_Names[i], _Names[i+1]); + } + _nConnections--; + return STATUS_OK; +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (NULL); + } + + return _Names[idx]; +} + +int +MPlayerManClass::Connection_Address(int id) +{ + int idx = Connection_Index(id); + if (idx==-1) { + return (0); + } + + return _Connections[idx]; +} + +int +MPlayerManClass::Num_Connections(void) +{ + return _nConnections; +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return _ID[index]; +} + +int +MPlayerManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < _nConnections; i++) { + if (_ID[i] == id) { + return i; + } + } + + return -1; +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return MGenGetQueueCtr(DOSPENDINGQUEUE); +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return STATUS_OK; +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + TGAMEDEF game_def; + int sz = sizeof(game_def); + + GetGameDef(&game_def, &sz); + + return (game_def.numPlayers - 1); + +} + + +void MPlayerManClass::Flush_All(void) +{ + MGenFlushNodes(DOSPENDINGQUEUE, FREEQUEUE); + MGenFlushNodes(GDOSPENDINGQUEUE, FREEQUEUE); +} + diff --git a/code/redalert/mpmgrd.h b/code/redalert/mpmgrd.h new file mode 100644 index 0000000..cb8c34a --- /dev/null +++ b/code/redalert/mpmgrd.h @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + int _myAddr; + int _Connections[CONNECT_MAX]; + int _ID[CONNECT_MAX]; + char _Names[CONNECT_MAX][MAX_NAME_LEN]; + int _nConnections; +}; + +#endif // mpmgr_h + + \ No newline at end of file diff --git a/code/redalert/mpmgrw.cpp b/code/redalert/mpmgrw.cpp new file mode 100644 index 0000000..5a7ee02 --- /dev/null +++ b/code/redalert/mpmgrw.cpp @@ -0,0 +1,297 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#if (0)//PG +#ifndef MP_LOAD_DLL_DYNAMICALLY +#define MP_LOAD_DLL_DYNAMICALLY +#endif + +#include "mpmgrw.h" + +extern "C" { +#include +#include +#include +#include +} + +#include + +#define STATUS_OK 1 +#define STATUS_BAD 0 + +#define BROADCAST_ADDR 0 + +#define PRIVATE 0 +#define PUBLIC 1 + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef void __cdecl (*MPlayerInit_Type)(void); +typedef void __cdecl (*MPlayerDestroy_Type)(void); +typedef int __cdecl (*Send_Private_Message_Type)(void *buf, + int buflen, + int ack_req, + int conn_id); +typedef int __cdecl (*Get_Private_Message_Type)(void *buf, int *buflen, + int *conn_id); +typedef int __cdecl (*Send_Global_Message_Type)(void *buf, int buflen, int ack_req, + int addr); +typedef int __cdecl (*Get_Global_Message_Type)(void *buf, int *buflen, int *address); +typedef int __cdecl (*Create_Connection_Type)(int id, char *name, int address); +typedef int __cdecl (*Delete_Connection_Type)(int id); +typedef char * __cdecl (*Connection_Name_Type)(int id); +typedef int __cdecl (*Connection_Address_Type)(int id); +typedef int __cdecl (*Num_Connections_Type)(void); +typedef int __cdecl (*Connection_ID_Type)(int id); +typedef int __cdecl (*Connection_Index_Type)(int id); +typedef int __cdecl (*Init_Type)(void); +typedef int __cdecl (*Find_Num_Connections_Type)(void); + +MPlayerInit_Type MPlayerManCreate; +MPlayerDestroy_Type MPlayerManDestroy; +Send_Private_Message_Type Send_Private_Message_DLL; +Get_Private_Message_Type Get_Private_Message_DLL; +Send_Global_Message_Type Send_Global_Message_DLL; +Get_Global_Message_Type Get_Global_Message_DLL; +Create_Connection_Type Create_Connection_DLL; +Delete_Connection_Type Delete_Connection_DLL; +Connection_Name_Type Connection_Name_DLL; +Connection_Address_Type Connection_Address_DLL; +Num_Connections_Type Num_Connections_DLL; +Connection_ID_Type Connection_ID_DLL; +Connection_Index_Type Connection_Index_DLL; +Init_Type Init_DLL; +Find_Num_Connections_Type Find_Num_Connections_DLL; + + +FARPROC MPGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { + FARPROC ret=GetProcAddress(hModule, lpProcName); + if (!ret) { + char msg[255]; + sprintf(msg, "Unable to load function %s from %s", + lpProcName, "RA95MP.DLL"); + MessageBox(0, msg, "Warning", MB_OK); + } + return ret; +} + +MPlayerManClass::MPlayerManClass(void) : ConnManClass() +{ + HMODULE lib; + + lib = LoadLibrary("ra95mp.dll"); + if (lib != 0) { + MPlayerManCreate = (MPlayerInit_Type) MPGetProcAddress(lib, + "MPlayerManCreate"); + MPlayerManDestroy = (MPlayerDestroy_Type) MPGetProcAddress(lib, + "MPlayerManDestroy"); + Send_Private_Message_DLL = (Send_Private_Message_Type) MPGetProcAddress(lib, + "Send_Private_Message"); + Get_Private_Message_DLL = (Get_Private_Message_Type) MPGetProcAddress(lib, + "Get_Private_Message"); + Send_Global_Message_DLL = (Send_Global_Message_Type) MPGetProcAddress(lib, + "Send_Global_Message"); + Get_Global_Message_DLL = (Get_Global_Message_Type) MPGetProcAddress(lib, + "Get_Global_Message"); + Create_Connection_DLL = (Create_Connection_Type) MPGetProcAddress(lib, + "Create_Connection"); + Delete_Connection_DLL = (Delete_Connection_Type) MPGetProcAddress(lib, + "Delete_Connection"); + Connection_Name_DLL = (Connection_Name_Type) MPGetProcAddress(lib, + "Connection_Name"); + Connection_Address_DLL = (Connection_Address_Type) MPGetProcAddress(lib, + "Connection_Address"); + Num_Connections_DLL = (Num_Connections_Type) MPGetProcAddress(lib, + "Num_Connections"); + Connection_ID_DLL = (Connection_ID_Type) MPGetProcAddress(lib, + "Connection_ID"); + Connection_Index_DLL = (Connection_Index_Type) MPGetProcAddress(lib, + "Connection_Index"); + Init_DLL = (Init_Type) MPGetProcAddress(lib, + "Init"); + Find_Num_Connections_DLL = (Find_Num_Connections_Type) MPGetProcAddress(lib, + "Find_Num_Connections"); + } else { + MessageBox(0, "RA95MP.DLL not found!", "Warning", MB_OK); + exit(0); + } + + if (MPlayerManCreate != 0) { + MPlayerManCreate(); + } +} + +MPlayerManClass::~MPlayerManClass() +{ + MPlayerManDestroy(); +} + +// here's what we do to get private & broadcasts over the same chunnel +// we package up an extra dword at the beginning to indicate the address + +int +MPlayerManClass::Send_Private_Message(void *buf, + int buflen, + int ack_req, + int conn_id) +{ + return Send_Private_Message_DLL(buf, buflen, ack_req, conn_id); +} + +int +MPlayerManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + return Get_Private_Message_DLL(buf, buflen, conn_id); +} + +int +MPlayerManClass::Send_Global_Message(void *buf, int buflen, int ack_req, + int addr) +{ + return Send_Global_Message_DLL(buf, buflen, ack_req, addr); +} + +int +MPlayerManClass::Get_Global_Message(void *buf, int *buflen, int *address) +{ + return Get_Global_Message_DLL(buf, buflen, address); +} + +int +MPlayerManClass::Service(void) +{ + return STATUS_OK; +} + +int +MPlayerManClass::Create_Connection(int id, char *name, int address) +{ + return Create_Connection_DLL(id, name, address); +} + +int +MPlayerManClass::Delete_Connection(int id) +{ + return Delete_Connection_DLL(id); +} + +char * +MPlayerManClass::Connection_Name(int id) +{ + return Connection_Name_DLL(id); +} + +int +MPlayerManClass::Connection_Address(int id) +{ + return Connection_Address_DLL(id); +} + +int +MPlayerManClass::Num_Connections(void) +{ + return Num_Connections_DLL(); +} + +int +MPlayerManClass::Connection_ID(int index) +{ + return Connection_ID_DLL(index); +} + +int +MPlayerManClass::Connection_Index(int id) +{ + return Connection_Index_DLL(id); +} + +int +MPlayerManClass::Global_Num_Send(void) +{ + return 0; +} + +int +MPlayerManClass::Global_Num_Receive(void) +{ + return 0; +// return MGenGetQueueCtr(GDOSPENDINGQUEUE); +} + +int +MPlayerManClass::Private_Num_Send(int /*id*/) +{ + return 0; +} + +int +MPlayerManClass::Private_Num_Receive(int /*id*/) +{ + return 0; +} + +void +MPlayerManClass::Reset_Response_Time(void) +{ + // unsupported +} + +unsigned long +MPlayerManClass::Response_Time(void) +{ + return (160 * 60) / 1000; // 160 microseconds one way (9 ticks) +} + +void +MPlayerManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, + unsigned long /*timeout*/) +{ + // unsupported +} + +void +MPlayerManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char ** /*names*/, + int /*namestart*/, int /*namecount*/) +{ + // unsupported +} + +void +MPlayerManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // unsupported +} + +int +MPlayerManClass::Init(void) +{ + return Init_DLL(); +} + +int MPlayerManClass::Find_Num_Connections(void) +{ + return Find_Num_Connections_DLL(); +} + + +void MPlayerManClass::Flush_All(void) +{ +} + +#endif \ No newline at end of file diff --git a/code/redalert/mpmgrw.h b/code/redalert/mpmgrw.h new file mode 100644 index 0000000..bfe7e6d --- /dev/null +++ b/code/redalert/mpmgrw.h @@ -0,0 +1,85 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifndef mpmgr_h +#define mpmgr_h + +#include "connmgr.h" +#include "mplayer.h" +#include + +// maximum number of connections +#define CONNECT_MAX 7 +#define MAX_NAME_LEN 128 + +class MPlayerManClass : public ConnManClass { +public: + MPlayerManClass(void); + ~MPlayerManClass(void); + + // queues up incoming packets appropriately + int Service(void); + + // initialization + int Init(void); + int Find_Num_Connections(void); + void Flush_All(void); + + // send/receive data + int Send_Private_Message(void *buf, int buflen, int ack_req = 1, int conn_id = CONNECTION_NONE); + int Get_Private_Message(void *buf, int *buflen, int *conn_id); + + int Send_Global_Message(void *buf, int buflen, int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, int *address = 0); + + // manage connections + int Num_Connections(void); + int Connection_ID(int index); + int Connection_Index(int id); + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // queueing routines + + int Global_Num_Send(void); + int Global_Num_Receive(void); + int Private_Num_Send(int id = CONNECTION_NONE); + int Private_Num_Receive(int id = CONNECTION_NONE); + + // timing magnagement + void Reset_Response_Time(void); + unsigned long Response_Time(void); + void Set_Timing(unsigned long retrydelta, unsigned long maxretries, unsigned long timeout); + + // debug + void Configure_Debug(int index, int type_offset, int type_size, char **names, int namestart, + int namecount); + void Mono_Debug_Print(int index, int refresh); + +private: + //HGULP _gulp; + //HGULP _pgulp; + //GAMEDEF _gameDef; + //int _myAddr; + //int _Connections[CONNECT_MAX]; + //int _ID[CONNECT_MAX]; + //char _Names[CONNECT_MAX][MAX_NAME_LEN]; + //int _nConnections; +}; + +#endif // mpmgr_h + diff --git a/code/redalert/mpu.cpp b/code/redalert/mpu.cpp new file mode 100644 index 0000000..9e101b2 --- /dev/null +++ b/code/redalert/mpu.cpp @@ -0,0 +1,45 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPU.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPU.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/17/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "mpu.h" + + +#ifdef __BORLANDC__ +unsigned long __cdecl Get_CPU_Clock(unsigned long & high) +{ + __asm db 0fh,031h + __asm mov [high],edx + return(_EAX); +} +#endif diff --git a/code/redalert/mpu.h b/code/redalert/mpu.h new file mode 100644 index 0000000..fb5e053 --- /dev/null +++ b/code/redalert/mpu.h @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MPU.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MPU.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/15/96 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MPU_H +#define MPU_H + + +/*********************************************************************************************** + * Get_CPU_Clock -- Fetches the current CPU clock time. * + * * + * This routine will return the internal Pentium clock accumulator. This accumulator is * + * incremented every clock tick. Since this clock value can get very very large, the value * + * returned is in 64 bits. The low half is returned directly, the high half is stored in * + * location specified. * + * * + * INPUT: high -- Reference to the high value of the 64 bit clock number. * + * * + * OUTPUT: Returns with the low half of the CPU clock value. * + * * + * WARNINGS: This instruction is only available on Pentium or later processors. * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +#if (0) +unsigned long Get_CPU_Clock(unsigned long & high); +#ifndef __BORLANDC__ +#pragma aux Get_CPU_Clock parm [esi] \ + modify [edx] \ + value [eax] = \ + "db 0fh,031h" \ + "mov [esi],edx" +#endif +#endif + +#endif diff --git a/code/redalert/msgbox.cpp b/code/redalert/msgbox.cpp new file mode 100644 index 0000000..7418406 --- /dev/null +++ b/code/redalert/msgbox.cpp @@ -0,0 +1,530 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGBOX.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : August 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMessageBox::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "msgbox.h" +#include "gadget.h" + + +#ifdef FIXIT_VERSION_3 +bool cancel_current_msgbox = false; +#endif + +/*********************************************************************************************** + * WWMessageBox::Process -- pops up a message with yes/no, etc * + * * + * This function displays a dialog box with a one-line message, and * + * up to two buttons. The 2nd button is optional. The buttons default * + * to "OK" and nothing, respectively. The hotkeys for the buttons are * + * RETURN and ESCAPE. * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 11/08/1994 BR : Created. * + * 05/18/1995 JLB : Uses new font and dialog style. * + * 08/24/1995 JLB : Handles three buttons. * + *=============================================================================================*/ +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int WWMessageBox::Process(const char * msg, const char * b1txt, const char * b2txt, const char * b3txt, bool preserve) +{ +#define BUFFSIZE (511) + char buffer[BUFFSIZE]; + int retval = 0; + bool process; // loop while true + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + void * back; + BOOL display = TRUE; // display level + int realval[5]; + +#ifndef WIN32 + int preservex,preservey,preservew,preserveh; +#endif + + #ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); + #endif + + if (b1txt != NULL && *b1txt == '\0') b1txt = NULL; + if (b2txt != NULL && *b2txt == '\0') b2txt = NULL; + if (b3txt != NULL && *b3txt == '\0') b3txt = NULL; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt != NULL) { + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + (2 * RESFACTOR); + bwidth = max((String_Pixel_Width(b1txt) + (8 * RESFACTOR)), (30 * RESFACTOR)); + + if (b2txt != NULL) { + numbuttons = 2; + bwidth = max(((int)String_Pixel_Width( b2txt ) + (8 * RESFACTOR)), bwidth); + + if (b3txt != NULL) { + numbuttons = 3; + } + + } else { + numbuttons = 1; + } + } +// jmarshall + else { + bwidth = 0; + bheight = 0; + } +// jmarshall end + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, msg, BUFFSIZE-1); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int width; + int height; + int lines = Format_Window_String(buffer, 255 * RESFACTOR, width, height); + TextPrintType tpf = TPF_TEXT; + + width = max(width, (90 * RESFACTOR)); + width += 40 * RESFACTOR; + height += (numbuttons == 0) ? (40 * RESFACTOR) : (60 * RESFACTOR); + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + int printx = x + (20 * RESFACTOR); + + /* + ** Special hack to center a one line dialog box text. + */ + if (lines == 1) { + printx = x + width/2; + tpf = tpf | TPF_CENTER; + } + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + #ifdef WIN32 +// jmarshall - not needed + //VisiblePage.Blit(seen_buff_save); +// jmarshall end + #endif + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + //DOS BUILD GERMAN BUTTONS NEED TO ONE ON TOP OF THE OTHER VG 11/6/96 + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Center button. + */ + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + (20 * RESFACTOR)), y + height - (bheight + (15 * RESFACTOR)), bwidth); + + /* + ** Right button. + */ + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, 0, y + height - (bheight + (15 * RESFACTOR))); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + memset(buttons, '\0', sizeof(buttons)); + if (numbuttons > 0) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else { + if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + if (preserve) { +#ifndef WIN32 + preservex = max(0, x-4); + preservey = max(0, y-4); + preservew = min(width+8, 320-preservex); + preserveh = min(height+8, 200-preservey); + back = new char[preservew * preserveh]; + SeenBuff.To_Buffer(preservex, preservey, preservew, preserveh, back, preservew * preserveh); +#else + back = new char[width * height]; + SeenBuff.To_Buffer(x, y, width, height, back, width * height); +#endif + } + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + if (buttonlist) { + process = true; + pressed = false; + while (process) { +// jmarshall + // If not in game, display the title screen. + if (g_inMainMenu) { + Load_Title_Screen("TITLE.PCX", NULL, NULL); + } +// jmarshall end + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + seen_buff_save.Blit(VisiblePage); + display = true; + } + #endif + + if (display) { + //display = false; + + Hide_Mouse(); + Dialog_Box(x, y, width, height); + Draw_Caption(Caption, x, y, width); + + /* + ** Draw the body of the message. + */ + Fancy_Text_Print(buffer, printx, y + 20*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, tpf); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); +#ifdef FIXIT_VERSION_3 + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = KN_ESC; + } +#endif + switch (input) { + case (KN_ESC): + selection = realval[numbuttons-1]; + pressed = true; + +#ifdef NEVER + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } +#endif + break; + + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (BUTTON_2|BUTTON_FLAG): + if (numbuttons > 2) { + selection = realval[2]; + } else { + selection = realval[1]; + } + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = realval[curbutton]; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + break; + } + + if (pressed) { + + TextButtonClass * toggle; + /* + ** Turn all the buttons off. + */ + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_1); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_2); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + toggle = (TextButtonClass *)buttonlist->Extract_Gadget(BUTTON_3); + if (toggle != NULL) { + toggle->Turn_Off(); + toggle->IsPressed = false; + } + + /* + ** Turn on and depress the button that was selected. + */ + if (selection == BUTTON_1 || selection == BUTTON_2 || selection == BUTTON_3) { + TextButtonClass * toggle = (TextButtonClass *)buttonlist->Extract_Gadget(selection); + if (toggle != NULL) { + toggle->Turn_On(); +// toggle->IsOn = true; + toggle->IsPressed = true; + } + } + Hide_Mouse(); + buttonlist->Draw_All(true); + Show_Mouse(); + + switch (selection) { + case (BUTTON_1): + retval = 0; + process = false; + break; + + case (BUTTON_2): + retval = 1; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + /* + ** Restore the screen if necessary. + */ + if (preserve) { + Hide_Mouse(); + if (SeenBuff.Lock()) { + #ifdef WIN32 + Buffer_To_Page(x, y, width, height, back, &SeenBuff); + #else + MCGA_Buffer_To_Page(preservex, preservey, preservew, preserveh, back, &SeenBuff); + #endif + } + SeenBuff.Unlock(); + + delete[] back; + back = NULL; + Show_Mouse(); + } + return(retval); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- this one takes integer text arguments * + * * + * INPUT: * + * msg message to display * + * b1txt text for 1st button * + * b2txt text for 2nd button; NULL = no 2nd button * + * * + * OUTPUT: * + * # of button selected (0 = 1st) * + * * + * WARNINGS: * + * 'msg' text must be <= 38 chars * + * 'b1txt' and 'b2txt' must each be <= 18 chars * + * * + * HISTORY: * + * 12/12/1994 BR : Created. * + * 06/18/1995 JLB : Simplified. * + *=============================================================================================*/ +int WWMessageBox::Process(int msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(Text_String(msg), b1txt, b2txt, b3txt, preserve)); +} + + +/*********************************************************************************************** + * WWMessageBox::Process -- Displays message box. * + * * + * This routine will display a message box and wait for player input. It varies from the * + * other message box functions only in the type of parameters it takes. * + * * + * INPUT: msg -- Pointer to text string for the message itself. * + * * + * b1txt -- Text number for the "ok" button. * + * * + * b2txt -- Text number for the "cancel" button. * + * * + * OUTPUT: Returns with the button selected. "true" if "OK" was pressed, and "false" if * + * "CANCEL" was pressed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +int WWMessageBox::Process(char const * msg, int b1txt, int b2txt, int b3txt, bool preserve) +{ + return(Process(msg, Text_String(b1txt), Text_String(b2txt), Text_String(b3txt), preserve)); +} diff --git a/code/redalert/msgbox.h b/code/redalert/msgbox.h new file mode 100644 index 0000000..cb4c720 --- /dev/null +++ b/code/redalert/msgbox.h @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGBOX.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGBOX_H +#define MSGBOX_H + +#include "jshell.h" + +class WWMessageBox +{ + int Caption; + + public: + WWMessageBox(int caption=TXT_NONE) {Caption = caption;}; + int Process(const char *msg, const char *b1txt, const char *b2txt=NULL, const char *b3txt=NULL, bool preserve=false); + int Process(int msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); + int Process(char const *msg, int b1txt=TXT_OK, int b2txt=TXT_NONE, int b3txt=TXT_NONE, bool preserve=false); +}; + +#endif + diff --git a/code/redalert/msglist.cpp b/code/redalert/msglist.cpp new file mode 100644 index 0000000..d8f1dea --- /dev/null +++ b/code/redalert/msglist.cpp @@ -0,0 +1,1408 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGLIST.CPP 2 3/04/97 2:52p Joe_bostic $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : March 4, 1997 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * MessageListClass::MessageListClass -- constructor * + * MessageListClass::~MessageListClass -- destructor * + * MessageListClass::Init -- Inits message system, sets options * + * MessageListClass::Add_Message -- displays the given message * + * MessageListClass::Get_Message -- retrieves given message * + * MessageListClass::Get_Label -- retrieves given text label * + * MessageListClass::Concat_Message -- concats the given message * + * MessageListClass::Add_Edit -- Adds editable string to message list * + * MessageListClass::Remove_Edit -- removes the edit field * + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * MessageListClass::Manage -- Manages multiplayer messages * + * MessageListClass::Input -- Handles input for sending messages * + * MessageListClass::Draw -- Draws the messages * + * MessageListClass::Num_Messages -- returns # messages in the list * + * MessageListClass::Set_Width -- sets allowable width of messages * + * MessageListClass::Trim_Message -- trims chars off start of message * + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * MessageListClass::Reset -- Reset so no messages are visible. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/**************************** Globals **************************************/ + + +/*************************************************************************** + * MessageListClass::MessageListClass -- constructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::MessageListClass(void) +{ + int i; + + //------------------------------------------------------------------------ + // Init all data members + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = 0; + MessageY = 0; + MaxMessages = 0; + MaxChars = 0; + Height = 0; + + EnableOverflow = 0; + AdjustEdit = 0; + IsEdit = 0; + EditX = 0; + EditY = 0; + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + OverflowStart = 0; + OverflowEnd = 0; + + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + +} // end of MessageListClass + + +/*************************************************************************** + * MessageListClass::~MessageListClass -- destructor * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, including edit message * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +MessageListClass::~MessageListClass() +{ + Init(0,0,0,0,0,0,0,0,0,0); + +} // end of ~MessageListClass + + +/*************************************************************************** + * MessageListClass::Init -- Inits message system, sets options * + * * + * INPUT: * + * x,y coord of upper-left of top message * + * max_msg max messages allowed, NOT including edit message * + * maxchars max # characters allowed per message * + * height pixel height of a line of text * + * edit_x x-coord of edit field; -1 = put at the top of the * + * other messages * + * edit_y y-coord of edit field; -1 = put at the top of the * + * other messages * + * overflow_on true = enable the overflow typing feature * + * over_start start index for overflow processing * + * over_end end index for overflow processing * + * width pixel width of message buffer * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Init(int x, int y, int max_msg, int maxchars, + int height, int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width) +{ + TextLabelClass * txtlabel; + int i; + + Width = width; + + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + BufferAvail[i] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + MessageX = x; + MessageY = y; + + MaxMessages = max_msg; + if (MaxMessages > MAX_NUM_MESSAGES) + MaxMessages = MAX_NUM_MESSAGES; + + MaxChars = maxchars; + if (MaxChars > MAX_MESSAGE_LENGTH) + MaxChars = MAX_MESSAGE_LENGTH; + + Height = height; + + //------------------------------------------------------------------------ + // Init the edit field variables. If edit_x or edit_y is -1, place the + // edit field above the other messages; otherwise, place it at the desired + // coords. + //------------------------------------------------------------------------ + EnableOverflow = overflow_on; + IsEdit = 0; + if (edit_x == -1 || edit_y == -1) { + AdjustEdit = 1; + EditX = x; + EditY = y; + } + else { + AdjustEdit = 0; + EditX = edit_x; + EditY = edit_y; + } + EditLabel = 0; + EditBuf[0] = 0; + OverflowBuf[0] = 0; + EditCurPos = 0; + EditInitPos = 0; + CursorChar = 0; + + //------------------------------------------------------------------------ + // Init the overflow processing indices + //------------------------------------------------------------------------ + OverflowStart = over_start; + OverflowEnd = over_end; + if (OverflowEnd >= MaxChars) { + OverflowEnd = MaxChars - 1; + } + if (OverflowStart >= OverflowEnd) { + OverflowStart = OverflowEnd - 1; + } + +} // end of Init + + +/*********************************************************************************************** + * MessageListClass::Reset -- Reset so no messages are visible. * + * * + * This routine will reset the message list tracker so that any displayed messages are * + * cleared. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/04/1997 JLB : Created. * + *=============================================================================================*/ +void MessageListClass::Reset(void) +{ + //------------------------------------------------------------------------ + // Remove every entry in the list + //------------------------------------------------------------------------ + TextLabelClass * txtlabel = MessageList; + while (txtlabel) { + MessageList = (TextLabelClass *)txtlabel->Remove(); + delete txtlabel; + txtlabel = MessageList; + } + + //------------------------------------------------------------------------ + // Mark all buffers as available + //------------------------------------------------------------------------ + for (int index = 0; index < MAX_NUM_MESSAGES; index++) { + BufferAvail[index] = 1; + } + + //------------------------------------------------------------------------ + // Remove the editable message + //------------------------------------------------------------------------ + if (IsEdit) { + delete EditLabel; + EditLabel = 0; + } + + //------------------------------------------------------------------------ + // Init variables + //------------------------------------------------------------------------ + MessageList = 0; + EditLabel = 0; + IsEdit = 0; +} + +extern void On_Message(const char* message, float timeout_seconds, long long message_id); + +/*************************************************************************** + * MessageListClass::Add_Message -- displays the given message * + * * + * INPUT: * + * name name of sender, NULL = none * + * id numerical ID for this message * + * txt text to display * + * color color to draw text in * + * style style to use * + * timeout # of ticks the thing is supposed to last (-1 = forever) * + * * + * OUTPUT: * + * ptr to new TextLabelClass object. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + * 10/16/1996 JLB : Audio feedback added. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Message(char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout) +{ + TextLabelClass * txtlabel = NULL; + char message[MAX_MESSAGE_LENGTH + 30]; + + //------------------------------------------------------------------------ + // Combine the name & message text, if there's a name given + //------------------------------------------------------------------------ + if (name) { + sprintf(message, "%s:%s", name, txt); + id = -1; + } else { + strcpy(message, txt); + } + +#if (0) + + int i; + int found; + char temp[MAX_MESSAGE_LENGTH + 30]; + int print_this_pass; + char save = 0; + int mess_start; + + //------------------------------------------------------------------------ + // Check that printing this wont overrun the width of the print area on screen + //------------------------------------------------------------------------ + + print_this_pass = 0; + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[color], TBLACK, style); + int wid = String_Pixel_Width(message); + if (wid >= Width-8) { + //------------------------------------------------------------------------ + // Bugger. Its too long. Loop through and find out how many chars we can print + //------------------------------------------------------------------------ + if (name) { + sprintf (temp, "%s:", name); + mess_start = strlen (name)+1; + } else { + mess_start = 0; + } + for (int i=1 ; i<(int)strlen(txt) ; i++) { + strncpy (&temp[mess_start], txt, i); + temp [mess_start + i] = 0; + wid = String_Pixel_Width(temp); + if (wid >= Width-8) { + print_this_pass = mess_start + i-1; + break; + } + } + + //------------------------------------------------------------------------ + // Prematurely terminate the string so it doesn't all print. + // We will re-enter at the end to print the rest. + //------------------------------------------------------------------------ + if (print_this_pass) { + save = message [print_this_pass]; + message [print_this_pass] = 0; + } + } + + + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if ( (MaxMessages > 0) && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + + if (txtlabel==NULL) + return(NULL); + + //..................................................................... + // Remove this message from the list; mark its buffer as being available. + //..................................................................... + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // Create the message + //------------------------------------------------------------------------ + txtlabel = new TextLabelClass (message, MessageX, MessageY, + &ColorRemaps[color], style); + if (timeout==-1) { + txtlabel->UserData1 = 0; + } + else { + txtlabel->UserData1 = TickCount + timeout; + } + txtlabel->UserData2 = id; + + //------------------------------------------------------------------------ + // Find a buffer to store our message in; if there are none, don't add the + // message. + //------------------------------------------------------------------------ + found = 0; + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (BufferAvail[i]) { + BufferAvail[i] = 0; + memset (MessageBuffers[i],0,MAX_MESSAGE_LENGTH + 30); + strcpy (MessageBuffers[i],message); + txtlabel->Text = MessageBuffers[i]; + found = 1; + break; + } + } + if (!found) { + delete txtlabel; + return (NULL); + } + +#endif + + On_Message(message, timeout * 60.0f / TICKS_PER_MINUTE, id); + //Sound_Effect(VOC_INCOMING_MESSAGE); + +#if (0) + + //------------------------------------------------------------------------ + // Attach the message to our list + //------------------------------------------------------------------------ + if (MessageList) { + txtlabel->Add_Tail (*MessageList); + } + else { + MessageList = txtlabel; + } + + //------------------------------------------------------------------------ + // Recompute all messages' y-coordinate values + //------------------------------------------------------------------------ + Compute_Y(); + + //------------------------------------------------------------------------ + // If we terminated the string before the end then we need to reenter to + // add a new message with the rest of the string. + //------------------------------------------------------------------------ + if (save) { + message [print_this_pass] = save; + Add_Message (name, id, &message [print_this_pass], color, style, timeout); + } + +#endif + + return(txtlabel); + +} // end of Add_Message + + +/*************************************************************************** + * MessageListClass::Get_Message -- retrieves given message * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Message(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg->Text); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Message + + +/*************************************************************************** + * MessageListClass::Get_Label -- retrieves given text label * + * * + * INPUT: * + * id ID of message to get * + * * + * OUTPUT: * + * ptr to message text, NULL if not found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Get_Label(int id) +{ + TextLabelClass * gadg; + + //------------------------------------------------------------------------ + // Scan the message list, searching for the given ID + //------------------------------------------------------------------------ + if (MessageList) { + gadg = MessageList; + while (gadg) { + if (gadg->UserData2 == id) { + return (gadg); + } + gadg = (TextLabelClass *)gadg->Get_Next(); + } + } + + return (NULL); + +} // end of Get_Label + + +/*************************************************************************** + * MessageListClass::Concat_Message -- concats the given message * + * * + * INPUT: * + * name name of sender; NULL = none * + * id ID of message to concatenate to * + * txt text to concatenate onto existing message * + * timeout new timeout for message * + * * + * OUTPUT: * + * 1 = OK, 0 = error (id or name not found) * + * * + * WARNINGS: * + * If the required message doesn't exist, this routine does nothing. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Concat_Message(char const * name, int id, char const * txt, int timeout) +{ + int min_chars; + int max_chars; + char * msg; + TextLabelClass * tlabel; + int found; + + //------------------------------------------------------------------------ + // If no name is given, or the concatenation feature is turned off, + // don't concatenate the message + //------------------------------------------------------------------------ + if (!name || !EnableOverflow) { + return (0); + } + + //------------------------------------------------------------------------ + // Scan through all active messages, searching for one with a matching + // name & ID + //------------------------------------------------------------------------ + found = 0; + if (MessageList) { + tlabel = MessageList; + while (tlabel) { + if (tlabel->UserData2 == id && + !memcmp(tlabel->Text,name,strlen(name))) { + found = 1; + break; + } + tlabel = (TextLabelClass *)tlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // name and ID not found; return + //------------------------------------------------------------------------ + if (!found) { + return (0); + } + + //------------------------------------------------------------------------ + // set a pointer to the text string, plus the name and colon + //------------------------------------------------------------------------ + msg = tlabel->Text + strlen(name) + 1; + + //------------------------------------------------------------------------ + // If there's room enough in the message, just add the given string + //------------------------------------------------------------------------ + if ( (int)(strlen(msg) + strlen(txt)) < MaxChars) { + + //--------------------------------------------------------------------- + // We need to trim the message if there is no room to draw it + //--------------------------------------------------------------------- + char *concat_test = new char [MaxChars+1]; + Fancy_Text_Print(TXT_NONE, 0, 0, tlabel->Color, TBLACK, tlabel->Style); + int name_width = String_Pixel_Width(tlabel->Text) - String_Pixel_Width(msg); + int width; + + strcpy (concat_test, msg); + strcat (concat_test, txt); + width = String_Pixel_Width(concat_test) + name_width; + min_chars = 10; + + while (width >= Width-8){ + + max_chars = strlen (msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + + Trim_Message (NULL, msg, min_chars, max_chars, 0); + + strcpy (concat_test, msg); + strcat (concat_test, txt); + + width = String_Pixel_Width(concat_test) + name_width; + }; + + delete [] concat_test; + + strcat (msg,txt); + } + + //------------------------------------------------------------------------ + // Otherwise, trim off some characters from the beginning of the + // message. Trim off at least enough to leave room for the new text. + // Trim from left to right to remove the minimum required text. + //------------------------------------------------------------------------ + else { + min_chars = (strlen(msg) + strlen(txt)) - MaxChars; + max_chars = strlen(msg); + if (max_chars < min_chars) { + max_chars = min_chars; + } + Trim_Message (NULL, msg, min_chars, max_chars, 0); + strcat (msg, txt); + } + + //------------------------------------------------------------------------ + // Set the new timeout value for the message + //------------------------------------------------------------------------ + if (timeout==-1) { + tlabel->UserData1 = 0; + } + else { + tlabel->UserData1 = TickCount + timeout; + } + + return (1); + +} // end of Concat_Message + + + +/*********************************************************************************************** + * MessageListClass::Set_Edit_Focus -- Give the gadget system focus to the edit box * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +void MessageListClass::Set_Edit_Focus (void) +{ + if (IsEdit) EditLabel->Set_Focus(); +} + + +/*********************************************************************************************** + * MessageListClass::Has_Edit_Focus -- Find out if the edit box has the input focus * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/19/96 4:41PM ST : Created * + *=============================================================================================*/ +bool MessageListClass::Has_Edit_Focus (void) +{ + if (IsEdit){ + return (EditLabel->Has_Focus()); + }else{ + return(false); + } +} + + + +/*************************************************************************** + * MessageListClass::Add_Edit -- Adds editable string to message list * + * * + * INPUT: * + * color color of edit message * + * style style of edit message * + * to string: who to send to; NULL = none * + * cursor character to use as a cursor; 0 = none * + * * + * OUTPUT: * + * ptr to new TextLabelClass * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +TextLabelClass * MessageListClass::Add_Edit(PlayerColorType color, + TextPrintType style, char * to, char cursor, int width) +{ + int i; + TextLabelClass * txtlabel; + + //------------------------------------------------------------------------ + // Do nothing if we're already in "edit" mode + //------------------------------------------------------------------------ + if (IsEdit) { + EditLabel->Set_Focus(); + return(NULL); + } + + //------------------------------------------------------------------------ + // Remove the top-most message if we're about to exceed the max allowed + //------------------------------------------------------------------------ + if (AdjustEdit && ((Num_Messages() + 1) > MaxMessages)) { + txtlabel = MessageList; + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) + BufferAvail[i] = 1; + } + delete txtlabel; + } + + //------------------------------------------------------------------------ + // If no 'to' field was passed in, ignore it + //------------------------------------------------------------------------ + if (!to) { + to = ""; + } + + //------------------------------------------------------------------------ + // Set the cursor character + //------------------------------------------------------------------------ + CursorChar = cursor; + + //------------------------------------------------------------------------ + // Initialize the buffer positions; create a new text label object + //------------------------------------------------------------------------ + memset (EditBuf, 0, sizeof(EditBuf)); + strcpy (EditBuf, to); + OverflowBuf[0] = 0; + EditCurPos = EditInitPos = strlen(to); + EditLabel = new TextLabelClass (EditBuf, EditX, EditY, + &ColorRemaps[color], style); + + Width = width; + + if (EditLabel) { + IsEdit = 1; + EditLabel->Set_Focus(); + } + else { + IsEdit = 0; + } + + //------------------------------------------------------------------------ + // If the edit field appears over the message list, recompute the y-value + // for all messages. Also, adjust MaxMessages down by one, since there + // is now one less slot available. + //------------------------------------------------------------------------ + if (AdjustEdit) { + Compute_Y(); + MaxMessages--; + } + + return(EditLabel); + +} // end of Add_Edit + + +/*************************************************************************** + * MessageListClass::Remove_Edit -- removes the edit field * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/06/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Remove_Edit(void) +{ + //------------------------------------------------------------------------ + // If the edit field is active, delete it + //------------------------------------------------------------------------ + if (IsEdit) { + IsEdit = 0; + delete EditLabel; + + //..................................................................... + // If the edit field appears over the message list, recompute the + // y-value for all messages. Adjust MaxMessages back up, since there + // is now a new available slot. + //..................................................................... + if (AdjustEdit) { + Compute_Y(); + MaxMessages++; + } + } + +} // end if Remove_Edit + + +/*************************************************************************** + * MessageListClass::Get_Edit_Buf -- gets edit buffer * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to edit buffer, minus the "To:" header * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/21/1995 BRR : Created. * + *=========================================================================*/ +char * MessageListClass::Get_Edit_Buf(void) +{ + return(EditBuf + EditInitPos); + +} // end of Get_Edit_Buf + + +/*************************************************************************** + * MessageListClass::Set_Edit_Color -- sets color of edit gizmo * + * * + * INPUT: * + * color color to set edit label to * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/08/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Edit_Color(PlayerColorType color) +{ + if (IsEdit) { + EditLabel->Color = &ColorRemaps[color]; + } + +} // end of Set_Edit_Color + + +/*************************************************************************** + * MessageListClass::Manage -- Manages multiplayer messages * + * * + * If this routine returns TRUE, the caller should update the display. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 0 = no change has occurred, 1 = changed * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Manage (void) +{ + TextLabelClass * txtlabel; + TextLabelClass * next; + int changed = 0; + int i; + + //------------------------------------------------------------------------ + // Loop through all messages + //------------------------------------------------------------------------ + txtlabel = MessageList; + while (txtlabel) { + + //..................................................................... + // If this message's time is up, remove it from the list + //..................................................................... + if (txtlabel->UserData1 != 0 && TickCount > txtlabel->UserData1) { + + //.................................................................. + // Save the next ptr in the list; remove this entry + //.................................................................. + next = (TextLabelClass *)txtlabel->Get_Next(); + MessageList = (TextLabelClass *)txtlabel->Remove(); + for (i = 0; i < MAX_NUM_MESSAGES; i++) { + if (txtlabel->Text == MessageBuffers[i]) { + BufferAvail[i] = 1; + } + } + delete txtlabel; + changed = 1; + txtlabel = next; + } + else { + txtlabel = (TextLabelClass *)txtlabel->Get_Next(); + } + } + + //------------------------------------------------------------------------ + // If a changed has been made, recompute the y-coord of all messages + //------------------------------------------------------------------------ + if (changed) { + Compute_Y(); + } + + return(changed); + +} // end of Manage + + +/*************************************************************************** + * MessageListClass::Input -- Handles input for sending messages * + * * + * INPUT: * + * input key value to process * + * * + * OUTPUT: * + * 1 = caller should redraw the message list (no need to complete * + * refresh, though) * + * 2 = caller should completely refresh the display. * + * 3 = caller should send the edit message. * + * (sets 'input' to 0 if it processes it.) * + * 4 = caller should send the Overflow buffer * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/05/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Input(KeyNumType &input) +{ + KeyASCIIType ascii; + int retcode = 0; + int numchars; + + //------------------------------------------------------------------------ + // Do nothing if nothing to do. + //------------------------------------------------------------------------ + if (input == KN_NONE) { + return(0); + } + + //------------------------------------------------------------------------ + // Leave mouse events alone. + //------------------------------------------------------------------------ + if ( (input & (~KN_RLSE_BIT))==KN_LMOUSE || + (input & (~KN_RLSE_BIT))==KN_RMOUSE) { + return(0); + } + + //------------------------------------------------------------------------ + // If we're in 'edit mode', handle keys + //------------------------------------------------------------------------ + if (IsEdit) { + + + ascii = (KeyASCIIType)(Keyboard->To_ASCII(input) & 0x00ff); + +#ifdef WIN32 + /* + ** Allow numeric keypad presses to map to ascii numbers + */ + if ((input & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + input = (KeyNumType)(input & ~WWKEY_VK_BIT); + + } else { + /* + ** Filter out all special keys except return, escape and backspace + */ + if ((!(input & WWKEY_VK_BIT) && !(input & KN_BUTTON) + && ascii >= ' ' && ascii <= 127) + || (input & 0xff)== (KN_RETURN & 0xff) + || (input & 0xff)== (KN_BACKSPACE & 0xff) + || (input & 0xff)== (KN_ESC & 0xff) ) { + + //ascii = (KeyASCIIType)(Keyboard->To_ASCII(input)); + } else { + input = KN_NONE; + return (0); + } + } +#endif //WIN32 + + + + switch (ascii) { + //.................................................................. + // ESC = abort message + //.................................................................. + case KA_ESC & 0xff: + Remove_Edit(); + retcode = 2; + input = KN_NONE; + break; + + //.................................................................. + // RETURN = send the message. + // Add a space to the end, in case another message gets concatenated + // onto this one after we send it; then, they won't be mushed + // together. + //.................................................................. + case KA_RETURN & 0xff: + if (EditCurPos == EditInitPos) { + retcode = 0; + input = KN_NONE; + break; + } + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + EditBuf[EditCurPos] = ' '; + EditCurPos++; + EditBuf[EditCurPos] = 0; + } + Remove_Edit(); + retcode = 3; + input = KN_NONE; + break; + + //.................................................................. + // BACKSPACE = remove a character + //.................................................................. + case KA_BACKSPACE & 0xff: + if (EditCurPos > EditInitPos) { + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 2; + } + input = KN_NONE; + EditLabel->Set_Focus(); + break; + + //.................................................................. + // default: add a character. Reserve the last buffer position for + // null. (EditCurPos - EditInitPos) is the buffer index # of the + // next character, after the "To:" prefix. + //.................................................................. + default: + EditLabel->Set_Focus(); + bool overflowed = false; + if (ascii >= ' ' && ascii <= 127) { + if ( (EditCurPos - EditInitPos) < (MaxChars - 1) ) { + + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 1; + + /* + ** Verify that the additional character would not overrun the on screen edit box. + */ + Fancy_Text_Print(TXT_NONE, 0, 0, EditLabel->Color, TBLACK, EditLabel->Style); + int width = String_Pixel_Width(EditBuf); + if (width >= Width-10) { + overflowed = true; + EditCurPos--; + EditBuf[EditCurPos] = 0; + retcode = 0; + } + } else { + //............................................................ + // If there's no room in the buffer, and overflow is enabled, + // trim the extra characters off (from right to left, to + // remove the max possible characters), and then add the new + // character in. + //............................................................ + overflowed = true; + } + + if (/*BGEnableOverflow &&*/ overflowed) { + numchars = Trim_Message (OverflowBuf, EditBuf + EditInitPos, + OverflowStart,OverflowEnd, 1); + EditCurPos -= numchars; + EditBuf[EditCurPos] = ascii; + EditCurPos++; + EditBuf[EditCurPos] = 0; + retcode = 4; + } + } + input = KN_NONE; + break; + } + } + + return(retcode); + +} // end of Input + + +/*************************************************************************** + * MessageListClass::Draw -- draws messages * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/22/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Draw(void) +{ + char txt[2] = {0,0}; + + if (IsEdit) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + EditLabel->Draw_Me(true); + + if (CursorChar && (EditCurPos - EditInitPos) < (MaxChars - 1) && EditLabel->Has_Focus()) { + txt[0] = CursorChar; + Fancy_Text_Print(txt, + EditLabel->X + String_Pixel_Width(EditLabel->Text), + EditLabel->Y, + EditLabel->Color, + TBLACK, + EditLabel->Style); + } + + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + if (MessageList) { + if (LogicPage == &SeenBuff) { + Hide_Mouse(); + } + MessageList->Draw_All(); + if (LogicPage == &SeenBuff) { + Show_Mouse(); + } + } + +} // end of Draw + + +/*************************************************************************** + * MessageListClass::Num_Messages -- returns # messages in the list * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # of messages, including the edit field if it's above the messages * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Num_Messages(void) +{ + GadgetClass * gadg; + int num; + + num = 0; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + num++; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit && AdjustEdit) { + num++; + } + + return (num); + +} // end of Num_Messages + + +/*************************************************************************** + * MessageListClass::Set_Width -- sets allowable width of messages * + * * + * INPUT: * + * width pixel width * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/26/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Set_Width(int width) +{ + GadgetClass * gadg; + + if (MessageList) { + gadg = MessageList; + while (gadg) { + ((TextLabelClass *)gadg)->PixWidth = width; + gadg = gadg->Get_Next(); + } + } + + if (IsEdit) { + EditLabel->PixWidth = width; + } + +} // end of Set_Width + + +/*************************************************************************** + * MessageListClass::Trim_Message -- trims chars off start of message * + * * + * INPUT: * + * dest buffer to store removed characters in; NULL = none * + * src text buffer to trim * + * min_chars min # chars that must be trimmed off * + * max_chars max # chars allowed to trim * + * scandir 0 = left-to-right, 1 = right-to-left * + * * + * OUTPUT: * + * # characters removed * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +int MessageListClass::Trim_Message(char * dest, char * src, int min_chars, + int max_chars, int scandir) +{ + int i; + int len; + int found; + + //------------------------------------------------------------------------ + // validate parameters + //------------------------------------------------------------------------ + if (min_chars <= 0) { + return(0); + } + + len = strlen (src); + if (max_chars > len) { + max_chars = len; + } + + //------------------------------------------------------------------------ + // find 1st available white space; if there is none, just trim off + // 'min_chars' characters. 'i' will be the number of chars to trim. + // The chars removed will include the white space. + //------------------------------------------------------------------------ + found = 0; + //........................................................................ + // scan from left to right + //........................................................................ + if (scandir == 0) { + for (i = min_chars; i <= max_chars; i++) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // scan from right to left + //........................................................................ + else { + for (i = max_chars; i >= min_chars; i--) { + if (isspace(src[i - 1])) { + found = 1; + break; + } + } + } + //........................................................................ + // If no whitespace was found, just set 'i' to the min # characters + //........................................................................ + if (!found) { + i = min_chars; + } + + //------------------------------------------------------------------------ + // Save trimmed characters in the dest buffer, if there is one + //------------------------------------------------------------------------ + if (dest) { + memcpy (dest, src, i); + dest[i] ='\0'; + } + + //------------------------------------------------------------------------ + // Shift characters over in the source buffer + //------------------------------------------------------------------------ + memmove (src, src + i, len - i + 1); + + return (i); + +} // end of Trim_Message + + +/*************************************************************************** + * MessageListClass::Compute_Y -- recomputes y-coord for all messages * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/07/1995 BRR : Created. * + *=========================================================================*/ +void MessageListClass::Compute_Y(void) +{ + GadgetClass * gadg; + int y; + + //------------------------------------------------------------------------ + // If the editable message is attached to the message list, 'AdjustEdit' + // will be set; so, adjust all y-values downward one line. Otherwise, + // the editable message has its own screen coordinates. + //------------------------------------------------------------------------ + if (IsEdit && AdjustEdit) { + y = MessageY + Height; + } + else { + y = MessageY; + } + if (MessageList) { + gadg = MessageList; + while (gadg) { + gadg->Y = y; + gadg = gadg->Get_Next(); + y += Height; + } + } + +} // end of Compute_Y + + +/*************************** end of msglist.cpp ****************************/ diff --git a/code/redalert/msglist.h b/code/redalert/msglist.h new file mode 100644 index 0000000..1e5d688 --- /dev/null +++ b/code/redalert/msglist.h @@ -0,0 +1,211 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/MSGLIST.H 2 3/04/97 2:53p Joe_bostic $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : MSGLIST.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 05/22/95 * + * * + * Last Update : May 22, 1995 [BRR] * + * * + * Initializing: * + * Call Init(), giving it the coords of the upper-left corner to display * + * the messages, the coordinates to display the editable message, max # * + * messages allowed, max # chars per message, and the pixel height of * + * the font. MaxChars should be less than or equal to the define * + * MAX_MESSAGE_LENGTH. * + * * + * Displaying a message: * + * Call Add_Message(); the buffer you pass in is copied into a static * + * buffer in this class. Each message is given a timeout; after this * + * time, the message is removed from the displayed list. * + * Each message also has a 2-byte ID, which can be used to identify the + * sender. + * * + * Editing a message: * + * Call Add_Edit(), giving it the color & style to print in. An * + * optional "To xxx" prefix is allowed. The edit message can appear in * + * the message list itself (it's placed first), or at a separate screen * + * location, depending on the values passed to Init(). * + * * + * Updating messages: * + * The Input() routine passes new keys to the editable message; this * + * routine's return code tells the app whether to redraw the messages, * + * and whether to send it the edit message. As the user types * + * characters into the edit field, if he overflows the max storage * + * for the edit field, the field is parsed & the first few words * + * removed; these words are put into the Overflow buffer, and the app * + * is told so; the app should send this buffer across to the destination.* + * * + * - MessageList is a gadget list of all current messages * + * - MessageX & Y are the upper left corner of the 1st message * + * - MaxMessages is the max # of messages allowed, including the editable * + * message; 0 = no limit. * + * - MaxChars is the max # of chars allowed per message * + * - Height is the pixel height of a single line * + * - EditLabel points to the textmessage gadget for the current editable * + * field. EditBuf points to the char buffer being edited. EditInitPos * + * & EditCurPos define buffer index positions. * + * - EditSendAddress is the IPX Address to send the message to when RETURN * + * is pressed. * + * * + * The low word in the UserData field in the TextLabelClass tells what the * + * timeout for each message is (0 = none); the high byte of the UserData * + * field is used for the text message ID. * + * When a message's timeout expires, it's deleted. When a new message * + * is added, the top message is deleted if MPlayerMaxMessages is exceeded. * + * * + * The Edit-able message is never deleted until ESC or RETURN is pressed. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MSGLIST_H +#define MSGLIST_H + +//*************************************************************************** +// Defines +//*************************************************************************** +//--------------------------------------------------------------------------- +// Max length of inter-player message buffers. Messages.Init() should specify +// a value <= this. For editing messages, the "to" field length is added to +// this length to generate the entire editable message length. For displayed +// messages, a "From" prefix length should be added to this value to generate +// the entire max displayable message length. +//--------------------------------------------------------------------------- +#define MAX_MESSAGE_LENGTH 120 + +//--------------------------------------------------------------------------- +// Max # of allowed messages at one time +//--------------------------------------------------------------------------- +#define MAX_NUM_MESSAGES 14 + +//*************************************************************************** +// Forward declarations +//*************************************************************************** +class TextLabelClass; + +//*************************************************************************** +// Class declaration +//*************************************************************************** +class MessageListClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + MessageListClass (void); + ~MessageListClass (); + + //..................................................................... + // Initialization + //..................................................................... + void Reset(void); + void Init (int x, int y, int max_msg, int maxchars, int height, + int edit_x, int edit_y, int overflow_on, int over_start, + int over_end, int width = 640); + TextLabelClass * Add_Message (char const * name, int id, char const * txt, + PlayerColorType color, TextPrintType style, int timeout); + int Concat_Message (char const * name, int id, char const * txt, int timeout); + + //..................................................................... + // Message access utility routines + //..................................................................... + char * Get_Message (int id); + TextLabelClass * Get_Label (int id); + + //..................................................................... + // Message-editing support routines + //..................................................................... + TextLabelClass * Add_Edit(PlayerColorType color, TextPrintType style, + char *to, char cursor = 0, int width = 640); + void Remove_Edit (void); + char * Get_Edit_Buf (void); + char * Get_Overflow_Buf (void) {return (OverflowBuf);} + void Clear_Overflow_Buf (void) {OverflowBuf[0] = 0;} + int Is_Edit(void) {return (IsEdit);} + void Set_Edit_Color(PlayerColorType color); + + //..................................................................... + // Maintenance routines + //..................................................................... + int Manage (void); + int Input (KeyNumType &input); + void Draw(void); + int Num_Messages(void); + void Set_Width(int width); + void Set_Edit_Focus(void); + bool Has_Edit_Focus(void); + + private: + + //..................................................................... + // Message parsing + //..................................................................... + int Trim_Message(char *dest, char *src, int min_chars, int max_chars, + int scandir); + + //..................................................................... + // Compute the y-coord of the message list + //..................................................................... + void Compute_Y(void); + + //..................................................................... + // Private Data + //..................................................................... + TextLabelClass * MessageList; // list of messages + int MessageX; // x-coord of upper-left + int MessageY; // y-coord of upper-left + int MaxMessages; // max messages allowed + int MaxChars; // max allowed chars per message + int Height; // height in pixels + + //..................................................................... + // Data for the edit field: the edit field will either appear at + // exact coordinates specified by the application, or it will appear + // vertically above the other messages. + //..................................................................... + unsigned EnableOverflow : 1; // 1 = enable overflow feature + unsigned IsEdit : 1; // 1 = there's an edit field + unsigned AdjustEdit : 1; // 1 = edit field appears over msgs + int EditX; // x-coord of edit field + int EditY; // y-coord of edit field + TextLabelClass *EditLabel; // ptr to current edit label + char EditBuf[MAX_MESSAGE_LENGTH + 30]; // buffer for editable message + char OverflowBuf[MAX_MESSAGE_LENGTH + 30]; // overflow area + int EditCurPos; // current edit position + int EditInitPos; // initial edit position + char CursorChar; // character to use a cursor + int OverflowStart; // 1st index for overflow trimming + int OverflowEnd; // last index for overflow trimming + int Width; // Maximum width in pixels of editable string + + //..................................................................... + // Buffers provided for messages. They must be long enough for + // both the message, and for the "To" prefix on edited messages, or + // the "Name:" prefix on received messages. + //..................................................................... + char MessageBuffers[MAX_NUM_MESSAGES][MAX_MESSAGE_LENGTH + 30]; + char BufferAvail[MAX_NUM_MESSAGES]; +}; + +#endif +/**************************** end of msglist.h *****************************/ + diff --git a/code/redalert/netdlg.cpp b/code/redalert/netdlg.cpp new file mode 100644 index 0000000..a2f4604 --- /dev/null +++ b/code/redalert/netdlg.cpp @@ -0,0 +1,7849 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NETDLG.CPP 13 10/13/97 2:20p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NETDLG.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : January 23, 1995 * + * * + * Last Update : December 12, 1995 [BRR] * + * * + *---------------------------------------------------------------------------------------------* + * * + * These routines establish & maintain peer-to-peer connections between this system * + * and all others in the game. Each system finds out the IPX address of the others, * + * and forms a direct connection (IPXConnectionClass) to that system. Systems are * + * found out via broadcast queries. Every system broadcasts its queries, and every * + * system replies to queries it receives. At the point when the game owner signals * + * 'OK', every system must know about all the other systems in the game. * + * * + * How Bridges are handled: * + * Currently, bridges are handled by specifying the destination IPX address of the * + * "server" (game owner's system) on the command-line. This address is used to * + * derive a broadcast address to that destination network, and this system's queries * + * are broadcast over its network & the server's network; replies to the queries come * + * with each system's IPX address attached, so once we have the address, we can form * + * a connection with any system on the bridged net. * + * * + * The flaw in this plan is that we can only cross one bridge. If there are 3 nets * + * bridged (A, B, & C), and the server is on net B, and we're on net A, our broadcasts * + * will reach nets A & B, but not C. The way to circumvent this (if it becomes a problem) * + * would be to have the server tell us what other systems are in its game, not each * + * individual player's system. Thus, each system would find out about all the other systems * + * by interacting with the game's owner system (this would be more involved than what * + * I'm doing here). * + * * + * Here's a list of all the different packets sent over the Global Channel: * + * * + * NET_QUERY_GAME * + * (no other data) * + * NET_ANSWER_GAME * + * Name: game owner's name * + * GameInfo: game's version & open state * + * NET_QUERY_PLAYER * + * Name: name of game we want players to respond for * + * NET_ANSWER_PLAYER * + * Name: player's name * + * PlayerInfo: info about player * + * NET_CHAT_ANNOUNCE * + * Chat: unique id of the local node, so I can tell * + * if this chat announcement is from myself * + * NET_QUERY_JOIN * + * Name: name of player wanting to join * + * PlayerInfo: player's requested house, color, & version range * + * NET_CONFIRM_JOIN * + * PlayerInfo: approves player's house & color * + * NET_REJECT_JOIN * + * Reject.Why: tells why we got rejected * + * NET_GAME_OPTIONS * + * ScenarioInfo: info about scenario * + * NET_SIGN_OFF * + * Name: name of player signing off * + * NET_PING * + * (no other data) * + * NET_GO * + * Delay: value of one-way response time, in frames * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Init_Network -- initializes network stuff * + * Shutdown_Network -- shuts down network stuff * + * Process_Global_Packet -- responds to remote queries * + * Destroy_Connection -- destroys the given connection * + * Remote_Connect -- handles connecting this user to others * + * Net_Join_Dialog -- lets user join an existing game, or start a new one * + * Request_To_Join -- Sends a JOIN request packet to game owner * + * Unjoin_Game -- Cancels joining a game * + * Send_Join_Queries -- sends queries for the Join Dialog * + * Get_Join_Responses -- sends queries for the Join Dialog * + * Net_New_Dialog -- lets user start a new game * + * Get_NewGame_Responses -- processes packets for New Game dialog * + * Compute_Name_CRC -- computes CRC from char string * + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +// Warning - Most disgusting cpp file of all time. ajw + +#include "function.h" + +//PG Stubs +void Destroy_Connection(int, int) {} +bool Process_Global_Packet(GlobalPacketType *, IPXAddressClass *) { return false; } +bool Client_Remote_Connect(void) { return false; } +bool Server_Remote_Connect(void) { return false; } +bool Init_Network(void) { return false; } +bool Remote_Connect(void) { return false; } + +#if (0) //PG + + +#ifdef WIN32 +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#endif //WINSOCK_IPX +#include "ccdde.h" +#else //WIN32 +#include "fakesock.h" +#endif //WIN32 + +#include +//#include + +//#include "WolDebug.h" + +#define SHOW_MONO 0 +//#define OLDWAY 1 + +#ifdef FRENCH +#define TXT_HACKHACK "Connexion En Cours..." +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_CONNECTING) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +#endif +#ifdef WOLAPI_INTEGRATION +#include "WolStrng.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +#include "COORDA.h" + +//--------------------------------------------------------------------------- +// The possible states of the join-game dialog +//--------------------------------------------------------------------------- +typedef enum { + JOIN_REJECTED = -1, // we've been rejected + JOIN_NOTHING, // we're not trying to join a game + JOIN_WAIT_CONFIRM, // we're asking to join, & waiting for confirmation + JOIN_CONFIRMED, // we've been confirmed + JOIN_GAME_START, // the game we've joined is starting + JOIN_GAME_START_LOAD, // the game we've joined is starting; load saved game +} JoinStateType; + +//--------------------------------------------------------------------------- +// The possible return codes from Get_Join_Responses() +//--------------------------------------------------------------------------- +typedef enum { + EV_NONE, // nothing happened + EV_STATE_CHANGE, // Join dialog is in a new state + EV_NEW_GAME, // a new game formed, or is now open + EV_NEW_PLAYER, // a new player was detected + EV_PLAYER_SIGNOFF, // a player has signed off + EV_GAME_SIGNOFF, // a gamed owner has signed off + EV_GAME_OPTIONS, // a game options packet was received + EV_MESSAGE, // a message was received +} JoinEventType; + + +//--------------------------------------------------------------------------- +// The possible reasons we're rejected from joining a game +//--------------------------------------------------------------------------- +typedef enum { + REJECT_DUPLICATE_NAME, // player's name is a duplicate + REJECT_GAME_FULL, // game is full + REJECT_VERSION_TOO_OLD, // joiner's version is too old + REJECT_VERSION_TOO_NEW, // joiner's version is too new + REJECT_BY_OWNER, // game owner clicked "reject" + REJECT_DISBANDED, // game was disbanded + REJECT_MISMATCH, // "rules.ini" file mismatch. +} RejectType; +#endif + +#ifdef ENGLISH +char const *EngMisStr[] = { + "Coastal Influence (Med)", + "Middle Mayhem (Sm)", + "Equal Opportunity (Sm)", + "Marooned II (Med)", + "Keep off the Grass (Sm)", + "Isle of Fury (Lg)", + "Ivory Wastelands (Sm)", + "Shallow Grave (Med)", + "North By Northwest (Lg)", + "First Come, First Serve (Sm)", + "Island Hoppers (Sm)", + "Raraku (Lg)", + "Central Conflict (Lg)", + "Combat Alley (Med)", + "Island Wars (Lg)", + "Desolation (Lg)", + "No Escape (Med)", + "No Man's Land (Med)", + "Normandy (Med)", + "Pond Skirmish (Med)", + "Ridge War (Med)", + "A Path Beyond (Lg)", + "Dugout Isle (Med)", + "Treasure Isle (Med)", + + "Africa (Lg)", + "Alaska Anarchy (Lg)", + "All that Glitters... (Lg)", + "Apre's Peace (Lg)", + "Antartica (Lg)", + "Armourgarden (Lg)", + "Austraila (Med)", + "Barrier to Entry (Lg)", + "Bavarian Blast (Med)", + "Be Shore (Med)", + "Bearing Straits (Med)", + "Blow Holes (Lg)", + "Bonsai (Sm)", + "Brother Stalin (Lg)", + "Bullseye (Lg)", + "C&C (Med)", + "Camos Canyon (Med)", + "Camos Coves (Lg)", + "Camos Cross (Lg)", + "Camos Crossing (Sm)", + "Central Arena (Lg)", + "Canyon River (Med)", + "Crossroads (Sm)", + "Czech Mate (Lg)", + "Dday (Med)", + "Disaster Central (Lg)", + "Docklands (Med)", + "East Coast (Med)", + "Eastern Seaboard (Lg)", + "Finger Lake (Lg)", + "Fjords (Med)", + "Floodlands (Lg)", + "Forest under fire (Lg)", + "Four Corners (Lg)", + "Frostbit Fjords (Lg)", + "Glenboig (Sm)", + "Hell Frozen Over (Lg)", + "India (Lg)", + "Indirect Fire (Lg)", + "Island Wars II (Lg)", + "Italy (Lg)", + "Kabalo (Lg)", + "King of the Hills (Lg)", + "Lake Divide (Med)", + "Lakelands (Lg)", + "Land Ladder (Lg)", + "Lotsa Lakes (Lg)", + "Lunar Battlefield (Lg Special)", + "Malibu Fields (Med)", + "Marshland (Med)", + "MyLai Delta (Med)", + "Natural Harbor (Med)", + "No Way Out (Lg)", + "Normandy Landing (Lg)", + "Ore Wars (Med)", + "Oz (Lg)", + "Pilgrim Fathers II (Lg)", + "Pip's Ice Tea (Med)", + "Polar Panic (Lg)", + "Ponds (Med)", + "Putney (Lg)", + "Return to Zion (Lg)", + "Ring of Land (Lg)", + "River Basin (Lg)", + "River Delta (Med)", + "River Islands (Med)", + "River Maze (Sm)", + "Rivers (Sm)", + "Run the Gauntlet (Med)", + "Scappa Flow (Lg)", + "Siberian Slaughter (Lg)", + "Sleepy Valley (Sm)", + "Snake River (Lg)", + "Snow Wars (Lg)", + "Snowball fight (Lg)", + "Snowy Island (Lg)", + "So Near So Far (Sm)", + "South America (Lg)", + "Spring Line (Lg)", + "Star (Lg)", + "Straighter & Narrower (Sm)", + "TerrainSpotting (Sm)", + "The Bay (Lg)", + "The Garden (Lg)", + "The Great Lakes (Med)", + "The Ice Arena (Lg)", + "The Lake District (Lg)", + "The Linked lands (Lg)", + "The Mississippi (Med)", + "The Sticky Bit (Lg)", + "The Valley (Med)", + "The Woods Today (Lg)", + "Things to Come (Lg)", + "Tiger Core (Sm)", + "To the Core (Sm)", + "Tournament Hills (Lg)", + "Tropical Storm (Med)", + "Tundra Trouble (Lg)", + "Uk (Med)", + "Undiscovered Country (Sm)", + "United States (Med)", + "Volcano (Sm)", + "Wastelands (Lg)", + "Water Works (Sm)", + "World Map (Med)", + "Zambezi (Lg)", + + "A Pattern of Islands (Lg 8 players)", + "Arena Valley Extreme (Mega 8 players)", + "Around the Rim (Sm 4 players)", + "Ashes to Ashes (Lg 6 players)", + "Artic Wasteland (Mega 8 players)", + "Badajoz (Med 4 players)", + "Baptism of Fire (Lg 6 players)", + "Big Fish, Small Pond (Lg 6 players)", + "Blue Lakes (Lg 8 players)", + "Booby Traps (Mega 8 players)", + "Bridgehead (Lg 6 players)", + "Butterfly Bay (Lg 6 players)", + "Central Conflict Extreme (Mega 8 players)", + "Circles of Death (Mega 8 players)", + "Cold Front (Med 6 players)", + "Cold Pass (Med 4 players)", + "Combat Zones (Mega 8 players)", + "Conflict Cove (Sm 4 players)", + "Culloden Moor (Med 8 players)", + "Damnation Alley (Mega 8 players)", + "Death Valley (Mega 8 players)", + "Deep Six (Mega 8 players)", + "Destruction Derby (Mega 8 players)", + "Diamonds Aren't Forever (Mega 8 players)", + "Elysium (Sm 4 players)", + "Equal Shares (Lg 4 players)", + "Frost Bitten (Mega 8 players)", + "Frozen Valley (Med 6 players)", + "Gettysburg (Sm 4 players)", + "Glacial Valley (Sm 4 players)", + "Gold Coast (Med 6 players)", + "Gold Rush (Lg 4 players)", + "Habitat (Lg 4 players)", + "Hades Frozen Over (Sm 4 players)", + "Hamburger Hill (Mega 8 players)", + "Hastings (Sm 4 players)", + "Hell's Pass (Med 6 players)", + "Holy Grounds (Mega 8 players)", + "Ice Bergs (Med 6 players)", + "Ice Station (Lg 6 players)", + "Ice Queen (Lg 4 players)", + "In the Sun (Med 6 players)", + "Innocents? (Mega 8 players)", + "Islands (Med 8 players)", + "Island Plateau (Lg 4 players)", + "Island Wars Extreme (Mega 8 players)", + "Kananga (Med 6 players)", + "King of the Hills Extreme (Mega 8 players)", + "Lake Land (Lg 8 players)", + "Land Locked (Lg 8 players)", + "Lanes (Med 8 players)", + "Leipzip (Sm 4 players)", + "Meander (Lg 8 players)", + "Mekong (Med 8 players)", + "Middle Ground (Med 8 players)", + "Naval Conquests (Mega 8 players)", + "On your Marks (Med 4 players)", + "Open Warfare (Mega 8 players)", + "Ore Gardens (Lg 8 players)", + "Potholes (Mega 8 players)", + "Puddles (Med 4 players)", + "Random Violence (Mega 8 players)", + "Revenge (Med 8 players)", + "Rias (Med 8 players)", + "River Crossing (Sm 4 players)", + "River Rampage (Mega 8 players)", + "River Rapids (Lg 6 players)", + "Rivers Wild (Mega 8 players)", + "Rorkes Drift (Lg 4 players)", + "Seaside (Med 4 players)", + "Shades (Med 8 players)", + "Smuggler's Cove (Lg 6 players)", + "Snow Garden (Sm 2 players)", + "Stalingrad (Sm 4 players)", + "Sticks & Stones (Med 4 players)", + "Strathearn Valley (Lg 6 players)", + "Super Bridgehead (Mega 8 players)", + "Super Mekong (Mega 8 players)", + "Super Ore Gardens (Mega 8 players)", + "Switch (Med 4 players)", + "The Berg (Mega 8 players)", + "The Boyne (Med 4 players)", + "The Bulge (Sm 4 players)", + "The Cauldron (Lg 6 players)", + "The Finger (Lg 6 players)", + "The Hills Have Eyes (Mega 8 players)", + "The Keyes (Med 6 players)", + "The Lakes (Med 8 players)", + "The Neck (Med 6 players)", + "The Web (Lg 6 players)", + "To the Core (Lg 4 players)", + "Trafalgar (Lg 4 players)", + "Twin Rivers (Sm 4 players)", + "Umtumbo Gorge (Lg 4 players)", + "Watch Your Step Extreme (Mega 8 players)", + "Waterfalls (Lg 8 players)", + "Waterloo Revisited (Lg 6 players)", + "Water Werks (Mega 8 players)", + "Warlord's Lake (Sm 4 players)", + "Zama (Sm 4 players)", + + NULL +}; +#endif + + +#if (0) //PG + + +#ifdef GERMAN +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Weg ins Jenseits (Gr)", + "Central Conflict (Lg)", "Der zentrale Konflikt (Gr)", + "Coastal Influence (Med)", "Sturm an der Kste (Mit)", + "Combat Alley (Med)", "Boulevard der Schlachten (Mit)", + "Desolation (Lg)", "Verwstung (Gr)", + "Dugout Isle (Med)", "Buddelschiff (Mit)", + "Equal Opportunity (Sm)", "Gleiche Chancen (Kl)", + "First Come, First Serve (Sm)", "Wer zuerst kommt... (Kl)", + "Island Hoppers (Sm)", "Inselspringen (Kl)", + "Island Wars (Lg)", "Der Krieg der Eilande (Gr)", + "Isle of Fury (Lg)", "Insel des Zorns (Gr)", + "Ivory Wastelands (Sm)", "Elfenbeinwste (Kl)", + "Keep off the Grass (Sm)", "Rasen betreten verboten (Kl)", + "Marooned II (Med)", "Gestrandet (Mit)", + "Middle Mayhem (Sm)", "Mittelsmann (Kl)", + "No Escape (Med)", "Kein Entrinnen (Mit)", + "No Man's Land (Med)", "Niemandsland (Mit)", + "Normandy (Med)", "Normandie (Mit)", + "North By Northwest (Lg)", "Nord auf Nordwest (Gr)", + "Pond Skirmish (Med)", "Teichgepl„nkel (Mit)", + "Raraku (Lg)", "Raraku (Gr)", + "Ridge War (Med)", "Das Tal der Cyborgs (Mit)", + "Shallow Grave (Med)", "Ein enges Grab (Mit)", + "Treasure Isle (Med)", "Die Schatzinsel (Mit)", + + "Africa (Lg)", "Afrika (Gr)", + "Alaska Anarchy (Lg)", "Anarchie in Alaska (Gr)", + "All that Glitters... (Lg)", "Alles was gl„nzt... (Gr)", + "Apre's Peace (Lg)", "Apres Frieden (Gr)", + "Antartica (Lg)", "Antarktica (Gr)", + "Armourgarden (Lg)", "Garten der Panzer (Gr)", + "Austraila (Med)", "Koalaland (Mit)", + "Barrier to Entry (Lg)", "Zutritt verboten (Gr)", + "Bavarian Blast (Med)", "Bayrische Blasmusik (Mit)", + "Be Shore (Med)", "Strandl„ufer (Mit)", + "Bearing Straits (Med)", "Die Heringstrasse (Mit)", + "Blow Holes (Lg)", "L”cheriger K„se (Gr)", + "Bonsai (Sm)", "Bonsai (Kl)", + "Brother Stalin (Lg)", "Brderchen Stalin (Gr)", + "Bullseye (Lg)", "Bullseye (Gr)", + "C&C (Med)", "C&C (Mit)", + "Camos Canyon (Med)", "Camos-Canyon (Mit)", + "Camos Coves (Lg)", "Camos-Grotte (Gr)", + "Camos Cross (Lg)", "Camos-Kreuz (Gr)", + "Camos Crossing (Sm)", "Camos-Kreuzweg (Kl)", + "Central Arena (Lg)", "Spielplatz des Teufels (Gr)", + "Canyon River (Med)", "Canyonfluss (Mit)", + "Crossroads (Sm)", "Kreuzung (Kl)", + "Czech Mate (Lg)", "Tschechische Er”ffnung (Gr)", + "Dday (Med)", "D-Day (Mit)", + "Disaster Central (Lg)", "Endstation Schweinebucht (Gr)", + "Docklands (Med)", "Docklands (Mit)", + "East Coast (Med)", "Ostkste (Mit)", + "Eastern Seaboard (Lg)", "Die Passage nach Osten (Gr)", + "Finger Lake (Lg)", "Fingersee (Gr)", + "Fjords (Med)", "Fjorde (Mit)", + "Floodlands (Lg)", "Land unter! (Gr)", + "Forest under fire (Lg)", "Waldsterben im Feuer (Gr)", + "Four Corners (Lg)", "Viereck (Gr)", + "Frostbit Fjords (Lg)", "Frostbeulenfjord (Gr)", + "Glenboig (Sm)", "Glenboig (Kl)", + "Hell Frozen Over (Lg)", "Winter in der H”lle (Gr)", + "India (Lg)", "Indien (Gr)", + "Indirect Fire (Lg)", "Indirekter Beschuss (Gr)", + "Island Wars II (Lg)", "Krieg der Inseln (Gr)", + "Italy (Lg)", "Italien (Gr)", + "Kabalo (Lg)", "Kabalo (Gr)", + "King of the Hills (Lg)", "K”nig des Maulwurfshgels (Gr)", + "Lake Divide (Med)", "Wasserscheide (Mit)", + "Lakelands (Lg)", "Seenplatte (Gr)", + "Land Ladder (Lg)", "Das Leiterspiel (Gr)", + "Lotsa Lakes (Lg)", "Mehr Seen (Gr)", + "Lunar Battlefield (Lg Special)", "Schlachtfeld Mond (Gr Spezial)", + "Malibu Fields (Med)", "Malibu (Mit)", + "Marshland (Med)", "Schlammschlacht (Mit)", + "MyLai Delta (Med)", "Das Delta von My Lai (Mit)", + "Natural Harbor (Med)", "Natrlicher Hafen (Mit)", + "No Way Out (Lg)", "Kein Entkommen (Gr)", + "Normandy Landing (Lg)", "Landung in der Normandie (Gr)", + "Ore Wars (Med)", "Die Erz-Kriege (Mit)", + "Oz (Lg)", "Das Land Oz (Gr)", + "Pilgrim Fathers II (Lg)", "Die Grnderv„ter (Gr)", + "Pip's Ice Tea (Med)", "Pips Eistee (Mit)", + "Polar Panic (Lg)", "Panik am Pol (Gr)", + "Ponds (Med)", "Tmpelspringer (Mit)", + "Putney (Lg)", "Putney (Gr)", + "Return to Zion (Lg)", "Rckkehr nach Zion (Gr)", + "Ring of Land (Lg)", "Der Landring (Gr)", + "River Basin (Lg)", "Flusslauf (Gr)", + "River Delta (Med)", "Flussdelta (Mit)", + "River Islands (Med)", "Flussinsel (Mit)", + "River Maze (Sm)", "Flussgewirr (Kl)", + "Rivers (Sm)", "Flsse (Kl)", + "Run the Gauntlet (Med)", "Spiessrutenlauf (Mit)", + "Scappa Flow (Lg)", "Scapa Flow (Gr)", + "Siberian Slaughter (Lg)", "Sibirisches Gemetzel (Gr)", + "Sleepy Valley (Sm)", "Tal der Ahnungslosen (Kl)", + "Snake River (Lg)", "Am Schlangenfluss (Gr)", + "Snow Wars (Lg)", "Krieg der Flocken (Gr)", + "Snowball fight (Lg)", "Schneeballschlacht (Gr)", + "Snowy Island (Lg)", "Schneeinsel (Gr)", + "So Near So Far (Sm)", "So nah und doch so fern (Kl)", + "South America (Lg)", "Sdamerika (Gr)", + "Spring Line (Lg)", "Frhlingsgefhle (Gr)", + "Star (Lg)", "Stern (Gr)", + "Straighter & Narrower (Sm)", "Enger & schmaler (Kl)", + "TerrainSpotting (Sm)", "TerrainSpotting (Kl)", + "The Bay (Lg)", "Die Bucht (Gr)", + "The Garden (Lg)", "Der Garten (Gr)", + "The Great Lakes (Med)", "Die Grossen Seen (Mit)", + "The Ice Arena (Lg)", "Eisarena (Gr)", + "The Lake District (Lg)", "Kalte Seenplatte (Gr)", + "The Linked lands (Lg)", "Die verbundenen L„nder (Gr)", + "The Mississippi (Med)", "Grsse von Tom Sawyer (Mit)", + "The Sticky Bit (Lg)", "Der klebrige Teil (Gr)", + "The Valley (Med)", "Das Tal (Mit)", + "The Woods Today (Lg)", "Waldl„ufer (Gr)", + "Things to Come (Lg)", "Was die Zukunft bringt (Gr)", + "Tiger Core (Sm)", "Das Herz des Tigers (Kl)", + "To the Core (Sm)", "Mitten ins Herz (Kl)", + "Tournament Hills (Lg)", "Hgel der Entscheidung (Gr)", + "Tropical Storm (Med)", "Tropenstrme (Mit)", + "Tundra Trouble (Lg)", "Tauziehen in der Tundra (Gr)", + "Uk (Med)", "GB (Mit)", + "Undiscovered Country (Sm)", "Unentdecktes Land (Kl)", + "United States (Med)", "US (Mit)", + "Volcano (Sm)", "Vulkan (Kl)", + "Wastelands (Lg)", "Wstenei (Gr)", + "Water Works (Sm)", "Wasserwerke (Kl)", + "World Map (Med)", "Weltkarte (Kl)", + "Zambezi (Lg)", "Sambesi (Gr)", + +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Inselmuster (gross, 8 Spieler)", + "Arena Valley Extreme (Mega 8 players)", "Arenatal (sehr gross, 8 Spieler)", + "Around the Rim (Sm 4 players)", "Um die Kante (klein, 4 Spieler)", + "Ashes to Ashes (Lg 6 players)", "Asche zu Asche (gross, 6 Spieler)", + "Artic Wasteland (Mega 8 players)", "Arktische Wste (sehr gross, 8 Spieler)", + "Badajoz (Med 4 players)", "Badjoz (mittelgross, 4 Spieler)", + "Baptism of Fire (Lg 6 players)", "Feuertaufe (gross, 6 Spieler)", + "Big Fish, Small Pond (Lg 6 players)", "Grosser Fisch im kleinen Teich (gross, 6 Spieler)", + "Blue Lakes (Lg 8 players)", "Die blauen Seen (gross, 8 Spieler)", + "Booby Traps (Mega 8 players)", "Vorsicht, Falle! (sehr gross, 8 Spieler)", + "Bridgehead (Lg 6 players)", "Brckenkopf im Niemandsland (gross, 6 Spieler)", + "Butterfly Bay (Lg 6 players)", "Schmetterlingsbucht (gross, 6 Spieler)", + "Central Conflict Extreme (Mega 8 players)", "Zentraler Konflikt fr K”nner (sehr gross, 8 Spieler)", + "Circles of Death (Mega 8 players)", "Todeskreise (sehr gross, 8 Spieler)", + "Cold Front (Med 6 players)", "Kaltfront ( mittelgross, 6 Spieler)", + "Cold Pass (Med 4 players)", "Cooler Pass (mittelgross, 4 Spieler)", + "Combat Zones (Mega 8 players)", "Kampfgebiete (sehr gross, 8 Spieler)", + "Conflict Cove (Sm 4 players)", "H”hlenkonflikt (klein, 4 Spieler)", + "Culloden Moor (Med 8 players)", "Culloden-Moor (mittelgross, 8 Spieler)", + "Damnation Alley (Mega 8 players)", "Strasse der Verdammten (sehr gross, 8 Spieler)", + "Death Valley (Mega 8 players)", "Tal des Todes (sehr gross, 8 Spieler)", + "Deep Six (Mega 8 players)", "Tiefe Sechs (sehr gross, 8 Spieler)", + "Destruction Derby (Mega 8 players)", "Destruction Derby (sehr gross, 8 Spieler)", + "Diamonds Aren't Forever (Mega 8 players)", "Verg„ngliche Diamanten (sehr gross, 8 Spieler)", + "Elysium (Sm 4 players)", "Elysium (klein, 4 Spieler)", + "Equal Shares (Lg 4 players)", "Gleiche Anteile (gross, 4 Spieler)", + "Frost Bitten (Mega 8 players)", "Frostbrand (sehr gross, 8 Spieler)", + "Frozen Valley (Med 6 players)", "Eisiges Tal (mittelgross, 6 Spieler)", + "Gettysburg (Sm 4 players)", "Gettysburg (klein, 4 Spieler)", + "Glacial Valley (Sm 4 players)", "Gletschertal (klein, 4 Spieler)", + "Gold Coast (Med 6 players)", "Goldkste (mittelgross, 6 Spieler)", + "Gold Rush (Lg 4 players)", "Goldrausch (gross, 4 Spieler)", + "Habitat (Lg 4 players)", "Habitat (gross, 4 Spieler)", + "Hades Frozen Over (Sm 4 players)", "Frostschutz fr die H”lle (klein, 4 Spieler)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (sehr gross, 8 Spieler)", + "Hastings (Sm 4 players)", "Hastings (klein, 4 Spieler)", + "Hell's Pass (Med 6 players)", "H”llenpass (mittelgross, 6 Spieler)", + "Holy Grounds (Mega 8 players)", "Heiliger Boden (sehr gross, 8 Spieler)", + "Ice Bergs (Med 6 players)", "Eisberge (mittelgross, 6 Spieler)", + "Ice Station (Lg 6 players)", "Eisstation (gross, 6 Spieler)", + "Ice Queen (Lg 4 players)", "Eisk”nigin (gross, 4 Spieler)", + "In the Sun (Med 6 players)", "Unter der Sonne (mittelgross, 6 Spieler)", + "Innocents? (Mega 8 players)", "Unschuldig? Wer? (sehr gross, 8 Spieler)", + "Islands (Med 8 players)", "Inseln im Nebel (mittelgross, 8 Spieler)", + "Island Plateau (Lg 4 players)", "Inselplateau (gross, 4 Spieler)", + "Island Wars Extreme (Mega 8 players)", "Extremes Inselspringen (sehr gross, 8 Spieler)", + "Kananga (Med 6 players)", "Kananga (mittelgross, 6 Spieler)", + "King of the Hills Extreme (Mega 8 players)", "K”nig des Maulwurfshgels (sehr gross, 8 Spieler)", + "Lake Land (Lg 8 players)", "Seenland (gross, 8 Spieler)", + "Land Locked (Lg 8 players)", "Das Verschlossene Land (gross, 8 Spieler)", + "Lanes (Med 8 players)", "Gassenjungen (mittelgross, 8 Spieler)", + "Leipzip (Sm 4 players)", "Leipzig (klein, 4 Spieler)", + "Meander (Lg 8 players)", "M„ander (gross, 8 Spieler)", + "Mekong (Med 8 players)", "Mekong (mittelgross, 8 Spieler)", + "Middle Ground (Med 8 players)", "Mittelsmann (mittelgross, 8 Spieler)", + "Naval Conquests (Mega 8 players)", "Kommt zur Marine, haben sie gesagt (sehr gross, 8 Spieler)", + "On your Marks (Med 4 players)", "Auf die Pl„tze (mittelgross, 4 Spieler)", + "Open Warfare (Mega 8 players)", "Offener Schlagabtausch (sehr gross, 8 Spieler)", + "Ore Gardens (Lg 8 players)", "Erzparadies (gross, 8 Spieler)", + "Potholes (Mega 8 players)", "Schlagl”cher (sehr gross, 8 Spieler)", + "Puddles (Med 4 players)", "Pftzen (mittelgross, 4 Spieler)", + "Random Violence (Mega 8 players)", "Unberechenbare Gewalt (sehr gross, 8 Spieler)", + "Revenge (Med 8 players)", "Rache (mittelgross, 8 Spieler)", + "Rias (Med 8 players)", "Kabul (mittelgross, 8 Spieler)", + "River Crossing (Sm 4 players)", "Die Furt (klein, 4 Spieler)", + "River Rampage (Mega 8 players)", "Flussfahrt (sehr gross, 8 Spieler)", + "River Rapids (Lg 6 players)", "Stromschnellen (gross, 6 Spieler)", + "Rivers Wild (Mega 8 players)", "Wildwasser (sehr gross, 8 Spieler)", + "Rorkes Drift (Lg 4 players)", "Rorkes Drift (gross, 4 Spieler)", + "Seaside (Med 4 players)", "Strandleben (mittelgross, 4 Spieler)", + "Shades (Med 8 players)", "Schattenreich (mittelgross, 8 Spieler)", + "Smuggler's Cove (Lg 6 players)", "Schmugglerh”hle (gross, 6 Spieler)", + "Snow Garden (Sm 2 players)", "Schneegest”ber (klein, 2 Spieler)", + "Stalingrad (Sm 4 players)", "Stalingrad (klein, 4 Spieler)", + "Sticks & Stones (Med 4 players)", "Holz und Steine (mittelgross, 4 Spieler)", + "Strathearn Valley (Lg 6 players)", "Das Tal von Strathearn (gross, 6 Spieler)", + "Super Bridgehead (Mega 8 players)", "Super-Brckenkopf (sehr gross, 8 Spieler)", + "Super Mekong (Mega 8 players)", "Super-Mekong (sehr gross, 8 Spieler)", + "Super Ore Gardens (Mega 8 players)", "Super-Erzparadies (sehr gross, 8 Spieler)", + "Switch (Med 4 players)", "Schalter (mittelgross, 4 Spieler)", + "The Berg (Mega 8 players)", "Der Berg (sehr gross, 8 Spieler)", + "The Boyne (Med 4 players)", "Boyne (mittelgross, 4 Spieler)", + "The Bulge (Sm 4 players)", "Die W”lbung (klein, 4 Spieler)", + "The Cauldron (Lg 6 players)", "Der Kessel (gross, 6 Spieler)", + "The Finger (Lg 6 players)", "Der Finger (gross, 6 Spieler)", + "The Hills Have Eyes (Mega 8 players)", "Die Hgel haben Augen (sehr gross, 8 Spieler)", + "The Keyes (Med 6 players)", "Ein Sumpf (mittelgross, 6 Spieler)", + "The Lakes (Med 8 players)", "Die Seen (mittelgross, 8 Spieler)", + "The Neck (Med 6 players)", "Der Hals (mittelgross, 6 Spieler)", + "The Web (Lg 6 players)", "Das Netz (gross, 6 Spieler)", + "To the Core (Lg 4 players)", "Mitten ins Herz (gross, 4 Spieler)", + "Trafalgar (Lg 4 players)", "Trafalgar (gross, 4 Spieler)", + "Twin Rivers (Sm 4 players)", "Zwillingsstr”me (klein, 4 Spieler)", + "Umtumbo Gorge (Lg 4 players)", "Die Umtumbo-Schlucht (gross, 4 Spieler)", + "Watch Your Step Extreme (Mega 8 players)", "Vorsicht, Lebensgefahr (sehr gross, 8 Spieler)", + "Waterfalls (Lg 8 players)", "Wasserfall (gross, 8 Spieler)", + "Waterloo Revisited (Lg 6 players)", "Zu Besuch in Waterloo (gross, 6 Spieler)", + "Water Werks (Mega 8 players)", "Wasserwerk (sehr gross, 8 Spieler)", + "Warlord's Lake (Sm 4 players)", "Der See des Kriegsgottes (klein, 4 Spieler)", + "Zama (Sm 4 players)", "Zama (klein, 4 Spieler)", +//#endif + NULL +}; +#endif +#ifdef FRENCH +char const *EngMisStr[] = { + + "A Path Beyond (Lg)", "Le Passage (Max)", + "Central Conflict (Lg)", "Conflit Central (Max)", + "Coastal Influence (Med)", "Le Chant des Canons (Moy)", + "Combat Alley (Med)", "Aux Armes! (Moy)", + "Desolation (Lg)", "D‚solation (Max)", + "Dugout Isle (Med)", "L'Ile Maudite (Moy)", + "Equal Opportunity (Sm)", "A Chances Egales (Min)", + "First Come, First Serve (Sm)", "La Loi du Plus Fort (Min)", + "Island Hoppers (Sm)", "D'une Ile … l'autre (Min)", + "Island Wars (Lg)", "Guerres Insulaires (Max)", + "Isle of Fury (Lg)", "L'Ile de la Furie(Max)", + "Ivory Wastelands (Sm)", "Terres d'Ivoire (Min)", + "Keep off the Grass (Sm)", "Hors de mon Chemin (Min)", + "Marooned II (Med)", "Isolement II (Moy)", + "Middle Mayhem (Sm)", "Chaos Interne (Min)", + "No Escape (Med)", "Le PiŠge (Moy)", + "No Man's Land (Med)", "No Man's Land (Moy)", + "Normandy (Med)", "Normandie (Moy)", + "North By Northwest (Lg)", "Nord, Nord-Ouest (Max)", + "Pond Skirmish (Med)", "Bain de Sang (Moy)", + "Raraku (Lg)", "Raraku (Max)", + "Ridge War (Med)", "Guerre au Sommet (Moy)", + "Shallow Grave (Med)", "La Saveur de la Mort (Moy)", + "Treasure Isle (Med)", "L'Ile au Tr‚sor (Moy)", + + "Africa (Lg)", "Afrique (Max)", + "Alaska Anarchy (Lg)", "Anarchie en Alaska (Max)", + "All that Glitters... (Lg)", "Tout ce qui brille... (Max)", + "Apre's Peace (Lg)", "Une Paix Durement N‚goci‚e... (Max)", + "Antartica (Lg)", "Antarctique (Max)", + "Armourgarden (Lg)", "La Guerre des Blind‚s (Max)", + "Austraila (Med)", "Australie (Moy)", + "Barrier to Entry (Lg)", "BarriŠre … l'Entr‚e (Max)", + "Bavarian Blast (Med)", "Tonnerre Bavarois (Moy)", + "Be Shore (Med)", "Plages Menac‚es (Moy)", + "Bearing Straits (Med)", "Droit Devant ! (Moy)", + "Blow Holes (Lg)", "CratŠres (Max)", + "Bonsai (Sm)", "Bonsa‹ (Min)", + "Brother Stalin (Lg)", "FrŠre Staline (Max)", + "Bullseye (Lg)", "L'oeil du Taureau (Max)", + "C&C (Med)", "C&C (Moy)", + "Camos Canyon (Med)", "Le Canyon (Moy)", + "Camos Coves (Lg)", "Criques (Max)", + "Camos Cross (Lg)", "La Croix de Guerre (Max)", + "Camos Crossing (Sm)", "La Crois‚e des Chemins (Min)", + "Central Arena (Lg)", "L'ArŠne Diabolique (Max)", + "Canyon River (Med)", "Au Milieu Coule Une RiviŠre (Moy)", + "Crossroads (Sm)", "Carrefours (Min)", + "Czech Mate (Lg)", "TchŠque et Mat (Max)", + "Dday (Med)", "Le Jour J (Moy)", + "Disaster Central (Lg)", "D‚sastre Central (Max)", + "Docklands (Med)", "L'Enfer des Docks (Moy)", + "East Coast (Med)", "C“te Est (Moy)", + "Eastern Seaboard (Lg)", "Rivages de l'Est (Max)", + "Finger Lake (Lg)", "Le Lac de tous les Dangers (Max)", + "Fjords (Med)", "Fjords (Moy)", + "Floodlands (Lg)", "Campagne Lacustre (Max)", + "Forest under fire (Lg)", "Forˆt en flammes (Max)", + "Four Corners (Lg)", "4 Coins (Max)", + "Frostbit Fjords (Lg)", "Fjords Gel‚s (Max)", + "Glenboig (Sm)", "Glenboig (Min)", + "Hell Frozen Over (Lg)", "Enfer de Glace Max)", + "India (Lg)", "Inde (Max)", + "Indirect Fire (Lg)", "Attaque Indirecte (Max)", + "Island Wars II (Lg)", "Guerres Insulaires II (Max)", + "Italy (Lg)", "Italie (Max)", + "Kabalo (Lg)", "Kabalo (Max)", + "King of the Hills (Lg)", "Le Roi des Montagnes (Max)", + "Lake Divide (Med)", "La Guerre du Lac (Moy)", + "Lakelands (Lg)", "Terres Submerg‚es (Max)", + "Land Ladder (Lg)", "Jusqu'au Sommet (Max)", + "Lotsa Lakes (Lg)", "Terres de Lacs (Max)", + "Lunar Battlefield (Lg Special)", "Combat Lunaire (Max Sp‚cial)", + "Malibu Fields (Med)", "Les Champs de Malibu (Moy)", + "Marshland (Med)", "Mar‚cages (Moy)", + "MyLai Delta (Med)", "Le Delta Mylai (Moy)", + "Natural Harbor (Med)", "Port Naturel (Moy)", + "No Way Out (Lg)", "Sans Issue (Max)", + "Normandy Landing (Lg)", "Le D‚barquement (Max)", + "Ore Wars (Med)", "La Guerre du Minerai (Moy)", + "Oz (Lg)", "Oz (Max)", + "Pilgrim Fathers II (Lg)", "Les PŠlerins 2 (Max)", + "Pip's Ice Tea (Med)", "Les Tranch‚es de Glace (Moy)", + "Polar Panic (Lg)", "Panique Polaire (Max)", + "Ponds (Med)", "Les Etangs (Moy)", + "Putney (Lg)", "La Meilleure D‚fense... (Max)", + "Return to Zion (Lg)", "Retour … Sion (Max)", + "Ring of Land (Lg)", "Le Cycle Infernal (Max)", + "River Basin (Lg)", "Confrontation Navale (Max)", + "River Delta (Med)", "Le Delta (Moy)", + "River Islands (Med)", "C“tes … Surveiller de PrŠs (Moy)", + "River Maze (Sm)", "Labyrinthe Fluvial (Min)", + "Rivers (Sm)", "RiviŠres (Min)", + "Run the Gauntlet (Med)", "Relevons le D‚fi ! (Moy)", + "Scappa Flow (Lg)", "Combats Sanglants (Max)", + "Siberian Slaughter (Lg)", "Carnage Sib‚rien (Max)", + "Sleepy Valley (Sm)", "La Vall‚e Endormie (Min)", + "Snake River (Lg)", "La RiviŠre aux Serpents (Max)", + "Snow Wars (Lg)", "Guerres de Neige (Max)", + "Snowball fight (Lg)", "Bataille de Boules de Neige (Max)", + "Snowy Island (Lg)", "L'Ile sous la Neige (Max)", + "So Near So Far (Sm)", "Si Loin, Si Proche (Min)", + "South America (Lg)", "Am‚rique du Sud (Max)", + "Spring Line (Lg)", "Ligne de Front (Max)", + "Star (Lg)", "Etoile (Max)", + "Straighter & Narrower (Sm)", "L'Entonnoir (Min)", + "TerrainSpotting (Sm)", "TerrainSpotting (Min)", + "The Bay (Lg)", "La Baie (Max)", + "The Garden (Lg)", "Le Jardin (Max)", + "The Great Lakes (Med)", "Les Grands Lacs (Moy)", + "The Ice Arena (Lg)", "L'ArŠne de Glace (Max)", + "The Lake District (Lg)", "Un Lac Imprenable (Max)", + "The Linked lands (Lg)", "Passages … Gu‚ (Max)", + "The Mississippi (Med)", "Mississippi (Moy)", + "The Sticky Bit (Lg)", "Marasme (Max)", + "The Valley (Med)", "La Vall‚e (Moy)", + "The Woods Today (Lg)", "Aujoud'hui: la Mort ! (Max)", + "Things to Come (Lg)", "D‚nouement Incertain (Max)", + "Tiger Core (Sm)", "Le Coeur du Tigre (Min)", + "To the Core (Sm)", "Le Coeur du Conflit (Min)", + "Tournament Hills (Lg)", "Combat en Altitude (Max)", + "Tropical Storm (Med)", "Ouragan Tropical (Moy)", + "Tundra Trouble (Lg)", "La Toundra (Max)", + "Uk (Med)", "Royaume Uni (Moy)", + "Undiscovered Country (Sm)", "Terre Inconnue (Min)", + "United States (Med)", "Etats Unis (Moy)", + "Volcano (Sm)", "Le Volcan (Min)", + "Wastelands (Lg)", "Terres D‚sol‚es (Max)", + "Water Works (Sm)", "Jeux d'Eau (Min)", + "World Map (Med)", "Carte du Monde (Moy)", + "Zambezi (Lg)", "ZambŠze (Max)", +//#if 0 + "A Pattern of Islands (Lg 8 players)", "Archipel (Max. 8 joueurs)", + "Arena Valley Extreme (Mega 8 players)", "La Vall‚e de l'arŠne (XL 8 joueurs)", + "Around the Rim (Sm 4 players)", "Autour de la crˆte (Min. 4 joueurs)", + "Ashes to Ashes (Lg 6 players)", "R‚duit en cendres (Max. 6 joueurs)", + "Artic Wasteland (Mega 8 players)", "D‚solation arctique (XL 8 joueurs)", + "Badajoz (Med 4 players)", "Badjoz (Moy. 4 joueurs)", + "Baptism of Fire (Lg 6 players)", "Baptˆme du feu (Max. 6 joueurs)", + "Big Fish, Small Pond (Lg 6 players)", "Gros poisson, Min. Mare (Max. 6 joueurs)", + "Blue Lakes (Lg 8 players)", "Lacs bleus (Max. 8 joueurs)", + "Booby Traps (Mega 8 players)", "PiŠges (XL 8 joueurs)", + "Bridgehead (Lg 6 players)", "Tˆte de pont (Max. 6 joueurs)", + "Butterfly Bay (Lg 6 players)", "La baie du papillon (Max. 6 joueurs)", + "Central Conflict Extreme (Mega 8 players)", "Conflit central extrˆme (XL 8 joueurs)", + "Circles of Death (Mega 8 players)", "Les cercles de la mort (XL 8 joueurs)", + "Cold Front (Med 6 players)", "Front froid ( Moy. 6 joueurs)", + "Cold Pass (Med 4 players)", "La Passe Glac‚e (Moy. 4 joueurs)", + "Combat Zones (Mega 8 players)", "Zones de combat (XL 8 joueurs)", + "Conflict Cove (Sm 4 players)", "La Crique du conflit (Min. 4 joueurs)", + "Culloden Moor (Med 8 players)", "La Lande de Culloden (Moy. 8 joueurs)", + "Damnation Alley (Mega 8 players)", "Le chemin de la damnation (XL 8 joueurs)", + "Death Valley (Mega 8 players)", "La vall‚e de la mort (XL 8 joueurs)", + "Deep Six (Mega 8 players)", "Six de profondeur (XL 8 joueurs)", + "Destruction Derby (Mega 8 players)", "Stock car (XL 8 joueurs)", + "Diamonds Aren't Forever (Mega 8 players)", "Les diamants ne sont pas ‚ternels (XL 8 joueurs)", + "Elysium (Sm 4 players)", "Elys‚e (Min. 4 joueurs)", + "Equal Shares (Lg 4 players)", "Parts ‚gales (Max. 4 joueurs)", + "Frost Bitten (Mega 8 players)", "Engelures (XL 8 joueurs)", + "Frozen Valley (Med 6 players)", "La Vall‚e glac‚e (Moy. 6 joueurs)", + "Gettysburg (Sm 4 players)", "Gettysburg (Min. 4 joueurs)", + "Glacial Valley (Sm 4 players)", "Vall‚e de glace (Min. 4 joueurs)", + "Gold Coast (Med 6 players)", "La c“te dor‚e (Moy. 6 joueurs)", + "Gold Rush (Lg 4 players)", "La ru‚e vers l'or (Max. 4 joueurs)", + "Habitat (Lg 4 players)", "Habitat (Max. 4 joueurs)", + "Hades Frozen Over (Sm 4 players)", "Les enfers glac‚s (Min. 4 joueurs)", + "Hamburger Hill (Mega 8 players)", "Hamburger Hill (XL 8 joueurs)", + "Hastings (Sm 4 players)", "Hastings (Min. 4 joueurs)", + "Hell's Pass (Med 6 players)", "La route de l'enfer (Moy. 6 joueurs)", + "Holy Grounds (Mega 8 players)", "Terres saintes (XL 8 joueurs)", + "Ice Bergs (Med 6 players)", "Icebergs (Moy. 6 joueurs)", + "Ice Station (Lg 6 players)", "Station glac‚e (Max. 6 joueurs)", + "Ice Queen (Lg 4 players)", "Reine des glaces (Max. 4 joueurs)", + "In the Sun (Med 6 players)", "Sous le soleil (Moy. 6 joueurs)", + "Innocents? (Mega 8 players)", "Innocents ? (XL 8 joueurs)", + "Islands (Med 8 players)", "Iles (Moy. 8 joueurs)", + "Island Plateau (Lg 4 players)", "Plateau des Œles (Max. 4 joueurs)", + "Island Wars Extreme (Mega 8 players)", "Guerres insulaires extrˆme (XL 8 joueurs)", + "Kananga (Med 6 players)", "Kananga (Moy. 6 joueurs)", + "King of the Hills Extreme (Mega 8 players)", "Roi des collines extrˆme (XL 8 joueurs)", + "Lake Land (Lg 8 players)", "Paysage lacustre (Max. 8 joueurs)", + "Land Locked (Lg 8 players)", "Enclave (Max. 8 joueurs)", + "Lanes (Med 8 players)", "Le parcours du combattant (Moy. 8 joueurs)", + "Leipzip (Sm 4 players)", "Leipzig (Min. 4 joueurs)", + "Meander (Lg 8 players)", "M‚andre (Max. 8 joueurs)", + "Mekong (Med 8 players)", "M‚kong (Moy. 8 joueurs)", + "Middle Ground (Med 8 players)", "Plateau m‚dian (Moy. 8 joueurs)", + "Naval Conquests (Mega 8 players)", "Conquˆtes navales (XL 8 joueurs)", + "On your Marks (Med 4 players)", "A vos marques (Moy. 4 joueurs)", + "Open Warfare (Mega 8 players)", "Guerre ouverte (XL 8 joueurs)", + "Ore Gardens (Lg 8 players)", "Jardins de minerai (Max. 8 joueurs)", + "Potholes (Mega 8 players)", "Nids de poules (XL 8 joueurs)", + "Puddles (Med 4 players)", "Flaques (Moy. 4 joueurs)", + "Random Violence (Mega 8 players)", "Violence al‚atoire (XL 8 joueurs)", + "Revenge (Med 8 players)", "Vengeance (Moy. 8 joueurs)", + "Rias (Med 8 players)", "Rias (Moy. 8 joueurs)", + "River Crossing (Sm 4 players)", "Passage … gu‚ (Min. 4 joueurs)", + "River Rampage (Mega 8 players)", "RiviŠre d‚chaŒn‚e (XL 8 joueurs)", + "River Rapids (Lg 6 players)", "Rapides (Max. 6 joueurs)", + "Rivers Wild (Mega 8 players)", "RiviŠres sauvages (XL 8 joueurs)", + "Rorkes Drift (Lg 4 players)", "L'Exode de Rorkes (Max. 4 joueurs)", + "Seaside (Med 4 players)", "C“te (Moy. 4 joueurs)", + "Shades (Med 8 players)", "Ombres (Moy. 8 joueurs)", + "Smuggler's Cove (Lg 6 players)", "La Crique du contrebandier (Max. 6 joueurs)", + "Snow Garden (Sm 2 players)", "Jardin de neige (Min. 2 joueurs)", + "Stalingrad (Sm 4 players)", "Stalingrad (Min. 4 joueurs)", + "Sticks & Stones (Med 4 players)", "Bƒton & Roches (Moy. 4 joueurs)", + "Strathearn Valley (Lg 6 players)", "La Vall‚e de Strathearn (Max. 6 joueurs)", + "Super Bridgehead (Mega 8 players)", "Super tˆte de pont (XL 8 joueurs)", + "Super Mekong (Mega 8 players)", "Super M‚kong (XL 8 joueurs)", + "Super Ore Gardens (Mega 8 players)", "Super jardin de minerai (XL 8 joueurs)", + "Switch (Med 4 players)", "Permutation (Moy. 4 joueurs)", + "The Berg (Mega 8 players)", "Le Berg (XL 8 joueurs)", + "The Boyne (Med 4 players)", "Le Boyne (Moy. 4 joueurs)", + "The Bulge (Sm 4 players)", "Le bombement (Min. 4 joueurs)", + "The Cauldron (Lg 6 players)", "Le chaudron (Max. 6 joueurs)", + "The Finger (Lg 6 players)", "Le doigt (Max. 6 joueurs)", + "The Hills Have Eyes (Mega 8 players)", "Les collines ont des yeux (XL 8 joueurs)", + "The Keyes (Med 6 players)", "Les Keyes (Moy. 6 joueurs)", + "The Lakes (Med 8 players)", "Les lacs (Moy. 8 joueurs)", + "The Neck (Med 6 players)", "Le goulot (Moy. 6 joueurs)", + "The Web (Lg 6 players)", "La toile (Max. 6 joueurs)", + "To the Core (Lg 4 players)", "Jusqu'au cour (Max. 4 joueurs)", + "Trafalgar (Lg 4 players)", "Trafalgar (Max. 4 joueurs)", + "Twin Rivers (Sm 4 players)", "Les deux riviŠres (Min. 4 joueurs)", + "Umtumbo Gorge (Lg 4 players)", "La Gorge de Umtumbo (Max. 4 joueurs)", + "Watch Your Step Extreme (Mega 8 players)", "Pas-…-pas extrˆme (XL 8 joueurs)", + "Waterfalls (Lg 8 players)", "Chutes d'eau (Max. 8 joueurs)", + "Waterloo Revisited (Lg 6 players)", "Waterloo II (Max. 6 joueurs)", + "Water Werks (Mega 8 players)", "Jeux d'eau (XL 8 joueurs)", + "Warlord's Lake (Sm 4 players)", "Le lac du guerrier (Min. 4 joueurs)", + "Zama (Sm 4 players)", "Zama (Min. 4 joueurs)", +//#endif + NULL +}; +#endif + + +/* +******************************** Prototypes ********************************* +*/ +static int Net_Join_Dialog(void); +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color); +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len); +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init = 0); +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, + ListClass *gamelist, ColorListClass *playerlist, int join_index, + char *my_name, RejectType *why); +static int Net_New_Dialog(void); +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used); +void Start_WWChat(ColorListClass *playerlist); +int Update_WWChat(void); + + +#define PCOLOR_BROWN PCOLOR_GREY + + +/*********************************************************************************************** + * Init_Network -- initializes network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = Initialization OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Init_Network (void) +{ + NetNumType net; + NetNodeType node; +#ifdef WINSOCK_IPX + assert ( PacketTransport != NULL ); +#endif //WINSOCK_IPX + + //------------------------------------------------------------------------ + // This call allocates all necessary queue buffers, allocates Real-mode + // memory, and commands IPX to start listening on the Global Channel. + //------------------------------------------------------------------------ + if (!Ipx.Init()) { + return(false); + } + + //------------------------------------------------------------------------ + // Set up the IPX manager to cross a bridge + //------------------------------------------------------------------------ + if (Session.Type != GAME_INTERNET) { + if (Session.IsBridge) { + Session.BridgeNet.Get_Address(net,node); + Ipx.Set_Bridge(net); + } + } + + return(true); + +} /* end of Init_Network */ + + +/*********************************************************************************************** + * Shutdown_Network -- shuts down network stuff * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +void Shutdown_Network (void) +{ +// +// Note: The thought behind this section of code was that if the program +// terminates early, without an EventClass::EXIT event, it still needs to +// tell the other systems that it's gone, so it would send a SIGN_OFF packet. +// BUT, this causes a sync bug if the systems are running slow and this system +// is running ahead of the others; it will send the NET_SIGN_OFF >>before<< +// the other system execute their EventClass::EXIT event, and the other systems +// will kill the connection at some random Frame # & turn my stuff over to +// the computer possibly at different times. +// BRR, 10/29/96 +// +#if 0 + //------------------------------------------------------------------------ + // If the Players vector contains at least one name, send a sign-off + // packet. If 'Players' is empty, I have no name, so there's no point + // in sending a sign-off. + //------------------------------------------------------------------------ + if (Session.Players.Count()) { + //..................................................................... + // Build a sign-off packet & send it + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Players[0]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge && !Winsock.Get_Connected()) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 0, + &Session.BridgeNet); + } + + //..................................................................... + // Wait for the packets to finish going out (or the Global Channel + // times out) + //..................................................................... + for (;;) { + if (Ipx.Global_Num_Send()==0) { + break; + } + Ipx.Service(); + } + } +#endif + + //------------------------------------------------------------------------ + // If I was in a game, I'm not now, so clear the game name + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + +} /* end of Shutdown_Network */ + + +/*********************************************************************************************** + * Process_Global_Packet -- responds to remote queries * + * * + * The only commands from other systems this routine responds to are NET_QUERY_GAME * + * and NET_QUERY_PLAYER. The other commands are too context-specific to be able * + * to handle here, such as joining the game or signing off; but this routine handles * + * the majority of the program's needs. * + * * + * INPUT: * + * packet ptr to packet to process * + * address source address of sender * + * * + * OUTPUT: * + * true = packet was processed, false = wasn't * + * * + * WARNINGS: * + * Session.GameName must have been filled in before this function can be called. * + * * + * HISTORY: * + * 02/15/1995 BR : Created. * + *=============================================================================================*/ +bool Process_Global_Packet(GlobalPacketType *packet, IPXAddressClass *address) +{ + GlobalPacketType mypacket; + + //------------------------------------------------------------------------ + // If our Players vector is empty, just return. + //------------------------------------------------------------------------ + if (Session.Players.Count()==0) { + return (true); + } + + //------------------------------------------------------------------------ + // Another system asking what game this is + //------------------------------------------------------------------------ + if (packet->Command==NET_QUERY_GAME && Session.NetStealth==0) { + + //..................................................................... + // If the game is closed, let every player respond, and let the sender of + // the query sort it all out. This way, if the game's host exits the game, + // the game still shows up on other players' dialogs. + // If the game is open, only the game owner may respond. + //..................................................................... + if (strlen(Session.GameName) > 0 && ((!Session.NetOpen) || + (Session.NetOpen && + !strcmp(Session.Players[0]->Name,Session.GameName)))) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); + + mypacket.Command = NET_ANSWER_GAME; + strcpy(mypacket.Name, Session.GameName); + mypacket.GameInfo.IsOpen = Session.NetOpen; + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, + address); + } + return(true); + } + + //------------------------------------------------------------------------ + // Another system asking what player I am + //------------------------------------------------------------------------ + else if (packet->Command==NET_QUERY_PLAYER && + !strcmp (packet->Name, Session.GameName) && + (strlen(Session.GameName) > 0) && Session.NetStealth==0) { + + memset (&mypacket, 0, sizeof(GlobalPacketType)); // changed DRD 9/26 + + mypacket.Command = NET_ANSWER_PLAYER; + strcpy(mypacket.Name, Session.Players[0]->Name); + mypacket.PlayerInfo.House = Session.House; + mypacket.PlayerInfo.Color = Session.ColorIdx; + mypacket.PlayerInfo.NameCRC = Compute_Name_CRC(Session.GameName); + + Ipx.Send_Global_Message (&mypacket, sizeof(GlobalPacketType), 1, address); + return(true); + } + + return(false); + +} /* end of Process_Global_Packet */ + + +/*********************************************************************************************** + * Destroy_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player * + * on this connection * + * error 0 = user signed off; 1 = connection error; otherwise, no error is shown. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/22/1995 BR : Created. * + *=============================================================================================*/ +void Destroy_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + if (Debug_Print_Events) { + printf("Destroying connection for house %d (%s)\n", + id,HouseClass::As_Pointer((HousesType)id)->IniName); + } + + //------------------------------------------------------------------------ + // Do nothing if the house isn't human. + //------------------------------------------------------------------------ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + //------------------------------------------------------------------------ + // Create a message to display to the user + //------------------------------------------------------------------------ + txt[0] = '\0'; + if (error==1) { + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + } else if (error==0) { + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL,0, txt, housep->RemapColor, TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + //------------------------------------------------------------------------ + // Remove this player from the Players vector + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + //------------------------------------------------------------------------ + // Delete the IPX connection + //------------------------------------------------------------------------ + Ipx.Delete_Connection(id); + + //------------------------------------------------------------------------ + // Turn the player's house over to the computer's AI + //------------------------------------------------------------------------ + housep->IsHuman = false; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + //------------------------------------------------------------------------ + // If we're the last player left, tell the user. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} /* end of Destroy_Connection */ + + +/*********************************************************************************************** + * Remote_Connect -- handles connecting this user to others * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = connections established; false = not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +bool Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // Init my game name to 0-length, since I haven't joined any game yet. + //------------------------------------------------------------------------ + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Keep looping until something useful happens. + //------------------------------------------------------------------------ + while (1) { + //..................................................................... + // Pop up the network Join/New dialog + //..................................................................... + rc = Net_Join_Dialog(); + + //..................................................................... + // -1 = user selected Cancel + //..................................................................... + if (rc==-1) { + Session.NetStealth = stealth; + Session.NetOpen = 0; + return(false); + } + + //..................................................................... + // 0 = user has joined an existing game; save values & return + //..................................................................... + else if (rc==0) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } + + //..................................................................... + // 1 = user requests New Network Game + //..................................................................... + else if (rc==1) { + //.................................................................. + // Pop up the New Network Game dialog; if user selects OK, return + // 'true'; otherwise, return to the Join Dialog. + //.................................................................. + if (Net_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + Session.NetStealth = stealth; + Session.NetOpen = 0; + + return(true); + } else { + continue; + } + } + } + +} /* end of Remote_Connect */ + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Join_Dialog(void) +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 17 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_name_w = 70 *RESFACTOR; + int d_name_h = 9 *RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + (2*RESFACTOR); + +#ifdef OLDWAY + int d_gdi_w = 40 *RESFACTOR; + int d_gdi_h = 9 *RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 *RESFACTOR; + int d_nod_h = 9 *RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 *RESFACTOR; + int d_color_h = 9 *RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_gamelist_w = 155 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 - 2*RESFACTOR; + int d_gamelist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +//BG int d_playerlist_w = 113 *RESFACTOR; + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_playerlist_w - 2*RESFACTOR); + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_count_w = 25 *RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + d_margin2; + + int d_level_w = 25 *RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 *RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 *RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_level_h; + + int d_options_w = d_playerlist_w; + int d_options_h = ((5 * 6) + 4) *RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin2 - (2*RESFACTOR); + + int d_message1_w = d_dialog_w - (d_margin1 * 2) + 4*RESFACTOR; + int d_message1_h = (14 * d_txt6_h) +3*RESFACTOR; + int d_message1_x = d_dialog_x + (d_dialog_w-d_message1_w)/2; + int d_message1_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message1_h); + + int d_message2_w = d_message1_w; + int d_message2_h = (8 * d_txt6_h) + 3*RESFACTOR; + int d_message2_x = d_message1_x; + int d_message2_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message2_h); + +#ifdef FRENCH //VG2 + int d_join_w = 60 *RESFACTOR; +#else + int d_join_w = 40 *RESFACTOR; +#endif + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + (d_dialog_w / 6) - (d_join_w / 2); + int d_join_y = d_dialog_y + d_dialog_h - d_join_h - 8*RESFACTOR; + + int d_cancel_w = 50 *RESFACTOR; + int d_cancel_h = 9 *RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_join_y; + +#ifdef FRENCH + int d_new_w = 60 *RESFACTOR; +#else + int d_new_w = 40 *RESFACTOR; +#endif + int d_new_h = 9 *RESFACTOR; + int d_new_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_new_w / 2); + int d_new_y = d_join_y; + + int d_send_w = d_message1_w; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_message1_x; + int d_send_y = d_message1_y + d_message1_h; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_JOIN, + BUTTON_CANCEL, + BUTTON_NEW, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {71 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + char txt[128]; + char const *p; + int parms_received = 0; // 1 = game options received + int found; + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + int goto_lobby; + bool messages_have_focus = true; // Gadget focus starts on the message system + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, + d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w); +#else + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass joinbtn(BUTTON_JOIN, TXT_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + TextButtonClass newbtn(BUTTON_NEW, TXT_NEW, TPF_BUTTON, d_new_x, d_new_y, d_new_w); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + StaticButtonClass descrip(0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + 16*RESFACTOR, d_name_y, d_dialog_w - 32*RESFACTOR, d_txt6_h+1); + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color(&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color(&ColorRemaps[Session.ColorIdx]); + } + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Rule.IsMPBasesOn); + optionlist.Check_Item(1, Rule.IsMPTiberiumGrow); + optionlist.Check_Item(2, Rule.IsMPCrates); + optionlist.Check_Item(3, Rule.IsMPCaptureTheFlag); + optionlist.Check_Item(4, Rule.IsMPShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { +// for (HousesType house = HOUSE_FIRST; house <= HOUSE_TURKEY; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Session.Options.AIPlayers); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif +#ifdef WIN32 +//char *fred; +#endif + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + } + } + + /* + ** Collapse the country list if we are going to redraw the game list + */ + if (gamelist.Is_To_Redraw() && housebtn.IsDropped) { + housebtn.Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + housebtn.Collapse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + //............................................................... + // For game-browsing, label the name, side, & color buttons: + //............................................................... + if (joinstate < JOIN_CONFIRMED) { + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, + d_gdi_x + d_gdi_w, + d_gdi_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, + d_house_x + (d_house_w / 2), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); +#endif + + Fancy_Text_Print(TXT_COLOR_COLON, + d_dialog_x + ((d_dialog_w / 4) * 3), + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + } else { + //............................................................... + // If we're joined to a game, just print the player's name & side. + //............................................................... +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(txt,Text_String(TXT_S_PLAYING_S),namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (txt, Text_String(TXT_S_PLAYING_S), namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + Fancy_Text_Print(txt,d_dialog_cx, d_dialog_y + d_margin2 + (1*RESFACTOR), + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx], + TBLACK, TPF_CENTER | TPF_TEXT); + } + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); +#ifdef OLDWAY + gdibtn.Zap(); + nodbtn.Zap(); +#else + housebtn.Zap(); +#endif + name_edt.Zap(); + joinbtn.Zap(); + newbtn.Zap(); + countgauge.Zap(); + levelgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + staticcount.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + //............................................................... + // Only add the name edit field, the House, Join & New buttons if + // we're doing nothing, or we've just been rejected. + //............................................................... + if (joinstate < JOIN_CONFIRMED) { +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + name_edt.Add_Tail(*commands); + joinbtn.Add_Tail(*commands); + newbtn.Add_Tail(*commands); + } else { + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + staticcount.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + descrip.Add_Tail(*commands); + } + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS && joinstate < JOIN_CONFIRMED) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the message system; erase old messages first + //.................................................................. + if (display >= REDRAW_MESSAGE) { + if (joinstate==JOIN_CONFIRMED) { + Draw_Box(d_message2_x, d_message2_y, d_message2_w, d_message2_h, BOXSTYLE_BOX, true); + } else { + Draw_Box(d_message1_x, d_message1_y, d_message1_w, d_message1_h, BOXSTYLE_BOX, true); + } + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received && joinstate >= JOIN_CONFIRMED) { + + //............................................................... + // Scenario title + //............................................................... +// LogicPage->Fill_Rect(d_dialog_x + 16 *RESFACTOR, d_name_y, d_dialog_x + d_dialog_w - 16 *RESFACTOR, d_name_y + d_txt6_h, BLACK); + + p = Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { + + // EW - Scenario language translation goes here!!!!!!!! VG + int ii; + for (ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[ii])) { + #if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[ii+1]); + #else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + descrip.Set_Text(txt); + +// sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +// descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, scheme, BLACK, TPF_TEXT | TPF_CENTER, p, Session.Options.ScenarioDescription); + } else { + sprintf(txt, "%s %s", p, Text_String(TXT_NOT_FOUND)); + descrip.Set_Text(txt); +// Fancy_Text_Print("%s %s", d_dialog_cx, d_name_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER, p, Text_String(TXT_NOT_FOUND)); + } + //............................................................... + // Unit count, tech level, credits, ai players + //............................................................... +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, d_count_x + d_count_w + 35 *RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + Fancy_Text_Print(TXT_COUNT, d_count_x - 2 *RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticcount.Set_Text(txt); + staticcount.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2 *RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2 *RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2 *RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2 *RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2 *RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2 *RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + if (input & KN_BUTTON) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // Mouse Click: + // If we're joined to a game, display an error if the user tries to + // modify a read-only control. + // If user clicks on a color button: + // - If we've joined a game, don't allow a new color selection + // - otherwise, select that color + // - Change the color of the user's name & message field to match + // the newly-selected color. + //.................................................................. + case KN_LMOUSE: + if (joinstate > JOIN_NOTHING) { + if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + break; + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + + if (Session.ColorIdx == PCOLOR_DIALOG_BLUE) { + name_edt.Set_Color (&ColorRemaps[PCOLOR_REALLY_BLUE]); + } else { + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + } + name_edt.Flag_To_Redraw(); + + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + + display = REDRAW_COLORS; + } + break; + + //.................................................................. + // User clicks on the game list: + //.................................................................. + case (BUTTON_GAMELIST | KN_BUTTON): + //............................................................... + // Handle a double-click + //............................................................... + if (lastclick_timer < 30 && gamelist.Current_Index() == lastclick_idx) { + + //............................................................ + // If we're in a game, & the item clicked on is a different + // game, un-join the game we're in. + //............................................................ + if ((joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) && + lastclick_idx != game_index) { + if (gamelist.Current_Index() == 0) { + goto_lobby = 1; + } else { + goto_lobby = 0; + } + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, goto_lobby, d_message1_x, d_message1_y, d_txt6_h, + d_send_x, d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + + //......................................................... + // Clear the Player vector & the player list box, since + // our game_index has changed. + //......................................................... + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + } + //............................................................ + // If we clicked on another game, join that game. + //............................................................ + if (joinstate != JOIN_CONFIRMED && + joinstate != JOIN_WAIT_CONFIRM && lastclick_idx > 0) { + gamelist.Set_Selected_Index(lastclick_idx); + game_index = lastclick_idx; + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + //............................................................ + // Otherwise, we must have joined the lobby + //............................................................ + if (game_index == 0) { + Clear_Listbox (&playerlist); + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + Session.WWChat = 0; + display = REDRAW_ALL; + } + } else { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = gamelist.Current_Index(); + + //............................................................ + // If we've joined a game, don't allow the selected item to + // change + //............................................................ + if (joinstate==JOIN_CONFIRMED || joinstate==JOIN_WAIT_CONFIRM) { + gamelist.Set_Selected_Index(game_index); + } + + //............................................................ + // If we're not in a game, and the user clicks on a different + // entry, clear the player list & send a player query; + // init the click timer, to detect a double-click of this item. + //............................................................ + else if (gamelist.Current_Index() != game_index) { + + Clear_Listbox (&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + break; + +#ifdef OLDWAY + //.................................................................. + // House Buttons: set the player's desired House + //.................................................................. + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + break; +#else +#endif + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + case (BUTTON_JOIN | KN_BUTTON): + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + strcpy (Session.Handle,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_MESSAGE; + } + break; + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + } + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, d_message1_x, d_message1_y, d_txt6_h, d_send_x, + d_send_y, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } else { + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + process = false; + rc = -1; + } + break; + + //.................................................................. + // NEW: bail out with return code 1 + //.................................................................. + case (BUTTON_NEW | KN_BUTTON): + //............................................................... + // Force user to enter a name + //............................................................... + if (strlen(namebuf)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + + //............................................................... + // Ensure name is unique + //............................................................... + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!stricmp(Session.Games[i]->Name, namebuf)) { + found = 1; + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAMENAME_MUSTBE_UNIQUE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + } + + if (found) { + break; + } + + //............................................................... + // Save player & game name + //............................................................... + strcpy(Session.Handle,namebuf); + strcpy(Session.GameName,namebuf); +#ifndef OLDWAY + Session.House = (HousesType)(housebtn.Current_Index()+HOUSE_USSR); +#endif + + name_edt.Clear_Focus(); + name_edt.Flag_To_Redraw(); + + rc = 1; + process = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Service keyboard input for any message being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + // (We have to redraw the edit line, to erase the cursor.) + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + // Rather than setting 'display', which would redraw all msgs, + // we only need to erase & redraw the edit box here. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'Input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, namebuf); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // If we're joined in a game, send the message to every player + // in our player list. Skip the local system (index 0). + //............................................................ + if (joinstate == JOIN_CONFIRMED) { + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + } else { + //............................................................ + // Otherwise, send the message to all players in our chat list. + //............................................................ + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + if (Obfuscate(Session.GPacket.Message.Buf) == 0x72A47EF6) { + Session.WWChat = 1; + Clear_Listbox (&playerlist); + Start_WWChat(&playerlist); + } + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + display = REDRAW_MESSAGE; + } + +#ifdef OBSOLETE + // + // This is for the old drop-down list of houses, not used any more. + // + if (housebtn.IsDropped) { + isdropped = 1; + } else if (isdropped) { + display = REDRAW_ALL; + } +#endif + + break; + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) { + load_game = 1; + } + + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + + bool ready_packet_was_sent = false; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + + /* + ** If the scenario that the host wants to play doesn't exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I don't have. Request that the host sends the + ** scenario to me provided it's not an official scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + + Ipx.Set_Timing (30, (unsigned long) -1, 600); + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + rc = 0; + process = false; + + } else if (joinstate==JOIN_CONFIRMED) { + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (d_message2_x + 1 *RESFACTOR, d_message2_y + 1 *RESFACTOR, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + } else if (joinstate==JOIN_REJECTED) { + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + Session.Messages.Init (d_message1_x + 1 *RESFACTOR, d_message1_y + 1 *RESFACTOR, 14, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 *RESFACTOR, d_send_y + 1 *RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message2_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message2_w); + + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_REQUEST_DENIED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + + item = NULL; + if (why==REJECT_DUPLICATE_NAME) { + item = (char *)Text_String(TXT_NAME_MUSTBE_UNIQUE); + } + else if (why==REJECT_GAME_FULL) { + item = (char *)Text_String(TXT_GAME_FULL); + } + else if (why==REJECT_VERSION_TOO_OLD) { + item = (char *)Text_String(TXT_YOURGAME_OUTDATED); + } + else if (why==REJECT_VERSION_TOO_NEW) { + item = (char *)Text_String(TXT_DESTGAME_OUTDATED); + } + else if (why==REJECT_MISMATCH) { + item = (char *)Text_String(TXT_MISMATCH); + } + else if (why==REJECT_DISBANDED) { + item = (char *)Text_String(TXT_GAME_CANCELLED); + } + if (item) { + Session.Messages.Add_Message(NULL, 0, item, PCOLOR_BROWN, TPF_TEXT, 1200); + } + + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + display = REDRAW_ALL; + + } + } else if (event == EV_GAME_OPTIONS) { + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } else { + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + optionlist.Check_Item(0,Session.Options.Bases); + optionlist.Check_Item(1,Session.Options.Tiberium); + optionlist.Check_Item(2,Session.Options.Goodies); + optionlist.Check_Item(3,Special.IsCaptureTheFlag); + optionlist.Check_Item(4,Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } else if (event == EV_MESSAGE) { + //..................................................................... + // Draw an incoming message + //..................................................................... + display = REDRAW_MESSAGE; + Sound_Effect(VOC_INCOMING_MESSAGE); + } else if (event == EV_NEW_GAME) { + //..................................................................... + // If a new game has formed, or an existing game has changed state + // (from open to closed or closed to open), redraw the message system. + //..................................................................... + display = REDRAW_MESSAGE; + } else if (event == EV_NEW_PLAYER || event == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + if (Session.Options.AIPlayers > Rule.MaxPlayers-Session.Players.Count()) { + aiplayersgauge.Set_Value(Rule.MaxPlayers-Session.Players.Count()); + } + } else if (event == EV_GAME_SIGNOFF) { + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + gamelist.Flag_To_Redraw(); + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + + //..................................................................... + // If I've changed my name or color, make sure those changes go into + // the Chat vector. + //..................................................................... + strcpy(Session.Chat[0]->Name, namebuf); + Session.Chat[0]->Chat.Color = Session.ColorIdx; + if (Session.Chat[0]->Chat.Color == PCOLOR_DIALOG_BLUE) { + Session.Chat[0]->Chat.Color = PCOLOR_REALLY_BLUE; + } + + //..................................................................... + // Clean out the chat vector. If we find a node that we haven't heard + // from in 6 seconds, delete that node. + // If we haven't heard from a node in 5 seconds, send him a request + // for a chat announcement; he then has 1 second to reply. + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + if (TickCount - Session.Chat[i]->Chat.LastTime > 360) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + } else if (TickCount - Session.Chat[i]->Chat.LastTime > 300 && + Session.Chat[i]->Chat.LastChance == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Name[0] = 0; + Session.GPacket.Command = NET_CHAT_REQUEST; + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &(Session.Chat[i]->Address)); + Ipx.Service(); + Session.Chat[i]->Chat.LastChance = 1; + } + } + + //..................................................................... + // Manage the Lobby list: + // If the user has selected the 1st Game ("Lobby"), the names of all + // users in the Chat area show up in the Players list box. + // Users can be changing their names and their colors at any time, so + // we scan the Chat list each time to see if anything's changed; if + // so, we redraw the player list. + // (If WWChat is on, the Chat list is ignored, and the playerlist + // contains custom names.) + //..................................................................... + if (game_index == 0) { + if (!Session.WWChat) { + while (Session.Chat.Count() > playerlist.Count()) { + item = new char [MPLAYER_NAME_MAX]; + item[0] = 0; + playerlist.Add_Item(item); + playerlist.Flag_To_Redraw(); + } + while (playerlist.Count() > Session.Chat.Count()) { + item = (char *)playerlist.Get_Item(0); + playerlist.Remove_Item(item); + delete [] item; + playerlist.Flag_To_Redraw(); + } + for (i = 0; i < Session.Chat.Count(); i++) { + if (stricmp(Session.Chat[i]->Name,playerlist.Get_Item(i)) || + &ColorRemaps[ (Session.Chat[i]->Chat.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.Chat[i]->Chat.Color] != + playerlist.Colors[i]) { + + playerlist.Colors[i] = + &ColorRemaps[Session.Chat[i]->Chat.Color]; + if (playerlist.Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist.Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + strcpy((char *)playerlist.Get_Item(i), Session.Chat[i]->Name); + playerlist.Flag_To_Redraw(); + } + } + } else { + if (stricmp(Session.Chat[0]->Name,playerlist.Get_Item(0)) || + &ColorRemaps[Session.Chat[0]->Chat.Color] != + playerlist.Colors[0]) { + playerlist.Colors[0] = &ColorRemaps[Session.Chat[0]->Chat.Color]; + strcpy((char *)playerlist.Get_Item(0), Session.Chat[0]->Name); + playerlist.Flag_To_Redraw(); + } + if (Update_WWChat()) { + display = REDRAW_MESSAGE; + } + } + } + + } + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } else { + + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + starttime = TickCount; + while (TickCount - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ +// Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, +// Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Remove the chat edit box + //------------------------------------------------------------------------ + Session.Messages.Remove_Edit(); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +} /* end of Net_Join_Dialog */ + + +/*************************************************************************** + * Request_To_Join -- Sends a JOIN request packet to game owner * + * * + * Regardless of the return code, the Join Dialog will need to be redrawn * + * after calling this routine. * + * * + * INPUT: * + * playername player's name * + * join_index index of game we're joining * + * house requested house * + * color requested color * + * * + * OUTPUT: * + * 1 = Packet sent, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + *=========================================================================*/ +static int Request_To_Join (char *playername, int join_index, + HousesType house, PlayerColorType color) +{ + //------------------------------------------------------------------------ + // Validate join_index + //------------------------------------------------------------------------ + if (join_index < 1) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_MUST_SELECT_GAME), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + if ( (Session.Games.Count()<=1) || join_index > Session.Games.Count()) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NOTHING_TO_JOIN), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // Force user to enter a name + //------------------------------------------------------------------------ + if (strlen(playername)==0) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_NAME_ERROR), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return(false); + } + + //------------------------------------------------------------------------ + // The game must be open + //------------------------------------------------------------------------ + if (!Session.Games[join_index]->Game.IsOpen) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_GAME_IS_CLOSED), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + return (false); + } + + //------------------------------------------------------------------------ + // Send packet to game's owner + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_JOIN; + strcpy (Session.GPacket.Name, playername); + Session.GPacket.PlayerInfo.House = house; + Session.GPacket.PlayerInfo.Color = color; +#ifdef FIXIT_VERSION_3 + // Guest sends host his version. + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + if( Is_Aftermath_Installed() ) + { +// debugprint( "Guest tells host 'I have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version() | 0x80000000; + } + else + { +// debugprint( "Guest tells host 'I don't have Aftermath'\n" ); + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); + } +#else + Session.GPacket.PlayerInfo.MinVersion = VerNum.Min_Version(); +#endif + Session.GPacket.PlayerInfo.MaxVersion = VerNum.Max_Version(); + Session.GPacket.PlayerInfo.CheatCheck = RuleINI.Get_Unique_ID(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[join_index]->Address)); + + return(true); + +} /* end of Request_To_Join */ + + +/*************************************************************************** + * Unjoin_Game -- Cancels joining a game * + * * + * INPUT: * + * namebuf current player name * + * joinstate current join state * + * gamelist ListBox of game names * + * playerlist ListBox of player names * + * game_index index in 'gamelist' of game we're leaving * + * goto_lobby true = we're going to the lobby * + * msg_x message system x-coord * + * msg_y message system y-coord * + * msg_h message system char height * + * send_x message system send x-coord * + * send_y message system send y-coord * + * msg_len message system max msg length * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/12/1995 BRR : Created. * + *=========================================================================*/ +static void Unjoin_Game(char *namebuf,JoinStateType joinstate, + ListClass *gamelist, ColorListClass *playerlist, int game_index, + int goto_lobby, int msg_x, int msg_y, int msg_h, int send_x, int send_y, + int msg_len) +{ + int i; + char *item; + + //------------------------------------------------------------------------ + // Fill in a SIGN_OFF packet + //------------------------------------------------------------------------ + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + + //------------------------------------------------------------------------ + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //------------------------------------------------------------------------ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + if (joinstate == JOIN_WAIT_CONFIRM || joinstate == JOIN_CONFIRMED) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), 1, + &(Session.Games[game_index]->Address)); + } + + //------------------------------------------------------------------------ + // Re-init the message system to its new larger size + //------------------------------------------------------------------------ + Session.Messages.Init (msg_x + 1, msg_y + 1, 14, + msg_len, msg_h, send_x + 1, send_y + 1, 1, + 20, msg_len - 5); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_'); + + //------------------------------------------------------------------------ + // Remove myself from the player list, and reset my game name + //------------------------------------------------------------------------ + if (playerlist->Count()) { + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + } + + if (Session.Players.Count() > 0) { + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + } + + Session.GameName[0] = 0; + + //------------------------------------------------------------------------ + // Highlight "Lobby" on the Game list, Announce I'm ready to chat + //------------------------------------------------------------------------ + if (goto_lobby) { + gamelist->Set_Selected_Index(0); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + } + +} // end of Unjoin_Game + + +/*********************************************************************************************** + * Send_Join_Queries -- sends queries for the Join Dialog * + * * + * This routine [re]sends the queries related to the Join Dialog: * + * - NET_QUERY_GAME * + * - NET_QUERY_PLAYER for the game currently selected (if there is one) * + * * + * The queries are "staggered" in time so they aren't all sent at once; otherwise, we'd * + * be inundated with reply packets & we'd miss some (even though the replies will require * + * ACK's). * + * * + * INPUT: * + * curgame index of currently-selected game; -1 = none * + * joinstate our current joinstate * + * gamenow if 1, will immediately send the game query * + * playernow if 1, will immediately send the player query for currently-selected game * + * chatnow if 1, will immediately send the chat announcement * + * myname user's name * + * init initialize the timers * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static void Send_Join_Queries(int curgame, JoinStateType joinstate, + int gamenow, int playernow, int chatnow, char *myname, int init) +{ + //........................................................................ + // These values control the timeouts for sending various types of packets; + // they're designed such that they'll rarely occur simultaneously. + //........................................................................ + enum { + GAME_QUERY_TIME = 120, + PLAYER_QUERY_TIME = 35, + CHAT_ANNOUNCE_TIME = 83, + }; + static CDTimerClass game_timer; // time between NET_QUERY_GAME's + static CDTimerClass player_timer; // time between NET_QUERY_PLAYERS's + static CDTimerClass chat_timer; // time between NET_CHAT_ANNOUNCE's + + + //------------------------------------------------------------------------ + // Initialize timers + //------------------------------------------------------------------------ + if (init) { + game_timer = GAME_QUERY_TIME; + player_timer = PLAYER_QUERY_TIME; + chat_timer = CHAT_ANNOUNCE_TIME; + } + + //------------------------------------------------------------------------ + // Send the game-name query if the time has expired, or we're told to do + // it right now + //------------------------------------------------------------------------ + if (!game_timer || gamenow) { + + game_timer = GAME_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_GAME; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the player query for the game currently clicked on, if the time has + // expired and there is a currently-selected game, or we're told to do it + // right now + //------------------------------------------------------------------------ + if ( ((curgame > 0) && (curgame < Session.Games.Count()) && + !player_timer) || playernow) { + + player_timer = PLAYER_QUERY_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_QUERY_PLAYER; + strcpy (Session.GPacket.Name, Session.Games[curgame]->Name); + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + //..................................................................... + // If the user specified a remote server address, broadcast over that + // network, too. + //..................................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + + //------------------------------------------------------------------------ + // Send the chat announcement + //------------------------------------------------------------------------ + if ((!chat_timer && joinstate!=JOIN_CONFIRMED) || chatnow) { + + chat_timer = CHAT_ANNOUNCE_TIME; + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy (Session.GPacket.Name, myname); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + } + +} /* end of Send_Join_Queries */ + + +/*********************************************************************************************** + * Get_Join_Responses -- sends queries for the Join Dialog * + * * + * This routine polls the Global Channel to see if there are any incoming packets; * + * if so, it processes them. This routine can change the state of the Join Dialog, or * + * the contents of the list boxes, based on what the packet is. * + * * + * The list boxes are passed in as pointers; they can't be made globals, because they * + * can't be constructed, because they require shape pointers to the arrow buttons, and * + * the mix files won't have been initialized when the global variables' constructors are * + * called. * + * * + * This routine sets the globals * + * Session.House (from NET_CONFIRM_JOIN) * + * Session.ColorIdx (from NET_CONFIRM_JOIN) * + * Session.Options.Bases (from NET_GAME_OPTIONS) * + * Session.Options.Tiberium (from NET_GAME_OPTIONS) * + * Session.Options.Goodies (from NET_GAME_OPTIONS) * + * Session.Options.Ghosts (from NET_GAME_OPTIONS) * + * ScenarioIdx (from NET_GAME_OPTIONS; -1 = scenario not found) * + * * + * INPUT: * + * joinstate current state of Join Dialog * + * gamelist list box containing game names * + * playerlist list box containing player names for the currently-selected game * + * join_index index of the game we've joined or are asking to join * + * my_name name of local system * + * why ptr: filled in with reason for rejection from a game * + * * + * OUTPUT: * + * Event that occurred * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 04/15/1995 BRR : Created. * + *=============================================================================================*/ +static JoinEventType Get_Join_Responses(JoinStateType *joinstate, ListClass *gamelist, + ColorListClass *playerlist, int join_index, char *my_name, RejectType *why) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Games or Players + int i; + int found; + JoinEventType retcode = EV_NONE; + char txt[80]; + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) + return(EV_NONE); + + //------------------------------------------------------------------------ + // If we're joined in a game, handle the packet in a standard way; otherwise, + // don't answer standard queries. + //------------------------------------------------------------------------ + if ( (*joinstate)==JOIN_CONFIRMED && + Process_Global_Packet(&Session.GPacket,&Session.GAddress)!=0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // NET_ANSWER_GAME: Another system is answering our GAME query, so add that + // system to our list box if it's new. + //------------------------------------------------------------------------ + if (Session.GPacket.Command==NET_ANSWER_GAME) { + + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name)) { + found = 1; + + //............................................................... + // If name was found, update the node's time stamp & IsOpen flag. + //............................................................... + Session.Games[i]->Game.LastTime = TickCount; + if (Session.Games[i]->Game.IsOpen != Session.GPacket.GameInfo.IsOpen) { + item = (char *)gamelist->Get_Item(i); + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME), + Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + Session.Games[i]->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + gamelist->Flag_To_Redraw(); + + //............................................................ + // If this game has gone from closed to open, copy the + // responder's address into our Game slot, since the guy + // responding to this must be game owner. + //............................................................ + if (Session.Games[i]->Game.IsOpen) { + Session.Games[i]->Address = Session.GAddress; + } + + //............................................................ + // If we're in chat mode, print a message that the state of + // this game has changed. + //............................................................ + if (*joinstate < JOIN_CONFIRMED) { + if (Session.Games[i]->Game.IsOpen) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_FORMING); + } + else { + sprintf(txt,Text_String(TXT_GAME_NOW_IN_PROGRESS), + Session.Games[Session.Games.Count()-1]->Name); + Sound_Effect(VOC_GAME_CLOSED); + } + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + retcode = EV_NEW_GAME; + } + } + break; + } + } + + //..................................................................... + // name not found (or addresses are different); add it to 'Games' + //..................................................................... + if (found==0) { + + //.................................................................. + // Create a new node structure, fill it in, add it to 'Games' + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Game.IsOpen = Session.GPacket.GameInfo.IsOpen; + who->Game.LastTime = TickCount; + Session.Games.Add (who); + + //.................................................................. + // Create a string for "xxx's Game", leaving room for brackets around + // the string if it's a closed game + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; + if (Session.GPacket.GameInfo.IsOpen) { + sprintf(item,Text_String(TXT_THATGUYS_GAME),Session.GPacket.Name); + } + else { + sprintf(item,Text_String(TXT_THATGUYS_GAME_BRACKET), + Session.GPacket.Name); + } + gamelist->Add_Item(item); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this game is open, display a message stating that it's + // now available. + //.................................................................. + if (Session.GPacket.GameInfo.IsOpen && (*joinstate) < JOIN_CONFIRMED) { + sprintf(txt,Text_String(TXT_S_FORMED_NEW_GAME), + Session.GPacket.Name); + Session.Messages.Add_Message(NULL, 0, txt, PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_GAME_FORMING); + } + + retcode = EV_NEW_GAME; + } + } + + //------------------------------------------------------------------------ + // NET_ANSWER_PLAYER: Another system is answering our PLAYER query, so add + // it to our player list box & the Player Vector if it's new + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_ANSWER_PLAYER) { + //..................................................................... + // See if this name is unique + //..................................................................... + retcode = EV_NONE; + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // If the address is already present, re-copy their name, color & + // house into the existing entry, in case they've changed it without + // our knowledge; set the 'found' flag so we won't create a new entry. + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + strcpy(Session.Players[i]->Name, Session.GPacket.Name); + Session.Players[i]->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players[i]->Player.Color = Session.GPacket.PlayerInfo.Color; + + playerlist->Colors[i] = + &ColorRemaps[Session.GPacket.PlayerInfo.Color]; + + if (playerlist->Colors[i] == &ColorRemaps[PCOLOR_DIALOG_BLUE]) { + playerlist->Colors[i] = &ColorRemaps[PCOLOR_REALLY_BLUE]; + } + + found = 1; + break; + } + } + + //..................................................................... + // Don't add this player if he's not part of the game that's selected. + //..................................................................... + i = gamelist->Current_Index(); + if (Session.Games.Count() && Session.GPacket.PlayerInfo.NameCRC != + Compute_Name_CRC(Session.Games[i]->Name)) { + found = 1; + } + + //..................................................................... + // Don't add this player if it's myself. (We must check the name + // since the address of myself in 'Players' won't be valid.) + //..................................................................... + if (!strcmp (my_name,Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // name not found, or address didn't match; add to player list box + // & Players Vector + //..................................................................... + if (found==0) { + //.................................................................. + // Create & add a node to the Vector + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + who->Player.Color = Session.GPacket.PlayerInfo.Color; + Session.Players.Add (who); + + //.................................................................. + // Create & add a string to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // If this player's in the Chat vector, remove him from there + //.................................................................. + for (i = 1; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + break; + } + } + + //.................................................................. + // If this player has joined our game, play a special sound. + //.................................................................. + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_JOINED); + } + + retcode = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_CONFIRM_JOIN: The game owner has confirmed our JOIN query; mark us + // as being confirmed, and start answering queries from other systems + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CONFIRM_JOIN) { + if ( (*joinstate) != JOIN_CONFIRMED) { + strcpy (Session.GameName, Session.GPacket.Name); + Session.House = Session.GPacket.PlayerInfo.House; + Session.ColorIdx = Session.GPacket.PlayerInfo.Color; + + (*joinstate) = JOIN_CONFIRMED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_REJECT_JOIN: The game owner has turned down our JOIN query; restore + // the dialog state to its first pop-up state. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_REJECT_JOIN) { + //..................................................................... + // If we're confirmed in a game, broadcast a sign-off to tell all other + // systems that I'm no longer a part of any game; this way, I'll be + // properly removed from their dialogs. + //..................................................................... + if ( (*joinstate) == JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name,my_name); + + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Session.GameName[0] = 0; + + //.................................................................. + // remove myself from the player list + //.................................................................. + item = (char *)(playerlist->Get_Item(0)); + playerlist->Remove_Item(item); + delete [] item; + playerlist->Flag_To_Redraw(); + + delete Session.Players[0]; + Session.Players.Delete(Session.Players[0]); + + (*joinstate) = JOIN_REJECTED; + (*why) = REJECT_BY_OWNER; + retcode = EV_STATE_CHANGE; + } + //..................................................................... + // If we're waiting for confirmation & got rejected, tell the user why + //..................................................................... + else if ((*joinstate) == JOIN_WAIT_CONFIRM) { + (*why) = (RejectType)Session.GPacket.Reject.Why; + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_GAME_OPTIONS: The game owner has changed the game options & is + // sending us the new values. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GAME_OPTIONS) { + if ( (*joinstate)==JOIN_CONFIRMED || (*joinstate)==JOIN_WAIT_CONFIRM) { + Session.Options.Credits = Session.GPacket.ScenarioInfo.Credits; + Session.Options.Bases = Session.GPacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = Session.GPacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = Session.GPacket.ScenarioInfo.IsGoodies; +// Session.Options.Ghosts = Session.GPacket.ScenarioInfo.IsGhosties; + Session.Options.AIPlayers = Session.GPacket.ScenarioInfo.AIPlayers; + BuildLevel = Session.GPacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = Session.GPacket.ScenarioInfo.UnitCount; + Seed = Session.GPacket.ScenarioInfo.Seed; + Special = Session.GPacket.ScenarioInfo.Special; + Options.GameSpeed = Session.GPacket.ScenarioInfo.GameSpeed; + +#ifdef FIXIT_VERSION_3 + // Guest receives game version number from host. + // Added to the transmitted version number is a bit indicating presence of Aftermath expansion. + unsigned long lVersion = Session.GPacket.ScenarioInfo.Version & ~0x80000000; // Actual version number. + Session.CommProtocol = VerNum.Version_Protocol( lVersion ); + bAftermathMultiplayer = Session.GPacket.ScenarioInfo.Version & 0x80000000; +// if( bAftermathMultiplayer ) +// debugprint( "Guest hears host say 'This is an Aftermath game'\n" ); +// else +// debugprint( "Guest hears host say 'This is NOT an Aftermath game'\n" ); +#else + Session.CommProtocol = VerNum.Version_Protocol(Session.GPacket.ScenarioInfo.Version); + PlayingAgainstVersion = Session.GPacket.ScenarioInfo.Version; +#endif + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Rule.IsTGrowth = 1; + Special.IsTSpread = 1; + Rule.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Rule.IsTGrowth = 0; + Special.IsTSpread = 0; + Rule.IsTSpread = 0; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, Session.GPacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, Session.GPacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest, sizeof (Session.GPacket.ScenarioInfo.FileDigest)); +#else + strcpy (Session.ScenarioDigest, (char*)Session.GPacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = Session.GPacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = Session.GPacket.ScenarioInfo.FileLength; + + retcode = EV_GAME_OPTIONS; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // both the game list & player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + //..................................................................... + // Remove this name from the list of games + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (!strcmp(Session.Games[i]->Name, Session.GPacket.Name) && + Session.Games[i]->Address==Session.GAddress) { + + //............................................................... + // If the system signing off is the currently-selected list + // item, clear the player list since that game is no longer + // forming. + //............................................................... + if (i==gamelist->Current_Index()) { + Clear_Listbox (playerlist); + Clear_Vector (&Session.Players); + } + + //............................................................... + // If the system signing off was the owner of our game, mark + // ourselves as rejected + //............................................................... + if ( (*joinstate) > JOIN_NOTHING && i==join_index) { + (*joinstate) = JOIN_REJECTED; + retcode = EV_STATE_CHANGE; + (*why) = REJECT_DISBANDED; + } + + //............................................................... + // Set my return code + //............................................................... + if (retcode == EV_NONE) { + if (i <= gamelist->Current_Index()) { + retcode = EV_GAME_SIGNOFF; + } + else { + retcode = EV_PLAYER_SIGNOFF; + } + } + + //............................................................... + // Remove game name from game list + //............................................................... + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + item = (char *)(gamelist->Get_Item (i)); + gamelist->Remove_Item (item); + delete [] item; + gamelist->Flag_To_Redraw(); + + } + } + + //..................................................................... + // Remove this name from the list of players + //..................................................................... + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Players[i]->Address==Session.GAddress) { + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + delete [] item; + + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + playerlist->Flag_To_Redraw(); + + //............................................................... + // If this player has left our game, play a special sound. + //............................................................... + if ((*joinstate)>=JOIN_CONFIRMED) { + Sound_Effect(VOC_PLAYER_LEFT); + } + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + + //..................................................................... + // Remove this name from the chat list + //..................................................................... + for (i = 1; i < Session.Chat.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (Session.Chat[i]->Address==Session.GAddress) { + + delete Session.Chat[i]; + Session.Chat.Delete(Session.Chat[i]); + + if (retcode == EV_NONE) { + retcode = EV_PLAYER_SIGNOFF; + } + } + } + } + + //------------------------------------------------------------------------ + // NET_GO: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_GO) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START; + retcode = EV_STATE_CHANGE; + Session.HostAddress = Session.GAddress; + } + } + + //------------------------------------------------------------------------ + // NET_LOADGAME: The game's owner is signalling us to start playing. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_LOADGAME) { + if ( (*joinstate)==JOIN_CONFIRMED) { + Session.MaxAhead = Session.GPacket.ResponseTime.OneWay; + (*joinstate) = JOIN_GAME_START_LOAD; + retcode = EV_STATE_CHANGE; + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_ANNOUNCE: Someone is ready to chat; add them to our list, if + // they aren't already on it, and it's not myself. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_ANNOUNCE) { + found = 0; + //..................................................................... + // If this packet is from myself, don't add it to the list + //..................................................................... + if (Session.GPacket.Chat.ID == Session.UniqueID) { + found = 1; + } + //..................................................................... + // Otherwise, see if we already have this address stored in our list + // If so, update that node's time values & name (in case the user + // changed it), and return. + //..................................................................... + else { + for (i = 0; i < Session.Chat.Count(); i++) { + if (Session.Chat[i]->Address==Session.GAddress) { + strcpy (Session.Chat[i]->Name, Session.GPacket.Name); + Session.Chat[i]->Chat.LastTime = TickCount; + Session.Chat[i]->Chat.LastChance = 0; + Session.Chat[i]->Chat.Color = Session.GPacket.Chat.Color; + found = 1; + break; + } + } + } + //..................................................................... + // Add a new node to the list + //..................................................................... + if (!found) { + who = new NodeNameType; + strcpy (who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Chat.LastTime = TickCount; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + } + } + + //------------------------------------------------------------------------ + // NET_CHAT_REQUEST: Someone is requesting a CHAT_ANNOUNCE from us; send + // one to him directly. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_CHAT_REQUEST) { + if ((*joinstate) != JOIN_WAIT_CONFIRM && (*joinstate) != JOIN_CONFIRMED) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CHAT_ANNOUNCE; + strcpy(Session.GPacket.Name, my_name); + Session.GPacket.Chat.ID = Session.UniqueID; + Session.GPacket.Chat.Color = Session.ColorIdx; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &Session.GAddress); + + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + //..................................................................... + // If we're in a game, the sender must be in our game. + //..................................................................... + if ( (*joinstate)==JOIN_CONFIRMED) { + if (Session.GPacket.Message.NameCRC == + Compute_Name_CRC(Session.GameName)) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + } + //..................................................................... + // Otherwise, we're in the chat room; display any old message. + //..................................................................... + else { + Session.Messages.Add_Message (Session.GPacket.Name, + Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + } + + retcode = EV_MESSAGE; + } + + //------------------------------------------------------------------------ + // NET_PING: Someone is pinging me to get a response time measure (will only + // happen after I've joined a game). Do nothing; the IPX Manager will handle + // sending an ACK, and updating the response time measurements. + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_PING) { + retcode = EV_NONE; + } + + //------------------------------------------------------------------------ + // Default case: nothing happened. (This case will be hit every time I + // receive my own NET_QUERY_GAME or NET_QUERY_PLAYER packets.) + //------------------------------------------------------------------------ + else { + retcode = EV_NONE; + } + + return(retcode); + +} /* end of Get_Join_Responses */ + + +/*********************************************************************************************** + * Net_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_New_Dialog(void) +{ + return 0; //PG +#if (0) + typedef enum { + NUM_MESSAGES = 10 + } NumMessagesType; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + +//BG int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_w = 124*RESFACTOR; + int d_playerlist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR; + + int d_scenariolist_w = 162*RESFACTOR; + int d_scenariolist_h = (6 * d_txt6_h) + 3*RESFACTOR; // 6 rows high + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - (5*RESFACTOR); + int d_scenariolist_y = d_playerlist_y; + + int d_reject_w = 55*RESFACTOR; + int d_reject_h = 9*RESFACTOR; + int d_reject_x = d_playerlist_x + (d_playerlist_w / 2) - (d_reject_w / 2); + int d_reject_y = d_playerlist_y + d_playerlist_h + d_margin2; + + int d_count_w = 25*RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_count_y = d_reject_y + d_reject_h /*KO+ d_margin2*/; + + int d_level_w = 25*RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25*RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106*RESFACTOR; + int d_options_h = ((5 * 6) + 4)*RESFACTOR; + int d_options_x = d_scenariolist_x + ((d_scenariolist_w - d_options_w) / 2); + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (NUM_MESSAGES * d_txt6_h) + 3*RESFACTOR; // 10 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_dialog_y + d_dialog_h - (27*RESFACTOR + d_message_h); +// int d_message_y = d_options_y + d_options_h + d_margin1; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9*RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 50*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + int d_cancel_w = 50*RESFACTOR; + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_SCENARIOLIST, + BUTTON_REJECT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int index; // index for rejecting a player + int rc; + int i,j; + char *item; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + char txt[80]; + JoinEventType whahoppa; // event generated by received packets + static int first_time = 1; // 1 = 1st time this dialog is run + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass rejectbtn(BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, 60*RESFACTOR); + + StaticButtonClass staticunit(0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y); + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y); + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y); + StaticButtonClass staticaiplayers(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y); + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + if (loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE +// loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init dialog values, only the first time through + //------------------------------------------------------------------------ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = + (SessionClass::CountMax[Session.Options.Bases] + + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = 0; + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = 0; + + //------------------------------------------------------------------------ + // Init scenario description list box + //------------------------------------------------------------------------ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + + break; + } + } + if ( EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + { + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected +#ifdef FIXIT_VERSION_3 + bAftermathMultiplayer = Is_Aftermath_Installed(); +// debugprint( "Host decides that, initially, bAftermathMultiplayer is %i\n", bAftermathMultiplayer ); +#else + PlayingAgainstVersion = VerNum.Version_Number(); +#endif + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the message display system + //------------------------------------------------------------------------ + Session.Messages.Init(d_message_x + 1*RESFACTOR, d_message_y + 1*RESFACTOR, NUM_MESSAGES, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1*RESFACTOR, d_send_y + 1*RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + /* + ** Zap, Zap, Zap + */ + playerlist.Zap(); + scenariolist.Zap(); + rejectbtn.Zap(); + staticunit.Zap(); + staticlevel.Zap(); + staticcredits.Zap(); + staticaiplayers.Zap(); + countgauge.Zap(); + creditsgauge.Zap(); + aiplayersgauge.Zap(); + levelgauge.Zap(); + optionlist.Zap(); + okbtn.Zap(); + cancelbtn.Zap(); + loadbtn.Zap(); + + + /* + ** Hack hack, hack + */ + commands = &playerlist; + scenariolist.Add_Tail(*commands); + rejectbtn.Add_Tail(*commands); + staticunit.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticaiplayers.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + okbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) { +#else + if (loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { +#endif +#ifdef FIXIT_MULTI_SAVE + loadbtn.Add_Tail(*commands); +#endif + } + commands->Draw_All(); + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2*RESFACTOR, d_count_y, d_count_x + d_count_w + 35*RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(txt,"%d",Session.Options.UnitCount); + staticunit.Set_Text(txt); + staticunit.Draw_Me(); +// Fancy_Text_Print(txt, d_count_x + d_count_w + 2*RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + staticlevel.Set_Text(txt); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 2*RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.Credits); + staticcredits.Set_Text(txt); + staticcredits.Draw_Me(); +// Fancy_Text_Print(txt, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT); + + sprintf(txt,"%d",Session.Options.AIPlayers); + staticaiplayers.Set_Text(txt); + staticaiplayers.Draw_Me(); +// Fancy_Text_Print(txt, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // New Scenario selected. + //.................................................................. +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + break; + +#else // FIXIT_VERSION_3 + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + }else + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + } else { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + } + break; +#endif // FIXIT_VERSION_3 + + //.................................................................. + // Reject the currently-selected player (don't allow rejecting myself, + // who will be the first entry in the list) + //.................................................................. + case (BUTTON_REJECT | KN_BUTTON): + index = playerlist.Current_Index(); + + if (index == 0) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_CANT_REJECT_SELF), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + + } else if (index < 0 || index >= playerlist.Count()) { + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_SELECT_PLAYER_REJECT), + PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + break; + } + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_REJECT_JOIN; + + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 1, + &(Session.Players[index]->Address)); + break; + + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case (BUTTON_OPTIONS | KN_BUTTON): + if (Special.IsCaptureTheFlag != optionlist.Is_Checked(3) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != (optionlist.Is_Checked(0) ? 1 : 0)) { + Session.Options.Bases = (optionlist.Is_Checked(0) ? 1 : 0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + optionlist.Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsCaptureTheFlag = optionlist.Is_Checked(3); + Special.IsShadowGrow = optionlist.Is_Checked(4); + + transmit = 1; + display = REDRAW_PARMS; + break; + + //.................................................................. + // OK: exit loop with TRUE status + //.................................................................. + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = max(Ipx.Global_Response_Time() * 2, 60); + while ((int)TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Players.Count() > 1 ) { +// if (Session.Players.Count() + Session.Options.AIPlayers > 1 ) { + rc = TRUE; + process = FALSE; + } else { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_ONE), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + display = REDRAW_MESSAGE; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) { + load_game = 1; + } else { + load_game = 0; + } + break; + + //.................................................................. + // CANCEL: send a SIGN_OFF, bail out with error code + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // Broadcast my sign-off over a bridged network if there is one + //............................................................... + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Messages.Manage()) { + display = REDRAW_MESSAGE; + } + + //............................................................... + // Re-draw the messages & service keyboard input for any message + // being edited. + //............................................................... + i = Session.Messages.Input(input); + + //............................................................... + // If 'Input' returned 1, it means refresh the message display. + //............................................................... + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + //............................................................... + // If 'Input' returned 2, it means redraw the message display. + //............................................................... + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); // (erase the cursor) + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + //............................................................... + // If 'input' returned 3, it means send the current message. + //............................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_MESSAGE; + strcpy (Session.GPacket.Name, Session.Handle); + if (i==3) { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Edit_Buf()); + } else { + strcpy (Session.GPacket.Message.Buf, + Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + Session.GPacket.Message.Color = Session.ColorIdx; + Session.GPacket.Message.NameCRC = + Compute_Name_CRC(Session.GameName); + + //............................................................ + // Send the message to every player in our player list, except + // myself. + //............................................................ + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Add the message to our own list, since we're not in the + // player list on this dialog. + // If there's no message with this ID already displayed, just + // add a new message; if there is one, concatenate it. + //............................................................ + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + display = REDRAW_MESSAGE; + } + break; + } + + //..................................................................... + // Process incoming packets + //..................................................................... +#ifndef FIXIT_VERSION_3 + int oldversion = PlayingAgainstVersion; +#endif + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + } +#ifdef FIXIT_VERSION_3 // All scenarios now allowable for download, regardless of if CS scen. or 126x126 scen. + if (display < REDRAW_PARMS) display = REDRAW_PARMS; +#else // FIXIT_VERSION_3 + if (oldversion == PlayingAgainstVersion){ + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + }else{ + + /* + ** If a CS scenario was selected and we now have a red alert only player + ** in the mix then deselect the cs scenario. + */ + #ifdef FIXIT_CSII // checked - ajw + if ((PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && + #else + if (PlayingAgainstVersion < VERSION_RED_ALERT_107 && + #endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #ifdef FIXIT_CSII // checked - ajw + /* + ** If an AM mega scenario was selected and we now have a non-AM + ** player in the mix then deselect the mega scenario. + */ + if (PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (0); + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + } + #endif + display = REDRAW_BACKGROUND; + } +#endif // FIXIT_VERSION_3 + + transmit = 1; + } else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } else if (whahoppa == EV_PLAYER_SIGNOFF) { + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - Session.Players.Count(); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + display = REDRAW_PARMS; + } + transmit = 1; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; +#ifdef FIXIT_VERSION_3 + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + // Host encodes whether or not this is an Aftermath game in the highest bit. + if( bAftermathMultiplayer ) + { +// debugprint( "Host tells guests 'This is an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version() | 0x80000000; + } + else + { +// debugprint( "Host tells guests 'This is NOT an Aftermath game'\n" ); + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + } +#else + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); +#endif + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = max( (Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response."); +#endif + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*10; // Wait for 10 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); +#endif +} /* end of Net_New_Dialog */ + + +/*************************************************************************** + * Get_NewGame_Responses -- processes packets for New Game dialog * + * * + * This routine can modify the contents of the given list box, as well * + * as the contents of the Players Vector global. * + * * + * INPUT: * + * playerlist list of players in this game * + * color_used array of color-used flags * + * * + * OUTPUT: * + * EV_NONE = nothing happened * + * EV_NEW_PLAYER = a new player has joined; false otherwise * + * EV_PLAYER_SIGNOFF = a player has left * + * EV_MESSAGE = a message was received * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/18/1995 BRR : Created. * + *=========================================================================*/ +static JoinEventType Get_NewGame_Responses(ColorListClass *playerlist, + int *color_used) +{ + int rc; + char * item; // general-purpose string + NodeNameType *who; // node to add to Players Vector + int i; + int found; + JoinEventType retval = EV_NONE; + int resend; + unsigned long version; // version # to use + + //------------------------------------------------------------------------ + // If there is no incoming packet, just return + //------------------------------------------------------------------------ + rc = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (!rc || Session.GProductID != IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + return(EV_NONE); + } + + //------------------------------------------------------------------------ + // Try to handle the packet in a standard way + //------------------------------------------------------------------------ + if (Process_Global_Packet(&Session.GPacket,&Session.GAddress) != 0) { + return(EV_NONE); + } else if (Session.GPacket.Command==NET_QUERY_JOIN) { + //------------------------------------------------------------------------ + // NET_QUERY_JOIN: + //------------------------------------------------------------------------ + //..................................................................... + // See if this name is unique: + // - If the name matches, but the address is different, reject this player + // - If the name & address match, this packet must be a re-send of a + // previous request; in this case, do nothing. The other player must have + // received my CONFIRM_JOIN packet (since it was sent with an ACK + // required), so we can ignore this resend. + //..................................................................... + found = 0; + resend = 0; + for (i = 1; i < Session.Players.Count(); i++) { + if (!strcmp(Session.Players[i]->Name,Session.GPacket.Name)) { + if (Session.Players[i]->Address != Session.GAddress) { + found = 1; + } + else { + resend = 1; + } + break; + } + } + //..................................................................... + // If his name is the same as mine, treat it like a duplicate name + //..................................................................... + if (!strcmp (Session.Players[0]->Name, Session.GPacket.Name)) { + found = 1; + } + + //..................................................................... + // Reject if name is a duplicate + //..................................................................... + if (found) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_DUPLICATE_NAME; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // Reject if there are too many players + //..................................................................... + else if ( (Session.Players.Count() >= Session.MaxPlayers) && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_GAME_FULL; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + /* + ** Don't allow joining if the rules.ini file doesn't appear to match. + */ + else if (Session.GPacket.PlayerInfo.CheatCheck != RuleINI.Get_Unique_ID() && !resend) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_MISMATCH; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), 1, &Session.GAddress); + return(EV_NONE); + } + + //..................................................................... + // If this packet is NOT a resend, accept the player. Grant him the + // requested color if possible. + //..................................................................... + else if (!resend) { + //.................................................................. + // Check the player's version range against our own, to see if + // there's an overlap region + //.................................................................. +#ifdef FIXIT_VERSION_3 + // Added to the transmitted _min_ version number is a bit indicating presence of Aftermath expansion. + bool bGuestHasAftermath = Session.GPacket.PlayerInfo.MinVersion & 0x80000000; + if( bGuestHasAftermath ) +// debugprint( "Host hears guest say 'I have Aftermath'\n" ); + ; + else + { +// debugprint( "Host hears guest say 'I don't have Aftermath'\n" ); + if( bAftermathMultiplayer ) + { +// debugprint( "Host decides this is no longer an Aftermath game!\n" ); + bAftermathMultiplayer = false; + } + } + + Session.GPacket.PlayerInfo.MinVersion &= ~0x80000000; // Strip special bit. +#endif + version = VerNum.Clip_Version (Session.GPacket.PlayerInfo.MinVersion, + Session.GPacket.PlayerInfo.MaxVersion); + +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + +// TCTCTC save off version number + + //.................................................................. + // Reject player if his version is too old + //.................................................................. + if (version == 0) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_OLD; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + + //.................................................................. + // Reject player if his version is too new + //.................................................................. + else if (version == 0xffffffff) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_REJECT_JOIN; + Session.GPacket.Reject.Why = (int)REJECT_VERSION_TOO_NEW; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + return(EV_NONE); + } + //.................................................................. + // If the player is accepted, our mutually-accepted version may be + // different; set the CommProtocol accordingly. + //.................................................................. + else { + Session.CommProtocol = VerNum.Version_Protocol(version); + } + + //.................................................................. + // Add node to the Vector list + //.................................................................. + who = new NodeNameType; + strcpy(who->Name, Session.GPacket.Name); + who->Address = Session.GAddress; + who->Player.House = Session.GPacket.PlayerInfo.House; + Session.Players.Add (who); + + //.................................................................. + // Set player's color; if requested color isn't used, give it to him; + // otherwise, give him the 1st available color. Mark the color we + // give him as used. + //.................................................................. + if (color_used[Session.GPacket.PlayerInfo.Color] == 0) { + who->Player.Color = Session.GPacket.PlayerInfo.Color; + } + else { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (color_used[i]==0) { + who->Player.Color = (PlayerColorType)i; + break; + } + } + } + color_used[who->Player.Color] = 1; + + //.................................................................. + // Add player name to the list box + //.................................................................. + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.GPacket.PlayerInfo.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.GPacket.Name, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.GPacket.Name, + Text_String(HouseTypeClass::As_Reference(Session.GPacket.PlayerInfo.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (who->Player.Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[who->Player.Color]); + + //.................................................................. + // Send a confirmation packet + //.................................................................. + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_CONFIRM_JOIN; + strcpy(Session.GPacket.Name,Session.Handle); + Session.GPacket.PlayerInfo.House = who->Player.House; + Session.GPacket.PlayerInfo.Color = who->Player.Color; + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.GAddress); + + //.................................................................. + // Play a special sound. + //.................................................................. + Sound_Effect(VOC_PLAYER_JOINED); + + retval = EV_NEW_PLAYER; + } + } + + //------------------------------------------------------------------------ + // NET_SIGN_OFF: Another system is signing off: search for that system in + // the player list, & remove it if found + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_SIGN_OFF) { + for (i = 0; i < Session.Players.Count(); i++) { + + //.................................................................. + // Name found; remove it + //.................................................................. + if (!strcmp (Session.Players[i]->Name, Session.GPacket.Name) && + Session.Players[i]->Address==Session.GAddress) { + + //............................................................... + // Remove from the list box + //............................................................... + item = (char *)(playerlist->Get_Item(i)); + playerlist->Remove_Item(item); + playerlist->Flag_To_Redraw(); + delete [] item; + + //............................................................... + // Mark his color as available + //............................................................... + color_used[Session.Players[i]->Player.Color] = 0; + + //............................................................... + // Delete from the Vector list + //............................................................... + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + + //............................................................... + // Play a special sound. + //............................................................... + Sound_Effect(VOC_PLAYER_LEFT); + + retval = EV_PLAYER_SIGNOFF; + + break; + } + } + } + + //------------------------------------------------------------------------ + // NET_MESSAGE: Someone is sending us a message + //------------------------------------------------------------------------ + else if (Session.GPacket.Command==NET_MESSAGE) { + Session.Messages.Add_Message (Session.GPacket.Name, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + Session.GPacket.Message.Buf, + (Session.GPacket.Message.Color == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.GPacket.Message.Color, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + + retval = EV_MESSAGE; + } + + return(retval); + +} /* end of Get_NewGame_Responses */ + + +/*************************************************************************** + * Compute_Name_CRC -- computes CRC from char string * + * * + * INPUT: * + * name string to create CRC for * + * * + * OUTPUT: * + * CRC * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/29/1995 BRR : Created. * + *=========================================================================*/ +unsigned long Compute_Name_CRC(char *name) +{ + char buf[80]; + unsigned long crc = 0L; + int i; + + strcpy (buf, name); + strupr (buf); + + for (i = 0; i < (int)strlen(buf); i++) { + Add_CRC (&crc, (unsigned long)buf[i]); + } + + return (crc); + +} /* end of Compute_Name_CRC */ + + +/*************************************************************************** + * Net_Reconnect_Dialog -- Draws/updates the network reconnect dialog * + * * + * INPUT: * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * oldest_index IPX connection index of oldest connection * + * (only used for reconnection) * + * timeval value to print in the countdown field * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/08/1995 BRR : Created. * + *=========================================================================*/ +void Net_Reconnect_Dialog(int reconn, int fresh, int oldest_index, + unsigned long timeval) +{ + reconn; fresh; oldest_index; timeval; +#if (0)//PG + static int x,y,w,h; + int id; + char buf1[40] = {0}; + char buf2[40] = {0}; + char const *buf3 = ""; + + int d_txt6_h = 6 * RESFACTOR+1; + int d_margin = 5 * RESFACTOR; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + fresh = true; + } +#endif + + //------------------------------------------------------------------------ + // Draw the dialog from scratch + //------------------------------------------------------------------------ + if (fresh) { + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + switch ( Session.Type) { + case GAME_IPX: + case GAME_INTERNET: + case GAME_MODEM: + case GAME_NULL_MODEM: + + if (reconn) { + id = Ipx.Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ipx.Connection_Name(id)); + } else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } + break; + +#if(TEN) + case GAME_TEN: + if (reconn) { + id = Ten->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + Ten->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // TEN + +#if(MPATH) + case GAME_MPATH: + if (reconn) { + id = MPath->Connection_ID(oldest_index); + sprintf(buf1,Text_String(TXT_RECONNECTING_TO), + MPath->Connection_Name(id)); + } + else { + sprintf(buf1,Text_String(TXT_WAITING_FOR_CONNECTIONS)); + } +#endif // MPATH + } + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + buf3 = Text_String(TXT_PRESS_ESC); + + w = max(String_Pixel_Width(buf1),String_Pixel_Width(buf2)); + +#ifdef FIXIT_VERSION_3 + char szNewCancelMessage[ 300 ]; + sprintf( szNewCancelMessage, "%s%s", buf3, TXT_WOL_CANCELMEANSFORFEIT ); + + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + { + w = max(String_Pixel_Width(szNewCancelMessage), w); // * RESFACTOR; why was it ever multiplied by this!!!? + w += (d_margin * 12); + } + else + { + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); + } +#else + w = max(String_Pixel_Width(buf3), w) * RESFACTOR; + w += (d_margin * 5); +#endif + + h = (d_txt6_h * 3) + (d_margin * 6); + x = 160*RESFACTOR - (w / 2); + y = 100*RESFACTOR - (h / 2); + + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, w, h); + + Fancy_Text_Print (buf1, 160*RESFACTOR, y + (d_margin * 2), scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + Fancy_Text_Print (szNewCancelMessage, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print (buf3, 160*RESFACTOR, y + (d_margin * 2) + (d_txt6_h + d_margin) * 2, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + + Show_Mouse(); + } + //------------------------------------------------------------------------ + // Just update the timeout value on the dialog + //------------------------------------------------------------------------ + else { + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + + sprintf(buf2,Text_String(TXT_TIME_ALLOWED), timeval + 1); + + int fillx = 160*RESFACTOR - (String_Pixel_Width (buf2) / 2) -6; + LogicPage->Fill_Rect (fillx, y + (d_margin * 2) + d_txt6_h + d_margin, + fillx + String_Pixel_Width (buf2) + 12, + y + (d_margin * 2) + d_txt6_h + d_margin + d_txt6_h +1*RESFACTOR, + BLACK); + + Fancy_Text_Print (buf2, 160*RESFACTOR, y + (d_margin * 2) + d_txt6_h + d_margin, scheme, BLACK, TPF_CENTER | TPF_TEXT); + + Show_Mouse(); + } +#endif +} /* end of Net_Reconnect_Dialog */ + +#define MAX_CHAT_NAME 12 +#define MAX_CHAT_PHRASE 45 + +struct WWPerson { + char Name[MAX_CHAT_NAME]; + char Phrase[MAX_CHAT_PHRASE]; + PlayerColorType Color; + unsigned long LastTime; +}; + +struct WWPerson WWPersons[] = { + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,105,115,110,39,116,32,116,104,105,115,32,99,111,111,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,82,0,0,0,0,0,0,}, + {72,101,121,44,32,119,97,110,116,32,115,111,109,101,32,115,101,97,102,111,111,100,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {71,114,101,97,116,46,32,74,117,115,116,32,103,114,101,97,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,97,114,114,121,32,71,0,0,0,0,0,}, + {87,111,110,100,101,114,102,117,108,46,32,80,101,114,102,101,99,116,44,32,105,110,32,102,97,99,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {89,111,117,32,99,97,108,108,32,116,104,105,115,32,65,73,63,32,32,83,104,101,101,115,104,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,108,108,32,80,0,0,0,0,0,0,}, + {66,105,108,108,115,32,114,117,108,101,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {84,97,107,101,32,116,104,105,115,32,111,117,116,44,32,110,111,119,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {87,104,121,32,99,97,110,39,116,32,121,111,117,32,102,105,116,32,97,110,121,32,109,111,114,101,32,108,101,116,116,101,114,115,32,111,110,32,116,104,101,32,108,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,73,32,119,97,110,116,32,116,111,32,116,97,108,107,32,116,111,32,121,111,117,32,97,98,111,117,116,32,116,104,105,115,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,101,116,116,32,83,0,0,0,0,0,}, + {69,100,44,32,99,111,109,101,32,116,111,32,109,121,32,111,102,102,105,99,101,44,32,110,111,119,33,32,32,91,99,114,97,99,107,33,93,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,115,116,97,112,108,101,114,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,116,104,101,115,97,117,114,117,115,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,101,110,105,110,97,32,70,0,0,0,0,}, + {72,97,115,32,97,110,121,111,110,101,32,115,101,101,110,32,109,121,32,112,101,110,99,105,108,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {72,101,121,32,83,116,101,118,101,44,32,99,97,110,32,119,101,32,109,101,101,116,32,105,110,32,121,111,117,114,32,111,102,102,105,99,101,63,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,108,111,32,66,0,0,0,0,0,0,}, + {83,116,101,118,101,44,32,105,116,39,108,108,32,111,110,108,121,32,116,97,107,101,32,97,32,109,105,110,117,116,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,111,98,32,83,0,0,0,0,0,0,0,}, + {77,105,108,111,44,32,100,105,97,108,32,50,52,57,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,119,105,116,104,32,97,110,32,69,45,76,45,83,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,115,98,97,114,102,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,108,115,98,101,116,104,32,87,0,0,0,}, + {84,104,97,116,39,115,32,39,69,108,115,98,101,116,104,39,44,32,110,111,116,32,39,69,108,102,98,117,116,116,39,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,115,111,111,111,32,119,101,105,114,100,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,97,114,101,110,32,71,0,0,0,0,0,}, + {68,117,104,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {72,101,121,44,32,105,116,32,119,111,114,107,101,100,32,111,110,32,109,121,32,99,111,109,112,117,116,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,104,105,108,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,32,105,115,32,99,108,101,97,114,108,121,32,97,32,119,97,115,116,101,32,111,102,32,109,101,109,111,114,121,46,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {72,109,109,44,32,73,32,115,101,101,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,66,0,0,0,0,0,0,0,}, + {65,104,104,44,32,121,101,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {78,111,116,32,97,110,111,116,104,101,114,32,105,110,115,116,97,108,108,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,114,105,97,32,68,77,77,76,0,0,}, + {72,65,32,72,65,32,72,65,32,72,65,32,72,65,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,114,101,97,108,108,121,32,99,111,111,108,33,32,32,71,111,115,104,32,103,117,121,115,33,32,32,87,111,119,33,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,0,0,0,0,0,0,}, + {87,111,119,33,32,32,89,111,117,32,103,117,121,115,32,97,114,101,32,116,104,101,32,103,114,101,97,116,101,115,116,33,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {71,114,97,121,102,105,115,104,32,102,111,114,32,108,117,110,99,104,32,97,103,97,105,110,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,71,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,108,97,109,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {84,104,105,115,32,116,104,105,110,103,39,115,32,98,117,103,103,101,100,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,108,101,110,110,32,83,0,0,0,0,0,}, + {83,104,105,112,32,105,116,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,87,0,0,0,0,0,}, + {79,75,32,101,118,101,114,121,111,110,101,44,32,111,117,116,32,111,102,32,109,121,32,111,102,102,105,99,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,99,104,97,116,32,112,114,111,103,114,97,109,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,100,32,68,0,0,0,0,0,0,0,0,}, + {65,32,103,111,111,100,32,110,105,110,106,97,32,105,115,32,108,105,107,101,32,97,32,110,105,110,106,97,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {73,32,119,101,97,114,32,100,101,115,105,103,110,101,114,32,106,101,97,110,115,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,107,32,89,0,0,0,0,0,0,}, + {72,101,121,32,66,105,108,108,44,32,116,104,105,115,32,116,104,105,110,103,32,107,101,101,112,115,32,99,114,97,25,26,24,104,105,110,103,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,98,101,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,70,0,0,0,0,0,0,}, + {84,104,105,115,32,105,115,32,97,98,115,111,108,117,116,101,108,121,32,116,104,101,32,119,111,114,115,116,32,101,118,101,114,33,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,78,0,0,0,0,0,0,}, + {83,111,117,110,100,115,32,108,105,107,101,32,97,32,100,114,105,118,101,114,32,112,114,111,98,108,101,109,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,99,111,116,116,32,66,0,0,0,0,0,}, + {73,32,110,101,101,100,32,116,104,105,115,32,102,111,114,32,76,79,76,50,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {83,116,101,118,101,32,84,0,0,0,0,0,}, + {84,104,105,115,32,115,101,101,109,115,32,114,97,116,104,101,114,32,115,105,108,108,121,44,32,97,99,116,117,97,108,108,121,46,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,117,108,32,77,0,0,0,0,0,0,}, + {78,111,32,46,46,46,46,32,32,87,32,65,32,89,33,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {82,105,99,107,32,80,0,0,0,0,0,0,}, + {65,72,32,72,65,32,72,65,32,72,65,33,33,32,32,72,65,72,65,33,33,32,32,65,72,72,32,72,65,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {83,116,97,108,105,110,32,107,105,108,108,101,100,32,109,121,32,102,97,116,104,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,101,32,75,0,0,0,0,0,0,0,}, + {73,32,104,111,112,101,32,121,111,117,39,114,101,32,112,114,111,117,100,32,111,102,32,121,111,117,114,115,101,108,102,44,32,66,105,108,108,46,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,105,103,32,87,105,108,108,121,0,0,0,}, + {65,110,121,98,111,100,121,32,102,101,114,32,97,32,115,112,105,116,116,105,110,39,32,99,111,110,116,101,115,116,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {69,114,105,99,32,87,0,0,0,0,0,0,}, + {75,97,114,97,116,101,32,105,115,32,103,111,111,100,32,98,117,116,32,121,111,117,32,117,115,101,32,105,116,32,102,111,114,32,101,118,105,108,33,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {84,101,100,32,77,0,0,0,0,0,0,0,}, + {72,97,46,32,32,70,117,110,110,121,46,32,32,84,104,105,115,32,105,115,32,102,117,110,110,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,97,109,111,110,32,82,0,0,0,0,0,}, + {73,32,98,108,97,109,101,32,116,104,101,32,70,114,101,110,99,104,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,119,105,103,104,116,32,79,0,0,0,0,}, + {79,104,44,32,109,97,110,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {73,111,110,32,99,97,110,110,111,110,32,114,101,97,100,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,105,97,32,72,0,0,0,0,0,0,0,}, + {83,101,108,101,99,116,32,116,97,114,103,101,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {66,114,117,99,101,32,74,0,0,0,0,0,}, + {73,32,97,109,32,116,104,101,32,71,101,110,105,101,32,111,102,32,116,104,101,32,108,97,109,112,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {76,97,117,114,97,32,87,0,0,0,0,0,}, + {71,111,32,97,119,97,121,44,32,66,105,108,108,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,97,118,105,100,32,68,0,0,0,0,0,}, + {72,109,109,109,46,46,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,105,107,101,32,76,105,0,0,0,0,0,}, + {67,97,108,108,32,109,101,32,78,97,116,101,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {68,101,110,122,105,108,32,76,0,0,0,0,}, + {84,104,105,115,32,119,111,117,108,100,32,98,101,32,98,101,116,116,101,114,32,111,110,32,116,104,101,32,77,97,99,46,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {71,114,101,103,32,72,0,0,0,0,0,0,}, + {66,117,116,32,100,111,101,115,32,105,116,32,102,105,116,32,105,110,116,111,32,50,32,77,66,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,110,97,116,104,97,110,32,76,0,0,}, + {73,32,116,104,105,110,107,32,73,32,110,101,101,100,32,97,32,104,97,105,114,99,117,116,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {65,100,97,109,32,73,0,0,0,0,0,0,}, + {87,104,97,116,101,118,101,114,46,32,32,66,101,101,102,33,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {75,117,114,116,32,79,0,0,0,0,0,0,}, + {70,105,120,32,105,116,32,121,111,117,114,115,101,108,102,46,32,32,73,39,109,32,98,117,115,121,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,111,115,101,112,104,32,72,0,0,0,0,}, + {60,73,99,121,32,103,108,97,114,101,62,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {77,97,116,116,32,72,0,0,0,0,0,0,}, + {73,39,109,32,110,101,118,101,114,32,103,111,111,100,32,119,105,116,104,32,99,108,101,118,101,114,32,111,110,32,116,104,101,32,115,112,111,116,46,46,46,0,0,0,0,},PCOLOR_GREEN,0}, + { {67,104,114,105,115,32,82,0,0,0,0,0,}, + {73,32,108,111,118,101,32,109,121,32,106,111,98,46,32,32,89,101,97,104,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {80,97,116,32,67,0,0,0,0,0,0,0,}, + {73,39,109,32,97,32,112,114,111,102,101,115,115,105,111,110,97,108,32,121,111,100,101,108,108,101,114,46,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {73,97,110,32,76,0,0,0,0,0,0,0,}, + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, + { {74,97,99,107,32,77,0,0,0,0,0,0,}, + {73,32,108,105,118,101,32,97,98,111,117,116,32,97,32,98,108,111,99,107,32,102,114,111,109,32,116,104,101,32,112,114,111,112,111,115,101,100,32,115,105,116,101,46,0,},PCOLOR_GREEN,0}, + { {74,101,102,102,32,66,0,0,0,0,0,0,}, + {84,104,101,121,32,97,108,108,32,108,111,111,107,32,108,105,107,101,32,97,110,116,115,32,102,114,111,109,32,117,112,32,104,101,114,101,46,0,0,0,0,0,0,0,0,},PCOLOR_GREEN,0}, +}; + +CDTimerClass wwperson_timer; + +void Start_WWChat(ColorListClass *playerlist) +{ + char *item; + int i; + HousesType house; + + //------------------------------------------------------------------------ + // Ensure a different sequence each time + //------------------------------------------------------------------------ + Scen.RandomNumber = rand(); + + //------------------------------------------------------------------------ + // Add myself to the player list + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist->Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + //------------------------------------------------------------------------ + // Add everyone else to the list + //------------------------------------------------------------------------ + for (i = 0; i < sizeof(WWPersons) / sizeof(struct WWPerson); i++) { + //..................................................................... + // Add the 1st entry to the list no matter what; for entries after the + // 1st, only add the name if it's different from the previous name. + //..................................................................... + if (i==0 || strcmp(WWPersons[i].Name, WWPersons[i-1].Name)) { + WWPersons[i].Color = (PlayerColorType)(Random_Pick(0,(int)(PCOLOR_LAST - 1))); + if (Percent_Chance(50)) { + house = HOUSE_GREECE; + } else { + house = HOUSE_USSR; + } +// house = (HousesType)Random_Pick((int)HOUSE_GOOD,(int)HOUSE_BAD); + item = new char [MPLAYER_NAME_MAX + 64]; + if (house != HOUSE_USSR && house != HOUSE_UKRAINE) { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s", WWPersons[i].Name, Text_String(TXT_SOVIET)); + } + playerlist->Add_Item(item, + (WWPersons[i].Color == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[WWPersons[i].Color]); + } + //..................................................................... + // If this entry's name is the same as the previous, copy the color + // value from the previous entry. + //..................................................................... + else if (i > 0) { + WWPersons[i].Color = WWPersons[i-1].Color; + } + + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + +} // end of Start_WWChat + + +int Update_WWChat(void) +{ + int i; + int j; + + //------------------------------------------------------------------------ + // Do nothing if it's too soon. + //------------------------------------------------------------------------ + if (wwperson_timer > 0) { + return(0); + } + + //wwperson_timer = 240; + wwperson_timer = Random_Pick(90,360); + + //------------------------------------------------------------------------ + // Even after the timer's gone off, there's only a 1/8 chance we'll + // print a message. + //------------------------------------------------------------------------ + if (Percent_Chance(12)) { + return(0); + } + + //------------------------------------------------------------------------ + // Randomly select a message to "send"; ensure we pick a message that + // hasn't been recently displayed. + //------------------------------------------------------------------------ + j = sizeof(WWPersons) / sizeof(struct WWPerson); + i = Random_Pick (0, j - 1); + if ((TickCount - WWPersons[i].LastTime) < 1800 && + WWPersons[i].LastTime != 0) { + return(0); + } + + Session.Messages.Add_Message (WWPersons[i].Name, + 0, + WWPersons[i].Phrase, WWPersons[i].Color, + TPF_TEXT, -1); + WWPersons[i].LastTime = TickCount; + + return (1); + +} // end of Update_WWChat + + +#if(0) +/*****************************************************************************/ +void Start_Logging(void) +{ + FILE *fp; + static char *ColorNames[6] = { + "Yellow", + "Red", + "BlueGreen", + "Orange", + "Green", + "Blue", + }; + int i; + int id; + HousesType house; + char *housenames[] = { + "ALLIES", + "SOVIET", + "Neutral", + "Special", + "Multi1", + "Multi2", + "Multi3", + "Multi4", + }; + time_t t; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + /* + ** Print game header + */ + t = time(NULL); + fprintf (fp,"==============================================================\n"); + fprintf (fp,"Date: %s",ctime(&t)); + fprintf (fp,"Scenario: %s\n", + Session.Scenarios[Session.Options.ScenarioIndex].Description()); + fprintf (fp,"Total # players: %d\n",Session.NumPlayers); + fprintf (fp,"==============================================================\n"); + + /* + ** Print connection info + */ + + /* + ** Print player data + */ + fprintf(fp,"Local Player:\n"); + fprintf (fp," Name:%s\n", Session.Handle); + fprintf (fp," Color:%s\n", ColorNames[Session.ColorIdx]); + fprintf (fp," House:%s\n", housenames[Session.House]); + fprintf (fp,"\n"); + + for (i = 0; i < Ipx.Num_Connections(); i++) { + id = Ipx.Connection_ID(i); + house = (HouseClass)id; + fprintf(fp,"Connection %d:\n",i); + fprintf (fp," Name:%s\n", Ipx.Connection_Name(id)); + fprintf (fp," Color:%s\n", + ColorNames[HouseClass::As_Pointer(house)->RemapColor); + fprintf (fp," House:%s\n", housenames[house]); + fprintf (fp,"\n"); + } + + /* + ** Print game options + */ + fprintf(fp,"Game Options:\n"); + fprintf(fp," Bases:%d\n",Session.Options.Bases); + fprintf(fp," Credits:%d\n",Session.Options.Credits); + fprintf(fp," Tiberium:%d\n",Session.Options.Tiberium); + fprintf(fp," Crates:%d\n",Session.Options.Goodies); + fprintf(fp," AI Players:%d\n",Session.Options.AIPlayers); + fprintf(fp," Build Level:%d\n",BuildLevel); + fprintf(fp," Unit Count:%d\n",Session.Options.UnitCount); + fprintf(fp," Seed:%d\n",Seed); + fprintf (fp,"\n"); + + fclose(fp); + +} /* end of Start_Logging */ + + +void Log_Message(char *msg) +{ + FILE *fp; + + fp = fopen("NETPLAY.LOG","at"); + if (!fp) + return; + + fprintf(fp,"%s (Frame:%d)\n",msg,Frame); + fclose(fp); + flushall(); + +} /* end of Log_Message */ +#endif + + + + + +#ifndef WOLAPI_INTEGRATION // Rest of file ifdeffed out. + + + + + +extern bool Spawn_WChat(bool can_launch); + +/*********************************************************************************************** + * Net_Fake_New_Dialog -- lets user start a new game * + * * + * This dialog shows a list of who's requesting to join this game, and lets * + * the game initiator selectively approve each user. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ New Network Game ³ * + * ³ ³ * + * ³ Players Scenario ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³ Boffo ³.³ ³ Green Acres ³.³ ³ * + * ³ ³ Bozo ÃÄ´ ³ Brown Sewers ÃÄ´ ³ * + * ³ ³ Bonzo ³ ³ ³ ... ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ [Reject] Count:--- ## ³ * + * ³ Level:--- ## ³ * + * ³ ³ * + * ³ Credits: _____ ³ * + * ³ [ Bases ] [ Crates ] ³ * + * ³ [ Tiberium ] [ AI Players ] ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * Session.GameName must contain this player's name. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_New_Dialog(void) +{ +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320 *RESFACTOR- d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 *RESFACTOR- d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5*RESFACTOR; // margin width/height + int d_margin2 = 2*RESFACTOR; // margin width/height + + int d_playerlist_w = 118*RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3)*RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = d_dialog_y + d_margin1 + d_txt6_h; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_PLAYERLIST = 100, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int transmit; // 1 = re-transmit new game options + + long ok_timer = 0; // for timing OK button + int rc; + int i; + char *item; + int tabs[] = {77}; // tabs for player list box + int optiontabs[] = {8}; // tabs for option list box + + NodeNameType *who; // node to add to Players + long ping_timer = 0; // for sending Ping packets + + int color_used[MAX_MPLAYER_COLORS]; // 1 = color has been used + JoinEventType whahoppa; // event generated by received packets + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int width; + int height; + + bool player_joined = false; + CountDownTimerClass join_timer; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + ColorListClass playerlist(BUTTON_PLAYERLIST, + d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + commands = &playerlist; + cancelbtn.Add_Tail(*commands); + playerlist.Set_Tabs(tabs); + + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Rule.IsTGrowth = Special.IsTGrowth; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Special.IsTSpread; + transmit = 0; + + //------------------------------------------------------------------------ + // Init player color-used flags + //------------------------------------------------------------------------ + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + color_used[i] = 0; // init all colors to available + } + color_used[Session.ColorIdx] = 1; // set my color to used + playerlist.Set_Selected_Style(ColorListClass::SELECT_BAR, scheme); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ +// randomize(); + Seed = rand(); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + //------------------------------------------------------------------------ + // Clear the list of players + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + //------------------------------------------------------------------------ + // Add myself to the list, and to the Players vector. + //------------------------------------------------------------------------ + item = new char [MPLAYER_NAME_MAX + 64]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_ALLIES)); + } + else { + sprintf(item,"%s\t%s",Session.Handle, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", Session.Handle, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + who = new NodeNameType; + strcpy(who->Name, Session.Handle); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //............................................................... + // Broadcast my sign-off over my network + //............................................................... + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................... + // And now, just be absolutely sure, send my sign-off to each + // player in my game. (If there's a bridge between us, the other + // player will have specified my address, so he can cross the + // bridge; but I may not have specified a bridge address, so the + // only way I have of crossing the bridge is to send a packet + // directly to him.) + // Don't send this message to myself. + //............................................................... + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, &(Session.Players[i]->Address)); + Ipx.Service(); + } + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + Session.GameName[0] = 0; + process = false; + rc = false; + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + break; + + //.................................................................. + // Default: manage the inter-player messages + //.................................................................. + default: + if (Session.Players.Count() > 1) { + + /* + ** Wait for several secs after receiving request to join before sending + ** start game packet + */ + if (!player_joined) { + player_joined = true; + join_timer.Set (3*60, true); + break; + } else { + if (join_timer.Time()) break; + } + + //............................................................... + // If a new player has joined in the last second, don't allow + // an OK; force a wait longer than 1 second (to give all players + // a chance to know about this new guy) + //............................................................... + i = MAX((int)Ipx.Global_Response_Time() * 2, 60*2); + while ((int)TickCount - ok_timer < i) { + Ipx.Service(); + } + + //............................................................... + // If there are at least 2 players, go ahead & play; error otherwise + //............................................................... + if (Session.Solo || Session.Players.Count() > 1 || Session.Options.Ghosts) { + rc = TRUE; + process = FALSE; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + Sound_Effect(VOC_SYS_ERROR); + } + //if (input==(BUTTON_LOAD | KN_BUTTON)) + // load_game = 1; + break; + } + break; + + } + + //..................................................................... + // Process incoming packets + //..................................................................... + whahoppa = Get_NewGame_Responses(&playerlist, color_used); + if (whahoppa == EV_NEW_PLAYER) { + ok_timer = TickCount; + transmit = 1; + } + else if (whahoppa == EV_MESSAGE) { + display = REDRAW_MESSAGE; + } + + //..................................................................... + // If our Transmit flag is set, we need to send out a game option packet + // Don't send it to myself. + //..................................................................... + if (transmit) { + for (i = 1; i < Session.Players.Count(); i++) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_GAME_OPTIONS; + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (Session.GPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + // ajw - I don't understand why this check is done here and not later. + if ( !file.Is_Available() ) { + + /* + ** Special new kludge for counterstrike. + ** + ** The only time the file can be unavailable is if its a counterstrike + ** mission and the CS CD is not in the drive so + ** make sure the counterstrike CD is in the drive. + ** + ** If Counterstrike is installed and + ** the file name matches a counterstrike map then force the CD + ** to be there. + ** + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if ( Expansion_CS_Present() || Expansion_AM_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName) && index!=2 && index!=3) { + needcd = true; + RequiredCD = 2; + } + if (Is_Mission_Aftermath(Session.ScenarioFileName) && index!=3) { + needcd = true; + RequiredCD = 3; + } + if (needcd) { +#else + if ( Expansion_CS_Present() ) { + if ( toupper (Session.ScenarioFileName [2]) == 'M' ){ + int scen_num; + sscanf ( Session.ScenarioFileName, "SCM%02d", &scen_num ); + + int current_drive = CCFileClass::Get_CD_Drive(); + if ( scen_num>24 && Get_CD_Index(current_drive, 1*60) != 2){ + RequiredCD = 2; +#endif + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + } + } + } + + /* + ** See if that file is available now. Its fatal if it isnt. + */ + if ( !file.Is_Available() ) { + + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, Session.Handle); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + //------------------------------------------------------------------------ + // Clear the Players vector if we're not starting a game. + //------------------------------------------------------------------------ + Clear_Vector(&Session.Players); + return (false); + } + } + + + Session.GPacket.ScenarioInfo.FileLength = file.Size(); +#ifdef WOLAPI_INTEGRATION + strcpy( Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (Session.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(Session.GPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)Session.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof (Session.GPacket.ScenarioInfo.FileDigest)); + Session.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + + Session.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + Session.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + Session.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + Session.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + //Session.GPacket.ScenarioInfo.IsGhosties = Session.Options.Ghosts; + Session.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + Session.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + Session.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + Session.GPacket.ScenarioInfo.Seed = Seed; + Session.GPacket.ScenarioInfo.Special = Special; + Session.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + Session.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + Sound_Effect(VOC_OPTIONS_CHANGED); + transmit = 0; + } + + //..................................................................... + // Ping every player in my game, to force the Global Channel to measure + // the connection response time. Don't ping myself (index 0). + //..................................................................... + if (TickCount - ping_timer > 15) { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_PING; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + ping_timer = TickCount; + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + } + + //------------------------------------------------------------------------ + // Prepare to load the scenario + //------------------------------------------------------------------------ + if (rc) { + //..................................................................... + // Set the player count & scenario number + //..................................................................... + Session.NumPlayers = Session.Players.Count(); + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + //..................................................................... + // Compute frame delay value for packet transmissions: + // - Divide global channel's response time by 8 (2 to convert to 1-way + // value, 4 more to convert from ticks to frames) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = MAX( ((((Ipx.Global_Response_Time() / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + else { + Session.MaxAhead = MAX( ((int)Ipx.Global_Response_Time() / 8), + NETWORK_MIN_MAX_AHEAD ); + } + + //..................................................................... + // Send all players the NET_GO packet. Wait until all ACK's have been + // received. + //..................................................................... + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + if (load_game) + Session.GPacket.Command = NET_LOADGAME; + else + Session.GPacket.Command = NET_GO; + Session.GPacket.ResponseTime.OneWay = Session.MaxAhead; + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &(Session.Players[i]->Address) ); + } + //..................................................................... + // Wait for all the ACK's to come in. + //..................................................................... + while (Ipx.Global_Num_Send() > 0) { + Ipx.Service(); + } + + + /* + ** Wait for the go responses from each player in case someone needs the scenario + ** file to be sent. + */ + int responses[20]; //In big trub if more than 20 players + memset (responses, 0, sizeof (responses)); + int num_responses = 0; + bool send_scenario = false; + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); + + CDTimerClass response_timer; // timeout timer for waiting for responses + response_timer = 60*30; // Wait for 30 seconds. If we dont hear by then assume someone crashed + + do { + Ipx.Service(); + int retcode = Ipx.Get_Global_Message (&Session.GPacket, &Session.GPacketlen, + &Session.GAddress, &Session.GProductID); + if (retcode && Session.GProductID == IPXGlobalConnClass::COMMAND_AND_CONQUER0) { + + for ( i=1 ; iAddress == Session.GAddress) { + if (!responses[i]) { + if (Session.GPacket.Command == NET_REQ_SCENARIO) { + responses[i] = Session.GPacket.Command; + send_scenario = true; + num_responses++; + } + if (Session.GPacket.Command == NET_READY_TO_GO) { + responses[i] = Session.GPacket.Command; + num_responses++; + } + } + } + } + } + } while ( num_responses < Session.Players.Count()-1 && response_timer ); + + WWDebugString ("RA95 - Exited response wait loop.\n"); + + /* + ** If one of the machines requested that the scenario be sent then send it. + */ + if (send_scenario) { + memset (Session.ScenarioRequests, 0, sizeof (Session.ScenarioRequests)); + Session.RequestCount = 0; + for ( i=1 ; i0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 +} /* end of Net_Fake_New_Dialog */ + + + + + + + + + + +/*********************************************************************************************** + * Net_Join_Dialog -- lets user join an existing game or start a new one * + * * + * This dialog displays an edit field for the player's name, and a list of all non-stealth- * + * mode games. Clicking once on a game name displays a list of who's in that game. Clicking * + * "New" takes the user to the Net_New dialog, where he waits for other users to join his * + * game. All other input is done through this dialog. * + * * + * The dialog has several "states": * + * * + * 1) Initially, it waits for the user to fill in his/her name and then to select Join or * + * New; if New is selected, this dialog is exited. * + * * + * 2) If Join is selected, the Join & New buttons are removed, but the Cancel button remains.* + * The join request is transmitted to the game's owner, and the message "Waiting for * + * Confirmation" is displayed, until a confirmation or denial is received from the game's * + * owner. The user may click Cancel at this point to cancel the join request. * + * (Once Join is selected, the name editing field is disabled, and becomes a display-only * + * field. If cancel is selected, it reappears as an edit field.) The user can still click* + * around & see who's in which games. * + * * + * 3) If the join request is denied, the dialog re-initializes to its pre-join state; the * + * Join & New buttons reappear, & the Name field is available again. * + * * + * 4) If join confirmation is obtained, the message changes to show all the current game * + * settings. The user cannot click around & look at other games any more. * + * * + * Any game running in Stealth mode won't show up on this dialog. * + * * + * The 'Players' vector matches one-for-one the contents of the list box. The local system * + * is always listed first; the IPX Address of the local system will NOT be valid in the * + * Players vector. The Games & Players vectors should be left filled in even after this * + * routine is exited; their contents are used to form connections to the other players, * + * after the scenario has been loaded. * + * * + * The 'Chat' vector contains the address of everyone who sends me a chat announcement. * + * The address field is used to send everyone my outgoing messages. The LastTime * + * field is used as a timeout; if enough time goes by & we don't hear from this node, * + * we ping him, requesting a CHAT_ANNOUNCE if he's still in chat. If we don't hear * + * from him after that, we remove him from our list. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Network Games ³ * + * ³ ³ * + * ³ Your Name: ____________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Games Players ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄ¿ ³ * + * ³ ³(Bill's Game )³.³ ³ Peter Parker GDI ³.³ ³ * + * ³ ³ Peter Parker's Game ÃÄ´ ³ Mary Jane GDI ÃÄ´ ³ * + * ³ ³(Magnum PI's Game )³ ³ ³ JJ Jameson NOD ³ ³ ³ * + * ³ ³ ÃÄ´ ³ ÃÄ´ ³ * + * ³ ³ ³.³ ³ ³.³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÙ ³ * + * ³ Scenario: Big Long Description ³ * + * ³ Starting Credits: xxxx ³ * + * ³ Count: --- Level: --- ³ * + * ³ Bases: ON Crates: ON ³ * + * ³ Tiberium: ON AI Players: ON ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Join] [Cancel] [New] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * -1 = cancel, 0 = OK, 1 = New net game requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +static int Net_Fake_Join_Dialog(void) +{ + +#ifdef WIN32 + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 120 *RESFACTOR; // dialog width + int d_dialog_h = 80 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_gamelist_w = 160 *RESFACTOR; + int d_gamelist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high +// int d_gamelist_x = d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_x = 500*RESFACTOR; //d_dialog_x + d_margin1 + d_margin1; + int d_gamelist_y = 50 + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + + int d_playerlist_w = 118 *RESFACTOR; + int d_playerlist_h = ((6 * 6) + 3) *RESFACTOR; // 6 rows high + //int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w; + int d_playerlist_x = 500*RESFACTOR; + int d_playerlist_y = 50+ d_margin2 + 2*RESFACTOR/*KO + d_txt6_h + d_margin2*/; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_GAMELIST = 100, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char housetext[25] = ""; // buffer for house droplist + int isdropped = 0; + + JoinStateType joinstate = JOIN_NOTHING; // current "state" of this dialog + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int playertabs[] = {77 *RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + int game_index = -1; // index of currently-selected game + int join_index = -1; // index of game we're joining + int rc = 0; // -1 = user cancelled, 1 = New + JoinEventType event; // event from incoming packet + int i; // loop counter + int parms_received = 0; // 1 = game options received + NodeNameType *who; // node to add to Players + RejectType why; // reason for rejection + TTimerClass lastclick_timer; // time b/w send periods + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + int ready_to_go = 0; + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + int width; + int height; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String((char*)TXT_HACKHACK, SeenBuff.Get_Height(), width, height); + char * item; + unsigned long starttime; + int load_game = 0; // 1 = load saved game + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, +//#if (GERMAN | FRENCH) +// d_cancel_x, d_cancel_y); +//#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +//#endif + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + lastclick_timer = 0; + + //------------------------------------------------------------------------ + // Clear the list of games, players, and the chat list + //------------------------------------------------------------------------ + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + Clear_Vector(&Session.Chat); + + //------------------------------------------------------------------------ + // Add myself to the Chat vector + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Chat.LastTime = 0; + who->Chat.LastChance = 0; + who->Chat.Color = Session.GPacket.Chat.Color; + Session.Chat.Add (who); + + //------------------------------------------------------------------------ + // Create the "Lobby" game name on the games list, and create a bogus + // node for the gamelist, so Games[i] will always match gamelist[i] + //------------------------------------------------------------------------ + who = new NodeNameType; + strcpy(who->Name, ""); + who->Game.IsOpen = 0; + who->Game.LastTime = 0; + Session.Games.Add (who); + item = new char [MPLAYER_NAME_MAX]; + strcpy(item, Text_String(TXT_LOBBY)); + gamelist.Add_Item(item); + gamelist.Set_Selected_Index(0); + game_index = 0; + + //------------------------------------------------------------------------ + // Send game-name query & chat announcement; also, initialize timers. + //------------------------------------------------------------------------ + Send_Join_Queries (game_index, joinstate, 1, 0, 1, namebuf, 1); + + /* + ** Process the message loop until we are in focus. + */ + CDTimerClass focus_timer; // Timer to allow a wait after client joins + focus_timer = 5*60; + + WWDebugString ("RA95 - About to enter wait for focus loop.\n"); + SetForegroundWindow ( MainWindow ); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + + if (!GameInFocus) { + do { + Keyboard->Check(); + if (!focus_timer){ + WWDebugString ("RA95 - Calling SetForgroundWindow.\n"); + SetForegroundWindow ( MainWindow ); + WWDebugString ("RA95 - Calling ShowWindow.\n"); + ShowWindow ( MainWindow, SW_SHOWMAXIMIZED ); + focus_timer = 5*60; + } + }while (!GameInFocus); + AllSurfaces.SurfacesRestored=FALSE; + } + + if (LogicPage != &SeenBuff && LogicPage!= &HidPage) { + Set_Logic_Page (SeenBuff); + } + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_HACKHACK, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + cancelbtn.Zap(); + gamelist.Zap(); + playerlist.Zap(); + + commands = &cancelbtn; + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + //............................................................... + // If we're joined to a game, make extra sure the other players in + // that game know I'm exiting; send my SIGN_OFF as an ack-required + // packet. Don't send this to myself (index 0). + //............................................................... + if (joinstate == JOIN_CONFIRMED) { + Unjoin_Game(namebuf, joinstate, &gamelist, &playerlist, + game_index, 1, 0, 0, d_txt6_h, 0, + 0, MAX_MESSAGE_LENGTH); + joinstate = JOIN_NOTHING; + display = REDRAW_ALL; + } + //............................................................... + // If I'm not joined to a game, send a SIGN_OFF to all players + // in my Chat vector (but not to myself, index 0) + //............................................................... + else { + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + Session.GPacket.Command = NET_SIGN_OFF; + strcpy(Session.GPacket.Name,namebuf); + for (i = 1; i < Session.Chat.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), + 1, &(Session.Chat[i]->Address)); + Ipx.Service(); + } + + //............................................................ + // Now broadcast a SIGN_OFF just to be thorough + //............................................................ + Ipx.Send_Global_Message (&Session.GPacket, + sizeof (GlobalPacketType), 0, NULL); + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + //............................................................ + // exit the dialog + //............................................................ + Send_Data_To_DDE_Server ("Hello", strlen("Hello"), DDEServerClass::DDE_CONNECTION_FAILED); + GameStatisticsPacketSent = false; + Spawn_WChat(false); + process = false; + rc = -1; + } + break; + + + //.................................................................. + // JOIN: send a join request packet & switch to waiting-for- + // confirmation mode. + //.................................................................. + default: + if (joinstate == JOIN_NOTHING && gamelist.Count() >1 ) { + gamelist.Set_Selected_Index(1); //lastclick_idx); + game_index = 1; + strcpy (Session.Handle,namebuf); + //Session.House = (HousesType)housebtn.Current_Index(); + join_index = gamelist.Current_Index(); + parms_received = 0; + if (Request_To_Join (namebuf, join_index, Session.House, + Session.ColorIdx)) { + joinstate = JOIN_WAIT_CONFIRM; + } else { + display = REDRAW_ALL; + } + } + break; + + } + + //..................................................................... + // Resend our query packets + //..................................................................... + Send_Join_Queries(game_index, joinstate, 0, 0, 0, namebuf); + + //..................................................................... + // Process incoming packets + //..................................................................... + event = Get_Join_Responses(&joinstate, &gamelist, &playerlist, + join_index, namebuf, &why); + + //..................................................................... + // If we've changed state, redraw everything; if we're starting the game, + // break out of the loop. If we've just joined, send out a player query + // so I'll get added to the list instantly. + //..................................................................... + if (event == EV_STATE_CHANGE) { + display = REDRAW_ALL; + if (joinstate==JOIN_GAME_START || joinstate == JOIN_GAME_START_LOAD) { + if (joinstate==JOIN_GAME_START_LOAD) load_game = 1; + + bool ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + CCFileClass testfile ( Session.ScenarioFileName ); + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** See if that scenario is available now. Its pretty fatal if it isnt. + */ + Session.Options.ScenarioIndex = -1; + for (i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } + } + } + } + + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if ( !ready_packet_was_sent ){ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided its not an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + Session.Options.ScenarioIndex = -1; + } else { +#endif + Session.Options.ScenarioIndex = 1; //We dont care what it + //is as long as it isnt -1 +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 1)) { + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&(Session.GPacket), 0, sizeof (Session.GPacket)); + Session.GPacket.Command = NET_READY_TO_GO; + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 1, &Session.HostAddress); + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0); + } + + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + + rc = 0; + process = false; + } + + //.................................................................. + // If we're newly-confirmed, add myself to the Players list, and + // immediately send out a player query + //.................................................................. + else if (joinstate==JOIN_CONFIRMED) { + //............................................................... + // Clear the player list, then add myself to the list. + //............................................................... + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + + item = new char [MPLAYER_NAME_MAX + 12]; +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Add_Item(item, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? &ColorRemaps[PCOLOR_REALLY_BLUE] : &ColorRemaps[Session.ColorIdx]); + + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + Session.Players.Add (who); + + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); +#if (0) + //............................................................... + // Re-init the message system to its new smaller size + //............................................................... + Session.Messages.Init (0,0, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, 0, 0, 1, + 20, MAX_MESSAGE_LENGTH - 5); + Session.Messages.Add_Edit(Session.ColorIdx, TPF_TEXT | TPF_BRIGHT_COLOR, NULL, '_'); +#endif //(0) + } + + //.................................................................. + // If we've been rejected, clear any messages we may have been + // typing, add a message stating why we were rejected, and send a + // chat announcement. + //.................................................................. + else if (joinstate==JOIN_REJECTED) { + Sound_Effect(VOC_SYS_ERROR); + Send_Join_Queries (game_index, joinstate, 0, 0, 1, namebuf); + + } + } // end of state change + + //..................................................................... + // If the game options have changed, print them. + //..................................................................... + else if (event == EV_GAME_OPTIONS) { + + Sound_Effect(VOC_OPTIONS_CHANGED); + + parms_received = 1; + display = REDRAW_PARMS; + } + + + //..................................................................... + // EV_GAME_SIGNOFF: + // A game before the one I've selected is gone, so we have a new index + // now. 'game_index' must be kept set to the currently-selected list + // item, so we send out queries for the currently-selected game. It's + // therefore imperative that we detect any changes to the game list. + // If we're joined in a game, we must decrement our game_index to keep + // it aligned with the game we're joined to. + //..................................................................... + else if (event == EV_GAME_SIGNOFF) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + + //..................................................................... + // Service the Ipx connections + //..................................................................... + Ipx.Service(); + + if (process) { + //..................................................................... + // Clean out the Game List; if an old entry is found: + // - Remove it + // - Clear the player list + // - Send queries for the new selected game, if there is one + //..................................................................... + for (i = 1; i < Session.Games.Count(); i++) { + if (TickCount - Session.Games[i]->Game.LastTime > 400) { + + delete Session.Games[i]; + Session.Games.Delete(Session.Games[i]); + + item = (char *)(gamelist.Get_Item (i)); + gamelist.Remove_Item (item); + delete [] item; + + if (i <= game_index) { + if (joinstate==JOIN_CONFIRMED) { + game_index--; + join_index--; + gamelist.Set_Selected_Index(join_index); + } + else { + gamelist.Flag_To_Redraw(); + Clear_Listbox(&playerlist); + Clear_Vector (&Session.Players); + game_index = gamelist.Current_Index(); + Send_Join_Queries (game_index, joinstate, 0, 1, 0, namebuf); + } + } + } + } + } + +#if (0) + /* + ** If we were flagged to start the game and we recognise both players then quit the loop + */ + if (ready_to_go && Session.Players.Count() == 2) { + rc = 0; + process = false; + } +#endif //(0) + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + + //------------------------------------------------------------------------ + // Establish connections with all other players. + //------------------------------------------------------------------------ + if (rc == 0) { + //..................................................................... + // If the other guys are playing a scenario I don't have (sniff), I can't + // play. Try to bail gracefully. + //..................................................................... + if (Session.Options.ScenarioIndex==-1) { + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + + memset (&Session.GPacket, 0, sizeof(GlobalPacketType)); + + Session.GPacket.Command = NET_SIGN_OFF; + strcpy (Session.GPacket.Name, namebuf); + + //.................................................................. + // Don't send myself the message. + //.................................................................. + for (i = 1; i < Session.Players.Count(); i++) { + Ipx.Send_Global_Message (&Session.GPacket, + sizeof(GlobalPacketType), 1, + &(Session.Players[i]->Address)); + Ipx.Service(); + } + + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + Ipx.Send_Global_Message (&Session.GPacket, sizeof (GlobalPacketType), + 0, NULL); + + if (Session.IsBridge) { + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + Ipx.Send_Global_Message (&Session.GPacket, sizeof(GlobalPacketType), + 0, &Session.BridgeNet); + } + + while (Ipx.Global_Num_Send() > 0 && Ipx.Service() != 0) ; + + rc = -1; + + } + //--------------------------------------------------------------------- + // Prepare to load the scenario. + //--------------------------------------------------------------------- + else { + //.................................................................. + // Set the number of players in this game, and the scenario number. + //.................................................................. + Session.NumPlayers = Session.Players.Count(); +///* +//** Hack to fake a scenario name as if it had been sent over the connection. +//*/ +//sprintf(Scen.ScenarioName, "SCM%02dEA.INI", Session.Options.ScenarioIndex+1); + +// Scen.Scenario = Session.Options.ScenarioIndex; + } + + //..................................................................... + // Wait a while, polling the IPX service routines, to give our ACK + // a chance to get to the other system. If he doesn't get our ACK, + // he'll be waiting the whole time we load MIX files. + //..................................................................... + i = MAX((int)Ipx.Global_Response_Time() * 2, 60*2); + starttime = TickCount; + while (TickCount - starttime < (unsigned)i) { + Ipx.Service(); + } + } + + //------------------------------------------------------------------------ + // Init network timing values, using previous response times as a measure + // of what our retry delta & timeout should be. + //------------------------------------------------------------------------ + //Ipx.Set_Timing (Ipx.Global_Response_Time() + 2, -1, + // Ipx.Global_Response_Time() * 4); + Ipx.Set_Timing (Ipx.Global_Response_Time () + 2, (unsigned long) -1, max (120, Ipx.Global_Response_Time () * 8)); + + //------------------------------------------------------------------------ + // Clear all lists, but NOT the Games & Players vectors. + //------------------------------------------------------------------------ + Clear_Listbox(&gamelist); + Clear_Listbox(&playerlist); + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + //------------------------------------------------------------------------ + // Load a game if the game owner told us to + //------------------------------------------------------------------------ + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = -1; + } + Frame++; + } + + //------------------------------------------------------------------------ + // Clear the Players & Games vectors if we're not joined to a game. + // Clear the Chat vector regardless. + //------------------------------------------------------------------------ + if (rc != 0) { + Clear_Vector(&Session.Games); + Clear_Vector(&Session.Players); + } + Clear_Vector(&Session.Chat); + + return(rc); + +#else //WIN32 + + return (0); + +#endif //WIN32 + +} /* end of Net_Join_Dialog */ + + + + + + + + + + +bool Server_Remote_Connect(void) +{ + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + if (!Net_Fake_New_Dialog()) { + Session.Write_MultiPlayer_Settings (); + return (false); + } + + Session.NetOpen = 0; + Session.NetStealth = stealth; + Session.Write_MultiPlayer_Settings (); + return (true); + +} /* end of Server_Remote_Connect */ + + + + + + + +bool Client_Remote_Connect(void) +{ + int rc; + int stealth; // original state of Session.NetStealth flag + + //------------------------------------------------------------------------ + // Init network timing parameters; these values should work for both a + // "real" network, and a simulated modem network (ie Kali) + //------------------------------------------------------------------------ + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + + //------------------------------------------------------------------------ + // Save the original value of the NetStealth flag, so we can turn stealth + // off for now (during this portion of the dialogs, we must show ourselves) + //------------------------------------------------------------------------ + stealth = Session.NetStealth; + Session.NetStealth = 0; + + //------------------------------------------------------------------------ + // The game is now "open" for joining. Close it as soon as we exit this + // routine. + //------------------------------------------------------------------------ + Session.NetOpen = 1; + + //------------------------------------------------------------------------ + // Read in last multiplayer game settings from the .INI file + //------------------------------------------------------------------------ + rc = Net_Fake_Join_Dialog(); + Session.Write_MultiPlayer_Settings (); + + Session.NetStealth = stealth; + Session.NetOpen = 0; + + if (rc == -1) { + return(false); + } else { + return(true); + } + +} /* end of Client_Remote_Connect */ + + + +/*************************** end of netdlg.cpp *****************************/ + +#endif //#ifndef WOLAPI_INTEGRATION +#endif \ No newline at end of file diff --git a/code/redalert/newblit.cpp b/code/redalert/newblit.cpp new file mode 100644 index 0000000..08610f9 --- /dev/null +++ b/code/redalert/newblit.cpp @@ -0,0 +1,62 @@ +// NEWBLIT.CPP +// + +#include +#include "FUNCTION.H" +#include "Image.h" + +extern byte backbuffer_palette[768]; +extern uint8_t g_ColorXlat[16]; + +void GL_SetClipRect(int x, int y, int width, int height) { + ImVec2 mi(x, y); + ImVec2 ma(x + width, y + height); + ImGui::GetForegroundDrawList()->PushClipRect(mi, ma); +} + +void GL_ResetClipRect(void) { + ImGui::GetForegroundDrawList()->PopClipRect(); +} + +void GL_RenderImage(Image_t* image, int x, int y, int width, int height, int colorRemap, int shapeId) { + ImVec2 mi(x, y); + ImVec2 ma(x + width, y + height); + + if (image->numFrames > 0) { + if(image->HouseImages[colorRemap].image[shapeId][((int)animFrameNum) % image->numFrames] == 0) + ImGui::GetForegroundDrawList()->AddImage((ImTextureID)image->HouseImages[colorRemap].image[shapeId][0], mi, ma); + else + ImGui::GetForegroundDrawList()->AddImage((ImTextureID)image->HouseImages[colorRemap].image[shapeId][((int)animFrameNum) % image->numFrames], mi, ma); + } + else { + ImGui::GetForegroundDrawList()->AddImage((ImTextureID)image->HouseImages[colorRemap].image[shapeId][0], mi, ma); + } + +} + +void GL_FillRect(int color, int x, int y, int width, int height) { + ImVec2 mi(x, y); + ImVec2 ma(x + width, y + height); + float r = backbuffer_palette[(color * 3) + 0] / 255.0f; + float g = backbuffer_palette[(color * 3) + 1] / 255.0f; + float b = backbuffer_palette[(color * 3) + 2] / 255.0f; + ImGui::GetForegroundDrawList()->AddRectFilled(mi, ma, ImGui::ColorConvertFloat4ToU32(ImVec4(r, g, b, 1))); +} + +void GL_DrawText(int color, int x, int y, char* text) { + ImVec2 pos(x, y); + color = g_ColorXlat[color % 15]; + float r = backbuffer_palette[(color * 3) + 0] / 255.0f; + float g = backbuffer_palette[(color * 3) + 1] / 255.0f; + float b = backbuffer_palette[(color * 3) + 2] / 255.0f; + ImGui::GetForegroundDrawList()->AddText(pos, ImGui::ColorConvertFloat4ToU32(ImVec4(r, g, b, 1)), text); +} + +void GL_DrawLine(int color, int x, int y, int dx, int dy) { + ImVec2 pos(x, y); + ImVec2 pos2(dx, dy); + float r = backbuffer_palette[(color * 3) + 0] / 255.0f; + float g = backbuffer_palette[(color * 3) + 1] / 255.0f; + float b = backbuffer_palette[(color * 3) + 2] / 255.0f; + ImGui::GetForegroundDrawList()->AddLine(pos, pos2, ImGui::ColorConvertFloat4ToU32(ImVec4(r, g, b, 1))); +} \ No newline at end of file diff --git a/code/redalert/newblit.h b/code/redalert/newblit.h new file mode 100644 index 0000000..36cab3a --- /dev/null +++ b/code/redalert/newblit.h @@ -0,0 +1,10 @@ +// NewBlit.h +// + +struct Image_t; +void GL_RenderImage(Image_t* image, int x, int y, int width, int height, int colorRemap = 0, int shapeId = 0); +void GL_DrawText(int color, int x, int y, char* text); +void GL_FillRect(int color, int x, int y, int width, int height); +void GL_DrawLine(int color, int x, int y, int dx, int dy); +void GL_ResetClipRect(void); +void GL_SetClipRect(int x, int y, int width, int height); \ No newline at end of file diff --git a/code/redalert/noseqcon.cpp b/code/redalert/noseqcon.cpp new file mode 100644 index 0000000..a414bbd --- /dev/null +++ b/code/redalert/noseqcon.cpp @@ -0,0 +1,688 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.cpv 1.10 01 Mar 1996 18:08:30 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * NonSequencedConnClass::Receive_Packet -- adds packet to receive queue * + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "NOSEQCON.H" + + +/*************************************************************************** + * NonSequencedConnClass::NonSequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::NonSequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommBufferClass (numsend, numreceive, MaxPacketLen); +} + + +/*************************************************************************** + * NonSequencedConnClass::~NonSequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NonSequencedConnClass::~NonSequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * NonSequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void NonSequencedConnClass::Init (void) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + LastSeqID = 0xffffffff; + LastReadID = 0xffffffff; + + Queue->Init(); +} + + +/*************************************************************************** + * NonSequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { +// Smart_Printf( "Packet ack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendAck++; + } else { +// Smart_Printf( "Packet noack Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + NumSendNoAck++; + } + return(true); + } else { +// Smart_Printf( "Packet not Queued ID %d \n", ((CommHeaderType *)PacketBuf)->PacketID ); + return(false); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to packet header + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to recv entry header + CommHeaderType *entry_data; // ptr to queue entry data + CommHeaderType ackpacket; // ACK packet to send + int i; + int save_packet = 1; // 0 = this is a resend + int found; + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber != MagicNum) { +// Smart_Printf( "Bad Magic Number\n" ); + return(false); + } + + /*------------------------------------------------------------------------ + Handle an incoming ACK + ------------------------------------------------------------------------*/ + if (packet->Code == PACKET_ACK) { + + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Get_Send(i); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry != NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Received ACK for %d \n", packet->PacketID ); + send_entry->IsACK = 1; + break; + } + } + } + +//{ +// if (i == Queue->Num_Send() ) { +// Smart_Printf( "Received bad ACK for %d \n", packet->PacketID ); +// } +//} + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_NOACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_NOACK) { + /*--------------------------------------------------------------------- + If there's only one slot left, don't tie up the queue with this packet + ---------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { +// Smart_Printf( "Only one slot left don't tie up with DATA NOACK packet %d \n", packet->PacketID ); + return(false); + } + + /*--------------------------------------------------------------------- + Error if we can't queue the packet + ---------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "Can't Queue the packet %d \n", packet->PacketID ); + return(false); + } + +// Smart_Printf( "Queued DATA NOACK for %d \n", packet->PacketID ); + NumRecNoAck++; + + return(true); + } + + /*------------------------------------------------------------------------ + Handle an incoming PACKET_DATA_ACK packet + ------------------------------------------------------------------------*/ + else if (packet->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Looking at ID %d, LastSeqID=%d \n", packet->PacketID, LastSeqID ); + /*.................................................................... + If this is a packet requires an ACK, and it's ID is older than our + "oldest" ID, we know it's a resend; send an ACK, but don't queue it + ....................................................................*/ + if (packet->PacketID <= LastSeqID && LastSeqID != 0xffffffff) { +// Smart_Printf( "Older than oldest\n" ); + save_packet = 0; + } + /*.................................................................... + Otherwise, scan the queue for this entry; if it's found, it's a + resend, so don't save it. + ....................................................................*/ + else { + save_packet = 1; + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + /*........................................................... + Packet is found; it's a resend + ...........................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == packet->PacketID) { +// Smart_Printf( "It's a resend\n" ); + save_packet = 0; + break; + } + } + } + } /* end of scan for resend */ + + /*--------------------------------------------------------------------- + Queue the packet & update our LastSeqID value. + ---------------------------------------------------------------------*/ + if (save_packet) { + /*------------------------------------------------------------------ + If there's only one slot left, make sure we only put a packet in it if + this packet will let us increment our LastSeqID; otherwise, we'll get + stuck, forever unable to increment LastSeqID. + ------------------------------------------------------------------*/ + if (Queue->Max_Receive() - Queue->Num_Receive() <= 1) { + if (packet->PacketID != (LastSeqID + 1) ) { +// Smart_Printf( "One slot left not what we looking for max=%d,num=%d \n", +// Queue->Max_Receive(), Queue->Num_Receive() ); + return(0); + } + } + + /*------------------------------------------------------------------ + If we can't queue the packet, return; don't send an ACK. + ------------------------------------------------------------------*/ + if (!Queue->Queue_Receive (buf, buflen)) { +// Smart_Printf( "unable to queue packet\n" ); + return(0); + } + + NumRecAck++; + + /*------------------------------------------------------------------ + Update our LastSeqID value if we can. Anything less than LastSeqID + we'll know is a resend. + ------------------------------------------------------------------*/ + if (packet->PacketID == (LastSeqID + 1)) { + LastSeqID = packet->PacketID; + /*............................................................ + Now that we have a new 'LastSeqID', search our Queue to see if + the next ID is there; if so, keep checking for the next one; + break only when the next one isn't found. This forces + LastSeqID to be the largest possible value. + ............................................................*/ + do { + found = 0; + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*...................................................... + Entry is found + ......................................................*/ + if (entry_data->Code == PACKET_DATA_ACK && + entry_data->PacketID == (LastSeqID + 1)) { + + LastSeqID = entry_data->PacketID; + found = 1; + break; + } + } + } + } while (found); + } + } /* end of save packet */ + + /*--------------------------------------------------------------------- + Send an ACK, regardless of whether this was a resend or not. + ---------------------------------------------------------------------*/ + ackpacket.MagicNumber = Magic_Num(); + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet->PacketID; +// Smart_Printf( "Sending ACK for %d \n", packet->PacketID ); + Send ((char *)&ackpacket, sizeof(CommHeaderType)); + + return(true); + + } else { +// Smart_Printf( "invalid packet type %d \n", packet->Code ); + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Get_Packet -- gets a packet from receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + CommHeaderType *entry_data; + int i; + + /*------------------------------------------------------------------------ + Ensure that we read the packets in order. LastReadID is the ID of the + last PACKET_DATA_ACK packet we read. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + + rec_entry = Queue->Get_Receive(i); + + /*..................................................................... + Only read this entry if it hasn't been yet + .....................................................................*/ + if (rec_entry && rec_entry->IsRead==0) { + + entry_data = (CommHeaderType *)rec_entry->Buffer; + + /*.................................................................. + If this is a DATA_ACK packet, its ID must be one greater than + the last one we read. + ..................................................................*/ + if ( (entry_data->Code == PACKET_DATA_ACK) && + (entry_data->PacketID == (LastReadID + 1))) { + + LastReadID = entry_data->PacketID; + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + /*.................................................................. + If this is a DATA_NOACK packet, who cares what the ID is? + ..................................................................*/ + else if (entry_data->Code == PACKET_DATA_NOACK) { + + rec_entry->IsRead = 1; + + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + } + } + + return(false); +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Send_Queue (void) +{ + int i; + int num_entries; + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + int bad_conn = 0; + + /*------------------------------------------------------------------------ + Remove any ACK'd packets from the queue + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Send(); i++) { + /* + ------------------------- Get this queue entry ------------------------ + */ + send_entry = Queue->Get_Send(i); + /* + ---------------- If ACK has been received, unqueue it ----------------- + */ + if (send_entry->IsACK) { + /* + ................ Update this queue's response time ................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + /* + ....................... unqueue the packet ......................... + */ + Queue->UnQueue_Send(NULL,NULL,i); + i--; + } + } + + /*------------------------------------------------------------------------ + Loop through all entries in the Send queue. [Re]Send any entries that + need it. + ------------------------------------------------------------------------*/ + num_entries = Queue->Num_Send(); + + for (i = 0; i < num_entries; i++) { + send_entry = Queue->Get_Send(i); + + if (send_entry->IsACK) { + continue; + } + + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ +#if(0) +{ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (send_entry->SendCount) { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Resending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Resending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } else { + if (packet_hdr->Code == PACKET_DATA_NOACK) { +// Smart_Printf( "Sending DATA NOACK for %d \n", packet_hdr->PacketID ); + } else { + if (packet_hdr->Code == PACKET_DATA_ACK) { +// Smart_Printf( "Sending DATA ACK for %d \n", packet_hdr->PacketID ); + } + } + } +} +#endif + Send (send_entry->Buffer, send_entry->BufLen); + + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) { + send_entry->IsACK = 1; + } + } + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) { +// Smart_Printf( "Max Retries!!! %d !!! \n", MaxRetries ); + bad_conn = 1; + } + + if (Timeout != -1 && + (send_entry->LastTime - send_entry->FirstTime) > Timeout) { +// Smart_Printf( "Timed out!!! Time %d, Timeout %d, buflen %d !!! \n", +// (send_entry->LastTime - send_entry->FirstTime), Timeout, +// send_entry->BufLen ); + bad_conn = 1; + } + } + } + + /*------------------------------------------------------------------------ + If the connection is going bad, return an error + ------------------------------------------------------------------------*/ + if (bad_conn) { +// Smart_Printf( "Connection going bad!!! \n" ); + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * NonSequencedConnClass::Service_Receive_Queue -- services recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NonSequencedConnClass::Service_Receive_Queue (void) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + int i; + + /*------------------------------------------------------------------------ + Remove all dead packets. + PACKET_DATA_NOACK: if it's been read, throw it away. + PACKET_DATA_ACK: if it's been read, and its ID is older than LastSeqID, + throw it away. + ------------------------------------------------------------------------*/ + for (i = 0; i < Queue->Num_Receive(); i++) { + rec_entry = Queue->Get_Receive(i); + + if (rec_entry->IsRead) { + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + + if (packet_hdr->Code == PACKET_DATA_NOACK) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } else { + if (packet_hdr->PacketID < LastSeqID) { + Queue->UnQueue_Receive(NULL,NULL,i); + i--; + } + } + } + } + + return(true); +} + + diff --git a/code/redalert/noseqcon.h b/code/redalert/noseqcon.h new file mode 100644 index 0000000..4a1f00e --- /dev/null +++ b/code/redalert/noseqcon.h @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\noseqcon.h_v 1.13 01 Mar 1996 17:32:42 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NOSEQCON.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Non-Sequenced" ACK/Retry approach to packet * + * transmission. It sends out as many packets as are in the queue, whose * + * resend delta times have expired; and it ACK's any packets its received * + * who haven't been ACK'd yet. Thus, order of delivery is NOT guaranteed; * + * however, the performance is better than the Sequenced approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NONSEQCONN_H +#define NONSEQCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +#include "combuf.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class NonSequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NonSequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~NonSequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet "queue"; this non-sequenced version isn't really much of + a queue, but more of a repository. + .....................................................................*/ + CommBufferClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + + /*..................................................................... + This is the ID of the last consecutively-received packet; anything older + than this, we know is a resend. Anything newer than this MUST be lying + around in the Queue for us to detect it as a resend. + .....................................................................*/ + unsigned long LastSeqID; + + /*..................................................................... + This is the ID of the PACKET_DATA_ACK packet we read last; it ensures + that the application reads that type of packet in order. + .....................................................................*/ + unsigned long LastReadID; +}; + +#endif + + diff --git a/code/redalert/nullconn.cpp b/code/redalert/nullconn.cpp new file mode 100644 index 0000000..1f63610 --- /dev/null +++ b/code/redalert/nullconn.cpp @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLCONN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : April 20, 1995 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemConnClass::NullModemConnClass -- class constructor * + * NullModemConnClass::~NullModemConnClass -- class destructor * + * NullModemConnClass::Init -- hardware-dependent initialization * + * NullModemConnClass::Send -- hardware-dependent packet sending * + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#endif //WIN32 +#include +//#include +#include "nullconn.h" + + +/*************************************************************************** + * NullModemConnClass::NullModemConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # send queue entries * + * numreceive desired # send receive entries * + * maxlen max length of application's packets * + * magicnum application-defined magic # for the packets * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::NullModemConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum) : + ConnectionClass (numsend, numreceive, maxlen, magicnum, + 60, // Retry Delta Time + -1, // Max Retries (-1 means ignore this timeout parameter) + 1200) // Timeout: 20 seconds +{ + /*------------------------------------------------------------------------ + Pre-set the port value to NULL, so Send won't send until we've been Init'd + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + /*------------------------------------------------------------------------ + Allocate the Send Buffer; the parent constructor has set MaxPacketLen, + so we can use it in our computation. + ------------------------------------------------------------------------*/ + SendBuf = new char [ Actual_Max_Packet() ]; + +} /* end of NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::~NullModemConnClass -- class destructor * + * * + * INPUT: * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemConnClass::~NullModemConnClass () +{ + delete [] SendBuf; + +} /* end of ~NullModemConnClass */ + + +/*************************************************************************** + * NullModemConnClass::Init -- hardware-dependent initialization * + * * + * INPUT: * + * port GreenLeaf port handle * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +#ifdef WIN32 +void NullModemConnClass::Init (HANDLE port_handle) +{ + ConnectionClass::Init(); + PortHandle = port_handle; +} +#else //WIN32 +void NullModemConnClass::Init (PORT *port) +{ + ConnectionClass::Init(); + Port = port; +} /* end of Init */ +#endif //WIN32 + +/*************************************************************************** + * NullModemConnClass::Send -- hardware-dependent packet sending * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer to send * + * extrabuf (not used by this class) * + * extralen (not used by this class) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * 1 = OK, 0 = error * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Send (char *buf, int buflen, void *, int ) +{ + return 0; +#if (0)//PG + int *ibuf; + SerialHeaderType *header; + unsigned long sendlen; + + /*------------------------------------------------------------------------ + Error if we haven't been properly initialized + ------------------------------------------------------------------------*/ +#ifdef WIN32 + if ( PortHandle == NULL ) return(false); + +#else //WIN32 + int status; + if ( Port == NULL ) { + return(0); + } +#endif //WIN32 + + /*------------------------------------------------------------------------ + Package the data into the Send Buffer + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *) SendBuf; + header->MagicNumber = PACKET_SERIAL_START; + header->Length = (short) buflen; + header->MagicNumber2 = PACKET_SERIAL_VERIFY; + + sendlen = sizeof( SerialHeaderType ); + memcpy (SendBuf + sendlen, buf, buflen); + sendlen += buflen; + ibuf = (int *)(SendBuf + sendlen); + *ibuf = Compute_CRC( buf, buflen ); + sendlen += sizeof( int ); + + *(SendBuf + sendlen) = '\r'; + sendlen += 1; + + /*------------------------------------------------------------------------ + Send the data + ------------------------------------------------------------------------*/ +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char *)SendBuf, (int)sendlen ); + return (true); + +#else //WIN32 + status = WriteBuffer( Port, SendBuf, sendlen ); + if ( status == ASSUCCESS ) { + return(1); + } + else { + return(0); + } +#endif //WIN32 +#endif//PG +} /* end of Send */ + + +/*************************************************************************** + * NullModemConnClass::Compute_CRC -- computes CRC for given buffer * + * * + * INPUT: * + * buf buffer to compute CRC for * + * buflen length of buffer in bytes * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemConnClass::Compute_CRC (char *buf, int buflen) +{ + unsigned int sum, hibit; + + sum = 0; + for (int i = 0; i < buflen; i++) { + if ( sum & 0x80000000 ) { // check hi bit to rotate into low bit + hibit = 1; + } + else { + hibit = 0; + } + + sum <<= 1; + sum += (hibit + (unsigned char)buf[i]); + } + + return((int)sum); + +} /* end of Compute_CRC */ + + +/*************************************************************************** + * NullModemConnClass::Packet_Overhead_Size -- number of extra bytes * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * number of bytes used for communications only. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/20/1995 DRD : Created. * + *=========================================================================*/ +int NullModemConnClass::Packet_Overhead_Size ( void ) +{ + /*------------------------------------------------------------------------ + short for Null Modem Magic Number + short for Null Modem length of packet + int for Null Modem CRC check + CommHeaderType for Queued packets + ------------------------------------------------------------------------*/ + return( (PACKET_SERIAL_OVERHEAD_SIZE + sizeof(CommHeaderType)) ); + +} /* end of Packet_Overhead_Size */ + +/************************** end of nullconn.cpp ****************************/ diff --git a/code/redalert/nullconn.h b/code/redalert/nullconn.h new file mode 100644 index 0000000..f21873d --- /dev/null +++ b/code/redalert/nullconn.h @@ -0,0 +1,147 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLCONN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Class for a NULL-Modem connection. It inherits * + * a Queue, PacketBuf, timeout variables from ConnectionClass. It * + * inherits its Send_/Receive_/Get_Packet functions, and the non-sequenced * + * ACK/Retry logic in Service_Send_Queue & Service_Receive_Queue from * + * ConnectionClass. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLCONN_H +#define NULLCONN_H + + +/* +********************************* Includes ********************************** +*/ +#include "connect.h" +//#include "commlib.h" + +/* +********************************** Defines ********************************** +*/ +#define PACKET_SERIAL_START 0xDABD +#define PACKET_SERIAL_VERIFY 0xDEAF + +#define PACKET_SERIAL_OVERHEAD_SIZE (sizeof( SerialHeaderType ) + sizeof( SerialCRCType )) + +typedef struct { + unsigned short MagicNumber; + unsigned short Length; + unsigned short MagicNumber2; +} SerialHeaderType; + +typedef struct { + int SerialCRC; +} SerialCRCType; + + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + NullModemConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum); + virtual ~NullModemConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ +#ifdef WIN32 + void Init (HANDLE port_handle); +#else //WIN32 + void Init (PORT *port); +#endif //WIN32 + + /*..................................................................... + Utility routines. + .....................................................................*/ + unsigned long Actual_Max_Packet (void) { return (MaxPacketLen + + (sizeof(SerialHeaderType)) + sizeof(int) + sizeof (char)); } + + /*..................................................................... + This routine computes a CRC value for the given buffer. + .....................................................................*/ + static int Compute_CRC(char *buf, int buflen); + + /*..................................................................... + This routine returns the number of bytes extra added the packet + for communication. + .....................................................................*/ + static int Packet_Overhead_Size( void ); + + /* + --------------------------- Private Interface ---------------------------- + */ + protected: + /*..................................................................... + This routine actually performs a hardware-dependent data send. + .....................................................................*/ + virtual int Send (char *buf, int buflen, void *extrabuf, int extralen); +#ifdef WIN32 + /* + ** This is the winsoze port handle + */ + HANDLE PortHandle; +#else //WIN32 + /*..................................................................... + This is the PORT value used by the GreenLeaf calls. + .....................................................................*/ + PORT *Port; +#endif //WIN32 + + /*..................................................................... + This buffer is a staging area for data sent out; it includes the + packet sent by the parent class (which includes the application's + packet, plus the CommHeaderType header), plus: + - 2-byte buffer start ID + - 2-byte length + - 4-byte CRC value (at the end of the buffer) + This is the actual packet that gets sent across the serial line. + .....................................................................*/ + char *SendBuf; +}; + +#endif + +/************************** end of nullconn.h ******************************/ + diff --git a/code/redalert/nulldlg.cpp b/code/redalert/nulldlg.cpp new file mode 100644 index 0000000..d35c81b --- /dev/null +++ b/code/redalert/nulldlg.cpp @@ -0,0 +1,9642 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/NULLDLG.CPP 14 3/17/97 1:05a Steve_tall $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLDLG.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/29/95 * + * * + * Last Update : Jan. 21, 1997 [V.Grippi] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Build_InitString_Listbox -- [re]builds the initstring entry listbox * + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * Com_Settings_Dialog -- Lets user select serial port settings * + * Destroy_Null_Connection -- destroys the given connection * + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * Init_Null_Modem -- Initializes Null Modem communications * + * Init_String_Compare -- for qsort * + * Phone_Compare -- for qsort * + * Phone_Dialog -- Lets user edit phone directory & dial * + * Reconnect_Null_Modem -- allows user to reconnect * + * Select_Serial_Dialog -- Serial Communications menu dialog * + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * Test_Null_Modem -- Null-Modem test routine * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef FIXIT_RANDOM_GAME +#include "time.h" +#endif +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +ModemRegistryEntryClass* ModemRegistry = NULL; //Ptr to modem registry data +#endif //WIN32 +#include "COORDA.h" +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126(char* file_name); +extern bool Is_Mission_Counterstrike(char* file_name); +extern bool Is_Mission_Aftermath(char* file_name); +#endif + +//PG +GameType Select_Serial_Dialog(void) { return GAME_NORMAL; } +extern char const* EngMisStr[]; + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +PlayerColorType TheirColor; +HousesType TheirHouse; +//static char DialString[CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1]; +static SerialSettingsType* DialSettings; + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available(const char* szName); +bool bSpecialAftermathScenario(const char* szScenarioDescription); +#endif + +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. + * 01/21/97 V.Grippi added check for CS before sending scenario file * + *=============================================================================================*/ +int Com_Scenario_Dialog(bool skirmish) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; + int d_dialog_h = 200 * RESFACTOR; + int d_dialog_x = ((SeenBuff.Get_Width() - d_dialog_w) / 2); + int d_dialog_y = ((SeenBuff.Get_Height() - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR + 1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 7 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + 1 * RESFACTOR; + + int d_house_w = 60 * RESFACTOR; + int d_house_h = (8 * 5 * RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5 * RESFACTOR; + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2 * RESFACTOR/*KO + d_txt6_h*/; + + int d_scenariolist_w = 162 * RESFACTOR; + int d_scenariolist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + + if (skirmish) { + d_scenariolist_h *= 2; + } + + + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - 5 * RESFACTOR; + int d_scenariolist_y = d_color_y + d_color_h + d_margin2 + 2 * RESFACTOR; + + if (skirmish) { + d_scenariolist_x = d_dialog_x + (d_dialog_w - d_scenariolist_w) / 2; + } + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_count_y = d_playerlist_y + d_playerlist_h + (d_margin1 * 2) - 2 * RESFACTOR; + + if (skirmish) { + d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2 * RESFACTOR; + } + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 * RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106 * RESFACTOR; + int d_options_h = (5 * 6 * RESFACTOR) + 4 * RESFACTOR; + int d_options_x = d_dialog_x + d_dialog_w - 149 * RESFACTOR; + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2 * RESFACTOR; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20 * RESFACTOR; + int d_message_h = (8 * d_txt6_h) + 3 * RESFACTOR; // 4 rows high + int d_message_x = d_dialog_x + d_margin1 + 10 * RESFACTOR; + int d_message_y = d_options_y + d_options_h + 2 * RESFACTOR; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20 * RESFACTOR; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10 * RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 45 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - RESFACTOR * 6; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - RESFACTOR * 6; + + int d_load_w = 45 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - RESFACTOR * 6; + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_PLAYERLIST, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + BUTTON_DIFFICULTY, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int playertabs[] = { 77 * RESFACTOR }; // tabs for player list box + int optiontabs[] = { 8 }; // tabs for player list box + char namebuf[MPLAYER_NAME_MAX] = { 0 }; // buffer for player's name + bool transmit; // 1 = re-transmit new game options + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + unsigned long version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static bool first_time = true; + bool oppscorescreen = false; + bool gameoptions = Session.Type == GAME_SKIRMISH; + EventClass* event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + CCFileClass loadfile("SAVEGAME.NET"); + bool load_game = false; // 1 = load a saved game + NodeNameType* who; // node to add to Players + char* item; // for filling in lists + RemapControlType* scheme = GadgetClass::Get_Color_Scheme(); + bool messages_have_focus = true; // Gadget focus starts on the message system + + Set_Logic_Page(SeenBuff); + + CDTimerClass kludge_timer; // Timer to allow a wait after client joins + // game before game can start + bool ok_button_added = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass* commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x + d_count_w + 3 * RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x + d_level_w + 3 * RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x + d_credits_w + 3 * RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 3 * RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, d_load_w, d_load_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + SliderClass difficulty(BUTTON_DIFFICULTY, d_name_x, optionlist.Y + optionlist.Height + d_margin1 + d_margin1, d_dialog_w - (d_name_x - d_dialog_x) * 2, 8 * RESFACTOR, true); + if (Rule.IsFineDifficulty) { + difficulty.Set_Maximum(5); + difficulty.Set_Value(2); + } + else { + difficulty.Set_Maximum(3); + difficulty.Set_Value(1); + } + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + if (!skirmish) { + playerlist.Add_Tail(*commands); + } + else { + difficulty.Add_Tail(*commands); + } + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + if (Session.Type == GAME_SKIRMISH) { + okbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + if (!skirmish && loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE + //Load button added only when other player has arrived + //loadbtn.Add_Tail(*commands); +#endif + } + else { + cancelbtn.X = loadbtn.X; + } +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy(namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf, MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + +#ifdef OLDWAY + if (Session.House == HOUSE_GOOD) { + gdibtn.Turn_On(); + } + else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only(true); +#endif + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Special.IsShadowGrow = Rule.IsMPShadowGrow; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = false; + } + + /*........................................................................ + Init button states + ........................................................................*/ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + if (!skirmish) { + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + } + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsShadowGrow); + if (!skirmish) { + optionlist.Check_Item(4, Special.IsCaptureTheFlag); + } + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + int maxp = Rule.MaxPlayers - 2; + // int maxp = Rule.MaxPlayers - (skirmish ? 1 : 2); + aiplayersgauge.Set_Maximum(maxp); + + if (skirmish) { + if (Session.Options.AIPlayers > 7) { + Session.Options.AIPlayers = 7; + } + Session.Options.AIPlayers = max(Session.Options.AIPlayers, 1); + } + else { + if (Session.Options.AIPlayers > 6) { + Session.Options.AIPlayers = 6; + } + } + + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = //Session.Options.Tiberium; + Rule.IsTGrowth = //Session.Options.Tiberium; + Special.IsTSpread = //Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < Session.Scenarios.Count(); i++) { + int j = -1; + for (j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if (!((Is_Mission_Counterstrike((char*)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed()) || + (Is_Mission_Aftermath((char*)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed()))) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j + 1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + break; + } + } + if (EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if (!Session.Scenarios[i]->Get_Official() || + !((Is_Mission_Counterstrike((char*)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed()) || + (Is_Mission_Aftermath((char*)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed()))) +#endif + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ +#ifdef FIXIT_RANDOM_GAME + srand(time(NULL)); + Seed = rand(); +#else + // randomize(); + // Seed = rand(); +#endif + + /*........................................................................ + Init the message display system + ........................................................................*/ + if (!skirmish) { + Session.Messages.Init(d_message_x + 1, d_message_y + 1, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1, d_send_y + 1, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + } + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + //if (strlen(ModemRXString) > 36) + // ModemRXString[36] = 0; + // + //if (strlen(ModemRXString) > 0) + // Session.Messages.Add_Message(NULL, 0, ModemRXString, PCOLOR_BROWN, + // TPF_TEXT, -1); + // + //ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ +#if(SHOW_MONO) + if (!skirmish) { + NullModem.Configure_Debug(sizeof(CommHeaderType), sizeof(SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + } +#endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + if (!skirmish) { + //NullModem.Reset_Response_Time(); // clear response time + } + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount; + + + /* + ** Those easily offended should avert their eyes from the following line. And whatever + ** you do, dont search for 'goto'. + */ +oh_dear_its_a_label: + + while (process) { +#if(SHOW_MONO) + if (!skirmish) { + NullModem.Mono_Debug_Print(0); + } +#endif + + if (!skirmish) { + if (!ok_button_added && gameoptions && kludge_timer == 0) { + okbtn.Add_Tail(*commands); + ok_button_added = true; +#ifdef FIXIT_VERSION_3 + if (loadfile.Is_Available()) + { + loadbtn.Add_Tail(*commands); + } +#else +#ifdef FIXIT_MULTI_SAVE + if (loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104) { + loadbtn.Add_Tail(*commands); + } +#endif +#endif + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + } + } + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (!skirmish) { + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } + else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + } + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = FALSE; + display = REDRAW_ALL; + } +#endif + + /* + ...................... Refresh display if needed ...................... + */ + { + //if (housebtn.IsDropped) { + //housebtn.Collapse(); + //display = REDRAW_BACKGROUND; + //} + Hide_Mouse(); + + /* + .................. Redraw backgound & dialog box ................... + */ + { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + if (!skirmish) { + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } + else { + Fancy_Text_Print(TXT_EASY, difficulty.X, difficulty.Y - 8 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_HARD, difficulty.X + difficulty.Width, difficulty.Y - 8 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + Fancy_Text_Print(TXT_NORMAL, difficulty.X + difficulty.Width / 2, difficulty.Y - 8 * RESFACTOR, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { +// jmarshall + //LogicPage->Fill_Rect(cbox_x[i] + 1, d_color_y + 1, + // cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + // ColorRemaps[i].Box); + // (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + GL_FillRect(ColorRemaps[i].Box, cbox_x[i] + 1, d_color_y + 1, 1 + d_color_w - 2, 1 + d_color_h - 2); +// jmarshall end + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } + else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE && !skirmish) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + { + + // LogicPage->Fill_Rect(d_count_x + d_count_w + 2, d_count_y, d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); + // Fancy_Text_Print("%d ", d_count_x + d_count_w + 3 * RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT, Session.Options.UnitCount); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } + else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); + // Fancy_Text_Print(txt, d_level_x + d_level_w + 3 * RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); + // Fancy_Text_Print("%d", d_credits_x + d_credits_w + 2 * RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT, Session.Options.Credits); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); + // Fancy_Text_Print("%d", d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT, Session.Options.AIPlayers); + } + + /* + .......................... Redraw buttons .......................... + */ + { + //commands->Flag_List_To_Redraw(); + commands->Draw_All(); + } + + Show_Mouse(); + //display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + + input = commands->Input(); + + /* + ........................ Invoke game callback ......................... + */ + //Call_Back(); + Device_Present(); + + Load_Title_Page(true); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (!skirmish) { + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } + else { + messages_have_focus = false; + display = REDRAW_MESSAGE; + } + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + if (input & KN_BUTTON) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + + Session.PrefColor = (PlayerColorType)((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx); + strcpy(Session.Handle, namebuf); + transmit = true; + changed = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy(Session.Handle, namebuf); + transmit = true; + changed = true; + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy(Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy(Session.Handle, namebuf); + transmit = true; + break; +#else + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index() + HOUSE_USSR); + strcpy(Session.Handle, namebuf); + display = REDRAW_BACKGROUND; + transmit = true; + break; +#endif + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + strcpy(Session.Handle, namebuf); + transmit = true; + } + break; + +#else // FIXIT_VERSION_3 Whoever duplicated Netdlg into Nulldlg should be shot. Wasn't it enough? + + Abandon all hope ye who hit enter here. + + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if (!skirmish && (PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if (!skirmish && PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()) { + scenariolist.Set_Selected_Index(Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char*)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + } + else + if (!skirmish && PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char*)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename())) { + scenariolist.Set_Selected_Index(Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char*)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + } + else { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + + strcpy(Session.Handle, namebuf); + transmit = true; + } + break; +#endif + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + SessionClass::CountMin[Session.Options.Bases]; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = ((Session.Options.Credits + 250) / 500) * 500; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + { + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + int humans = 2; // Two humans. + if (skirmish) { + Session.Options.AIPlayers += 1; // Always one forced AI player. + humans = 1; // One human. +// if (Session.Options.AIPlayers == 0) { +// Session.Options.AIPlayers = 1; +// aiplayersgauge.Set_Value(0); +// } + } + if (Session.Options.AIPlayers + humans >= Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - humans; + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + } + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + break; + } + + //------------------------------------------------------------------ + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //------------------------------------------------------------------ + case (BUTTON_OPTIONS | KN_BUTTON): + if (!skirmish && Special.IsCaptureTheFlag != optionlist.Is_Checked(4) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != optionlist.Is_Checked(0)) { + Session.Options.Bases = optionlist.Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0] - SessionClass::CountMin[0], + Session.Options.UnitCount - SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } + else { + if (!skirmish) optionlist.Check_Item(4, false); + Session.Options.UnitCount = Fixed_To_Cardinal( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1] - SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsShadowGrow = optionlist.Is_Checked(3); + if (!skirmish) { + Special.IsCaptureTheFlag = optionlist.Is_Checked(4); + } + + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + rc = true; + process = false; + + // force transmitting of game options packet one last time + + transmit = true; + transmittime = 0; + } + else { + WWMessageBox().Process(TXT_ONLY_ONE, TXT_OOPS, NULL); + display = REDRAW_ALL; + } + if (input == (BUTTON_LOAD | KN_BUTTON)) + load_game = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!skirmish) { + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i == 1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } + else if (i == 2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } + else if (i == 3 || i == 4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset(&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy(SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i == 3) { + strcpy(SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } + else { + strcpy(SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + if (!skirmish) { + //NullModem.Send_Message(&SendPacket, sizeof(SendPacket), 1); + //NullModem.Service(); + } + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message(SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp(namebuf, Session.Handle)) { + strcpy(Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (skirmish) { + transmit = false; + } + + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset(&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy(SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ScenarioInfo.Credits = Session.Options.Credits; + SendPacket.ScenarioInfo.IsBases = Session.Options.Bases; + SendPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + SendPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + SendPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + SendPacket.ScenarioInfo.BuildLevel = BuildLevel; + SendPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + SendPacket.ScenarioInfo.Seed = Seed; + SendPacket.ScenarioInfo.Special = Special; + SendPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + SendPacket.ID = Session.ModemType; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy(SendPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file(Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + SendPacket.ScenarioInfo.FileLength = file.Size(); + +#ifdef WOLAPI_INTEGRATION + strcpy(SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); +#else + strncpy(SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(SendPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy((char*)SendPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof SendPacket.ScenarioInfo.FileDigest); + SendPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + //NullModem.Send_Message(&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char*)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House == HOUSE_GOOD) { + sprintf(item, "%s\t%s", namebuf, + Text_String(TXT_ALLIES)); + } + else { + sprintf(item, "%s\t%s", namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf(item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if (!skirmish && (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset(&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + //SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + //NullModem.Send_Message(&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (!skirmish && false) { + + lastmsgtime = TickCount; + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process(TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass*)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ((TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } + else { + switch (ReceivePacket.Command) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + //while (TickCount - starttime < 60) + //NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + kludge_timer = 2 * 60; + strcpy(TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + transmit = true; + + parms_received = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process(TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } + else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process(TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } + else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process(TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + } + else { + + + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists + .........................................................*/ + if (playerlist.Count() == 0) { + //...................................................... + // Add two strings to the player list + //...................................................... + item = new char[MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[Session.ColorIdx]); + item = new char[MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[TheirColor]); + } + + //......................................................... + // Ensure the player list has the latest, greatest copy of + // our names & colors. Do this every time we receive an + // options packet. + //......................................................... + item = (char*)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House == HOUSE_GOOD) { + sprintf(item, "%s\t%s", namebuf, + Text_String(TXT_ALLIES)); + } + else { + sprintf(item, "%s\t%s", namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf(item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char*)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse == HOUSE_GOOD) { + sprintf(item, "%s\t%s", TheirName, + Text_String(TXT_ALLIES)); + } + else { + sprintf(item, "%s\t%s", TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf(item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : TheirColor]; + + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message(ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ScenarioInfo.ResponseTime; + + if (!gameoptions) { + + // retransmit of game options packet again + transmit = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if (!skirmish && (TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process(TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + if (!skirmish) { + //NullModem.Service(); + } + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = skirmish ? 1 : 2; + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy(Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add(who); + + /* + ** Fetch the difficulty setting when in skirmish mode. + */ + if (skirmish) { + int diff = difficulty.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2); + switch (diff) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } + else { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } + + if (!skirmish) { + who = new NodeNameType; + +#ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + TheirName[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(TheirName, "2"); + } + } +#endif + + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add(who); + } + + /*..................................................................... + Send all players a GO packet. + .....................................................................*/ + memset(&SendPacket, 0, sizeof(SerialPacketType)); + if (load_game) { + SendPacket.Command = SERIAL_LOADGAME; + } + else { + SendPacket.Command = SERIAL_GO; + } + + if (!skirmish) { + //SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + if (theirresponsetime == 10000) { + ; + } + else if (SendPacket.ScenarioInfo.ResponseTime < theirresponsetime) { + SendPacket.ScenarioInfo.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + if (!skirmish) { + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max(((((SendPacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) + ); + } + else { + Session.MaxAhead = max((SendPacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD); + } + } + SendPacket.ID = Session.ModemType; + + if (!skirmish) { + //NullModem.Send_Message(&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; +// while ((NullModem.Num_Send() +// && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT)) +// || ((TickCount - starttime) < 60)) { +//#if(SHOW_MONO) +// NullModem.Mono_Debug_Print(0); +//#endif +// +// NullModem.Service(); +// } + + /* + ** Wait for the go response. This will be either a 'GO' reply, a + ** request for the scenario to be sent or a reply to say that the scenario + ** cant be played. + */ +#ifdef WIN32 + WWDebugString("RA95 - About to wait for 'GO' response.\n"); +#endif +// do { + //NullModem.Service(); + +// if (NullModem.Get_Message(&ReceivePacket, &packetlen) > 0) { +// +//#ifdef FIXIT_VERSION_3 +// if (ReceivePacket.Command == SERIAL_READY_TO_GO) +// { +// if (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official()) +// { +// if (!Force_Scenario_Available(Scen.ScenarioName)) +// Emergency_Exit(EXIT_FAILURE); +// } +// break; +// } +//#else +// if (ReceivePacket.Command == SERIAL_READY_TO_GO) break; +//#endif +// +// if (ReceivePacket.Command == SERIAL_NO_SCENARIO) { +// WWMessageBox().Process(TXT_NO_EXPANSION_SCENARIO, TXT_CANCEL); +// /* +// ** We have to recover from this somehow so.... +// */ +// process = true; +// display = REDRAW_ALL; +// lastmsgtime = TickCount; +// goto oh_dear_its_a_label; +// } +// +// if (ReceivePacket.Command == SERIAL_REQ_SCENARIO) { +//#ifdef WIN32 +// WWDebugString("RA95 - About to call 'Send_Remote_File'.\n"); +// +//#endif +// +//#ifdef FIXIT_VERSION_3 +// if (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official()) +// { +// if (!Force_Scenario_Available(Scen.ScenarioName)) +// Emergency_Exit(EXIT_FAILURE); +// } +//#endif +// +// Send_Remote_File(Scen.ScenarioName, 0); +// +// break; +// } +// } +// +// } while (!Keyboard->Check()); +// +// // clear queue to keep from doing any resends +// NullModem.Init_Send_Queue(); + } + + } + else { + if (!recsignedoff) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + if (!skirmish) { + memset(&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + //NullModem.Send_Message(&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; +// while ((NullModem.Num_Send() +// && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT)) +// || ((TickCount - starttime) < 60)) { +//#if(SHOW_MONO) +// NullModem.Mono_Debug_Print(0); +//#endif +// +// if (NullModem.Get_Message(&ReceivePacket, &packetlen) > 0) { +// +// // are we getting our own packets back?? +// +// if (ReceivePacket.Command == SERIAL_SIGN_OFF +// && ReceivePacket.ID == Session.ModemType) { +// +// // exit while +// break; +// } +// } +// +// NullModem.Service(); +// } + } + } + + // if (!skirmish) Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ + while (playerlist.Count() > 0) { + item = (char*)playerlist.Get_Item(0); + delete[] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings(); + } + + if (load_game && !skirmish) { + if (!Load_Game(-1)) { + WWMessageBox().Process(TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); +} + + + +#if (0)//PG +#ifdef FIXIT_RANDOM_GAME +#include "time.h" +#endif +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +ModemRegistryEntryClass *ModemRegistry = NULL; //Ptr to modem registry data +#endif //WIN32 +#include "COORDA.h" +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_126x126 (char *file_name); +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath( char* file_name ); +#endif + +//#include "WolDebug.h" + +// +// how much time (ticks) to go by before thinking other system +// is not responding. +// +#define PACKET_SENDING_TIMEOUT 1800 +#define PACKET_CANCEL_TIMEOUT 900 + +//extern char const *ForMisStr[]; +extern char const *EngMisStr[]; + + +// +// how much time (ticks) to go by before sending another packet +// of game options or serial connect. +// +#define PACKET_RETRANS_TIME 30 +#define PACKET_REDRAW_TIME 60 + +static int Reconnect_Null_Modem( void ); +static int Com_Settings_Dialog( SerialSettingsType *settings ); +static int Phone_Dialog (void); +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index); +static int Init_String_Compare (const void *p1, const void *p2); +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf); +static int Phone_Compare (const void *p1, const void *p2); +static int Edit_Phone_Dialog (PhoneEntryClass *phone); +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ); +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ); +static void Modem_Echo( char c ); + +void Smart_Printf( char *format, ... ); +void Hex_Dump_Data( char *buffer, int length ); +void itoh( int i, char *s); + + +static SerialPacketType SendPacket; +static SerialPacketType ReceivePacket; +char TheirName[MPLAYER_NAME_MAX]; +PlayerColorType TheirColor; +HousesType TheirHouse; +static char DialString[ CWAITSTRBUF_MAX + PhoneEntryClass::PHONE_MAX_NUM - 1 ]; +static SerialSettingsType *DialSettings; + +#ifdef FIXIT_VERSION_3 +bool Force_Scenario_Available( const char* szName ); +bool bSpecialAftermathScenario( const char* szScenarioDescription ); +#endif + +#define PCOLOR_BROWN PCOLOR_GREY + + +#define SHOW_MONO 0 + +/*************************************************************************** + * Init_Null_Modem -- Initializes Null Modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Init_Null_Modem( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 0, 8, 1, + settings->HardwareFlowControl ) ) { + return (true); + } else { + return (false); + } + +#else //WIN32 + if ( NullModem.Init( settings->Port, settings->IRQ, + settings->ModemName, + settings->Baud, 'N', 8, 1, + settings->HardwareFlowControl) ) { + NullModem.Change_IRQ_Priority( settings->IRQ ); + + return(true); + } else { + return(false); + } +#endif //WIN32 +} + + +/*************************************************************************** + * Shutdown_Modem -- Shuts down modem/null-modem communications * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +void Shutdown_Modem( void ) +{ + if (!Session.Play) { + if (Session.Type == GAME_MODEM) { + NullModem.Hangup_Modem(); + } + } + + NullModem.Change_IRQ_Priority( 0 ); // reset priority of interrupts + + // + // close port + // + NullModem.Shutdown(); +} + + +/*************************************************************************** + * Modem_Signoff -- sends EXIT event * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/03/1995 DRD : Created. * + *=========================================================================*/ +void Modem_Signoff( void ) +{ + unsigned long starttime; + EventClass event; + + if (!Session.Play) { + /* + ** Send a sign-off packet + */ + memset (&event, 0, sizeof(EventClass)); + event.Type = EventClass::EXIT; + NullModem.Send_Message (&event,sizeof(EventClass),0); + NullModem.Send_Message (&event,sizeof(EventClass),0); + + starttime = TickCount; + while ( (TickCount - starttime) < 30) { + NullModem.Service(); + } + } +} + + +/*************************************************************************** + * Test_Null_Modem -- Null-Modem test routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = I'm the game owner, 2 = I'm not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + * 8/2/96 ST : Win32 support added * + *=========================================================================*/ +int Test_Null_Modem( void ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + + int retval; + unsigned long starttime; + int packetlen; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_WAITING_CONNECT ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + Load_Title_Page(true); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20 * RESFACTOR, y + 25 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + while (Get_Mouse_State() > 0) Show_Mouse(); + +#ifdef WIN32 + /* + ** This is supposed to be a direct connection so hang up any modem on this port + ** just to annoy British Telecom + */ + /* + ** Go into break mode + */ + SetCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Send hangup command + */ + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATH\r", strlen("ATH\r")); + CountDownTimerClass time; + time.Set(2*60); + while (time.Time()) {} + + /* + ** Back out of break mode + */ + ClearCommBreak(SerialPort->Get_Port_Handle()); + + /* + ** Drop DTR as well - just in case the modem still hasnt got the message + */ + EscapeCommFunction(SerialPort->Get_Port_Handle(), CLRDTR); +#endif //WIN32 + + + /* + ** Check for a packet. If we detect one, the other system has already been + ** started. Wait 1/2 sec for him to receive my ACK, then exit with success. + ** Note: The initial time must be a little longer than the resend delay. + ** Just in case we just missed the packet. + */ + starttime = TickCount; + while ( TickCount - starttime < 80) { + NullModem.Service(); + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + process = false; + retval = 2; + break; + } + } + } + + /* + ** Send a packet across. As long as Num_Send() is non-zero, the other system + ** hasn't received it yet. + */ + if (process) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + // + // put time from start of game for determining the host in case of tie. + // + SendPacket.ScenarioInfo.Seed = TickCount; + SendPacket.ID = (int) buffer; // address of buffer for more uniqueness. + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while (TickCount - starttime < 80) { + NullModem.Service(); + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + break; + } + } + } + } + + starttime = TickCount; + + /* + ** Main Processing Loop + */ + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = 0; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + if (NullModem.Num_Send() == 0) { + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + if (ReceivePacket.Command == SERIAL_CONNECT) { + starttime = TickCount; + while (TickCount - starttime < 30) + NullModem.Service(); + + // + // whoever has the highest time is the host + // + if (ReceivePacket.ScenarioInfo.Seed > SendPacket.ScenarioInfo.Seed) { + process = false; + retval = 2; + + } else if (ReceivePacket.ScenarioInfo.Seed == SendPacket.ScenarioInfo.Seed) { + if (ReceivePacket.ID > SendPacket.ID) { + process = false; + retval = 2; + + // + // if they are equal then it's a loopback cable or a modem + // + } else if (ReceivePacket.ID == SendPacket.ID) { + process = false; + retval = 3; + } + } + + } else { + retval = 0; + process = false; + } + } else { + retval = 1; + process = false; + } + } + + if (TickCount - starttime > 3600) { // only wait 1 minute + retval = 0; + process = false; + } + } /* end of while */ + + return( retval ); + +} + + +/*************************************************************************** + * Reconnect_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +int Reconnect_Modem( void ) +{ + int status; + int modemstatus; + + + switch (Session.ModemType) { + case (MODEM_NULL_HOST): + case (MODEM_NULL_JOIN): + status = Reconnect_Null_Modem(); + break; + + case (MODEM_DIALER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Dial_Modem( DialSettings, true ); + } + break; + + case (MODEM_ANSWERER): + modemstatus = NullModem.Get_Modem_Status(); + if ( (modemstatus & CD_SET) ) { + status = Reconnect_Null_Modem(); + } else { + status = Answer_Modem( DialSettings, true ); + } + break; + } + + return( status ); +} + + +/*************************************************************************** + * Reconnect_Null_Modem -- allows user to reconnect * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 = failure to connect; 1 = connect OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconnect_Null_Modem( void ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Dialog variables + */ + bool process = true; // process while true + KeyNumType input; + + int retval; + unsigned long starttime; + unsigned long lastmsgtime; + int packetlen; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + strcpy( buffer, Text_String( TXT_NULL_CONNERR_CHECK_CABLES ) ); + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 50 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2*RESFACTOR) - 10 * RESFACTOR); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + process = true; + + /* + ** Create the list + */ + commands = &cancelbtn; + + commands->Flag_List_To_Redraw(); + + /* + ** Draw the dialog + */ + Hide_Mouse(); + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + commands->Draw_All(); + Show_Mouse(); + + /* + ** Main Processing Loop + */ + starttime = lastmsgtime = TickCount; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + commands->Draw_All(); + } +#endif //WIN32 + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + retval = false; + process = false; + break; + + default: + break; + } + /* + ** Service the connection. + */ + NullModem.Service(); + + /* + ** Resend our message if it's time + */ + if (TickCount - starttime > PACKET_RETRANS_TIME) { + starttime = TickCount; + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + } + + /* + ** Check for an incoming message + */ + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + if (ReceivePacket.Command == SERIAL_CONNECT) { + + // are we getting our own packets back?? + + if (ReceivePacket.ID == Session.ColorIdx) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + retval = false; + process = false; + break; + } + + /* + ** OK, we got our message; now we have to make certain the other + ** guy gets his, so send him one with an ACK required. + */ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_CONNECT; + SendPacket.ID = Session.ColorIdx; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + retval = true; + process = false; + } + } + + // + // timeout if we do not get any packets + // + if (TickCount - lastmsgtime > PACKET_CANCEL_TIMEOUT) { + retval = false; + process = false; + } + + } /* end of while */ + + return( retval ); + +} + + +/*********************************************************************************************** + * Destroy_Null_Connection -- destroys the given connection * + * * + * Call this routine when a connection goes bad, or another player signs off. * + * * + * INPUT: * + * id connection ID to destroy; this should be the HousesType of the player being * + * "destroyed". * + * error 0 = user signed off; 1 = connection error * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/31/1995 DRD : Created. * + *=============================================================================================*/ +void Destroy_Null_Connection(int id, int error) +{ + int i; + HouseClass *housep; + char txt[80]; + + + if ( Session.NumPlayers == 1 ) { + return; + } + + /* + ** Do nothing if the house isn't human. + */ + housep = HouseClass::As_Pointer((HousesType)id); + if (!housep || !housep->IsHuman) + return; + + /* + ** Create a message to display to the user + */ + txt[0] = '\0'; + switch (error) { + case 1: + sprintf(txt,Text_String(TXT_CONNECTION_LOST), housep->IniName); + break; + + case 0: + sprintf(txt,Text_String(TXT_LEFT_GAME), housep->IniName); + break; + + case -1: + NullModem.Delete_Connection(); + break; + } + + if (strlen(txt)) { + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + + /* + ** Remove this player from the Players vector + */ + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name,housep->IniName)) { + delete Session.Players[i]; + Session.Players.Delete(Session.Players[i]); + break; + } + } + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName,Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + + /* + ** If we're the last player left, tell the user. + */ + if (Session.NumPlayers == 1) { + sprintf(txt,"%s",Text_String(TXT_JUST_YOU_AND_ME)); + Session.Messages.Add_Message (NULL, 0, txt, + (housep->RemapColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : housep->RemapColor, + TPF_TEXT, Rule.MessageDelay * TICKS_PER_MINUTE); + Map.Flag_To_Redraw(false); + } + +} + + +/*************************************************************************** + * Select_Serial_Dialog -- Serial Communications menu dialog * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * GAME_MODEM user wants to play a modem game * + * GAME_NULL_MODEM user wants to play a null-modem game * + * GAME_NORMAL user hit Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +GameType Select_Serial_Dialog( void ) +{ + int rc; +// int value, i; + int com = -1, baud = -1; + int error = 0; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 160 * RESFACTOR; // dialog width + int d_dialog_h = 94 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = 80*RESFACTOR; +// int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_dial_w = 90 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - d_dial_w / 2; + int d_dial_y = d_dialog_y + d_margin + d_txt6_h + d_margin; + + int d_answer_w = 90 * RESFACTOR; + int d_answer_h = 9 * RESFACTOR; + int d_answer_x = d_dialog_cx - d_answer_w / 2; + int d_answer_y = d_dial_y + d_dial_h + 2; + + int d_nullmodem_w = 90 * RESFACTOR; + int d_nullmodem_h = 9 * RESFACTOR; + int d_nullmodem_x = d_dialog_cx - d_nullmodem_w / 2; + int d_nullmodem_y = d_answer_y + d_answer_h + 2; + + int d_settings_w = 90 * RESFACTOR; + int d_settings_h = 9 * RESFACTOR; + int d_settings_x = d_dialog_cx - d_settings_w / 2; + int d_settings_y = d_nullmodem_y + d_nullmodem_h + 2; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_settings_y + d_settings_h + d_margin; + + /* + ** Button Enumerations + */ + enum { + BUTTON_DIAL = 100, + BUTTON_ANSWER, + BUTTON_NULLMODEM, + BUTTON_SETTINGS, + BUTTON_CANCEL, + + NUM_OF_BUTTONS = 5, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + int tabs[] = {77*RESFACTOR}; // tabs for player list box + GameType retval; // return value + + int selection; + bool pressed; + int curbutton; + TextButtonClass *buttons[NUM_OF_BUTTONS]; + + SerialSettingsType *settings; + bool selectsettings = false; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL_MODEM, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass answerbtn(BUTTON_ANSWER, TXT_ANSWER_MODEM, TPF_BUTTON, d_answer_x, d_answer_y, d_answer_w, d_answer_h); + TextButtonClass nullmodembtn(BUTTON_NULLMODEM, TXT_NULL_MODEM, TPF_BUTTON, d_nullmodem_x, d_nullmodem_y, d_nullmodem_w, d_nullmodem_h); + TextButtonClass settingsbtn(BUTTON_SETTINGS, TXT_SETTINGS, TPF_BUTTON, d_settings_x, d_settings_y, d_settings_w, d_settings_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + if (Session.SerialDefaults.Port == 0 || + Session.SerialDefaults.IRQ == -1 || + Session.SerialDefaults.Baud == -1) { + selectsettings = true; + + } else if ( NullModem.Detect_Port( &Session.SerialDefaults ) != PORT_VALID ) { + selectsettings = true; + } + + /* + ** Create the list + */ + commands = &dialbtn; + answerbtn.Add_Tail(*commands); + nullmodembtn.Add_Tail(*commands); + settingsbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Fill array of button ptrs + */ + curbutton = 0; + buttons[0] = &dialbtn; + buttons[1] = &answerbtn; + buttons[2] = &nullmodembtn; + buttons[3] = &settingsbtn; + buttons[4] = &cancelbtn; + buttons[curbutton]->Turn_On(); + + Keyboard->Clear(); + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Main Processing Loop + */ + display = REDRAW_ALL; + process = true; + pressed = false; + while (process) { + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /* + ** Refresh the backdrop + */ + Load_Title_Page(true); + /* + ** Draw the background + */ + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + /* + ** Draw the labels + */ + Draw_Caption (TXT_SELECT_SERIAL_GAME, d_dialog_x, d_dialog_y, d_dialog_w); + } + commands->Draw_All(); + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case (BUTTON_DIAL | KN_BUTTON): + selection = BUTTON_DIAL; + pressed = true; + break; + + case (BUTTON_ANSWER | KN_BUTTON): + selection = BUTTON_ANSWER; + pressed = true; + break; + + case (BUTTON_NULLMODEM | KN_BUTTON): + selection = BUTTON_NULLMODEM; + pressed = true; + break; + + case (BUTTON_SETTINGS | KN_BUTTON): + selection = BUTTON_SETTINGS; + pressed = true; + break; + + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + selection = BUTTON_CANCEL; + pressed = true; + break; + + case (KN_UP): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton--; + if (curbutton < 0) + curbutton = (NUM_OF_BUTTONS - 1); + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_DOWN): + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton++; + if (curbutton > (NUM_OF_BUTTONS - 1) ) + curbutton = 0; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_DIAL; + pressed = true; + break; + + default: + break; + } + + if (pressed) { + // + // to make sure the selection is correct in case they used the mouse + // + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + curbutton = selection - BUTTON_DIAL; + buttons[curbutton]->Turn_On(); + buttons[curbutton]->IsPressed = true; + buttons[curbutton]->Draw_Me(true); + + switch (selection) { + case (BUTTON_DIAL): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Remote-connect + */ + } else if ( Phone_Dialog() ) { + if (Session.PhoneBook[Session.CurPhoneIdx]->Settings.Port == 0) { + settings = &Session.SerialDefaults; + } else { + settings = &(Session.PhoneBook[Session.CurPhoneIdx]->Settings); + } +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + + if (settings->CallWaitStringIndex == CALL_WAIT_CUSTOM) { + strcpy( DialString, settings->CallWaitString ); + } else { + strcpy( DialString, + Session.CallWaitStrings[ settings->CallWaitStringIndex ] ); + } + strcat( DialString, Session.PhoneBook[ Session.CurPhoneIdx ]->Number ); + + if ( Dial_Modem( settings, false ) ) { + Session.ModemType = MODEM_DIALER; + if ( Com_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_ANSWER): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } else { + /* + ** Remote-connect + */ + settings = &Session.SerialDefaults; +#ifdef WIN32 + if (SerialPort) { + delete SerialPort; + } + SerialPort = new WinModemClass; +#endif //WIN32 + if ( Init_Null_Modem( settings ) ) { + if ( Answer_Modem( settings, false ) ) { + Session.ModemType = MODEM_ANSWERER; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_MODEM; + process = false; + } + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_NULLMODEM): + + if (selectsettings) { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + + /* + ** Otherwise, remote-connect; save values if we're recording + */ + } else if ( Init_Null_Modem( &Session.SerialDefaults ) ) { + rc = Test_Null_Modem(); + switch (rc) { + case (1): + Session.ModemType = MODEM_NULL_HOST; + if ( Com_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (2): + Session.ModemType = MODEM_NULL_JOIN; + if ( Com_Show_Scenario_Dialog() ) { + retval = GAME_NULL_MODEM; + process = false; + } + break; + + case (3): + WWMessageBox().Process( TXT_MODEM_OR_LOOPBACK ); + break; + } + + if (process) { // restore to default + NullModem.Change_IRQ_Priority( 0 ); + } + } else { + WWMessageBox().Process(TXT_SELECT_SETTINGS); + } + + if (process) { + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + } + + display = REDRAW_ALL; + break; + + case (BUTTON_SETTINGS): + if ( Com_Settings_Dialog( &Session.SerialDefaults ) ) { + Session.Write_MultiPlayer_Settings (); + + selectsettings = true; + + if (Session.SerialDefaults.Port != 0 && + Session.SerialDefaults.IRQ != -1 && + Session.SerialDefaults.Baud != -1) { + if ( NullModem.Detect_Port( &Session.SerialDefaults ) == PORT_VALID) { + selectsettings = false; + } + } + } + + buttons[curbutton]->IsPressed = false; + buttons[curbutton]->Flag_To_Redraw(); + display = REDRAW_ALL; + break; + + case (BUTTON_CANCEL): + retval = GAME_NORMAL; + process = false; + break; + } + + pressed = false; + } + } /* end of while */ + + return( retval ); +} + + + + + + +#ifdef WIN32 +/*********************************************************************************************** + * Advanced_Modem_Settings -- Allows to user to set additional modem settings * + * * + * * + * * + * INPUT: current settings * + * * + * OUTPUT: modified settings * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 12/16/96 2:29PM ST : Created * + *=============================================================================================*/ +Advanced_Modem_Settings (SerialSettingsType *settings) +{ + + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ +#if (FRENCH | GERMAN) + int d_dialog_w = 440; // dialog width +#else + int d_dialog_w = 340; // dialog width +#endif + + int d_dialog_h = 200; // dialog height + int d_dialog_x = 320 - d_dialog_w/2; // dialog x-coord + int d_dialog_y = 200 - d_dialog_h/ 4; // dialog y-coord + + + int d_compression_w = 50; + int d_compression_h = 18; + int d_compression_x = d_dialog_x + d_dialog_w/2 +40; + int d_compression_y = d_dialog_y + 40; + + int d_errorcorrection_w = 50; + int d_errorcorrection_h = 18; + int d_errorcorrection_x = d_dialog_x + d_dialog_w/2 +40; + int d_errorcorrection_y = d_dialog_y + 65; + + int d_hardwareflowcontrol_w = 50; + int d_hardwareflowcontrol_h = 18; + int d_hardwareflowcontrol_x = d_dialog_x + d_dialog_w/2 +40; + int d_hardwareflowcontrol_y = d_dialog_y + 90; + + int d_default_w = 100; + int d_default_h = 18; + int d_default_x = d_dialog_x + d_dialog_w / 2 - d_default_w / 2; + int d_default_y = d_dialog_y + d_dialog_h - 70; + + int d_ok_w = 100; + int d_ok_h = 18; + int d_ok_x = d_dialog_x + d_dialog_w/2 - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - 40; + + enum { + BUTTON_COMPRESSION = 100, + BUTTON_ERROR_CORRECTION, + BUTTON_HARDWARE_FLOW_CONTROL, + BUTTON_DEFAULT, + BUTTON_OK, + }; + + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND, + } RedrawType; + + /* + ** Yes/No strings + */ + char compress_text [16]; + char correction_text [16]; + char flowcontrol_text[16]; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Initialise the button text + */ + strcpy (compress_text, settings->Compression ? Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + + /* + ** Create the buttons + */ + TextButtonClass compressionbutton(BUTTON_COMPRESSION, compress_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_compression_x, d_compression_y, d_compression_w, d_compression_h); + + TextButtonClass errorcorrectionbutton(BUTTON_ERROR_CORRECTION, correction_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_errorcorrection_x, d_errorcorrection_y, d_errorcorrection_w, d_errorcorrection_h); + + TextButtonClass hardwareflowcontrolbutton(BUTTON_HARDWARE_FLOW_CONTROL, flowcontrol_text, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_hardwareflowcontrol_x, d_hardwareflowcontrol_y, d_hardwareflowcontrol_w, d_hardwareflowcontrol_h); + + TextButtonClass defaultbutton(BUTTON_DEFAULT, TXT_DEFAULT, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_default_x, d_default_y, d_default_w, d_default_h); + + TextButtonClass okbutton(BUTTON_OK, TXT_OK, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, + d_ok_x, d_ok_y, d_ok_w, d_ok_h); + + + /* + ** Misc. variables. + */ + RedrawType display = REDRAW_ALL; // redraw level + BOOL process = true; // process while true + KeyNumType input; + GadgetClass *commands; // button list + + + commands = &okbutton; + defaultbutton.Add_Tail(*commands); + compressionbutton.Add_Tail(*commands); + errorcorrectionbutton.Add_Tail(*commands); + hardwareflowcontrolbutton.Add_Tail(*commands); + + + /* + ** Main process loop + */ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored){ + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, scheme, TBLACK, TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_MODEM_INITIALISATION, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print( TXT_DATA_COMPRESSION, + d_compression_x - 26, d_compression_y + 2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_ERROR_CORRECTION, + d_errorcorrection_x - 26, d_errorcorrection_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + Fancy_Text_Print( TXT_HARDWARE_FLOW_CONTROL, + d_hardwareflowcontrol_x -26, d_hardwareflowcontrol_y +2, + scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + compressionbutton.Flag_To_Redraw(); + errorcorrectionbutton.Flag_To_Redraw(); + hardwareflowcontrolbutton.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_COMPRESSION | KN_BUTTON): + settings->Compression = settings->Compression ^ 1; + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_ERROR_CORRECTION | KN_BUTTON): + settings->ErrorCorrection = settings->ErrorCorrection ^ 1; + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_HARDWARE_FLOW_CONTROL | KN_BUTTON): + settings->HardwareFlowControl = settings->HardwareFlowControl ^ 1; + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_DEFAULT | KN_BUTTON): + settings->Compression = false; + settings->ErrorCorrection = false; + settings->HardwareFlowControl = true; + + strcpy (compress_text, settings->Compression ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (correction_text, settings->ErrorCorrection ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + strcpy (flowcontrol_text, settings->HardwareFlowControl ? + Text_String (TXT_ON) : Text_String (TXT_OFF) ); + + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + break; + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } +} +#endif //WIN32 + + + +/*************************************************************************** + * Com_Settings_Dialog -- Lets user select serial port settings * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Settings ³ * + * ³ ³ * + * ³ Port:____ IRQ:__ Baud:______ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ³ ³ ³ ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Initialization: [Add] [Delete] ³ * + * ³ _____________________________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ Call Waiting: ³ * + * ³ _______________ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ [Tone Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ [Pulse Dialing] ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ ³ * + * ³ [OK] [Cancel] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * settings ptr to SerialSettingsType structure * + * * + * OUTPUT: * + * true = OK, false = Cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Com_Settings_Dialog( SerialSettingsType *settings ) +{ + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 5 * RESFACTOR; // margin width/height + + int d_portlist_w = 80 * RESFACTOR + 80*(RESFACTOR-1); //Port list wider in hires + int d_portlist_h = 33 * RESFACTOR; + int d_portlist_x = d_dialog_x + (d_dialog_w / 6) - (d_portlist_w / 2); +#ifdef WIN32 + d_portlist_x = 0x45; +#endif + int d_portlist_y = d_dialog_y + ((d_margin + d_txt6_h) * 2) + d_margin + 10 * RESFACTOR; + +#ifdef WIN32 + int d_port_w = d_portlist_w; + int d_port_x = 0x45; +#else + int d_port_w = (5 * 6 * RESFACTOR) + 3 * RESFACTOR; + +// int d_port_x = d_portlist_x + 29 * RESFACTOR; +#ifdef FRENCH //VG2 + int d_port_x = (d_portlist_x + 29 * RESFACTOR) + 5; +#else + int d_port_x = d_portlist_x + 29 * RESFACTOR; +#endif + +#endif //WIN32 + int d_port_h = 9 * RESFACTOR; + int d_port_y = d_portlist_y - d_margin - d_txt6_h; + + int d_irqlist_w = 80 * RESFACTOR; + int d_irqlist_h = 33 * RESFACTOR; + int d_irqlist_x = d_dialog_x + (d_dialog_w / 2) - (d_irqlist_w / 2); + int d_irqlist_y = d_portlist_y; + + int d_irq_w = ((IRQBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_irq_h = 9 * RESFACTOR; + int d_irq_x = d_irqlist_x + 25 * RESFACTOR; + int d_irq_y = d_irqlist_y - d_margin - d_txt6_h; + + int d_baudlist_w = 80 * RESFACTOR; + int d_baudlist_h = 33 * RESFACTOR; + int d_baudlist_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_baudlist_w / 2); +#ifdef WIN32 + d_baudlist_x -= 32; +#endif + int d_baudlist_y = d_irqlist_y; + + int d_baud_w = ((BAUDBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_baud_h = 9 * RESFACTOR; + int d_baud_x = d_baudlist_x + 29 * RESFACTOR; + int d_baud_y = d_baudlist_y - d_margin - d_txt6_h; + + int d_initstrlist_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 8 * RESFACTOR + 3 * RESFACTOR; + int d_initstrlist_h = 21 * RESFACTOR; + int d_initstrlist_x = d_dialog_cx - (d_initstrlist_w / 2); + int d_initstrlist_y = d_portlist_y + d_portlist_h + ((d_margin + d_txt6_h) * 2) + d_margin + 4; + + int d_initstr_w = ((INITSTRBUF_MAX - 1) * 6 * RESFACTOR) + 3 * RESFACTOR; + int d_initstr_h = 9 * RESFACTOR; + int d_initstr_x = d_initstrlist_x; + int d_initstr_y = d_initstrlist_y - d_margin - d_txt6_h; + + int d_add_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_add_x = d_dialog_cx - (d_add_w / 2); +#else + int d_add_x = (d_dialog_cx - (d_add_w / 2)) + 30; +#endif + int d_add_h = 9 * RESFACTOR; + int d_add_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_delete_w = 45 * RESFACTOR; +#ifdef FRENCH + int d_delete_x = (d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2)) + 10; +#else + int d_delete_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_delete_w / 2); +#endif + int d_delete_h = 9 * RESFACTOR; + int d_delete_y = d_initstr_y - d_add_h - 3 * RESFACTOR; + + int d_cwaitstrlist_w = (((CWAITSTRBUF_MAX - 1) + 9) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstrlist_h = 27 * RESFACTOR; + int d_cwaitstrlist_x = d_initstrlist_x; + int d_cwaitstrlist_y = d_initstrlist_y + d_initstrlist_h + ((d_margin + d_txt6_h) * 2) + 2; + + int d_cwaitstr_w = ((CWAITSTRBUF_MAX - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_cwaitstr_h = 9 * RESFACTOR; + int d_cwaitstr_x = d_cwaitstrlist_x; + int d_cwaitstr_y = d_cwaitstrlist_y - d_margin - d_txt6_h; + + int d_tone_w = 80 * RESFACTOR; + int d_tone_h = 9 * RESFACTOR; + int d_tone_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_tone_w / 2); + int d_tone_y = d_cwaitstrlist_y; + + int d_pulse_w = 80 * RESFACTOR; + int d_pulse_h = 9 * RESFACTOR; + int d_pulse_x = d_dialog_x + ((d_dialog_w * 3) / 4) - (d_pulse_w / 2); + int d_pulse_y = d_tone_y + d_tone_h + d_margin; + +#ifdef FRENCH + int d_save_w = 80 * RESFACTOR; +#else + int d_save_w = 40 * RESFACTOR; +#endif + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_x + (d_dialog_w / 5) - (d_save_w / 2); + int d_save_y = d_dialog_y + d_dialog_h - d_save_h - d_margin - 4*RESFACTOR; + + int d_cancel_w = 50 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ((d_dialog_w * 4) / 5) - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin - 4*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_advanced_w = 50*RESFACTOR; +#else + int d_advanced_w = 40*RESFACTOR; +#endif + int d_advanced_h = 9*RESFACTOR; + int d_advanced_x = d_dialog_x + ((d_dialog_w) / 2) - (d_advanced_w / 2); + int d_advanced_y = d_dialog_y + d_dialog_h - d_advanced_h - d_margin - 4 *RESFACTOR; + + /* + ** Button Enumerations + */ + enum { + BUTTON_PORT = 100, + BUTTON_PORTLIST, + BUTTON_IRQ, + BUTTON_IRQLIST, + BUTTON_BAUD, + BUTTON_BAUDLIST, + BUTTON_INITSTR, + BUTTON_INITSTRLIST, + BUTTON_ADD, + BUTTON_DELETE, + BUTTON_CWAITSTR, + BUTTON_CWAITSTRLIST, + BUTTON_TONE, + BUTTON_PULSE, + BUTTON_SAVE, + BUTTON_ADVANCED, + BUTTON_CANCEL, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + static char *portname[4] = { + "COM1 - 3F8", + "COM2 - 2F8", + "COM3 - 3E8", + "COM4 - 2E8", + }; + + static char custom_port[10 + MODEM_NAME_MAX] = {"CUSTOM - ????"}; + + + +#ifndef WIN32 // No IRQ dialog in Win version + static char *irqname[5] = { + "2 / 9", + "3 - [COM2 & 4]", + "4 - [COM1 & 3]", + "5", + "CUSTOM - ??" + }; + + static int _irqidx[4] = { + 2, + 1, + 2, + 1 + }; + +#endif //WIN32 + +#ifdef WIN32 + static char *baudname[5] = { + "14400", + "19200", + "28800", + "38400", + "57600", + }; + + static char modemnames[10][MODEM_NAME_MAX]; + +#else //WIN32 + static char *baudname[5] = { + "9600", + "14400", + "19200", + "28800", + "38400" + }; +#endif //WIN32 + /* + ** Dialog variables + */ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + char * item; // general-purpose string + char * temp; // general-purpose string + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + char portbuf[ PORTBUF_MAX ] = {0}; // buffer for port + char irqbuf[ IRQBUF_MAX ] = {0}; // buffer for irq + char baudbuf[ BAUDBUF_MAX ] = {0}; // buffer for baud + char initstrbuf[ INITSTRBUF_MAX ] = {0}; // buffer for init string + char cwaitstrbuf[ CWAITSTRBUF_MAX ] = {0}; // buffer for call waiting string + + int port_index = 1; // index of currently-selected port (default = com2) + int port_custom_index = 4; //index of custom entry in port list + int irq_index = 1; // index of currently-selected irq (default = 3) + int baud_index = 1; // index of currently-selected baud (default = 19200) + int initstr_index = 0; // index of currently-selected modem init (default = "ATZ") + int cwaitstr_index = CALL_WAIT_CUSTOM; // index of currently-selected call waiting (default = "") + int rc = 0; // -1 = user cancelled, 1 = New + int i; // loop counter + int pos; + int len; + bool firsttime = true; + SerialSettingsType tempsettings; + DetectPortType dpstatus; + + /* + ** Buttons + */ + GadgetClass *commands; // button list + + EditClass port_edt (BUTTON_PORT, portbuf, PORTBUF_MAX, + TPF_TEXT, d_port_x, d_port_y, d_port_w, d_port_h, EditClass::ALPHANUMERIC); + + ListClass portlist(BUTTON_PORTLIST, d_portlist_x, d_portlist_y, d_portlist_w, d_portlist_h, + TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#ifndef WIN32 + EditClass irq_edt (BUTTON_IRQ, irqbuf, IRQBUF_MAX, TPF_TEXT, d_irq_x, d_irq_y, d_irq_w, d_irq_h, EditClass::NUMERIC); + + ListClass irqlist(BUTTON_IRQLIST, d_irqlist_x, d_irqlist_y, d_irqlist_w, d_irqlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); +#endif //WIN32 + + EditClass baud_edt (BUTTON_BAUD, baudbuf, BAUDBUF_MAX, TPF_TEXT, d_baud_x, d_baud_y, d_baud_w, d_baud_h, EditClass::NUMERIC); + ListClass baudlist(BUTTON_BAUDLIST, d_baudlist_x, d_baudlist_y, d_baudlist_w, d_baudlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + EditClass initstr_edt (BUTTON_INITSTR, initstrbuf, INITSTRBUF_MAX, TPF_TEXT, d_initstr_x, d_initstr_y, d_initstr_w, d_initstr_h, EditClass::ALPHANUMERIC); + ListClass initstrlist(BUTTON_INITSTRLIST, d_initstrlist_x, d_initstrlist_y, d_initstrlist_w, d_initstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + EditClass cwaitstr_edt (BUTTON_CWAITSTR, cwaitstrbuf, CWAITSTRBUF_MAX, TPF_TEXT, d_cwaitstr_x, d_cwaitstr_y, d_cwaitstr_w, d_cwaitstr_h, EditClass::ALPHANUMERIC); + ListClass cwaitstrlist(BUTTON_CWAITSTRLIST, d_cwaitstrlist_x, d_cwaitstrlist_y, d_cwaitstrlist_w, d_cwaitstrlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass tonebtn(BUTTON_TONE, TXT_TONE_BUTTON, TPF_BUTTON, d_tone_x, d_tone_y, d_tone_w, d_tone_h); + TextButtonClass pulsebtn(BUTTON_PULSE, TXT_PULSE_BUTTON, TPF_BUTTON, d_pulse_x, d_pulse_y, d_pulse_w, d_pulse_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#ifdef WIN32 + TextButtonClass advancedbutton(BUTTON_ADVANCED, TXT_ADVANCED, TPF_BUTTON, d_advanced_x, d_advanced_y, d_advanced_w, d_advanced_h); +#endif //WIN32 + /* + ** Various Inits + */ + memcpy( &tempsettings, settings, sizeof(SerialSettingsType) ); + + if (tempsettings.Port == 0) { + tempsettings.Port = 0x2f8; + } + + if (tempsettings.IRQ == -1) { + tempsettings.IRQ = 3; + } + + if (tempsettings.Baud == -1) { +#ifdef WIN32 + tempsettings.Baud = 19200; +#else //WIN32 + tempsettings.Baud = 9600; +#endif //WIN32 + } + + + /* + ** Set the current indices + */ +#ifndef WIN32 + switch ( tempsettings.IRQ ) { + case ( 2 ): + irq_index = 0; + strcpy (irqbuf, "2"); + break; + + case ( 3 ): + irq_index = 1; + strcpy (irqbuf, "3"); + break; + + case ( 4 ): + irq_index = 2; + strcpy (irqbuf, "4"); + break; + + case ( 5 ): + irq_index = 3; + strcpy (irqbuf, "5"); + break; + + default: + irq_index = 4; + sprintf (irqbuf, "%d", tempsettings.IRQ); + temp = strchr( irqname[4], '-' ); + if ( temp ) { + pos = (int)(temp - irqname[4]) + 2; + len = strlen( irqbuf ); + strncpy( irqname[4] + pos, irqbuf, len ); + *(irqname[4] + pos + len) = 0; + } + break; + } +#endif //WIN32 + +#ifdef WIN32 + if (tempsettings.Baud == 14400) { + baud_index = 0; + } else if (tempsettings.Baud == 19200) { + baud_index = 1; + } else if (tempsettings.Baud == 28800) { + baud_index = 2; + } else if (tempsettings.Baud == 38400) { + baud_index = 3; + } else { + baud_index = 4; + } +#else //WIN32 + if (tempsettings.Baud == 9600) { + baud_index = 0; + } else if (tempsettings.Baud == 14400) { + baud_index = 1; + } else if (tempsettings.Baud == 19200) { + baud_index = 2; + } else if (tempsettings.Baud == 28800) { + baud_index = 3; + } else { + baud_index = 4; + } +#endif //WIN32 + sprintf (baudbuf, "%d", tempsettings.Baud); + + /* + ** Set up the port list box & edit box + */ + for (i = 0; i < 4; i++) { + portlist.Add_Item( portname[ i ] ); + } + +#ifdef WIN32 + /* + ** Loop through the first 10 possible modem entries in the registry. Frankly, its just + ** tough luck if the user has more than 10 modems attached! + */ + if (ModemRegistry) { + delete ModemRegistry; + } + int modems_found = 0; + for (i=0 ; i<10 ; i++) { + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()) { + strncpy (modemnames[modems_found], ModemRegistry->Get_Modem_Name(), MODEM_NAME_MAX); + portlist.Add_Item( modemnames [modems_found++] ); + port_custom_index ++; + } + delete ModemRegistry; + } + ModemRegistry = NULL; + +#endif //WIN32 + + portlist.Add_Item ( custom_port ); + + + /* + ** Work out the current port index + */ + port_index = -1; +#ifdef WIN32 + if (tempsettings.ModemName[0]) { + for ( i=0 ; i= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /* + ** Dialog & Field labels + */ + Draw_Caption(TXT_SETTINGS, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(TXT_PORT_COLON, d_port_x - 3 * RESFACTOR, d_port_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#ifndef WIN32 + Fancy_Text_Print(TXT_IRQ_COLON, d_irq_x - 3 * RESFACTOR, d_irq_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); +#endif //WIN32 + Fancy_Text_Print(TXT_BAUD_COLON, d_baud_x - 3 * RESFACTOR, d_baud_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + + Fancy_Text_Print(TXT_INIT_STRING, d_initstr_x, d_initstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + Fancy_Text_Print(TXT_CWAIT_STRING, d_cwaitstr_x, d_cwaitstr_y - d_txt6_h - 3 * RESFACTOR, scheme, TBLACK, TPF_TEXT); + + } + + /* + ** Redraw buttons + */ + if (display >= REDRAW_BUTTONS) { + cancelbtn.Flag_To_Redraw(); + port_edt.Flag_To_Redraw(); + portlist.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Flag_To_Redraw(); + irqlist.Flag_To_Redraw(); +#endif //WIN32 + baud_edt.Flag_To_Redraw(); + baudlist.Flag_To_Redraw(); +#ifdef WIN32 + advancedbutton.Flag_To_Redraw(); +#endif //WIN32 + initstr_edt.Flag_To_Redraw(); + initstrlist.Flag_To_Redraw(); + addbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + cwaitstr_edt.Flag_To_Redraw(); + cwaitstrlist.Flag_To_Redraw(); + tonebtn.Flag_To_Redraw(); + pulsebtn.Flag_To_Redraw(); + savebtn.Flag_To_Redraw(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ** Get user input + */ + input = commands->Input(); + + if ( firsttime ) { +// port_edt.Set_Focus(); + port_edt.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ** Process input + */ + switch (input) { + +#ifdef WIN32 + case (BUTTON_ADVANCED | KN_BUTTON): + Advanced_Modem_Settings (&tempsettings); + display = REDRAW_ALL; + break; +#endif //WIN32 + + case (BUTTON_PORT | KN_BUTTON): + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( portbuf, item, PORTBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + port_edt.Flag_To_Redraw(); +#ifndef WIN32 + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); +#endif //WIN32 + } else { + strupr( portbuf ); + if ( stricmp(portbuf, "3F8") == 0 ) { + port_index = 0; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM1"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2F8") == 0 ) { + port_index = 1; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM2"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "3E8") == 0 ) { + port_index = 2; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM3"); + display = REDRAW_BUTTONS; + + } else if ( stricmp(portbuf, "2E8") == 0 ) { + port_index = 3; + portlist.Set_Selected_Index( port_index ); + strcpy (portbuf, "COM4"); + display = REDRAW_BUTTONS; + + } else if ( strncmp(portbuf, "COM", 3) == 0 ) { + display = REDRAW_BUTTONS; + + switch ( (portbuf[3] - '0') ) { + case 1: + port_index = 0; + break; + + case 2: + port_index = 1; + break; + + case 3: + port_index = 2; + break; + + case 4: + port_index = 3; + break; + + default: +#ifdef WIN32 + if (portbuf[3] <= '9' && portbuf[3] >'0') { + portbuf[4] = 0; + port_index = port_custom_index; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + break; + } +#endif //WIN32 + WWMessageBox().Process( TXT_INVALID_PORT_ADDRESS ); + port_edt.Set_Focus(); + display = REDRAW_ALL; + break; + } + + portlist.Set_Selected_Index( port_index ); + + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( portbuf ); + strncpy( item + pos, portbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } +#ifndef WIN32 + if (display == REDRAW_BUTTONS) { + irq_edt.Set_Focus(); + irq_edt.Flag_To_Redraw(); + } +#endif //WIN32 + } + break; + + case (BUTTON_PORTLIST | KN_BUTTON): + if (portlist.Current_Index() != port_index) { + port_index = portlist.Current_Index(); + item = (char *)portlist.Current_Item(); + if (port_index < 4) { + temp = strchr( item, ' ' ); + pos = (int)(temp - item); + strncpy( portbuf, item, pos ); + portbuf[pos] = 0; + port_edt.Clear_Focus(); + + // auto select the irq for port +#ifndef WIN32 + irq_index = _irqidx[ port_index ]; + irqlist.Set_Selected_Index( irq_index ); + item = (char *)irqlist.Current_Item(); + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, 2 ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); +#endif //WIN32 + } else { + if (port_index == port_custom_index) { + /* + ** This is the custom entry + */ + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + portbuf[0] = 0; + } else { + strncpy( portbuf, item + pos, PORTBUF_MAX ); + } + } + port_edt.Set_Focus(); + } else { + /* + ** Must be a modem name entry so just copy iy + */ + strncpy (portbuf, item, PORTBUF_MAX); + } + } + port_edt.Set_Text( portbuf, PORTBUF_MAX ); + } else if (port_index < port_custom_index) { + port_edt.Clear_Focus(); + } else { + port_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + +#ifndef WIN32 + case (BUTTON_IRQ | KN_BUTTON): + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + irq_edt.Flag_To_Redraw(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( irqbuf ); + strncpy( item + pos, irqbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + baud_edt.Set_Focus(); + baud_edt.Flag_To_Redraw(); + break; + + case (BUTTON_IRQLIST | KN_BUTTON): + if (irqlist.Current_Index() != irq_index) { + irq_index = irqlist.Current_Index(); + item = (char *)irqlist.Current_Item(); + if (irq_index < 4) { + temp = strchr( item, ' ' ); + if ( !temp ) { + strncpy( irqbuf, item, IRQBUF_MAX ); + } else { + pos = (int)(temp - item); + strncpy( irqbuf, item, pos ); + irqbuf[pos] = 0; + } + irq_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + if ( *(item + pos) == '?' ) { + irqbuf[0] = 0; + } else { + strncpy( irqbuf, item + pos, IRQBUF_MAX ); + } + } + irq_edt.Set_Focus(); + } + irq_edt.Set_Text( irqbuf, IRQBUF_MAX ); + } else if (irq_index < 4) { + irq_edt.Clear_Focus(); + } else { + irq_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; +#endif //WIN32 + case (BUTTON_BAUD | KN_BUTTON): + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + case (BUTTON_BAUDLIST | KN_BUTTON): + if (baudlist.Current_Index() != baud_index) { + baud_index = baudlist.Current_Index(); + item = (char *)baudlist.Current_Item(); + strncpy( baudbuf, item, BAUDBUF_MAX ); + baud_edt.Set_Text( baudbuf, BAUDBUF_MAX ); + baud_edt.Clear_Focus(); + display = REDRAW_BUTTONS; + } + break; + +#if 0 + case (BUTTON_INITSTR | KN_BUTTON): + strupr( initstrbuf ); + strncpy( Session.InitStrings[ initstr_index ], initstrbuf, INITSTRBUF_MAX ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + cwaitstr_edt.Set_Focus(); + cwaitstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; +#endif + + case (BUTTON_INITSTRLIST | KN_BUTTON): + if (initstrlist.Current_Index() != initstr_index) { + initstr_index = initstrlist.Current_Index(); + item = (char *)initstrlist.Current_Item(); + strncpy( initstrbuf, item, INITSTRBUF_MAX ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /* + ** Add a new InitString entry + */ + case (BUTTON_ADD | KN_BUTTON): + + item = new char[ INITSTRBUF_MAX ]; + memset (item, 0, INITSTRBUF_MAX); + + strupr ( initstrbuf ); + strncpy ( item, initstrbuf, INITSTRBUF_MAX-1 ); + + Session.InitStrings.Add ( item ); + Build_Init_String_Listbox (&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + if (item == Session.InitStrings[i]) { + initstr_index = i; + strcpy( initstrbuf, Session.InitStrings[ initstr_index ] ); + initstr_edt.Set_Text( initstrbuf, INITSTRBUF_MAX ); + initstrlist.Set_Selected_Index( initstr_index ); + } + } + initstr_edt.Set_Focus(); + initstr_edt.Flag_To_Redraw(); + display = REDRAW_BUTTONS; + break; + + /*------------------------------------------------------------------ + Delete the current InitString entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + if ( Session.InitStrings.Count() && initstr_index != -1) { + Session.InitStrings.Delete( initstr_index ); + Build_Init_String_Listbox(&initstrlist, &initstr_edt, initstrbuf, + &initstr_index); + } + break; + + case (BUTTON_CWAITSTR | KN_BUTTON): + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + len = strlen( cwaitstrbuf ); + strncpy( item + pos, cwaitstrbuf, len ); + *(item + pos + len) = 0; + display = REDRAW_BUTTONS; + } + } + break; + + case (BUTTON_CWAITSTRLIST | KN_BUTTON): + if (cwaitstrlist.Current_Index() != cwaitstr_index) { + cwaitstr_index = cwaitstrlist.Current_Index(); + item = (char *)cwaitstrlist.Current_Item(); + if (cwaitstr_index < 3) { + strncpy( cwaitstrbuf, item, CWAITSTRBUF_MAX ); + cwaitstr_edt.Clear_Focus(); + } else { + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } + cwaitstr_edt.Set_Focus(); + } + cwaitstr_edt.Set_Text( cwaitstrbuf, CWAITSTRBUF_MAX ); + } else if (cwaitstr_index < 3) { + cwaitstr_edt.Clear_Focus(); + } else { + cwaitstr_edt.Set_Focus(); + } + display = REDRAW_BUTTONS; + break; + + case (BUTTON_TONE | KN_BUTTON): + tempsettings.DialMethod = DIAL_TOUCH_TONE; + tonebtn.Turn_On(); + pulsebtn.Turn_Off(); + break; + + case (BUTTON_PULSE | KN_BUTTON): + tempsettings.DialMethod = DIAL_PULSE; + tonebtn.Turn_Off(); + pulsebtn.Turn_On(); + break; + + /*------------------------------------------------------------------ + SAVE: save the com settings + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + switch (port_index) { + case ( 0 ): + tempsettings.Port = 0x3f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 1 ): + tempsettings.Port = 0x2f8; + tempsettings.ModemName[0] = 0; + break; + + case ( 2 ): + tempsettings.Port = 0x3e8; + tempsettings.ModemName[0] = 0; + break; + + case ( 3 ): + tempsettings.Port = 0x2e8; + tempsettings.ModemName[0] = 0; + break; + + default: + if (port_index == port_custom_index) { +#ifdef WIN32 + strncpy ( tempsettings.ModemName, portbuf, MODEM_NAME_MAX ); + tempsettings.Port = 1; +#else //WIN32 + sscanf( portbuf, "%x", &tempsettings.Port ); + tempsettings.ModemName[0] = 0; +#endif //WIN32 + } else { + /* + ** Must be a modem name index + */ + strcpy (tempsettings.ModemName, portlist.Current_Item()); + tempsettings.Port = 1; + } + break; + } + + +#ifndef WIN32 + switch (irq_index) { + case ( 0 ): + tempsettings.IRQ = 2; + break; + + case ( 1 ): + tempsettings.IRQ = 3; + break; + + case ( 2 ): + tempsettings.IRQ = 4; + break; + + case ( 3 ): + tempsettings.IRQ = 5; + break; + + default: + sscanf( irqbuf, "%d", &tempsettings.IRQ ); + break; + } +#endif //WIN32 + sscanf( baudbuf, "%d", &tempsettings.Baud ); + + tempsettings.InitStringIndex = initstr_index; + tempsettings.CallWaitStringIndex = cwaitstr_index; + + item = Session.CallWaitStrings[ CALL_WAIT_CUSTOM ]; + temp = strchr( item, '-' ); + if ( temp ) { + pos = (int)(temp - item) + 2; + strncpy( cwaitstrbuf, item + pos, CWAITSTRBUF_MAX ); + } else { + cwaitstrbuf[ 0 ] = 0; + } + + strncpy( tempsettings.CallWaitString, cwaitstrbuf, CWAITSTRBUF_MAX ); + + dpstatus = NullModem.Detect_Port( &tempsettings ); + + if (dpstatus == PORT_VALID) { + process = false; + rc = true; + + } else if (dpstatus == PORT_INVALID) { +#ifdef WIN32 + WWMessageBox().Process( TXT_UNABLE_TO_OPEN_PORT ); +#else //WIN32 + WWMessageBox().Process( TXT_INVALID_SETTINGS ); +#endif //WIN32 + firsttime = true; + display = REDRAW_ALL; + + } else if (dpstatus == PORT_IRQ_INUSE) { + WWMessageBox().Process( TXT_IRQ_ALREADY_IN_USE ); + firsttime = true; + display = REDRAW_ALL; + } + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save values into the Settings structure + ------------------------------------------------------------------------*/ + if (rc) { + memcpy( settings, &tempsettings, sizeof(SerialSettingsType) ); + } + + return(rc); + +} /* end of Com_Settings_Dialog */ + + +/*************************************************************************** + * Build_Init_String_Listbox -- [re]builds the initstring listbox * + * * + * This routine rebuilds the initstring list box from scratch; it also * + * updates the contents of the initstring edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for initstring * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static void Build_Init_String_Listbox (ListClass *list, EditClass *edit, char *buf, int *index) +{ + int i, curidx; + char *item; + + + curidx = *index; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the init string list by name then number + */ + qsort ((void *)(&Session.InitStrings[0]), Session.InitStrings.Count(), sizeof(char *), Init_String_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.InitStrings.Count(); i++) { + item = new char[ INITSTRBUF_MAX ]; + strcpy( item, Session.InitStrings[i] ); + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || curidx < -1) { + curidx = -1; + } else if (curidx >= list->Count() ) { + curidx = 0; + } + + /*........................................................................ + Fill in initstring edit buffer + ........................................................................*/ + if (curidx > -1) { + strcpy (buf, Session.InitStrings[ curidx ]); + edit->Set_Text (buf, INITSTRBUF_MAX ); + list->Set_Selected_Index( curidx ); + } + + *index = curidx; +} + + +/*************************************************************************** + * Init_String_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/08/1995 DRD : Created. * + *=========================================================================*/ +static int Init_String_Compare (const void *p1, const void *p2) +{ + return( strcmp( *((char **)p1), *((char **)p2) ) ); +} + + +/*********************************************************************************************** + * Com_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. + * 01/21/97 V.Grippi added check for CS before sending scenario file * + *=============================================================================================*/ +int Com_Scenario_Dialog(bool skirmish) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 7 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin2 + d_txt6_h + 1 * RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; +#else + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; +#endif + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_y = d_name_y; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_margin1 + d_margin1 + 5*RESFACTOR; + int d_playerlist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR/*KO + d_txt6_h*/; + + int d_scenariolist_w = 162 * RESFACTOR; + int d_scenariolist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + + if (skirmish) { + d_scenariolist_h *= 2; + } + + + int d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_scenariolist_w - 5*RESFACTOR; + int d_scenariolist_y = d_color_y + d_color_h + d_margin2 + 2*RESFACTOR; + + if (skirmish) { + d_scenariolist_x = d_dialog_x + (d_dialog_w-d_scenariolist_w)/2; + } + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_count_y = d_playerlist_y + d_playerlist_h + (d_margin1 * 2) - 2 * RESFACTOR; + + if (skirmish) { + d_count_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + } + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_playerlist_x + (d_playerlist_w / 2) + 20 * RESFACTOR; // (fudged) + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25*RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_playerlist_x + (d_playerlist_w / 2) + 20*RESFACTOR; // (fudged) + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 106 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_dialog_x + d_dialog_w - 149 * RESFACTOR; + int d_options_y = d_scenariolist_y + d_scenariolist_h + d_margin1 - 2*RESFACTOR; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (8 * d_txt6_h) + 3 * RESFACTOR; // 4 rows high + int d_message_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + 2*RESFACTOR; + + int d_send_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_send_y = d_message_y + d_message_h; + + int d_ok_w = 45 * RESFACTOR; + int d_ok_h = 9 * RESFACTOR; + int d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - RESFACTOR*6; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - RESFACTOR*6; + + int d_load_w = 45 * RESFACTOR; + int d_load_h = 9 * RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - RESFACTOR*6; + + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else + BUTTON_HOUSE, +#endif + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_OPTIONS, + BUTTON_PLAYERLIST, + BUTTON_SCENARIOLIST, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_OK, + BUTTON_LOAD, + BUTTON_CANCEL, + BUTTON_DIFFICULTY, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int playertabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for player list box + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name + bool transmit; // 1 = re-transmit new game options + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = false; + int i; + unsigned long version; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + unsigned long theirresponsetime; + int packetlen; + static bool first_time = true; + bool oppscorescreen = false; + bool gameoptions = Session.Type == GAME_SKIRMISH; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + + CCFileClass loadfile ("SAVEGAME.NET"); + bool load_game = false; // 1 = load a saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + bool messages_have_focus = true; // Gadget focus starts on the message system + + Set_Logic_Page(SeenBuff); + + CDTimerClass kludge_timer; // Timer to allow a wait after client joins + // game before game can start + bool ok_button_added = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass * commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, d_load_w, d_load_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + SliderClass difficulty(BUTTON_DIFFICULTY, d_name_x, optionlist.Y + optionlist.Height + d_margin1 + d_margin1, d_dialog_w - (d_name_x-d_dialog_x)*2, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + difficulty.Set_Maximum(5); + difficulty.Set_Value(2); + } else { + difficulty.Set_Maximum(3); + difficulty.Set_Value(1); + } + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + if (!skirmish) { + playerlist.Add_Tail(*commands); + } else { + difficulty.Add_Tail(*commands); + } + scenariolist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); + if (Session.Type == GAME_SKIRMISH){ + okbtn.Add_Tail(*commands); + } + cancelbtn.Add_Tail(*commands); + if (!skirmish && loadfile.Is_Available()) { +#ifdef FIXIT_MULTI_SAVE + //Load button added only when other player has arrived + //loadbtn.Add_Tail(*commands); +#endif + } else { + cancelbtn.X = loadbtn.X; + } +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else + housebtn.Add_Tail(*commands); +#endif + + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init player name & house + ........................................................................*/ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif + + /*........................................................................ + Init scenario values, only the first time through + ........................................................................*/ + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; + if (first_time) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Special.IsShadowGrow = Rule.IsMPShadowGrow; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + first_time = false; + } + + /*........................................................................ + Init button states + ........................................................................*/ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(0); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + if (!skirmish) { + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + } + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsShadowGrow); + if (!skirmish) { + optionlist.Check_Item(4, Special.IsCaptureTheFlag); + } + + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + int maxp = Rule.MaxPlayers - 2; +// int maxp = Rule.MaxPlayers - (skirmish ? 1 : 2); + aiplayersgauge.Set_Maximum(maxp); + + if (skirmish) { + if ( Session.Options.AIPlayers > 7 ) { + Session.Options.AIPlayers = 7; + } + Session.Options.AIPlayers = max(Session.Options.AIPlayers, 1); + }else{ + if ( Session.Options.AIPlayers > 6 ) { + Session.Options.AIPlayers = 6; + } + } + + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + + /*........................................................................ + Init other scenario parameters + ........................................................................*/ + Special.IsTGrowth = //Session.Options.Tiberium; + Rule.IsTGrowth = //Session.Options.Tiberium; + Special.IsTSpread = //Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + transmit = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init scenario description list box + ........................................................................*/ + for (i = 0; i < Session.Scenarios.Count(); i++) { + for (int j = 0; EngMisStr[j] != NULL; j++) { + if (!strcmp(Session.Scenarios[i]->Description(), EngMisStr[j])) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). + // Add mission if it's available to us. + if( !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif +#if defined(GERMAN) || defined(FRENCH) + scenariolist.Add_Item(EngMisStr[j+1]); +#else + scenariolist.Add_Item(EngMisStr[j]); +#endif + break; + } + } + if (EngMisStr[j] == NULL) { +#ifdef FIXIT_CSII // ajw Added Aftermath installed checks (before, it was assumed). Added officialness check. + // Add mission if it's available to us. + if( !Session.Scenarios[i]->Get_Official() || + !( ( Is_Mission_Counterstrike((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Counterstrike_Installed() ) || + ( Is_Mission_Aftermath((char *)(Session.Scenarios[i]->Get_Filename())) && !Is_Aftermath_Installed() ) ) ) +#endif + scenariolist.Add_Item(Session.Scenarios[i]->Description()); + } + } + + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + /*........................................................................ + Init random-number generator, & create a seed to be used for all random + numbers from here on out + ........................................................................*/ +#ifdef FIXIT_RANDOM_GAME + srand(time(NULL)); + Seed = rand(); +#else +// randomize(); +// Seed = rand(); +#endif + + /*........................................................................ + Init the message display system + ........................................................................*/ + if (!skirmish) { + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 8, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1, d_send_y + 1, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + } + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + if (strlen(ModemRXString) > 36) + ModemRXString[36] = 0; + + if (strlen(ModemRXString) > 0) + Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + TPF_TEXT, -1); + + ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + } + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + if (!skirmish) { + NullModem.Reset_Response_Time(); // clear response time + } + theirresponsetime = 10000; // initialize to an invalid value + timingtime = lastmsgtime = lastredrawtime = TickCount; + + + /* + ** Those easily offended should avert their eyes from the following line. And whatever + ** you do, dont search for 'goto'. + */ +oh_dear_its_a_label: + + while (process) { + #if(SHOW_MONO) + if (!skirmish) { + NullModem.Mono_Debug_Print(0); + } + #endif + + if (!skirmish){ + if (!ok_button_added && gameoptions && kludge_timer == 0){ + okbtn.Add_Tail(*commands); + ok_button_added = true; +#ifdef FIXIT_VERSION_3 + if( loadfile.Is_Available() ) + { + loadbtn.Add_Tail( *commands ); + } +#else +#ifdef FIXIT_MULTI_SAVE + if ( loadfile.Is_Available() && PlayingAgainstVersion > VERSION_RED_ALERT_104 ) { + loadbtn.Add_Tail ( *commands ); + } +#endif +#endif + if (display < REDRAW_BUTTONS) display = REDRAW_BUTTONS; + } + } + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (!skirmish) { + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + if (!skirmish) { + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + Fancy_Text_Print(TXT_EASY, difficulty.X, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_TEXT); + Fancy_Text_Print(TXT_HARD, difficulty.X + difficulty.Width, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_RIGHT|TPF_TEXT); + Fancy_Text_Print(TXT_NORMAL, difficulty.X + difficulty.Width/2, difficulty.Y-8*RESFACTOR, scheme, TBLACK, TPF_CENTER|TPF_TEXT); + } + Fancy_Text_Print(TXT_SCENARIOS, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE && !skirmish) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) { + +// LogicPage->Fill_Rect(d_count_x + d_count_w + 2, d_count_y, d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); +// Fancy_Text_Print("%d ", d_count_x + d_count_w + 3 * RESFACTOR, d_count_y, scheme, BLACK, TPF_TEXT, Session.Options.UnitCount); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); +// Fancy_Text_Print(txt, d_level_x + d_level_w + 3 * RESFACTOR, d_level_y, scheme, BLACK, TPF_TEXT); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); +// Fancy_Text_Print("%d", d_credits_x + d_credits_w + 2 * RESFACTOR, d_credits_y, scheme, BLACK, TPF_TEXT, Session.Options.Credits); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); +// Fancy_Text_Print("%d", d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y, scheme, BLACK, TPF_TEXT, Session.Options.AIPlayers); + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (!skirmish) { + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + display = REDRAW_MESSAGE; + } + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + if (input & KN_BUTTON) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + + Session.PrefColor = (PlayerColorType)((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + Session.ColorIdx = Session.PrefColor; + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + + name_edt.Set_Color (&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx); + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + break; + + /*------------------------------------------------------------------ + User edits the name field; retransmit new game options + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + display = REDRAW_BACKGROUND; + transmit = true; + break; +#endif + + /*------------------------------------------------------------------ + New Scenario selected. + ------------------------------------------------------------------*/ +#ifdef FIXIT_VERSION_3 // All scenarios now allowable as downloads. ajw + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) + { + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; + +#else // FIXIT_VERSION_3 Whoever duplicated Netdlg into Nulldlg should be shot. Wasn't it enough? + + Abandon all hope ye who hit enter here. + + case (BUTTON_SCENARIOLIST | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + if (scenariolist.Current_Index() != Session.Options.ScenarioIndex) { +#ifdef FIXIT_CSII // checked - ajw + if ( !skirmish && (PlayingAgainstVersion != VERSION_RED_ALERT_107 && PlayingAgainstVersion != VERSION_RED_ALERT_108 && PlayingAgainstVersion < VERSION_AFTERMATH_CS) && +#else + if ( !skirmish && PlayingAgainstVersion < VERSION_RED_ALERT_107 && +#endif + Session.Scenarios[scenariolist.Current_Index()]->Get_Expansion()){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#ifdef FIXIT_CSII // checked - ajw + } else + if ( !skirmish && PlayingAgainstVersion < VERSION_AFTERMATH_CS && + Is_Mission_126x126((char *)Session.Scenarios[scenariolist.Current_Index()]->Get_Filename() ) ){ + scenariolist.Set_Selected_Index (Session.Options.ScenarioIndex); + Session.Messages.Add_Message(NULL, 0, + (char *)Text_String(TXT_NO_CS_SCENARIOS), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; +#endif + }else{ + Session.Options.ScenarioIndex = scenariolist.Current_Index(); + transmit = 1; + } + + strcpy (Session.Handle, namebuf); + transmit = true; + } + break; +#endif + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = countgauge.Get_Value() + SessionClass::CountMin[Session.Options.Bases]; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts build level + ------------------------------------------------------------------*/ + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = levelgauge.Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + /*------------------------------------------------------------------ + User adjusts max # units + ------------------------------------------------------------------*/ + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = creditsgauge.Get_Value(); + Session.Options.Credits = ((Session.Options.Credits + 250) / 500) * 500; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + transmit = true; + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + { + Session.Options.AIPlayers = aiplayersgauge.Get_Value(); + int humans = 2; // Two humans. + if (skirmish) { + Session.Options.AIPlayers += 1; // Always one forced AI player. + humans = 1; // One human. + // if (Session.Options.AIPlayers == 0) { + // Session.Options.AIPlayers = 1; + // aiplayersgauge.Set_Value(0); + // } + } + if (Session.Options.AIPlayers+humans >= Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - humans; + aiplayersgauge.Set_Value(Session.Options.AIPlayers - (skirmish ? 1 : 0)); + } + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + + break; + } + + //------------------------------------------------------------------ + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //------------------------------------------------------------------ + case (BUTTON_OPTIONS | KN_BUTTON): + if (!skirmish && Special.IsCaptureTheFlag != optionlist.Is_Checked(4) && !Special.IsCaptureTheFlag) { + optionlist.Check_Item(0, true); + } + if (Session.Options.Bases != optionlist.Is_Checked(0)) { + Session.Options.Bases = optionlist.Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + if (!skirmish) optionlist.Check_Item(4, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = optionlist.Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = optionlist.Is_Checked(2); + Special.IsShadowGrow = optionlist.Is_Checked(3); + if (!skirmish) { + Special.IsCaptureTheFlag = optionlist.Is_Checked(4); + } + + transmit = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + break; + + /*------------------------------------------------------------------ + OK: exit loop with true status + ------------------------------------------------------------------*/ + case (BUTTON_LOAD | KN_BUTTON): + case (BUTTON_OK | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + // + // make sure we got a game options packet from the other player + // + if (gameoptions) { + rc = true; + process = false; + + // force transmitting of game options packet one last time + + transmit = true; + transmittime = 0; + } else { + WWMessageBox().Process (TXT_ONLY_ONE,TXT_OOPS,NULL); + display = REDRAW_ALL; + } + if (input==(BUTTON_LOAD | KN_BUTTON)) + load_game = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (!skirmish) { + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + } + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } /* end of send message */ + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet. + This message requires an ACK. The first time through the loop, transmit + should be set, so we send out our default options; we'll then send + any changes we make to the defaults. + ---------------------------------------------------------------------*/ + if (skirmish) { + transmit = false; + } + + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ScenarioInfo.Credits = Session.Options.Credits; + SendPacket.ScenarioInfo.IsBases = Session.Options.Bases; + SendPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + SendPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + SendPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + SendPacket.ScenarioInfo.BuildLevel = BuildLevel; + SendPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + SendPacket.ScenarioInfo.Seed = Seed; + SendPacket.ScenarioInfo.Special = Special; + SendPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + SendPacket.ID = Session.ModemType; + + /* + ** Set up the scenario info so the remote player can match the scenario on his machine + ** or request a download if it doesnt exist + */ + strcpy (SendPacket.ScenarioInfo.Scenario, Session.Scenarios[Session.Options.ScenarioIndex]->Description()); + CCFileClass file (Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + SendPacket.ScenarioInfo.FileLength = file.Size(); + +#ifdef WOLAPI_INTEGRATION + strcpy( SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename() ); +#else + strncpy (SendPacket.ScenarioInfo.ShortFileName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename(), sizeof(SendPacket.ScenarioInfo.ShortFileName)); +#endif + strncpy ((char*)SendPacket.ScenarioInfo.FileDigest, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Digest(), sizeof SendPacket.ScenarioInfo.FileDigest); + SendPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official(); + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if (!skirmish && (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (!skirmish && NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + msg_timeout = 600; // reset timeout value to 10 seconds + // (only the 1st time through is 20 seconds) + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Sign-off: Give the other machine time to receive my ACK, display a + message, and exit. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while (TickCount - starttime < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store the other machine's name, color & house; + If they've picked the same color as myself, re-transmit my settings + to force him to choose a different color. (Com_Show_Scenario_Dialog + is responsible for ensuring the colors are different.) + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + kludge_timer = 2*60; + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + transmit = true; + + parms_received = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process (TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + } else { + + + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists + .........................................................*/ + if (playerlist.Count()==0) { + //...................................................... + // Add two strings to the player list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[TheirColor]); + } + + //......................................................... + // Ensure the player list has the latest, greatest copy of + // our names & colors. Do this every time we receive an + // options packet. + //......................................................... + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : TheirColor]; + + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + + break; + + // + // get their response time + // + case (SERIAL_TIMING): + oppscorescreen = false; + theirresponsetime = ReceivePacket.ScenarioInfo.ResponseTime; + + if ( !gameoptions ) { + + // retransmit of game options packet again + transmit = true; + } + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if (!skirmish && (TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + if (!skirmish) { + NullModem.Service(); + } + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = skirmish ? 1 : 2; + + Scen.Scenario = Session.Options.ScenarioIndex; + strcpy (Scen.ScenarioName, Session.Scenarios[Session.Options.ScenarioIndex]->Get_Filename()); + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + /* + ** Fetch the difficulty setting when in skirmish mode. + */ + if (skirmish) { + int diff = difficulty.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2); + switch (diff) { + case 0: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_EASY; + break; + + case 1: + Scen.CDifficulty = DIFF_HARD; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 2: + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 3: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_NORMAL; + break; + + case 4: + Scen.CDifficulty = DIFF_EASY; + Scen.Difficulty = DIFF_HARD; + break; + } + } else { + Scen.CDifficulty = DIFF_NORMAL; + Scen.Difficulty = DIFF_NORMAL; + } + + if (!skirmish) { + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + TheirName[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(TheirName, "2"); + } + } + #endif + + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + } + + /*..................................................................... + Send all players a GO packet. + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + if (load_game) { + SendPacket.Command = SERIAL_LOADGAME; + } else { + SendPacket.Command = SERIAL_GO; + } + + if (!skirmish) { + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + if ( theirresponsetime == 10000 ) { + ; + } else if (SendPacket.ScenarioInfo.ResponseTime < theirresponsetime) { + SendPacket.ScenarioInfo.ResponseTime = theirresponsetime; + } + } + + // + // calculated one way delay for a packet and overall delay to execute + // a packet + // + if (!skirmish) { + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((SendPacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) + ); + } else { + Session.MaxAhead = max( (SendPacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + SendPacket.ID = Session.ModemType; + + if (!skirmish) { + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + /* + ** Wait for the go response. This will be either a 'GO' reply, a + ** request for the scenario to be sent or a reply to say that the scenario + ** cant be played. + */ +#ifdef WIN32 + WWDebugString ("RA95 - About to wait for 'GO' response.\n"); +#endif + do { + NullModem.Service(); + + if (NullModem.Get_Message (&ReceivePacket, &packetlen) > 0) { + +#ifdef FIXIT_VERSION_3 + if (ReceivePacket.Command == SERIAL_READY_TO_GO) + { + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } + break; + } +#else + if (ReceivePacket.Command == SERIAL_READY_TO_GO) break; +#endif + + if (ReceivePacket.Command == SERIAL_NO_SCENARIO) { + WWMessageBox().Process(TXT_NO_EXPANSION_SCENARIO, TXT_CANCEL); + /* + ** We have to recover from this somehow so.... + */ + process = true; + display = REDRAW_ALL; + lastmsgtime = TickCount; + goto oh_dear_its_a_label; + } + + if (ReceivePacket.Command == SERIAL_REQ_SCENARIO) { +#ifdef WIN32 + WWDebugString ("RA95 - About to call 'Send_Remote_File'.\n"); + +#endif + +#ifdef FIXIT_VERSION_3 + if( Session.Scenarios[Session.Options.ScenarioIndex]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + Emergency_Exit(EXIT_FAILURE); + } +#endif + + Send_Remote_File (Scen.ScenarioName, 0); + + break; + } + } + + } while ( !Keyboard->Check() ); + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + } + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + if (!skirmish) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + } + + //if (!skirmish) Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clear all lists + ------------------------------------------------------------------------*/ + while (scenariolist.Count()) { + scenariolist.Remove_Item(scenariolist.Get_Item(0)); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings(); + } + + if (load_game && !skirmish) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); +} + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Find_Local_Scenario -- finds the file name of the scenario with matching attributes * + * * + * * + * * + * INPUT: ptr to Scenario description * + * ptr to Scenario filename to fix up * + * length of file for trivial rejection of scenario files * + * ptr to digest. Digests must match. * + * * + * * + * OUTPUT: true if scenario is available locally * + * * + * WARNINGS: We need to reject files that don't match exactly because scenarios with the same * + * description can exist on both machines but have different contents. For example * + * there will be lots of scenarios called 'my map' and 'aaaaaa'. * + * * + * HISTORY: * + * 8/23/96 12:36PM ST : Created * + *=============================================================================================*/ +bool Find_Local_Scenario (char *description, char *filename, unsigned int length, char *digest, bool official) +{ +//FILE *fp; +//fp = fopen("findscen.txt","wt"); +//debugprint("looking for local scenario: description = %s, name=%s, length=%d, digest=%s, official=%d\n", description, filename, length, digest, official); + + char digest_buffer[32]; + /* + ** Scan through the scenario list looking for scenarios with matching descriptions. + */ + for (int index = 0; index < Session.Scenarios.Count(); index++) { + +//debugprint( "Checking against scenario: %s\n", Session.Scenarios[index]->Description()); + if (!strcmp (Session.Scenarios[index]->Description(), description)) { +//debugprint("found matching description.\n"); + CCFileClass file (Session.Scenarios[index]->Get_Filename()); + + /* + ** Possible rejection on the basis of availability. + */ + if (file.Is_Available()) { +//debugprint("file is available.\n"); + /* + ** Possible rejection on the basis of size. + */ + if (file.Size() == length) { +//debugprint("length matches.\n"); + /* + ** We don't know the digest for 'official' scenarios so assume its correct + */ + if (!official) { +//debugprint("!official.\n"); + /* + ** Possible rejection on the basis of digest + */ + INIClass ini; + ini.Load(file); + ini.Get_String ("Digest", "1", "No digest here mate. Nope.", digest_buffer, sizeof (digest_buffer) ); + } +//debugprint("digest = %s, digest_buffer = %s.\n", digest, digest_buffer); +#ifdef FIXIT_CSII // checked - ajw 9/28/98. But don't know why this happens. Because of autodownload? + /* + ** If this is an aftermath scenario then ignore the digest and return success. + */ + if ( Is_Mission_Aftermath ((char*)Session.Scenarios[index]->Get_Filename()) ) { +//debugprint("a 1match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } +#endif + + /* + ** This must be the same scenario. Copy the name and return true. + */ + if (official || !strcmp (digest, digest_buffer)) { +//debugprint("a match!\n"); + strcpy (filename, Session.Scenarios[index]->Get_Filename()); + return (true); + } + } + } +// else +// debugprint("file not available '%s'.\n", Session.Scenarios[index]->Get_Filename()); + + } + } +//debugprint("failed match.\n"); + /* + ** Couldnt find the scenario locally. Return failure. + */ + return (false); +} + + + + + + +/*********************************************************************************************** + * Com_Show_Scenario_Dialog -- Serial game scenario selection dialog * + * * + * The 'Players' vector is filled in by this routine, when the game starts; this * + * is for the Assign_Houses routine, which expects this vector to contain all * + * players' names & houses & colors. Other than that, the Players vector, Games * + * vector, and Chat vector aren't used at all by this routine. The Game & Players * + * list boxes are filled in manually in the processing loop. * + * * + * ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ * + * ³ Serial Game ³ * + * ³ ³ * + * ³ Your Name: __________ ³ * + * ³ House: [GDI] [NOD] ³ * + * ³ Desired Color: [ ][ ][ ][ ] ³ * + * ³ ³ * + * ³ Opponent: Name ³ * + * ³ Scenario: Description ³ * + * ³ Credits: xxxx ³ * + * ³ Bases: ON ³ * + * ³ Crates: ON ³ * + * ³ Tiberium: ON ³ * + * ³ Ghosts: ON ³ * + * ³ ³ * + * ³ [Cancel] ³ * + * ³ ³ * + * ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ * + * ³ ³ ³ ³ * + * ³ ³ ³ ³ * + * ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ³ * + * ³ [Send Message] ³ * + * ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = success, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=============================================================================================*/ +int Com_Show_Scenario_Dialog(void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 320 * RESFACTOR; // dialog width + int d_dialog_h = 200 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 * RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 * RESFACTOR; // margin width/height + int d_margin2 = 2 * RESFACTOR; // margin width/height + + int d_name_w = 70 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (d_dialog_w / 4) - (d_name_w / 2); + int d_name_y = d_dialog_y + d_margin1 + d_margin2 + d_txt6_h + 1*RESFACTOR; + +#ifdef OLDWAY + int d_gdi_w = 40 * RESFACTOR; + int d_gdi_h = 9 * RESFACTOR; + int d_gdi_x = d_dialog_cx - d_gdi_w; + int d_gdi_y = d_name_y; + + int d_nod_w = 40 * RESFACTOR; + int d_nod_h = 9 * RESFACTOR; + int d_nod_x = d_dialog_cx; + int d_nod_y = d_name_y; + +#else //OLDWAY + + int d_house_w = 60 *RESFACTOR; + int d_house_h = (8 * 5 *RESFACTOR); + int d_house_x = d_dialog_cx - (d_house_w / 2); + int d_house_y = d_name_y; + +#endif //OLDWAY + + int d_color_w = 10 * RESFACTOR; + int d_color_h = 9 * RESFACTOR; + int d_color_x = d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + int d_color_y = d_name_y; + + int d_scenario_y = d_name_y + d_name_h + d_margin2; + + int d_gamelist_w = 160 * RESFACTOR; + int d_gamelist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_gamelist_x = d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_gamelist_y = d_scenario_y + d_txt6_h + d_margin2 + d_txt6_h + d_margin2; + +//BG int d_playerlist_w = 112 * RESFACTOR; + int d_playerlist_w = 118 * RESFACTOR; + int d_playerlist_h = (6 * 6 * RESFACTOR) + 3 * RESFACTOR; // 6 rows high + int d_playerlist_x = d_dialog_x + d_dialog_w - d_margin1 - d_margin1 - d_playerlist_w - 5*RESFACTOR; + int d_playerlist_y = d_gamelist_y; + + int d_count_w = 25 * RESFACTOR; + int d_count_h = d_txt6_h; + int d_count_x = d_gamelist_x + (d_gamelist_w / 2); + int d_count_y = d_gamelist_y + d_gamelist_h + (d_margin1 * 2) - d_margin2; + + int d_level_w = 25 * RESFACTOR; + int d_level_h = d_txt6_h; + int d_level_x = d_gamelist_x + (d_gamelist_w / 2); + int d_level_y = d_count_y + d_count_h; + + int d_credits_w = 25 * RESFACTOR; + int d_credits_h = d_txt6_h; + int d_credits_x = d_gamelist_x + (d_gamelist_w / 2); + int d_credits_y = d_level_y + d_level_h; + + int d_aiplayers_w = 25 * RESFACTOR; + int d_aiplayers_h = d_txt6_h; + int d_aiplayers_x = d_gamelist_x + (d_gamelist_w / 2); + int d_aiplayers_y = d_credits_y + d_credits_h; + + int d_options_w = 112 * RESFACTOR; + int d_options_h = (5 * 6* RESFACTOR) + 4*RESFACTOR; + int d_options_x = d_playerlist_x; + int d_options_y = d_playerlist_y + d_playerlist_h + d_margin1 - d_margin2; + + int d_message_w = d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + int d_message_h = (7 * d_txt6_h) + 3 * RESFACTOR; // 7 rows high + int d_message_x = d_gamelist_x;//d_dialog_x + d_margin1 + 10*RESFACTOR; + int d_message_y = d_options_y + d_options_h + d_margin2/*KO + d_margin1*/; + + int d_send_w = d_message_w; + int d_send_h = 9 * RESFACTOR; + int d_send_x = d_message_x; + int d_send_y = d_message_y + d_message_h; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx - (d_cancel_w / 2); + int d_cancel_y = d_send_y + d_send_h/*KO + d_margin2*/; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, +#ifdef OLDWAY + BUTTON_GDI, + BUTTON_NOD, +#else //OLDWAY + BUTTON_HOUSE, +#endif //OLDWAY + BUTTON_GAMELIST, + BUTTON_PLAYERLIST, + BUTTON_CANCEL, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AI_PLAYERS, + BUTTON_OPTIONS, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7) + }; + + char namebuf[MPLAYER_NAME_MAX] = {0}; // buffer for player's name +//BG int playertabs[] = {77}; // tabs for player list box + int playertabs[] = {71*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8}; // tabs for options list box + bool transmit; // 1 = re-transmit new game options + bool first; // 1 = no packets received yet + bool parms_received = false; // 1 = game options received + bool changed = false; // 1 = user has changed an option + + int rc; + int recsignedoff = 0; + int i; + unsigned long version; + char txt[80]; + unsigned long starttime; + unsigned long timingtime; + unsigned long lastmsgtime; + unsigned long lastredrawtime; + unsigned long transmittime = 0; + int packetlen; + bool oppscorescreen = false; + bool gameoptions = false; + EventClass *event; // event ptr + unsigned long msg_timeout = 1200; // init to 20 seconds + bool load_game = false; // 1 = load saved game + NodeNameType *who; // node to add to Players + char *item; // for filling in lists + char *p; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + Session.Options.ScenarioDescription[0] = 0; //Flag that we dont know the scenario name yet + bool messages_have_focus = true; + bool ready_packet_was_sent = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass name_edt(BUTTON_NAME, namebuf, MPLAYER_NAME_MAX, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); +#ifdef OLDWAY + TextButtonClass gdibtn(BUTTON_GDI, TXT_ALLIES, TPF_BUTTON, d_gdi_x, d_gdi_y, d_gdi_w, d_gdi_h); + TextButtonClass nodbtn(BUTTON_NOD, TXT_SOVIET, TPF_BUTTON, d_nod_x, d_nod_y, d_nod_w, d_nod_h); +#else //OLDWAY + char housetext[25] = ""; + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP")); +#endif //OLDWAY + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w); + ListClass gamelist(BUTTON_GAMELIST, d_gamelist_x, d_gamelist_y, d_gamelist_w, d_gamelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + ColorListClass playerlist(BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + GaugeClass countgauge(BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h); + char staticcountbuff[35]; + StaticButtonClass staticcount(0, " ", TPF_TEXT, d_count_x+d_count_w+3*RESFACTOR, d_count_y); + + GaugeClass levelgauge(BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h); + char staticlevelbuff[35]; + StaticButtonClass staticlevel(0, " ", TPF_TEXT, d_level_x+d_level_w+3*RESFACTOR, d_level_y); + + GaugeClass creditsgauge(BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h); + char staticcreditsbuff[35]; + StaticButtonClass staticcredits(0, " ", TPF_TEXT, d_credits_x+d_credits_w+3*RESFACTOR, d_credits_y); + + GaugeClass aiplayersgauge(BUTTON_AI_PLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h); + char staticaibuff[35]; + StaticButtonClass staticai(0, " ", TPF_TEXT, d_aiplayers_x+d_aiplayers_w+3*RESFACTOR, d_aiplayers_y); + + CheckListClass optionlist(BUTTON_OPTIONS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &name_edt; + staticcount.Add_Tail(*commands); + staticcredits.Add_Tail(*commands); + staticai.Add_Tail(*commands); + staticlevel.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + gamelist.Add_Tail(*commands); + playerlist.Add_Tail(*commands); + countgauge.Add_Tail(*commands); + levelgauge.Add_Tail(*commands); + creditsgauge.Add_Tail(*commands); + aiplayersgauge.Add_Tail(*commands); + optionlist.Add_Tail(*commands); +#ifdef OLDWAY + gdibtn.Add_Tail(*commands); + nodbtn.Add_Tail(*commands); +#else //OLDWAY + housebtn.Add_Tail(*commands); +#endif //OLDWAY + + + //------------------------------------------------------------------------ + // Init the button states + //------------------------------------------------------------------------ + //........................................................................ + // Name & Color + //........................................................................ + Session.ColorIdx = Session.PrefColor; // init my preferred color + strcpy (namebuf, Session.Handle); // set my name + name_edt.Set_Text(namebuf,MPLAYER_NAME_MAX); + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + + //........................................................................ + // List boxes + //........................................................................ + playerlist.Set_Tabs(playertabs); + playerlist.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + optionlist.Set_Tabs(optiontabs); + optionlist.Set_Read_Only(1); + + optionlist.Add_Item(Text_String(TXT_BASES)); + optionlist.Add_Item(Text_String(TXT_ORE_SPREADS)); + optionlist.Add_Item(Text_String(TXT_CRATES)); + optionlist.Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + optionlist.Add_Item(Text_String(TXT_SHADOW_REGROWS)); + + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + + //........................................................................ + // House buttons + //........................................................................ +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + gdibtn.Turn_On(); + } else { + nodbtn.Turn_On(); + } +#else //OLDWAY + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + housebtn.Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + housebtn.Set_Selected_Index(Session.House - HOUSE_USSR); + housebtn.Set_Read_Only (true); +#endif //OLDWAY + + //........................................................................ + // Option gauges + //........................................................................ + countgauge.Use_Thumb(0); + countgauge.Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + levelgauge.Use_Thumb(0); + levelgauge.Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + levelgauge.Set_Value(BuildLevel - 1); + + creditsgauge.Use_Thumb(0); + creditsgauge.Set_Maximum(Rule.MPMaxMoney); + creditsgauge.Set_Value(Session.Options.Credits); + + aiplayersgauge.Use_Thumb(0); + aiplayersgauge.Set_Maximum(Rule.MaxPlayers-2); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + transmit = true; + first = true; + + /*........................................................................ + Clear the Players vector + ........................................................................*/ + Clear_Vector(&Session.Players); + + /*........................................................................ + Init the message display system + ........................................................................*/ + Session.Messages.Init (d_message_x + 1, d_message_y + 1, 7, + MAX_MESSAGE_LENGTH, d_txt6_h, d_send_x + 1 * RESFACTOR, d_send_y + 1 * RESFACTOR, 1, + 20, MAX_MESSAGE_LENGTH - 5, d_message_w); + Session.Messages.Add_Edit ((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + Session.WWChat = 0; + + /*........................................................................ + Init version number clipping system + ........................................................................*/ + VerNum.Init_Clipping(); + Load_Title_Page(true); + CCPalette.Set(); + + extern char ModemRXString[]; + + //if (strlen(ModemRXString) > 36) + // ModemRXString[36] = 0; + // + //if (strlen(ModemRXString) > 0) + // Session.Messages.Add_Message (NULL, 0, ModemRXString, PCOLOR_BROWN, + // TPF_TEXT, -1); + // + //ModemRXString[0] = '\0'; + + /* + ---------------------------- Init Mono Output ---------------------------- + */ + #if(SHOW_MONO) + NullModem.Configure_Debug(sizeof (CommHeaderType),sizeof (SerialCommandType), + SerialPacketNames, 100, 8); + NullModem.Mono_Debug_Print(1); + #endif + + /* + ---------------------------- Processing loop ----------------------------- + */ + NullModem.Reset_Response_Time(); // clear response time + timingtime = lastmsgtime = lastredrawtime = TickCount; + + bool process = true; // process while true + while (process) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + /* + ** Kludge to make sure we redraw the message input line when it loses focus. + ** If we dont do this then the cursor doesnt disappear. + */ + if (messages_have_focus) { + if (name_edt.Has_Focus()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + messages_have_focus = false; + } + } else { + if (!name_edt.Has_Focus()) { + messages_have_focus = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + Session.Messages.Set_Edit_Focus(); + } + } + + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print(TXT_CHANNEL_GAMES, d_gamelist_x + (d_gamelist_w / 2), d_gamelist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_YOUR_NAME, d_name_x + (d_name_w / 2), d_name_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#ifdef OLDWAY + Fancy_Text_Print(TXT_SIDE_COLON, d_gdi_x + d_gdi_w, d_gdi_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#else + Fancy_Text_Print(TXT_SIDE_COLON, d_house_x + (d_house_w / 2), d_house_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); +#endif + Fancy_Text_Print(TXT_COLOR_COLON, d_dialog_x + ((d_dialog_w / 4) * 3), d_color_y - d_txt6_h, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + Fancy_Text_Print (TXT_COUNT, d_count_x - 2 * RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_LEVEL, d_level_x - 2 * RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_CREDITS_COLON, d_credits_x - 2 * RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print (TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2 * RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + + } + + /*.................................................................. + Draw the color boxes + ..................................................................*/ + if (display >= REDRAW_COLORS) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1 * RESFACTOR, d_color_y + 1 * RESFACTOR, + cbox_x[i] + 1 * RESFACTOR + d_color_w - 2 * RESFACTOR, d_color_y + 1 * RESFACTOR + d_color_h - 2 * RESFACTOR, + ColorRemaps[i].Box); +// (i == PCOLOR_DIALOG_BLUE) ? ColorRemaps[PCOLOR_REALLY_BLUE].Box : ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, + BOXSTYLE_RAISED, false); + } + } + } + + /*.................................................................. + Draw the message system; erase old messages first + ..................................................................*/ + if (display >= REDRAW_MESSAGE) { + Draw_Box(d_message_x, d_message_y, d_message_w, d_message_h, + BOXSTYLE_BOX, true); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + + //.................................................................. + // Redraw the game options + //.................................................................. + if (display >= REDRAW_PARMS && parms_received) { + if (oppscorescreen) { + sprintf(txt,"%s",Text_String(TXT_WAITING_FOR_OPPONENT)); + + int txtwidth = String_Pixel_Width( txt ); + + Fancy_Text_Print(txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + } else { + /*............................................................ + Scenario description + ............................................................*/ + //LogicPage->Fill_Rect(d_dialog_x + 16*RESFACTOR, d_scenario_y, + // d_dialog_x + d_dialog_w - 16*RESFACTOR, d_scenario_y + d_txt6_h, BLACK); + + p = (char *)Text_String(TXT_SCENARIO_COLON); + if (Session.Options.ScenarioDescription[0]) { +// sprintf(txt,"%s %s",p, Session.Options.ScenarioDescription); +// Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + // EW - Scenario language translation goes here!!!!!!!! VG + for (i = 0; EngMisStr[i] != NULL; i++) { + if (!strcmp(Session.Options.ScenarioDescription, EngMisStr[i])) { +#if defined(GERMAN) || defined(FRENCH) + sprintf(txt, "%s %s", p, EngMisStr[i+1]); +#else + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); +#endif + break; + } + } + if (EngMisStr[i] == NULL) { + sprintf(txt, "%s %s", p, Session.Options.ScenarioDescription); + } + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + + } else { + sprintf(txt,"%s %s",p,Text_String(TXT_NOT_FOUND)); + + Fancy_Text_Print (txt, d_dialog_cx, d_scenario_y, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_TEXT | TPF_CENTER); + } + + //......................................................... + // Unit count, tech level, credits + //......................................................... + //LogicPage->Fill_Rect(d_count_x + d_count_w + 2 * RESFACTOR, d_count_y, + // d_count_x + d_count_w + 35 * RESFACTOR, d_aiplayers_y + d_aiplayers_h+RESFACTOR, + // BLACK); + + sprintf(staticcountbuff, "%d", Session.Options.UnitCount); + staticcount.Set_Text(staticcountbuff); + staticcount.Draw_Me(); + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(staticlevelbuff, "%d ", BuildLevel); + } else { + sprintf(staticlevelbuff, "**"); + } + staticlevel.Set_Text(staticlevelbuff); + staticlevel.Draw_Me(); + + sprintf(staticcreditsbuff, "%d", Session.Options.Credits); + staticcredits.Set_Text(staticcreditsbuff); + staticcredits.Draw_Me(); + + sprintf(staticaibuff, "%d", Session.Options.AIPlayers); + staticai.Set_Text(staticaibuff); + staticai.Draw_Me(); + } + } + } + + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + messages_have_focus = Session.Messages.Has_Edit_Focus(); + bool droplist_is_dropped = housebtn.IsDropped; + KeyNumType input = commands->Input(); + + /* + ** Sort out the input focus between the name edit box and the message system + */ + if (messages_have_focus) { + if (!name_edt.Has_Focus()) { + Session.Messages.Set_Edit_Focus(); + } else { + messages_have_focus = false; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + } + + /* + ** Redraw everything if the droplist collapsed + */ + if (droplist_is_dropped && !housebtn.IsDropped) { + display = REDRAW_BACKGROUND; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + User clicks on a color button + ------------------------------------------------------------------*/ + case KN_LMOUSE: + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) { + /*......................................................... + Compute my preferred color as the one I clicked on. + .........................................................*/ + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + changed = true; + + /*......................................................... + If 'TheirColor' is set to the other player's color, make + sure we can't pick that color. + .........................................................*/ + if (parms_received) { + if (Session.PrefColor == TheirColor) + break; + } + Session.ColorIdx = Session.PrefColor; + + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + Session.Messages.Set_Edit_Color((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + strcpy (Session.Handle, namebuf); + transmit = true; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } else if ( (Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h) || + (Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h) ) { + Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + Sound_Effect(VOC_SYS_ERROR); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + break; + +#ifdef OLDWAY + /*------------------------------------------------------------------ + House Buttons: set the player's desired House + ------------------------------------------------------------------*/ + case (BUTTON_GDI | KN_BUTTON): + Session.House = HOUSE_GOOD; + gdibtn.Turn_On(); + nodbtn.Turn_Off(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; + + case (BUTTON_NOD | KN_BUTTON): + Session.House = HOUSE_BAD; + gdibtn.Turn_Off(); + nodbtn.Turn_On(); + strcpy (Session.Handle, namebuf); + transmit = true; + break; +#else //OLDWAY + case (BUTTON_HOUSE | KN_BUTTON): + Session.House = HousesType(housebtn.Current_Index()+HOUSE_USSR); + strcpy (Session.Handle, namebuf); + transmit = true; + //display = REDRAW_BACKGROUND; + break; +#endif //OLDWAY + + /*------------------------------------------------------------------ + User edits the name value; retransmit + ------------------------------------------------------------------*/ + case (BUTTON_NAME | KN_BUTTON): + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + break; + + /*------------------------------------------------------------------ + CANCEL: send a SIGN_OFF, bail out with error code + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Default: manage the inter-player messages + ------------------------------------------------------------------*/ + default: + if (Session.Messages.Manage()) { + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + + /*............................................................... + Service keyboard input for any message being edited. + ...............................................................*/ + i = Session.Messages.Input(input); + + /*............................................................... + If 'Input' returned 1, it means refresh the message display. + (We have to redraw the edit line, to erase the cursor.) + ...............................................................*/ + if (i==1) { + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==2) { + /*............................................................... + If 'Input' returned 2, it means redraw the message display. + Rather than setting 'display', which would redraw all msgs, + we only need to erase & redraw the edit box here. + ...............................................................*/ + Hide_Mouse(); + Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, + BOXSTYLE_BOX, true); + Session.Messages.Draw(); + Show_Mouse(); + } else if (i==3 || i==4) { + /*............................................................... + If 'input' returned 3, it means send the current message. + ...............................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_MESSAGE; + strcpy (SendPacket.Name, namebuf); + SendPacket.ID = Session.ColorIdx; + if (i==3) { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Edit_Buf()); + } else { + strcpy (SendPacket.Message.Message, Session.Messages.Get_Overflow_Buf()); + Session.Messages.Clear_Overflow_Buf(); + } + + /*.................................................................. + Send the message + ..................................................................*/ + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + NullModem.Service(); + + /*.................................................................. + Add the message to our own screen + ..................................................................*/ + Session.Messages.Add_Message (SendPacket.Name, SendPacket.ID, + SendPacket.Message.Message, + (Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, -1); + Session.Messages.Add_Edit((Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx, + TPF_TEXT, NULL, '_', d_message_w); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + } + break; + } + + /*--------------------------------------------------------------------- + Detect editing of the name buffer, transmit new values to players + ---------------------------------------------------------------------*/ + if (strcmp (namebuf, Session.Handle)) { + strcpy (Session.Handle, namebuf); + transmit = true; + changed = true; + } + + /*--------------------------------------------------------------------- + If our Transmit flag is set, we need to send out a game option packet + ---------------------------------------------------------------------*/ + if (transmit && (TickCount - transmittime) > PACKET_RETRANS_TIME) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_GAME_OPTIONS; + strcpy (SendPacket.Name, namebuf); + SendPacket.ScenarioInfo.CheatCheck = RuleINI.Get_Unique_ID(); + SendPacket.ScenarioInfo.MinVersion = VerNum.Min_Version(); + SendPacket.ScenarioInfo.MaxVersion = VerNum.Max_Version(); + SendPacket.ScenarioInfo.House = Session.House; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + transmittime = TickCount; + transmit = false; + + //.................................................................. + // Keep the player list up to date + //.................................................................. + if (playerlist.Count()) { + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + playerlist.Flag_To_Redraw(); + } + + //.................................................................. + // Play a little sound effect + //.................................................................. + Sound_Effect(VOC_OPTIONS_CHANGED); + } + + // + // send a timing packet if enough time has gone by. + // + if ((TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_TIMING; + SendPacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + SendPacket.ID = Session.ModemType; + + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 0); + timingtime = TickCount; + } + + /*--------------------------------------------------------------------- + Check for an incoming message + ---------------------------------------------------------------------*/ + if (NullModem.Get_Message(&ReceivePacket, &packetlen) > 0) { + + lastmsgtime = TickCount; + + msg_timeout = 600; + + // are we getting our own packets back?? + + if (ReceivePacket.Command >= SERIAL_CONNECT && + ReceivePacket.Command < SERIAL_LAST_COMMAND && + ReceivePacket.Command != SERIAL_MESSAGE && + ReceivePacket.ID == Session.ModemType) { + + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + break; + } + + event = (EventClass *)&ReceivePacket; + if (event->Type <= EventClass::FRAMEINFO) { + if ( (TickCount - lastredrawtime) > PACKET_REDRAW_TIME) { + lastredrawtime = TickCount; + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + } + } else { + switch ( ReceivePacket.Command ) { + /*.................................................................. + Other system signs off: Give it time to receive my ACK, then show + a message. + ..................................................................*/ + case (SERIAL_SIGN_OFF): + starttime = TickCount; + while ( (TickCount - starttime) < 60) + NullModem.Service(); + WWMessageBox().Process(TXT_USER_SIGNED_OFF); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + recsignedoff = true; + break; + + /*.................................................................. + Game Options: Store all options; check my color & game version. + ..................................................................*/ + case (SERIAL_GAME_OPTIONS): + oppscorescreen = false; + gameoptions = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + + strcpy (TheirName, ReceivePacket.Name); + TheirColor = ReceivePacket.ScenarioInfo.Color; + TheirHouse = ReceivePacket.ScenarioInfo.House; + + /*............................................................... + Make sure I don't have the same color as the other guy. + ...............................................................*/ + if (Session.ColorIdx == TheirColor) { + + // force transmitting of game options packet + + transmit = true; + transmittime = 0; + + Session.ColorIdx = (PlayerColorType)(TheirColor + 1); + if (Session.ColorIdx >= 6) { + Session.ColorIdx = PCOLOR_FIRST; + } + name_edt.Set_Color(&ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : Session.ColorIdx]); + name_edt.Flag_To_Redraw(); + if (display < REDRAW_COLORS) display = REDRAW_COLORS; + if (housebtn.IsDropped) { + housebtn.Collapse(); + display = REDRAW_BACKGROUND; + } + } + + /*............................................................... + Save scenario settings. + ...............................................................*/ + Session.Options.Credits = ReceivePacket.ScenarioInfo.Credits; + Session.Options.Bases = ReceivePacket.ScenarioInfo.IsBases; + Session.Options.Tiberium = ReceivePacket.ScenarioInfo.IsTiberium; + Session.Options.Goodies = ReceivePacket.ScenarioInfo.IsGoodies; + Session.Options.AIPlayers = ReceivePacket.ScenarioInfo.AIPlayers; + BuildLevel = ReceivePacket.ScenarioInfo.BuildLevel; + Session.Options.UnitCount = ReceivePacket.ScenarioInfo.UnitCount; + Seed = ReceivePacket.ScenarioInfo.Seed; + Special = ReceivePacket.ScenarioInfo.Special; + Options.GameSpeed = ReceivePacket.ScenarioInfo.GameSpeed; + + if (Session.Options.Tiberium) { + Special.IsTGrowth = true; + Rule.IsTGrowth = true; + Special.IsTSpread = true; + Rule.IsTSpread = true; + } else { + Special.IsTGrowth = false; + Rule.IsTGrowth = false; + Special.IsTSpread = false; + Rule.IsTSpread = false; + } + + //......................................................... + // Adjust the gauges + //......................................................... + countgauge.Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + countgauge.Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + levelgauge.Set_Value(BuildLevel - 1); + creditsgauge.Set_Value(Session.Options.Credits); + aiplayersgauge.Set_Value(Session.Options.AIPlayers); + + //......................................................... + // Update the options list box + //......................................................... + optionlist.Check_Item(0, Session.Options.Bases); + optionlist.Check_Item(1, Session.Options.Tiberium); + optionlist.Check_Item(2, Session.Options.Goodies); + optionlist.Check_Item(3, Special.IsCaptureTheFlag); + optionlist.Check_Item(4, Special.IsShadowGrow); + optionlist.Flag_To_Redraw(); + + /* + ** If the scenario name changed then we need to redraw the whole lot. + */ + if (strcmp (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario)) { + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + + /*............................................................... + Copy the information about the scenario that the host wants to + play so ee can request this scenario from the host if we don't + have it locally. + ...............................................................*/ + strcpy (Session.Options.ScenarioDescription, ReceivePacket.ScenarioInfo.Scenario); + strcpy (Session.ScenarioFileName, ReceivePacket.ScenarioInfo.ShortFileName); +#ifdef WOLAPI_INTEGRATION + strncpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest, sizeof( ReceivePacket.ScenarioInfo.FileDigest )); +#else + strcpy (Session.ScenarioDigest, (char*)ReceivePacket.ScenarioInfo.FileDigest); +#endif + Session.ScenarioIsOfficial = ReceivePacket.ScenarioInfo.OfficialScenario; + Session.ScenarioFileLength = ReceivePacket.ScenarioInfo.FileLength; + + //......................................................... + // "Clip" the other system's version range to our own + // ........................................................ + version = VerNum.Clip_Version(ReceivePacket.ScenarioInfo.MinVersion, + ReceivePacket.ScenarioInfo.MaxVersion); + // ........................................................ + // If the greatest-common-version comes back 0, the other + // system's range is too low for ours + // ........................................................ + if (version == 0) { + WWMessageBox().Process (TXT_DESTGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else if (version == 0xffffffff) { + // ........................................................ + // If the greatest-common-version comes back 0xffffffff, + // the other system's range is too high for ours + // ........................................................ + WWMessageBox().Process (TXT_YOURGAME_OUTDATED); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } + // ........................................................ + // Otherwise, 'version' is the highest version we have in + // common; look up the protocol that goes with this version. + // ........................................................ + else { + + if (ReceivePacket.ScenarioInfo.CheatCheck != RuleINI.Get_Unique_ID()) { + WWMessageBox().Process(TXT_MISMATCH); + + // to skip the other system not responding msg + lastmsgtime = TickCount; + + process = false; + rc = false; + } else { + + Session.CommProtocol = VerNum.Version_Protocol(version); +#ifndef FIXIT_VERSION_3 + PlayingAgainstVersion = version; +#endif + } + } + + /*......................................................... + If this is the first game-options packet we've received, + init the game & player lists, then transmit our options + to him. + .........................................................*/ + if (first) { + //...................................................... + // Add a string to the game list, and two to the player + // list + //...................................................... + item = new char [MPLAYER_NAME_MAX + 64]; + gamelist.Add_Item(item); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]); + item = new char [MPLAYER_NAME_MAX + 64]; //Need room to display country name + playerlist.Add_Item(item, + &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]); + + first = false; + transmit = true; + transmittime = 0; + } + + //......................................................... + // Ensure the game list & player list have the latest, + // greatest copy of our names & colors. Do this every time + // we receive an options packet. + //......................................................... + item = (char *)gamelist.Get_Item(0); + sprintf(item,Text_String(TXT_THATGUYS_GAME), + TheirName); + item = (char *)playerlist.Get_Item(0); +#ifdef OLDWAY + if (Session.House==HOUSE_GOOD) { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",namebuf, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", namebuf, Text_String(HouseTypeClass::As_Reference(Session.House).Full_Name())); + +#endif //OLDWAY + playerlist.Colors[0] = &ColorRemaps[(Session.ColorIdx == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : Session.ColorIdx]; + + item = (char *)playerlist.Get_Item(1); +#ifdef OLDWAY + if (TheirHouse==HOUSE_GOOD) { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_ALLIES)); + } else { + sprintf(item,"%s\t%s",TheirName, + Text_String(TXT_SOVIET)); + } +#else //OLDWAY + sprintf (item, "%s\t%s", TheirName, Text_String(HouseTypeClass::As_Reference(TheirHouse).Full_Name())); +#endif //OLDWAY + playerlist.Colors[1] = &ColorRemaps[(TheirColor == PCOLOR_DIALOG_BLUE) ? PCOLOR_REALLY_BLUE : TheirColor]; + + gamelist.Flag_To_Redraw(); + playerlist.Flag_To_Redraw(); + + //......................................................... + // Play a little sound effect + //......................................................... + Sound_Effect(VOC_OPTIONS_CHANGED); + + break; + + /*.................................................................. + GO: Exit this routine with a success code. + ..................................................................*/ + case (SERIAL_LOADGAME): + load_game = true; + case (SERIAL_GO): + + ready_packet_was_sent = false; + + if (!load_game){ + /* + ** Special new kludge for counterstrike. + ** + ** Find local scenario will fail to match a counterstrike mission + ** unless the CS CD is in the drive. So.... + ** + ** If Counterstrike is installed and this is an official map and + ** the file name matches a counterstrike map then tell the host + ** that I have the scenario so he can continue while we make + ** sure the local user has the Counterstrike CD in the drive. + ** + */ + // This is duplicated for Aftermath scenarios. ajw + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Session.ScenarioIsOfficial && + ( (Expansion_CS_Present() && Is_Mission_Counterstrike(Session.ScenarioFileName)) || + (Expansion_AM_Present() && Is_Mission_Aftermath(Session.ScenarioFileName)) ) ) { +#else + if ( Expansion_CS_Present() && Session.ScenarioIsOfficial ) { +#endif + + CCFileClass check_file ( Session.ScenarioFileName ); + if ( !check_file.Is_Available() ) { + + int current_drive = CCFileClass::Get_CD_Drive(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + int index = Get_CD_Index(current_drive, 1*60); + bool needcd = false; + if (Is_Mission_Counterstrike(Session.ScenarioFileName)) { + if (index != 2 && index != 3) { + RequiredCD = 2; + needcd = true; + } + } + if (Is_Mission_Aftermath(Session.ScenarioFileName)) { + if (index != 3) { + RequiredCD = 3; + needcd = true; + } + } + if (needcd) { +#else + if ( Get_CD_Index(current_drive, 1*60) != 2 ){ + RequiredCD = 2; +#endif + #ifdef WIN32 + WWDebugString ("RA95 - Counterstrike CD is not in drive\n"); + #endif + + /* + ** We should have the scenario but the wrong disk is in. + ** Tell the host that I am ready to go anyway. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + ready_packet_was_sent = true; + + if (!Force_CD_Available (RequiredCD)){ + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + + /* + ** Make sure we dont time out because of the disk swap + */ + lastmsgtime = TickCount; + } + } + } + + /* + ** If the scenario that the host wants to play doesnt exist locally then we + ** need to request that it is sent. If we can identify the scenario locally then + ** we need to fix up the file name so we load the right one. + */ + if (Find_Local_Scenario (Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial)) { + + /* + ** We have the scenario. Tell the host that I am ready to go. + */ + if (!ready_packet_was_sent) { + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + } else { +#ifndef FIXIT_VERSION_3 // Removed restriction on downloading official maps. + /* + ** Oh dear. Thats a scenario I dont have. Request that the host sends the + ** scenario to me provided it isnt an official Westwood scenario. + ** + ** If the file is received OK then we will get a true return value and the + ** actual file name to load will be in Session.ScenarioFileName + */ + if (Session.ScenarioIsOfficial) { + /* + ** We dont have the scenario and we dont want to request that it gets + ** sent because its an official one. + ** Print up a message saying we cant play this scenario and reply to the + ** host, telling him to select another. + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_NO_SCENARIO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + NullModem.Service(); + WWMessageBox().Process (TXT_UNABLE_PLAY_WAAUGH); + display = REDRAW_ALL; + lastmsgtime = TickCount; + break; + } else { +#endif +#ifdef FIXIT_VERSION_3 + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + break; +#endif + if (!Get_Scenario_File_From_Host(Session.ScenarioFileName, 0)) { + rc = false; + break; + } else { + /* + ** Make sure we dont time-out because of the download + */ + lastmsgtime = TickCount; + } +#ifndef FIXIT_VERSION_3 + } +#endif + } + }else{ + /* + ** Make sure we respond to the host in a load game + */ + memset ((void*)&SendPacket, 0, sizeof (SendPacket)); + SendPacket.Command = SERIAL_READY_TO_GO; + NullModem.Send_Message (&SendPacket, sizeof (SendPacket), 1); + starttime = TickCount; + + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + NullModem.Service(); + } + } + + /* + ** Fall through here... + */ + strcpy (Scen.ScenarioName, Session.ScenarioFileName); + // + // calculated one way delay for a packet and overall delay + // to execute a packet + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Session.MaxAhead = max( ((((ReceivePacket.ScenarioInfo.ResponseTime / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } else { + Session.MaxAhead = max( (ReceivePacket.ScenarioInfo.ResponseTime / 8), + MODEM_MIN_MAX_AHEAD ); + } + + process = false; + rc = true; + if (ReceivePacket.Command == SERIAL_LOADGAME) + load_game = true; + break; + + /*.................................................................. + Incoming message: add to our list + ..................................................................*/ + case (SERIAL_MESSAGE): + oppscorescreen = false; + + Session.Messages.Add_Message (ReceivePacket.Name, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + ReceivePacket.Message.Message, + ((PlayerColorType)ReceivePacket.ID == PCOLOR_DIALOG_BLUE) ? + PCOLOR_REALLY_BLUE : (PlayerColorType)ReceivePacket.ID, + TPF_TEXT, -1); + + Sound_Effect(VOC_INCOMING_MESSAGE); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + + // + // throw away timing packet + // + case (SERIAL_TIMING): + oppscorescreen = false; + break; + + // + // print msg waiting for opponent + // + case (SERIAL_SCORE_SCREEN): + oppscorescreen = true; + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + parms_received = true; + break; + + default: + break; + } + } + } + + // if we haven't received a msg for 10 seconds exit + + if ((TickCount - lastmsgtime) > msg_timeout) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + process = false; + rc = false; + + // say we did receive sign off to keep from sending one + recsignedoff = true; + } + + /*--------------------------------------------------------------------- + Service the connection + ---------------------------------------------------------------------*/ + NullModem.Service(); + } + + /*------------------------------------------------------------------------ + Prepare to load the scenario + ------------------------------------------------------------------------*/ + if (rc) { + Session.NumPlayers = 2; + + /*..................................................................... + Add both players to the Players vector; the local system is always + index 0. + .....................................................................*/ + who = new NodeNameType; + + #ifdef FIXIT_MODEM_LOAD_CRASH + /* If the names of the players are the same then we MUST force them + * be be unique. This is necessary to prevent a crash after loading + * a modem save game. + */ + if (strcmp(TheirName, namebuf) == 0) + { + if (strlen(TheirName) == (MPLAYER_NAME_MAX - 1)) + { + namebuf[MPLAYER_NAME_MAX - 1] = '\0'; + } + else + { + strcat(namebuf, "2"); + } + } + #endif + + strcpy(who->Name, namebuf); + who->Player.House = Session.House; + who->Player.Color = Session.ColorIdx; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + who = new NodeNameType; + strcpy(who->Name, TheirName); + who->Player.House = TheirHouse; + who->Player.Color = TheirColor; + who->Player.ProcessTime = -1; + Session.Players.Add (who); + + starttime = TickCount; + while ( ( NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_SENDING_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + NullModem.Service(); + } + + // clear queue to keep from doing any resends + NullModem.Init_Send_Queue(); + + } else { + if ( !recsignedoff ) { + /*..................................................................... + Broadcast my sign-off over my network + .....................................................................*/ + memset (&SendPacket, 0, sizeof(SerialPacketType)); + SendPacket.Command = SERIAL_SIGN_OFF; + SendPacket.ScenarioInfo.Color = Session.ColorIdx; // use Color for ID + SendPacket.ID = Session.ModemType; + NullModem.Send_Message (&SendPacket, sizeof(SendPacket), 1); + + starttime = TickCount; + while ( (NullModem.Num_Send() + && ((TickCount - starttime) < PACKET_CANCEL_TIMEOUT) ) + || ((TickCount - starttime) < 60) ) { + #if(SHOW_MONO) + NullModem.Mono_Debug_Print(0); + #endif + + if ( NullModem.Get_Message( &ReceivePacket, &packetlen ) > 0) { + + // are we getting our own packets back?? + + if (ReceivePacket.Command == SERIAL_SIGN_OFF + && ReceivePacket.ID == Session.ModemType) { + + // exit while + break; + } + } + + NullModem.Service(); + } + } + + // Shutdown_Modem(); + } + + /*------------------------------------------------------------------------ + Clean up the list boxes + ------------------------------------------------------------------------*/ +// while (optionlist.Count()>0) { +// item = (char *)optionlist.Get_Item(0); +// delete [] item; +// optionlist.Remove_Item(item); +// } + while (gamelist.Count()>0) { + item = (char *)gamelist.Get_Item(0); + delete [] item; + gamelist.Remove_Item(item); + } + while (playerlist.Count()>0) { + item = (char *)playerlist.Get_Item(0); + delete [] item; + playerlist.Remove_Item(item); + } + + /*------------------------------------------------------------------------ + Remove the chat edit box + ------------------------------------------------------------------------*/ + Session.Messages.Remove_Edit(); + + /*------------------------------------------------------------------------ + Restore screen + ------------------------------------------------------------------------*/ + Hide_Mouse(); + Load_Title_Page(true); +//#ifdef WIN32 +// Load_Uncompress(CCFileClass("TITLE.CPS"), SysMemPage, SysMemPage, CCPalette); +// SysMemPage.Scale(SeenPage); +//#else +// Load_Uncompress(CCFileClass("TITLE.CPS"), HidPage, HidPage, CCPalette); +// HidPage.Blit(SeenPage); +//#endif + Show_Mouse(); + + /*------------------------------------------------------------------------ + Save any changes made to our options + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + if (load_game) { + if (!Load_Game (-1)) { + WWMessageBox().Process (TXT_ERROR_LOADING_GAME); + rc = false; + } + Frame++; + } + + return(rc); + +} /* end of Com_Show_Scenario_Dialog */ + + +/*************************************************************************** + * Phone_Dialog -- Lets user edit phone directory & dial * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = dial the current phone book entry, false = cancel. * + * * + * WARNINGS: * + * Serial options must have been read from CC.INI. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Phone_Dialog (void) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 280 * RESFACTOR; // dialog width + int d_dialog_h = 160 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_phonelist_w = 248 * RESFACTOR; + int d_phonelist_h = 87 * RESFACTOR; + int d_phonelist_x = d_dialog_cx - (d_phonelist_w / 2); + int d_phonelist_y = d_dialog_y + d_margin + d_txt6_h + 11 * RESFACTOR; + + int d_add_w = 45 * RESFACTOR; + int d_add_h = 9 * RESFACTOR; + int d_add_x = d_dialog_cx - (d_add_w / 2) - d_margin - d_add_w; + int d_add_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_edit_w = 45 * RESFACTOR; + int d_edit_h = 9 * RESFACTOR; + int d_edit_x = d_dialog_cx - (d_edit_w / 2); + int d_edit_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_delete_w = 45 * RESFACTOR; + int d_delete_h = 9 * RESFACTOR; + int d_delete_x = d_dialog_cx + (d_delete_w / 2) + d_margin; + int d_delete_y = d_phonelist_y + d_phonelist_h + d_margin; + + int d_numedit_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_numedit_h = 9 * RESFACTOR; + int d_numedit_x = d_dialog_cx - (d_numedit_w / 2); + int d_numedit_y = d_add_y + d_add_h + d_margin; + + int d_dial_w = 45 * RESFACTOR; + int d_dial_h = 9 * RESFACTOR; + int d_dial_x = d_dialog_cx - (d_numedit_w / 2) - d_margin - d_dial_w; + int d_dial_y = d_add_y + d_add_h + d_margin; + + int d_cancel_w = 45 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + (d_numedit_w / 2) + d_margin; + int d_cancel_y = d_add_y + d_add_h + d_margin; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_PHONELIST = 100, + BUTTON_ADD, + BUTTON_EDIT, + BUTTON_DELETE, + BUTTON_DIAL, + BUTTON_CANCEL, + BUTTON_NUMEDIT, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + char phone_num[ PhoneEntryClass::PHONE_MAX_NUM ] = { 0 }; // buffer for editing phone # + int rc; + int i; + int tabs[] = {123*RESFACTOR, 207*RESFACTOR}; // tabs for list box + char *item; // for removing items from list box + PhoneEntryClass *p_entry; // for creating / editing phonebook entries + bool changed = false; // 1 = save changes to INI file + bool firsttime = false; + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + ListClass phonelist(BUTTON_PHONELIST, d_phonelist_x, d_phonelist_y, d_phonelist_w, d_phonelist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + TextButtonClass addbtn(BUTTON_ADD, TXT_ADD, TPF_BUTTON, d_add_x, d_add_y, d_add_w, d_add_h); + TextButtonClass editbtn(BUTTON_EDIT, TXT_EDIT, TPF_BUTTON, d_edit_x, d_edit_y, d_edit_w, d_edit_h); + TextButtonClass deletebtn(BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w, d_delete_h); + TextButtonClass dialbtn(BUTTON_DIAL, TXT_DIAL, TPF_BUTTON, d_dial_x, d_dial_y, d_dial_w, d_dial_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + EditClass numedit (BUTTON_NUMEDIT, phone_num, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_numedit_x, d_numedit_y, d_numedit_w, d_numedit_h, EditClass::ALPHANUMERIC); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &phonelist; + addbtn.Add_Tail(*commands); + editbtn.Add_Tail(*commands); + deletebtn.Add_Tail(*commands); + dialbtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + numedit.Add_Tail(*commands); + dialbtn.Turn_On(); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Fill in the phone directory list box + ........................................................................*/ + phonelist.Set_Tabs(tabs); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + firsttime = true; + } + + /* + ---------------------------- Processing loop ----------------------------- + */ + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + // init font variables + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_CENTER | TPF_TEXT); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Draw_Caption (TXT_PHONE_LIST, d_dialog_x, d_dialog_y, d_dialog_w); + phonelist.Draw_Me (true); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + addbtn.Flag_To_Redraw(); + editbtn.Flag_To_Redraw(); + deletebtn.Flag_To_Redraw(); + dialbtn.Flag_To_Redraw(); + cancelbtn.Flag_To_Redraw(); + numedit.Flag_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + input = commands->Input(); + + if ( firsttime ) { + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + /*------------------------------------------------------------------ + New phone listing selected. + ------------------------------------------------------------------*/ + case (BUTTON_PHONELIST | KN_BUTTON): + /*............................................................... + Detect a change in the selected item; update CurPhoneIdx, and + the edit box buffer. + ...............................................................*/ + #ifdef FIXIT_PHONELIST_CRASH + if (Session.CurPhoneIdx != -1) { + #endif + if (phonelist.Current_Index() != Session.CurPhoneIdx) { + Session.CurPhoneIdx = phonelist.Current_Index(); + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + changed = true; + } + #ifdef FIXIT_PHONELIST_CRASH + } + #endif + break; + + /*------------------------------------------------------------------ + Add a new entry + ------------------------------------------------------------------*/ + case (BUTTON_ADD | KN_BUTTON): + + /*............................................................... + Allocate a new phone book entry + ...............................................................*/ + p_entry = new PhoneEntryClass(); + p_entry->Name[0] = 0; + p_entry->Number[0] = 0; + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + /*............................................................... + Invoke the entry editor; if user clicks Save, add the new entry + to the list, and rebuild the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox( &phonelist, &numedit, phone_num ); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } else { + + /*............................................................... + If the user clicked Cancel, delete the entry & keep looping. + ...............................................................*/ + delete p_entry; + } + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Edit the current entry + ------------------------------------------------------------------*/ + case (BUTTON_EDIT | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx==-1) + break; + + /*............................................................... + Allocate a new entry & copy the currently-selected entry into it + ...............................................................*/ + p_entry = new PhoneEntryClass(); + (*p_entry) = (*Session.PhoneBook[Session.CurPhoneIdx]); + + /*............................................................... + Pass the new entry to the entry editor; if the user selects OK, + copy the data back into our phone book. Rebuild the list so + the changes show up in the list box. + ...............................................................*/ + if ( Edit_Phone_Dialog( p_entry ) ) { + (*Session.PhoneBook[Session.CurPhoneIdx]) = (*p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (Session.PhoneBook[Session.CurPhoneIdx] == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + strcpy (phone_num, Session.PhoneBook[Session.CurPhoneIdx]->Number); + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + phonelist.Set_Selected_Index( Session.CurPhoneIdx ); + } + } + changed = true; + } + delete p_entry; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + Delete the current entry + ------------------------------------------------------------------*/ + case (BUTTON_DELETE | KN_BUTTON): + + /*............................................................... + Do nothing if no entry is selected. + ...............................................................*/ + if (Session.CurPhoneIdx == -1) + break; + + /*............................................................... + Delete the current item & rebuild the phone listbox + ...............................................................*/ + Session.PhoneBook.Delete (Session.CurPhoneIdx); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + + if (Session.CurPhoneIdx == -1) { + *phone_num = 0; + numedit.Set_Text (phone_num, PhoneEntryClass::PHONE_MAX_NUM ); + } + changed = true; + break; + + /*------------------------------------------------------------------ + Dial the current number + ------------------------------------------------------------------*/ + case (KN_RETURN): + dialbtn.IsPressed = true; + dialbtn.Draw_Me(true); + // fall thru + + case (BUTTON_DIAL | KN_BUTTON): + + /*............................................................... + If no item is selected, just dial the number in the phone # + edit box: + - Create a new phone entry + - Copy the phone number into it + - Set settings to defaults + ...............................................................*/ + if (Session.CurPhoneIdx == -1 || + strcmp( Session.PhoneBook[Session.CurPhoneIdx]->Number, phone_num) ) { + + if ( strlen(phone_num) == 0) { // do not dial + dialbtn.IsPressed = true; + dialbtn.Flag_To_Redraw(); + break; + } + + p_entry = new PhoneEntryClass(); + strcpy( p_entry->Name, "NONAME" ); + strcpy( p_entry->Number, phone_num); + p_entry->Settings.Port = 0; + p_entry->Settings.IRQ = -1; + p_entry->Settings.Baud = -1; + p_entry->Settings.DialMethod = DIAL_TOUCH_TONE; + p_entry->Settings.InitStringIndex = 0; + p_entry->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + p_entry->Settings.CallWaitString[0] = 0; + + Session.PhoneBook.Add (p_entry); + Build_Phone_Listbox(&phonelist, &numedit, phone_num); + /*............................................................ + Set the current listbox index to the newly-added item. + ............................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + if (p_entry == Session.PhoneBook[i]) { + Session.CurPhoneIdx = i; + } + } + changed = true; + } + + process = false; + rc = true; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + Save any changes we've made to the phone list or settings + ------------------------------------------------------------------------*/ + if (changed) { + Session.Write_MultiPlayer_Settings (); + } + + /*------------------------------------------------------------------------ + Clear the list box + ------------------------------------------------------------------------*/ + while (phonelist.Count()) { + item = (char *)phonelist.Get_Item(0); + phonelist.Remove_Item(item); + delete [] item; + } + + return(rc); + +} /* end of Phone_Dialog */ + + +/*************************************************************************** + * Build_Phone_Listbox -- [re]builds the phone entry listbox * + * * + * This routine rebuilds the phone list box from scratch; it also updates * + * the contents of the phone # edit field. * + * * + * INPUT: * + * list ptr to list box * + * edit ptr to edit box * + * buf ptr to buffer for phone # * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static void Build_Phone_Listbox (ListClass *list, EditClass *edit, char *buf) +{ + int i; + char *item; + char phonename[21]; + char phonenum[15]; + + /*........................................................................ + Clear the list + ........................................................................*/ + while (list->Count()) { + item = (char *)(list->Get_Item(0)); + list->Remove_Item(item); + delete [] item; + } + + /* + ** Now sort the phone list by name then number + */ + qsort ((void *)(&Session.PhoneBook[0]), Session.PhoneBook.Count(), sizeof(class PhoneEntryClass *), Phone_Compare); + + /*........................................................................ + Build the list + ........................................................................*/ + for (i = 0; i < Session.PhoneBook.Count(); i++) { + item = new char[80]; + if ( !(strlen( Session.PhoneBook[i]->Name )) ) { + strcpy( phonename, " " ); + } else { + strncpy( phonename, Session.PhoneBook[i]->Name, 20 ); + phonename[21] = 0; + } + + if ( !(strlen( Session.PhoneBook[i]->Number )) ) { + strcpy( phonenum, " " ); + } else { + if ( strlen( Session.PhoneBook[i]->Number ) < 14) { + strcpy( phonenum, Session.PhoneBook[i]->Number ); + } else { + strncpy( phonenum, Session.PhoneBook[i]->Number, 12 ); + phonenum[12] = 0; + strcat( phonenum, "..." ); + } + } + + if (Session.PhoneBook[i]->Settings.Baud != -1) { + sprintf(item,"%s\t%s\t%d", phonename, phonenum, + Session.PhoneBook[i]->Settings.Baud); + } else { + sprintf(item,"%s\t%s\t[%s]", phonename, phonenum, + Text_String(TXT_DEFAULT)); + } + list->Add_Item(item); + } + list->Flag_To_Redraw(); + + /*........................................................................ + Init the current phone book index + ........................................................................*/ + if (list->Count() == 0 || Session.CurPhoneIdx < -1) { + Session.CurPhoneIdx = -1; + } else { + if (Session.CurPhoneIdx >= list->Count() ) { + Session.CurPhoneIdx = 0; + } + } + + /*........................................................................ + Fill in phone number edit buffer + ........................................................................*/ + if (Session.CurPhoneIdx > -1) { + strcpy (buf, Session.PhoneBook[Session.CurPhoneIdx]->Number); + edit->Set_Text (buf, PhoneEntryClass::PHONE_MAX_NUM ); + list->Set_Selected_Index( Session.CurPhoneIdx ); + } +} + + +/*************************************************************************** + * Phone_Compare -- for qsort * + * * + * INPUT: * + * p1,p2 ptrs to elements to compare * + * * + * OUTPUT: * + * 0 = same, -1 = (*p1) goes BEFORE (*p2), 1 = (*p1) goes AFTER (*p2) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +static int Phone_Compare (const void *p1, const void *p2) +{ + class PhoneEntryClass *pe1,*pe2; + int result; + + pe1 = *((class PhoneEntryClass **)p1); + pe2 = *((class PhoneEntryClass **)p2); + + result = strcmp( pe1->Name, pe2->Name ); + + // if strings are equal then check the phone number + + if ( !result ) { + result = strcmp( pe1->Number, pe2->Number ); + } + + return(result); +} + + +/*************************************************************************** + * Edit_Phone_Dialog -- lets user edit a phone book entry * + * * + * INPUT: * + * phone entry to edit * + * * + * OUTPUT: * + * true = OK, false = cancel * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/29/1995 BRR : Created. * + *=========================================================================*/ +static int Edit_Phone_Dialog (PhoneEntryClass *phone) +{ + /*........................................................................ + Dialog & button dimensions + ........................................................................*/ + int d_dialog_w = 230 * RESFACTOR; // dialog width + int d_dialog_h = 110 * RESFACTOR; // dialog height + int d_dialog_x = ((320 * RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((136 * RESFACTOR - d_dialog_h) / 2); // dialog y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 7 * RESFACTOR; // ht of 6-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + + int d_name_w = ( (PhoneEntryClass::PHONE_MAX_NAME - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_name_h = 9 * RESFACTOR; + int d_name_x = d_dialog_x + (((d_dialog_w - d_name_w) * 3) / 4) - 5 * RESFACTOR; + int d_name_y = d_dialog_y + 25 * RESFACTOR; + + int d_number_w = ( (PhoneEntryClass::PHONE_MAX_NUM - 1) * 6) * RESFACTOR + 3 * RESFACTOR; + int d_number_h = 9 * RESFACTOR; + int d_number_x = d_dialog_x + (((d_dialog_w - d_number_w) * 3) / 4) - 5 * RESFACTOR; + int d_number_y = d_name_y + d_name_h + d_margin; + + int d_default_w = 130 * RESFACTOR; + int d_default_h = 9 * RESFACTOR; + int d_default_x = d_dialog_cx - (d_default_w / 2); + int d_default_y = d_number_y + d_number_h + d_margin; + + int d_custom_w = 130 * RESFACTOR; + int d_custom_h = 9 * RESFACTOR; + int d_custom_x = d_dialog_cx - (d_default_w / 2); + int d_custom_y = d_default_y + d_default_h + d_margin; + + int d_save_w = 55 * RESFACTOR; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_dialog_cx - d_margin - d_save_w; + int d_save_y = d_dialog_y + d_dialog_h - d_margin - d_save_h - 5*RESFACTOR; + + int d_cancel_w = 55 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_cx + d_margin; + int d_cancel_y = d_dialog_y + d_dialog_h - d_margin - d_cancel_h - 5*RESFACTOR; + + /*........................................................................ + Button Enumerations + ........................................................................*/ + enum { + BUTTON_NAME = 100, + BUTTON_NUMBER, + BUTTON_DEFAULT, + BUTTON_CUSTOM, + BUTTON_SAVE, + BUTTON_CANCEL, + }; + + /*........................................................................ + Redraw values: in order from "top" to "bottom" layer of the dialog + ........................................................................*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*........................................................................ + Dialog variables + ........................................................................*/ + RedrawType display = REDRAW_ALL; // redraw level + + char namebuf[PhoneEntryClass::PHONE_MAX_NAME] = { 0 }; // buffer for editing name + char numbuf[PhoneEntryClass::PHONE_MAX_NUM] = { 0 }; // buffer for editing phone # + int rc; + SerialSettingsType settings; + bool custom = false; + bool firsttime = true; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /*........................................................................ + Buttons + ........................................................................*/ + GadgetClass *commands; // button list + + EditClass nameedit(BUTTON_NAME, namebuf, PhoneEntryClass::PHONE_MAX_NAME, TPF_TEXT, d_name_x, d_name_y, d_name_w, d_name_h, EditClass::ALPHANUMERIC); + EditClass numedit(BUTTON_NUMBER, numbuf, PhoneEntryClass::PHONE_MAX_NUM, TPF_TEXT, d_number_x, d_number_y, d_number_w, d_number_h, EditClass::ALPHANUMERIC); + TextButtonClass defaultbtn(BUTTON_DEFAULT, TXT_DEFAULT_SETTINGS, TPF_BUTTON, d_default_x, d_default_y, d_default_w, d_default_h); + TextButtonClass custombtn(BUTTON_CUSTOM, TXT_CUSTOM_SETTINGS, TPF_BUTTON, d_custom_x, d_custom_y, d_custom_w, d_custom_h); + TextButtonClass savebtn(BUTTON_SAVE, TXT_SAVE_BUTTON, TPF_BUTTON, d_save_x, d_save_y, d_save_w, d_save_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + /* + ------------------------- Build the button list -------------------------- + */ + commands = &nameedit; + numedit.Add_Tail(*commands); + defaultbtn.Add_Tail(*commands); + custombtn.Add_Tail(*commands); + savebtn.Add_Tail(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ----------------------------- Various Inits ------------------------------ + */ + /*........................................................................ + Init the settings; if the phone entry is set to use defaults, init our + settings to sensible values (in case we invoke the setting editor); + otherwise, copy the entry's settings. + ........................................................................*/ + if (phone->Settings.Port == 0 || phone->Settings.IRQ == -1 || + phone->Settings.Baud == -1) { + settings = Session.SerialDefaults; + defaultbtn.Turn_On(); + custom = false; + } else { + settings = phone->Settings; + custombtn.Turn_On(); + custom = true; + } + + strcpy (namebuf, phone->Name); + nameedit.Set_Text (namebuf, PhoneEntryClass::PHONE_MAX_NAME); + + strcpy (numbuf, phone->Number); + numedit.Set_Text (numbuf, PhoneEntryClass::PHONE_MAX_NUM); + + /* + ---------------------------- Processing loop ----------------------------- + */ + bool process = true; // process while true + while (process) { + /* + ........................ Invoke game callback ......................... + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + #endif + /* + ...................... Refresh display if needed ...................... + */ + if (display) { + Hide_Mouse(); + /* + .................. Redraw backgound & dialog box ................... + */ + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption (TXT_PHONE_LISTING, d_dialog_x, d_dialog_y, d_dialog_w); + + /*............................................................... + Dialog & Field labels + ...............................................................*/ + Fancy_Text_Print (TXT_NAME_COLON, d_name_x - 5 * RESFACTOR, d_name_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + Fancy_Text_Print (TXT_NUMBER_COLON, d_number_x - 5 * RESFACTOR, d_number_y + 1 * RESFACTOR, scheme, TBLACK, TPF_RIGHT | TPF_TEXT); + } + /* + .......................... Redraw buttons .......................... + */ + if (display >= REDRAW_BUTTONS) { + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + + /* + ........................... Get user input ............................ + */ + KeyNumType input = commands->Input(); + + if ( firsttime ) { + nameedit.Set_Focus(); + nameedit.Flag_To_Redraw(); + input = commands->Input(); + firsttime = false; + } + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + case (BUTTON_NAME | KN_BUTTON): + numedit.Set_Focus(); + numedit.Flag_To_Redraw(); + break; + +// case (BUTTON_NUMBER | KN_BUTTON): +// nameedit.Clear_Focus(); +// nameedit.Flag_To_Redraw(); +// break; + + /*------------------------------------------------------------------ + Use Default Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_DEFAULT | KN_BUTTON): + custombtn.Turn_Off(); + defaultbtn.Turn_On(); + custom = false; + break; + + /*------------------------------------------------------------------ + Use Custom Serial Settings + ------------------------------------------------------------------*/ + case (BUTTON_CUSTOM | KN_BUTTON): + if (Com_Settings_Dialog (&settings)) { + custombtn.Turn_On(); + defaultbtn.Turn_Off(); + } + custom = true; + display = REDRAW_ALL; + break; + + /*------------------------------------------------------------------ + CANCEL: bail out + ------------------------------------------------------------------*/ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + rc = false; + break; + + /*------------------------------------------------------------------ + Save: save changes + ------------------------------------------------------------------*/ + case (KN_RETURN): + case (BUTTON_SAVE | KN_BUTTON): + process = false; + rc = true; + break; + } + + } /* end of while */ + + /*------------------------------------------------------------------------ + If 'Save', save all current settings + ------------------------------------------------------------------------*/ + if (rc) { + strcpy( phone->Name, strupr( namebuf ) ); + + // if nothing was entered then make if NONAME + + if ( !(phone->Name[0]) ) { + strcpy( phone->Name, "NONAME" ); + } + + strcpy( phone->Number, strupr( numbuf ) ); + + if (custom) { + phone->Settings = settings; + } else { + phone->Settings.Port = 0; + phone->Settings.IRQ = -1; + phone->Settings.Baud = -1; + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + phone->Settings.InitStringIndex = 0; + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + phone->Settings.CallWaitString[0] = 0; + } + } + + return(rc); + + +} /* end of Edit_Phone_Dialog */ + + +static bool Dial_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + //Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + //Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + + + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); +// WWMessageBox().Process( "Error in the InitString." ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + + /* + ** Completely disable audio. This is required for MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Dial_Modem( DialString, settings->DialMethod, reconnect ); + + if (reconnect) { + /* + --------------------------- Redraw the display --------------------------- + */ + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + + case DIAL_BUSY: + WWMessageBox().Process(TXT_LINE_BUSY); + connected = false; + break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_NO_DIAL_TONE: + WWMessageBox().Process(TXT_NO_DIAL_TONE); + connected = false; + break; + + case DIAL_CANCELED: + NullModem.Hangup_Modem(); + Session.ModemService = false; + WWMessageBox().Process(TXT_DIALING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Dial_Modem */ + + +static bool Answer_Modem( SerialSettingsType *settings, bool reconnect ) +{ + bool connected = false; + DialStatusType dialstatus; + int modemstatus; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + // save for later to reconnect + + DialSettings = settings; + + modemstatus = NullModem.Get_Modem_Status(); + if (reconnect) { + if ( (modemstatus & CD_SET) ) { + connected = true; + Session.ModemService = true; + return( connected ); + } + } else if ( (modemstatus & CD_SET) ) { + NullModem.Hangup_Modem(); + Session.ModemService = false; + } + + NullModem.Setup_Modem_Echo( Modem_Echo ); + + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); +//#ifndef WIN32 + /* + ** If our first attempt to detect the modem failed, and we're at + ** 14400 or 28800, bump up to the next baud rate & try again. + */ + switch (settings->Baud) { + case 14400: + settings->Baud = 19200; + //Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + case 28800: + settings->Baud = 38400; + //Shutdown_Modem(); + Init_Null_Modem(settings); + NullModem.Setup_Modem_Echo( Modem_Echo ); + modemstatus = NullModem.Detect_Modem( settings, reconnect ); + if ( !modemstatus ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + } + break; + + default: + WWMessageBox().Process( TXT_UNABLE_FIND_MODEM ); + Session.ModemService = true; + return( connected ); + + } +//#else //WIN32 +// Session.ModemService = true; +// return( connected ); +//#endif //WIN32 + } else if ( modemstatus == -1 ) { + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + WWMessageBox().Process( TXT_ERROR_IN_INITSTRING ); + Session.ModemService = true; + return( connected ); + } + + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Completely disable audio. This is required for some MWave devices like those + ** found in the IBM Aptiva. + */ + ThemeType old_theme = THEME_NONE; + if (SoundOn){ + old_theme = Theme.What_Is_Playing(); + Theme.Stop(); + CountDownTimerClass wait; + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + Sound_End(); + Call_Back(); + wait.Set (60,true); + while ( wait.Time() ) { + Call_Back(); + } + SoundOn = 0; + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + dialstatus = NullModem.Answer_Modem( reconnect ); + + switch ( dialstatus ) { + case DIAL_CONNECTED: + connected = true; + break; + + case DIAL_NO_CARRIER: + WWMessageBox().Process(TXT_NO_CARRIER); + connected = false; + break; + +// case DIAL_BUSY: +// WWMessageBox().Process(TXT_LINE_BUSY); +// connected = false; +// break; + + case DIAL_ERROR: + WWMessageBox().Process(TXT_NUMBER_INVALID); + connected = false; + break; + + case DIAL_CANCELED: + WWMessageBox().Process(TXT_ANSWERING_CANCELED); + connected = false; + break; + } + + NullModem.Remove_Modem_Echo(); + NullModem.Print_EchoBuf(); + NullModem.Reset_EchoBuf(); + +#ifdef WIN32 +#ifdef FIXIT_APTIVA_MODEM + /* + ** Restore audio capability + */ + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); + if (SoundOn){ + Theme.Play_Song (old_theme); + } +#endif //FIXIT_APTIVA_MODEM +#endif //WIN32 + + Session.ModemService = true; + return( connected ); + +} /* end of Answer_Modem */ + + +static void Modem_Echo( char c ) +{ + if (NullModem.EchoCount < (NullModem.EchoSize - 1) ) { + *(NullModem.EchoBuf + NullModem.EchoCount) = c; + *(NullModem.EchoBuf + NullModem.EchoCount + 1) = 0; + NullModem.EchoCount++; + } + +} /* end of Modem_Echo */ + + +void Smart_Printf( char *format, ... ) +{ + va_list arglist; + char buf[501]; + + + va_start(arglist,format); + vsprintf(buf,format,arglist); + va_end(arglist); + + if (Debug_Smart_Print) { + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s",buf); + } else { + printf("%s",buf); + } + } else { + if (Debug_Heap_Dump) { + printf("%s",buf); + } + if (Debug_Modem_Dump) { + printf("%s",buf); + } + } +} + + +void Hex_Dump_Data( char *buffer, int length ) +{ + int i; + int offset = 0; + char buff[10]; + char ptr[16]; + char c; + + + while (length >= 16) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + + c = ptr[i]; + itoh(c, buff); + + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + + Smart_Printf("%s ", buff); + } + + Smart_Printf(" "); + + for (i = 0; i < 16; i++) { + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + + offset += 16; + length -= 16; + } + + if (length) { + memcpy( ptr, (buffer + offset), 16); + + Smart_Printf("%05lX ", offset); + + for (i = 0; i < 16; i++) { + if (i < length) { + c = ptr[i]; + itoh(c, buff); + if (!(i & 0x3) && i) { + Smart_Printf("³ "); + } + Smart_Printf("%s ", buff); + } else { + if (!(i & 0x3) && i) { + Smart_Printf(" "); + } + Smart_Printf(" "); + } + } + + Smart_Printf(" "); + + for (i = 0; i < length; i++) { + + c = ptr[i]; + + if (c && ((c < 7) || (c > 11)) && (c != 13)) { + Smart_Printf("%c", c); + } else { + Smart_Printf("."); + } + } + + Smart_Printf("\n"); + } + +} /* end of Hex_Dump_Data */ + + +void itoh( int i, char *s) +{ + + int nibble, loop; + +// *s++ = '0'; +// *s++ = 'x'; + + if (i == 0) { + *s++ = '0'; + *s++ = '0'; + } else { + for (loop = 1; loop >= 0; loop--) { + nibble = (i >> (loop << 2)) & 0x000F; + + /* decimal range */ + if (nibble < 10) { + *s++ = '0' + nibble; + } else { + *s++ = 'A' + (nibble - 10); + } + } + } + *s = 0; /* null terminate it */ +} + +void Log_Start_Time( char *string ) +{ +// LogDump_Print = true; + + LogLevel = 0; + LogLevelTime[ LogLevel ] = LogLastTime = TickCount; + + Smart_Printf( "start tick=%d, %s \n", LogLastTime, string ); +} + + +void Log_End_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + while (LogLevel >= 0) { + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel-- ]; + Smart_Printf( "end tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + } + + LogDump_Print = false; +} + + +void Log_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + + Smart_Printf( "tick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + LogLastTime = currtime; +} + + +void Log_Start_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLastTime; + Smart_Printf( "start ntick=%d, ticks=%d, tsecs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel >= (MAX_LOG_LEVEL - 1) ) { + Smart_Printf( "Could not start another nesting Maxed at %d,%d!-! \n", + LogLevel, (MAX_LOG_LEVEL - 1) ); + } else { + LogLevelTime[ ++LogLevel ] = currtime; + } + + LogLastTime = currtime; +} + + +void Log_End_Nest_Time( char *string ) +{ + int i; + unsigned long currtime; + unsigned long ticks; + + + currtime = TickCount; + + if (LogLevel <= 0) { + Smart_Printf( "Could not end another nesting Mined at %d,%d!-! \n", + LogLevel, 0 ); + LogLevel = 0; + } + + if (LogLevel < MAX_LOG_LEVEL) { + // + // put one space for each level as indenting + // + i = 0; + while (i++ < LogLevel) { + Smart_Printf( " " ); + } + } else { + Smart_Printf( "LogLevel %d too large!-! \n", LogLevel); + LogLevel = MAX_LOG_LEVEL - 1; + } + + ticks = currtime - LogLevelTime[ LogLevel ]; + Smart_Printf( "end ntick=%d, ticks=%d, secs=%d, %s \n", + currtime, ticks, ((ticks * 10) / 60), string ); + + if (LogLevel) { + LogLevel--; + } + + LogLastTime = currtime; +} + +#endif \ No newline at end of file diff --git a/code/redalert/nullmgr.cpp b/code/redalert/nullmgr.cpp new file mode 100644 index 0000000..92da4fb --- /dev/null +++ b/code/redalert/nullmgr.cpp @@ -0,0 +1,2618 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/NULLMGR.CPP 2 3/07/97 6:40p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : NULLMGR.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : April 5, 1995 * + * * + * Last Update : May 1, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * NullModemClass::NullModemClass -- class constructor * + * NullModemClass::~NullModemClass -- class destructor * + * NullModemClass::Init -- initialization * + * NullModemClass::Send_Message -- sends a message * + * NullModemClass::Get_Message -- polls the Queue for a message * + * NullModemClass::Service -- main polling routine * + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * NullModemClass::Reset_Response_Time -- Resets response time computatio* + * NullModemClass::Oldest_Send -- Returns ptr to oldest unACK'd send buf * + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * NullModemClass::Dial_Modem -- dials a number passed * + * NullModemClass::Answer_Modem -- waits for call and answers * + * NullModemClass::Hangup_Modem -- hangs up the modem * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "function.h" +#ifdef WIN32 +#include "wincomm.h" +#include "modemreg.h" +extern ModemRegistryEntryClass *ModemRegistry; +#endif //WIN32 +#include "i86.h" + + +// Turn off "expression is not meaningful". +#pragma warning 628 9 + + +// the following line was taken from Greenleaf's +// because of other define conflicts + +#define ESC 27 +#define NOKEY 0xffff +#define INIT_COMMAND_RETRIES 2 + +// this time is in milliseconds + +#define DEFAULT_TIMEOUT 1500 + +// +// the following is for a fix around a greenleaf bug +// where they do not check for the value of abortkey +// to determine whether or not they call the abort modem function. +// +extern "C" { + extern void (*_AbortModemFunctionPtr)(int); +} + +static void (*NullModemClass::OrigAbortModemFunc)(int); + +static KeyNumType NullModemClass::Input; +static GadgetClass *NullModemClass::Commands; // button list + +/* +** Ugly hack: this string stores the string received from the modem +*/ +char ModemRXString[80]; + + +/*************************************************************************** + * NullModemClass::NullModemClass -- class constructor * + * * + * INPUT: * + * numsend # desired entries for the send queue * + * numreceive # desired entries for the receive queue * + * maxlen application's max packet length * + * magicnum application-specific magic # (so we don't * + * accidentally end up talking to another one of our own * + * products using the same protocol) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::NullModemClass (int numsend, int numreceive, int maxlen, + unsigned short magicnum) : ConnManClass() +{ + /*------------------------------------------------------------------------ + Init Port to NULL; we haven't opened Greenleaf yet. + ------------------------------------------------------------------------*/ +#ifdef WIN32 + PortHandle = NULL; +#else //WIN32 + Port = NULL; +#endif //WIN32 + + Connection = NULL; + + NumSend = numsend; + NumReceive = numreceive; + MaxLen = maxlen; + MagicNum = magicnum; + + RXBuf = 0; + BuildBuf = 0; + + EchoSize = 500; + EchoBuf = 0; + + OldIRQPri = -1; + + ModemVerboseOn = false; // default true + ModemEchoOn = false; // default true + ModemWaitCarrier = 50000; // default 50 * 1000ms = 50 secs + ModemCarrierDetect = 600; // default 6 * 100ms = .6 secs + ModemCarrierLoss = 1400; // default 14 * 100ms = 1.4 secs + ModemHangupDelay = 20000; // default 20 * 1000ms = 20 secs + ModemGuardTime = 1000; // default 50 * 20ms = 1 sec + ModemEscapeCode = '+'; // default ASCII 43 + + SendOverflows = 0; + ReceiveOverflows = 0; + CRCErrors = 0; + + NumConnections = 0; + + /*------------------------------------------------------------------------ + Init timing parameters + ------------------------------------------------------------------------*/ + RetryDelta = 60; // 60 ticks between retries + MaxRetries = -1; // disregard # retries + Timeout = 1200; // report bad connection after 20 seconds + +} /* end of NullModemClass */ + + +/*************************************************************************** + * NullModemClass::~NullModemClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +NullModemClass::~NullModemClass () +{ + Delete_Connection(); + +} /* end of ~NullModemClass */ + + +/*************************************************************************** + * NullModemClass::Init -- initialization * + * * + * INPUT: * + * port address * + * irq 2-15 * + * dev_name name of communications device (win32 only) * + * baud 300, 1200, 9600, etc * + * parity 'O' (odd), 'E' (even), 'N' (none), 'S' (space), * + * 'M' (mark) * + * wordlength 5, 6, 7, or 8 * + * stopbits 1 or 2 * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 10/9/1996 ST : Modified to take device name in win32 * + *=========================================================================*/ +int NullModemClass::Init (int port, int irq, char *dev_name, int baud, char parity, int wordlen, int stopbits, int flowcontrol) +{ + int com; + +#ifdef WIN32 + + /* + ** Get rid of the 'no reference to' warning + */ + irq = irq; + + /* + ** Make sure the port is closed before we start + */ + if (PortHandle) { + CloseHandle (PortHandle); + PortHandle = NULL; + } + + if (!Connection) { + +#else //WIN32 + + int irqnum; + int address; + int status; + + /* + ** Get rid of the 'no reference to' warning + */ + dev_name = dev_name; + + if (Port) { + PortClose(Port); + Port = NULL; + }else { + +#endif //WIN32 + + /*------------------------------------------------------------------------ + Init our Connection + ------------------------------------------------------------------------*/ + Connection = new NullModemConnClass (NumSend, NumReceive, MaxLen, + MagicNum); + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + + /*--------------------------------------------------------------------- + Allocate our packet parsing buffer; make it the same # of packets as the + # of receive queue entries the application has requested. Use the + "Actual" maximum packet size, given from the connection; this allows for + both headers that get added to the packet. + ---------------------------------------------------------------------*/ + RXSize = Connection->Actual_Max_Packet() * NumReceive; + RXBuf = new char [RXSize]; + + BuildBuf = new char [MaxLen]; + + EchoBuf = new char [ EchoSize ]; + } + + RXCount = 0; + EchoCount = 0; + + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch (port) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + +#ifdef WIN32 + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + } + + /* + ** Shift up the baud rate to sensible values + */ +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + char *device; + + switch ( port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = dev_name; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++ ){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (dev_name, ModemRegistry->Get_Modem_Name() )){ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + device = NULL; + } + + + + /* + ** Open the com port + */ + PortHandle = SerialPort->Serial_Port_Open (device, baud, parity, wordlen, stopbits, flowcontrol); + if (PortHandle == INVALID_HANDLE_VALUE) { + Shutdown(); + return(false); + } + + Connection->Init (PortHandle); + +#else //WIN32 + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (port != address || irq != irqnum) { + + status = FastSetPortHardware( com, irq, port ); + + if (status < ASSUCCESS) { + Mono_Printf( "Unable to set Com port status %d\n", status ); + Mono_Printf( "Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } else { + Mono_Printf( "Changed Com port number %d to address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "No changes to Com port number %d with address %x, irq %d\n", + com + 1, port, irq ); + } + } else { + Mono_Printf( "Com port number %d\n", com + 1 ); + } + + if (status != ASSUCCESS) { + Delete_Connection(); + return(false); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, baud, parity, wordlen, stopbits); + + if (Port->status != ASSUCCESS) { + Shutdown(); + return(false); + } + +// UseRtsCts( Port, 1 ); // use RTS CTS hardware flow control + + /*------------------------------------------------------------------------ + Init the Connection + ------------------------------------------------------------------------*/ + Connection->Init(Port); + //because Watcom is so stupid + flowcontrol = flowcontrol; + +#endif //WIN32 + + NumConnections = 1; + + return(true); +} + + +/*********************************************************************************************** + * NMC::Num_Connections -- returns NumConnections member * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: NumConnections * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Num_Connections( void ) +{ + return(NumConnections); +} + + +/*********************************************************************************************** + * NMC::Delete_Connection -- removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:44AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Delete_Connection( void ) +{ + if (Connection) { + delete Connection; + Connection = NULL; + } + + if (RXBuf) { + delete [] RXBuf; + RXBuf = NULL; + } + + if (BuildBuf) { + delete [] BuildBuf; + BuildBuf = NULL; + } + + if (EchoBuf) { + delete [] EchoBuf; + EchoBuf = NULL; + } + + NumConnections = 0; + + return( true ); +} /* end of Delete_Connection */ + + + +/*********************************************************************************************** + * NMC::Init_Send_Queue -- Initialises the connections send queue * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:46AM ST : Documented / Win32 support * + *=============================================================================================*/ +int NullModemClass::Init_Send_Queue( void ) +{ + + /*------------------------------------------------------------------------ + Init the send queue + ------------------------------------------------------------------------*/ + if ( Connection ) { + Connection->Queue->Init_Send_Queue(); + } + + return(true); +} + + + +/*********************************************************************************************** + * NMC::Detect_Port -- Checks that the specified com port exists * + * * + * * + * * + * INPUT: ptr to SerialSettingsType * + * * + * OUTPUT: true if port is valid * + * * + * WARNINGS: Win32 version always returns true as win95 shouldnt allow us to open the * + * port if it doesnt exist or is in use by the mouse * + * * + * HISTORY: * + * 8/2/96 11:47AM ST : Documented / Win32 support * + *=============================================================================================*/ +DetectPortType NullModemClass::Detect_Port( SerialSettingsType *settings ) +{ + +#ifdef WIN32 + + static char com_ids[9][5]={ + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9" + }; + + int i; + + /* + ** Create a new modem class for our com port + */ + if (!SerialPort) { + SerialPort = new WinModemClass; + }else{ + SerialPort->Serial_Port_Close(); + } + + /* + ** Shift up the baud rate to sensible values + */ + int baud = settings->Baud; +// if (baud == 14400) baud = 19200; +// if (baud == 28800) baud = 38400; + + + /* + ** Translate the port address into a usable device name + */ + char *device; + + switch ( settings->Port ) { + + case 0x3f8: + device = com_ids[0]; + break; + + case 0x2f8: + device = com_ids[1]; + break; + + case 0x3e8: + device = com_ids[2]; + break; + + case 0x2e8: + device = com_ids[3]; + break; + + case 1: + /* + ** 1 is a special value. It means use the device name not the port address. + */ + device = settings->ModemName; + + /* + ** If we can match a registry entry with the device name then use that, otherwise use + ** the device name directly to open the port with. + */ + if (ModemRegistry){ + delete ModemRegistry; + ModemRegistry = NULL; + } + for ( i=0 ; i<10 ; i++){ + ModemRegistry = new ModemRegistryEntryClass (i); + if (ModemRegistry->Get_Modem_Name()){ + if (!strcmp (device, ModemRegistry->Get_Modem_Name() )){ + /* + ** Got a match. Break out leaving the registry info intact. + */ + device = ModemRegistry->Get_Modem_Device_Name(); + break; + } + } + delete ModemRegistry; + ModemRegistry = NULL; + } + break; + + default: + return (PORT_INVALID); + } + + /* + ** Open the com port + */ + HANDLE porthandle = SerialPort->Serial_Port_Open (device, baud, 0, 8, 1, settings->HardwareFlowControl); + + if (porthandle == INVALID_HANDLE_VALUE){ + return (PORT_INVALID); + } + + SerialPort->Serial_Port_Close(); + return (PORT_VALID); + + + +#else //WIN32 + + union REGS regs ; + int com; + int irqnum; + int address; + int status; + + + // shutdown previous port + + Shutdown(); + + if (settings->IRQ > 0xf) { + return( PORT_INVALID ); + } + + // + // check if the IRQ is the same as the mouse + // call mouse func to get mouse IRQ number + // + + regs.x.eax = 0x0024; + int386( 0x33, ®s, ®s ); + + // + // check for error + // + if (regs.w.ax != 0xffff) { + // + // is the mouse IRQ the same as that they selected + // + if (regs.h.cl == 0) { // PS/2 IRQ 0xc + if (settings->IRQ == 0xc) { + return( PORT_IRQ_INUSE ); + } + } else if (regs.h.cl == (unsigned char)(settings->IRQ)) { + return( PORT_IRQ_INUSE ); + } + } + + if (settings->IRQ < 2 // 0 timer, 1 keyboard + || settings->IRQ == 6 // floppy disk + || settings->IRQ == 8 // CMOS real-time clock + || settings->IRQ == 0xd // math coprocessor error + || settings->IRQ == 0xe) { // hard disk + return( PORT_IRQ_INUSE ); + } + + /*------------------------------------------------------------------------ + This call allocates all necessary queue buffers + ------------------------------------------------------------------------*/ + switch ( settings->Port ) { + case 0x3f8: + com = COM1; + break; + + case 0x2f8: + com = COM2; + break; + + case 0x3e8: + com = COM3; + break; + + case 0x2e8: + com = COM4; + break; + + default: + com = COM5; + break; + } + + status = FastGetPortHardware( com, &irqnum, &address ); + if (status == ASSUCCESS) { + if (settings->Port != address || settings->IRQ != irqnum) { + status = FastSetPortHardware( com, settings->IRQ, settings->Port ); + } + } + + if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + /*------------------------------------------------------------------------ + Open the Greenleaf port + ------------------------------------------------------------------------*/ + Port = PortOpenGreenleafFast (com, settings->Baud, 'N', 8, 1); + + status = Port->status; + + PortClose(Port); + Port = NULL; + + if (status == ASIRQINUSE) { + return( PORT_IRQ_INUSE ); + } else if (status != ASSUCCESS) { + return( PORT_INVALID ); + } + + return( PORT_VALID ); +#endif //WIN32 +} + + +/*********************************************************************************************** + * NullModemClass::ShutDown -- Closes serial port and removes the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 11:43AM ST : Documented / Win32 support * + *=============================================================================================*/ +void NullModemClass::Shutdown ( void ) +{ + +#ifdef WIN32 + + if (PortHandle && SerialPort) { + SerialPort->Serial_Port_Close(); + delete SerialPort; + SerialPort = NULL; + PortHandle = NULL; + Delete_Connection(); + } + +#else //WIN32 + if (Port) { + PortClose(Port); + Port = NULL; + Delete_Connection(); + } + +#endif //WIN32 + +} /* end of Shutdown */ + + +/*************************************************************************** + * NullModemClass::Set_Timing -- sets timing for all connections * + * * + * This will set the timing parameters. This allows an application to * + * measure the Response_Time while running, and adjust timing accordingly. * + * * + * INPUT: * + * retrydelta value to set for retry delta * + * maxretries value to set for max # retries * + * timeout value to set for connection timeout * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/07/1995 DRD : Created. * + *=========================================================================*/ +void NullModemClass::Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout) +{ + RetryDelta = retrydelta; + MaxRetries = maxretries; + Timeout = timeout; + + Connection->Set_Retry_Delta (RetryDelta); + Connection->Set_Max_Retries (MaxRetries); + Connection->Set_TimeOut (Timeout); + +} /* end of Set_Timing */ + + +/*************************************************************************** + * NullModemClass::Send_Message -- sends a message * + * * + * For clarity's sake, here's what happens to the buffer passed in: * + * - It gets passed to the Connection's Send_Packet() routine * + * - The CommHeaderType header gets tacked onto it * + * - The resulting buffer gets added to the Connection's Send Queue * + * - When Service() determines that it needs to send the data, it * + * copies the entire packet (CommHeaderType and all) into its local * + * SendBuf, adds the packet start ID, length, and CRC, then sends it out.* + * * + * The ack_req argument will almost always be '1' (the default). The only * + * reason to use 0 is if you don't know whether the other system is * + * ready or not, so you have to periodically send out a query packet, * + * and wait for a response. (Using the connection's built-in retry * + * system would just blast out useless data if the other system isn't * + * even there.) * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req 1 = ACK is required; 0 = not * + * * + * OUTPUT: * + * 1 = OK; 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Send_Message (void *buf, int buflen, int ack_req) +{ + int rc; + + if (NumConnections == 0) { + return( false ); + } + + rc = Connection->Send_Packet(buf,buflen,ack_req); + if (!rc) + SendOverflows++; + + return(rc); + +} /* end of Send_Message */ + + +/*************************************************************************** + * NullModemClass::Get_Message -- polls the Queue for a message * + * * + * INPUT: * + * buf buffer to store message in * + * buflen ptr filled in with length of message * + * * + * OUTPUT: * + * 1 = message was received; 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int NullModemClass::Get_Message (void *buf, int *buflen) +{ + if (NumConnections == 0) { + return( false ); + } + return( Connection->Get_Packet( buf, buflen ) ); +} + + +/*************************************************************************** + * NullModemClass::Service -- main polling routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = connection has gone bad * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +int NullModemClass::Service (void) +{ + int pos; // current position in RXBuf + int i; // loop counter + unsigned short length; + SerialHeaderType *header; // decoded packet start, length + SerialCRCType *crc; // decoded packet CRC + char moredata = 0; + + if (NumConnections == 0) { + return( false ); + } + +#ifdef WIN32 + + RXCount += SerialPort->Read_From_Serial_Port((unsigned char*)(RXBuf + RXCount), int(RXSize - RXCount) ); + +#else //WIN32 + + int status; + static unsigned long last_time = 0; + + /*------------------------------------------------------------------------ + First, copy all the bytes we can from the Greenleaf RX buffer to our + own buffer. + (For Win95, only call GetLineStatus() 60 times a second at most. + Otherwise, we don't receive any packets!) + ------------------------------------------------------------------------*/ + if ( (TickCount - last_time) > 0) { + if ( GetLineStatus( Port ) ) { + Mono_Set_Cursor(0,0); + ClearLineStatus( Port ); + } + last_time = TickCount; + } + + status = SpaceUsedInRXBuffer( Port ); + + if ( status < ASSUCCESS) { +// Smart_Printf( "SpaceUsedInRXBuffer status = %d, port status = %d \n", status, Port->status ); + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + } else if (status > 0) { + status = ReadBuffer( Port, RXBuf + RXCount, RXSize - RXCount ); + +#if (CONN_DEBUG) + printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); +#endif +// Smart_Printf( "ReadBuffer status = %d, port status = %d, count = %d \n", status, Port->status, Port->count ); + if (status < ASSUCCESS && status != ASBUFREMPTY) { +// Smart_Printf( "ReadBuffer ERRRRRRORRRRRR! \n" ); + } else { + moredata = 1; + } + + if ( Port->status < ASSUCCESS ) { + ClearError( Port ); + } + + RXCount += Port->count; + } + + +#endif //WIN32 + + // minimum packet size + + if ( RXCount < (PACKET_SERIAL_OVERHEAD_SIZE + 1) ) { + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Now scan the buffer for the start of a packet. + ------------------------------------------------------------------------*/ + pos = -1; + for (i = 0; i <= RXCount - sizeof( short ); i++) { + if ( *((unsigned short *)(RXBuf + i)) == PACKET_SERIAL_START ) { + pos = i; + break; + } + } + + /*------------------------------------------------------------------------ + No start code was found; throw away all bytes except the last few, and + return. + ------------------------------------------------------------------------*/ + if (pos==-1) { +// Smart_Printf( "No magic number found \n" ); + /*..................................................................... + move the remaining, un-checked bytes to the start of the buffer + .....................................................................*/ + memmove (RXBuf, RXBuf + i, sizeof( short ) - 1); + RXCount = sizeof( short ) - 1; + return( Connection->Service() ); + } + + /*------------------------------------------------------------------------ + Check to see if there are enough bytes for the header to be decoded + ------------------------------------------------------------------------*/ + if ( (RXCount - pos) < sizeof( SerialHeaderType ) ) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + A start code was found; check the packet's length & CRC + ------------------------------------------------------------------------*/ + header = (SerialHeaderType *)(RXBuf + pos); + + /*------------------------------------------------------------------------ + If we lost a byte in the length, we may end up waiting a very long time + for the buffer to get to the right length; check the verify value to + make sure this didn't happen. + ------------------------------------------------------------------------*/ + if ( header->MagicNumber2 != PACKET_SERIAL_VERIFY ) { +// Smart_Printf( "Verify failed\n"); +// Hex_Dump_Data( (RXBuf + pos), PACKET_SERIAL_OVERHEAD_SIZE ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + length = header->Length; + + /*------------------------------------------------------------------------ + Special case: if the length comes out too long for us to process: + - Assume the packet is bad + - Throw away the bogus packet-start code + - Return; we'll search for another packet-start code next time. + ------------------------------------------------------------------------*/ + if (length > MaxLen) { +#if (CONN_DEBUG) + printf( "length too lonnng\n" ); +#endif +// Smart_Printf( "length too lonnng %d, max %d \n", length, MaxLen ); + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + If the entire packet isn't stored in our buffer, copy the remaining bytes + to the front of the buffer & return. + ------------------------------------------------------------------------*/ + if ( (pos + length + PACKET_SERIAL_OVERHEAD_SIZE) > RXCount) { + + if ( moredata ) { +// Smart_Printf( "waiting for more data %d, pos = %d \n", ((length + PACKET_SERIAL_OVERHEAD_SIZE) - (RXCount - pos)), pos ); + } + + if (pos) { + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + } + return(Connection->Service()); + } + + /*------------------------------------------------------------------------ + Now grab the CRC value in the packet, & compare it to the CRC value + computed from the actual data. If they don't match, throw away the bogus + start-code, move the rest to the front of the buffer, & return. + We'll continue parsing this data when we're called next time. + ------------------------------------------------------------------------*/ + crc = (SerialCRCType *)(RXBuf + pos + sizeof( SerialHeaderType ) + length); + if (NullModemConnClass::Compute_CRC(RXBuf + pos + + sizeof( SerialHeaderType ), length) != crc->SerialCRC) { + + CRCErrors++; + +#if (CONN_DEBUG) + printf( "CRC check failed\n" ); +#endif +// Smart_Printf( "CRC check failed for packet of length %d \n", length ); + +// if (length < 100) { +// Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +// } + + pos += sizeof ( short ); // throw away the bogus start code + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + return(Connection->Service()); + } + +#if(0) +// Mono_Printf( "received %d bytes \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received tick=%d, Protocol=%d \n", TickCount, Session.CommProtocol ); + Hex_Dump_Data( (RXBuf + pos), (sizeof( SerialHeaderType) + length) ); + Debug_Modem_Dump = false; +#endif + + /*------------------------------------------------------------------------ + Give the new packet to the Connection to process. + ------------------------------------------------------------------------*/ + if (!Connection->Receive_Packet(RXBuf + pos + sizeof( SerialHeaderType ), length)) { + ReceiveOverflows++; +// Smart_Printf( "Received overflows %d \n", ReceiveOverflows ); + } + +#if(0) + else { +// Mono_Printf( "added packet \n", sendlen ); + Debug_Modem_Dump = true; + Smart_Printf( "Received Packet \n" ); + Debug_Modem_Dump = false; + } +#endif + +#if (0) + Hex_Dump_Data( (RXBuf + pos), (PACKET_SERIAL_OVERHEAD_SIZE + length) ); +#endif + + /*------------------------------------------------------------------------ + Move all data past this packet to the front of the buffer. + ------------------------------------------------------------------------*/ + pos += (PACKET_SERIAL_OVERHEAD_SIZE + length); + memmove (RXBuf, RXBuf + pos, RXCount - pos); + RXCount -= pos; + + /*------------------------------------------------------------------------ + Now, service the connection's Queue's; this will handle ACK & Retries. + ------------------------------------------------------------------------*/ + return(Connection->Service()); + +} /* end of Service */ + + +/*************************************************************************** + * NullModemClass::Num_Send -- Returns # of unACK'd send entries * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Send(void) +{ + if (Connection) + return( Connection->Queue->Num_Send() ); + else + return (0); + +} /* end of Num_Send */ + + +/*************************************************************************** + * NullModemClass::Num_Receive -- Returns # entries in the receive queue * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +int NullModemClass::Num_Receive(void) +{ + if (Connection) + return( Connection->Queue->Num_Receive() ); + else + return (0); + +} /* end of Num_Receive */ + + +/*************************************************************************** + * NullModemClass::Response_Time -- Returns Queue's avg response time * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +unsigned long NullModemClass::Response_Time(void) +{ + if (Connection) + return( Connection->Queue->Avg_Response_Time() ); + else + return (0); + +} /* end of Response_Time */ + + +/*************************************************************************** + * NullModemClass::Reset_Response_Time -- Resets response time computation * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Reset_Response_Time(void) +{ + if (Connection) + Connection->Queue->Reset_Response_Time(); + +} /* end of Reset_Response_Time */ + + +/*************************************************************************** + * Oldest_Send -- Returns ptr to oldest unACK'd send buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/01/1995 BRR : Created. * + *=========================================================================*/ +void * NullModemClass::Oldest_Send(void) +{ + int i; + SendQueueType *send_entry; // ptr to send entry header + CommHeaderType *packet; + void *buf = NULL; + + for (i = 0; i < Connection->Queue->Num_Send(); i++) { + send_entry = Connection->Queue->Get_Send(i); + if (send_entry) { + packet = (CommHeaderType *)send_entry->Buffer; + if (packet->Code == ConnectionClass::PACKET_DATA_ACK && send_entry->IsACK == 0) { + buf = send_entry->Buffer; + break; + } + } + } + + return(buf); + +} /* end of Oldest_Send */ + + +/*************************************************************************** + * NullModemClass::Configure_Debug -- sets up special debug values * + * * + * Mono_Debug_Print2() can look into a packet to pull out a particular * + * ID, and can print both that ID and a string corresponding to * + * that ID. This routine configures these values so it can find * + * and decode the ID. This ID is used in addition to the normal * + * CommHeaderType values. * + * * + * INPUT: * + * index (not used) * + * type_offset ID's byte offset into packet * + * type_size size of ID, in bytes; 0 if none * + * names ptr to array of names; use ID as an index into this * + * namestart numerical start of the 1st name value * + * namecount # in the names array; 0 if none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Names shouldn't be longer than 12 characters. * + * * + * HISTORY: * + * 05/31/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Configure_Debug(int, int type_offset, int type_size, + char **names, int namestart, int namecount) +{ + if (Connection) + Connection->Queue->Configure_Debug (type_offset, type_size, names, + namestart, namecount); +} + +#ifdef CHEAT_KEYS +/*************************************************************************** + * Mono_Debug_Print -- Debug output routine * + * * + * INPUT: * + * index (not used) * + * refresh 1 = clear screen & completely refresh * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/02/1995 BRR : Created. * + *=========================================================================*/ +void NullModemClass::Mono_Debug_Print(int,int refresh) +{ + if (!Connection) + return; + + Connection->Queue->Mono_Debug_Print (refresh); + + if (refresh) { + Mono_Set_Cursor (31,1); + Mono_Printf ("Serial Port Queues"); + + Mono_Set_Cursor (9,2); + Mono_Printf ("Average Response Time:"); + + Mono_Set_Cursor (20,3); + Mono_Printf ("CRC Errors:"); + + Mono_Set_Cursor (43,2); + Mono_Printf ("Send Overflows:"); + + Mono_Set_Cursor (40,3); + Mono_Printf ("Receive Overflows:"); + } + + Mono_Set_Cursor (32,2); + Mono_Printf ("%d ", Connection->Queue->Avg_Response_Time()); + + Mono_Set_Cursor (32,3); + Mono_Printf ("%d ", CRCErrors); + + Mono_Set_Cursor (59,2); + Mono_Printf ("%d ", SendOverflows); + + Mono_Set_Cursor (59,3); + Mono_Printf ("%d ", ReceiveOverflows); + + Mono_Set_Cursor (2,5); + Mono_Printf ("%d ", Num_Send()); + + Mono_Set_Cursor (41,5); + Mono_Printf ("%d ", Num_Receive()); + +} /* end of Mono_Debug_Print */ +#endif + + +/*************************************************************************** + * NullModemClass::Detect_Modem -- Detects and initializes the modem * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * -1 init string invalid * + * 0 no modem found * + * 1 modem found * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +int NullModemClass::Detect_Modem( SerialSettingsType *settings, bool reconnect ) +{ + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + int status; + int error_count = 0; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + /* + ** Get resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + strcpy( buffer, Text_String( TXT_INITIALIZING_MODEM ) ); + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + int lines = Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 40 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + if (lines == 1) { + Fancy_Text_Print(buffer, x + width/2, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_CENTER); + } else { + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + } + + Show_Mouse(); + + + HMWaitForOK( 0, NULL ); + + + /* + ** OK, lets not mess about any more. Just turn on echo, verbose, and result codes + ** before we even begin. At least this way when we get an error later on we have already + ** removed all the steps we use to try and recover. + ** The timeouts need to be quite small in case the modem is turned off. + */ + + /* + ** Turn on result codes. + */ + Send_Modem_Command ( "ATQ0", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Make result codes verbose. + */ + Send_Modem_Command ( "ATV1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + /* + ** Turn on echo. + */ + Send_Modem_Command ( "ATE1", '\r', buffer, 81, DEFAULT_TIMEOUT / 2, 2); + + ModemVerboseOn = true; + ModemEchoOn = true; + + + + + + /* + ** Try sending a plain old AT command to the modem. Now that we have theoretically + ** turned on verbose result codes we should get an 'OK' back. + ** + */ + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 2 ); + + if (status < ASSUCCESS) { +#ifdef FIXIT_MULTI_SAVE + return(false); +#else + if (WWMessageBox().Process(TXT_ERROR_NO_RESP, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; +#endif + } + + /* + ** Send the user supplied modem init string + */ + if (settings->InitStringIndex == -1) { + status = Send_Modem_Command( "", '\r', buffer, 81, 300, 1 ); + } else { + /* + ** Split up the init string into seperate strings if it contains one or more '|' characters. + ** This character acts as a carriage return/pause. + */ + char *istr = new char [2 + strlen( Session.InitStrings [settings->InitStringIndex] )]; + char *tokenptr; + strcpy (istr, Session.InitStrings [settings->InitStringIndex] ); + + /* + ** Tokenise the string and send it in chunks + */ + tokenptr = strtok ( istr, "|" ); + while ( tokenptr ) { + + status = Send_Modem_Command( tokenptr, '\r', buffer, 81, 3000, 1 ); + /* + ** Handle error case. + */ + if (status < ASSUCCESS) { + if (WWMessageBox().Process(TXT_ERROR_NO_INIT, TXT_IGNORE, TXT_CANCEL)) { + delete istr; + return( false ); + } +#ifdef WIN32 + error_count++; +#endif //WIN32 + break; + } + + tokenptr = strtok ( NULL, "|"); + + } + } + +#ifdef WIN32 + + if (settings->Port == 1 && ModemRegistry) { + /* + ** Send the init strings from the registry if available + */ + char send_string[256] = {"AT"}; + + /* + ** Send the init string for hardware flow control + */ + if (settings->HardwareFlowControl){ + if (ModemRegistry->Get_Modem_Hardware_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Hardware_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, 300, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + /* + ** Send the init string for no flow control + */ + if (ModemRegistry->Get_Modem_No_Flow_Control()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_No_Flow_Control()); + status = Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_FLOW_CONTROL_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for data compresseion + */ + if (settings->Compression){ + + if (ModemRegistry->Get_Modem_Compression_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + + if (ModemRegistry->Get_Modem_Compression_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Compression_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_COMPRESSION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + + + /* + ** Send the string for error correction + */ + if (settings->ErrorCorrection){ + + if (ModemRegistry->Get_Modem_Error_Correction_Enable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Enable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + }else{ + if (ModemRegistry->Get_Modem_Error_Correction_Disable()) { + strcpy (&send_string[2], ModemRegistry->Get_Modem_Error_Correction_Disable()); + Send_Modem_Command (send_string, '\r', buffer, 81, DEFAULT_TIMEOUT, 1); + if (status != MODEM_CMD_OK && status != MODEM_CMD_0) { + if (WWMessageBox().Process(TXT_NO_ERROR_CORRECTION_RESPONSE, TXT_IGNORE, TXT_CANCEL)) return (false); + } + } + } + } + +#endif //WIN32 + + + /* + ** We require that auto-answer be disabled so turn it off now. + */ + status = Send_Modem_Command( "ATS0=0", '\r', buffer, 81, DEFAULT_TIMEOUT, INIT_COMMAND_RETRIES ); + if (status != MODEM_CMD_OK) { + if (WWMessageBox().Process(TXT_ERROR_NO_DISABLE, TXT_IGNORE, TXT_CANCEL)) return( false ); + error_count++; + } + + /* + ** If we had an unreasonable number of ignored errors then return failure + */ + if (error_count >= 3) { + WWMessageBox().Process(TXT_ERROR_TOO_MANY, TXT_OK); + return (false); + } + + return( true ); +} + + +/*************************************************************************** + * NullModemClass::Dial_Modem -- dials a number passed * + * * + * INPUT: * + * settings ptr to SerialSettings structure * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Dial_Modem( char *string, DialMethodType method, bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = SeenBuff.Get_Width()/320; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + + int x,y,width,height; // dialog dimensions + char buffer[80*3]; + + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( buffer, Text_String( TXT_MODEM_CONNERR_REDIALING ) ); + } else { + strcpy( buffer, Text_String( TXT_DIALING ) ); + } + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(buffer, SeenBuff.Get_Height(), width, height); + + int text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /*------------------------------------------------------------------------ + Draw the dialog + ------------------------------------------------------------------------*/ + Hide_Mouse(); + if ( !reconnect ) { + Load_Title_Page(true); + } + + Dialog_Box(x, y, width, height); + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 *RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + Show_Mouse(); + + /* + ** Start waiting for connection response + */ +#ifdef WIN32 + SerialPort->Set_Modem_Dial_Type((WinCommDialMethodType) method); + /* + ** Clear out any old modem results that might be hanging around + */ + SerialPort->Get_Modem_Result(60, buffer, 81); + /* + ** Dial that sucker + */ + SerialPort->Dial_Modem(string); + +#else //WIN32 + HMSetDialingMethod( Port, (int)method ); + status = HMDial( Port, string ); + +#endif //WIN32 + + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = ModemWaitCarrier; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + Commands->Draw_All(); + } + + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + /*..................................................................... + Process input + .....................................................................*/ + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( buffer, "NO D", 4 ) == 0 ) { + dialstatus = DIAL_NO_DIAL_TONE; + process = false; + } else if ( strncmp( buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + process = false; + } + } + + if (delay <= 0) { + process = false; + } + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Dial_Modem */ + + +/*************************************************************************** + * NullModemClass::Answer_Modem -- waits for call and answers * + * * + * INPUT: * + * reconnect whether this is to reconnect * + * * + * OUTPUT: * + * status DialStatus * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 ST : Added Win32 support * + *=========================================================================*/ +DialStatusType NullModemClass::Answer_Modem( bool reconnect ) +{ + /* + ** Get the resolution factor + */ +// int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + /*------------------------------------------------------------------------ + Button Enumerations + ------------------------------------------------------------------------*/ + enum { + BUTTON_CANCEL = 100, + }; + + /*------------------------------------------------------------------------ + Redraw values: in order from "top" to "bottom" layer of the dialog + ------------------------------------------------------------------------*/ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /*------------------------------------------------------------------------ + Dialog variables + ------------------------------------------------------------------------*/ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true +#ifndef WIN32 + int status; +#endif //WIN32 + int delay; + DialStatusType dialstatus; + bool ring = false; + + int x,y,width,height; // dialog dimensions + int text_width; + char text_buffer[80*3]; + char comm_buffer[80*3]; + + /*------------------------------------------------------------------------ + Determine the dimensions of the text to be used for the dialog box. + These dimensions will control how the dialog box looks. + ------------------------------------------------------------------------*/ + if (reconnect) { + strcpy( text_buffer, Text_String( TXT_MODEM_CONNERR_WAITING ) ); + } else { + strcpy( text_buffer, Text_String( TXT_WAITING_FOR_CALL ) ); + } + + Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, + x + ((width - (String_Pixel_Width( Text_String( TXT_CANCEL ) ) + 8 * RESFACTOR)) >> 1), + y + height - (FontHeight + FontYSpacing + 2 * RESFACTOR) - 10 * RESFACTOR); + + /*------------------------------------------------------------------------ + Initialize + ------------------------------------------------------------------------*/ + Set_Logic_Page(SeenBuff); + Load_Title_Page(true); + + Input = KN_NONE; + + /*------------------------------------------------------------------------ + Create the list + ------------------------------------------------------------------------*/ + Commands = &cancelbtn; + + Commands->Flag_List_To_Redraw(); + + /* + ** Sets up the ability to abort modem commands when any input is in the + ** Keyboard buffer. This also calls the game CallBack() and Input(). + */ + Setup_Abort_Modem(); + + /*------------------------------------------------------------------------ + Main Processing Loop + ------------------------------------------------------------------------*/ + process = true; + delay = 60000; + while (process) { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display=REDRAW_ALL; + } +#endif //WIN32 + + /*..................................................................... + Refresh display if needed + .....................................................................*/ + if (display) { + Hide_Mouse(); + if (display >= REDRAW_BACKGROUND) { + /*............................................................... + Refresh the backdrop + ...............................................................*/ + if ( !reconnect ) { + Load_Title_Page(true); + } + + /*............................................................... + Draw the background + ...............................................................*/ + Dialog_Box(x, y, width, height); + + /*............................................................... + Draw the labels + ...............................................................*/ + Draw_Caption(TXT_NONE, x, y, width); + + Fancy_Text_Print(text_buffer, SeenBuff.Get_Width()/2 - text_width/2, y + 25 * RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT); + + Commands->Draw_All(); + } + Show_Mouse(); + display = REDRAW_NONE; + } + +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, comm_buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, comm_buffer, 81 ); +#endif //WIN32 + + + /*..................................................................... + Process input + .....................................................................*/ + if (!Input) Input = Commands->Input(); + switch (Input) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + dialstatus = DIAL_CANCELED; + process = false; + break; + + default: + break; + } + + if (process) { + if ( strncmp( comm_buffer, "RING", 4 ) == 0 ) { + + strcpy( text_buffer, Text_String( TXT_ANSWERING ) ); + + Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_TEXT); + Format_Window_String(text_buffer, SeenBuff.Get_Height(), width, height); + + text_width = width; + width = max(width, 90 * RESFACTOR); + width += 40 * RESFACTOR; + height += 60 * RESFACTOR; + + x = (SeenBuff.Get_Width() - width) / 2; + y = (SeenBuff.Get_Height() - height) / 2; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port ((unsigned char*)"ATA\r", strlen("ATA\r")); +#else //WIN32 + PortKillTime( Port, 100 ); + HMWaitForOK( 0, NULL ); + status = HMAnswer( Port ); +#endif //WIN32 + + ring = true; + delay = ModemWaitCarrier; + display = REDRAW_ALL; + } else if ( strncmp( comm_buffer, "CON", 3 ) == 0 ) { + memset (ModemRXString, 0, 80); + strncpy (ModemRXString, comm_buffer, 79); + dialstatus = DIAL_CONNECTED; + process = false; + } else if ( strncmp( comm_buffer, "BUSY", 4 ) == 0 ) { + dialstatus = DIAL_BUSY; + process = false; + } else if ( strncmp( comm_buffer, "NO C", 4 ) == 0 ) { + dialstatus = DIAL_NO_CARRIER; + process = false; + } else if ( strncmp( comm_buffer, "ERRO", 4 ) == 0 ) { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_ERROR, TXT_OK); + process = false; + } + } + +#ifdef WIN32 + + if (delay <= 0) { + if (ring) { + if (SerialPort->Get_Modem_Status() & CD_SET) { + sprintf(ModemRXString, "%s", "Connected"); + dialstatus = DIAL_CONNECTED; + } else { + dialstatus = DIAL_ERROR; + WWMessageBox().Process(TXT_ERROR_TIMEOUT, TXT_OK); + } + process = false; + } else { + delay = 60000; + } + } +#else //WIN32 + + if (delay <= 0) { + if (ring) { + process = false; + } else { + delay = 60000; + } + } +#endif //WIN32 + + } + + Remove_Abort_Modem(); + + return( dialstatus ); + +} /* end of Answer_Modem */ + + +/*************************************************************************** + * NullModemClass::Hangup_Modem -- hangs up the modem * + * * + * INPUT: * + * none * + * * + * OUTPUT: * + * status successful or not * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 DRD : Created. * + * 8/2/96 : Added Win32 support * + *=========================================================================*/ +bool NullModemClass::Hangup_Modem( void ) +{ + int status; + int delay; + char buffer[81]; + char escape[4]; + + /* + ** Turn modem servicing off in the callback routine. + */ + Session.ModemService = false; + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + /* + ** Toggle DTR low then high + */ +#ifdef WIN32 + SerialPort->Set_Serial_DTR(FALSE); + Delay(3200/60); + SerialPort->Set_Serial_DTR(TRUE); +#else //WIN32 + + SetDtr( Port, 0 ); + PortKillTime( Port, 3200 ); + SetDtr( Port, 1 ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + Session.ModemService = true; + return( true ); + } + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + } + + /* + ** Send modem break commmand + */ + escape[0] = ModemEscapeCode; + escape[1] = ModemEscapeCode; + escape[2] = ModemEscapeCode; + escape[3] = 0; + +#ifdef WIN32 + SerialPort->Write_To_Serial_Port((unsigned char*)escape, 3); +#else //WIN32 + status = HMSendStringNoWait( Port, escape, -1 ); +#endif //WIN32 + + + delay = ModemGuardTime; + while ( delay > 0 ) { +#ifdef WIN32 + delay = SerialPort->Get_Modem_Result(delay, buffer, 81); +#else //WIN32 + delay = HMInputLine( Port, delay, buffer, 81 ); +#endif //WIN32 + + if ( strncmp( buffer, "OK", 2 ) == 0 ) { + break; + } + } + + /* + ** Send the hangup command + */ + status = Send_Modem_Command( "ATH", '\r', buffer, 81, ModemHangupDelay, 1 ); + + if (status == MODEM_CMD_OK) { + } else { + Session.ModemService = true; + return( false ); + } + + /* + ** Send spurious ATZ command for no apparent reason + */ + status = Send_Modem_Command( "ATZ", '\r', buffer, 81, 5000, 1 ); + + if (status != MODEM_CMD_OK) { + Session.ModemService = true; + return( false ); + } + + Session.ModemService = true; + return( true ); + +} /* end of Hangup_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Modem_Echo -- Sets the echo callback function pointer * + * * + * * + * * + * INPUT: Ptr to callback function * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:48PM ST : Documented and added WIn32 support * + *=============================================================================================*/ +void NullModemClass::Setup_Modem_Echo( void ( *func )( char c) ) +{ +#ifdef WIN32 + SerialPort->Set_Echo_Function(func); +#else //WIN32 + HMSetUpEchoRoutine( func ); +#endif //WIN32 + +} /* end of Setup_Modem_Echo */ + + +/*********************************************************************************************** + * NMC::Remove_Modem_Echo -- Set the echo function callback pointer to null * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:50PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Modem_Echo( void ) +{ +// Smart_Printf( "Remove Echo modem code\n" ); + HMSetUpEchoRoutine( NULL ); + +} /* end of Remove_Modem_Echo */ + + + +/*********************************************************************************************** + * NMC::Print_EchoBuf -- Print out the contents of the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Print_EchoBuf( void ) +{ + for (int i = 0; i < strlen(NullModem.EchoBuf); i++) { + if (NullModem.EchoBuf[i] == '\r') { + NullModem.EchoBuf[i] = 1; + } else { + if (NullModem.EchoBuf[i] == '\n') { + NullModem.EchoBuf[i] = 2; + } + } + } +// Smart_Printf( "Echo buffer length %d (%s)\n", NullModem.EchoCount, NullModem.EchoBuf ); +} + + + +/*********************************************************************************************** + * NMC::Reset_EchoBuf -- Empties the echo buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:51PM ST : Documented * + *=============================================================================================*/ +void NullModemClass::Reset_EchoBuf( void ) +{ + *EchoBuf = 0; + EchoCount = 0; +} + + +/*********************************************************************************************** + * NMC::Abort_Modem -- Checks for user input so that modem operations can be aborted * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: ASUSERABORT if abort key pressed. ASSUCESS otherwise. * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 12:52PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Abort_Modem( PORT * ) +{ + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + }; + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Get user input + */ + Input = Commands->Input(); + + switch ( Input ) { + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + return( ASUSERABORT ); + } + + return( ASSUCCESS ); + +} /* end of Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Setup_Abort_Modem -- sets the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 2:59PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Setup_Abort_Modem( void ) +{ + +#ifdef WIN32 + + SerialPort->Set_Abort_Function((int(*)(void))Abort_Modem); + +#else //WIN32 + /* + ** save off original abort modem function to later restore + */ + OrigAbortModemFunc = _AbortModemFunctionPtr; + HMSetUpAbortKey( ESC ); + SetAbortModemFunctionPtr( Abort_Modem ); +#endif //WIN32 + +} /* end of Setup_Abort_Modem */ + + +/*********************************************************************************************** + * NMC::Remove_Abort_Modem -- Removes the modem abort function pointer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:01PM ST : Documented / Win32 support added * + *=============================================================================================*/ +void NullModemClass::Remove_Abort_Modem( void ) +{ + +#ifdef WIN32 + SerialPort->Set_Abort_Function(NULL); + +#else //WIN32 + + HMSetUpAbortKey( NOKEY ); + + /* + ** Restore the original abort modem function + */ + _AbortModemFunctionPtr = OrigAbortModemFunc; +#endif //WIN32 + +} /* end of Remove_Abort_Modem */ + + + +/*********************************************************************************************** + * NMC::Change_IRQ_Priority -- Increases the priority of the serial interrupt * + * * + * * + * * + * INPUT: Interrupt request number * + * * + * OUTPUT: ASSUCCESS if changed * + * * + * WARNINGS: The Win32 version of this function does nothing. * + * Priorities are controlled by windoze * + * * + * HISTORY: * + * 8/2/96 3:03PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Change_IRQ_Priority( int irq ) +{ +#ifdef WIN32 + + irq = irq; + return ( ASSUCCESS ); + +#else //WIN32 + + if (irq != OldIRQPri) { + OldIRQPri = irq; + return( Change8259Priority( irq ) ); + } else { + return( ASSUCCESS ); + } + +#endif //WIN32 + +} /* end of Change_IRQ_Priority */ + + + +/*********************************************************************************************** + * NMC::Get_Modem_Status -- returns status of modem control bits * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Modem status * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:06PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Get_Modem_Status( void ) +{ + int modemstatus; + int status; + char buffer[81]; + +#ifdef WIN32 + modemstatus = SerialPort->Get_Modem_Status(); +#else //WIN32 + modemstatus = GetModemStatus( Port ); +#endif //WIN32 + + status = Send_Modem_Command( "AT", '\r', buffer, 81, DEFAULT_TIMEOUT, 1 ); + + if (status == MODEM_CMD_OK) { + modemstatus &= (~CD_SET); + } + + return( modemstatus ); + +} /* end of Get_Modem_Status */ + + +/*********************************************************************************************** + * NMC::Send_Modem_Command -- Sends an 'AT' command to the modem and gets the response * + * * + * * + * * + * INPUT: command to send to modem. e.g. 'ATZ' * + * terminator byte for command string * + * buffer to put modem response into * + * length of above buffer * + * delay to wait for response * + * number of times to retry when modem doesnt respond * + * * + * OUTPUT: input delay less the time it took the modem to respond * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:09PM ST : Documented / Win32 support added * + *=============================================================================================*/ +int NullModemClass::Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ) +{ + +#ifdef WIN32 + return (SerialPort->Send_Command_To_Modem(command, terminator, buffer, buflen, delay, retries)); +#else //WIN32 + + int status, tmpdelay, retry; + char tempbuf[81]; + + + *buffer = 0; + + for ( retry = 0; retry < retries; retry++ ) { + + PortKillTime( Port, 100 ); + + status = HMSendStringNoWait( Port, command, terminator ); + + + if (status < ASSUCCESS) { + return( status ); + } + + tmpdelay = delay; + while ( tmpdelay > 0 ) { + + tmpdelay = HMInputLine( Port, tmpdelay, tempbuf, 81 ); + + if ( strcmp( tempbuf, "OK" ) == 0 ) { + return( MODEM_CMD_OK ); + } else if ( strcmp( tempbuf, "0" ) == 0 ) { + return( MODEM_CMD_0 ); + } else if ( strcmp( tempbuf, "ERROR" ) == 0 ) { + return( MODEM_CMD_ERROR ); + } else if ( tempbuf[0] != 0 ) { + strncpy( buffer, tempbuf, buflen ); + } + } + + PortKillTime( Port, 100 ); + + /* + ** Send and extra return to help clear out any problems with the modem (if any). + */ + HMWaitForOK( 300, NULL ); + status = HMSendString( Port, "" ); + HMWaitForOK( 0, NULL ); + } + + return( tmpdelay ); + +#endif //WIN32 +} + + +/*********************************************************************************************** + * NMC::Verify_And_Convert_To_Int -- converts a text string of numbers to an int * + * * + * * + * * + * INPUT: ptr to buffer * + * * + * OUTPUT: value of text number in buffer * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/2/96 3:13PM ST : Documented * + *=============================================================================================*/ +int NullModemClass::Verify_And_Convert_To_Int( char *buffer ) +{ + int value = 0; + int len = strlen(buffer); + + + for (int i = 0; i < len; i++) { + if ( !isdigit( *(buffer + i) ) ) { + value = -1; + break; + } + } + + if (value == 0) { + value = atoi( buffer ); + } + + return( value ); + +} /* end of Verify_And_Convert_To_Int */ + +/*************************** end of nullmgr.cpp ****************************/ + +#endif \ No newline at end of file diff --git a/code/redalert/nullmgr.h b/code/redalert/nullmgr.h new file mode 100644 index 0000000..8f350ea --- /dev/null +++ b/code/redalert/nullmgr.h @@ -0,0 +1,223 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/NULLMGR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : CONNECT.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 3, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This is the Connection Manager for a NULL-Modem connection. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NULLMODEM_H +#define NULLMODEM_H + + +/* +********************************* Includes ********************************** +*/ +#include "nullconn.h" +#include "connmgr.h" +#include "commlib.h" + +/* +***************************** Class Declaration ***************************** +*/ +class NullModemClass : public ConnManClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + char *BuildBuf; + int MaxLen; + + char *EchoBuf; + int EchoSize; + int EchoCount; + + int OldIRQPri; + + int ModemVerboseOn; + int ModemEchoOn; + int ModemWaitCarrier; + int ModemCarrierDetect; + int ModemCarrierLoss; + int ModemHangupDelay; + int ModemGuardTime; + char ModemEscapeCode; + + static void (*OrigAbortModemFunc)(int); + static KeyNumType Input; + static GadgetClass *Commands; // button list + + /* + ** Constructor/destructor. + */ + NullModemClass (int numsend, int numreceive, int maxlen, unsigned short magicnum); + virtual ~NullModemClass (); + + /* + ** This is the main initialization routine. + */ + int Init( int port, int irq, char *dev_name, int baud, char parity, int wordlength, int stopbits, int flowcontrol ); + int Delete_Connection( void ); + virtual int Num_Connections(void); + virtual int Connection_ID(int ) {return (0);} + virtual int Connection_Index(int ) {return (0);} + int Init_Send_Queue( void ); + void Shutdown( void ); + + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + /* + ** This is how the application sends & receives messages. + */ + int Send_Message (void *buf, int buflen, int ack_req = 1); + int Get_Message (void *buf, int *buflen); + + /* + ** These are for compatibility + */ + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int = CONNECTION_NONE) + {return (Send_Message(buf,buflen,ack_req));} + virtual int Get_Private_Message (void *buf, int *buflen, int *) + {return (Get_Message(buf,buflen));} + + /* + ** The main polling routine; should be called as often as possible. + */ + virtual int Service (void); + + /* + ** Queue utility routines. The application can determine how many + ** messages are in the send/receive queues, and the queue's average + ** response time (in clock ticks). + */ + int Num_Send(void); + int Num_Receive(void); + virtual unsigned long Response_Time(void); + virtual void Reset_Response_Time(void); + void * Oldest_Send(void); + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); +#ifdef CHEAT_KEYS + virtual void Mono_Debug_Print(int index, int refresh = 0); +#endif + + /* + ** These are for compatibility + */ + virtual int Global_Num_Send(void) {return (Num_Send());} + virtual int Global_Num_Receive(void) {return (Num_Receive());} + virtual int Private_Num_Send(int = CONNECTION_NONE) + {return (Num_Send());} + virtual int Private_Num_Receive(int = CONNECTION_NONE) + {return (Num_Receive());} + + DetectPortType Detect_Port( SerialSettingsType *settings ); + int Detect_Modem( SerialSettingsType *settings, int reconnect = 0 ); + DialStatusType Dial_Modem(char *string, DialMethodType method, int reconnect = 0); + DialStatusType Answer_Modem(int reconnect = 0); + int Hangup_Modem(void); + void Setup_Modem_Echo(void (*func)(char c)); + void Remove_Modem_Echo(void); + void Print_EchoBuf(void); + void Reset_EchoBuf(void); + static int Abort_Modem(PORT *); + void Setup_Abort_Modem(void); + void Remove_Abort_Modem(void); + + int Change_IRQ_Priority(int irq); + int Get_Modem_Status(void); + int Send_Modem_Command( char *command, char terminator, char *buffer, int buflen, int delay, int retries ); + int Verify_And_Convert_To_Int( char *buffer ); + + /* + ** Private Interface. + */ + private: + + /* + ** This is a pointer to the NULL-Modem Connection object. + */ + NullModemConnClass *Connection; + int NumConnections; // # connection objects in use + +#ifdef WIN32 + /* + ** This is the Win95 port handle + */ + HANDLE PortHandle; +#else //WIN32 + /* + ** This is the Greenleaf port handle. + */ + PORT *Port; +#endif //WIN32 + + int NumSend; + int NumReceive; + unsigned short MagicNum; + + /* + ** This is the staging buffer for parsing incoming packets. + ** RXSize is the allocated size of the RX buffer. + ** RXCount is the # of characters we currently have in our buffer. + */ + char *RXBuf; + int RXSize; + int RXCount; + + /*..................................................................... + Timing parameters for all connections + .....................................................................*/ + unsigned long RetryDelta; + unsigned long MaxRetries; + unsigned long Timeout; + + /* + ** Various Statistics + */ + int SendOverflows; + int ReceiveOverflows; + int CRCErrors; +}; + +#endif + +/*************************** end of nullmgr.h ******************************/ diff --git a/code/redalert/object.cpp b/code/redalert/object.cpp new file mode 100644 index 0000000..6b39275 --- /dev/null +++ b/code/redalert/object.cpp @@ -0,0 +1,2494 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OBJECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ObjectClass::AI -- Handles generic object AI processing. * + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * ObjectClass::Detach -- Detach the specified target from this object. * + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * ObjectClass::Init -- Initializes the basic object system. * + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * ObjectClass::Mark -- Handles basic marking logic. * + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * ObjectClass::Name -- Fetches the identification name of this object. * + * ObjectClass::ObjectClass -- Default constructor for objects. * + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * ObjectClass::Passive_Click_With -- Right mouse button click process. * + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * ObjectClass::Render -- Displays the object onto the map. * + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * ObjectClass::Repair -- Handles object repair control. * + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * ObjectClass::Scatter -- Tries to scatter this object. * + * ObjectClass::Select -- Try to make this object the "selected" object. * + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * ObjectClass::Take_Damage -- Applies damage to the object. * + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * ObjectClass::Value -- Fetches the target value of this object. * + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * ObjectTypeClass::Who_Can_Build_Me -- Finds the factory building that can build this object* + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** Selected objects have a special marking box around them. This is the shapes that are +** used for this purpose. +*/ +void const * ObjectTypeClass::SelectShapes = 0; + +void const * ObjectTypeClass::PipShapes = 0; + + + +/*********************************************************************************************** + * ObjectClass::ObjectClass -- Default constructor for objects. * + * * + * This is the default constructor for objects. It is called as an inherent part of the * + * construction process for all the normal game objects instantiated. It serves merely to * + * initialize the object values to a common (default) state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Objects always start in a state of limbo. They must be Unlimbo()ed before they * + * can be used. * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass::ObjectClass(RTTIType rtti, int id) : + AbstractClass(rtti, id), + IsDown(false), + IsToDamage(false), + IsToDisplay(false), + IsInLimbo(true), + IsSelected(false), + IsAnimAttached(false), + IsFalling(false), + Riser(0), + Next(0), + Trigger(NULL), + Strength(255), + IsSelectedMask(0) // Mask showing who has selected this object +{ +} + + +/*********************************************************************************************** + * ObjectClass::Get_Image_Data -- Fetches the image data to use for this object. * + * * + * This routine will return with a pointer to the image data that should be used when * + * this object is drawn. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the shape data for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +void const * ObjectClass::Get_Image_Data(void) const +{ + return(Class_Of().Get_Image_Data()); +} + +struct Image_t *ObjectClass::Get_HDImage_Data(void) const +{ + return(Class_Of().Get_HDImage_Data()); +} + + +/*********************************************************************************************** + * ObjectClass::Name -- Fetches the identification name of this object. * + * * + * This routine will return a pointer to the identifier name for this object. This name * + * is usually short and is used in the INI files to identify this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text identifier name of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +char const * ObjectClass::Name(void) const +{ + return(Class_Of().Name()); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Coord -- Return with the exit coordinate for this object. * + * * + * Ths exit coordinate is the location that a piggy back or newly produced object will * + * appear at when it exits this object. Transports and factory buildings will utilize this * + * routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that an object will appear at when exiting this * + * object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Exit_Coord(void) const +{ + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::AI -- Handles generic object AI processing. * + * * + * This routine is used to handle the AI processing that occurs for all object types. * + * Typically, this isn't much, but there is the concept of falling that all objects can * + * be subjected to (e.g., grenades). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::AI(void) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Falling logic is handled here. + */ + if (IsFalling) { + LayerType layer = In_Which_Layer(); + + Height += Riser; + if (Height <= 0) { + Height = 0; + IsFalling = false; + Per_Cell_Process(PCP_END); + + Shorten_Attached_Anims(this); + } + if (IsAnimAttached) { + Riser -= 1; + Riser = max(Riser, -3); + } else { + Riser -= Rule.Gravity; +// Riser -= GRAVITY; + Riser = max(Riser, -100); + } + + if (layer != In_Which_Layer()) { + Map.Remove(this, layer); + Map.Submit(this, In_Which_Layer()); + + if (Class_Of().IsFootprint) { + if (In_Which_Layer() == LAYER_GROUND) { + Map.Place_Down(Coord_Cell(Center_Coord()), this); + } else { + Map.Pick_Up(Coord_Cell(Center_Coord()), this); + } + } + } + } +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Determines what action to perform on specified object. * + * * + * This routine will return that action that this object could perform if the mouse were * + * clicked over the object specified. * + * * + * INPUT: object -- Pointer to the object to check this object against when determining * + * the action to perform. * + * * + * OUTPUT: It returns that action that will be performed if the mouse were clicked over the * + * object. Since non-derived objects cannot do anything, and cannot even be * + * instantiated, this routine will always return ACTION_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(ObjectClass const *) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::What_Action -- Returns with the action to perform for this object. * + * * + * This routine is called when information on a potential action if the mouse were clicked * + * on the cell specified. This routine merely serves as a virtual placeholder so that * + * object types that can actually perform some action will override this routine to provide * + * true functionality. * + * * + * INPUT: cell -- The cell that the mouse is over and might be clicked on. * + * * + * OUTPUT: Returns with the action that this object would try to perform if the mouse were * + * clicked. Since objects at this level have no ability to do anything, this routine * + * will always returns ACTION_NONE unless it is overridden. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +ActionType ObjectClass::What_Action(CELL) const +{ + assert(this != 0); + assert(IsActive); + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::In_Which_Layer -- Fetches what layer this object is located in. * + * * + * The default layer for object location is the LAYER_GROUND. Aircraft will override this * + * routine and make adjustments as necessary according to the aircraft's altitude. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the layer that this object is located in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +LayerType ObjectClass::In_Which_Layer(void) const +{ + assert(this != 0); + assert(IsActive); + + if (Height < (FLIGHT_LEVEL - (FLIGHT_LEVEL/3))) { + return(LAYER_GROUND); + } + return(LAYER_TOP); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Ownable -- Fetches the house owner legality options for this object. * + * * + * This routine will return the ownable bits for this object. Objects at this level can't * + * really be owned by anyone, but return the full spectrum of legality just to be safe. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable flags (as a combined bitfield) for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Get_Ownable(void) const +{ + assert(this != 0); + assert(IsActive); + + return(HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Repair -- Queries whether this object can be repaired. * + * * + * Most objects cannot be repaired. This routine defaults to returning "false", but is * + * overridden by derived functions defined by object types that can support repair. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be repaired? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Repair(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Demolish -- Queries whether this object can be sold back. * + * * + * This routine is used to determine if this object can be sold. Most objects cannot be * + * but for those objects that can, this routine will be overridden as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be sold back? Typically, the answer is no. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Demolish(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +bool ObjectClass::Can_Demolish_Unit(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? * + * * + * This routine is used to determine if attacking is an option under player control with * + * respect to this unit. This routine will be overridden as necessary for those objects * + * that have the ability to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given an attack order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Fire(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Can_Player_Move -- Can the player give this object a movement mission? * + * * + * This routine is used to determine if the player has the ability to command this object * + * with a movement mission. This routine will be overridden as necessary to support this * + * ability. * + * * + * INPUT: none * + * * + * OUTPUT: Can this object be given a movement mission by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Can_Player_Move(void) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Target_Coord -- Fetches the coordinate if this object is a target. * + * * + * When the coordinate to use when firing at this object is needed, this routine will * + * provide it. Normal objects just use the center of the object for this, but there are * + * some more sophisticated objects that are not fired upon the center. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to fire at if this object is a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Target_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord_Add(XY_Coord(0, -Height), Center_Coord())); +// return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Center_Coord -- Fetches the center coordinate for the object. * + * * + * This routine will return the center coordinate for the object. The center coordinate is * + * typically the coordinate recorded in the object structure. Exceptions to this include * + * the trees and other terrain elements. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate that is considered the center point of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Center_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Render_Coord -- Fetches the coordinate to draw this object at. * + * * + * This routine will return the coordinate to base the drawing of this object's graphic * + * at. This is adjusted according to the nature of the graphic associated with this * + * object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the coordinate to draw the graphic of this object at. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Render_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Docking_Coord -- Fetches the coordinate to dock at this object. * + * * + * This routine returns the coordinate that a potential docking object should home in on. * + * Typically, this the center of the object, but in certain cases it is adjusted off center * + * according to the object type. An example of this would be the airfield. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate that a docking object should head for. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Docking_Coord(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Center_Coord()); +} + + +/*********************************************************************************************** + * ObjectClass::Sort_Y -- Returns the coordinate used for display order sorting. * + * * + * This routine will return the value to be used for object sorting. The sorting ensures * + * that the object are rendered from a top to bottom order. Certain object use a sorting * + * value different from their center coordinate. This is true if the object "touches the * + * ground" at a point that is different from the object's center point. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value to use as the Y sorting value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Sort_Y(void) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Coord -- Fetches the coordinate a projectile will launch from. * + * * + * For those objects that fire, the coordinate that the projectile it launches will appear * + * at the location specified by the return value from this function. * + * * + * INPUT: which -- Which weapon to consider when determining fire coordinate? * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the coordinate that a launched projectile will appear at if this * + * object were to fire the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE ObjectClass::Fire_Coord(int ) const +{ + assert(this != 0); + assert(IsActive); + + return(Coord); +} + + +/*********************************************************************************************** + * ObjectClass::Record_The_Kill -- Records this object as killed by the specified object. * + * * + * This routine is called when this object is killed. If the source of the death is known, * + * then a pointer to the responsible object is provided as a parameter. * + * * + * INPUT: source -- Pointer to the cause of this unit's death. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Record_The_Kill(TechnoClass * ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Do_Shimmer -- Shimmers this object if it is cloaked. * + * * + * When an object is cloaked, there are several conditions that would cause it to shimmer * + * and thus reveal itself. When such a condition arrises, this function is called. If the * + * object is cloaked, then it will shimmer. At this derivation level, cloaking is * + * undefined. Objects that can cloak will override this function as necessary. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Do_Shimmer(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Exit_Object -- Causes the specified object to leave this object. * + * * + * This routine is called, typically, by a transport building type that requires an object * + * to leave it. This routine will place the object at a suitable location or return * + * a value indicating why not. * + * * + * INPUT: object -- Pointer to the object that wishes to leave this object. * + * * + * OUTPUT: Returns the success value of the attempt: * + * 0: Object could not be placed -- ever * + * 1: Object placement is temporarily delayed -- try again later. * + * 2: Object placement proceeded normally * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Exit_Object(TechnoClass *) +{ + assert(this != 0); + assert(IsActive); + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Hidden -- Called when this object becomes hidden from the player. * + * * + * This routine is called when the object becomes hidden from the player. It can result in * + * lost targeting and tracking abilities with respect to the hidden object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Hidden(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Look -- Called when this object needs to reveal terrain. * + * * + * This routine is called when the object needs to look around the terrain. For player * + * owned objects, the terrain is revealed. For non-player objects, not effect occurs. * + * * + * INPUT: incremental -- If true, then the looking algorithm will only examine the edges * + * of the sight range. This is more efficient and work well if the * + * object has only moved one cell since the last time it has performed * + * the look operation. * + * * + * OUTPUT: none * + * * + * WARNINGS: This can be a time consuming operation. Call only when necessary. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Look(bool ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the object specified. * + * * + * This routine is called when this object is selected and the mouse was clicked on the * + * tactical map. An action is required from the object. The object that the mouse was * + * over and the tentative action to perform are provided as parameters. * + * * + * INPUT: action -- The requested action to perform with the object specified. * + * * + * object -- The object that the action should be performed on. This object is * + * what the mouse was over when the click occurred. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , ObjectClass *) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Active_Click_With -- Dispatches action on the specified cell. * + * * + * This routine will dispatch the action requested upon the cell specified. It is called * + * when the mouse is clicked over a cell while this object is selected. * + * * + * INPUT: action -- The action to perform. * + * * + * cell -- The location (cell) to perform this action upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Active_Click_With(ActionType , CELL ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Clicked_As_Target -- Triggers target selection animation. * + * * + * This routine is called when this object is the target of some player click action. * + * For more sophisticated object, this will trigger the object to begin flashing a few * + * times. At this level, no action is performed. * + * * + * INPUT: flashes -- The requested number of times to flash this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Clicked_As_Target(HousesType house, int) // 2019/09/20 JAS - Added record of who clicked on the object +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::In_Range -- Determines if the coordinate is within weapon range. * + * * + * This routine will determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to check to see if it is within weapon range. * + * * + * which -- The weapon to check against. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: bool; Is the specified coordinate within weapon range for the weapon type * + * specified? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::In_Range(COORDINATE , int) const +{ + assert(this != 0); + assert(IsActive); + + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Weapon_Range -- Returns the weapon range for the weapon specified. * + * * + * This routine will return the weapon range according to the type of weapon specified. * + * * + * INPUT: which -- The weapon to fetch the range from. * + * 0: primary weapon * + * 1: secondary weapon * + * * + * OUTPUT: Returns with the range (in leptons) of the weapon specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Weapon_Range(int) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Scatter -- Tries to scatter this object. * + * * + * This routine is used when the object should scatter from its current location. It * + * applies to units that have the ability to move. * + * * + * INPUT: coord -- The source of the threat that is causing the scatter. * + * * + * forced-- Whether this scatter attempt is serious and scattering should occur * + * regardless of what is doing now. * + * * + * OUTPUT: none * + * * + * WARNINGS: This may or may not cause the object to scatter. It is merely a request to the * + * object that it would be good if it were to scatter. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Scatter(COORDINATE , bool, bool) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Catch_Fire -- Called when animation is attached to this object. * + * * + * This routine is called when an animation is attached to this object. It might be a * + * fire animation (hence the name), but it might also be smoke or any other animation * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object caught on fire by this routine? Actually, this is really * + * the answer to this question; "Is this animation attaching to this object * + * that doesn't already have an animation attached?" * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Catch_Fire(void) +{ + assert(this != 0); + assert(IsActive); + + return false; +} + + +/*********************************************************************************************** + * ObjectClass::Fire_Out -- Informs object that attached animation has finished. * + * * + * This routine is called if there is an attached animation on this object and that * + * animation has finished. Typically, this is necessary for when trees are on fire. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Fire_Out(void) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Value -- Fetches the target value of this object. * + * * + * This routine will return the target value of this object. The higher the number, the * + * better the object will be as a target. This routine is called when searching for * + * targets. Generic objects have no target potential, and this routine returns zero to * + * reflect that. Other object types will override this routine to return the appropriate * + * target value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the value of this object as a target. Higher values mean better * + * target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectClass::Value(void) const +{ + assert(this != 0); + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * ObjectClass::Get_Mission -- Fetches the current mission of this object. * + * * + * Generic objects don't have a mission, so this routine will just return MISSION_NONE. * + * However, techno objects do have a mission and this routine is overloaded to handle * + * those objects in order to return the correct mission value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current mission being followed by this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +MissionType ObjectClass::Get_Mission(void) const +{ + assert(this != 0); + assert(IsActive); + + return(MISSION_NONE); +} + + +/*********************************************************************************************** + * ObjectClass::Repair -- Handles object repair control. * + * * + * This routine will control object repair mode. At the object level, no repair is * + * possible, so it is expected that any object that can repair will override this function * + * as necessary. * + * * + * INPUT: control -- The repair control parameter. * + * 0 = turn repair off * + * 1 = turn repair on * + * -1 = toggle repair state * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Repair(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Sell_Back -- Sells the object -- if possible. * + * * + * This routine is called to sell back the object. Override this routine for the more * + * sophisticated objects that can actually be sold back. Normal objects can't be sold and * + * this routine does nothing as a consequence. * + * * + * INPUT: control -- How to control the sell state of this object. * + * 0 = stop selling. * + * 1 = start selling. * + * -1 = toggle selling state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Sell_Back(int ) +{ + assert(this != 0); + assert(IsActive); +} + + +/*********************************************************************************************** + * ObjectClass::Move -- Moves (by force) the object in the desired direction. * + * * + * This routine will instantly move the object one cell in the specified direction. It * + * moves the object by force. This is typically ONLY used by the scenario editor * + * process. * + * * + * INPUT: facing -- The direction to move the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: Naturally, this can cause illegal placement situations -- use with caution. * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Move(FacingType facing) +{ + assert(this != 0); + assert(IsActive); + + COORDINATE coord; + + Mark(MARK_UP); + coord = Adjacent_Cell(Coord, facing); + if (Can_Enter_Cell(Coord_Cell(coord)) == MOVE_OK) { + Coord = coord; + } + Mark(MARK_DOWN); +} + + +// Object selection list is switched with player context for GlyphX. ST - 4/17/2019 9:42AM +extern void Logic_Switch_Player_Context(ObjectClass *object); + +/*********************************************************************************************** + * ObjectClass::Unselect -- This will un-select the object if it was selected. * + * * + * This routine brings a currently selected object into an unselected state. This is * + * needed when another object becomes selected as well as if the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect(void) +{ + assert(this != 0); + assert(IsActive); + + //if (IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (Is_Selected_By_Player()) { + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + + IsSelected = false; + + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Unselected_By_Player(); + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + } +} + + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players -- This will un-select the object if it was selected * + * from all players * + * * + * This routine brings a currently selected object into an unselected state for all players.* + * This is needed when the object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players(void) +{ + CurrentObject.Delete_All(this); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + IsSelected = false; + IsSelectedMask = 0; + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + +/*********************************************************************************************** + * ObjectClass::Unselect_All_Players_Except_Owner -- This will un-select the object if it was * + * selected for all players except for the object's owner * + * * + * This routine brings a currently selected object into an unselected state for all players.* + * This is needed when the object cloaks. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/2019 JAS : Created. * + *=============================================================================================*/ +void ObjectClass::Unselect_All_Players_Except_Owner(void) +{ + CurrentObject.Delete_All_Except(this, Owner()); + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_UP); + } + + int owner_mask = 1 << Owner(); + if (IsSelectedMask & owner_mask) + { + IsSelected = true; + IsSelectedMask = owner_mask; + } + else + { + IsSelected = false; + IsSelectedMask = 0; + } + + if (In_Which_Layer() == LAYER_GROUND) { + Mark(MARK_OVERLAP_DOWN); + } +} + +/*********************************************************************************************** + * ObjectClass::Select -- Try to make this object the "selected" object. * + * * + * This routine is used to make this object into the one that is "selected". A selected * + * object usually displays a floating bar graph and is available to be given orders from * + * the player's I/O. * + * * + * INPUT: allow_mixed -- Allow a mix of player and non-player controlled units? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + * 06/12/1995 JLB : Cannot select a loaner object. * + * 07/23/1995 JLB : Adds to head or tail depending on leader type flag. * + *=============================================================================================*/ +bool ObjectClass::Select(bool allow_mixed) +{ + assert(this != 0); + assert(IsActive); + + //if (!Debug_Map && (IsSelected || !Class_Of().IsSelectable)) { + // return(false); + //} + // Updated to function for multiplayer - 6/26/2019 JAS + if (!Debug_Map && (Is_Selected_By_Player() || !Class_Of().IsSelectable)) { + return(false); + } + + if (!Debug_Map && Can_Player_Move() && Is_Techno() && ((TechnoClass *)this)->IsALoaner) { + return(false); + } + + /* + ** Don't allow selection if the object is still in the air. + */ + if (Height > 0 && (What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_VESSEL || What_Am_I() == RTTI_INFANTRY)) { + return(false); + } + + /* + ** Don't allow selection of object when in building placement mode. + */ + if (Map.PendingObject) { + return(false); + } + + if (!allow_mixed) { + /* + ** If selecting an object of a different house than the player's, make sure that + ** the entire selection list is cleared. + */ + for (int i = 0; i < CurrentObject.Count(); i++) { + HouseClass * tryhptr = HouseClass::As_Pointer(Owner()); + HouseClass * oldhptr = HouseClass::As_Pointer(CurrentObject[i]->Owner()); + // if (Owner() != CurrentObject[0]->Owner() || CurrentObject[0]->Owner() != PlayerPtr->Class->House) { + if (oldhptr->IsPlayerControl != tryhptr->IsPlayerControl) { + Unselect_All(); + break; + } + } + } + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_UP); + + //IsSelected = true; + // Updated to function for multiplayer - 6/26/2019 JAS + Set_Selected_By_Player(); + + if (In_Which_Layer() == LAYER_GROUND) Mark(MARK_OVERLAP_DOWN); + + return(true); +} + + +/*********************************************************************************************** + * ObjectClass::Render -- Displays the object onto the map. * + * * + * This routine will determine the location of the object and if it is roughly on the * + * visible screen, it will display it. Not displaying objects that are not on the screen * + * will save valuable time. * + * * + * INPUT: bool; Should the render be forced regardless of whether the object is flagged to * + * be redrawn? * + * * + * OUTPUT: bool; Was the draw code called for this object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Render(bool forced) const +{ + assert(this != 0); + assert(IsActive); + + int x, y; + COORDINATE coord = Render_Coord(); + CELL cell = Coord_Cell(coord); + + if (Debug_Map || Debug_Unshroud || ((forced || IsToDisplay) && IsDown && !IsInLimbo)) { + const_cast(this)->IsToDisplay = false; // added const_cast ST - 5/9/2019 + + if (Map.Coord_To_Pixel(coord, x, y)) { + + /* + ** Draw the object itself + */ + Draw_It(x, y, WINDOW_TACTICAL); + +#ifdef SCENARIO_EDITOR + /* + ** Draw the trigger attached to the object. Draw_It is window- + ** relative, so add the window's x-coord to 'x'. + */ + if (Debug_Map && Trigger.Is_Valid()) { + Fancy_Text_Print(Trigger->Class->IniName, + x + (WinX), y, + &ColorRemaps[PCOLOR_RED], TBLACK, + TPF_CENTER | TPF_NOSHADOW | TPF_6POINT); + } +#endif + + return(true); + } + } + return(false); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * ObjectClass::Debug_Dump -- Displays status of the object class to the mono monitor. * + * * + * This routine is used to display the current status of the object class to the mono * + * monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 1);mono->Printf("%-18.18s", Text_String(Full_Name())); + if (Next != NULL) { + mono->Set_Cursor(20, 5);mono->Printf("%08X", Next->As_Target()); + } + if (Trigger.Is_Valid()) { + mono->Text_Print(Trigger->Class->IniName, 11, 3); + } + mono->Set_Cursor(34, 1);mono->Printf("%3d", Strength); + + mono->Fill_Attrib(1, 13, 12, 1, IsDown ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 14, 12, 1, IsToDamage ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 15, 12, 1, IsToDisplay ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(1, 16, 12, 1, IsInLimbo ? MonoClass::INVERSE : MonoClass::NORMAL); + // Updated to function for multiplayer - 6/26/2019 JAS + mono->Fill_Attrib(1, 17, 12, 1, Is_Selected_By_Player() ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 13, 12, 1, IsAnimAttached ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(23, 14);mono->Printf("%d", Riser); + mono->Fill_Attrib(14, 12, 14, 1, IsFalling ? MonoClass::INVERSE : MonoClass::NORMAL); + + AbstractClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * ObjectClass::Mark_For_Redraw -- Marks object and system for redraw. * + * * + * This routine will mark the object and inform the display system * + * that appropriate rendering is needed. Whenever it is determined * + * that an object needs to be redrawn, call this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This is a subordinate function to the function Mark(). If an object needs to * + * be redrawn it is probably better to call the function Mark(MARK_CHANGE) rather * + * than this function. This function does not inform the map system that * + * overlapping objects are to be redrawn and thus unless you are really sure that * + * this routine should be called, don't. * + * * + * HISTORY: * + * 05/08/1994 JLB : Created. * + * 12/23/1994 JLB : Flags map and flags unit only. * + *=============================================================================================*/ +void ObjectClass::Mark_For_Redraw(void) +{ + assert(this != 0); + assert(IsActive); + + if (!IsToDisplay) { + IsToDisplay = true; + + /* + ** This tells the map rendering logic to "go through the motions" and call the + ** rendering function. In the rendering function, it will sort out what gets + ** rendered and what doesn't. + */ + Map.Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * ObjectClass::Limbo -- Brings the object into a state of limbo. * + * * + * An object brought into a state of limbo by this routine can be safely deleted. This * + * routine will remove the object from all game lists and tracking systems. It is called * + * prior to deleting the object or placing the object "on ice". * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the object successfully placed in limbo? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Limbo(void) +{ + assert(this != 0); + assert(IsActive); + + if (GameActive && !IsInLimbo) { + + //Unselect(); + // Updated to function for multiplayer - 6/26/2019 JAS + Unselect_All_Players(); + + Detach_All(); + Mark(MARK_UP); + + /* + ** Remove the object from the appropriate display list. + */ + Map.Remove(this, In_Which_Layer()); + + /* + ** Remove the object from the logic processing list. + */ + if (Class_Of().IsSentient) { + Logic.Delete(this); + } + + Hidden(); + IsInLimbo = true; + IsToDisplay = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Unlimbo -- Brings the object into the game system. * + * * + * This routine will place the object into the game tracking and display systems. It is * + * called as a consequence of creating the object. Every game object must be unlimboed at * + * some point. * + * * + * INPUT: coord -- The coordinate to place the object into the game system. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the game object successfully unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Sets object strength. * + *=============================================================================================*/ +bool ObjectClass::Unlimbo(COORDINATE coord, DirType ) +{ + assert(this != 0); + assert(IsActive); + if (GameActive && IsInLimbo && !IsDown) { + if (ScenarioInit || Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { + IsInLimbo = false; + IsToDisplay = false; + Coord = Class_Of().Coord_Fixup(coord); + + if (Mark(MARK_DOWN)) { + if (IsActive) { + + /* + ** Add the object to the appropriate map layer. This layer is used + ** for rendering purposes. + */ + if (In_Which_Layer() != LAYER_NONE) { + Map.Submit(this, In_Which_Layer()); + } + + if (Class_Of().IsSentient) { + Logic.Submit(this); + } + } + return(true); + } + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Detach -- Detach the specified target from this object. * + * * + * This routine is called when the object (as specified) is to be removed from the game * + * engine and thus, all references to it must be severed. Typically, the only thing * + * checked for at this level is the attached trigger. * + * * + * INPUT: target -- The target that will be removed from the game system. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach(TARGET target, bool ) +{ + if (Trigger.Is_Valid() && Is_Target_Trigger(target) && Trigger->As_Target() == target) { + Attach_Trigger(NULL); + } +} + + +/*********************************************************************************************** + * ObjectClass::Detach_All -- Removes the object from all tracking systems. * + * * + * This routine will take the object and see that it is removed from all miscellaneous * + * tracking systems in the game. This operation is vital when deleting an object. It is * + * necessary so that when the object is removed from the game, existing game objects won't * + * be referencing a now invalid game object. This typically affects the targeting * + * and navigation computers of other game objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Detach_All(bool all) +{ + assert(this != 0); + assert(IsActive); + + /* + ** Unselect this object if it was selected. + */ + //if (all || Owner() != PlayerPtr->Class->House) { + // Unselect(); + //} + + //Added some error handling incase there was an issue removing the object - JAS 6/28/2019 + if (all) { + //Unselect(); + // Updated to function for multiplayer - 6/28/2019 JAS + Unselect_All_Players(); + } + else + { + Unselect_All_Players_Except_Owner(); + } + //End of change - JAS 6/28/2019 + + Map.Detach(this); + + /* + ** Remove from targeting computers. + */ + Detach_This_From_All(As_Target(), all); +} + + +/*********************************************************************************************** + * ObjectClass::Receive_Message -- Processes an incoming radio message. * + * * + * Any radio message received that applies to objects in general are handled by this * + * routine. Typically, this is the "redraw" message, which occurs when another object is * + * loading or unloading and thus overlapping. * + * * + * INPUT: message -- The message received. * + * * + * OUTPUT: Returns with the appropriate radio response. If the message was recognized, then * + * RADIO_ROGER is returned, otherwise, just RADIO_STATIC is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType ObjectClass::Receive_Message(RadioClass *, RadioMessageType message, long & ) +{ + assert(this != 0); + assert(IsActive); + + switch (message) { + + /* + ** This message serves as a rendering convenience. It lets the system + ** know that there might be a visual conflict and the unit in radio + ** contact should be redrawn. This typically occurs when a vehicle + ** is being unloaded from a hover lander. + */ + case RADIO_REDRAW: + Mark(MARK_CHANGE); + return(RADIO_ROGER); + + default: + break; + } + return(RADIO_STATIC); +} + + +/*********************************************************************************************** + * ObjectClass::Take_Damage -- Applies damage to the object. * + * * + * This routine applies damage to the object according to the damage parameters. It handles * + * reducing the strength of the object and also returns the result of that damage. The * + * result value can be examined to determine if the object was destroyed, greatly damaged, * + * or other results. * + * * + * INPUT: damage -- Reference to the damage number to apply. This number will be adjusted * + * according to defensive armor and distance. Examine this value after * + * the call to determine the actual amount of damage applied. * + * * + * distance -- The distance (in leptons) from the center of the damage causing * + * explosion to the object itself. * + * * + * warhead -- The warhead type that is causing the damage. * + * * + * source -- The perpetrator of this damage. * + * * + * forced -- Is the damage forced upon the object regardless of whether it * + * is normally immune? * + * * + * OUTPUT: Returns the ResultType that indicates what the affect of the damage was. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/29/1994 JLB : Created. * + * 12/27/1994 JLB : Trigger event processing for attacked or destroyed. * + * 01/01/1995 JLB : Reduces damage greatly depending on range. * + *=============================================================================================*/ +ResultType ObjectClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(this != 0); + assert(IsActive); + + ResultType result = RESULT_NONE; + int oldstrength = Strength; + + if (oldstrength && damage != 0 && (forced || !Class_Of().IsImmune)) { + int maxstrength = Class_Of().MaxStrength; + + /* + ** Modify damage based on the warhead type and the armor of the object. This results + ** in a reduced damage value, but never below 1 damage point. Unless + ** it's forced damage, in which case we want full damage. + */ + if (!forced /*&& damage > 0*/) { + damage = Modify_Damage(damage, warhead, Class_Of().Armor, distance); + + /* + ** Special hack to ensure that dogs only do damage to intended victim and no + ** damage to others. + */ + if (source && source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) { + if (source->TarCom == As_Target()) { + damage = Strength; + } else { + damage = 0; + } + } + } + if (damage == 0) return(RESULT_NONE); + + /* + ** Are we healing/repairing? If so, add strength, but in + ** any case, return that no damage was done. + */ + if (damage < 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_INFANTRY || What_Am_I() == RTTI_UNIT || What_Am_I() == RTTI_AIRCRAFT) { +#else + if (What_Am_I() == RTTI_INFANTRY) { +#endif + Clicked_As_Target(PlayerPtr->Class->House, 7); // 2019/09/20 JAS - Added record of who clicked on the object + Strength -= damage; + if (Strength > maxstrength) { + Strength = maxstrength; + } + } + return(RESULT_NONE); + } + + /* + ** At this point, we KNOW that at least light damage has occurred. + */ + result = RESULT_LIGHT; + + /* + ** A non-fatal blow has occurred. Check to see if the object transitioned to below + ** half strength or if it is now down to one hit point. + */ + if (oldstrength > damage) { + + if (oldstrength >= (maxstrength >> 1) && (oldstrength-damage) < (maxstrength >> 1)) { + result = RESULT_HALF; + } + } else { + + /* + ** When an object is damaged to destruction, it will instead stop at one + ** damage point. This will prolong the damage state as well as + ** give greater satisfaction when it is finally destroyed. + */ + damage = oldstrength; + } + + /* + ** Apply the damage to the object. + */ + Strength = oldstrength - damage; + + /* + ** Check to see if the object is majorly damaged or destroyed. + */ + switch (Strength) { + case 0: + Record_The_Kill(source); + result = RESULT_DESTROYED; + if (this->Is_Techno()) { + if (this == ::As_Object(((TechnoClass *)this)->House->UnitToTeleport)) ((TechnoClass *)this)->House->UnitToTeleport = 0; + } + Detach_All(); + break; + + case 1: + result = RESULT_MAJOR; + break; + + default: + break; + } + + /* + ** Handle any trigger event associated with this object. + */ + if (source && Trigger.Is_Valid() && result != RESULT_DESTROYED) { + Trigger->Spring(TEVENT_ATTACKED, this); + } + + /* + ** If any damage was assessed and this object is selected, then flag + ** the object to be redrawn so that the health bar will be updated. + */ + //if (result != RESULT_NONE && IsSelected) { + // Updated to function for multiplayer - 6/26/2019 JAS + if (result != RESULT_NONE && Is_Selected_By_Player()) { + Mark(MARK_CHANGE); + } + } + + /* + ** Return with the result of the damage taken. + */ + return(result); +} + + +/*********************************************************************************************** + * ObjectClass::Mark -- Handles basic marking logic. * + * * + * This routine handles the base logic for marking an object up or down on the map. It * + * manages the IsDown flag as well as flagging the object to be redrawn if necessary. * + * Whenever an object is to be marked, it should call this base class function first. If * + * this function returns true, then the higher level function should proceed with its own * + * logic. * + * * + * INPUT: mark -- The marking method to use for this object. It can be either MARK_DOWN, * + * MARK_UP, or MARK_CHANGE. * + * * + * OUTPUT: bool; Was the object marked successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Mark(MarkType mark) +{ + assert(this != 0); + assert(IsActive); + + if (!IsInLimbo && IsActive) { + + /* + ** A mark for change is always successful UNLESS the object + ** is not placed down or has already been flagged as changed + ** this game frame. + */ + if (mark == MARK_CHANGE || mark == MARK_CHANGE_REDRAW) { + if (IsToDisplay && mark != MARK_CHANGE_REDRAW) return(false); + if (IsDown) { + Mark_For_Redraw(); + return(true); + } + return(false); + } + + /* + ** Handle adding or removing the object in the cells' overlap lists + */ + if (mark == MARK_OVERLAP_UP) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Up(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + if (mark == MARK_OVERLAP_DOWN) { + if (IsDown == true) { + if (Class_Of().IsFootprint) { + Map.Overlap_Down(Coord_Cell(Coord), this); + } + Mark_For_Redraw(); + return(true); + } + } + + /* + ** It is important to know whether the object is a techno class + ** or not to see if we have to adjust the regional threat ratings + */ + int threat = 0; + HousesType house = HOUSE_NONE; + CELL cell = 0; + TechnoClass * tech; + if (Is_Techno()) { + tech = (TechnoClass *)this; + threat = tech->Risk(); + house = tech->Owner(); + cell = Coord_Cell(Coord); + } else { + tech = NULL; + } + + /* + ** Marking down is only successful if the object isn't already + ** placed down. + */ + if (mark == MARK_DOWN && !IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, threat); + } + IsDown = true; + Mark_For_Redraw(); + return(true); + } + + /* + ** Lifting up is only successful if the object isn't already + ** lifted up from the map. + */ + if (mark == MARK_UP && IsDown) { + if (tech && Session.Type == GAME_NORMAL && In_Which_Layer() == LAYER_GROUND) { + Map[cell].Adjust_Threat(house, -threat); + } + if (Class_Of().IsFootprint) { + Map.Overlap_Up(Coord_Cell(Coord), this); + } + IsDown = false; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Init -- Initializes the basic object system. * + * * + * This routine should be called when the basic object system needs to be initialized. This * + * occurs when the scenario is about to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectClass::Init(void) +{ + CurrentObject.Clear_All(); +} + + +/*********************************************************************************************** + * ObjectClass::Revealed -- Reveals this object to the house specified. * + * * + * This routine is called when this object gets revealed to the house specified. * + * * + * INPUT: house -- Pointer to the house that this object is being revealed to. * + * * + * OUTPUT: Was this object revealed for the first time to this house? Generic objects always * + * return true unless an invalid house pointer was specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Revealed(HouseClass * house) +{ + assert(this != 0); + assert(IsActive); + + return(house != NULL); +} + + + + +/*********************************************************************************************** + * ObjectClass::Set_Selected_By_Player -- Set this object as selected by the given player or * + * the default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Selected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + if (((TechnoTypeClass const &)Class_Of()).IsLeader) { + CurrentObject.Add_Head(house, this); + } + else { + CurrentObject.Add(house, this); + } + + int shift = (int)house; + IsSelectedMask |= (1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsSelected = true; + } +} + +/*********************************************************************************************** + * ObjectClass::Set_Unselected_By_Player -- Set this object as unselected by the given player * + * orthe default player. * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +void ObjectClass::Set_Unselected_By_Player(HouseClass *player) +{ + if (!player || !player->Class) { + player = PlayerPtr; + } + + HousesType house = player->Class->House; + CurrentObject.Delete(house, this); + + int shift = (int)house; + IsSelectedMask &= ~(1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsSelected = false; + } +} + +/*********************************************************************************************** + * ObjectClass::Is_Selected_By_Player -- Has this object been selected by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if selected by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 6/25/2019 - JAS * + *=============================================================================================*/ +bool ObjectClass::Is_Selected_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int)player->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + else { + int shift = (int)PlayerPtr->Class->House; + return (IsSelectedMask & (1 << shift)) ? true : false; + } + return false; +} + + + + +/*********************************************************************************************** + * ObjectClass::Paradrop -- Unlimbos object in paradrop mode. * + * * + * Call this routine as a replacement for Unlimbo() if the object is to be paradropped onto * + * the playing field. * + * * + * INPUT: coord -- The desired landing coordinate to give the dropping unit. * + * * + * OUTPUT: bool; Was the object successfully unlimboed and has begun paradropping? * + * * + * WARNINGS: The unit may not be successful in paradropping if the desired destination * + * location cannot be occupied by the object. * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Paradrop(COORDINATE coord) +{ + assert(this != 0); + assert(IsActive); + + Height = FLIGHT_LEVEL; + IsFalling = true; + if (Unlimbo(coord, DIR_S)) { + AnimClass * anim = NULL; + + if (What_Am_I() == RTTI_BULLET) { + anim = new AnimClass(ANIM_PARA_BOMB, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } else { + anim = new AnimClass(ANIM_PARACHUTE, Coord_Move(Center_Coord(), DIR_N, 0x0030 + Height)); + } + + /* + ** If the animation was created, then attach it to this object. + */ + if (anim != NULL) { + anim->Attach_To(this); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * ObjectClass::Attach_Trigger -- Attach specified trigger to object. * + * * + * This routine is used to attach the specified trigger to the object. * + * * + * INPUT: trigger -- Pointer to the trigger to attach. If any existing trigger is desired * + * to be detached, then pass NULL to this routine. * + * * + * OUTPUT: bool; Was the trigger attached? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/06/1996 JLB : Created. * + *=============================================================================================*/ +bool ObjectClass::Attach_Trigger(TriggerClass * trigger) +{ + if (Trigger.Is_Valid()) { + TriggerClass * tptr = Trigger; + tptr->AttachCount--; + Trigger = NULL; + } + + if (trigger) { + Trigger = trigger; + trigger->AttachCount++; + return(true); + } + return(false); +} + + +// These can't be made inline (for various reasons). +short const * ObjectClass::Occupy_List(bool placement) const {return(Class_Of().Occupy_List(placement));}; +short const * ObjectClass::Overlap_List(bool ) const {return(Class_Of().Overlap_List());}; +BuildingClass * ObjectClass::Who_Can_Build_Me(bool intheory, bool legal) const {return(Class_Of().Who_Can_Build_Me(intheory, legal, Owner()));}; +fixed ObjectClass::Health_Ratio(void) const {return(fixed(Strength, Class_Of().MaxStrength));}; +int ObjectClass::Full_Name(void) const {return Class_Of().Full_Name();}; + + +//********************************************************************************************** +// MODULE SEPARATION -- ObjectTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * ObjectTypeClass::ObjectTypeClass -- Normal constructor for object type class objects. * + * * + * This is the base constructor that is used when constructing the object type classes. * + * Every tangible game piece type calls this constructor for the ObjectTypeClass. This * + * class holds static information that is common to objects in general. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/23/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass::ObjectTypeClass( + RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int name, + char const * ini) : + AbstractTypeClass(rtti, id, name, ini), + IsCrushable(false), + IsStealthy(is_stealthy), + IsSelectable(is_selectable), + IsLegalTarget(is_legal_target), + IsInsignificant(is_insignificant), + IsImmune(is_immune), + IsSentient(is_sentient), + IsFootprint(is_footprint), + Armor(ARMOR_NONE), + MaxStrength(0), + ImageData(0), + RadarIcon(0), + HDImageData(0) +{ + /* + ** Init the DimensionData rect. Not sure how this was ever working before without being allocated. It was just trashing + ** memory later on when the pointer was being dereferenced and written to without being initialized. ST - 8/14/2019 3:15PM + */ + DimensionData = NULL; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Max_Pips -- Fetches the maximum pips allowed for this object. * + * * + * This routine will return the maximum number of pips that can be displayed for this * + * object. When dealing with generic objects, this value is always zero. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pip boxes (empty or otherwise) to display. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Max_Pips(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Dimensions -- Gets the dimensions of the object in pixels. * + * * + * This routine will fetch the dimensions of this object expressed as pixels width and * + * pixels height. This information can be used to intelligently update the clipping * + * rectangles. * + * * + * INPUT: width -- Reference to the width variable that will be filled in. * + * * + * height -- Reference to the height variable that will be filled in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::Dimensions(int &width, int &height) const +{ + width = 10; + height = 10; +} + + +/*********************************************************************************************** + * ObjectTypeClass::Cost_Of -- Returns the cost to buy this unit. * + * * + * This routine will return the cost to purchase this unit. This routine is expected to be * + * overridden by the objects that can actually be purchased. All other object types can * + * simply return zero since this value won't be used. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the cost of the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Cost_Of(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Time_To_Build -- Fetches the time to construct this object. * + * * + * This routine will fetch the time in takes to construct this object. Objects that can * + * be constructed will override this routine in order to return a useful value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time units (arbitrary) that it takes to construct this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int ObjectTypeClass::Time_To_Build(void) const +{ + return(0); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Get_Cameo_Data -- Fetches pointer to cameo data for this object type. * + * * + * This routine will return with the cameo data pointer for this object type. It is * + * expected that objects that can appear on the sidebar will override this routine in order * + * to provide proper cameo data pointer. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo shape data. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +void const * ObjectTypeClass::Get_Cameo_Data(void) const +{ + return(NULL); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Occupy_List -- Returns with simple occupation list for object. * + * * + * This routine returns a pointer to a simple occupation list for this object. Since at * + * this tier of the object class chain, the exact shape of the object is indeterminate, * + * this function merely returns a single cell occupation list. This actually works for * + * most vehicles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to a simple occupation list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Occupy_List(bool) const +{ + static short const _list[] = {0, REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::Overlap_List -- Returns a pointer to a simple overlap list. * + * * + * This function returns a pointer to an overlap list for the object. An overlap list is * + * the offsets from the object's cell to get the cells the imagery overlaps, but is object * + * is not considered to occupy. Since at this stage, the overlap information is not * + * available, this function merely returns a pointer to an empty list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns a pointer to the generic overlap list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +short const * ObjectTypeClass::Overlap_List(void) const +{ + static short const _list[] = {REFRESH_EOL}; + return(_list); +} + + +/*********************************************************************************************** + * ObjectTypeClass::One_Time -- Handles one time processing for object types. * + * * + * This routine is used to handle the once per game processing required for object types. * + * This consists of loading any data and initializing any data tables the game requires. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine goes to disk. * + * * + * HISTORY: * + * 11/01/1994 JLB : Created. * + *=============================================================================================*/ +void ObjectTypeClass::One_Time(void) +{ + SelectShapes = MFCD::Retrieve("SELECT.SHP"); + + #ifndef NDEBUG + RawFileClass file("PIPS.SHP"); + if (file.Is_Available()) { + PipShapes = Load_Alloc_Data(file); + } else { + PipShapes = MFCD::Retrieve("PIPS.SHP"); + } + #else + PipShapes = MFCD::Retrieve("PIPS.SHP"); + #endif +} + + +/*********************************************************************************************** + * ObjectTypeClass::Who_Can_Build_Me -- Determine what building can build this object type. * + * * + * This routine will scan through all available factory buildings and determine which * + * is capable of building this object type. The scan can be controlled to scan for only * + * factory buildings that are free to produce now or those that could produce this * + * object type if conditions permit. * + * * + * INPUT: intheory -- Should the general (when conditions permit) case be examined to see * + * if a building could build this object type "in theory" even though it * + * might currently be otherwise occupied? * + * * + * legal -- Check for building prerequisite and technology level rules? Usually * + * this would be 'true' for human controlled requests and 'false' for * + * the computer. This is because the computer is usually not under * + * the normal restrictions that the player is under. * + * * + * OUTPUT: Returns with a pointer to the building that can produce the object of this * + * type. If no suitable factory building could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * ObjectTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + if (!intheory && What_Am_I() == RTTI_AIRCRAFTTYPE && ((AircraftTypeClass*)this)->IsFixedWing) { + int num_builders = 0, num_fixed_wings = 0; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI && + building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Get_Ownable()) && + (!legal || building->House->Can_Build(this, building->ActLike))) { + num_builders++; + } + } + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + assert(aircraft != NULL); + + if ( !aircraft->IsInLimbo && + aircraft->House->Class->House == house && + aircraft->Class->IsFixedWing) { + num_fixed_wings++; + } + } + if (num_fixed_wings >= num_builders) { + return NULL; + } + } + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI && + building->Mission != MISSION_DECONSTRUCTION && building->MissionQueue != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Get_Ownable()) && + (!legal || building->House->Can_Build(this, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + // BG: Hack so only kennels can build dogs, and no other, and barracks can + // only build humans and no other. + if (What_Am_I() == RTTI_INFANTRYTYPE) { + InfantryTypeClass * me = (InfantryTypeClass *)this; + if (me->IsDog) { + if (*building == STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } else { + if (*building != STRUCT_KENNEL) { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } else { + + /* + ** HACK ALERT: Helipads can build aircraft and airstrips can build + ** fixed wing craft only. + */ + if (What_Am_I() == RTTI_AIRCRAFTTYPE) { + AircraftTypeClass * air = (AircraftTypeClass *)this; + if ((*building == STRUCT_HELIPAD && !air->IsFixedWing) || (*building == STRUCT_AIRSTRIP && air->IsFixedWing)) { + if (building->IsLeader) return(building); + anybuilding = building; + } + + } else { + if (building->IsLeader) return(building); + anybuilding = building; + } + } + } + } + return(anybuilding); +} \ No newline at end of file diff --git a/code/redalert/object.h b/code/redalert/object.h new file mode 100644 index 0000000..5c5c47e --- /dev/null +++ b/code/redalert/object.h @@ -0,0 +1,273 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OBJECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OBJECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OBJECT_H +#define OBJECT_H + +#include "abstract.h" + +class ObjectClass; +class TechnoClass; +class ObjectTypeClass; +class HouseClass; +class BuildingClass; +class RadioClass; +class TriggerClass; + + +/********************************************************************** +** Every game object (that can exist on the map) is ultimately derived from this object +** class. It holds the common information between all objects. This is primarily the +** object unique ID number and its location in the world. All common operations between +** game objects are represented by virtual functions in this class. +*/ +class ObjectClass : public AbstractClass +{ + public: + /* + ** The object can be in one of two states -- placed down on the map, or not. If the + ** object is placed down on the map, then this flag will be true. + */ + unsigned IsDown:1; + + /* + ** This is a support flag that is only used while building a list of objects to + ** be damaged by a proximity affect (explosion). When this flag is set, this object + ** will not be added to the list of units to damage. When damage is applied to the + ** object, this flag is cleared again. This process ensures that an object is never + ** subject to "double jeopardy". + */ + unsigned IsToDamage:1; + + /* + ** Is this object flagged to be displayed during the next rendering process? This + ** flag could be set by many different circumstances. It is automatically cleared + ** when the object is rerendered. + */ + unsigned IsToDisplay:1; + + /* + ** An object in the game may be valid yet held in a state of "limbo". Units are in such + ** a state if they are being transported or are otherwise "inside" another unit. They can + ** also be in limbo if they have been created but are being held until the proper time + ** for delivery. + */ + unsigned IsInLimbo:1; + + /* + ** When an object is "selected" it is given a floating bar graph or other graphic imagery + ** to display this fact. When the player performs I/O, the actions may have a direct + ** bearing on the actions of the currently selected object. For quick checking purposes, + ** if this object is the one that is "selected", this flag will be true. + */ + unsigned IsSelected:1; + + //Added a mask instead of bool for selecting players. This is because we must now support multiplayer. + // - 6/26/2019 + // Needs more than 16 bits since RA has more than 16 factions. ST - 8/14/2019 12:15PM + unsigned int IsSelectedMask; + + /* + ** If an animation is attached to this object, then this flag will be true. + */ + unsigned IsAnimAttached:1; + + /* + ** If this object should process falling logic, then this flag will be true. Such + ** objects might be ballistic projectiles, grenades, or parachuters. + */ + unsigned IsFalling:1; + int Riser; + + /* + ** Several objects could exist in the same cell list. This is a pointer to the + ** next object in the cell list. The objects in this list are not in any + ** significant order. + */ + SmartPtr Next; + + /* + ** Every object can be assigned a trigger; the same trigger can be assigned + ** to multiple objects. + */ + CCPtr Trigger; + + /* + ** This is the current strength of this object. + */ + short Strength; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + /*----------------------------------------------------------------------------------- + ** Constructor & destructors. + */ + ObjectClass(RTTIType rtti, int id); + ObjectClass(NoInitClass const & x) : AbstractClass(x), Next(x), Trigger(x) {}; + virtual ~ObjectClass(void) {Next = 0;}; + int operator < (ObjectClass const & object) const {return Sort_Y() < object.Sort_Y();}; + int operator > (ObjectClass const & object) const {return Sort_Y() > object.Sort_Y();}; + + /* + ** Object selection control. + */ + static void Init(void); + + /* + ** Query functions. + */ + virtual bool Is_Players_Army(void) const {return(false);} + virtual void const * Get_Image_Data(void) const; + virtual struct Image_t *Get_HDImage_Data(void) const; + virtual ActionType What_Action(ObjectClass const *) const; + virtual ActionType What_Action(CELL) const; + virtual LayerType In_Which_Layer(void) const; + bool Is_Infantry(void) const {return(RTTI == RTTI_INFANTRY);}; + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRY || RTTI == RTTI_UNIT || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + bool Is_Techno(void) const {return(RTTI == RTTI_BUILDING || RTTI == RTTI_UNIT || RTTI == RTTI_INFANTRY || RTTI == RTTI_VESSEL || RTTI == RTTI_AIRCRAFT);}; + virtual int Get_Ownable(void) const; + virtual ObjectTypeClass const & Class_Of(void) const = 0; + virtual char const * Name(void) const; + virtual int Full_Name(void) const; + virtual bool Can_Repair(void) const; + virtual bool Can_Demolish(void) const; + virtual bool Can_Demolish_Unit(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Docking_Coord(void) const; + virtual COORDINATE Target_Coord(void) const; + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const; + virtual COORDINATE Sort_Y(void) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual COORDINATE Exit_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing = DIR_N); + virtual void Detach(TARGET target, bool all=true); + virtual void Detach_All(bool all=true); + virtual void Record_The_Kill(TechnoClass * ); + virtual bool Paradrop(COORDINATE coord); + bool Attach_Trigger(TriggerClass * trigger); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Do_Shimmer(void); + virtual int Exit_Object(TechnoClass *); + virtual bool Render(bool forced) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual fixed Health_Ratio(void) const; + virtual void Draw_It(int x, int y, WindowNumberType ) const = 0; + virtual void Hidden(void); + virtual void Look(bool incremental=false); + virtual bool Mark(MarkType=MARK_CHANGE); + + private: + virtual void Mark_For_Redraw(void); + + public: + + /* + ** User I/O. + */ + virtual void Active_Click_With(ActionType , ObjectClass *); + virtual void Active_Click_With(ActionType , CELL ); + virtual void Clicked_As_Target(HousesType house, int = 7); // 2019/09/20 JAS - Added record of who clicked on the object + virtual bool Select(bool allow_mixed = false); + virtual void Unselect(void); + + //These selection functions were added to handle the fact that we now need to support + //client-server multiplayer. - JAS 6/26/2019 + virtual void Unselect_All_Players(void); + virtual void Unselect_All_Players_Except_Owner(void); + virtual bool Is_Selected_By_Player(HouseClass *player = NULL) const; + virtual void Set_Selected_By_Player(HouseClass *player = NULL); + virtual void Set_Unselected_By_Player(HouseClass *player = NULL); + + /* + ** Combat related. + */ + virtual bool In_Range(COORDINATE , int=0) const; + virtual int Weapon_Range(int =0) const; + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual void Scatter(COORDINATE , bool forced=false, bool nokidding=false); + virtual bool Catch_Fire(void); + virtual void Fire_Out(void); + virtual int Value(void) const; + virtual MissionType Get_Mission(void) const; + + /* + ** AI. + */ + virtual void Per_Cell_Process(PCPType) {} + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual bool Revealed(HouseClass * house); + virtual void Repair(int ); + virtual void Sell_Back(int ); + virtual void AI(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual void Move(FacingType); + + enum {FLIGHT_LEVEL=256}; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/ocidl.h b/code/redalert/ocidl.h new file mode 100644 index 0000000..4fdd6df --- /dev/null +++ b/code/redalert/ocidl.h @@ -0,0 +1,8026 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + +/* File created by MIDL compiler version 3.00.39 */ +/* at Sat Jul 13 21:56:58 1996 + */ +/* Compiler settings for c:\oa\src\idl\ocidl.idl: + Oic (OptLev=i1), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data +*/ +//@@MIDL_FILE_HEADING( ) +#include "rpc.h" +#include "rpcndr.h" +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __ocidl_h__ +#define __ocidl_h__ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* Forward Declarations */ + +#ifndef __IEnumConnections_FWD_DEFINED__ +#define __IEnumConnections_FWD_DEFINED__ +typedef interface IEnumConnections IEnumConnections; +#endif /* __IEnumConnections_FWD_DEFINED__ */ + + +#ifndef __IConnectionPoint_FWD_DEFINED__ +#define __IConnectionPoint_FWD_DEFINED__ +typedef interface IConnectionPoint IConnectionPoint; +#endif /* __IConnectionPoint_FWD_DEFINED__ */ + + +#ifndef __IEnumConnectionPoints_FWD_DEFINED__ +#define __IEnumConnectionPoints_FWD_DEFINED__ +typedef interface IEnumConnectionPoints IEnumConnectionPoints; +#endif /* __IEnumConnectionPoints_FWD_DEFINED__ */ + + +#ifndef __IConnectionPointContainer_FWD_DEFINED__ +#define __IConnectionPointContainer_FWD_DEFINED__ +typedef interface IConnectionPointContainer IConnectionPointContainer; +#endif /* __IConnectionPointContainer_FWD_DEFINED__ */ + + +#ifndef __IClassFactory2_FWD_DEFINED__ +#define __IClassFactory2_FWD_DEFINED__ +typedef interface IClassFactory2 IClassFactory2; +#endif /* __IClassFactory2_FWD_DEFINED__ */ + + +#ifndef __IProvideClassInfo_FWD_DEFINED__ +#define __IProvideClassInfo_FWD_DEFINED__ +typedef interface IProvideClassInfo IProvideClassInfo; +#endif /* __IProvideClassInfo_FWD_DEFINED__ */ + + +#ifndef __IProvideClassInfo2_FWD_DEFINED__ +#define __IProvideClassInfo2_FWD_DEFINED__ +typedef interface IProvideClassInfo2 IProvideClassInfo2; +#endif /* __IProvideClassInfo2_FWD_DEFINED__ */ + + +#ifndef __IOleControl_FWD_DEFINED__ +#define __IOleControl_FWD_DEFINED__ +typedef interface IOleControl IOleControl; +#endif /* __IOleControl_FWD_DEFINED__ */ + + +#ifndef __IOleControlSite_FWD_DEFINED__ +#define __IOleControlSite_FWD_DEFINED__ +typedef interface IOleControlSite IOleControlSite; +#endif /* __IOleControlSite_FWD_DEFINED__ */ + + +#ifndef __IPropertyPage_FWD_DEFINED__ +#define __IPropertyPage_FWD_DEFINED__ +typedef interface IPropertyPage IPropertyPage; +#endif /* __IPropertyPage_FWD_DEFINED__ */ + + +#ifndef __IPropertyPage2_FWD_DEFINED__ +#define __IPropertyPage2_FWD_DEFINED__ +typedef interface IPropertyPage2 IPropertyPage2; +#endif /* __IPropertyPage2_FWD_DEFINED__ */ + + +#ifndef __IPropertyPageSite_FWD_DEFINED__ +#define __IPropertyPageSite_FWD_DEFINED__ +typedef interface IPropertyPageSite IPropertyPageSite; +#endif /* __IPropertyPageSite_FWD_DEFINED__ */ + + +#ifndef __IPropertyNotifySink_FWD_DEFINED__ +#define __IPropertyNotifySink_FWD_DEFINED__ +typedef interface IPropertyNotifySink IPropertyNotifySink; +#endif /* __IPropertyNotifySink_FWD_DEFINED__ */ + + +#ifndef __ISpecifyPropertyPages_FWD_DEFINED__ +#define __ISpecifyPropertyPages_FWD_DEFINED__ +typedef interface ISpecifyPropertyPages ISpecifyPropertyPages; +#endif /* __ISpecifyPropertyPages_FWD_DEFINED__ */ + + +#ifndef __IPersistMemory_FWD_DEFINED__ +#define __IPersistMemory_FWD_DEFINED__ +typedef interface IPersistMemory IPersistMemory; +#endif /* __IPersistMemory_FWD_DEFINED__ */ + + +#ifndef __IPersistStreamInit_FWD_DEFINED__ +#define __IPersistStreamInit_FWD_DEFINED__ +typedef interface IPersistStreamInit IPersistStreamInit; +#endif /* __IPersistStreamInit_FWD_DEFINED__ */ + + +#ifndef __IPersistPropertyBag_FWD_DEFINED__ +#define __IPersistPropertyBag_FWD_DEFINED__ +typedef interface IPersistPropertyBag IPersistPropertyBag; +#endif /* __IPersistPropertyBag_FWD_DEFINED__ */ + + +#ifndef __ISimpleFrameSite_FWD_DEFINED__ +#define __ISimpleFrameSite_FWD_DEFINED__ +typedef interface ISimpleFrameSite ISimpleFrameSite; +#endif /* __ISimpleFrameSite_FWD_DEFINED__ */ + + +#ifndef __IFont_FWD_DEFINED__ +#define __IFont_FWD_DEFINED__ +typedef interface IFont IFont; +#endif /* __IFont_FWD_DEFINED__ */ + + +#ifndef __IPicture_FWD_DEFINED__ +#define __IPicture_FWD_DEFINED__ +typedef interface IPicture IPicture; +#endif /* __IPicture_FWD_DEFINED__ */ + + +#ifndef __IFontDisp_FWD_DEFINED__ +#define __IFontDisp_FWD_DEFINED__ +typedef interface IFontDisp IFontDisp; +#endif /* __IFontDisp_FWD_DEFINED__ */ + + +#ifndef __IPictureDisp_FWD_DEFINED__ +#define __IPictureDisp_FWD_DEFINED__ +typedef interface IPictureDisp IPictureDisp; +#endif /* __IPictureDisp_FWD_DEFINED__ */ + + +#ifndef __IAdviseSinkEx_FWD_DEFINED__ +#define __IAdviseSinkEx_FWD_DEFINED__ +typedef interface IAdviseSinkEx IAdviseSinkEx; +#endif /* __IAdviseSinkEx_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceObjectWindowless_FWD_DEFINED__ +#define __IOleInPlaceObjectWindowless_FWD_DEFINED__ +typedef interface IOleInPlaceObjectWindowless IOleInPlaceObjectWindowless; +#endif /* __IOleInPlaceObjectWindowless_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteEx_FWD_DEFINED__ +#define __IOleInPlaceSiteEx_FWD_DEFINED__ +typedef interface IOleInPlaceSiteEx IOleInPlaceSiteEx; +#endif /* __IOleInPlaceSiteEx_FWD_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteWindowless_FWD_DEFINED__ +#define __IOleInPlaceSiteWindowless_FWD_DEFINED__ +typedef interface IOleInPlaceSiteWindowless IOleInPlaceSiteWindowless; +#endif /* __IOleInPlaceSiteWindowless_FWD_DEFINED__ */ + + +#ifndef __IViewObjectEx_FWD_DEFINED__ +#define __IViewObjectEx_FWD_DEFINED__ +typedef interface IViewObjectEx IViewObjectEx; +#endif /* __IViewObjectEx_FWD_DEFINED__ */ + + +#ifndef __IOleUndoUnit_FWD_DEFINED__ +#define __IOleUndoUnit_FWD_DEFINED__ +typedef interface IOleUndoUnit IOleUndoUnit; +#endif /* __IOleUndoUnit_FWD_DEFINED__ */ + + +#ifndef __IOleParentUndoUnit_FWD_DEFINED__ +#define __IOleParentUndoUnit_FWD_DEFINED__ +typedef interface IOleParentUndoUnit IOleParentUndoUnit; +#endif /* __IOleParentUndoUnit_FWD_DEFINED__ */ + + +#ifndef __IEnumOleUndoUnits_FWD_DEFINED__ +#define __IEnumOleUndoUnits_FWD_DEFINED__ +typedef interface IEnumOleUndoUnits IEnumOleUndoUnits; +#endif /* __IEnumOleUndoUnits_FWD_DEFINED__ */ + + +#ifndef __IOleUndoManager_FWD_DEFINED__ +#define __IOleUndoManager_FWD_DEFINED__ +typedef interface IOleUndoManager IOleUndoManager; +#endif /* __IOleUndoManager_FWD_DEFINED__ */ + + +#ifndef __IQuickActivate_FWD_DEFINED__ +#define __IQuickActivate_FWD_DEFINED__ +typedef interface IQuickActivate IQuickActivate; +#endif /* __IQuickActivate_FWD_DEFINED__ */ + + +#ifndef __IPointerInactive_FWD_DEFINED__ +#define __IPointerInactive_FWD_DEFINED__ +typedef interface IPointerInactive IPointerInactive; +#endif /* __IPointerInactive_FWD_DEFINED__ */ + + +#ifndef __IObjectWithSite_FWD_DEFINED__ +#define __IObjectWithSite_FWD_DEFINED__ +typedef interface IObjectWithSite IObjectWithSite; +#endif /* __IObjectWithSite_FWD_DEFINED__ */ + + +#ifndef __IErrorLog_FWD_DEFINED__ +#define __IErrorLog_FWD_DEFINED__ +typedef interface IErrorLog IErrorLog; +#endif /* __IErrorLog_FWD_DEFINED__ */ + + +#ifndef __IPropertyBag_FWD_DEFINED__ +#define __IPropertyBag_FWD_DEFINED__ +typedef interface IPropertyBag IPropertyBag; +#endif /* __IPropertyBag_FWD_DEFINED__ */ + + +#ifndef __IPerPropertyBrowsing_FWD_DEFINED__ +#define __IPerPropertyBrowsing_FWD_DEFINED__ +typedef interface IPerPropertyBrowsing IPerPropertyBrowsing; +#endif /* __IPerPropertyBrowsing_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oleidl.h" +#include "oaidl.h" + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +/**************************************** + * Generated header for interface: __MIDL__intf_0000 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [local] */ + + +//+------------------------------------------------------------------------- +// +// Microsoft Windows +// Copyright (C) Microsoft Corporation, 1993 - 1996. +// +//-------------------------------------------------------------------------- + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL__intf_0000_v0_0_s_ifspec; + +#ifndef __IOleControlTypes_INTERFACE_DEFINED__ +#define __IOleControlTypes_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControlTypes + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [auto_handle][unique][version] */ + + +typedef /* [v1_enum] */ +enum tagUASFLAGS + { UAS_NORMAL = 0, + UAS_BLOCKED = 0x1, + UAS_NOPARENTENABLE = 0x2, + UAS_MASK = 0x3 + } UASFLAGS; + +/* State values for the DISPID_READYSTATE property */ +typedef /* [v1_enum] */ +enum tagREADYSTATE + { READYSTATE_UNINITIALIZED = 0, + READYSTATE_LOADING = 1, + READYSTATE_LOADED = 2, + READYSTATE_INTERACTIVE = 3, + READYSTATE_COMPLETE = 4 + } READYSTATE; + +typedef /* [represent_as] */ struct tagUserHWND + { + wireHWND pRemHwnd; + } UserHWND; + +typedef /* [represent_as] */ struct tagUserHWND UserHACCEL; + +typedef /* [represent_as] */ struct tagUserHWND UserHDC; + +typedef /* [represent_as] */ struct tagUserHWND UserHFONT; + +typedef /* [represent_as] */ struct tagUserMSG + { + wireHWND pRemHwnd; + UINT message; + WPARAM wParam; + LPARAM lParam; + DWORD time; + POINT pt; + } UserMSG; + +typedef /* [represent_as] */ struct tagUserBSTR + { + wireBSTR pBstrBlob; + } UserBSTR; + +typedef struct tagVARIANT_BLOB + { + DWORD clSize; + DWORD rpcReserved; + /* [size_is] */ ULONGLONG ahData[ 1 ]; + } __RPC_FAR *wireVARIANT_BLOB; + +typedef /* [represent_as] */ struct tagUserVARIANT + { + wireVARIANT_BLOB pVarBlob; + } UserVARIANT; + +typedef /* [represent_as] */ struct tagUserEXCEPINFO + { + WORD wCode; + WORD wReserved; + wireBSTR bstrSource; + wireBSTR bstrDescription; + wireBSTR bstrHelpFile; + DWORD dwHelpContext; + ULONG pvReserved; + ULONG pfnDeferredFillIn; + SCODE scode; + } UserEXCEPINFO; + + + +extern RPC_IF_HANDLE IOleControlTypes_v1_0_c_ifspec; +extern RPC_IF_HANDLE IOleControlTypes_v1_0_s_ifspec; +#endif /* __IOleControlTypes_INTERFACE_DEFINED__ */ + +#ifndef __IEnumConnections_INTERFACE_DEFINED__ +#define __IEnumConnections_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumConnections + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IEnumConnections __RPC_FAR *PENUMCONNECTIONS; + +typedef IEnumConnections __RPC_FAR *LPENUMCONNECTIONS; + +typedef struct tagCONNECTDATA + { + IUnknown __RPC_FAR *pUnk; + DWORD dwCookie; + } CONNECTDATA; + +typedef struct tagCONNECTDATA __RPC_FAR *PCONNECTDATA; + +typedef struct tagCONNECTDATA __RPC_FAR *LPCONNECTDATA; + + +EXTERN_C const IID IID_IEnumConnections; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumConnections : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cConnections) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumConnectionsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumConnections __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumConnections __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumConnections __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumConnections __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumConnections __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumConnectionsVtbl; + + interface IEnumConnections + { + CONST_VTBL struct IEnumConnectionsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumConnections_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumConnections_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumConnections_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumConnections_Next(This,cConnections,rgcd,pcFetched) \ + (This)->lpVtbl -> Next(This,cConnections,rgcd,pcFetched) + +#define IEnumConnections_Skip(This,cConnections) \ + (This)->lpVtbl -> Skip(This,cConnections) + +#define IEnumConnections_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumConnections_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnections_RemoteNext_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +void __RPC_STUB IEnumConnections_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Skip_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + +void __RPC_STUB IEnumConnections_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Reset_Proxy( + IEnumConnections __RPC_FAR * This); + + +void __RPC_STUB IEnumConnections_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnections_Clone_Proxy( + IEnumConnections __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumConnections_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumConnections_INTERFACE_DEFINED__ */ + + +#ifndef __IConnectionPoint_INTERFACE_DEFINED__ +#define __IConnectionPoint_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IConnectionPoint + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IConnectionPoint __RPC_FAR *PCONNECTIONPOINT; + +typedef IConnectionPoint __RPC_FAR *LPCONNECTIONPOINT; + + +EXTERN_C const IID IID_IConnectionPoint; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IConnectionPoint : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetConnectionInterface( + /* [out] */ IID __RPC_FAR *pIID) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetConnectionPointContainer( + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC) = 0; + + virtual HRESULT STDMETHODCALLTYPE Advise( + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE Unadvise( + /* [in] */ DWORD dwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumConnections( + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IConnectionPointVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IConnectionPoint __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IConnectionPoint __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetConnectionInterface )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IID __RPC_FAR *pIID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetConnectionPointContainer )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Advise )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unadvise )( + IConnectionPoint __RPC_FAR * This, + /* [in] */ DWORD dwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumConnections )( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IConnectionPointVtbl; + + interface IConnectionPoint + { + CONST_VTBL struct IConnectionPointVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IConnectionPoint_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IConnectionPoint_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IConnectionPoint_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IConnectionPoint_GetConnectionInterface(This,pIID) \ + (This)->lpVtbl -> GetConnectionInterface(This,pIID) + +#define IConnectionPoint_GetConnectionPointContainer(This,ppCPC) \ + (This)->lpVtbl -> GetConnectionPointContainer(This,ppCPC) + +#define IConnectionPoint_Advise(This,pUnkSink,pdwCookie) \ + (This)->lpVtbl -> Advise(This,pUnkSink,pdwCookie) + +#define IConnectionPoint_Unadvise(This,dwCookie) \ + (This)->lpVtbl -> Unadvise(This,dwCookie) + +#define IConnectionPoint_EnumConnections(This,ppEnum) \ + (This)->lpVtbl -> EnumConnections(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_GetConnectionInterface_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IID __RPC_FAR *pIID); + + +void __RPC_STUB IConnectionPoint_GetConnectionInterface_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_GetConnectionPointContainer_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IConnectionPointContainer __RPC_FAR *__RPC_FAR *ppCPC); + + +void __RPC_STUB IConnectionPoint_GetConnectionPointContainer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_Advise_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSink, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + +void __RPC_STUB IConnectionPoint_Advise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_Unadvise_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [in] */ DWORD dwCookie); + + +void __RPC_STUB IConnectionPoint_Unadvise_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPoint_EnumConnections_Proxy( + IConnectionPoint __RPC_FAR * This, + /* [out] */ IEnumConnections __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IConnectionPoint_EnumConnections_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IConnectionPoint_INTERFACE_DEFINED__ */ + + +#ifndef __IEnumConnectionPoints_INTERFACE_DEFINED__ +#define __IEnumConnectionPoints_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumConnectionPoints + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IEnumConnectionPoints __RPC_FAR *PENUMCONNECTIONPOINTS; + +typedef IEnumConnectionPoints __RPC_FAR *LPENUMCONNECTIONPOINTS; + + +EXTERN_C const IID IID_IEnumConnectionPoints; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumConnectionPoints : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cConnections) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumConnectionPointsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumConnectionPoints __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumConnectionPoints __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumConnectionPoints __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumConnectionPoints __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumConnectionPointsVtbl; + + interface IEnumConnectionPoints + { + CONST_VTBL struct IEnumConnectionPointsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumConnectionPoints_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumConnectionPoints_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumConnectionPoints_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumConnectionPoints_Next(This,cConnections,ppCP,pcFetched) \ + (This)->lpVtbl -> Next(This,cConnections,ppCP,pcFetched) + +#define IEnumConnectionPoints_Skip(This,cConnections) \ + (This)->lpVtbl -> Skip(This,cConnections) + +#define IEnumConnectionPoints_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumConnectionPoints_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_RemoteNext_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +void __RPC_STUB IEnumConnectionPoints_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Skip_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections); + + +void __RPC_STUB IEnumConnectionPoints_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Reset_Proxy( + IEnumConnectionPoints __RPC_FAR * This); + + +void __RPC_STUB IEnumConnectionPoints_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Clone_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumConnectionPoints_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumConnectionPoints_INTERFACE_DEFINED__ */ + + +#ifndef __IConnectionPointContainer_INTERFACE_DEFINED__ +#define __IConnectionPointContainer_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IConnectionPointContainer + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IConnectionPointContainer __RPC_FAR *PCONNECTIONPOINTCONTAINER; + +typedef IConnectionPointContainer __RPC_FAR *LPCONNECTIONPOINTCONTAINER; + + +EXTERN_C const IID IID_IConnectionPointContainer; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IConnectionPointContainer : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE EnumConnectionPoints( + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindConnectionPoint( + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP) = 0; + + }; + +#else /* C style interface */ + + typedef struct IConnectionPointContainerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IConnectionPointContainer __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IConnectionPointContainer __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumConnectionPoints )( + IConnectionPointContainer __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindConnectionPoint )( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP); + + END_INTERFACE + } IConnectionPointContainerVtbl; + + interface IConnectionPointContainer + { + CONST_VTBL struct IConnectionPointContainerVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IConnectionPointContainer_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IConnectionPointContainer_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IConnectionPointContainer_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IConnectionPointContainer_EnumConnectionPoints(This,ppEnum) \ + (This)->lpVtbl -> EnumConnectionPoints(This,ppEnum) + +#define IConnectionPointContainer_FindConnectionPoint(This,riid,ppCP) \ + (This)->lpVtbl -> FindConnectionPoint(This,riid,ppCP) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IConnectionPointContainer_EnumConnectionPoints_Proxy( + IConnectionPointContainer __RPC_FAR * This, + /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IConnectionPointContainer_EnumConnectionPoints_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IConnectionPointContainer_FindConnectionPoint_Proxy( + IConnectionPointContainer __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [out] */ IConnectionPoint __RPC_FAR *__RPC_FAR *ppCP); + + +void __RPC_STUB IConnectionPointContainer_FindConnectionPoint_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IConnectionPointContainer_INTERFACE_DEFINED__ */ + + +#ifndef __IClassFactory2_INTERFACE_DEFINED__ +#define __IClassFactory2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IClassFactory2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IClassFactory2 __RPC_FAR *LPCLASSFACTORY2; + +typedef struct tagLICINFO + { + LONG cbLicInfo; + BOOL fRuntimeKeyAvail; + BOOL fLicVerified; + } LICINFO; + +typedef struct tagLICINFO __RPC_FAR *LPLICINFO; + + +EXTERN_C const IID IID_IClassFactory2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IClassFactory2 : public IClassFactory + { + public: + virtual HRESULT STDMETHODCALLTYPE GetLicInfo( + /* [out] */ LICINFO __RPC_FAR *pLicInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE RequestLicKey( + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE CreateInstanceLic( + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj) = 0; + + }; + +#else /* C style interface */ + + typedef struct IClassFactory2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IClassFactory2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IClassFactory2 __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateInstance )( + IClassFactory2 __RPC_FAR * This, + /* [unique][in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockServer )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ BOOL fLock); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLicInfo )( + IClassFactory2 __RPC_FAR * This, + /* [out] */ LICINFO __RPC_FAR *pLicInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestLicKey )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateInstanceLic )( + IClassFactory2 __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj); + + END_INTERFACE + } IClassFactory2Vtbl; + + interface IClassFactory2 + { + CONST_VTBL struct IClassFactory2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IClassFactory2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IClassFactory2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IClassFactory2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IClassFactory2_CreateInstance(This,pUnkOuter,riid,ppvObject) \ + (This)->lpVtbl -> CreateInstance(This,pUnkOuter,riid,ppvObject) + +#define IClassFactory2_LockServer(This,fLock) \ + (This)->lpVtbl -> LockServer(This,fLock) + + +#define IClassFactory2_GetLicInfo(This,pLicInfo) \ + (This)->lpVtbl -> GetLicInfo(This,pLicInfo) + +#define IClassFactory2_RequestLicKey(This,dwReserved,pBstrKey) \ + (This)->lpVtbl -> RequestLicKey(This,dwReserved,pBstrKey) + +#define IClassFactory2_CreateInstanceLic(This,pUnkOuter,pUnkReserved,riid,bstrKey,ppvObj) \ + (This)->lpVtbl -> CreateInstanceLic(This,pUnkOuter,pUnkReserved,riid,bstrKey,ppvObj) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IClassFactory2_GetLicInfo_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [out] */ LICINFO __RPC_FAR *pLicInfo); + + +void __RPC_STUB IClassFactory2_GetLicInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IClassFactory2_RequestLicKey_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ DWORD dwReserved, + /* [out] */ BSTR __RPC_FAR *pBstrKey); + + +void __RPC_STUB IClassFactory2_RequestLicKey_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IClassFactory2_RemoteCreateInstanceLic_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObj); + + +void __RPC_STUB IClassFactory2_RemoteCreateInstanceLic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IClassFactory2_INTERFACE_DEFINED__ */ + + +#ifndef __IProvideClassInfo_INTERFACE_DEFINED__ +#define __IProvideClassInfo_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IProvideClassInfo + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IProvideClassInfo __RPC_FAR *LPPROVIDECLASSINFO; + + +EXTERN_C const IID IID_IProvideClassInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IProvideClassInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetClassInfo( + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI) = 0; + + }; + +#else /* C style interface */ + + typedef struct IProvideClassInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IProvideClassInfo __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IProvideClassInfo __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IProvideClassInfo __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassInfo )( + IProvideClassInfo __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + END_INTERFACE + } IProvideClassInfoVtbl; + + interface IProvideClassInfo + { + CONST_VTBL struct IProvideClassInfoVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IProvideClassInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IProvideClassInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IProvideClassInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IProvideClassInfo_GetClassInfo(This,ppTI) \ + (This)->lpVtbl -> GetClassInfo(This,ppTI) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IProvideClassInfo_GetClassInfo_Proxy( + IProvideClassInfo __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + +void __RPC_STUB IProvideClassInfo_GetClassInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IProvideClassInfo_INTERFACE_DEFINED__ */ + + +#ifndef __IProvideClassInfo2_INTERFACE_DEFINED__ +#define __IProvideClassInfo2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IProvideClassInfo2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IProvideClassInfo2 __RPC_FAR *LPPROVIDECLASSINFO2; + +typedef +enum tagGUIDKIND + { GUIDKIND_DEFAULT_SOURCE_DISP_IID = 1 + } GUIDKIND; + + +EXTERN_C const IID IID_IProvideClassInfo2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IProvideClassInfo2 : public IProvideClassInfo + { + public: + virtual HRESULT STDMETHODCALLTYPE GetGUID( + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IProvideClassInfo2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IProvideClassInfo2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IProvideClassInfo2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassInfo )( + IProvideClassInfo2 __RPC_FAR * This, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTI); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetGUID )( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID); + + END_INTERFACE + } IProvideClassInfo2Vtbl; + + interface IProvideClassInfo2 + { + CONST_VTBL struct IProvideClassInfo2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IProvideClassInfo2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IProvideClassInfo2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IProvideClassInfo2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IProvideClassInfo2_GetClassInfo(This,ppTI) \ + (This)->lpVtbl -> GetClassInfo(This,ppTI) + + +#define IProvideClassInfo2_GetGUID(This,dwGuidKind,pGUID) \ + (This)->lpVtbl -> GetGUID(This,dwGuidKind,pGUID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IProvideClassInfo2_GetGUID_Proxy( + IProvideClassInfo2 __RPC_FAR * This, + /* [in] */ DWORD dwGuidKind, + /* [out] */ GUID __RPC_FAR *pGUID); + + +void __RPC_STUB IProvideClassInfo2_GetGUID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IProvideClassInfo2_INTERFACE_DEFINED__ */ + + +#ifndef __IOleControl_INTERFACE_DEFINED__ +#define __IOleControl_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControl + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IOleControl __RPC_FAR *LPOLECONTROL; + +typedef struct tagCONTROLINFO + { + ULONG cb; + HACCEL hAccel; + USHORT cAccel; + DWORD dwFlags; + } CONTROLINFO; + +typedef struct tagCONTROLINFO __RPC_FAR *LPCONTROLINFO; + +typedef +enum tagCTRLINFO + { CTRLINFO_EATS_RETURN = 1, + CTRLINFO_EATS_ESCAPE = 2 + } CTRLINFO; + + +EXTERN_C const IID IID_IOleControl; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleControl : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetControlInfo( + /* [out] */ CONTROLINFO __RPC_FAR *pCI) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnMnemonic( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnAmbientPropertyChange( + /* [in] */ DISPID dispID) = 0; + + virtual HRESULT STDMETHODCALLTYPE FreezeEvents( + /* [in] */ BOOL bFreeze) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleControlVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleControl __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleControl __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleControl __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetControlInfo )( + IOleControl __RPC_FAR * This, + /* [out] */ CONTROLINFO __RPC_FAR *pCI); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnMnemonic )( + IOleControl __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnAmbientPropertyChange )( + IOleControl __RPC_FAR * This, + /* [in] */ DISPID dispID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FreezeEvents )( + IOleControl __RPC_FAR * This, + /* [in] */ BOOL bFreeze); + + END_INTERFACE + } IOleControlVtbl; + + interface IOleControl + { + CONST_VTBL struct IOleControlVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleControl_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleControl_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleControl_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleControl_GetControlInfo(This,pCI) \ + (This)->lpVtbl -> GetControlInfo(This,pCI) + +#define IOleControl_OnMnemonic(This,pMsg) \ + (This)->lpVtbl -> OnMnemonic(This,pMsg) + +#define IOleControl_OnAmbientPropertyChange(This,dispID) \ + (This)->lpVtbl -> OnAmbientPropertyChange(This,dispID) + +#define IOleControl_FreezeEvents(This,bFreeze) \ + (This)->lpVtbl -> FreezeEvents(This,bFreeze) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleControl_GetControlInfo_Proxy( + IOleControl __RPC_FAR * This, + /* [out] */ CONTROLINFO __RPC_FAR *pCI); + + +void __RPC_STUB IOleControl_GetControlInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_OnMnemonic_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IOleControl_OnMnemonic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_OnAmbientPropertyChange_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IOleControl_OnAmbientPropertyChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControl_FreezeEvents_Proxy( + IOleControl __RPC_FAR * This, + /* [in] */ BOOL bFreeze); + + +void __RPC_STUB IOleControl_FreezeEvents_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleControl_INTERFACE_DEFINED__ */ + + +#ifndef __IOleControlSite_INTERFACE_DEFINED__ +#define __IOleControlSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleControlSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IOleControlSite __RPC_FAR *LPOLECONTROLSITE; + +typedef struct tagPOINTF + { + FLOAT x; + FLOAT y; + } POINTF; + +typedef struct tagPOINTF __RPC_FAR *LPPOINTF; + +typedef +enum tagXFORMCOORDS + { XFORMCOORDS_POSITION = 0x1, + XFORMCOORDS_SIZE = 0x2, + XFORMCOORDS_HIMETRICTOCONTAINER = 0x4, + XFORMCOORDS_CONTAINERTOHIMETRIC = 0x8 + } XFORMCOORDS; + + +EXTERN_C const IID IID_IOleControlSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleControlSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnControlInfoChanged( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE LockInPlaceActive( + /* [in] */ BOOL fLock) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetExtendedControl( + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp) = 0; + + virtual HRESULT STDMETHODCALLTYPE TransformCoords( + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnFocus( + /* [in] */ BOOL fGotFocus) = 0; + + virtual HRESULT STDMETHODCALLTYPE ShowPropertyFrame( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleControlSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleControlSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleControlSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleControlSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnControlInfoChanged )( + IOleControlSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockInPlaceActive )( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fLock); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetExtendedControl )( + IOleControlSite __RPC_FAR * This, + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TransformCoords )( + IOleControlSite __RPC_FAR * This, + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IOleControlSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnFocus )( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fGotFocus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ShowPropertyFrame )( + IOleControlSite __RPC_FAR * This); + + END_INTERFACE + } IOleControlSiteVtbl; + + interface IOleControlSite + { + CONST_VTBL struct IOleControlSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleControlSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleControlSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleControlSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleControlSite_OnControlInfoChanged(This) \ + (This)->lpVtbl -> OnControlInfoChanged(This) + +#define IOleControlSite_LockInPlaceActive(This,fLock) \ + (This)->lpVtbl -> LockInPlaceActive(This,fLock) + +#define IOleControlSite_GetExtendedControl(This,ppDisp) \ + (This)->lpVtbl -> GetExtendedControl(This,ppDisp) + +#define IOleControlSite_TransformCoords(This,pPtlHimetric,pPtfContainer,dwFlags) \ + (This)->lpVtbl -> TransformCoords(This,pPtlHimetric,pPtfContainer,dwFlags) + +#define IOleControlSite_TranslateAccelerator(This,pMsg,grfModifiers) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg,grfModifiers) + +#define IOleControlSite_OnFocus(This,fGotFocus) \ + (This)->lpVtbl -> OnFocus(This,fGotFocus) + +#define IOleControlSite_ShowPropertyFrame(This) \ + (This)->lpVtbl -> ShowPropertyFrame(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleControlSite_OnControlInfoChanged_Proxy( + IOleControlSite __RPC_FAR * This); + + +void __RPC_STUB IOleControlSite_OnControlInfoChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_LockInPlaceActive_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fLock); + + +void __RPC_STUB IOleControlSite_LockInPlaceActive_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_GetExtendedControl_Proxy( + IOleControlSite __RPC_FAR * This, + /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDisp); + + +void __RPC_STUB IOleControlSite_GetExtendedControl_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_TransformCoords_Proxy( + IOleControlSite __RPC_FAR * This, + /* [out][in] */ POINTL __RPC_FAR *pPtlHimetric, + /* [out][in] */ POINTF __RPC_FAR *pPtfContainer, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IOleControlSite_TransformCoords_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_TranslateAccelerator_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg, + /* [in] */ DWORD grfModifiers); + + +void __RPC_STUB IOleControlSite_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_OnFocus_Proxy( + IOleControlSite __RPC_FAR * This, + /* [in] */ BOOL fGotFocus); + + +void __RPC_STUB IOleControlSite_OnFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleControlSite_ShowPropertyFrame_Proxy( + IOleControlSite __RPC_FAR * This); + + +void __RPC_STUB IOleControlSite_ShowPropertyFrame_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleControlSite_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPage_INTERFACE_DEFINED__ +#define __IPropertyPage_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPage + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPage __RPC_FAR *LPPROPERTYPAGE; + +typedef struct tagPROPPAGEINFO + { + ULONG cb; + LPOLESTR pszTitle; + SIZE size; + LPOLESTR pszDocString; + LPOLESTR pszHelpFile; + DWORD dwHelpContext; + } PROPPAGEINFO; + +typedef struct tagPROPPAGEINFO __RPC_FAR *LPPROPPAGEINFO; + + +EXTERN_C const IID IID_IPropertyPage; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPage : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetPageSite( + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite) = 0; + + virtual HRESULT STDMETHODCALLTYPE Activate( + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal) = 0; + + virtual HRESULT STDMETHODCALLTYPE Deactivate( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPageInfo( + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetObjects( + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0; + + virtual HRESULT STDMETHODCALLTYPE Show( + /* [in] */ UINT nCmdShow) = 0; + + virtual HRESULT STDMETHODCALLTYPE Move( + /* [in] */ LPCRECT pRect) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsPageDirty( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Apply( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Help( + /* [in] */ LPCOLESTR pszHelpDir) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPageVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPage __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPage __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetPageSite )( + IPropertyPage __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Activate )( + IPropertyPage __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Deactivate )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageInfo )( + IPropertyPage __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjects )( + IPropertyPage __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Show )( + IPropertyPage __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Move )( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsPageDirty )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Apply )( + IPropertyPage __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Help )( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPage __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + END_INTERFACE + } IPropertyPageVtbl; + + interface IPropertyPage + { + CONST_VTBL struct IPropertyPageVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPage_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPage_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPage_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPage_SetPageSite(This,pPageSite) \ + (This)->lpVtbl -> SetPageSite(This,pPageSite) + +#define IPropertyPage_Activate(This,hWndParent,pRect,bModal) \ + (This)->lpVtbl -> Activate(This,hWndParent,pRect,bModal) + +#define IPropertyPage_Deactivate(This) \ + (This)->lpVtbl -> Deactivate(This) + +#define IPropertyPage_GetPageInfo(This,pPageInfo) \ + (This)->lpVtbl -> GetPageInfo(This,pPageInfo) + +#define IPropertyPage_SetObjects(This,cObjects,ppUnk) \ + (This)->lpVtbl -> SetObjects(This,cObjects,ppUnk) + +#define IPropertyPage_Show(This,nCmdShow) \ + (This)->lpVtbl -> Show(This,nCmdShow) + +#define IPropertyPage_Move(This,pRect) \ + (This)->lpVtbl -> Move(This,pRect) + +#define IPropertyPage_IsPageDirty(This) \ + (This)->lpVtbl -> IsPageDirty(This) + +#define IPropertyPage_Apply(This) \ + (This)->lpVtbl -> Apply(This) + +#define IPropertyPage_Help(This,pszHelpDir) \ + (This)->lpVtbl -> Help(This,pszHelpDir) + +#define IPropertyPage_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPage_SetPageSite_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + +void __RPC_STUB IPropertyPage_SetPageSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Activate_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + +void __RPC_STUB IPropertyPage_Activate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Deactivate_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_Deactivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_GetPageInfo_Proxy( + IPropertyPage __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + +void __RPC_STUB IPropertyPage_GetPageInfo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_SetObjects_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + +void __RPC_STUB IPropertyPage_SetObjects_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Show_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + +void __RPC_STUB IPropertyPage_Show_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Move_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + +void __RPC_STUB IPropertyPage_Move_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_IsPageDirty_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_IsPageDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Apply_Proxy( + IPropertyPage __RPC_FAR * This); + + +void __RPC_STUB IPropertyPage_Apply_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_Help_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + +void __RPC_STUB IPropertyPage_Help_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPage_TranslateAccelerator_Proxy( + IPropertyPage __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IPropertyPage_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPage_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPage2_INTERFACE_DEFINED__ +#define __IPropertyPage2_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPage2 + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPage2 __RPC_FAR *LPPROPERTYPAGE2; + + +EXTERN_C const IID IID_IPropertyPage2; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPage2 : public IPropertyPage + { + public: + virtual HRESULT STDMETHODCALLTYPE EditProperty( + /* [in] */ DISPID dispID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPage2Vtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPage2 __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetPageSite )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ IPropertyPageSite __RPC_FAR *pPageSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Activate )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ HWND hWndParent, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL bModal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Deactivate )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageInfo )( + IPropertyPage2 __RPC_FAR * This, + /* [out] */ PROPPAGEINFO __RPC_FAR *pPageInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjects )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ ULONG cObjects, + /* [size_is][in] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Show )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ UINT nCmdShow); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Move )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ LPCRECT pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsPageDirty )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Apply )( + IPropertyPage2 __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Help )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ LPCOLESTR pszHelpDir); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EditProperty )( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ DISPID dispID); + + END_INTERFACE + } IPropertyPage2Vtbl; + + interface IPropertyPage2 + { + CONST_VTBL struct IPropertyPage2Vtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPage2_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPage2_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPage2_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPage2_SetPageSite(This,pPageSite) \ + (This)->lpVtbl -> SetPageSite(This,pPageSite) + +#define IPropertyPage2_Activate(This,hWndParent,pRect,bModal) \ + (This)->lpVtbl -> Activate(This,hWndParent,pRect,bModal) + +#define IPropertyPage2_Deactivate(This) \ + (This)->lpVtbl -> Deactivate(This) + +#define IPropertyPage2_GetPageInfo(This,pPageInfo) \ + (This)->lpVtbl -> GetPageInfo(This,pPageInfo) + +#define IPropertyPage2_SetObjects(This,cObjects,ppUnk) \ + (This)->lpVtbl -> SetObjects(This,cObjects,ppUnk) + +#define IPropertyPage2_Show(This,nCmdShow) \ + (This)->lpVtbl -> Show(This,nCmdShow) + +#define IPropertyPage2_Move(This,pRect) \ + (This)->lpVtbl -> Move(This,pRect) + +#define IPropertyPage2_IsPageDirty(This) \ + (This)->lpVtbl -> IsPageDirty(This) + +#define IPropertyPage2_Apply(This) \ + (This)->lpVtbl -> Apply(This) + +#define IPropertyPage2_Help(This,pszHelpDir) \ + (This)->lpVtbl -> Help(This,pszHelpDir) + +#define IPropertyPage2_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + + +#define IPropertyPage2_EditProperty(This,dispID) \ + (This)->lpVtbl -> EditProperty(This,dispID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPage2_EditProperty_Proxy( + IPropertyPage2 __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyPage2_EditProperty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPage2_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyPageSite_INTERFACE_DEFINED__ +#define __IPropertyPageSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyPageSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyPageSite __RPC_FAR *LPPROPERTYPAGESITE; + +typedef +enum tagPROPPAGESTATUS + { PROPPAGESTATUS_DIRTY = 0x1, + PROPPAGESTATUS_VALIDATE = 0x2, + PROPPAGESTATUS_CLEAN = 0x4 + } PROPPAGESTATUS; + + +EXTERN_C const IID IID_IPropertyPageSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyPageSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnStatusChange( + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLocaleID( + /* [out] */ LCID __RPC_FAR *pLocaleID) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPageContainer( + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk) = 0; + + virtual HRESULT STDMETHODCALLTYPE TranslateAccelerator( + /* [in] */ MSG __RPC_FAR *pMsg) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyPageSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyPageSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyPageSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnStatusChange )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLocaleID )( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ LCID __RPC_FAR *pLocaleID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPageContainer )( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *TranslateAccelerator )( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + END_INTERFACE + } IPropertyPageSiteVtbl; + + interface IPropertyPageSite + { + CONST_VTBL struct IPropertyPageSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyPageSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyPageSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyPageSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyPageSite_OnStatusChange(This,dwFlags) \ + (This)->lpVtbl -> OnStatusChange(This,dwFlags) + +#define IPropertyPageSite_GetLocaleID(This,pLocaleID) \ + (This)->lpVtbl -> GetLocaleID(This,pLocaleID) + +#define IPropertyPageSite_GetPageContainer(This,ppUnk) \ + (This)->lpVtbl -> GetPageContainer(This,ppUnk) + +#define IPropertyPageSite_TranslateAccelerator(This,pMsg) \ + (This)->lpVtbl -> TranslateAccelerator(This,pMsg) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_OnStatusChange_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IPropertyPageSite_OnStatusChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_GetLocaleID_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ LCID __RPC_FAR *pLocaleID); + + +void __RPC_STUB IPropertyPageSite_GetLocaleID_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_GetPageContainer_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [out] */ IUnknown __RPC_FAR *__RPC_FAR *ppUnk); + + +void __RPC_STUB IPropertyPageSite_GetPageContainer_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyPageSite_TranslateAccelerator_Proxy( + IPropertyPageSite __RPC_FAR * This, + /* [in] */ MSG __RPC_FAR *pMsg); + + +void __RPC_STUB IPropertyPageSite_TranslateAccelerator_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyPageSite_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyNotifySink_INTERFACE_DEFINED__ +#define __IPropertyNotifySink_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyNotifySink + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyNotifySink __RPC_FAR *LPPROPERTYNOTIFYSINK; + + +EXTERN_C const IID IID_IPropertyNotifySink; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyNotifySink : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnChanged( + /* [in] */ DISPID dispID) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnRequestEdit( + /* [in] */ DISPID dispID) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyNotifySinkVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyNotifySink __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyNotifySink __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnChanged )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnRequestEdit )( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + END_INTERFACE + } IPropertyNotifySinkVtbl; + + interface IPropertyNotifySink + { + CONST_VTBL struct IPropertyNotifySinkVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyNotifySink_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyNotifySink_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyNotifySink_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyNotifySink_OnChanged(This,dispID) \ + (This)->lpVtbl -> OnChanged(This,dispID) + +#define IPropertyNotifySink_OnRequestEdit(This,dispID) \ + (This)->lpVtbl -> OnRequestEdit(This,dispID) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPropertyNotifySink_OnChanged_Proxy( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyNotifySink_OnChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyNotifySink_OnRequestEdit_Proxy( + IPropertyNotifySink __RPC_FAR * This, + /* [in] */ DISPID dispID); + + +void __RPC_STUB IPropertyNotifySink_OnRequestEdit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyNotifySink_INTERFACE_DEFINED__ */ + + +#ifndef __ISpecifyPropertyPages_INTERFACE_DEFINED__ +#define __ISpecifyPropertyPages_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: ISpecifyPropertyPages + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef ISpecifyPropertyPages __RPC_FAR *LPSPECIFYPROPERTYPAGES; + +typedef struct tagCAUUID + { + ULONG cElems; + /* [size_is] */ GUID __RPC_FAR *pElems; + } CAUUID; + +typedef struct tagCAUUID __RPC_FAR *LPCAUUID; + + +EXTERN_C const IID IID_ISpecifyPropertyPages; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface ISpecifyPropertyPages : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetPages( + /* [out] */ CAUUID __RPC_FAR *pPages) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISpecifyPropertyPagesVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + ISpecifyPropertyPages __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + ISpecifyPropertyPages __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + ISpecifyPropertyPages __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPages )( + ISpecifyPropertyPages __RPC_FAR * This, + /* [out] */ CAUUID __RPC_FAR *pPages); + + END_INTERFACE + } ISpecifyPropertyPagesVtbl; + + interface ISpecifyPropertyPages + { + CONST_VTBL struct ISpecifyPropertyPagesVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISpecifyPropertyPages_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISpecifyPropertyPages_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISpecifyPropertyPages_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISpecifyPropertyPages_GetPages(This,pPages) \ + (This)->lpVtbl -> GetPages(This,pPages) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISpecifyPropertyPages_GetPages_Proxy( + ISpecifyPropertyPages __RPC_FAR * This, + /* [out] */ CAUUID __RPC_FAR *pPages); + + +void __RPC_STUB ISpecifyPropertyPages_GetPages_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISpecifyPropertyPages_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistMemory_INTERFACE_DEFINED__ +#define __IPersistMemory_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistMemory + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistMemory __RPC_FAR *LPPERSISTMEMORY; + + +EXTERN_C const IID IID_IPersistMemory; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistMemory : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Load( + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Save( + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSizeMax( + /* [out] */ ULONG __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistMemoryVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistMemory __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistMemory __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistMemory __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistMemory __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsDirty )( + IPersistMemory __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSizeMax )( + IPersistMemory __RPC_FAR * This, + /* [out] */ ULONG __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistMemory __RPC_FAR * This); + + END_INTERFACE + } IPersistMemoryVtbl; + + interface IPersistMemory + { + CONST_VTBL struct IPersistMemoryVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistMemory_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistMemory_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistMemory_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistMemory_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistMemory_IsDirty(This) \ + (This)->lpVtbl -> IsDirty(This) + +#define IPersistMemory_Load(This,pMem,cbSize) \ + (This)->lpVtbl -> Load(This,pMem,cbSize) + +#define IPersistMemory_Save(This,pMem,fClearDirty,cbSize) \ + (This)->lpVtbl -> Save(This,pMem,fClearDirty,cbSize) + +#define IPersistMemory_GetSizeMax(This,pCbSize) \ + (This)->lpVtbl -> GetSizeMax(This,pCbSize) + +#define IPersistMemory_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistMemory_IsDirty_Proxy( + IPersistMemory __RPC_FAR * This); + + +void __RPC_STUB IPersistMemory_IsDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_RemoteLoad_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ ULONG cbSize); + + +void __RPC_STUB IPersistMemory_RemoteLoad_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_RemoteSave_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + +void __RPC_STUB IPersistMemory_RemoteSave_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistMemory_GetSizeMax_Proxy( + IPersistMemory __RPC_FAR * This, + /* [out] */ ULONG __RPC_FAR *pCbSize); + + +void __RPC_STUB IPersistMemory_GetSizeMax_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistMemory_InitNew_Proxy( + IPersistMemory __RPC_FAR * This); + + +void __RPC_STUB IPersistMemory_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistMemory_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistStreamInit_INTERFACE_DEFINED__ +#define __IPersistStreamInit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistStreamInit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistStreamInit __RPC_FAR *LPPERSISTSTREAMINIT; + + +EXTERN_C const IID IID_IPersistStreamInit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistStreamInit : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE IsDirty( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Load( + /* [in] */ LPSTREAM pStm) = 0; + + virtual HRESULT STDMETHODCALLTYPE Save( + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSizeMax( + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistStreamInitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistStreamInit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistStreamInit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsDirty )( + IPersistStreamInit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSizeMax )( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistStreamInit __RPC_FAR * This); + + END_INTERFACE + } IPersistStreamInitVtbl; + + interface IPersistStreamInit + { + CONST_VTBL struct IPersistStreamInitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistStreamInit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistStreamInit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistStreamInit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistStreamInit_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistStreamInit_IsDirty(This) \ + (This)->lpVtbl -> IsDirty(This) + +#define IPersistStreamInit_Load(This,pStm) \ + (This)->lpVtbl -> Load(This,pStm) + +#define IPersistStreamInit_Save(This,pStm,fClearDirty) \ + (This)->lpVtbl -> Save(This,pStm,fClearDirty) + +#define IPersistStreamInit_GetSizeMax(This,pCbSize) \ + (This)->lpVtbl -> GetSizeMax(This,pCbSize) + +#define IPersistStreamInit_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_IsDirty_Proxy( + IPersistStreamInit __RPC_FAR * This); + + +void __RPC_STUB IPersistStreamInit_IsDirty_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_Load_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm); + + +void __RPC_STUB IPersistStreamInit_Load_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_Save_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [in] */ LPSTREAM pStm, + /* [in] */ BOOL fClearDirty); + + +void __RPC_STUB IPersistStreamInit_Save_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_GetSizeMax_Proxy( + IPersistStreamInit __RPC_FAR * This, + /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize); + + +void __RPC_STUB IPersistStreamInit_GetSizeMax_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistStreamInit_InitNew_Proxy( + IPersistStreamInit __RPC_FAR * This); + + +void __RPC_STUB IPersistStreamInit_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistStreamInit_INTERFACE_DEFINED__ */ + + +#ifndef __IPersistPropertyBag_INTERFACE_DEFINED__ +#define __IPersistPropertyBag_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPersistPropertyBag + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPersistPropertyBag __RPC_FAR *LPPERSISTPROPERTYBAG; + + +EXTERN_C const IID IID_IPersistPropertyBag; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPersistPropertyBag : public IPersist + { + public: + virtual HRESULT STDMETHODCALLTYPE InitNew( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Load( + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog) = 0; + + virtual HRESULT STDMETHODCALLTYPE Save( + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPersistPropertyBagVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPersistPropertyBag __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPersistPropertyBag __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )( + IPersistPropertyBag __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClassID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InitNew )( + IPersistPropertyBag __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties); + + END_INTERFACE + } IPersistPropertyBagVtbl; + + interface IPersistPropertyBag + { + CONST_VTBL struct IPersistPropertyBagVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPersistPropertyBag_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPersistPropertyBag_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPersistPropertyBag_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPersistPropertyBag_GetClassID(This,pClassID) \ + (This)->lpVtbl -> GetClassID(This,pClassID) + + +#define IPersistPropertyBag_InitNew(This) \ + (This)->lpVtbl -> InitNew(This) + +#define IPersistPropertyBag_Load(This,pPropBag,pErrorLog) \ + (This)->lpVtbl -> Load(This,pPropBag,pErrorLog) + +#define IPersistPropertyBag_Save(This,pPropBag,fClearDirty,fSaveAllProperties) \ + (This)->lpVtbl -> Save(This,pPropBag,fClearDirty,fSaveAllProperties) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_InitNew_Proxy( + IPersistPropertyBag __RPC_FAR * This); + + +void __RPC_STUB IPersistPropertyBag_InitNew_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_Load_Proxy( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + +void __RPC_STUB IPersistPropertyBag_Load_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPersistPropertyBag_Save_Proxy( + IPersistPropertyBag __RPC_FAR * This, + /* [in] */ IPropertyBag __RPC_FAR *pPropBag, + /* [in] */ BOOL fClearDirty, + /* [in] */ BOOL fSaveAllProperties); + + +void __RPC_STUB IPersistPropertyBag_Save_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPersistPropertyBag_INTERFACE_DEFINED__ */ + + +#ifndef __ISimpleFrameSite_INTERFACE_DEFINED__ +#define __ISimpleFrameSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: ISimpleFrameSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef ISimpleFrameSite __RPC_FAR *LPSIMPLEFRAMESITE; + + +EXTERN_C const IID IID_ISimpleFrameSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface ISimpleFrameSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE PreMessageFilter( + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE PostMessageFilter( + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie) = 0; + + }; + +#else /* C style interface */ + + typedef struct ISimpleFrameSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + ISimpleFrameSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + ISimpleFrameSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PreMessageFilter )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PostMessageFilter )( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie); + + END_INTERFACE + } ISimpleFrameSiteVtbl; + + interface ISimpleFrameSite + { + CONST_VTBL struct ISimpleFrameSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define ISimpleFrameSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define ISimpleFrameSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define ISimpleFrameSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define ISimpleFrameSite_PreMessageFilter(This,hWnd,msg,wp,lp,plResult,pdwCookie) \ + (This)->lpVtbl -> PreMessageFilter(This,hWnd,msg,wp,lp,plResult,pdwCookie) + +#define ISimpleFrameSite_PostMessageFilter(This,hWnd,msg,wp,lp,plResult,dwCookie) \ + (This)->lpVtbl -> PostMessageFilter(This,hWnd,msg,wp,lp,plResult,dwCookie) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE ISimpleFrameSite_PreMessageFilter_Proxy( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [out] */ DWORD __RPC_FAR *pdwCookie); + + +void __RPC_STUB ISimpleFrameSite_PreMessageFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE ISimpleFrameSite_PostMessageFilter_Proxy( + ISimpleFrameSite __RPC_FAR * This, + /* [in] */ HWND hWnd, + /* [in] */ UINT msg, + /* [in] */ WPARAM wp, + /* [in] */ LPARAM lp, + /* [out] */ LRESULT __RPC_FAR *plResult, + /* [in] */ DWORD dwCookie); + + +void __RPC_STUB ISimpleFrameSite_PostMessageFilter_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __ISimpleFrameSite_INTERFACE_DEFINED__ */ + + +#ifndef __IFont_INTERFACE_DEFINED__ +#define __IFont_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IFont + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IFont __RPC_FAR *LPFONT; + +#if defined(_WIN32) && !defined(OLE2ANSI) +typedef TEXTMETRICW TEXTMETRICOLE; + +#else +typedef TEXTMETRIC TEXTMETRICOLE; +#endif +typedef TEXTMETRICOLE __RPC_FAR *LPTEXTMETRICOLE; + + +EXTERN_C const IID IID_IFont; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IFont : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE get_Name( + /* [out] */ BSTR __RPC_FAR *pName) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Name( + /* [in] */ BSTR name) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Size( + /* [out] */ CY __RPC_FAR *pSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Size( + /* [in] */ CY size) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Bold( + /* [out] */ BOOL __RPC_FAR *pBold) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Bold( + /* [in] */ BOOL bold) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Italic( + /* [out] */ BOOL __RPC_FAR *pItalic) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Italic( + /* [in] */ BOOL italic) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Underline( + /* [out] */ BOOL __RPC_FAR *pUnderline) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Underline( + /* [in] */ BOOL underline) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Strikethrough( + /* [out] */ BOOL __RPC_FAR *pStrikethrough) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Strikethrough( + /* [in] */ BOOL strikethrough) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Weight( + /* [out] */ SHORT __RPC_FAR *pWeight) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Weight( + /* [in] */ SHORT weight) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Charset( + /* [out] */ SHORT __RPC_FAR *pCharset) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_Charset( + /* [in] */ SHORT charset) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_hFont( + /* [out] */ HFONT __RPC_FAR *phFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE IsEqual( + /* [in] */ IFont __RPC_FAR *pFontOther) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetRatio( + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryTextMetrics( + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM) = 0; + + virtual HRESULT STDMETHODCALLTYPE AddRefHfont( + /* [in] */ HFONT hFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReleaseHfont( + /* [in] */ HFONT hFont) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetHdc( + /* [in] */ HDC hDC) = 0; + + }; + +#else /* C style interface */ + + typedef struct IFontVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IFont __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IFont __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IFont __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Name )( + IFont __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pName); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Name )( + IFont __RPC_FAR * This, + /* [in] */ BSTR name); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Size )( + IFont __RPC_FAR * This, + /* [out] */ CY __RPC_FAR *pSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Size )( + IFont __RPC_FAR * This, + /* [in] */ CY size); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Bold )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pBold); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Bold )( + IFont __RPC_FAR * This, + /* [in] */ BOOL bold); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Italic )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pItalic); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Italic )( + IFont __RPC_FAR * This, + /* [in] */ BOOL italic); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Underline )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pUnderline); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Underline )( + IFont __RPC_FAR * This, + /* [in] */ BOOL underline); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Strikethrough )( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pStrikethrough); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Strikethrough )( + IFont __RPC_FAR * This, + /* [in] */ BOOL strikethrough); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Weight )( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pWeight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Weight )( + IFont __RPC_FAR * This, + /* [in] */ SHORT weight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Charset )( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pCharset); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_Charset )( + IFont __RPC_FAR * This, + /* [in] */ SHORT charset); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_hFont )( + IFont __RPC_FAR * This, + /* [out] */ HFONT __RPC_FAR *phFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IFont __RPC_FAR * This, + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsEqual )( + IFont __RPC_FAR * This, + /* [in] */ IFont __RPC_FAR *pFontOther); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetRatio )( + IFont __RPC_FAR * This, + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryTextMetrics )( + IFont __RPC_FAR * This, + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddRefHfont )( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseHfont )( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetHdc )( + IFont __RPC_FAR * This, + /* [in] */ HDC hDC); + + END_INTERFACE + } IFontVtbl; + + interface IFont + { + CONST_VTBL struct IFontVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IFont_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IFont_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IFont_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IFont_get_Name(This,pName) \ + (This)->lpVtbl -> get_Name(This,pName) + +#define IFont_put_Name(This,name) \ + (This)->lpVtbl -> put_Name(This,name) + +#define IFont_get_Size(This,pSize) \ + (This)->lpVtbl -> get_Size(This,pSize) + +#define IFont_put_Size(This,size) \ + (This)->lpVtbl -> put_Size(This,size) + +#define IFont_get_Bold(This,pBold) \ + (This)->lpVtbl -> get_Bold(This,pBold) + +#define IFont_put_Bold(This,bold) \ + (This)->lpVtbl -> put_Bold(This,bold) + +#define IFont_get_Italic(This,pItalic) \ + (This)->lpVtbl -> get_Italic(This,pItalic) + +#define IFont_put_Italic(This,italic) \ + (This)->lpVtbl -> put_Italic(This,italic) + +#define IFont_get_Underline(This,pUnderline) \ + (This)->lpVtbl -> get_Underline(This,pUnderline) + +#define IFont_put_Underline(This,underline) \ + (This)->lpVtbl -> put_Underline(This,underline) + +#define IFont_get_Strikethrough(This,pStrikethrough) \ + (This)->lpVtbl -> get_Strikethrough(This,pStrikethrough) + +#define IFont_put_Strikethrough(This,strikethrough) \ + (This)->lpVtbl -> put_Strikethrough(This,strikethrough) + +#define IFont_get_Weight(This,pWeight) \ + (This)->lpVtbl -> get_Weight(This,pWeight) + +#define IFont_put_Weight(This,weight) \ + (This)->lpVtbl -> put_Weight(This,weight) + +#define IFont_get_Charset(This,pCharset) \ + (This)->lpVtbl -> get_Charset(This,pCharset) + +#define IFont_put_Charset(This,charset) \ + (This)->lpVtbl -> put_Charset(This,charset) + +#define IFont_get_hFont(This,phFont) \ + (This)->lpVtbl -> get_hFont(This,phFont) + +#define IFont_Clone(This,ppFont) \ + (This)->lpVtbl -> Clone(This,ppFont) + +#define IFont_IsEqual(This,pFontOther) \ + (This)->lpVtbl -> IsEqual(This,pFontOther) + +#define IFont_SetRatio(This,cyLogical,cyHimetric) \ + (This)->lpVtbl -> SetRatio(This,cyLogical,cyHimetric) + +#define IFont_QueryTextMetrics(This,pTM) \ + (This)->lpVtbl -> QueryTextMetrics(This,pTM) + +#define IFont_AddRefHfont(This,hFont) \ + (This)->lpVtbl -> AddRefHfont(This,hFont) + +#define IFont_ReleaseHfont(This,hFont) \ + (This)->lpVtbl -> ReleaseHfont(This,hFont) + +#define IFont_SetHdc(This,hDC) \ + (This)->lpVtbl -> SetHdc(This,hDC) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IFont_get_Name_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pName); + + +void __RPC_STUB IFont_get_Name_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Name_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BSTR name); + + +void __RPC_STUB IFont_put_Name_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Size_Proxy( + IFont __RPC_FAR * This, + /* [out] */ CY __RPC_FAR *pSize); + + +void __RPC_STUB IFont_get_Size_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Size_Proxy( + IFont __RPC_FAR * This, + /* [in] */ CY size); + + +void __RPC_STUB IFont_put_Size_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Bold_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pBold); + + +void __RPC_STUB IFont_get_Bold_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Bold_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL bold); + + +void __RPC_STUB IFont_put_Bold_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Italic_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pItalic); + + +void __RPC_STUB IFont_get_Italic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Italic_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL italic); + + +void __RPC_STUB IFont_put_Italic_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Underline_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pUnderline); + + +void __RPC_STUB IFont_get_Underline_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Underline_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL underline); + + +void __RPC_STUB IFont_put_Underline_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Strikethrough_Proxy( + IFont __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pStrikethrough); + + +void __RPC_STUB IFont_get_Strikethrough_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Strikethrough_Proxy( + IFont __RPC_FAR * This, + /* [in] */ BOOL strikethrough); + + +void __RPC_STUB IFont_put_Strikethrough_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Weight_Proxy( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pWeight); + + +void __RPC_STUB IFont_get_Weight_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Weight_Proxy( + IFont __RPC_FAR * This, + /* [in] */ SHORT weight); + + +void __RPC_STUB IFont_put_Weight_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_Charset_Proxy( + IFont __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pCharset); + + +void __RPC_STUB IFont_get_Charset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_put_Charset_Proxy( + IFont __RPC_FAR * This, + /* [in] */ SHORT charset); + + +void __RPC_STUB IFont_put_Charset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_get_hFont_Proxy( + IFont __RPC_FAR * This, + /* [out] */ HFONT __RPC_FAR *phFont); + + +void __RPC_STUB IFont_get_hFont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_Clone_Proxy( + IFont __RPC_FAR * This, + /* [out] */ IFont __RPC_FAR *__RPC_FAR *ppFont); + + +void __RPC_STUB IFont_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_IsEqual_Proxy( + IFont __RPC_FAR * This, + /* [in] */ IFont __RPC_FAR *pFontOther); + + +void __RPC_STUB IFont_IsEqual_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_SetRatio_Proxy( + IFont __RPC_FAR * This, + /* [in] */ LONG cyLogical, + /* [in] */ LONG cyHimetric); + + +void __RPC_STUB IFont_SetRatio_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_QueryTextMetrics_Proxy( + IFont __RPC_FAR * This, + /* [out] */ TEXTMETRICOLE __RPC_FAR *pTM); + + +void __RPC_STUB IFont_QueryTextMetrics_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_AddRefHfont_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + +void __RPC_STUB IFont_AddRefHfont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_ReleaseHfont_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HFONT hFont); + + +void __RPC_STUB IFont_ReleaseHfont_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IFont_SetHdc_Proxy( + IFont __RPC_FAR * This, + /* [in] */ HDC hDC); + + +void __RPC_STUB IFont_SetHdc_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IFont_INTERFACE_DEFINED__ */ + + +#ifndef __IPicture_INTERFACE_DEFINED__ +#define __IPicture_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPicture + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPicture __RPC_FAR *LPPICTURE; + +typedef +enum tagPictureAttributes + { PICTURE_SCALABLE = 0x1, + PICTURE_TRANSPARENT = 0x2 + } PICTUREATTRIBUTES; + +typedef UINT OLE_HANDLE; + +typedef LONG OLE_XPOS_HIMETRIC; + +typedef LONG OLE_YPOS_HIMETRIC; + +typedef LONG OLE_XSIZE_HIMETRIC; + +typedef LONG OLE_YSIZE_HIMETRIC; + + +EXTERN_C const IID IID_IPicture; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPicture : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE get_Handle( + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_hPal( + /* [out] */ OLE_HANDLE __RPC_FAR *phPal) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Type( + /* [out] */ SHORT __RPC_FAR *pType) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Width( + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Height( + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight) = 0; + + virtual HRESULT STDMETHODCALLTYPE Render( + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds) = 0; + + virtual HRESULT STDMETHODCALLTYPE set_hPal( + /* [in] */ OLE_HANDLE hPal) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_CurDC( + /* [out] */ HDC __RPC_FAR *phDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE SelectPicture( + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_KeepOriginalFormat( + /* [out] */ BOOL __RPC_FAR *pKeep) = 0; + + virtual HRESULT STDMETHODCALLTYPE put_KeepOriginalFormat( + /* [in] */ BOOL keep) = 0; + + virtual HRESULT STDMETHODCALLTYPE PictureChanged( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SaveAsFile( + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE get_Attributes( + /* [out] */ DWORD __RPC_FAR *pDwAttr) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPictureVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPicture __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPicture __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPicture __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Handle )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_hPal )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *phPal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Type )( + IPicture __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pType); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Width )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Height )( + IPicture __RPC_FAR * This, + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Render )( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *set_hPal )( + IPicture __RPC_FAR * This, + /* [in] */ OLE_HANDLE hPal); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_CurDC )( + IPicture __RPC_FAR * This, + /* [out] */ HDC __RPC_FAR *phDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SelectPicture )( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_KeepOriginalFormat )( + IPicture __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pKeep); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *put_KeepOriginalFormat )( + IPicture __RPC_FAR * This, + /* [in] */ BOOL keep); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *PictureChanged )( + IPicture __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SaveAsFile )( + IPicture __RPC_FAR * This, + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *get_Attributes )( + IPicture __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pDwAttr); + + END_INTERFACE + } IPictureVtbl; + + interface IPicture + { + CONST_VTBL struct IPictureVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPicture_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPicture_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPicture_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPicture_get_Handle(This,pHandle) \ + (This)->lpVtbl -> get_Handle(This,pHandle) + +#define IPicture_get_hPal(This,phPal) \ + (This)->lpVtbl -> get_hPal(This,phPal) + +#define IPicture_get_Type(This,pType) \ + (This)->lpVtbl -> get_Type(This,pType) + +#define IPicture_get_Width(This,pWidth) \ + (This)->lpVtbl -> get_Width(This,pWidth) + +#define IPicture_get_Height(This,pHeight) \ + (This)->lpVtbl -> get_Height(This,pHeight) + +#define IPicture_Render(This,hDC,x,y,cx,cy,xSrc,ySrc,cxSrc,cySrc,pRcWBounds) \ + (This)->lpVtbl -> Render(This,hDC,x,y,cx,cy,xSrc,ySrc,cxSrc,cySrc,pRcWBounds) + +#define IPicture_set_hPal(This,hPal) \ + (This)->lpVtbl -> set_hPal(This,hPal) + +#define IPicture_get_CurDC(This,phDC) \ + (This)->lpVtbl -> get_CurDC(This,phDC) + +#define IPicture_SelectPicture(This,hDCIn,phDCOut,phBmpOut) \ + (This)->lpVtbl -> SelectPicture(This,hDCIn,phDCOut,phBmpOut) + +#define IPicture_get_KeepOriginalFormat(This,pKeep) \ + (This)->lpVtbl -> get_KeepOriginalFormat(This,pKeep) + +#define IPicture_put_KeepOriginalFormat(This,keep) \ + (This)->lpVtbl -> put_KeepOriginalFormat(This,keep) + +#define IPicture_PictureChanged(This) \ + (This)->lpVtbl -> PictureChanged(This) + +#define IPicture_SaveAsFile(This,pStream,fSaveMemCopy,pCbSize) \ + (This)->lpVtbl -> SaveAsFile(This,pStream,fSaveMemCopy,pCbSize) + +#define IPicture_get_Attributes(This,pDwAttr) \ + (This)->lpVtbl -> get_Attributes(This,pDwAttr) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPicture_get_Handle_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *pHandle); + + +void __RPC_STUB IPicture_get_Handle_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_hPal_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_HANDLE __RPC_FAR *phPal); + + +void __RPC_STUB IPicture_get_hPal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Type_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ SHORT __RPC_FAR *pType); + + +void __RPC_STUB IPicture_get_Type_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Width_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_XSIZE_HIMETRIC __RPC_FAR *pWidth); + + +void __RPC_STUB IPicture_get_Width_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Height_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ OLE_YSIZE_HIMETRIC __RPC_FAR *pHeight); + + +void __RPC_STUB IPicture_get_Height_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_Render_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDC, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ LONG cx, + /* [in] */ LONG cy, + /* [in] */ OLE_XPOS_HIMETRIC xSrc, + /* [in] */ OLE_YPOS_HIMETRIC ySrc, + /* [in] */ OLE_XSIZE_HIMETRIC cxSrc, + /* [in] */ OLE_YSIZE_HIMETRIC cySrc, + /* [in] */ LPCRECT pRcWBounds); + + +void __RPC_STUB IPicture_Render_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_set_hPal_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ OLE_HANDLE hPal); + + +void __RPC_STUB IPicture_set_hPal_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_CurDC_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ HDC __RPC_FAR *phDC); + + +void __RPC_STUB IPicture_get_CurDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_SelectPicture_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ HDC hDCIn, + /* [out] */ HDC __RPC_FAR *phDCOut, + /* [out] */ OLE_HANDLE __RPC_FAR *phBmpOut); + + +void __RPC_STUB IPicture_SelectPicture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_KeepOriginalFormat_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pKeep); + + +void __RPC_STUB IPicture_get_KeepOriginalFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_put_KeepOriginalFormat_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ BOOL keep); + + +void __RPC_STUB IPicture_put_KeepOriginalFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_PictureChanged_Proxy( + IPicture __RPC_FAR * This); + + +void __RPC_STUB IPicture_PictureChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_SaveAsFile_Proxy( + IPicture __RPC_FAR * This, + /* [in] */ LPSTREAM pStream, + /* [in] */ BOOL fSaveMemCopy, + /* [out] */ LONG __RPC_FAR *pCbSize); + + +void __RPC_STUB IPicture_SaveAsFile_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPicture_get_Attributes_Proxy( + IPicture __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pDwAttr); + + +void __RPC_STUB IPicture_get_Attributes_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPicture_INTERFACE_DEFINED__ */ + + +#ifndef __IFontDisp_INTERFACE_DEFINED__ +#define __IFontDisp_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IFontDisp + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IFontDisp __RPC_FAR *LPFONTDISP; + + +EXTERN_C const IID IID_IFontDisp; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IFontDisp : public IDispatch + { + public: + }; + +#else /* C style interface */ + + typedef struct IFontDispVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IFontDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IFontDisp __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IFontDisp __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IFontDisp __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IFontDisp __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IFontDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IFontDisp __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + END_INTERFACE + } IFontDispVtbl; + + interface IFontDisp + { + CONST_VTBL struct IFontDispVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IFontDisp_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IFontDisp_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IFontDisp_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IFontDisp_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IFontDisp_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IFontDisp_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IFontDisp_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IFontDisp_INTERFACE_DEFINED__ */ + + +#ifndef __IPictureDisp_INTERFACE_DEFINED__ +#define __IPictureDisp_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPictureDisp + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPictureDisp __RPC_FAR *LPPICTUREDISP; + + +EXTERN_C const IID IID_IPictureDisp; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPictureDisp : public IDispatch + { + public: + }; + +#else /* C style interface */ + + typedef struct IPictureDispVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPictureDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPictureDisp __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPictureDisp __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfoCount )( + IPictureDisp __RPC_FAR * This, + /* [out] */ UINT __RPC_FAR *pctinfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetTypeInfo )( + IPictureDisp __RPC_FAR * This, + /* [in] */ UINT iTInfo, + /* [in] */ LCID lcid, + /* [out] */ ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetIDsOfNames )( + IPictureDisp __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [size_is][in] */ LPOLESTR __RPC_FAR *rgszNames, + /* [in] */ UINT cNames, + /* [in] */ LCID lcid, + /* [size_is][out] */ DISPID __RPC_FAR *rgDispId); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Invoke )( + IPictureDisp __RPC_FAR * This, + /* [in] */ DISPID dispIdMember, + /* [in] */ REFIID riid, + /* [in] */ LCID lcid, + /* [in] */ WORD wFlags, + /* [out][in] */ DISPPARAMS __RPC_FAR *pDispParams, + /* [out] */ VARIANT __RPC_FAR *pVarResult, + /* [out] */ EXCEPINFO __RPC_FAR *pExcepInfo, + /* [out] */ UINT __RPC_FAR *puArgErr); + + END_INTERFACE + } IPictureDispVtbl; + + interface IPictureDisp + { + CONST_VTBL struct IPictureDispVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPictureDisp_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPictureDisp_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPictureDisp_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPictureDisp_GetTypeInfoCount(This,pctinfo) \ + (This)->lpVtbl -> GetTypeInfoCount(This,pctinfo) + +#define IPictureDisp_GetTypeInfo(This,iTInfo,lcid,ppTInfo) \ + (This)->lpVtbl -> GetTypeInfo(This,iTInfo,lcid,ppTInfo) + +#define IPictureDisp_GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) \ + (This)->lpVtbl -> GetIDsOfNames(This,riid,rgszNames,cNames,lcid,rgDispId) + +#define IPictureDisp_Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) \ + (This)->lpVtbl -> Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr) + + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + + +#endif /* __IPictureDisp_INTERFACE_DEFINED__ */ + + +#ifndef __IAdviseSinkEx_INTERFACE_DEFINED__ +#define __IAdviseSinkEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IAdviseSinkEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IAdviseSinkEx __RPC_FAR *LPADVISESINKEX; + + +EXTERN_C const IID IID_IAdviseSinkEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IAdviseSinkEx : public IAdviseSink + { + public: + virtual /* [local] */ void STDMETHODCALLTYPE OnViewStatusChange( + /* [in] */ DWORD dwViewStatus) = 0; + + }; + +#else /* C style interface */ + + typedef struct IAdviseSinkExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IAdviseSinkEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnDataChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [unique][in] */ FORMATETC __RPC_FAR *pFormatetc, + /* [unique][in] */ STGMEDIUM __RPC_FAR *pStgmed); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnViewChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnRename )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ IMoniker __RPC_FAR *pmk); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnSave )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnClose )( + IAdviseSinkEx __RPC_FAR * This); + + /* [local] */ void ( STDMETHODCALLTYPE __RPC_FAR *OnViewStatusChange )( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + END_INTERFACE + } IAdviseSinkExVtbl; + + interface IAdviseSinkEx + { + CONST_VTBL struct IAdviseSinkExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IAdviseSinkEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IAdviseSinkEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IAdviseSinkEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IAdviseSinkEx_OnDataChange(This,pFormatetc,pStgmed) \ + (This)->lpVtbl -> OnDataChange(This,pFormatetc,pStgmed) + +#define IAdviseSinkEx_OnViewChange(This,dwAspect,lindex) \ + (This)->lpVtbl -> OnViewChange(This,dwAspect,lindex) + +#define IAdviseSinkEx_OnRename(This,pmk) \ + (This)->lpVtbl -> OnRename(This,pmk) + +#define IAdviseSinkEx_OnSave(This) \ + (This)->lpVtbl -> OnSave(This) + +#define IAdviseSinkEx_OnClose(This) \ + (This)->lpVtbl -> OnClose(This) + + +#define IAdviseSinkEx_OnViewStatusChange(This,dwViewStatus) \ + (This)->lpVtbl -> OnViewStatusChange(This,dwViewStatus) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [async][call_as] */ void STDMETHODCALLTYPE IAdviseSinkEx_RemoteOnViewStatusChange_Proxy( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + +void __RPC_STUB IAdviseSinkEx_RemoteOnViewStatusChange_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IAdviseSinkEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ +#define __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceObjectWindowless + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IOleInPlaceObjectWindowless __RPC_FAR *LPOLEINPLACEOBJECTWINDOWLESS; + + +EXTERN_C const IID IID_IOleInPlaceObjectWindowless; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceObjectWindowless : public IOleInPlaceObject + { + public: + virtual HRESULT STDMETHODCALLTYPE OnWindowMessage( + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDropTarget( + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceObjectWindowlessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InPlaceDeactivate )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UIDeactivate )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetObjectRects )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect, + /* [in] */ LPCRECT lprcClipRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReactivateAndUndo )( + IOleInPlaceObjectWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnWindowMessage )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDropTarget )( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); + + END_INTERFACE + } IOleInPlaceObjectWindowlessVtbl; + + interface IOleInPlaceObjectWindowless + { + CONST_VTBL struct IOleInPlaceObjectWindowlessVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceObjectWindowless_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceObjectWindowless_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceObjectWindowless_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceObjectWindowless_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceObjectWindowless_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceObjectWindowless_InPlaceDeactivate(This) \ + (This)->lpVtbl -> InPlaceDeactivate(This) + +#define IOleInPlaceObjectWindowless_UIDeactivate(This) \ + (This)->lpVtbl -> UIDeactivate(This) + +#define IOleInPlaceObjectWindowless_SetObjectRects(This,lprcPosRect,lprcClipRect) \ + (This)->lpVtbl -> SetObjectRects(This,lprcPosRect,lprcClipRect) + +#define IOleInPlaceObjectWindowless_ReactivateAndUndo(This) \ + (This)->lpVtbl -> ReactivateAndUndo(This) + + +#define IOleInPlaceObjectWindowless_OnWindowMessage(This,msg,wParam,lParam,plResult) \ + (This)->lpVtbl -> OnWindowMessage(This,msg,wParam,lParam,plResult) + +#define IOleInPlaceObjectWindowless_GetDropTarget(This,ppDropTarget) \ + (This)->lpVtbl -> GetDropTarget(This,ppDropTarget) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceObjectWindowless_OnWindowMessage_Proxy( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + +void __RPC_STUB IOleInPlaceObjectWindowless_OnWindowMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceObjectWindowless_GetDropTarget_Proxy( + IOleInPlaceObjectWindowless __RPC_FAR * This, + /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); + + +void __RPC_STUB IOleInPlaceObjectWindowless_GetDropTarget_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceObjectWindowless_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteEx_INTERFACE_DEFINED__ +#define __IOleInPlaceSiteEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceSiteEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleInPlaceSiteEx __RPC_FAR *LPOLEINPLACESITEEX; + +typedef /* [v1_enum] */ +enum tagACTIVATEFLAGS + { ACTIVATE_WINDOWLESS = 1 + } ACTIVATEFLAGS; + + +EXTERN_C const IID IID_IOleInPlaceSiteEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceSiteEx : public IOleInPlaceSite + { + public: + virtual HRESULT STDMETHODCALLTYPE OnInPlaceActivateEx( + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInPlaceDeactivateEx( + /* [in] */ BOOL fNoRedraw) = 0; + + virtual HRESULT STDMETHODCALLTYPE RequestUIActivate( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceSiteExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceSiteEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceSiteEx __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanInPlaceActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindowContext )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ IOleInPlaceFrame __RPC_FAR *__RPC_FAR *ppFrame, + /* [out] */ IOleInPlaceUIWindow __RPC_FAR *__RPC_FAR *ppDoc, + /* [out] */ LPRECT lprcPosRect, + /* [out] */ LPRECT lprcClipRect, + /* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Scroll )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ SIZE scrollExtant); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIDeactivate )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fUndoable); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardUndoState )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeactivateAndUndo )( + IOleInPlaceSiteEx __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPosRectChange )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivateEx )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivateEx )( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUIActivate )( + IOleInPlaceSiteEx __RPC_FAR * This); + + END_INTERFACE + } IOleInPlaceSiteExVtbl; + + interface IOleInPlaceSiteEx + { + CONST_VTBL struct IOleInPlaceSiteExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceSiteEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceSiteEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceSiteEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceSiteEx_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceSiteEx_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceSiteEx_CanInPlaceActivate(This) \ + (This)->lpVtbl -> CanInPlaceActivate(This) + +#define IOleInPlaceSiteEx_OnInPlaceActivate(This) \ + (This)->lpVtbl -> OnInPlaceActivate(This) + +#define IOleInPlaceSiteEx_OnUIActivate(This) \ + (This)->lpVtbl -> OnUIActivate(This) + +#define IOleInPlaceSiteEx_GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) \ + (This)->lpVtbl -> GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) + +#define IOleInPlaceSiteEx_Scroll(This,scrollExtant) \ + (This)->lpVtbl -> Scroll(This,scrollExtant) + +#define IOleInPlaceSiteEx_OnUIDeactivate(This,fUndoable) \ + (This)->lpVtbl -> OnUIDeactivate(This,fUndoable) + +#define IOleInPlaceSiteEx_OnInPlaceDeactivate(This) \ + (This)->lpVtbl -> OnInPlaceDeactivate(This) + +#define IOleInPlaceSiteEx_DiscardUndoState(This) \ + (This)->lpVtbl -> DiscardUndoState(This) + +#define IOleInPlaceSiteEx_DeactivateAndUndo(This) \ + (This)->lpVtbl -> DeactivateAndUndo(This) + +#define IOleInPlaceSiteEx_OnPosRectChange(This,lprcPosRect) \ + (This)->lpVtbl -> OnPosRectChange(This,lprcPosRect) + + +#define IOleInPlaceSiteEx_OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) \ + (This)->lpVtbl -> OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) + +#define IOleInPlaceSiteEx_OnInPlaceDeactivateEx(This,fNoRedraw) \ + (This)->lpVtbl -> OnInPlaceDeactivateEx(This,fNoRedraw) + +#define IOleInPlaceSiteEx_RequestUIActivate(This) \ + (This)->lpVtbl -> RequestUIActivate(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_OnInPlaceActivateEx_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + +void __RPC_STUB IOleInPlaceSiteEx_OnInPlaceActivateEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_OnInPlaceDeactivateEx_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + +void __RPC_STUB IOleInPlaceSiteEx_OnInPlaceDeactivateEx_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteEx_RequestUIActivate_Proxy( + IOleInPlaceSiteEx __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteEx_RequestUIActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceSiteEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ +#define __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleInPlaceSiteWindowless + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IOleInPlaceSiteWindowless __RPC_FAR *LPOLEINPLACESITEWINDOWLESS; + +typedef /* [v1_enum] */ +enum tagOLEDCFLAGS + { OLEDC_NODRAW = 0x1, + OLEDC_PAINTBKGND = 0x2, + OLEDC_OFFSCREEN = 0x4 + } OLEDCFLAGS; + + +EXTERN_C const IID IID_IOleInPlaceSiteWindowless; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleInPlaceSiteWindowless : public IOleInPlaceSiteEx + { + public: + virtual HRESULT STDMETHODCALLTYPE CanWindowlessActivate( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetCapture( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetCapture( + /* [in] */ BOOL fCapture) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFocus( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetFocus( + /* [in] */ BOOL fFocus) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDC( + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE ReleaseDC( + /* [in] */ HDC hDC) = 0; + + virtual HRESULT STDMETHODCALLTYPE InvalidateRect( + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase) = 0; + + virtual HRESULT STDMETHODCALLTYPE InvalidateRgn( + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase) = 0; + + virtual HRESULT STDMETHODCALLTYPE ScrollRect( + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip) = 0; + + virtual HRESULT STDMETHODCALLTYPE AdjustRect( + /* [out][in] */ LPRECT prc) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnDefWindowMessage( + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleInPlaceSiteWindowlessVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + /* [input_sync] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindow )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ HWND __RPC_FAR *phwnd); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ContextSensitiveHelp )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fEnterMode); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanInPlaceActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetWindowContext )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ IOleInPlaceFrame __RPC_FAR *__RPC_FAR *ppFrame, + /* [out] */ IOleInPlaceUIWindow __RPC_FAR *__RPC_FAR *ppDoc, + /* [out] */ LPRECT lprcPosRect, + /* [out] */ LPRECT lprcClipRect, + /* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Scroll )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ SIZE scrollExtant); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnUIDeactivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fUndoable); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardUndoState )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DeactivateAndUndo )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnPosRectChange )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT lprcPosRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceActivateEx )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out] */ BOOL __RPC_FAR *pfNoRedraw, + /* [in] */ DWORD dwFlags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInPlaceDeactivateEx )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fNoRedraw); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RequestUIActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CanWindowlessActivate )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetCapture )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetCapture )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fCapture); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetFocus )( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetFocus )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fFocus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDC )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ReleaseDC )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HDC hDC); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InvalidateRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *InvalidateRgn )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ScrollRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AdjustRect )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out][in] */ LPRECT prc); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnDefWindowMessage )( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + END_INTERFACE + } IOleInPlaceSiteWindowlessVtbl; + + interface IOleInPlaceSiteWindowless + { + CONST_VTBL struct IOleInPlaceSiteWindowlessVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleInPlaceSiteWindowless_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleInPlaceSiteWindowless_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleInPlaceSiteWindowless_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleInPlaceSiteWindowless_GetWindow(This,phwnd) \ + (This)->lpVtbl -> GetWindow(This,phwnd) + +#define IOleInPlaceSiteWindowless_ContextSensitiveHelp(This,fEnterMode) \ + (This)->lpVtbl -> ContextSensitiveHelp(This,fEnterMode) + + +#define IOleInPlaceSiteWindowless_CanInPlaceActivate(This) \ + (This)->lpVtbl -> CanInPlaceActivate(This) + +#define IOleInPlaceSiteWindowless_OnInPlaceActivate(This) \ + (This)->lpVtbl -> OnInPlaceActivate(This) + +#define IOleInPlaceSiteWindowless_OnUIActivate(This) \ + (This)->lpVtbl -> OnUIActivate(This) + +#define IOleInPlaceSiteWindowless_GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) \ + (This)->lpVtbl -> GetWindowContext(This,ppFrame,ppDoc,lprcPosRect,lprcClipRect,lpFrameInfo) + +#define IOleInPlaceSiteWindowless_Scroll(This,scrollExtant) \ + (This)->lpVtbl -> Scroll(This,scrollExtant) + +#define IOleInPlaceSiteWindowless_OnUIDeactivate(This,fUndoable) \ + (This)->lpVtbl -> OnUIDeactivate(This,fUndoable) + +#define IOleInPlaceSiteWindowless_OnInPlaceDeactivate(This) \ + (This)->lpVtbl -> OnInPlaceDeactivate(This) + +#define IOleInPlaceSiteWindowless_DiscardUndoState(This) \ + (This)->lpVtbl -> DiscardUndoState(This) + +#define IOleInPlaceSiteWindowless_DeactivateAndUndo(This) \ + (This)->lpVtbl -> DeactivateAndUndo(This) + +#define IOleInPlaceSiteWindowless_OnPosRectChange(This,lprcPosRect) \ + (This)->lpVtbl -> OnPosRectChange(This,lprcPosRect) + + +#define IOleInPlaceSiteWindowless_OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) \ + (This)->lpVtbl -> OnInPlaceActivateEx(This,pfNoRedraw,dwFlags) + +#define IOleInPlaceSiteWindowless_OnInPlaceDeactivateEx(This,fNoRedraw) \ + (This)->lpVtbl -> OnInPlaceDeactivateEx(This,fNoRedraw) + +#define IOleInPlaceSiteWindowless_RequestUIActivate(This) \ + (This)->lpVtbl -> RequestUIActivate(This) + + +#define IOleInPlaceSiteWindowless_CanWindowlessActivate(This) \ + (This)->lpVtbl -> CanWindowlessActivate(This) + +#define IOleInPlaceSiteWindowless_GetCapture(This) \ + (This)->lpVtbl -> GetCapture(This) + +#define IOleInPlaceSiteWindowless_SetCapture(This,fCapture) \ + (This)->lpVtbl -> SetCapture(This,fCapture) + +#define IOleInPlaceSiteWindowless_GetFocus(This) \ + (This)->lpVtbl -> GetFocus(This) + +#define IOleInPlaceSiteWindowless_SetFocus(This,fFocus) \ + (This)->lpVtbl -> SetFocus(This,fFocus) + +#define IOleInPlaceSiteWindowless_GetDC(This,pRect,grfFlags,phDC) \ + (This)->lpVtbl -> GetDC(This,pRect,grfFlags,phDC) + +#define IOleInPlaceSiteWindowless_ReleaseDC(This,hDC) \ + (This)->lpVtbl -> ReleaseDC(This,hDC) + +#define IOleInPlaceSiteWindowless_InvalidateRect(This,pRect,fErase) \ + (This)->lpVtbl -> InvalidateRect(This,pRect,fErase) + +#define IOleInPlaceSiteWindowless_InvalidateRgn(This,hRGN,fErase) \ + (This)->lpVtbl -> InvalidateRgn(This,hRGN,fErase) + +#define IOleInPlaceSiteWindowless_ScrollRect(This,dx,dy,pRectScroll,pRectClip) \ + (This)->lpVtbl -> ScrollRect(This,dx,dy,pRectScroll,pRectClip) + +#define IOleInPlaceSiteWindowless_AdjustRect(This,prc) \ + (This)->lpVtbl -> AdjustRect(This,prc) + +#define IOleInPlaceSiteWindowless_OnDefWindowMessage(This,msg,wParam,lParam,plResult) \ + (This)->lpVtbl -> OnDefWindowMessage(This,msg,wParam,lParam,plResult) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_CanWindowlessActivate_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_CanWindowlessActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetCapture_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_SetCapture_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fCapture); + + +void __RPC_STUB IOleInPlaceSiteWindowless_SetCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetFocus_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_SetFocus_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ BOOL fFocus); + + +void __RPC_STUB IOleInPlaceSiteWindowless_SetFocus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_GetDC_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ DWORD grfFlags, + /* [out] */ HDC __RPC_FAR *phDC); + + +void __RPC_STUB IOleInPlaceSiteWindowless_GetDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_ReleaseDC_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HDC hDC); + + +void __RPC_STUB IOleInPlaceSiteWindowless_ReleaseDC_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_InvalidateRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ LPCRECT pRect, + /* [in] */ BOOL fErase); + + +void __RPC_STUB IOleInPlaceSiteWindowless_InvalidateRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_InvalidateRgn_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ HRGN hRGN, + /* [in] */ BOOL fErase); + + +void __RPC_STUB IOleInPlaceSiteWindowless_InvalidateRgn_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_ScrollRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ INT dx, + /* [in] */ INT dy, + /* [in] */ LPCRECT pRectScroll, + /* [in] */ LPCRECT pRectClip); + + +void __RPC_STUB IOleInPlaceSiteWindowless_ScrollRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_AdjustRect_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [out][in] */ LPRECT prc); + + +void __RPC_STUB IOleInPlaceSiteWindowless_AdjustRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleInPlaceSiteWindowless_OnDefWindowMessage_Proxy( + IOleInPlaceSiteWindowless __RPC_FAR * This, + /* [in] */ UINT msg, + /* [in] */ WPARAM wParam, + /* [in] */ LPARAM lParam, + /* [out] */ LRESULT __RPC_FAR *plResult); + + +void __RPC_STUB IOleInPlaceSiteWindowless_OnDefWindowMessage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleInPlaceSiteWindowless_INTERFACE_DEFINED__ */ + + +#ifndef __IViewObjectEx_INTERFACE_DEFINED__ +#define __IViewObjectEx_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IViewObjectEx + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IViewObjectEx __RPC_FAR *LPVIEWOBJECTEX; + +typedef /* [v1_enum] */ +enum tagVIEWSTATUS + { VIEWSTATUS_OPAQUE = 1, + VIEWSTATUS_SOLIDBKGND = 2, + VIEWSTATUS_DVASPECTOPAQUE = 4, + VIEWSTATUS_DVASPECTTRANSPARENT = 8 + } VIEWSTATUS; + +typedef /* [v1_enum] */ +enum tagHITRESULT + { HITRESULT_OUTSIDE = 0, + HITRESULT_TRANSPARENT = 1, + HITRESULT_CLOSE = 2, + HITRESULT_HIT = 3 + } HITRESULT; + +typedef /* [v1_enum] */ +enum tagDVASPECT2 + { DVASPECT_OPAQUE = 16, + DVASPECT_TRANSPARENT = 32 + } DVASPECT2; + +typedef struct tagExtentInfo + { + ULONG cb; + DWORD dwExtentMode; + SIZEL sizelProposed; + } DVEXTENTINFO; + +typedef /* [v1_enum] */ +enum tagExtentMode + { DVEXTENT_CONTENT = 0, + DVEXTENT_INTEGRAL = DVEXTENT_CONTENT + 1 + } DVEXTENTMODE; + +typedef /* [v1_enum] */ +enum tagAspectInfoFlag + { DVASPECTINFOFLAG_CANOPTIMIZE = 1 + } DVASPECTINFOFLAG; + +typedef struct tagAspectInfo + { + ULONG cb; + DWORD dwFlags; + } DVASPECTINFO; + + +EXTERN_C const IID IID_IViewObjectEx; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IViewObjectEx : public IViewObject2 + { + public: + virtual HRESULT STDMETHODCALLTYPE GetRect( + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetViewStatus( + /* [out] */ DWORD __RPC_FAR *pdwStatus) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryHitPoint( + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE QueryHitRect( + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNaturalExtent( + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel) = 0; + + }; + +#else /* C style interface */ + + typedef struct IViewObjectExVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IViewObjectEx __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IViewObjectEx __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Draw )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hdcTargetDev, + /* [in] */ HDC hdcDraw, + /* [in] */ LPCRECTL lprcBounds, + /* [unique][in] */ LPCRECTL lprcWBounds, + /* [in] */ BOOL ( STDMETHODCALLTYPE __RPC_FAR *pfnContinue )( + DWORD dwContinue), + /* [in] */ DWORD dwContinue); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetColorSet )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [out] */ LOGPALETTE __RPC_FAR *__RPC_FAR *ppColorSet); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Freeze )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ void __RPC_FAR *pvAspect, + /* [out] */ DWORD __RPC_FAR *pdwFreeze); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Unfreeze )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwFreeze); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetAdvise )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD aspects, + /* [in] */ DWORD advf, + /* [unique][in] */ IAdviseSink __RPC_FAR *pAdvSink); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAdvise )( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pAspects, + /* [out] */ DWORD __RPC_FAR *pAdvf, + /* [out] */ IAdviseSink __RPC_FAR *__RPC_FAR *ppAdvSink); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetExtent )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwDrawAspect, + /* [in] */ LONG lindex, + /* [unique][in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [out] */ LPSIZEL lpsizel); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetRect )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetViewStatus )( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwStatus); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryHitPoint )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryHitRect )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetNaturalExtent )( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel); + + END_INTERFACE + } IViewObjectExVtbl; + + interface IViewObjectEx + { + CONST_VTBL struct IViewObjectExVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IViewObjectEx_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IViewObjectEx_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IViewObjectEx_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IViewObjectEx_Draw(This,dwDrawAspect,lindex,pvAspect,ptd,hdcTargetDev,hdcDraw,lprcBounds,lprcWBounds,pfnContinue,dwContinue) \ + (This)->lpVtbl -> Draw(This,dwDrawAspect,lindex,pvAspect,ptd,hdcTargetDev,hdcDraw,lprcBounds,lprcWBounds,pfnContinue,dwContinue) + +#define IViewObjectEx_GetColorSet(This,dwDrawAspect,lindex,pvAspect,ptd,hicTargetDev,ppColorSet) \ + (This)->lpVtbl -> GetColorSet(This,dwDrawAspect,lindex,pvAspect,ptd,hicTargetDev,ppColorSet) + +#define IViewObjectEx_Freeze(This,dwDrawAspect,lindex,pvAspect,pdwFreeze) \ + (This)->lpVtbl -> Freeze(This,dwDrawAspect,lindex,pvAspect,pdwFreeze) + +#define IViewObjectEx_Unfreeze(This,dwFreeze) \ + (This)->lpVtbl -> Unfreeze(This,dwFreeze) + +#define IViewObjectEx_SetAdvise(This,aspects,advf,pAdvSink) \ + (This)->lpVtbl -> SetAdvise(This,aspects,advf,pAdvSink) + +#define IViewObjectEx_GetAdvise(This,pAspects,pAdvf,ppAdvSink) \ + (This)->lpVtbl -> GetAdvise(This,pAspects,pAdvf,ppAdvSink) + + +#define IViewObjectEx_GetExtent(This,dwDrawAspect,lindex,ptd,lpsizel) \ + (This)->lpVtbl -> GetExtent(This,dwDrawAspect,lindex,ptd,lpsizel) + + +#define IViewObjectEx_GetRect(This,dwAspect,pRect) \ + (This)->lpVtbl -> GetRect(This,dwAspect,pRect) + +#define IViewObjectEx_GetViewStatus(This,pdwStatus) \ + (This)->lpVtbl -> GetViewStatus(This,pdwStatus) + +#define IViewObjectEx_QueryHitPoint(This,dwAspect,pRectBounds,ptlLoc,lCloseHint,pHitResult) \ + (This)->lpVtbl -> QueryHitPoint(This,dwAspect,pRectBounds,ptlLoc,lCloseHint,pHitResult) + +#define IViewObjectEx_QueryHitRect(This,dwAspect,pRectBounds,pRectLoc,lCloseHint,pHitResult) \ + (This)->lpVtbl -> QueryHitRect(This,dwAspect,pRectBounds,pRectLoc,lCloseHint,pHitResult) + +#define IViewObjectEx_GetNaturalExtent(This,dwAspect,lindex,ptd,hicTargetDev,pExtentInfo,pSizel) \ + (This)->lpVtbl -> GetNaturalExtent(This,dwAspect,lindex,ptd,hicTargetDev,pExtentInfo,pSizel) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetRect_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [out] */ LPRECTL pRect); + + +void __RPC_STUB IViewObjectEx_GetRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetViewStatus_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwStatus); + + +void __RPC_STUB IViewObjectEx_GetViewStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_QueryHitPoint_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ POINT ptlLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + +void __RPC_STUB IViewObjectEx_QueryHitPoint_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_QueryHitRect_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LPCRECT pRectLoc, + /* [in] */ LONG lCloseHint, + /* [out] */ DWORD __RPC_FAR *pHitResult); + + +void __RPC_STUB IViewObjectEx_QueryHitRect_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IViewObjectEx_GetNaturalExtent_Proxy( + IViewObjectEx __RPC_FAR * This, + /* [in] */ DWORD dwAspect, + /* [in] */ LONG lindex, + /* [in] */ DVTARGETDEVICE __RPC_FAR *ptd, + /* [in] */ HDC hicTargetDev, + /* [in] */ DVEXTENTINFO __RPC_FAR *pExtentInfo, + /* [out] */ LPSIZEL pSizel); + + +void __RPC_STUB IViewObjectEx_GetNaturalExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IViewObjectEx_INTERFACE_DEFINED__ */ + + +#ifndef __IOleUndoUnit_INTERFACE_DEFINED__ +#define __IOleUndoUnit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleUndoUnit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleUndoUnit __RPC_FAR *LPOLEUNDOUNIT; + + +EXTERN_C const IID IID_IOleUndoUnit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleUndoUnit : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Do( + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetUnitType( + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnNextAdd( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleUndoUnitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleUndoUnit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Do )( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDescription )( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetUnitType )( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNextAdd )( + IOleUndoUnit __RPC_FAR * This); + + END_INTERFACE + } IOleUndoUnitVtbl; + + interface IOleUndoUnit + { + CONST_VTBL struct IOleUndoUnitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleUndoUnit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleUndoUnit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleUndoUnit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleUndoUnit_Do(This,pUndoManager) \ + (This)->lpVtbl -> Do(This,pUndoManager) + +#define IOleUndoUnit_GetDescription(This,pBstr) \ + (This)->lpVtbl -> GetDescription(This,pBstr) + +#define IOleUndoUnit_GetUnitType(This,pClsid,plID) \ + (This)->lpVtbl -> GetUnitType(This,pClsid,plID) + +#define IOleUndoUnit_OnNextAdd(This) \ + (This)->lpVtbl -> OnNextAdd(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_Do_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + +void __RPC_STUB IOleUndoUnit_Do_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_GetDescription_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoUnit_GetDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_GetUnitType_Proxy( + IOleUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + +void __RPC_STUB IOleUndoUnit_GetUnitType_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoUnit_OnNextAdd_Proxy( + IOleUndoUnit __RPC_FAR * This); + + +void __RPC_STUB IOleUndoUnit_OnNextAdd_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleUndoUnit_INTERFACE_DEFINED__ */ + + +#ifndef __IOleParentUndoUnit_INTERFACE_DEFINED__ +#define __IOleParentUndoUnit_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleParentUndoUnit + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IOleParentUndoUnit __RPC_FAR *LPOLEPARENTUNDOUNIT; + + +EXTERN_C const IID IID_IOleParentUndoUnit; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleParentUndoUnit : public IOleUndoUnit + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit) = 0; + + virtual HRESULT STDMETHODCALLTYPE Add( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindUnit( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetParentState( + /* [out] */ DWORD __RPC_FAR *pdwState) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleParentUndoUnitVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleParentUndoUnit __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleParentUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Do )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoManager __RPC_FAR *pUndoManager); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDescription )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetUnitType )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ CLSID __RPC_FAR *pClsid, + /* [out] */ LONG __RPC_FAR *plID); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnNextAdd )( + IOleParentUndoUnit __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Add )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *FindUnit )( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetParentState )( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + END_INTERFACE + } IOleParentUndoUnitVtbl; + + interface IOleParentUndoUnit + { + CONST_VTBL struct IOleParentUndoUnitVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleParentUndoUnit_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleParentUndoUnit_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleParentUndoUnit_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleParentUndoUnit_Do(This,pUndoManager) \ + (This)->lpVtbl -> Do(This,pUndoManager) + +#define IOleParentUndoUnit_GetDescription(This,pBstr) \ + (This)->lpVtbl -> GetDescription(This,pBstr) + +#define IOleParentUndoUnit_GetUnitType(This,pClsid,plID) \ + (This)->lpVtbl -> GetUnitType(This,pClsid,plID) + +#define IOleParentUndoUnit_OnNextAdd(This) \ + (This)->lpVtbl -> OnNextAdd(This) + + +#define IOleParentUndoUnit_Open(This,pPUU) \ + (This)->lpVtbl -> Open(This,pPUU) + +#define IOleParentUndoUnit_Close(This,pPUU,fCommit) \ + (This)->lpVtbl -> Close(This,pPUU,fCommit) + +#define IOleParentUndoUnit_Add(This,pUU) \ + (This)->lpVtbl -> Add(This,pUU) + +#define IOleParentUndoUnit_FindUnit(This,pUU) \ + (This)->lpVtbl -> FindUnit(This,pUU) + +#define IOleParentUndoUnit_GetParentState(This,pdwState) \ + (This)->lpVtbl -> GetParentState(This,pdwState) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Open_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + +void __RPC_STUB IOleParentUndoUnit_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Close_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + +void __RPC_STUB IOleParentUndoUnit_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_Add_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleParentUndoUnit_Add_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_FindUnit_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleParentUndoUnit_FindUnit_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleParentUndoUnit_GetParentState_Proxy( + IOleParentUndoUnit __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + +void __RPC_STUB IOleParentUndoUnit_GetParentState_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleParentUndoUnit_INTERFACE_DEFINED__ */ + + +#ifndef __IEnumOleUndoUnits_INTERFACE_DEFINED__ +#define __IEnumOleUndoUnits_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IEnumOleUndoUnits + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IEnumOleUndoUnits __RPC_FAR *LPENUMOLEUNDOUNITS; + + +EXTERN_C const IID IID_IEnumOleUndoUnits; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IEnumOleUndoUnits : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Next( + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched) = 0; + + virtual HRESULT STDMETHODCALLTYPE Skip( + /* [in] */ ULONG cElt) = 0; + + virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Clone( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + }; + +#else /* C style interface */ + + typedef struct IEnumOleUndoUnitsVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IEnumOleUndoUnits __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IEnumOleUndoUnits __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Next )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Skip )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Reset )( + IEnumOleUndoUnits __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )( + IEnumOleUndoUnits __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + END_INTERFACE + } IEnumOleUndoUnitsVtbl; + + interface IEnumOleUndoUnits + { + CONST_VTBL struct IEnumOleUndoUnitsVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IEnumOleUndoUnits_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IEnumOleUndoUnits_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IEnumOleUndoUnits_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IEnumOleUndoUnits_Next(This,cElt,rgElt,pcEltFetched) \ + (This)->lpVtbl -> Next(This,cElt,rgElt,pcEltFetched) + +#define IEnumOleUndoUnits_Skip(This,cElt) \ + (This)->lpVtbl -> Skip(This,cElt) + +#define IEnumOleUndoUnits_Reset(This) \ + (This)->lpVtbl -> Reset(This) + +#define IEnumOleUndoUnits_Clone(This,ppEnum) \ + (This)->lpVtbl -> Clone(This,ppEnum) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_RemoteNext_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + +void __RPC_STUB IEnumOleUndoUnits_RemoteNext_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Skip_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt); + + +void __RPC_STUB IEnumOleUndoUnits_Skip_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Reset_Proxy( + IEnumOleUndoUnits __RPC_FAR * This); + + +void __RPC_STUB IEnumOleUndoUnits_Reset_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Clone_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IEnumOleUndoUnits_Clone_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IEnumOleUndoUnits_INTERFACE_DEFINED__ */ + + +#ifndef __IOleUndoManager_INTERFACE_DEFINED__ +#define __IOleUndoManager_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IOleUndoManager + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +#define SID_SOleUndoManager IID_IOleUndoManager; +typedef IOleUndoManager __RPC_FAR *LPOLEUNDOMANAGER; + + +EXTERN_C const IID IID_IOleUndoManager; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IOleUndoManager : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE Open( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit) = 0; + + virtual HRESULT STDMETHODCALLTYPE Add( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOpenParentState( + /* [out] */ DWORD __RPC_FAR *pdwState) = 0; + + virtual HRESULT STDMETHODCALLTYPE DiscardFrom( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE UndoTo( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE RedoTo( + /* [in] */ IOleUndoUnit __RPC_FAR *pUU) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumUndoable( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE EnumRedoable( + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLastUndoDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetLastRedoDescription( + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE Enable( + /* [in] */ BOOL fEnable) = 0; + + }; + +#else /* C style interface */ + + typedef struct IOleUndoManagerVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IOleUndoManager __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IOleUndoManager __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Open )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Add )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOpenParentState )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *DiscardFrom )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UndoTo )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *RedoTo )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumUndoable )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *EnumRedoable )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLastUndoDescription )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetLastRedoDescription )( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Enable )( + IOleUndoManager __RPC_FAR * This, + /* [in] */ BOOL fEnable); + + END_INTERFACE + } IOleUndoManagerVtbl; + + interface IOleUndoManager + { + CONST_VTBL struct IOleUndoManagerVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IOleUndoManager_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IOleUndoManager_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IOleUndoManager_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IOleUndoManager_Open(This,pPUU) \ + (This)->lpVtbl -> Open(This,pPUU) + +#define IOleUndoManager_Close(This,pPUU,fCommit) \ + (This)->lpVtbl -> Close(This,pPUU,fCommit) + +#define IOleUndoManager_Add(This,pUU) \ + (This)->lpVtbl -> Add(This,pUU) + +#define IOleUndoManager_GetOpenParentState(This,pdwState) \ + (This)->lpVtbl -> GetOpenParentState(This,pdwState) + +#define IOleUndoManager_DiscardFrom(This,pUU) \ + (This)->lpVtbl -> DiscardFrom(This,pUU) + +#define IOleUndoManager_UndoTo(This,pUU) \ + (This)->lpVtbl -> UndoTo(This,pUU) + +#define IOleUndoManager_RedoTo(This,pUU) \ + (This)->lpVtbl -> RedoTo(This,pUU) + +#define IOleUndoManager_EnumUndoable(This,ppEnum) \ + (This)->lpVtbl -> EnumUndoable(This,ppEnum) + +#define IOleUndoManager_EnumRedoable(This,ppEnum) \ + (This)->lpVtbl -> EnumRedoable(This,ppEnum) + +#define IOleUndoManager_GetLastUndoDescription(This,pBstr) \ + (This)->lpVtbl -> GetLastUndoDescription(This,pBstr) + +#define IOleUndoManager_GetLastRedoDescription(This,pBstr) \ + (This)->lpVtbl -> GetLastRedoDescription(This,pBstr) + +#define IOleUndoManager_Enable(This,fEnable) \ + (This)->lpVtbl -> Enable(This,fEnable) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Open_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU); + + +void __RPC_STUB IOleUndoManager_Open_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Close_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleParentUndoUnit __RPC_FAR *pPUU, + /* [in] */ BOOL fCommit); + + +void __RPC_STUB IOleUndoManager_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Add_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_Add_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetOpenParentState_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwState); + + +void __RPC_STUB IOleUndoManager_GetOpenParentState_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_DiscardFrom_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_DiscardFrom_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_UndoTo_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_UndoTo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_RedoTo_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ IOleUndoUnit __RPC_FAR *pUU); + + +void __RPC_STUB IOleUndoManager_RedoTo_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_EnumUndoable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IOleUndoManager_EnumUndoable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_EnumRedoable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ IEnumOleUndoUnits __RPC_FAR *__RPC_FAR *ppEnum); + + +void __RPC_STUB IOleUndoManager_EnumRedoable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetLastUndoDescription_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoManager_GetLastUndoDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_GetLastRedoDescription_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IOleUndoManager_GetLastRedoDescription_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IOleUndoManager_Enable_Proxy( + IOleUndoManager __RPC_FAR * This, + /* [in] */ BOOL fEnable); + + +void __RPC_STUB IOleUndoManager_Enable_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IOleUndoManager_INTERFACE_DEFINED__ */ + + +#ifndef __IQuickActivate_INTERFACE_DEFINED__ +#define __IQuickActivate_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IQuickActivate + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object][local] */ + + +typedef IQuickActivate __RPC_FAR *LPQUICKACTIVATE; + +typedef /* [v1_enum] */ +enum tagQACONTAINERFLAGS + { QACONTAINER_SHOWHATCHING = 0x1, + QACONTAINER_SHOWGRABHANDLES = 0x2, + QACONTAINER_USERMODE = 0x4, + QACONTAINER_DISPLAYASDEFAULT = 0x8, + QACONTAINER_UIDEAD = 0x10, + QACONTAINER_AUTOCLIP = 0x20, + QACONTAINER_MESSAGEREFLECT = 0x40, + QACONTAINER_SUPPORTSMNEMONICS = 0x80 + } QACONTAINERFLAGS; + +typedef DWORD OLE_COLOR; + +typedef struct tagQACONTAINER + { + ULONG cbSize; + IOleClientSite __RPC_FAR *pClientSite; + IAdviseSinkEx __RPC_FAR *pAdviseSink; + IPropertyNotifySink __RPC_FAR *pPropertyNotifySink; + IUnknown __RPC_FAR *pUnkEventSink; + DWORD dwAmbientFlags; + OLE_COLOR colorFore; + OLE_COLOR colorBack; + IFont __RPC_FAR *pFont; + IOleUndoManager __RPC_FAR *pUndoMgr; + DWORD dwAppearance; + LONG lcid; + HPALETTE hpal; + struct IBindHost __RPC_FAR *pBindHost; + } QACONTAINER; + +typedef struct tagQACONTROL + { + ULONG cbSize; + DWORD dwMiscStatus; + DWORD dwViewStatus; + DWORD dwEventCookie; + DWORD dwPropNotifyCookie; + DWORD dwPointerActivationPolicy; + } QACONTROL; + + +EXTERN_C const IID IID_IQuickActivate; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IQuickActivate : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE QuickActivate( + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetContentExtent( + LPSIZEL pSizel) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetContentExtent( + LPSIZEL pSizel) = 0; + + }; + +#else /* C style interface */ + + typedef struct IQuickActivateVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IQuickActivate __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IQuickActivate __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IQuickActivate __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QuickActivate )( + IQuickActivate __RPC_FAR * This, + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetContentExtent )( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetContentExtent )( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + END_INTERFACE + } IQuickActivateVtbl; + + interface IQuickActivate + { + CONST_VTBL struct IQuickActivateVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IQuickActivate_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IQuickActivate_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IQuickActivate_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IQuickActivate_QuickActivate(This,pQaContainer,pQaControl) \ + (This)->lpVtbl -> QuickActivate(This,pQaContainer,pQaControl) + +#define IQuickActivate_SetContentExtent(This,pSizel) \ + (This)->lpVtbl -> SetContentExtent(This,pSizel) + +#define IQuickActivate_GetContentExtent(This,pSizel) \ + (This)->lpVtbl -> GetContentExtent(This,pSizel) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IQuickActivate_QuickActivate_Proxy( + IQuickActivate __RPC_FAR * This, + /* [in] */ QACONTAINER __RPC_FAR *pQaContainer, + /* [out] */ QACONTROL __RPC_FAR *pQaControl); + + +void __RPC_STUB IQuickActivate_QuickActivate_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IQuickActivate_SetContentExtent_Proxy( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + +void __RPC_STUB IQuickActivate_SetContentExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IQuickActivate_GetContentExtent_Proxy( + IQuickActivate __RPC_FAR * This, + LPSIZEL pSizel); + + +void __RPC_STUB IQuickActivate_GetContentExtent_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IQuickActivate_INTERFACE_DEFINED__ */ + + +#ifndef __IPointerInactive_INTERFACE_DEFINED__ +#define __IPointerInactive_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPointerInactive + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [uuid][unique][object] */ + + +typedef IPointerInactive __RPC_FAR *LPPOINTERINACTIVE; + +typedef /* [v1_enum] */ +enum tagPOINTERINACTIVE + { POINTERINACTIVE_ACTIVATEONENTRY = 1, + POINTERINACTIVE_DEACTIVATEONLEAVE = 2, + POINTERINACTIVE_ACTIVATEONDRAG = 4 + } POINTERINACTIVE; + + +EXTERN_C const IID IID_IPointerInactive; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPointerInactive : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetActivationPolicy( + /* [out] */ DWORD __RPC_FAR *pdwPolicy) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInactiveMouseMove( + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnInactiveSetCursor( + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPointerInactiveVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPointerInactive __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPointerInactive __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPointerInactive __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetActivationPolicy )( + IPointerInactive __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwPolicy); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInactiveMouseMove )( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnInactiveSetCursor )( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways); + + END_INTERFACE + } IPointerInactiveVtbl; + + interface IPointerInactive + { + CONST_VTBL struct IPointerInactiveVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPointerInactive_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPointerInactive_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPointerInactive_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPointerInactive_GetActivationPolicy(This,pdwPolicy) \ + (This)->lpVtbl -> GetActivationPolicy(This,pdwPolicy) + +#define IPointerInactive_OnInactiveMouseMove(This,pRectBounds,x,y,grfKeyState) \ + (This)->lpVtbl -> OnInactiveMouseMove(This,pRectBounds,x,y,grfKeyState) + +#define IPointerInactive_OnInactiveSetCursor(This,pRectBounds,x,y,dwMouseMsg,fSetAlways) \ + (This)->lpVtbl -> OnInactiveSetCursor(This,pRectBounds,x,y,dwMouseMsg,fSetAlways) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPointerInactive_GetActivationPolicy_Proxy( + IPointerInactive __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwPolicy); + + +void __RPC_STUB IPointerInactive_GetActivationPolicy_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPointerInactive_OnInactiveMouseMove_Proxy( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD grfKeyState); + + +void __RPC_STUB IPointerInactive_OnInactiveMouseMove_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPointerInactive_OnInactiveSetCursor_Proxy( + IPointerInactive __RPC_FAR * This, + /* [in] */ LPCRECT pRectBounds, + /* [in] */ LONG x, + /* [in] */ LONG y, + /* [in] */ DWORD dwMouseMsg, + /* [in] */ BOOL fSetAlways); + + +void __RPC_STUB IPointerInactive_OnInactiveSetCursor_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPointerInactive_INTERFACE_DEFINED__ */ + + +#ifndef __IObjectWithSite_INTERFACE_DEFINED__ +#define __IObjectWithSite_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IObjectWithSite + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IObjectWithSite __RPC_FAR *LPOBJECTWITHSITE; + + +EXTERN_C const IID IID_IObjectWithSite; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IObjectWithSite : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetSite( + /* [in] */ IUnknown __RPC_FAR *pUnkSite) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetSite( + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite) = 0; + + }; + +#else /* C style interface */ + + typedef struct IObjectWithSiteVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IObjectWithSite __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IObjectWithSite __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetSite )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSite); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSite )( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite); + + END_INTERFACE + } IObjectWithSiteVtbl; + + interface IObjectWithSite + { + CONST_VTBL struct IObjectWithSiteVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IObjectWithSite_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IObjectWithSite_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IObjectWithSite_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IObjectWithSite_SetSite(This,pUnkSite) \ + (This)->lpVtbl -> SetSite(This,pUnkSite) + +#define IObjectWithSite_GetSite(This,riid,ppvSite) \ + (This)->lpVtbl -> GetSite(This,riid,ppvSite) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IObjectWithSite_SetSite_Proxy( + IObjectWithSite __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkSite); + + +void __RPC_STUB IObjectWithSite_SetSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IObjectWithSite_GetSite_Proxy( + IObjectWithSite __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvSite); + + +void __RPC_STUB IObjectWithSite_GetSite_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IObjectWithSite_INTERFACE_DEFINED__ */ + + +#ifndef __IErrorLog_INTERFACE_DEFINED__ +#define __IErrorLog_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IErrorLog + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IErrorLog __RPC_FAR *LPERRORLOG; + + +EXTERN_C const IID IID_IErrorLog; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IErrorLog : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AddError( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo) = 0; + + }; + +#else /* C style interface */ + + typedef struct IErrorLogVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IErrorLog __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IErrorLog __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IErrorLog __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *AddError )( + IErrorLog __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo); + + END_INTERFACE + } IErrorLogVtbl; + + interface IErrorLog + { + CONST_VTBL struct IErrorLogVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IErrorLog_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IErrorLog_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IErrorLog_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IErrorLog_AddError(This,pszPropName,pExcepInfo) \ + (This)->lpVtbl -> AddError(This,pszPropName,pExcepInfo) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IErrorLog_AddError_Proxy( + IErrorLog __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO __RPC_FAR *pExcepInfo); + + +void __RPC_STUB IErrorLog_AddError_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IErrorLog_INTERFACE_DEFINED__ */ + + +#ifndef __IPropertyBag_INTERFACE_DEFINED__ +#define __IPropertyBag_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPropertyBag + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPropertyBag __RPC_FAR *LPPROPERTYBAG; + + +EXTERN_C const IID IID_IPropertyBag; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPropertyBag : public IUnknown + { + public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read( + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog) = 0; + + virtual HRESULT STDMETHODCALLTYPE Write( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPropertyBagVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPropertyBag __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPropertyBag __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPropertyBag __RPC_FAR * This); + + /* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Read )( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Write )( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar); + + END_INTERFACE + } IPropertyBagVtbl; + + interface IPropertyBag + { + CONST_VTBL struct IPropertyBagVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPropertyBag_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPropertyBag_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPropertyBag_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPropertyBag_Read(This,pszPropName,pVar,pErrorLog) \ + (This)->lpVtbl -> Read(This,pszPropName,pVar,pErrorLog) + +#define IPropertyBag_Write(This,pszPropName,pVar) \ + (This)->lpVtbl -> Write(This,pszPropName,pVar) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPropertyBag_RemoteRead_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog, + /* [in] */ DWORD varType, + /* [in] */ IUnknown __RPC_FAR *pUnkObj); + + +void __RPC_STUB IPropertyBag_RemoteRead_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPropertyBag_Write_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT __RPC_FAR *pVar); + + +void __RPC_STUB IPropertyBag_Write_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPropertyBag_INTERFACE_DEFINED__ */ + + +#ifndef __IPerPropertyBrowsing_INTERFACE_DEFINED__ +#define __IPerPropertyBrowsing_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IPerPropertyBrowsing + * at Sat Jul 13 21:56:58 1996 + * using MIDL 3.00.39 + ****************************************/ +/* [unique][uuid][object] */ + + +typedef IPerPropertyBrowsing __RPC_FAR *LPPERPROPERTYBROWSING; + +typedef struct tagCALPOLESTR + { + ULONG cElems; + /* [size_is] */ LPOLESTR __RPC_FAR *pElems; + } CALPOLESTR; + +typedef struct tagCALPOLESTR __RPC_FAR *LPCALPOLESTR; + +typedef struct tagCADWORD + { + ULONG cElems; + /* [size_is] */ DWORD __RPC_FAR *pElems; + } CADWORD; + +typedef struct tagCADWORD __RPC_FAR *LPCADWORD; + + +EXTERN_C const IID IID_IPerPropertyBrowsing; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface IPerPropertyBrowsing : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetDisplayString( + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr) = 0; + + virtual HRESULT STDMETHODCALLTYPE MapPropertyToPage( + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPredefinedStrings( + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetPredefinedValue( + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut) = 0; + + }; + +#else /* C style interface */ + + typedef struct IPerPropertyBrowsingVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IPerPropertyBrowsing __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IPerPropertyBrowsing __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetDisplayString )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *MapPropertyToPage )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPredefinedStrings )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetPredefinedValue )( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut); + + END_INTERFACE + } IPerPropertyBrowsingVtbl; + + interface IPerPropertyBrowsing + { + CONST_VTBL struct IPerPropertyBrowsingVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IPerPropertyBrowsing_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IPerPropertyBrowsing_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IPerPropertyBrowsing_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IPerPropertyBrowsing_GetDisplayString(This,dispID,pBstr) \ + (This)->lpVtbl -> GetDisplayString(This,dispID,pBstr) + +#define IPerPropertyBrowsing_MapPropertyToPage(This,dispID,pClsid) \ + (This)->lpVtbl -> MapPropertyToPage(This,dispID,pClsid) + +#define IPerPropertyBrowsing_GetPredefinedStrings(This,dispID,pCaStringsOut,pCaCookiesOut) \ + (This)->lpVtbl -> GetPredefinedStrings(This,dispID,pCaStringsOut,pCaCookiesOut) + +#define IPerPropertyBrowsing_GetPredefinedValue(This,dispID,dwCookie,pVarOut) \ + (This)->lpVtbl -> GetPredefinedValue(This,dispID,dwCookie,pVarOut) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetDisplayString_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ BSTR __RPC_FAR *pBstr); + + +void __RPC_STUB IPerPropertyBrowsing_GetDisplayString_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_MapPropertyToPage_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CLSID __RPC_FAR *pClsid); + + +void __RPC_STUB IPerPropertyBrowsing_MapPropertyToPage_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetPredefinedStrings_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [out] */ CALPOLESTR __RPC_FAR *pCaStringsOut, + /* [out] */ CADWORD __RPC_FAR *pCaCookiesOut); + + +void __RPC_STUB IPerPropertyBrowsing_GetPredefinedStrings_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IPerPropertyBrowsing_GetPredefinedValue_Proxy( + IPerPropertyBrowsing __RPC_FAR * This, + /* [in] */ DISPID dispID, + /* [in] */ DWORD dwCookie, + /* [out] */ VARIANT __RPC_FAR *pVarOut); + + +void __RPC_STUB IPerPropertyBrowsing_GetPredefinedValue_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IPerPropertyBrowsing_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + + +void __RPC_USER UserHWND_from_local( HWND __RPC_FAR *, UserHWND __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHWND_to_local( UserHWND __RPC_FAR *, HWND __RPC_FAR * ); + void __RPC_USER UserHWND_free_inst( UserHWND __RPC_FAR * ); +void __RPC_USER UserHWND_free_local( HWND __RPC_FAR * ); + +void __RPC_USER UserHACCEL_from_local( HACCEL __RPC_FAR *, UserHACCEL __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHACCEL_to_local( UserHACCEL __RPC_FAR *, HACCEL __RPC_FAR * ); + void __RPC_USER UserHACCEL_free_inst( UserHACCEL __RPC_FAR * ); +void __RPC_USER UserHACCEL_free_local( HACCEL __RPC_FAR * ); + +void __RPC_USER UserHDC_from_local( HDC __RPC_FAR *, UserHDC __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHDC_to_local( UserHDC __RPC_FAR *, HDC __RPC_FAR * ); + void __RPC_USER UserHDC_free_inst( UserHDC __RPC_FAR * ); +void __RPC_USER UserHDC_free_local( HDC __RPC_FAR * ); + +void __RPC_USER UserHFONT_from_local( HFONT __RPC_FAR *, UserHFONT __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserHFONT_to_local( UserHFONT __RPC_FAR *, HFONT __RPC_FAR * ); + void __RPC_USER UserHFONT_free_inst( UserHFONT __RPC_FAR * ); +void __RPC_USER UserHFONT_free_local( HFONT __RPC_FAR * ); + +void __RPC_USER UserMSG_from_local( MSG __RPC_FAR *, UserMSG __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserMSG_to_local( UserMSG __RPC_FAR *, MSG __RPC_FAR * ); + void __RPC_USER UserMSG_free_inst( UserMSG __RPC_FAR * ); +void __RPC_USER UserMSG_free_local( MSG __RPC_FAR * ); + +void __RPC_USER UserBSTR_from_local( BSTR __RPC_FAR *, UserBSTR __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserBSTR_to_local( UserBSTR __RPC_FAR *, BSTR __RPC_FAR * ); + void __RPC_USER UserBSTR_free_inst( UserBSTR __RPC_FAR * ); +void __RPC_USER UserBSTR_free_local( BSTR __RPC_FAR * ); + +void __RPC_USER UserVARIANT_from_local( VARIANT __RPC_FAR *, UserVARIANT __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserVARIANT_to_local( UserVARIANT __RPC_FAR *, VARIANT __RPC_FAR * ); + void __RPC_USER UserVARIANT_free_inst( UserVARIANT __RPC_FAR * ); +void __RPC_USER UserVARIANT_free_local( VARIANT __RPC_FAR * ); + +void __RPC_USER UserEXCEPINFO_from_local( EXCEPINFO __RPC_FAR *, UserEXCEPINFO __RPC_FAR * __RPC_FAR * ); +void __RPC_USER UserEXCEPINFO_to_local( UserEXCEPINFO __RPC_FAR *, EXCEPINFO __RPC_FAR * ); + void __RPC_USER UserEXCEPINFO_free_inst( UserEXCEPINFO __RPC_FAR * ); +void __RPC_USER UserEXCEPINFO_free_local( EXCEPINFO __RPC_FAR * ); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumConnections_Next_Proxy( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnections_Next_Stub( + IEnumConnections __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTDATA rgcd, + /* [out] */ ULONG __RPC_FAR *pcFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Next_Proxy( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumConnectionPoints_Next_Stub( + IEnumConnectionPoints __RPC_FAR * This, + /* [in] */ ULONG cConnections, + /* [length_is][size_is][out] */ LPCONNECTIONPOINT __RPC_FAR *ppCP, + /* [out] */ ULONG __RPC_FAR *pcFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IClassFactory2_CreateInstanceLic_Proxy( + IClassFactory2 __RPC_FAR * This, + /* [in] */ IUnknown __RPC_FAR *pUnkOuter, + /* [in] */ IUnknown __RPC_FAR *pUnkReserved, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ PVOID __RPC_FAR *ppvObj); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IClassFactory2_CreateInstanceLic_Stub( + IClassFactory2 __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [in] */ BSTR bstrKey, + /* [iid_is][out] */ IUnknown __RPC_FAR *__RPC_FAR *ppvObj); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Load_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ ULONG cbSize); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Load_Stub( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ ULONG cbSize); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Save_Proxy( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ LPVOID pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPersistMemory_Save_Stub( + IPersistMemory __RPC_FAR * This, + /* [size_is][in] */ BYTE __RPC_FAR *pMem, + /* [in] */ BOOL fClearDirty, + /* [in] */ ULONG cbSize); + +/* [local] */ void STDMETHODCALLTYPE IAdviseSinkEx_OnViewStatusChange_Proxy( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + + +/* [async][call_as] */ void STDMETHODCALLTYPE IAdviseSinkEx_OnViewStatusChange_Stub( + IAdviseSinkEx __RPC_FAR * This, + /* [in] */ DWORD dwViewStatus); + +/* [local] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Next_Proxy( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IEnumOleUndoUnits_Next_Stub( + IEnumOleUndoUnits __RPC_FAR * This, + /* [in] */ ULONG cElt, + /* [length_is][size_is][out] */ IOleUndoUnit __RPC_FAR *__RPC_FAR *rgElt, + /* [out] */ ULONG __RPC_FAR *pcEltFetched); + +/* [local] */ HRESULT STDMETHODCALLTYPE IPropertyBag_Read_Proxy( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog); + + +/* [call_as] */ HRESULT STDMETHODCALLTYPE IPropertyBag_Read_Stub( + IPropertyBag __RPC_FAR * This, + /* [in] */ LPCOLESTR pszPropName, + /* [out] */ VARIANT __RPC_FAR *pVar, + /* [in] */ IErrorLog __RPC_FAR *pErrorLog, + /* [in] */ DWORD varType, + /* [in] */ IUnknown __RPC_FAR *pUnkObj); + + + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/code/redalert/odata.cpp b/code/redalert/odata.cpp new file mode 100644 index 0000000..7c10d41 --- /dev/null +++ b/code/redalert/odata.cpp @@ -0,0 +1,951 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ODATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ODATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1994 * + * * + * Last Update : August 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static OverlayTypeClass const Sandbag( + OVERLAY_SANDBAG_WALL, // Overlay type number. + "SBAG", // INI name of overlay. + TXT_SANDBAG_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 20, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Cyclone( + OVERLAY_CYCLONE_WALL, // Overlay type number. + "CYCL", // INI name of overlay. + TXT_CYCLONE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Brick( + OVERLAY_BRICK_WALL, // Overlay type number. + "BRIK", // INI name of overlay. + TXT_BRICK_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 3, // If this is a wall, how many damage levels? + 70, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + true, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Barbwire( + OVERLAY_BARBWIRE_WALL, // Overlay type number. + "BARB", // INI name of overlay. + TXT_BARBWIRE_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Wood( + OVERLAY_WOOD_WALL, // Overlay type number. + "WOOD", // INI name of overlay. + TXT_WOOD_WALL, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 1, // If this is a wall, how many damage levels? + 2, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + true, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Fence( + OVERLAY_FENCE, // Overlay type number. + "FENC", // INI name of overlay. + TXT_FENCE, // Full name of overlay. + LAND_WALL, // What kind of ground is it? + 2, // If this is a wall, how many damage levels? + 10, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + true, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + true, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gold1( + OVERLAY_GOLD1, // Overlay type number. + "GOLD01", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold2( + OVERLAY_GOLD2, // Overlay type number. + "GOLD02", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold3( + OVERLAY_GOLD3, // Overlay type number. + "GOLD03", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gold4( + OVERLAY_GOLD4, // Overlay type number. + "GOLD04", // INI name of overlay. + TXT_GOLD, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); + +static OverlayTypeClass const Gems1( + OVERLAY_GEMS1, // Overlay type number. + "GEM01", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems2( + OVERLAY_GEMS2, // Overlay type number. + "GEM02", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems3( + OVERLAY_GEMS3, // Overlay type number. + "GEM03", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const Gems4( + OVERLAY_GEMS4, // Overlay type number. + "GEM04", // INI name of overlay. + TXT_GEMS, // Full name of overlay. + LAND_TIBERIUM, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + true, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V12( + OVERLAY_V12, // Overlay type number. + "V12", // INI name of overlay. + TXT_CIV12, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V13( + OVERLAY_V13, // Overlay type number. + "V13", // INI name of overlay. + TXT_CIV13, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V14( + OVERLAY_V14, // Overlay type number. + "V14", // INI name of overlay. + TXT_CIV14, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V15( + OVERLAY_V15, // Overlay type number. + "V15", // INI name of overlay. + TXT_CIV15, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V16( + OVERLAY_V16, // Overlay type number. + "V16", // INI name of overlay. + TXT_CIV16, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V17( + OVERLAY_V17, // Overlay type number. + "V17", // INI name of overlay. + TXT_CIV17, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const V18( + OVERLAY_V18, // Overlay type number. + "V18", // INI name of overlay. + TXT_CIV18, // Full name of overlay. + LAND_ROCK, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + true, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + true, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const FlagSpot( + OVERLAY_FLAG_SPOT, // Overlay type number. + "FPLS", // INI name of overlay. + TXT_FLAG_SPOT, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + true, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + false // Is this a crate? +); +static OverlayTypeClass const WoodCrate( + OVERLAY_WOOD_CRATE, // Overlay type number. + "WCRATE", // INI name of overlay. + TXT_WOOD_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const WaterCrate( + OVERLAY_WATER_CRATE, // Overlay type number. + "WWCRATE", // INI name of overlay. + TXT_WATER_CRATE, // Full name of overlay. + LAND_WATER, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); +static OverlayTypeClass const SteelCrate( + OVERLAY_STEEL_CRATE, // Overlay type number. + "SCRATE", // INI name of overlay. + TXT_STEEL_CRATE, // Full name of overlay. + LAND_CLEAR, // What kind of ground is it? + 0, // If this is a wall, how many damage levels? + 0, // If this is a wall, how many damage points can it take per level? + false, // Visible on the radar map? + false, // Is it a wooden overlay (affected by fire)? + false, // Targetable as a destroyable overlay? + false, // Crushable by tracked vehicle? + false, // Is this harvestable Tiberium? + false, // Stops low level bullets in flight? + false, // Theater specific art? + false, // Is this a wall type? + true // Is this a crate? +); + + +/*********************************************************************************************** + * OverlayTypeClass::OverlayTypeClass -- Constructor for overlay type objects. * + * * + * This is the constructor for the overlay types. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1994 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass::OverlayTypeClass( + OverlayType iconset, + char const * ininame, + int fullname, + LandType ground, + int damagelevels, + int damagepoints, + bool isradarvisible, + bool iswooden, + bool istarget, + bool iscrushable, + bool istiberium, + bool high, + bool theater, + bool walltype, + bool iscrate) : + ObjectTypeClass(RTTI_OVERLAYTYPE, + int(iconset), + false, + true, + false, + istarget, + true, + false, + false, + fullname, + ininame), + Type(iconset), + Land(ground), + DamageLevels(damagelevels), + DamagePoints(damagepoints), + IsTheater(theater), + IsWall(walltype), + IsHigh(high), + IsTiberium(istiberium), + IsWooden(iswooden), + IsCrate(iscrate), + IsRadarVisible(isradarvisible) +{ + IsCrushable = iscrushable; +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator new -- Allocate an overlay type class object from pool. * + * * + * This will allocate an overlay type class object from the special memory pool that is * + * for that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlay type class object allocated. If there is * + * insufficient memory to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * OverlayTypeClass::operator new(size_t) +{ + return(OverlayTypes.Alloc()); +} + + +/*********************************************************************************************** + * OverlayTypeClass::operator delete -- Returns an overlay type object back to the pool. * + * * + * This will return a previously allcoated overaly type object to the special memory * + * pool that it was allocated from. * + * * + * INPUT: pointer -- Pointer to the overlay type class object to return the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::operator delete(void * pointer) +{ + OverlayTypes.Free((OverlayTypeClass *)pointer); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init_Heap -- Initialize the overlay type class heap. * + * * + * This will initialize the overlay type heap by pre-allocated all overlay types known * + * to exist. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: It should be called once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init_Heap(void) +{ + /* + ** These overlay type class objects must be allocated in the exact order that they + ** are specified in the OverlayType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new OverlayTypeClass(Sandbag); // OVERLAY_SANDBAG_WALL + new OverlayTypeClass(Cyclone); // OVERLAY_CYCLONE_WALL + new OverlayTypeClass(Brick); // OVERLAY_BRICK_WALL + new OverlayTypeClass(Barbwire); // OVERLAY_BARBWIRE_WALL + new OverlayTypeClass(Wood); // OVERLAY_WOOD_WALL + new OverlayTypeClass(Gold1); // OVERLAY_GOLD1 + new OverlayTypeClass(Gold2); // OVERLAY_GOLD2 + new OverlayTypeClass(Gold3); // OVERLAY_GOLD3 + new OverlayTypeClass(Gold4); // OVERLAY_GOLD4 + new OverlayTypeClass(Gems1); // OVERLAY_GEMS1 + new OverlayTypeClass(Gems2); // OVERLAY_GEMS2 + new OverlayTypeClass(Gems3); // OVERLAY_GEMS3 + new OverlayTypeClass(Gems4); // OVERLAY_GEMS4 + new OverlayTypeClass(V12); // OVERLAY_V12 + new OverlayTypeClass(V13); // OVERLAY_V13 + new OverlayTypeClass(V14); // OVERLAY_V14 + new OverlayTypeClass(V15); // OVERLAY_V15 + new OverlayTypeClass(V16); // OVERLAY_V16 + new OverlayTypeClass(V17); // OVERLAY_V17 + new OverlayTypeClass(V18); // OVERLAY_V18 + new OverlayTypeClass(FlagSpot); // OVERLAY_FLAG_SPOT + new OverlayTypeClass(WoodCrate); // OVERLAY_WOOD_CRATE + new OverlayTypeClass(SteelCrate); // OVERLAY_STEEL_CRATE + new OverlayTypeClass(Fence); // OVERLAY_FENCE + new OverlayTypeClass(WaterCrate); // OVERLAY_WATER_CRATE +} + + +/*********************************************************************************************** + * OverlayTypeClass::One_Time -- Loads all the necessary general overlay shape data. * + * * + * This routine should be called once when the game first starts. It will establish * + * pointers to the graphic data of the overlay objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * OverlayTypeClass::From_Name -- Determine overlay from ASCII name. * + * * + * This routine is used to determine the overlay number given only * + * an ASCII representation. The scenario loader uses this routine * + * to construct the map from the INI control file. * + * * + * INPUT: name -- Pointer to the ASCII name of the overlay. * + * * + * OUTPUT: Returns with the overlay number. If the name had no match, * + * then returns with OVERLAY_NONE. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +OverlayType OverlayTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(OVERLAY_NONE); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Occupy_List -- Determines occupation list. * + * * + * This routine is used to examine the overlay map and build an * + * occupation list. This list is used to render a overlay cursor as * + * well as placement of icon numbers. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns with a pointer to the overlay occupation list. * + * * + * WARNINGS: The return pointer is valid only until the next time that * + * this routine is called. * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +short const * OverlayTypeClass::Occupy_List(bool) const +{ + static short _simple[] = {0, REFRESH_EOL}; + + return(_simple); +} + + +/*************************************************************************** + * OverlayTypeClass::Radar_Icon -- Gets a pointer to the radar icons * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +unsigned char * OverlayTypeClass::Radar_Icon(int data) const +{ + unsigned char * icon = (unsigned char *)Get_Radar_Data(); // Get pointer to radar icons + if (icon != NULL) icon += (data * 9) + 2; // move icon ptr to correct icon + return(icon); // Return the correct icon +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * OverlayTypeClass::Display -- Displays a generic representation of overlay. * + * * + * This routine is used to display a generic view of the overlay * + * object. This is necessary for selection in the scenario editor. * + * * + * INPUT: x,y -- The coordinates to center the display about. * + * * + * window-- The window to base the coordinates upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + if (Get_Image_Data() != NULL) { + int frame = 0; + + if (IsTiberium) { + frame = 7; + } + if (Type == OVERLAY_GEMS1 || Type == OVERLAY_GEMS2 || Type == OVERLAY_GEMS3 || Type == OVERLAY_GEMS4) { + frame = 2; + } + + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), frame, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::Prep_For_Add -- Prepares to add overlay to scenario. * + * * + * This routine prepares a list of overlay objects so that the * + * scenario editor can use this list to display a dialog box. The * + * selection of a overlay object will allow its placement upon the * + * map. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1994 JLB : Created * + *=============================================================================================*/ +void OverlayTypeClass::Prep_For_Add(void) +{ + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass const & overlay = As_Reference(index); + if (overlay.Get_Image_Data() != NULL && + !overlay.IsWall && + (!overlay.IsTiberium || index == OVERLAY_GOLD1 || index == OVERLAY_GEMS1)) { + + Map.Add_To_List(&overlay); + } + } +} +#endif + + +/*********************************************************************************************** + * OverlayTypeClass::Create_And_Place -- Creates and places a overlay object on the map. * + * * + * This support routine is used by the scenario editor to add a overlay object to the map * + * and to the game. * + * * + * INPUT: cell -- The cell to place the overlay object. * + * * + * OUTPUT: bool; Was the overlay object placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool OverlayTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new OverlayClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Create_One_Of -- Creates an object of this overlay type. * + * * + * This routine will create an object of this type. For certain overlay objects, such * + * as walls, it is actually created as a building. The "building" wall is converted into * + * a overlay at the moment of placing down on the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the appropriate object for this overlay type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * OverlayTypeClass::Create_One_Of(HouseClass *) const +{ + return(new OverlayClass(Type, -1)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Draw_It -- Draws the overlay image at location specified. * + * * + * This routine will draw the overlay shape at the coordinates specified. It is presumed * + * that all the underlying layers have already been rendered by the time this routine is * + * called. * + * * + * INPUT: x, y -- Coordinate (upper left) of cell where overlay image is to be drawn. * + * * + * data -- Cell specific data that controls the imagery of the overlay. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Draw_It(int x, int y, int data) const +{ + IsTheaterShape = IsTheater; + CC_Draw_Shape(Get_Image_Data(), data, Map.TacPixelX+x+(CELL_PIXEL_W>>1), Map.TacPixelY+y+(CELL_PIXEL_H>>1), WINDOW_MAIN, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, NULL, Map.UnitShadow); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * OverlayTypeClass::Init -- Initialize the overlay graphic data per theater. * + * * + * This routine will update the overlay graphic data according to the theater specified. * + * It is typically called when the scenario is first loaded (theater change). * + * * + * INPUT: theater -- The theater to load specific data for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + + for (OverlayType index = OVERLAY_FIRST; index < OVERLAY_COUNT; index++) { + OverlayTypeClass & overlay = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed iconset name. + + if (overlay.IsTheater) { + _makepath(fullname, NULL, NULL, overlay.IniName, Theaters[theater].Suffix); + } else { + _makepath(fullname, NULL, NULL, overlay.IniName, ".SHP"); + } + overlay.ImageData = MFCD::Retrieve(fullname); + + IsTheaterShape = overlay.IsTheater; //Tell Build_Frame if this is a theater specific shape + if (overlay.RadarIcon != NULL) delete[] (char *)overlay.RadarIcon; + overlay.RadarIcon = Get_Radar_Icon(overlay.Get_Image_Data(), 0, -1, 3); + IsTheaterShape = false; + } + } +} + + +/*********************************************************************************************** + * OverlayTypeClass::As_Reference -- Fetch a reference to the overlay type specified. * + * * + * Use this routine to get a reference that corresponds to the overlay type. * + * * + * INPUT: type -- The overlay type to fetch a reference to. * + * * + * OUTPUT: Returns with a reference to the overlay type specified. * + * * + * WARNINGS: Be sure that the overlay type specified is legal. Illegal type value will * + * result in undefined behavior. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +OverlayTypeClass & OverlayTypeClass::As_Reference(OverlayType type) +{ + return(*OverlayTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * OverlayTypeClass::Coord_Fixup -- Adjust the coord to be legal for assignment. * + * * + * This will adjust the coordinate specified so that it will be of legal format to * + * assign as the coordinate of an overlay. Overlays are always relative to the upper left * + * corner of the cell, so this routine drops the fractional cell components. * + * * + * INPUT: coord -- The coordinate to fixup to be legal for assignment. * + * * + * OUTPUT: Returns with a properly fixed up coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/14/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE OverlayTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/code/redalert/options.cpp b/code/redalert/options.cpp new file mode 100644 index 0000000..cff3732 --- /dev/null +++ b/code/redalert/options.cpp @@ -0,0 +1,920 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OPTIONS.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * OptionsClass::Get_Game_Speed -- Fetches the current game speed setting. * + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * OptionsClass::Get_Scroll_Rate -- Fetches the current scroll rate setting. * + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * OptionsClass::One_Time -- This performs any one time initialization for the options class.* + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * OptionsClass::Process -- Handles all the options graphic interface. * + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * OptionsClass::Set -- Sets options based on current settings * + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * OptionsClass::Set_Game_Speed -- Sets the game speed as specified. * + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * OptionsClass::Set_Scroll_Rate -- Sets the scroll rate as specified. * + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * OptionsClass::Set_Tint -- Sets the tint setting. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "options.h" + + +#ifdef WIN32 +char const * const OptionsClass::HotkeyName = "WinHotkeys"; +#else +char const * const OptionsClass::HotkeyName = "DOSHotkeys"; +#endif + + +/*********************************************************************************************** + * OptionsClass::OptionsClass -- The default constructor for the options class. * + * * + * This is the constructor for the options class. It handles setting up all the globals * + * necessary for the options. This includes setting them to their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +OptionsClass::OptionsClass(void) : + GameSpeed(3), + ScrollRate(3), +#ifdef WIN32 + Volume(".40"), // was .295 + ScoreVolume(".25"), +#ifdef FIXIT_VERSION_3 + MultiScoreVolume("0"), +#endif +#else + Volume(".8"), + ScoreVolume(".6"), +#endif + Brightness(fixed::_1_2), + Tint(fixed::_1_2), + Saturation(fixed::_1_2), + Contrast(fixed::_1_2), + AutoScroll(true), + IsScoreRepeat(false), + IsScoreShuffle(false), + IsPaletteScroll(true), + + KeyForceMove1(KN_LALT), + KeyForceMove2(KN_RALT), + KeyForceAttack1(KN_LCTRL), + KeyForceAttack2(KN_RCTRL), + KeySelect1(KN_LSHIFT), + KeySelect2(KN_RSHIFT), + KeyScatter(KN_X), + KeyStop(KN_S), + KeyGuard(KN_G), + KeyNext(KN_N), + KeyPrevious(KN_B), + KeyFormation(KN_F), + KeyHome1(KN_HOME), + KeyHome2(KN_E_HOME), + KeyBase(KN_H), + KeyResign(KN_R), + KeyAlliance(KN_A), + KeyBookmark1(KN_F9), + KeyBookmark2(KN_F10), + KeyBookmark3(KN_F11), + KeyBookmark4(KN_F12), + KeySelectView(KN_E), + KeyRepair(KN_T), + KeyRepairOn(KN_NONE), + KeyRepairOff(KN_NONE), + KeySell(KN_Y), + KeySellOn(KN_NONE), + KeySellOff(KN_NONE), + KeyMap(KN_U), + KeySidebarUp(KN_UP), + KeySidebarDown(KN_DOWN), + KeyOption1(KN_ESC), + KeyOption2(KN_SPACE), + KeyScrollLeft(KN_NONE), + KeyScrollRight(KN_NONE), + KeyScrollUp(KN_NONE), + KeyScrollDown(KN_NONE), + KeyQueueMove1(KN_Q), + KeyQueueMove2(KN_Q), + KeyTeam1(KN_1), + KeyTeam2(KN_2), + KeyTeam3(KN_3), + KeyTeam4(KN_4), + KeyTeam5(KN_5), + KeyTeam6(KN_6), + KeyTeam7(KN_7), + KeyTeam8(KN_8), + KeyTeam9(KN_9), + KeyTeam10(KN_0) +{ +} + + +/*********************************************************************************************** + * OptionsClass::One_Time -- This performs any one time initialization for the options class. * + * * + * This routine should be called only once and it will perform any initializations for the * + * options class that is needed. This may include things like file loading and memory * + * allocation. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once. * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::One_Time(void) +{ + Set_Score_Vol(ScoreVolume * 256); +} + + +/*********************************************************************************************** + * OptionsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Process(void) +{ +} + + +/*********************************************************************************************** + * OptionsClass::Set_Shuffle -- Controls the play shuffle setting. * + * * + * This routine will control the score shuffle flag. The setting to use is provided as * + * a parameter. When shuffling is on, the score play order is scrambled. * + * * + * INPUT: on -- Should the shuffle option be activated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Shuffle(int on) +{ + IsScoreShuffle = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Repeat -- Controls the score repeat option. * + * * + * This routine is used to control whether scores repeat or not. The setting to use for * + * the repeat flag is provided as a parameter. * + * * + * INPUT: on -- Should the scores repeat? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Repeat(int on) +{ + IsScoreRepeat = on; +} + + +/*********************************************************************************************** + * OptionsClass::Set_Score_Volume -- Sets the global score volume to that specified. * + * * + * This routine will set the global score volume to the value specified. The value ranges * + * from zero to 255. * + * * + * INPUT: volume -- The new volume setting to use for scores. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Score_Volume(fixed volume, bool feedback) +{ + ScoreVolume = Sub_Saturate(volume, 1); + Set_Score_Vol(ScoreVolume * 256); + if (feedback && !Theme.Still_Playing()) { + Sound_Effect(VOC_BEEP, ScoreVolume); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Sound_Volume -- Sets the sound effects volume level. * + * * + * This routine will set the sound effect volume level as indicated. It can generate a * + * sound effect for feedback purposes if desired. The volume setting can range from zero * + * to 255. The value of 255 is the loudest. * + * * + * INPUT: volume -- The volume setting to use for the new value. 0 to 255. * + * * + * feedback -- Should a feedback sound effect be generated? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Sound_Volume(fixed volume, bool feedback) +{ + Volume = Sub_Saturate(volume, 1); + if (feedback) { + Sound_Effect(VOC_BEEP); + } +} + + +/*********************************************************************************************** + * OptionsClass::Set_Brightness -- Sets the brightness level to that specified. * + * * + * This routine will set the current brightness level to the value specified. This value * + * can range from zero to 255, with 128 being the normal (default) brightness level. * + * * + * INPUT: brightness -- The brightness level to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Brightness(fixed brightness) +{ + Brightness = fixed::_1_4 + (fixed::_1_2 * brightness); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Brightness -- Fetches the current brightness setting. * + * * + * This routine will fetch the current setting for the brightness level. The value ranges * + * from zero to 255, with 128 being the normal (default) value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current brightness setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Brightness(void) const +{ + return((Brightness - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Saturation -- Sets the color to the value specified. * + * * + * This routine will set the color value to that specified. The value specified can range * + * from zero to 255. The value of 128 is the normal default color setting. * + * * + * INPUT: color -- The new color value to set as current. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Saturation(fixed color) +{ + Saturation = color; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Saturation -- Fetches the current color setting. * + * * + * This routine will fetch the current color setting. This value ranges from zero to * + * 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current color setting. The value of 128 is the normal (default) * + * color setting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Saturation(void) const +{ + return(Saturation); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Contrast -- Sets the contrast to the value specified. * + * * + * This routine will set the contrast to the setting specified. This setting ranges from * + * zero to 255. The value o 128 is the normal default value. * + * * + * INPUT: contrast -- The contrast setting to make as the current setting. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Contrast(fixed contrast) +{ + Contrast = fixed::_1_4 + (fixed::_1_2 * contrast); + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Contrast -- Gets the current contrast setting. * + * * + * This routine will get the current contrast setting. The value returned is in the range * + * of zero to 255. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the current contrast setting. A setting of 128 is the normal default value.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Contrast(void) const +{ + return((Contrast - fixed::_1_4) / fixed::_1_2); +} + + +/*********************************************************************************************** + * OptionsClass::Set_Tint -- Sets the tint setting. * + * * + * This routine will change the current tint setting according to the value specified. * + * * + * INPUT: tint -- The desired tint setting. This value ranges from zero to 255. * + * * + * OUTPUT: none * + * * + * WARNINGS: The value of 128 is the default (normal) tint setting. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Set_Tint(fixed tint) +{ + Tint = tint; + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + InGamePalette.Set(); +} + + +/*********************************************************************************************** + * OptionsClass::Get_Tint -- Fetches the current tint setting. * + * * + * This fetches the current tint setting. The value is returned as a number between * + * zero and 255. This has been adjusted for the valid range allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current tint setting. Normal tint setting is 128. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +fixed OptionsClass::Get_Tint(void) const +{ + return(Tint); +} + + +/*********************************************************************************************** + * OptionsClass::Adjust_Palette -- Adjusts the palette according to the settings specified. * + * * + * This routine is used to adjust the palette according to the settings provided. It is * + * used by the options class to monkey with the palette. * + * * + * INPUT: oldpal -- Pointer to the original (unmodified) palette. * + * * + * newpal -- The new palette to create according to the settings provided. * + * * + * brightness -- The brightness level (0..255). * + * * + * color -- The color level (0..255). * + * * + * tint -- The tint (hue) level (0..255). * + * * + * contrast -- The contrast level (0..255). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/21/1994 JLB : Created. * + * 12/02/1995 JLB : Uses palette class objects. * + *=============================================================================================*/ +void OptionsClass::Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const +{ + //if (!oldpal || !newpal) return; Can't be NULL... ST - 5/9/2019 + + /* + ** Adjust for palette. + */ + for (int index = 0; index < PaletteClass::COLOR_COUNT; index++) { + if (index == CC_MOUSE_COLOR) { + newpal[index] = oldpal[index]; + } else { + /* + ** Convert the working palette entry into an HSV format for + ** manipulation. + */ + HSVClass hsv = oldpal[index]; + + /* + ** Adjust contrast by moving the value toward the center according to the + ** percentage indicated. + */ + int temp; + temp = (hsv.Value_Component() * (brightness * 256)) / 0x80; // Brightness + temp = Bound(temp, 0, 0xFF); + int v = temp; + temp = (((((int)v) - 0x80) * (contrast * 256)) / 0x80) + 0x80; // Contrast + temp = Bound(temp, 0, 0xFF); + v = temp; + temp = (hsv.Saturation_Component() * (color * 256)) / 0x80; // Color + temp = Bound(temp, 0, 0xFF); + int s = temp; + temp = (hsv.Hue_Component() * (tint * 256)) / 0x80; // Tint + temp = Bound(temp, 0, 0xFF); + int h = temp; + + /* + ** Replace the working palette entry according to the newly calculated + ** hue, saturation, and value. + */ + newpal[index] = HSVClass(h, s, v); + } + } +} + + +/*********************************************************************************************** + * OptionsClass::Load_Settings -- reads options settings from the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Reworked to use new INI handler. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Load_Settings(void) +{ + /* + ** Create filename and read the file. + */ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + ini.Load(file); + + /* + ** Read in the Options values + */ + static char const * const OPTIONS = "Options"; + GameSpeed = ini.Get_Int(OPTIONS, "GameSpeed", GameSpeed); + ScrollRate = ini.Get_Int(OPTIONS, "ScrollRate", ScrollRate); + Set_Brightness(ini.Get_Fixed(OPTIONS, "Brightness", Brightness)); + Set_Sound_Volume(ini.Get_Fixed(OPTIONS, "Volume", Volume), false); + Set_Score_Volume(ini.Get_Fixed(OPTIONS, "ScoreVolume", ScoreVolume), false); +#ifdef FIXIT_VERSION_3 + MultiScoreVolume = ini.Get_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + Set_Contrast(ini.Get_Fixed(OPTIONS, "Contrast", Contrast)); + Set_Saturation(ini.Get_Fixed(OPTIONS, "Color", Saturation)); + Set_Tint(ini.Get_Fixed(OPTIONS, "Tint", Tint)); + AutoScroll = ini.Get_Bool(OPTIONS, "AutoScroll", AutoScroll); + Set_Repeat(ini.Get_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat)); + Set_Shuffle(ini.Get_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle)); + SlowPalette = ini.Get_Bool(OPTIONS, "SlowPalette", SlowPalette); + IsPaletteScroll = ini.Get_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + KeyForceMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + KeyForceMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + KeyForceAttack1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + KeyForceAttack2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + KeySelect1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect1", KeySelect1); + KeySelect2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelect2", KeySelect2); + KeyScatter = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScatter", KeyScatter); + KeyStop = (KeyNumType)ini.Get_Int(HotkeyName, "KeyStop", KeyStop); + KeyGuard = (KeyNumType)ini.Get_Int(HotkeyName, "KeyGuard", KeyGuard); + KeyNext = (KeyNumType)ini.Get_Int(HotkeyName, "KeyNext", KeyNext); + KeyPrevious = (KeyNumType)ini.Get_Int(HotkeyName, "KeyPrevious", KeyPrevious); + KeyFormation = (KeyNumType)ini.Get_Int(HotkeyName, "KeyFormation", KeyFormation); + KeyHome1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome1", KeyHome1); + KeyHome2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyHome2", KeyHome2); + KeyBase = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBase", KeyBase); + KeyResign = (KeyNumType)ini.Get_Int(HotkeyName, "KeyResign", KeyResign); + KeyAlliance = (KeyNumType)ini.Get_Int(HotkeyName, "KeyAlliance", KeyAlliance); + KeyBookmark1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + KeyBookmark2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + KeyBookmark3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + KeyBookmark4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + KeySelectView = (KeyNumType)ini.Get_Int(HotkeyName, "KeySelectView", KeySelectView); + KeyRepair = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + KeyRepairOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + KeyRepairOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + KeySell = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellToggle", KeySell); + KeySellOn = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOn", KeySellOn); + KeySellOff = (KeyNumType)ini.Get_Int(HotkeyName, "KeySellOff", KeySellOff); + KeyMap = (KeyNumType)ini.Get_Int(HotkeyName, "KeyMapToggle", KeyMap); + KeySidebarUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + KeySidebarDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + KeyOption1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption1", KeyOption1); + KeyOption2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyOption2", KeyOption2); + KeyScrollLeft = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + KeyScrollRight = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + KeyScrollUp = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + KeyScrollDown = (KeyNumType)ini.Get_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + KeyQueueMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + KeyQueueMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + KeyTeam1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam1", KeyTeam1); + KeyTeam2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam2", KeyTeam2); + KeyTeam3 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam3", KeyTeam3); + KeyTeam4 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam4", KeyTeam4); + KeyTeam5 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam5", KeyTeam5); + KeyTeam6 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam6", KeyTeam6); + KeyTeam7 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam7", KeyTeam7); + KeyTeam8 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam8", KeyTeam8); + KeyTeam9 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam9", KeyTeam9); + KeyTeam10 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + +#ifdef WIN32 + KeyForceMove1 = (KeyNumType)(KeyForceMove1 & ~WWKEY_VK_BIT); + KeyForceMove2 = (KeyNumType)(KeyForceMove2 & ~WWKEY_VK_BIT); + KeyForceAttack1 = (KeyNumType)(KeyForceAttack1 & ~WWKEY_VK_BIT); + KeyForceAttack2 = (KeyNumType)(KeyForceAttack2 & ~WWKEY_VK_BIT); + KeySelect1 = (KeyNumType)(KeySelect1 & ~WWKEY_VK_BIT); + KeySelect2 = (KeyNumType)(KeySelect2 & ~WWKEY_VK_BIT); + KeyScatter = (KeyNumType)(KeyScatter & ~WWKEY_VK_BIT); + KeyStop = (KeyNumType)(KeyStop & ~WWKEY_VK_BIT); + KeyGuard = (KeyNumType)(KeyGuard & ~WWKEY_VK_BIT); + KeyNext = (KeyNumType)(KeyNext & ~WWKEY_VK_BIT); + KeyPrevious = (KeyNumType)(KeyPrevious & ~WWKEY_VK_BIT); + KeyFormation = (KeyNumType)(KeyFormation & ~WWKEY_VK_BIT); + KeyHome1 = (KeyNumType)(KeyHome1 & ~WWKEY_VK_BIT); + KeyHome2 = (KeyNumType)(KeyHome2 & ~WWKEY_VK_BIT); + KeyBase = (KeyNumType)(KeyBase & ~WWKEY_VK_BIT); + KeyResign = (KeyNumType)(KeyResign & ~WWKEY_VK_BIT); + KeyAlliance = (KeyNumType)(KeyAlliance & ~WWKEY_VK_BIT); + KeyBookmark1 = (KeyNumType)(KeyBookmark1 & ~WWKEY_VK_BIT); + KeyBookmark2 = (KeyNumType)(KeyBookmark2 & ~WWKEY_VK_BIT); + KeyBookmark3 = (KeyNumType)(KeyBookmark3 & ~WWKEY_VK_BIT); + KeyBookmark4 = (KeyNumType)(KeyBookmark4 & ~WWKEY_VK_BIT); + KeySelectView = (KeyNumType)(KeySelectView & ~WWKEY_VK_BIT); + KeyRepair = (KeyNumType)(KeyRepair & ~WWKEY_VK_BIT); + KeyRepairOn = (KeyNumType)(KeyRepairOn & ~WWKEY_VK_BIT); + KeyRepairOff = (KeyNumType)(KeyRepairOff & ~WWKEY_VK_BIT); + KeySell = (KeyNumType)(KeySell & ~WWKEY_VK_BIT); + KeySellOn = (KeyNumType)(KeySellOn & ~WWKEY_VK_BIT); + KeySellOff = (KeyNumType)(KeySellOff & ~WWKEY_VK_BIT); + KeyMap = (KeyNumType)(KeyMap & ~WWKEY_VK_BIT); + KeySidebarUp = (KeyNumType)(KeySidebarUp & ~WWKEY_VK_BIT); + KeySidebarDown = (KeyNumType)(KeySidebarDown & ~WWKEY_VK_BIT); + KeyOption1 = (KeyNumType)(KeyOption1 & ~WWKEY_VK_BIT); + KeyOption2 = (KeyNumType)(KeyOption2 & ~WWKEY_VK_BIT); + KeyScrollLeft = (KeyNumType)(KeyScrollLeft & ~WWKEY_VK_BIT); + KeyScrollRight = (KeyNumType)(KeyScrollRight & ~WWKEY_VK_BIT); + KeyScrollUp = (KeyNumType)(KeyScrollUp & ~WWKEY_VK_BIT); + KeyScrollDown = (KeyNumType)(KeyScrollDown & ~WWKEY_VK_BIT); + KeyQueueMove1 = (KeyNumType)(KeyQueueMove1 & ~WWKEY_VK_BIT); + KeyQueueMove2 = (KeyNumType)(KeyQueueMove2 & ~WWKEY_VK_BIT); + KeyTeam1 = (KeyNumType)(KeyTeam1 & ~WWKEY_VK_BIT); + KeyTeam2 = (KeyNumType)(KeyTeam2 & ~WWKEY_VK_BIT); + KeyTeam3 = (KeyNumType)(KeyTeam3 & ~WWKEY_VK_BIT); + KeyTeam4 = (KeyNumType)(KeyTeam4 & ~WWKEY_VK_BIT); + KeyTeam5 = (KeyNumType)(KeyTeam5 & ~WWKEY_VK_BIT); + KeyTeam6 = (KeyNumType)(KeyTeam6 & ~WWKEY_VK_BIT); + KeyTeam7 = (KeyNumType)(KeyTeam7 & ~WWKEY_VK_BIT); + KeyTeam8 = (KeyNumType)(KeyTeam8 & ~WWKEY_VK_BIT); + KeyTeam9 = (KeyNumType)(KeyTeam9 & ~WWKEY_VK_BIT); + KeyTeam10 = (KeyNumType)(KeyTeam10 & ~WWKEY_VK_BIT); +#endif +} + + +/*********************************************************************************************** + * OptionsClass::Save_Settings -- writes options settings to the INI file * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 07/03/1996 JLB : Revamped and tightened up. * + * 07/30/1996 JLB : Handles hotkeys. * + *=============================================================================================*/ +void OptionsClass::Save_Settings (void) +{ + CCFileClass file(CONFIG_FILE_NAME); + INIClass ini; + + /* + ** Load any existing options file because it may contain entries that won't be + ** explicitly written out by this routine. By preloading the database, these entries + ** will be carried over. + */ + if (file.Is_Available()) { + ini.Load(file); + } + + /* + ** Save Options settings + */ + static char const * const OPTIONS = "Options"; + ini.Put_Int(OPTIONS, "GameSpeed", GameSpeed); + ini.Put_Int(OPTIONS, "ScrollRate", ScrollRate); + ini.Put_Fixed(OPTIONS, "Brightness", Brightness); + ini.Put_Fixed(OPTIONS, "Volume", Volume); +#ifdef FIXIT_VERSION_3 + if( Session.Type == GAME_NORMAL ) // Save only when non-multiplayer. + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#else + ini.Put_Fixed(OPTIONS, "ScoreVolume", ScoreVolume); +#endif +#ifdef FIXIT_VERSION_3 + ini.Put_Fixed(OPTIONS, "MultiplayerScoreVolume", MultiScoreVolume); +#endif + ini.Put_Fixed(OPTIONS, "Contrast", Contrast); + ini.Put_Fixed(OPTIONS, "Color", Saturation); + ini.Put_Fixed(OPTIONS, "Tint", Tint); + ini.Put_Bool(OPTIONS, "AutoScroll", AutoScroll); + ini.Put_Bool(OPTIONS, "IsScoreRepeat", IsScoreRepeat); + ini.Put_Bool(OPTIONS, "IsScoreShuffle", IsScoreShuffle); + ini.Put_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + + ini.Put_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); + ini.Put_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); + ini.Put_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); + ini.Put_Int(HotkeyName, "KeyForceAttack2", KeyForceAttack2); + ini.Put_Int(HotkeyName, "KeySelect1", KeySelect1); + ini.Put_Int(HotkeyName, "KeySelect2", KeySelect2); + ini.Put_Int(HotkeyName, "KeyScatter", KeyScatter); + ini.Put_Int(HotkeyName, "KeyStop", KeyStop); + ini.Put_Int(HotkeyName, "KeyGuard", KeyGuard); + ini.Put_Int(HotkeyName, "KeyNext", KeyNext); + ini.Put_Int(HotkeyName, "KeyPrevious", KeyPrevious); + ini.Put_Int(HotkeyName, "KeyFormation", KeyFormation); + ini.Put_Int(HotkeyName, "KeyHome1", KeyHome1); + ini.Put_Int(HotkeyName, "KeyHome2", KeyHome2); + ini.Put_Int(HotkeyName, "KeyBase", KeyBase); + ini.Put_Int(HotkeyName, "KeyResign", KeyResign); + ini.Put_Int(HotkeyName, "KeyAlliance", KeyAlliance); + ini.Put_Int(HotkeyName, "KeyBookmark1", KeyBookmark1); + ini.Put_Int(HotkeyName, "KeyBookmark2", KeyBookmark2); + ini.Put_Int(HotkeyName, "KeyBookmark3", KeyBookmark3); + ini.Put_Int(HotkeyName, "KeyBookmark4", KeyBookmark4); + ini.Put_Int(HotkeyName, "KeySelectView", KeySelectView); + ini.Put_Int(HotkeyName, "KeyRepairToggle", KeyRepair); + ini.Put_Int(HotkeyName, "KeyRepairOn", KeyRepairOn); + ini.Put_Int(HotkeyName, "KeyRepairOff", KeyRepairOff); + ini.Put_Int(HotkeyName, "KeySellToggle", KeySell); + ini.Put_Int(HotkeyName, "KeySellOn", KeySellOn); + ini.Put_Int(HotkeyName, "KeySellOff", KeySellOff); + ini.Put_Int(HotkeyName, "KeyMapToggle", KeyMap); + ini.Put_Int(HotkeyName, "KeySidebarUp", KeySidebarUp); + ini.Put_Int(HotkeyName, "KeySidebarDown", KeySidebarDown); + ini.Put_Int(HotkeyName, "KeyOption1", KeyOption1); + ini.Put_Int(HotkeyName, "KeyOption2", KeyOption2); + ini.Put_Int(HotkeyName, "KeyScrollLeft", KeyScrollLeft); + ini.Put_Int(HotkeyName, "KeyScrollRight", KeyScrollRight); + ini.Put_Int(HotkeyName, "KeyScrollUp", KeyScrollUp); + ini.Put_Int(HotkeyName, "KeyScrollDown", KeyScrollDown); + ini.Put_Int(HotkeyName, "KeyQueueMove1", KeyQueueMove1); + ini.Put_Int(HotkeyName, "KeyQueueMove2", KeyQueueMove2); + ini.Put_Int(HotkeyName, "KeyTeam1", KeyTeam1); + ini.Put_Int(HotkeyName, "KeyTeam2", KeyTeam2); + ini.Put_Int(HotkeyName, "KeyTeam3", KeyTeam3); + ini.Put_Int(HotkeyName, "KeyTeam4", KeyTeam4); + ini.Put_Int(HotkeyName, "KeyTeam5", KeyTeam5); + ini.Put_Int(HotkeyName, "KeyTeam6", KeyTeam6); + ini.Put_Int(HotkeyName, "KeyTeam7", KeyTeam7); + ini.Put_Int(HotkeyName, "KeyTeam8", KeyTeam8); + ini.Put_Int(HotkeyName, "KeyTeam9", KeyTeam9); + ini.Put_Int(HotkeyName, "KeyTeam10", KeyTeam10); + + /* + ** Write the INI data out to a file. + */ + ini.Save(file); +} + + +/*********************************************************************************************** + * OptionsClass::Set -- Sets options based on current settings * + * * + * Use this routine to adjust the palette or sound settings after a fresh scenario load. * + * It assumes the values needed are already loaded into OptionsClass. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void OptionsClass::Set(void) +{ + Set_Brightness(Brightness); + Set_Contrast(Contrast); + Set_Saturation(Saturation); + Set_Tint(Tint); + Set_Sound_Volume(Volume, false); + Set_Score_Volume(ScoreVolume, false); + Set_Repeat(IsScoreRepeat); + Set_Shuffle(IsScoreShuffle); +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Delay -- Normalizes delay factor to keep rate constant. * + * * + * This routine is used to adjust delay factors that MUST be synchronized on all machines * + * but should maintain a speed as close to constant as possible. Building animations are * + * a good example of this. * + * * + * INPUT: delay -- The normal delay factor. * + * * + * OUTPUT: Returns with the delay to use that has been modified so that a reasonably constant * + * rate will result. * + * * + * WARNINGS: This calculation is crude due to the coarse resolution that a 1/15 second timer * + * allows. * + * * + * Use of this routine ASSUMES that the GameSpeed is synchronized on all machines. * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + * 06/30/1995 JLB : Handles low values in a more consistent manner. * + *=============================================================================================*/ +int OptionsClass::Normalize_Delay(int delay) const +{ + static int _adjust[][8] = { + {2,2,1,1,1,1,1,1}, + {3,3,3,2,2,2,1,1}, + {5,4,4,3,3,2,2,1}, + {7,6,5,4,4,4,3,2} + }; + if (delay) { + if (delay < 5) { + delay = _adjust[delay-1][GameSpeed]; + } else { + delay = ((delay * 8) / (GameSpeed+1)); + } + } + return(delay); +} + + +/*********************************************************************************************** + * OptionsClass::Fixup_Palette -- Adjusts the real palette to match the palette sliders. * + * * + * This routine is used to adjust the real palette to match the values for the palette * + * control. The adjusted palette is placed into the palette buffer specified. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The palette is not actually set by this routine. * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void OptionsClass::Fixup_Palette(void) const +{ + Adjust_Palette(OriginalPalette, InGamePalette, Brightness, Saturation, Tint, Contrast); + CCPalette = InGamePalette; +} + + +/*********************************************************************************************** + * OptionsClass::Normalize_Volume -- Convert to a real volume value. * + * * + * This routine will take a relative volume value and convert it to the real volume value * + * to use. This allows all the game volumes to be corrected to the correct global volume. * + * * + * INPUT: volume -- Requested volume level. * + * * + * OUTPUT: Returns with the actual volume level to use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +int OptionsClass::Normalize_Volume(int volume) const +{ + return(volume * Volume); +} diff --git a/code/redalert/options.h b/code/redalert/options.h new file mode 100644 index 0000000..1e08bb2 --- /dev/null +++ b/code/redalert/options.h @@ -0,0 +1,157 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OPTIONS.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +class OptionsClass { + public: + enum { + MAX_SCROLL_SETTING=7, + MAX_SPEED_SETTING=7 + }; + + OptionsClass(void); + + void One_Time(void); + void Process(void); + + void Fixup_Palette(void) const; + void Set_Shuffle(int on); + void Set_Repeat(int on); + void Set_Score_Volume(fixed volume, bool feedback); + void Set_Sound_Volume(fixed volume, bool feedback); + void Set_Brightness(fixed brightness); + fixed Get_Brightness(void) const; + void Set_Saturation(fixed color); + fixed Get_Saturation(void) const; + void Set_Contrast(fixed contrast); + fixed Get_Contrast(void) const; + void Set_Tint(fixed tint); + fixed Get_Tint(void) const; + int Normalize_Delay(int delay) const; + int Normalize_Volume(int volume) const; + + /* + ** File I/O routines + */ + void Load_Settings(void); + void Save_Settings(void); + + void Set(void); + + /* + ** This is actually the delay between game frames expressed as 1/60 of + ** a second. The default value is 4 (1/15 second). + */ + unsigned int GameSpeed; + + int ScrollRate; // Distance to scroll. + fixed Volume; // Volume for sound effects. + fixed ScoreVolume; // Volume for scores. +#ifdef FIXIT_VERSION_3 + fixed MultiScoreVolume; // Volume for scores during multiplayer games. +#endif + fixed Brightness; // Brightness. + fixed Tint; // Hue + fixed Saturation; // Saturation + fixed Contrast; // Value + unsigned AutoScroll:1; // Does map autoscroll? + unsigned IsScoreRepeat:1; // Score should repeat? + unsigned IsScoreShuffle:1; // Score list should shuffle? + unsigned IsPaletteScroll:1;// Allow palette scrolling? + + /* + ** These are the hotkeys used for keyboard control. + */ + KeyNumType KeyForceMove1; + KeyNumType KeyForceMove2; + KeyNumType KeyForceAttack1; + KeyNumType KeyForceAttack2; + KeyNumType KeySelect1; + KeyNumType KeySelect2; + KeyNumType KeyScatter; + KeyNumType KeyStop; + KeyNumType KeyGuard; + KeyNumType KeyNext; + KeyNumType KeyPrevious; + KeyNumType KeyFormation; + KeyNumType KeyHome1; + KeyNumType KeyHome2; + KeyNumType KeyBase; + KeyNumType KeyResign; + KeyNumType KeyAlliance; + KeyNumType KeyBookmark1; + KeyNumType KeyBookmark2; + KeyNumType KeyBookmark3; + KeyNumType KeyBookmark4; + KeyNumType KeySelectView; + KeyNumType KeyRepair; + KeyNumType KeyRepairOn; + KeyNumType KeyRepairOff; + KeyNumType KeySell; + KeyNumType KeySellOn; + KeyNumType KeySellOff; + KeyNumType KeyMap; + KeyNumType KeySidebarUp; + KeyNumType KeySidebarDown; + KeyNumType KeyOption1; + KeyNumType KeyOption2; + KeyNumType KeyScrollLeft; + KeyNumType KeyScrollRight; + KeyNumType KeyScrollUp; + KeyNumType KeyScrollDown; + KeyNumType KeyQueueMove1; + KeyNumType KeyQueueMove2; + KeyNumType KeyTeam1; + KeyNumType KeyTeam2; + KeyNumType KeyTeam3; + KeyNumType KeyTeam4; + KeyNumType KeyTeam5; + KeyNumType KeyTeam6; + KeyNumType KeyTeam7; + KeyNumType KeyTeam8; + KeyNumType KeyTeam9; + KeyNumType KeyTeam10; + + void Adjust_Palette(PaletteClass const & oldpal, PaletteClass & newpal, fixed brightness, fixed color, fixed tint, fixed contrast) const; + protected: + + private: + + static char const * const HotkeyName; +}; + + +#endif diff --git a/code/redalert/overlay.cpp b/code/redalert/overlay.cpp new file mode 100644 index 0000000..faacbd9 --- /dev/null +++ b/code/redalert/overlay.cpp @@ -0,0 +1,381 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OVERLAY.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : July 24, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * OverlayClass::Init -- Resets the overlay object system. * + * OverlayClass::Mark -- Marks the overlay down on the map. * + * OverlayClass::OverlayClass -- Overlay object constructor. * + * OverlayClass::delete -- Returns a overlay object to the pool. * + * OverlayClass::new -- Allocates a overlay object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "overlay.h" + + +HousesType OverlayClass::ToOwn = HOUSE_NONE; + + + +/*********************************************************************************************** + * OverlayClass::Init -- Resets the overlay object system. * + * * + * This routine resets the overlay object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::Init(void) +{ + Overlays.Free_All(); +} + + +/*********************************************************************************************** + * OverlayClass::new -- Allocates a overlay object from pool * + * * + * This routine is used to allocate a overlay object from the * + * overlay object pool. * + * * + * INPUT: size -- The size of a overlay object (not used). * + * * + * OUTPUT: Returns with a pointer to an available overlay object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * OverlayClass::operator new(size_t ) +{ + void * ptr = Overlays.Allocate(); + if (ptr) { + ((OverlayClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * OverlayClass::delete -- Returns a overlay object to the pool. * + * * + * This routine will return a overlay object to the overlay object * + * pool. A overlay so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void OverlayClass::operator delete(void * ptr) +{ + if (ptr) { + ((OverlayClass *)ptr)->IsActive = false; + } + Overlays.Free((OverlayClass *)ptr); +} + + +/*********************************************************************************************** + * OverlayClass::OverlayClass -- Overlay object constructor. * + * * + * This is the constructor for a overlay object. * + * * + * INPUT: type -- The overlay object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +OverlayClass::OverlayClass(OverlayType type, CELL pos, HousesType house) : + ObjectClass(RTTI_OVERLAY, Overlays.ID(this)), + Class(OverlayTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + Unlimbo(Cell_Coord(pos)); + ToOwn = HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * OverlayClass::Mark -- Marks the overlay down on the map. * + * * + * This routine will place the overlay onto the map. The overlay object is deleted by this * + * operation. The map is updated to reflect the presence of the overlay. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the overlay successfully marked? Failure occurs if it is not being * + * marked down. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool OverlayClass::Mark(MarkType mark) +{ + assert(Overlays.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL cell = Coord_Cell(Coord); + CellClass * cellptr = &Map[cell]; + + /* + ** Walls have special logic when they are marked down. + */ + if (Class->IsWall) { + if (cellptr->Is_Clear_To_Build()) { + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + cellptr->Redraw_Objects(); + cellptr->Wall_Update(); + Map.Zone_Reset(Class->IsCrushable ? MZONE_NORMAL : MZONE_NORMAL|MZONE_CRUSHER); + + /* + ** Flag ownership of the cell if the 'global' ownership flag indicates that this + ** is necessary for the overlay. + */ + if (ToOwn != HOUSE_NONE) { + cellptr->Owner = ToOwn; + } + + } else { + delete this; + return(false); + } + } else { + + bool clear = false; + if (!ScenarioInit) { + if (Class->Type == OVERLAY_WATER_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_FLOAT, false, false); + } else { + if (Class->Type == OVERLAY_STEEL_CRATE || Class->Type == OVERLAY_WOOD_CRATE) { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, false, false); + } else { + clear = cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true); + } + } + } else { + clear = true; + } + + if ((ScenarioInit || cellptr->Overlay == OVERLAY_NONE) && clear) { + + cellptr->Overlay = Class->Type; + cellptr->OverlayData = 0; + + cellptr->Redraw_Objects(); + if (Class->Land == LAND_TIBERIUM) { + cellptr->OverlayData = 1; + cellptr->Tiberium_Adjust(); + } + } + } + + /* + ** ***** Is this really needed? + */ + cellptr->Recalc_Attributes(); + + /* + ** Remove the overlay and make sure the system thinks it was never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * OverlayClass::Read_INI -- Reads the overlay data from an INI file. * + * * + * This routine is used to load a scenario's overlay data. The overlay objects are read * + * from the INI file and then created on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: Requires that all the buildings be placed first, so the scan for assigning wall * + * ownership to the nearest building will work. * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Specifically forbid manual crates in multiplayer scenarios. * + *=============================================================================================*/ +void OverlayClass::Read_INI(CCINIClass & ini) +{ + if (NewINIFormat > 1) { + int len = ini.Get_UUBlock("OverlayPack", _staging_buffer, sizeof(_staging_buffer)); + + if (len > 0) { + BufferStraw bpipe(_staging_buffer, len); + LCWStraw uncomp(LCWStraw::DECOMPRESS); + uncomp.Get_From(&bpipe); + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + OverlayType classid; + + uncomp.Get(&classid, sizeof(classid)); + + if (classid != OVERLAY_NONE) { + + if (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } + } + } + + if (NewINIFormat < 2 || ini.Is_Present("Overlay")) { + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + CELL cell = atoi(entry); + OverlayType classid = ini.Get_OverlayType(INI_Name(), entry, OVERLAY_NONE); + + /* + ** Don't allow placement of crates in the multiplayer scenarios. + */ + if (classid != OVERLAY_NONE && (Session.Type == GAME_NORMAL || !OverlayTypeClass::As_Reference(classid).IsCrate)) { + + /* + ** Don't allow placement of overlays on the top or bottom rows of + ** the map. + */ + if (cell >= MAP_CELL_W && cell <= MAP_CELL_TOTAL - MAP_CELL_W) { + new OverlayClass(classid, cell); + + // Assign house ownership to cells with walls in 'em. + if (OverlayTypeClass::As_Reference(classid).IsWall) { + HousesType owner = HOUSE_NONE; + int distance = 0x7FFFFFFF; + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + int newdist = ::Distance(building->Center_Coord(), Cell_Coord(cell)); + if (newdist < distance) { + distance = newdist; + owner = building->Owner(); + } + } + Map[cell].Owner = owner; + } + } + } + } + } +} + + +void OverlayClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + ini.Clear("OverlayPack"); + + BufferPipe bpipe(_staging_buffer, sizeof(_staging_buffer)); + LCWPipe comppipe(LCWPipe::COMPRESS); + + comppipe.Put_To(&bpipe); + + int total = 0; + CellClass * cellptr = &Map[(CELL)0]; + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + total += comppipe.Put(&cellptr->Overlay, sizeof(cellptr->Overlay)); + cellptr++; + } + if (total) { + ini.Put_UUBlock("OverlayPack", _staging_buffer, total); + } + +// for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { +// CellClass * cellptr = &Map[index]; +// if (cellptr->Overlay != OVERLAY_NONE) { +// char uname[10]; +// sprintf(uname, "%d", index); +// ini.Put_Overlay(INI_Name(), uname, cellptr->Overlay); +// } +// } +} \ No newline at end of file diff --git a/code/redalert/overlay.h b/code/redalert/overlay.h new file mode 100644 index 0000000..64c8f49 --- /dev/null +++ b/code/redalert/overlay.h @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/OVERLAY.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OVERLAY.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef OVERLAY_H +#define OVERLAY_H + +#include "object.h" +#include "type.h" + +/****************************************************************************** +** This class controls the overlay object. Overlay objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class OverlayClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the overlay object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + OverlayClass(OverlayType type, CELL pos=-1, HousesType = HOUSE_NONE); + OverlayClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~OverlayClass(void) {if (GameActive) OverlayClass::Limbo();Class=0;}; + operator OverlayType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "OVERLAY";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Virtual support functionality. + */ + virtual bool Mark(MarkType); + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + private: + /* + ** This is used to control the marking process of the overlay. If this is + ** set to a valid house number, then the cell that the overlay is marked down + ** upon will be flagged as being owned by the specified house. + */ + static HousesType ToOwn; + + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/packet.cpp b/code/redalert/packet.cpp new file mode 100644 index 0000000..7b63cb8 --- /dev/null +++ b/code/redalert/packet.cpp @@ -0,0 +1,463 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/22/96 * + * * + * Last Update : April 24, 1996 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * *PacketClass::Find_Field -- Finds a field if it exists in the packets * + * Get_Field -- Find specified name and returns data * + * PacketClass::~PacketClass -- destroys a packet class be freeing list * + * PacketClass::Add_Field -- Adds a FieldClass entry to head of packet li* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0) //PG + +#include +#include +#include + +enum {false=0,true=1}; +typedef int bool; + +#include "packet.h" + + +/************************************************************************** + * PACKETCLASS::~PACKETCLASS -- destroys a packet class be freeing list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +PacketClass::~PacketClass(void) +{ + FieldClass *current; + FieldClass *next; + // + // Loop through the entire field list and delete each entry. + // + for (current = Head; current; current = next) { + next = current->Next; + delete current; + } +} + + +/************************************************************************** + * PACKETCLASS::ADD_FIELD -- Adds a FieldClass entry to head of packet li * + * * + * INPUT: FieldClass * - a properly constructed field class entry. * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 04/24/1996 PWG : Created. * + *========================================================================*/ +void PacketClass::Add_Field(FieldClass *field) +{ + field->Next = Head; + Head = field; +} + +/************************************************************************** + * PACKETCLASS::PACKETCLASS -- Creates a Packet object from a COMMS packe * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +PacketClass::PacketClass(char *curbuf) +{ + int remaining_size; + // + // Pull the size and packet ID out of the linear packet stream. + // + Size = *(unsigned short *)curbuf; + curbuf += sizeof (unsigned short); + Size = ntohs(Size); + ID = *(short *)curbuf; + curbuf += sizeof (short); + ID = ntohs(ID); + Head = NULL; + + // + // Calculate the remaining size so that we can loop through the + // packets and extract them. + // + remaining_size = Size - 4; + + // + // Loop through the linear packet until we run out of room and + // create a field for each. + // + while (remaining_size > 0) { + FieldClass *field = new FieldClass; + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(field, curbuf, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + remaining_size -= FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer + // + int size = ntohs(field->Size); + field->Data = new char[size]; + memcpy(field->Data, curbuf, size); + curbuf += size; + remaining_size -= size; + // + // Make sure we allow for the pad bytes. + // + int pad = (4 - (ntohs(field->Size) & 3)) & 3; + curbuf += pad; + remaining_size -= pad; + + // + // Convert the field back to the host format + // + field->Net_To_Host(); + + // + // Finally add the field to the field list in the packet + // structure. + // + Add_Field(field); + } +} + +/************************************************************************** + * CREATE_COMMS_PACKET -- Walks field list creating a packet * + * * + * INPUT: short - the id of the packet so the server can identify it * + * unsigned short & - the size of the packet returned here * + * * + * OUTPUT: void * pointer to the linear packet data * + * * + * WARNINGS: This routine allocates memory that the user is responsible * + * for freeing. * + * * + * HISTORY: * + * 04/22/1996 PWG : Created. * + *========================================================================*/ +char *PacketClass::Create_Comms_Packet(int &size) +{ + FieldClass *current; + + // + // Size starts at four because that is the size of the packet header. + // + size = 4; + + // + // Take a quick spin through and calculate the size of the packet we + // are building. + // + for (current = Head; current; current=current->Next) { + size += (unsigned short)FIELD_HEADER_SIZE; // add in packet header size + size += current->Size; // add in data size + size += (4 - (size & 3)) & 3; // add in pad value to dword align next packet + } + + // + // Now that we know the size allocate a buffer big enough to hold the + // packet. + // + char *retval = new char[size]; + char *curbuf = retval; + + // + // write the size into the packet header + // + *(unsigned short *)curbuf = (unsigned short)htons((unsigned short)size); + curbuf += sizeof (unsigned short); + *(short *)curbuf = htons(ID); + curbuf += sizeof (short); + + // + // Ok now that the actual header information has been written we need to write out + // field information. + // + for (current = Head; current; current = current->Next) { + // + // Temporarily convert the packet to net format (this saves alot of + // effort, and seems safe...) + // + current->Host_To_Net(); + + // + // Copy the adjusted header into the buffer and then advance the buffer + // + memcpy(curbuf, current, FIELD_HEADER_SIZE); + curbuf += FIELD_HEADER_SIZE; + + // + // Copy the data into the buffer and then advance the buffer + // + memcpy(curbuf, current->Data, ntohs(current->Size)); + curbuf += ntohs(current->Size); + + // + // Finally take care of any pad bytes by setting them to 0 + // + int pad = (4 - (ntohs(current->Size) & 3)) & 3; + + // + // If there is any pad left over, make sure you memset it + // to zeros, so it looks like a pad. + // + if (pad) { + memset(curbuf, 0, pad); + curbuf += pad; + } + + current->Net_To_Host(); + } + return(retval); +} + + +/************************************************************************** + * PACKETCLASS::FIND_FIELD -- Finds a field if it exists in the packets * + * * + * INPUT: char * - the id of the field we are looking for. * + * * + * OUTPUT: FieldClass * pointer to the field class * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +FieldClass *PacketClass::Find_Field(char *id) +{ + for (FieldClass *current = Head; current; current = current->Next) { + if ( strncmp(id, current->ID, 4) == 0) + return current; + } + return NULL; +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned char & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned char &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned char *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned short & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned short &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned short *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((long *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data as a string * + * * + * INPUT: char * - the id of the field that holds the data. * + * char * - the string to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The string is not changed if the field is not found. It * + * is assumed that the string variabled specified by the * + * pointer is large enough to hold the data. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, char *data) +{ + FieldClass *field = Find_Field(id); + if (field) { + strcpy(data, (char *)field->Data); + } + return((field) ? true : false); +} + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * unsigned long & - the reference to store the data into * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 04/23/1996 PWG : Created. * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, unsigned long &data) +{ + FieldClass *field = Find_Field(id); + if (field) { + data = *((unsigned long *)field->Data); + } + return((field) ? true : false); +} + + +/************************************************************************** + * GET_FIELD -- Find specified name and returns data * + * * + * INPUT: char * - the id of the field that holds the data. * + * void * - the reference to store the data into * + * int - the length of the buffer passed in * + * * + * OUTPUT: true if the field was found, false if it was not. * + * * + * WARNINGS: The data reference is not changed if the field is not * + * found. * + * * + * HISTORY: * + * 6/4/96 4:46PM ST : Created * + *========================================================================*/ +bool PacketClass::Get_Field(char *id, void *data, int &length) +{ + FieldClass *field = Find_Field(id); + if (field) { + memcpy (data, field->Data, min(field->Size, length)); + length = (int) field->Size; + } + return((field) ? true : false); +} +#endif \ No newline at end of file diff --git a/code/redalert/packet.h b/code/redalert/packet.h new file mode 100644 index 0000000..367d802 --- /dev/null +++ b/code/redalert/packet.h @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Westwood Auto Registration App * + * * + * File Name : PACKET.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 04/19/96 * + * * + * Last Update : April 19, 1996 [PWG] * + * * + * This header defines the functions for the PacketClass. The packet * + * class is used to create a linked list of field entries which can be * + * converted to a linear packet in a COMMS API compatible format. * + * * + * Packets can be created empty and then have fields added to them or can * + * be created from an existing linear packet. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef __PACKET_H +#define __PACKET_H + + +#include "field.h" + +class PacketClass { + public: + PacketClass(short id = 0) + { + Size = 0; + ID = id; + Head = 0; + } + PacketClass(char *cur_buf); + ~PacketClass(void); + + // + // This function allows us to add a field to the start of the list. As the field is just + // a big linked list it makes no difference which end we add a member to. + // + void Add_Field(FieldClass *field); + + // + // These conveniance functions allow us to add a field directly to the list without + // having to worry about newing one first. + // + void Add_Field(char *field, char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned char data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned short data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, unsigned long data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, char *data) {Add_Field(new FieldClass(field, data));}; + void Add_Field(char *field, void *data, int length) {Add_Field(new FieldClass(field, data, length));}; + + // + // These functions search for a field of a given name in the list and + // return the data via a reference value. + // + FieldClass *Find_Field(char *id); + bool Get_Field(char *id, char &data); + bool Get_Field(char *id, unsigned char &data); + bool Get_Field(char *id, short &data); + bool Get_Field(char *id, unsigned short &data); + bool Get_Field(char *id, long &data); + bool Get_Field(char *id, unsigned long &data); + bool Get_Field(char *id, char *data); + bool Get_Field(char *id, void *data, int &length); + + char *Create_Comms_Packet(int &size); + + private: + unsigned short Size; + short ID; + FieldClass *Head; + FieldClass *Current; +}; + + +#endif diff --git a/code/redalert/palettec.cpp b/code/redalert/palettec.cpp new file mode 100644 index 0000000..33bcc56 --- /dev/null +++ b/code/redalert/palettec.cpp @@ -0,0 +1,436 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/palette.cpp 2 9/23/97 11:00p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PALETTE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PaletteClass::Adjust -- Adjusts the palette toward another palette. * + * PaletteClass::Adjust -- Adjusts this palette toward black. * + * PaletteClass::Closest_Color -- Finds closest match to color specified. * + * PaletteClass::Set -- Fade the display palette to this palette. * + * PaletteClass::PaletteClass -- Constructor that fills palette with color specified. * + * PaletteClass::operator = -- Assignment operator for palette objects. * + * PaletteClass::operator == -- Equality operator for palette objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef NOINITCLASS +#define NOINITCLASS +struct NoInitClass { + public: + void operator () (void) const {}; +}; +#endif + +void __cdecl Set_Palette(void * palette); + + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +#include "function.h" +#include "watcom.h" +#include "palette.h" +#include "palettec.h" +#include "ftimer.h" +//#define TIMER_H +#include "wwlib32.h" +//#include "timer.h" +#include + +#ifndef SYSTEM_TIMER_CLASS +#define SYSTEM_TIMER_CLASS + +#ifdef WIN32 +extern WinTimerClass * WindowsTimer; +#endif + +class SystemTimerClass +{ + public: + #ifdef WIN32 + long operator () (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + operator long (void) const {if (!WindowsTimer) return(0);return(WindowsTimer->Get_System_Tick_Count());}; + #else + long operator () (void) const {return(Get_System_Tick_Count());}; + operator long (void) const {return(Get_System_Tick_Count());}; + #endif +}; +#endif + + +//PaletteClass const PaletteClass::CurrentPalette; +extern "C" unsigned char CurrentPalette[]; + +PaletteClass const & PaletteClass::CurrentPalette = *(PaletteClass *)&::CurrentPalette[0]; + + +/*********************************************************************************************** + * PaletteClass::PaletteClass -- Constructor that fills palette with color specified. * + * * + * This constructor will fill the palette with the color specified. * + * * + * INPUT: rgb -- Reference to the color to fill the entire palette with. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +PaletteClass::PaletteClass(RGBClass const & rgb) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + Palette[index] = rgb; + } +} + + +/*********************************************************************************************** + * PaletteClass::operator == -- Equality operator for palette objects. * + * * + * This is the comparison for equality operator. It will compare palette objects to * + * determine if they are identical. * + * * + * INPUT: palette -- Reference to the palette to compare to this palette. * + * * + * OUTPUT: Are the two palettes identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int PaletteClass::operator == (PaletteClass const & palette) const +{ + if (this == &palette) return(true); + return(memcmp(&Palette[0], &palette.Palette[0], sizeof(Palette)) == 0); +} + + +/*********************************************************************************************** + * PaletteClass::operator = -- Assignment operator for palette objects. * + * * + * This is the assignment operator for palette objects. Although the default C++ generated * + * assignment operator would function correctly, it would not check for self-assignment * + * and thus this routine can be faster. * + * * + * INPUT: palette -- Reference to that palette that will be copied into this palette. * + * * + * OUTPUT: Returns with a reference to the newly copied to palette. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +PaletteClass & PaletteClass::operator = (PaletteClass const & palette) +{ + if (this == &palette) return(*this); + + memcpy(&Palette[0], &palette.Palette[0], sizeof(Palette)); + return(*this); +} + + +/*********************************************************************************************** + * PaletteClass::Adjust -- Adjusts this palette toward black. * + * * + * This routine is used to adjust this palette toward black. Typical use of this routine * + * is when fading the palette to black. * + * * + * INPUT: ratio -- The ratio to fade this palette to black. 0 means no fading at all. 255 * + * means 100% faded to black. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't actually set the palette to the video card. Use the Set() * + * function to achieve that purpose. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Adjust(int ratio) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + Palette[index].Adjust(ratio, BlackColor); + } +} + + +/*********************************************************************************************** + * PaletteClass::Adjust -- Adjusts the palette toward another palette. * + * * + * This routine is used to adjust a palette toward a destination palette by the ratio * + * specified. This is primarily used by the palette fading routines. * + * * + * INPUT: palette -- Reference to the destination palette. * + * * + * ratio -- The ratio to adjust this palette toward the destination palette. A * + * value of 0 means no adjustment at all. A value of 255 means 100% * + * adjustment. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Adjust(int ratio, PaletteClass const & palette) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + +//if (index == 1) { +// Mono_Printf("From R=%d,G=%d,B=%d ", Palette[index].Red_Component(), Palette[index].Green_Component(), Palette[index].Blue_Component()); +// Mono_Printf("To R=%d,G=%d,B=%d [%d] ", palette[index].Red_Component(), palette[index].Green_Component(), palette[index].Blue_Component(), ratio); +//} + Palette[index].Adjust(ratio, palette[index]); + +//if (index == 1) { +// Mono_Printf("Equals R=%d,G=%d,B=%d.\n", Palette[index].Red_Component(), Palette[index].Green_Component(), Palette[index].Blue_Component()); +//} + + } +} + + +/*********************************************************************************************** + * PaletteClass::Partial_Adjust -- Adjusts the specified parts of this palette toward black. * + * * + * This routine is used to adjust this palette toward black. Typical use of this routine * + * is when fading the palette to black. The input lookup table is used to determine * + * which entries should fade and which should stay the same * + * * + * INPUT: ratio -- The ratio to fade this palette to black. 0 means no fading at all. 255 * + * means 100% faded to black. * + * * + * lookup -- ptr to lookup table * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine doesn't actually set the palette to the video card. Use the Set() * + * function to achieve that purpose. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Partial_Adjust(int ratio, char *lut) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + if (lut[index]) { + Palette[index].Adjust(ratio, BlackColor); + } + } +} + + +/*********************************************************************************************** + * PaletteClass::Partial_Adjust -- Adjusts the palette toward another palette. * + * * + * This routine is used to adjust a palette toward a destination palette by the ratio * + * specified. This is primarily used by the palette fading routines. The input lookup * + * table is used to determine which entries should fade and which should stay the same * + * * + * * + * INPUT: palette -- Reference to the destination palette. * + * * + * ratio -- The ratio to adjust this palette toward the destination palette. A * + * value of 0 means no adjustment at all. A value of 255 means 100% * + * adjustment. * + * * + * lookup -- ptr to lookup table * + * * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void PaletteClass::Partial_Adjust(int ratio, PaletteClass const & palette, char *lut) +{ + for (int index = 0; index < COLOR_COUNT; index++) { + if (lut[index]) { + Palette[index].Adjust(ratio, palette[index]); + } + } +} + + +/*********************************************************************************************** + * PaletteClass::Closest_Color -- Finds closest match to color specified. * + * * + * This routine will examine the palette and return with the color index number for the * + * color that most closely matches the color specified. Remap operations rely heavily on * + * this routine to allow working with a constant palette. * + * * + * INPUT: rgb -- Reference to a color to search for in the current palette. * + * * + * OUTPUT: Returns with a color index value to most closely matches the specified color. * + * * + * WARNINGS: This routine will quite likely not find an exact match. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int PaletteClass::Closest_Color(RGBClass const & rgb) const +{ + int closest = 0; + int value = -1; + + RGBClass const * ptr = &Palette[0]; + for (int index = 0; index < COLOR_COUNT; index++) { + int difference = rgb.Difference(*ptr++); + if (value == -1 || difference < value) { + value = difference; + closest = index; + } + } + return(closest); +} + + +#ifndef WIN32 +extern void Vsync(void); +#pragma aux Vsync modify [edx ebx eax] = \ + "mov edx,03DAh" \ + "mov ebx,[VertBlank]" \ + "and bl,001h" \ + "shl bl,3" \ + "in_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "je in_vbi" \ + "out_vbi:" \ + "in al,dx" \ + "and al,008h" \ + "xor al,bl" \ + "jne out_vbi" +#endif //WIN32 + + +/*********************************************************************************************** + * PaletteClass::Set -- Fade the display palette to this palette. * + * * + * This routine will fade the display palette to match this palette over the time period * + * specified. For smooth palette transitions, this is the routine to call. * + * * + * INPUT: time -- The time period (in system tick increments) to fade the display palette * + * to match this palette. * + * * + * callback -- Optional pointer to callback function that, if non-null, will be * + * called as often as possible during the fading process. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will not return until the palette is completely faded to the * + * destination palette. * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + * 02/05/1996 JLB : Uses new timer system. * + *=============================================================================================*/ +void PaletteClass::Set(int time, void (* callback)(void)) const +{ + CDTimerClass timer = time; + PaletteClass original = CurrentPalette; + + while (timer) { + + /* + ** Build an intermediate palette that is as close to the destination palette + ** as the current time is proportional to the ending time. + */ + PaletteClass palette = original; + int adjust = ((time - timer) * 256) / time; + palette.Adjust(adjust, *this); + + /* + ** Remember the current time so that multiple palette sets within the same game + ** time tick won't occur. This is probably unnecessary since the palette setting + ** code, at the time of this writing, delays at least one game tick in the process + ** of setting the palette. + */ + long holdtime = timer; + + /* + ** Set the palette to this intermediate palette and then loop back + ** to calculate and set a new intermediate palette. + */ + Set_Palette((void*)&palette[0]); + + // This updates the screen + Device_Present(); + + /* + ** If the callback routine was specified, then call it once per palette + ** setting loop. + */ + if (callback) callback(); + + /* + ** This loop ensures that the palette won't be set more than once per game tick. Setting + ** the palette more than once per game tick will have no effect since the calculation will + ** result in the same intermediate palette that was previously calculated. + */ + while (timer == holdtime && holdtime != 0) { + if (callback) callback(); + } + } + + /* + ** Ensure that the final palette exactly matches the requested + ** palette before exiting the fading routine. + */ + Set_Palette((void*)&Palette[0]); +} diff --git a/code/redalert/palettec.h b/code/redalert/palettec.h new file mode 100644 index 0000000..d8d7c9c --- /dev/null +++ b/code/redalert/palettec.h @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PALETTE.H 2 9/23/97 11:00p Steve_t $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PALETTE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PALETTE_Hx +#define PALETTE_Hx + +#include "rgb.h" + +/* +** The palette class is used to manipulate a palette as a whole. All 256 colors are +** represented by the palette class object. +*/ +class PaletteClass +{ + public: + enum { + COLOR_COUNT=256 // Number of color indices on the palette. + }; + + PaletteClass(void) {}; + PaletteClass(RGBClass const & rgb); + + RGBClass & operator[] (unsigned index) {return(Palette[index % COLOR_COUNT]);}; + const RGBClass & operator[] (unsigned index) const {return(Palette[index % COLOR_COUNT]);}; + int operator == (PaletteClass const & palette) const; + int operator != (PaletteClass const & palette) const {return(!(operator ==(palette)));}; + PaletteClass & operator = (PaletteClass const & palette); + + // Removed these and replaced with function for clarity. ST - 5/9/2019 + //operator const unsigned char * (void) const {return((const unsigned char *)&Palette[0]);}; + //operator unsigned char * (void) {return((unsigned char *)&Palette[0]);}; + const void *Get_Data(void) const { return &Palette[0]; } + void *Get_Data(void) { return &Palette[0]; } + + void Adjust(int ratio); + void Adjust(int ratio, PaletteClass const & palette); + void Partial_Adjust(int ratio, char *lut); + void Partial_Adjust(int ratio, PaletteClass const & palette, char *lut); + void Set(int time = 0, void (*callback)(void) = 0) const; + int Closest_Color(RGBClass const & rgb) const; + + static PaletteClass const & CurrentPalette; + + protected: + RGBClass Palette[COLOR_COUNT]; +}; + + +#endif diff --git a/code/redalert/pipe.cpp b/code/redalert/pipe.cpp new file mode 100644 index 0000000..382e6d9 --- /dev/null +++ b/code/redalert/pipe.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * Pipe::Flush -- Flush all pending data out the pipe. * + * Pipe::Put -- Feed some data through the pipe. * + * Pipe::~Pipe -- Destructor for pipe class object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pipe.h" +#include +#include + + +/*********************************************************************************************** + * Pipe::~Pipe -- Destructor for pipe class object. * + * * + * This destructor will unlink itself from any other pipes that it may be chained to. In * + * the process, it will flush any output it may have pending. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Pipe::~Pipe(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Put_To(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Pipe::Put_To -- Connect a pipe to flow data into from this pipe. * + * * + * This routine will link two pipes together. The specified pipe will be fed data from * + * this pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that data will flow to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Pipe::Put_To(Pipe * pipe) +{ + if (ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + ChainTo->Flush(); + } + + ChainTo = pipe; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Pipe::Put -- Feed some data through the pipe. * + * * + * Use this to force feed data through the pipe. It is guaranteed to accept data as fast * + * as you can supply it. * + * * + * INPUT: source -- Pointer to the data to feed to this routine. * + * * + * length -- The number of bytes of data to submit. * + * * + * OUTPUT: Returns with the number of bytes actually output at the other far distant final * + * end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Put(void const * source, int length) +{ + if (ChainTo != NULL) { + return(ChainTo->Put(source, length)); + } + return(length); +} + + +/*********************************************************************************************** + * Pipe::Flush -- Flush all pending data out the pipe. * + * * + * Then the pipe needs to be flushed, this routine will be called. Since pipe segments * + * might have internal staging buffer for the data, this routine is necessary to force * + * all staging buffers to be clear. This routine is called when the pipe is being * + * destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes output at the far distant final end of the pipe * + * chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Pipe::Flush(void) +{ + if (ChainTo != NULL) { + return(ChainTo->Flush()); + } + return(0); +} + + diff --git a/code/redalert/pipe.h b/code/redalert/pipe.h new file mode 100644 index 0000000..cc1971e --- /dev/null +++ b/code/redalert/pipe.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/29/96 * + * * + * Last Update : June 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PIPE_H +#define PIPE_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** A "push through" pipe interface abstract class used for such purposes as compression +** and translation of data. In STL terms, this is functionally similar to an output +** iterator but with a few enhancements. A pipe class object that is not derived into +** another useful class serves only as a pseudo null-pipe. It will accept data but +** just throw it away but pretend that it sent it somewhere. +*/ +class Pipe +{ + public: + Pipe(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Pipe(void); + + virtual int Flush(void); + virtual int End(void) {return(Flush());} + virtual void Put_To(Pipe * pipe); + void Put_To(Pipe & pipe) {Put_To(&pipe);} + virtual int Put(void const * source, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Pipe * ChainTo; + Pipe * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Pipe(Pipe & rvalue); + Pipe & operator = (Pipe const & pipe); +}; + +#endif diff --git a/code/redalert/pk.cpp b/code/redalert/pk.cpp new file mode 100644 index 0000000..f935d0e --- /dev/null +++ b/code/redalert/pk.cpp @@ -0,0 +1,360 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PK.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PK.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * PKey::Generate -- Generate a public and private key. * + * PKey::PKey -- Construct a key using encoded strings. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "pk.h" +#include "rndstraw.h" + + +/*********************************************************************************************** + * PKey::PKey -- Construct a key using encoded strings. * + * * + * This constructor will construct a key based on the encoded strings supplied. * + * * + * INPUT: exponent -- The encoded string for the exponent portion of the key. * + * * + * modulus -- The encoded string for the modulus portion of the key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +PKey::PKey(void const * exponent, void const * modulus) +{ + Modulus.DERDecode((unsigned char *)modulus); + Exponent.DERDecode((unsigned char *)exponent); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Encode_Modulus -- Encode the modulus portion of the key. * + * * + * This will store the modulus portion of the key into a buffer. The number of bytes * + * stored into the buffer depends on the value of the key. * + * * + * INPUT: buffer -- Pointer to the buffer that will hold the encoded modulus value. * + * * + * OUTPUT: Returns with the number of bytes stored to the buffer. * + * * + * WARNINGS: Be sure that the buffer can hold the encoded bytes. This is normally around the * + * same size as the Crypt_Block_Size() (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Modulus(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Modulus.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Encode_Exponent -- Encode the exponent portion of the key into a buffer. * + * * + * This routine will encode the exponent portion of the key. This is only necessary for the * + * slow key since the fast key always has an exponent of 65537. * + * * + * INPUT: buffer -- Pointer to the buffer that will be filled with the encoded exponent. * + * * + * OUTPUT: Returns with the nuber of bytes stored into the buffer. * + * * + * WARNINGS: Be sure the buffer is big enough to hold the encoded exponent. Usually this is * + * about the same size as the Crypt_Block_Size (plus a byte or two). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encode_Exponent(void * buffer) const +{ + if (buffer == NULL) { + return(0); + } + return(Exponent.DEREncode((unsigned char *)buffer)); +} + + +/*********************************************************************************************** + * PKey::Decode_Modulus -- Decodes the modulus value back into the key. * + * * + * This is the counterpart to the Encode_Modulus() function. It will initialize the * + * modulus portion of the key with the encoded data supplied. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the previously encoded modulus value. * + * * + * OUTPUT: void * + * * + * WARNINGS: void * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Modulus(void * buffer) +{ + Modulus.DERDecode((unsigned char *)buffer); + BitPrecision = Modulus.BitCount()-1; +} + + +/*********************************************************************************************** + * PKey::Decode_Exponent -- Decodes the exponent back into the key. * + * * + * This is the counterpart to the Encode_Exponent function. It will decode a previously * + * encoded exponent portion back into the key. * + * * + * INPUT: buffer -- Pointer to the buffer that holds the encoded exponent value. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKey::Decode_Exponent(void * buffer) +{ + Exponent.DERDecode((unsigned char *)buffer); +} + + +/*********************************************************************************************** + * PKey::Generate -- Generate a public and private key. * + * * + * Public key cryptography relies on having two paired keys. The key used to encrypt * + * data must be decrypted by using the other key. Which key designated as the public or * + * private key is arbitrary. However, one is faster than the other. Use the faster key for * + * the more common operation. * + * * + * INPUT: random -- Reference to a source of random data. * + * * + * bits -- The number of bits to use for key generation. Use a number greater * + * than 16 but less than 2048. The ideal bit size is one that is evenly * + * divisible by 8 and then add one. Practical numbers range from 65 to * + * 1025 bits. * + * * + * fastkey -- Reference to the key that has fast encryption/decryption properties. * + * * + * slowkey -- Reference to the mate key of the other. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can take a very long time. It can take MINUTES to generate a * + * 1024 bit key (even on a Pentium Pro 200Mghz machine). * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + * 07/10/1996 JLB : Must supply source of random data. * + *=============================================================================================*/ +void PKey::Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey) +{ + //PG_TO_FIX + fastkey; + slowkey; +#if (0) + /* + ** Key generation consists of create a key pair and then testing the key + ** pair. If the test fails, then repeat the process. The test and repeat + ** method is required since the prime number generating process can't + ** guarantee the generation of a prime number -- it can only generate a + ** highly likely prime number. + */ + for (;;) { + /* + ** Generate the two random prime numbers. This is the longest + ** step. + */ + BigInt p = Generate_Prime(random, bits, &p); + BigInt q = Generate_Prime(random, bits, &q); + + /* + ** The exponent factors are easy to calculate from the prime numbers. + */ + BigInt e = Fast_Exponent(); + BigInt n = p * q; + BigInt pqmin = (p-(unsigned short)1)*(q-(unsigned short)1); + BigInt d = e.Inverse(pqmin); + + /* + ** Store the data into the key objects. Notice that the modulus is the + ** same for both the fast and slow keys. Also notice that the exponent for + ** the fast key is ALWAYS 65537. Given this, it is possible to economize the + ** fast key into being just the modulus and the slow key to being just the + ** exponent (presuming the slow key also has access to the fast key so that + ** it can get the modulus). + */ + fastkey.Exponent = e; + fastkey.Modulus = n; + fastkey.BitPrecision = n.BitCount()-1; + + slowkey.Exponent = d; + slowkey.Modulus = n; + slowkey.BitPrecision = fastkey.BitPrecision; + + /* + ** Test the keys by encrypting a block of random bytes. If it decrypts + ** correctly, then a valid key pair has been generated -- bail. + */ + char before[256]; + char after[256]; + + for (int index = 0; index < fastkey.Plain_Block_Size(); index++) { + before[index] = (char)rand(); + } + fastkey.Encrypt(before, fastkey.Plain_Block_Size(), after); + slowkey.Decrypt(after, slowkey.Crypt_Block_Size(), after); + + /* + ** Compare the pre and post processing buffer. A match indicates + ** a valid key pair. + */ + if (memcmp(before, after, fastkey.Plain_Block_Size()) == 0) break; + } +#endif +} + + +/*********************************************************************************************** + * PKey::Encrypt -- Encrypt blocks of plaintext. * + * * + * This routine will encrypt the supplied plaintext into cyphertext by processing the input * + * in block. The source is processed in whole blocks. Partial blocks are not supported by * + * public key cryptography. * + * * + * INPUT: source -- Pointer to the source plaintext that will be encrypted. * + * * + * length -- The length of the plaintext to encrypt. * + * * + * dest -- Pointer to the buffer that will hold the encrypted data. * + * * + * OUTPUT: Returns with the number of cypher text bytes placed into the destination buffer. * + * * + * WARNINGS: Be sure that the destination buffer is big enough to hold the output. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Encrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + + /* + ** Encrypt the source data in full blocks. Partial blocks are not processed and are not + ** copied to the destination buffer. + */ + while (slen >= Plain_Block_Size()) { + + /* + ** Perform the encryption of the block. + */ + BigInt temp = 0; + memmove(&temp, source, Plain_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Crypt_Block_Size()); + slen -= Plain_Block_Size(); + source = (char *)source + Plain_Block_Size(); + dest = (char *)dest + Crypt_Block_Size(); + total += Crypt_Block_Size(); + } + + return(total); +} + + +/*********************************************************************************************** + * PKey::Decrypt -- Decrypt supplied cyphertext into its original plaintext. * + * * + * This routine will process the supplied cyphertext by breaking it up into blocks and * + * then decrypting each block in turn. The block size is dependant upon the key. By NOT * + * embedding this information into the cypher data, it makes the encryption more secure. * + * * + * INPUT: source -- Pointer to the cypher text to be decrypted. * + * * + * length -- The number of cypher text bytes supplied to this routine. * + * * + * dest -- Pointer to the buffer to hold the plaintext. * + * * + * OUTPUT: Returns with the number of plaintext bytes output to the destination buffer. * + * * + * WARNINGS: Only whole blocks are processed. If the source has any partial block sized * + * data, then it will be left unprocessed. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int PKey::Decrypt(void const * source, int slen, void * dest) const +{ + int total = 0; + BigInt temp; + + /* + ** Decrypt the source data in full blocks. Partial blocks are not processed in any way. + */ + while (slen >= Crypt_Block_Size()) { + + /* + ** Perform the encryption. + */ + temp = 0; + memmove(&temp, source, Crypt_Block_Size()); + temp = temp.exp_b_mod_c(Exponent, Modulus); + + /* + ** Move the cypher block to the destination. + */ + memmove(dest, &temp, Plain_Block_Size()); + slen -= Crypt_Block_Size(); + source = (char *)source + Crypt_Block_Size(); + dest = (char *)dest + Plain_Block_Size(); + total += Plain_Block_Size(); + } + + return(total); +} diff --git a/code/redalert/pk.h b/code/redalert/pk.h new file mode 100644 index 0000000..f197424 --- /dev/null +++ b/code/redalert/pk.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PK.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PK.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PK_H +#define PK_H + +#include "int.h" + +/* +** This class holds a public or private key used in Public Key Cryptography. It also serves +** as the conduit for encrypting/decrypting data using that key. Cryptography, using this +** method, has a couple of characteristics that affect how it is used. One, the process of +** encrypting/decrypting is very slow. This limits the effective quantity of data that can +** be processed. Two, the ciphertext is larger than the plaintext. This property generally +** limits its use to streaming data as opposed to random access data. The data is processed +** in blocks. The size of the ciphertext and plaintext blocks can be determined only from +** the key itself. +** +** A reasonable use of this technology would be to encrypt only critical data such as the +** password for a fast general purpose cryptographic algorithm. +*/ +class PKey +{ + public: + PKey(void) : Modulus(0), Exponent(0), BitPrecision(0) {} + PKey(void const * exponent, void const * modulus); // DER initialization. + + int Encrypt(void const * source, int slen, void * dest) const; + int Decrypt(void const * source, int slen, void * dest) const; + + static void Generate(Straw & random, int bits, PKey & fastkey, PKey & slowkey); + + int Plain_Block_Size(void) const {return((BitPrecision-1)/8);} + int Crypt_Block_Size(void) const {return(Plain_Block_Size()+1);} + int Block_Count(int plaintext_length) const {return((((plaintext_length-1)/Plain_Block_Size())+1));} + + int Encode_Modulus(void * buffer) const; + int Encode_Exponent(void * buffer) const; + + void Decode_Modulus(void * buffer); + void Decode_Exponent(void * buffer); + + static long Fast_Exponent(void) {return(65537L);} + + private: + + // p*q + BigInt Modulus; + + // 65537 or + // inverse of (p-1)(q-1). + BigInt Exponent; + + // Maximum bits allowed for block. + int BitPrecision; +}; + + +#endif diff --git a/code/redalert/pkpipe.cpp b/code/redalert/pkpipe.cpp new file mode 100644 index 0000000..2562872 --- /dev/null +++ b/code/redalert/pkpipe.cpp @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * Project Name : Command & Conquer * + * * + * * + * File Name : PKPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/07/96 * + * * + * Last Update : July 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * PKPipe::Put -- Submit data to the pipe for processing. * + * PKPipe::Put_To -- Chains one pipe to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkpipe.h" + + +/*********************************************************************************************** + * PKPipe::PKPipe -- Constructor for the public key pipe object. * + * * + * This will construct the public key pipe object. * + * * + * INPUT: control -- The method used to process the data flow (encrypt or decrypt). * + * * + * rnd -- Reference to a random number generate used to create the internal * + * blowfish key. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKPipe::PKPipe(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowPipe::ENCRYPT : BlowPipe::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ +} + + +/*********************************************************************************************** + * PKPipe::Put_To -- Chains one pipe to another. * + * * + * This handles linking of one pipe to this pipe. Data will flow from this PKPipe to the * + * pipe segment specified. Special handling is done so that piping actually flows to the * + * embedded blowfish pipe and then flows to the designated pipe. * + * * + * INPUT: pipe -- Pointer to the pipe that this pipe segment is to send data to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Put_To(Pipe * pipe) +{ + if (BF.ChainTo != pipe) { + if (pipe != NULL && pipe->ChainFrom != NULL) { + pipe->ChainFrom->Put_To(NULL); + pipe->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + BF.ChainTo = pipe; + if (pipe != NULL) { + pipe->ChainFrom = &BF; + } + BF.ChainFrom = this; + ChainTo = &BF; + } +} + + +/*********************************************************************************************** + * PKPipe::Key -- Submit a key to enable processing of data flow. * + * * + * This routine must be called with a valid key pointer in order for encryption/description * + * to be performed on the data stream. Prior to calling this routine or after calling this * + * routine with a NULL pointer, the data stream will pass through this pipe without * + * modification. * + * * + * INPUT: key -- Pointer to the key to use for processing. Pass NULL if process is to be * + * terminated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +void PKPipe::Key(PKey const * key) +{ + if (key == NULL) { + Flush(); + IsGettingKey = false; + } + CipherKey = key; + + if (CipherKey != NULL) { + IsGettingKey = true; + if (Control == DECRYPT) { + Counter = BytesLeft = Encrypted_Key_Length(); + } + } +} + + +/*********************************************************************************************** + * PKPipe::Put -- Submit data to the pipe for processing. * + * * + * This routine (if processing as been enabled by a previous key submission) will * + * encrypt or decrypt the data stream that passes through it. When encrypting, the data * + * stream will increase in size by about 10% (bit it varies according to the key used). * + * * + * INPUT: source -- Pointer to the data to be submitted to the pipe stream. * + * * + * length -- The number of bytes submitted. * + * * + * OUTPUT: Returns with the actual number of byte output at the final end of the pipe. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Put(void const * source, int length) +{ + /* + ** If the parameter seem illegal, then pass the pipe request to the + ** next pipe in the chain and let them deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Pipe::Put(source, length)); + } + + int total = 0; + + /* + ** Perform a special process if the this is the first part of the data flow. The special + ** key must be processed first. After this initial key processing, the rest of the data flow + ** is processed by the blowfish pipe and ignored by the PKPipe. + */ + if (IsGettingKey) { + + /* + ** When encrypting, first make the key block and then pass the data through the + ** normal blowfish processor. + */ + if (Control == ENCRYPT) { + + /* + ** Generate the largest blowfish key possible. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + int didput = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + total += Pipe::Put(Buffer, didput); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + + } else { + + /* + ** First try to accumulate a full key. + */ + int toget = (BytesLeft < length) ? BytesLeft : length; + memmove(&Buffer[Counter-BytesLeft], source, toget); + length -= toget; + BytesLeft -= toget; + source = (char *)source + toget; + + /* + ** If a full key has been accumulated, then decrypt it and feed the + ** key to the blowfish engine. + */ + if (BytesLeft == 0) { + char buffer[MAX_KEY_BLOCK_SIZE]; + CipherKey->Decrypt(Buffer, Counter, buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + + IsGettingKey = false; + } + } + } + + /* + ** If there are any remaining bytes to pipe through, then + ** pipe them through now -- they will be processed by the + ** blowfish engine. + */ + total += Pipe::Put(source, length); + return(total); +} + + +/*********************************************************************************************** + * PKPipe::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKPipe::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKPipe::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/code/redalert/pkpipe.h b/code/redalert/pkpipe.h new file mode 100644 index 0000000..3f91608 --- /dev/null +++ b/code/redalert/pkpipe.h @@ -0,0 +1,133 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/06/96 * + * * + * Last Update : July 6, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PKPIPE_H +#define PKPIPE_H + +#include "pipe.h" +#include "pk.h" +#include "rndstraw.h" +#include "blowpipe.h" + + +/* +** This pipe will encrypt/decrypt the data stream. The data is encrypted by generating a +** symetric key that is then encrypted using the public key system. This symetric key is then +** used to encrypt the remaining data. +*/ +class PKPipe : public Pipe +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKPipe(CryptControl control, RandomStraw & rnd); + + virtual void Put_To(Pipe * pipe); + virtual void Put_To(Pipe & pipe) {Put_To(&pipe);} + + // Feed data through for processing. + virtual int Put(void const * source, int length); + + // Submit key for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowPipe BF; + + /* + ** Controls the method of processing the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption/decryption. The actual process + ** performed is controlled by the Control member. A key can be used for + ** either encryption or decryption -- it makes no difference. However, whichever + ** process is performed, the opposite process must be performed using the + ** other key. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key (whichever is greater). + */ + char Buffer[MAX_KEY_BLOCK_SIZE]; + + /* + ** The working counter that holds the number of bytes in the staging buffer. + */ + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKPipe(PKPipe & rvalue); + PKPipe & operator = (PKPipe const & pipe); +}; + + +#endif diff --git a/code/redalert/pkstraw.cpp b/code/redalert/pkstraw.cpp new file mode 100644 index 0000000..910e4f4 --- /dev/null +++ b/code/redalert/pkstraw.cpp @@ -0,0 +1,301 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * PKStraw::Get -- Fetch data and process it accordingly. * + * PKStraw::Get_From -- Chains one straw to another. * + * PKStraw::Key -- Assign a key to the cipher process straw. * + * PKStraw::PKStraw -- Initialize the public key straw object. * + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + + +/*********************************************************************************************** + * PKStraw::PKStraw -- Initialize the public key straw object. * + * * + * This constructs the public key straw object. The operation to perform (encrypt or * + * decrypt) as well as a random number generator must be provided. * + * * + * INPUT: control -- What operation to perform on the data. Pass in either ENCRYPT or * + * DECRYPT. * + * * + * rnd -- Reference to a random number straw that is used internally to * + * generate the sub-key. The final strength of the cipher depends on * + * quality of this random number generator. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +PKStraw::PKStraw(CryptControl control, RandomStraw & rnd) : + IsGettingKey(true), + Rand(rnd), + BF((control == ENCRYPT) ? BlowStraw::ENCRYPT : BlowStraw::DECRYPT), + Control(control), + CipherKey(NULL), + Counter(0), + BytesLeft(0) +{ + Straw::Get_From(BF); +} + + +/*********************************************************************************************** + * PKStraw::Get_From -- Chains one straw to another. * + * * + * This routine handles the special case of this straw object in that there is an * + * embedded blowfish straw segment. It must be chained on correctly. * + * * + * INPUT: straw -- Pointer to the straw segment that this segment is to receive data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Get_From(Straw * straw) +{ + if (BF.ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = NULL; + } + + BF.ChainTo = straw; + BF.ChainFrom = this; + ChainTo = &BF; + if (BF.ChainTo != NULL) { + BF.ChainTo->ChainFrom = this; + } + } +} + + + +/*********************************************************************************************** + * PKStraw::Key -- Assign a key to the cipher process straw. * + * * + * This routine will assign the key (or NULL if the current key is to be removed) to the * + * cipher stream process. When a key has been assigned, encryption or decryption will * + * take place. In the absence (NULL key pointer) of a key, the data passes through * + * unchanged. * + * * + * INPUT: key -- Pointer to the key to assign to the stream. If the key pointer is NULL, * + * then this causes the cipher stream to stop processing the data and will * + * pass the data through unchanged. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the key passed to this routine is the opposite key to that used * + * to process the stream originally (when decrypting). * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +void PKStraw::Key(PKey const * key) +{ + CipherKey = key; + if (key != NULL) { + IsGettingKey = true; + } + Counter = 0; + BytesLeft = 0; +} + + +/*********************************************************************************************** + * PKStraw::Get -- Fetch data and process it accordingly. * + * * + * This routine will fetch the number of bytes requested. If a valid key has been assigned * + * to this stream, then the data will be processed as it passes through. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The number of data bytes requested. * + * * + * OUTPUT: Returns with the actual number of data bytes stored to the destination buffer. If * + * this number is less than that requested, then it indicates that the data source * + * has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Get(void * source, int length) +{ + /* + ** If the parameters seem invalid, then pass the request on so that someone + ** else can deal with it. + */ + if (source == NULL || length < 1 || CipherKey == NULL) { + return(Straw::Get(source, length)); + } + + int total = 0; + + /* + ** The first part of the data flow must process the special key. After the special + ** key has been processed, the data flows through this straw without direct + ** modification (the blowfish straw will process the data). + */ + if (IsGettingKey) { + + if (Control == DECRYPT) { + + /* + ** Retrieve the pk encrypted blowfish key block. + */ + char cbuffer[MAX_KEY_BLOCK_SIZE]; + int got = Straw::Get(cbuffer, Encrypted_Key_Length()); + + /* + ** If the entire key block could not be retrieved, then this indicates + ** a major data flow error -- just return with no action performed. + */ + if (got != Encrypted_Key_Length()) return(0); + + /* + ** Decrypt the blowfish key and then activate the blowfish straw + ** with that key. + */ + CipherKey->Decrypt(cbuffer, got, Buffer); + BF.Key(Buffer, BLOWFISH_KEY_SIZE); + + } else { + + /* + ** Generate the blowfish key by using random numbers. + */ + char buffer[MAX_KEY_BLOCK_SIZE]; + memset(buffer, '\0', sizeof(buffer)); + Rand.Get(buffer, BLOWFISH_KEY_SIZE); + + /* + ** Encrypt the blowfish key (along with any necessary pad bytes). + */ + Counter = BytesLeft = CipherKey->Encrypt(buffer, Plain_Key_Length(), Buffer); + BF.Key(buffer, BLOWFISH_KEY_SIZE); + } + + /* + ** The first phase of getting the special key has been accomplished. Now, all + ** subsequent data is passed (unmodified) though this straw segment. The blowfish + ** straw takes over the compression/decompression from this point forward. + */ + IsGettingKey = false; + } + + /* + ** If there are any pending bytes in the buffer, then pass + ** these on first. The only time this should be is when the blowfish + ** key has first been generated. + */ + if (BytesLeft > 0) { + int tocopy = (length < BytesLeft) ? length : BytesLeft; + memmove(source, &Buffer[Counter-BytesLeft], tocopy); + source = (char *)source + tocopy; + BytesLeft -= tocopy; + length -= tocopy; + total += tocopy; + } + + /* + ** Any requested bytes that haven't been satisfied are copied over now by + ** drawing the data through the blowfish engine. The blowfish engine happens + ** to be linked to the chain so a normal Get() operation is sufficient. + */ + total += Straw::Get(source, length); + + return(total); +} + + +/*********************************************************************************************** + * PKStraw::Encrypted_Key_Length -- Fetch the encrypted key length. * + * * + * This returns the total number of bytes (after encryption) that the blowfish key will * + * consume. It should be possible to get a block of this size, then pass it to the * + * public key decrypter and the result will be the full blowfish key. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the encrypted blowfish key required. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Encrypted_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Crypt_Block_Size()); +} + + +/*********************************************************************************************** + * PKStraw::Plain_Key_Length -- Returns the number of bytes to encrypt key. * + * * + * This is the number of plain (unencrypted) bytes that the blowfish key will take up. This * + * is actually the number of plain blocks minimum that can contain the full blowfish * + * key. The public key cryptography system encrypts in whole blocks only. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the total number of bytes that will contain the full blowfish key * + * and still be an even block size for the public key cryptography process. * + * * + * WARNINGS: This value is probably be larger than the actual blowfish key length. * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +int PKStraw::Plain_Key_Length(void) const +{ + if (CipherKey == NULL) return(0); + return(CipherKey->Block_Count(BLOWFISH_KEY_SIZE) * CipherKey->Plain_Block_Size()); +} diff --git a/code/redalert/pkstraw.h b/code/redalert/pkstraw.h new file mode 100644 index 0000000..f469566 --- /dev/null +++ b/code/redalert/pkstraw.h @@ -0,0 +1,122 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PKSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PKSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/08/96 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef PKSTRAW_H +#define PKSTRAW_H + +#include "pk.h" +#include "pkstraw.h" +#include "rndstraw.h" +#include "blwstraw.h" + +class PKStraw : public Straw +{ + public: + typedef enum CryptControl { + ENCRYPT, + DECRYPT + } CryptControl; + + PKStraw(CryptControl control, RandomStraw & rnd); + + virtual void Get_From(Straw * straw); + virtual void Get_From(Straw & straw) {Get_From(&straw);} + + virtual int Get(void * source, int slen); + + // Submit key to be used for encryption/decryption. + void Key(PKey const * key); + + private: + enum { + BLOWFISH_KEY_SIZE=BlowfishEngine::MAX_KEY_LENGTH, + MAX_KEY_BLOCK_SIZE=256 // Maximum size of pk encrypted blowfish key. + }; + + /* + ** This flag indicates whether the PK (fetch blowfish key) phase is + ** in progress or not. + */ + bool IsGettingKey; + + /* + ** This is the random straw that is needed to generate the + ** blowfish key. + */ + RandomStraw & Rand; + + /* + ** This is the attached blowfish pipe. After the blowfish key has been + ** decrypted, then the PK processor goes dormant and the blowfish processor + ** takes over the data flow. + */ + BlowStraw BF; + + /* + ** This control member tells what method (encryption or decryption) that should + ** be performed on the data stream. + */ + CryptControl Control; + + /* + ** Pointer to the key to use for encryption or decryption. If this pointer is NULL, then + ** the data passing through this segment will not be modified. + */ + PKey const * CipherKey; + + /* + ** This is the staging buffer for the block of data. This block must be as large as + ** the largest possible key size or the largest blowfish key size (whichever is + ** greater). + */ + char Buffer[256]; + + int Counter; + + /* + ** This records the number of bytes remaining in the current block. This + ** will be the number of bytes left to accumulate before the block can be + ** processed either for encryption or decryption. + */ + int BytesLeft; + + int Encrypted_Key_Length(void) const; + int Plain_Key_Length(void) const; + + PKStraw(PKStraw & rvalue); + PKStraw & operator = (PKStraw const & straw); +}; + +#endif diff --git a/code/redalert/power.cpp b/code/redalert/power.cpp new file mode 100644 index 0000000..8c1b18f --- /dev/null +++ b/code/redalert/power.cpp @@ -0,0 +1,485 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/POWER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : October 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * PowerClass::AI -- Process the power bar logic. * + * PowerClass::Draw_It -- Renders the power bar graphic. * + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * PowerClass::One_Time -- One time processing for the power bar. * + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** Points to the shape to use for the "desired" power level indicator. +*/ +void const * PowerClass::PowerShape; +void const * PowerClass::PowerBarShape; + +PowerClass::PowerButtonClass PowerClass::PowerButton; + + +/*********************************************************************************************** + * PowerClass::PowerClass -- Default constructor for the power bar class. * + * * + * This is the default constructor for the power bar class. It doesn't really do anything. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +PowerClass::PowerClass(void) : + IsToRedraw(false), + IsActive(false), + FlashTimer(0), + RecordedDrain(-1), + RecordedPower(-1), + DesiredDrainHeight(0), + DesiredPowerHeight(0), + DrainHeight(0), + PowerHeight(0), + DrainBounce(0), + PowerBounce(0), + PowerDir(0), + DrainDir(0) +{ +} + + +/*********************************************************************************************** + * PowerClass::Init_Clear -- Clears all the power bar variables. * + * * + * This routine is called in preparation for the start of a scenario. The power bar is * + * initialized into the null state by this routine. As soon as the scenario starts, the * + * power bar will rise to reflect the actual power output and drain. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Init_Clear(void) +{ + RadarClass::Init_Clear(); + RecordedDrain = -1; + RecordedPower = -1; + DesiredDrainHeight = 0; + DesiredPowerHeight = 0; + DrainHeight = 0; + PowerHeight = 0; + PowerBounce = 0; + DrainBounce = 0; + DrainDir = 0; + PowerDir = 0; + FlashTimer = 0; +} + + +/*********************************************************************************************** + * PowerClass::One_Time -- One time processing for the power bar. * + * * + * This routine is for code that truly only needs to be done once per game run. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +void PowerClass::One_Time(void) +{ + RadarClass::One_Time(); + PowerButton.X = ScreenWidth - (640 - (POWER_X * RESFACTOR)); + PowerButton.Y = POWER_Y * RESFACTOR; + PowerButton.Width = (POWER_WIDTH * RESFACTOR)-1; + PowerButton.Height = ScreenHeight - ( POWER_Y * RESFACTOR ) ;// POWER_HEIGHT* RESFACTOR; + PowerShape = MFCD::Retrieve("POWER.SHP"); + PowerBarShape = MFCD::Retrieve("POWERBAR.SHP"); +} + + +/*********************************************************************************************** + * PowerClass::Draw_It -- Renders the power bar graphic. * + * * + * This routine will draw the power bar graphic to the LogicPage. * + * * + * INPUT: complete -- Should the power bar be redrawn even if it isn't specifically flagged * + * to do so? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/27/1994 JLB : Changes power bar color depending on amount of power. * + *=============================================================================================*/ +void PowerClass::Draw_It(bool complete) +{ + static int _modtable[]={ + 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 + }; + +// jmarshall - render the world before the power bar. + RadarClass::Draw_It(complete); +// jmarshall end + + { + BStart(BENCH_POWER); + + if (LogicPage->Lock()) { + if (Map.IsSidebarActive) { + IsToRedraw = false; + ShapeFlags_Type flags = SHAPE_NORMAL; + void const * remap = NULL; + + if (FlashTimer > 1 && ((FlashTimer % 3) & 0x01) != 0) { + flags = flags | SHAPE_FADING; + remap = Map.FadingRed; + } + + /* + ** Hires power strip is too big to fit into a shape so it is in two parts + */ + CC_Draw_Shape(PowerBarShape, 0, ScreenWidth - (640 - (240 * RESFACTOR)), (88 * RESFACTOR), WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); + + int bottomOfBar = (88 * RESFACTOR) + 56; + + while(bottomOfBar < PowerButton.Height + 42 ) + { + CC_Draw_Shape(PowerBarShape, 1, ScreenWidth - (640 - (240 * RESFACTOR)), bottomOfBar, WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); + bottomOfBar += 8 * RESFACTOR; + } + + int bottom = bottomOfBar + ( 28 * RESFACTOR ); + + /* + ** Determine how much the power production exceeds or falls short + ** of power demands. + */ + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + power_height = Bound(power_height, 0, PowerButton.Height - 2); + drain_height = Bound(drain_height, 0, PowerButton.Height - 2); + + /* + ** Draw the power output graphic on top of the power bar framework. + */ + if (power_height) { + int color1 = 3; + int color2 = 4; + + if (PlayerPtr->Drain > PlayerPtr->Power) { + color1 = 214; + color2 = 211; + } + if (PlayerPtr->Drain > (PlayerPtr->Power * 2)) { + color1 = 235; + color2 = 230; + } + + /* + ** New power bar is in slightly different place + ** + ** Old power bar was 107 pixels high. New bar is 153 pixels high. + ** + ** ST - 5/2/96 11:23AM + */ + + power_height = (power_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); + drain_height = (drain_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); + + //bottom = (175*RESFACTOR)+1; + LogicPage->Fill_Rect(ScreenWidth - (640 - (245*RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (245*RESFACTOR+1)), bottom, color2 ); + LogicPage->Fill_Rect(ScreenWidth - (640 - (246*RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (246*RESFACTOR+1)), bottom, color1 ); + } + + /* + ** Draw the power drain threshold marker. + */ + if (PlayerPtr->Drain > 0) + { + CC_Draw_Shape(PowerShape, 0, ScreenWidth - (640 - (((POWER_X * RESFACTOR)) + RESFACTOR)), bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + } + } + LogicPage->Unlock(); + } + BEnd(BENCH_POWER); + } +} + + +/*********************************************************************************************** + * PowerClass::AI -- Process the power bar logic. * + * * + * Use this routine to process the power bar logic. This consists of animation effects. * + * * + * INPUT: input -- The player input value to be consumed or ignored as appropriate. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void PowerClass::AI(KeyNumType &input, int x, int y) +{ + if (Map.IsSidebarActive /*IsActive*/) { + int olddrain = DrainHeight; + int oldpower = PowerHeight; + + + /* + ** If the recorded power value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Power != RecordedPower) { + DesiredPowerHeight = Power_Height(PlayerPtr->Power); + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; + if (PowerHeight > DesiredPowerHeight) { + PowerDir = -1; + } else if (PowerHeight < DesiredPowerHeight) { + PowerDir = 1; + } else { + PowerBounce = 0; + } + } + + /* + ** If the recorded drain value has changed we need to adjust for + ** it. + */ + if (PlayerPtr->Drain != RecordedDrain) { + DesiredDrainHeight = Power_Height(PlayerPtr->Drain); + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; + if (DrainHeight > DesiredDrainHeight) { + DrainDir = -1; + } else if (DrainHeight < DesiredDrainHeight) { + DrainDir = 1; + } else { + DrainBounce = 0; + } + } + + if (DrainBounce && DrainHeight == DesiredDrainHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + DrainBounce--; + } else { + /* + ** If we need to move the drain height then do so. + */ + if (DrainHeight != DesiredDrainHeight) { + DrainHeight += DrainDir; + } + } + + if (PowerBounce && PowerHeight == DesiredPowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + PowerBounce--; + } else { + /* + ** If we need to move the power height then do so. + */ + if (PowerHeight != DesiredPowerHeight) { + PowerHeight += PowerDir; + } + } + + if (olddrain != DrainHeight || oldpower != PowerHeight) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Flag to redraw if the power bar flash effect has expired. + */ +// if (FlashTimer == 1) { + if (FlashTimer > 0) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + RadarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * PowerClass::Refresh_Cells -- Intercepts the redraw logic to see if sidebar to redraw too. * + * * + * This routine will examine a refresh list request and determine if the sidebar would be * + * affect. If so, it will flag the sidebar to be redrawn. * + * * + * INPUT: cell -- The cell that the offset list is base on. * + * * + * list -- The list of cell offset used to flag for redraw. If the special sidebar * + * affecting cell magic offset number is detected, the sidebar is flagged * + * for redraw and the magic offset is removed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1995 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + RadarClass::Refresh_Cells(cell, list); +} + + +/*************************************************************************** + * PowerClass::Power_Height -- Given a value figure where it falls on bar * + * * + * INPUT: int value - the value we are testing * + * * + * OUTPUT: int the height of the point that this value is on graph * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 PWG : Created. * + *=========================================================================*/ +int PowerClass::Power_Height(int value) +{ + int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power + + /* + ** Loop through the different hundreds figuring out the fractional piece + ** of each. + */ + for (int lp = 0; lp < num; lp ++) { + retval = retval + (((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; + } + + /* + ** Adjust the retval to factor in the remainder + */ + if (value) { + retval = retval + (((((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); + } + + retval = Bound(retval, 0, POWER_HEIGHT-2); + return(retval); +} + + +/*********************************************************************************************** + * PowerClass::PowerButtonClass::Action -- Handles the mouse over the power bar area. * + * * + * This routine handles input on the power bar area. Since no input is used for the power * + * bar, this routine just pops up appropriate help text for the power bar. * + * * + * INPUT: flags -- The event flags that triggered this action call. * + * * + * key -- The key code (if any) associated with the trigger event. * + * * + * OUTPUT: Should further button processing be stopped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/07/1995 JLB : Created. * + *=============================================================================================*/ +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +{ + if (!Map.IsSidebarActive) { + return(false); + } + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + Map.Override_Mouse_Shape(MOUSE_NORMAL); + if (PlayerPtr->Power_Fraction() < 1 && PlayerPtr->Power > 0) { + Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } else { + Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, GadgetClass::Get_Color_Scheme()->Color); + } + GadgetClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * PowerClass::Flash_Power -- Flag the power bar to flash. * + * * + * This will cause the power bar to display with a flash so as to draw attention to * + * itself. Typical use of this effect is when power is low. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/14/1996 JLB : Created. * + *=============================================================================================*/ +void PowerClass::Flash_Power(void) +{ + FlashTimer = TICKS_PER_SECOND; + IsToRedraw = true; + Flag_To_Redraw(false); +} diff --git a/code/redalert/power.h b/code/redalert/power.h new file mode 100644 index 0000000..d030986 --- /dev/null +++ b/code/redalert/power.h @@ -0,0 +1,118 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/POWER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : POWER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef POWER_H +#define POWER_H + +#include "radar.h" + +class PowerClass : public RadarClass +{ + public: + PowerClass(void); + PowerClass(NoInitClass const & x) : RadarClass(x), FlashTimer(x) {}; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + + virtual void Init_Clear(void); // Clears all to known state + virtual void Draw_It(bool complete=false); + virtual void AI(KeyNumType &input, int x, int y); + virtual void Refresh_Cells(CELL cell, short const *list); + void Flash_Power(void); + + unsigned IsToRedraw:1; + + protected: + /* + ** This gadget is used to capture mouse input on the power bar. + */ + class PowerButtonClass : public GadgetClass { + public: + PowerButtonClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class PowerClass; + }; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static PowerButtonClass PowerButton; + + enum PowerEnums { + POWER_X=10*ICON_PIXEL_W, + POWER_Y= (7+70+13), + POWER_HEIGHT=(200-(7+70+13)), + POWER_WIDTH=8, + POWER_LINE_SPACE=5, + POWER_LINE_WIDTH=3, + POWER_STEP_LEVEL=100, + POWER_STEP_FACTOR=5 + }; + + private: + int Power_Height(int value); + + unsigned IsActive:1; + + /* + ** If the power bar should be rendered with some flash effect then + ** this specifies the duration that the flash will occur. + */ + CDTimerClass FlashTimer; + + int RecordedDrain; + int RecordedPower; + int DesiredDrainHeight; + int DesiredPowerHeight; + int DrainHeight; + int PowerHeight; + int DrainBounce; + int PowerBounce; + short PowerDir; + short DrainDir; + + /* + ** Points to the shape to use for the "desired" power level indicator. + */ + static void const * PowerShape; + static void const * PowerBarShape; +}; + +#endif diff --git a/code/redalert/profile.cpp b/code/redalert/profile.cpp new file mode 100644 index 0000000..3c6b996 --- /dev/null +++ b/code/redalert/profile.cpp @@ -0,0 +1,867 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/PROFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : PROFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : September 10, 1993 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWGetPrivateProfileInt -- Fetches integer value from INI. * + * WWGetPrivateProfileString -- Fetch string from INI. * + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * WWWritePrivateProfileString -- Write a string to the profile data block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +static char * WriteBinBuffer = NULL; +static int WriteBinBufferLen = 0; +static int WriteBinBufferPos = 0; +static int WriteBinBufferMax = 0; +static char * ReadBinBuffer = NULL; +static int ReadBinBufferLen = 0; +static int ReadBinBufferPos = 0; +static int ReadBinBufferMax = 0; + + +/*************************************************************************** + * Read_Private_Config_Struct -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=========================================================================*/ +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config) +{ + INIClass ini; + ini.Load(file); + + config->DigitCard = ini.Get_Hex("Sound", "Card", 0); + config->IRQ = ini.Get_Int("Sound", "IRQ", 0); + config->DMA = ini.Get_Int("Sound", "DMA", 0); + config->Port = ini.Get_Hex("Sound", "Port", 0); + config->BitsPerSample= ini.Get_Int("Sound", "BitsPerSample", 0); + config->Channels = ini.Get_Int("Sound", "Channels", 0); + config->Reverse = ini.Get_Int("Sound", "Reverse", 0); + config->Speed = ini.Get_Int("Sound", "Speed", 0); + ini.Get_String("Language", "Language", NULL, config->Language, sizeof(config->Language)); + + +// config->DigitCard = WWGetPrivateProfileHex("Sound", "Card", profile); +// config->IRQ = WWGetPrivateProfileInt("Sound", "IRQ", 0,profile); +// config->DMA = WWGetPrivateProfileInt("Sound", "DMA", 0,profile); +// config->Port = WWGetPrivateProfileHex("Sound", "Port", profile); +// config->BitsPerSample= WWGetPrivateProfileInt("Sound", "BitsPerSample",0,profile); +// config->Channels = WWGetPrivateProfileInt("Sound", "Channels", 0,profile); +// config->Reverse = WWGetPrivateProfileInt("Sound", "Reverse", 0,profile); +// config->Speed = WWGetPrivateProfileInt("Sound", "Speed", 0,profile); +// WWGetPrivateProfileString("Language", "Language", NULL, config->Language, 3, profile); + + return((config->DigitCard == 0) && (config->IRQ == 0) && (config->DMA == 0)); +} + + +/*************************************************************************** + * Get_Private_Profile_Hex -- Fetches override integer value. * + * * + * INPUT: * + * OUTPUT: * + * WARNINGS: * + * HISTORY: * + * 08/05/1992 MML : Created. * + *=========================================================================*/ +unsigned WWGetPrivateProfileHex (char const * section, char const * entry, char * profile) +{ + char buffer[16]; // Integer staging buffer. + unsigned card; + + memset (buffer, '0', sizeof(buffer)); // MAX_ENTRY_SIZE = 15 + buffer[sizeof(buffer)-1] = '\0'; + + WWGetPrivateProfileString(section, entry, "0", buffer, sizeof(buffer), profile); + + if (strlen (buffer) > 0) { + sscanf (buffer, "%x", &card); + } else { + card = 0; + } + + return(card); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileInt -- Fetches integer value. * + * * + * INPUT: * + * section section to read from * + * * + * entry name of entry to read * + * * + * def default value, if entry isn't found * + * * + * profile buffer containing INI data * + * * + * OUTPUT: * + * integer requested * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +int WWGetPrivateProfileInt(char const * section, char const * entry, int def, char * profile) +{ + char buffer[16]; // Integer staging buffer. + + /* + ** Store the default in the buffer. + */ + sprintf(buffer, "%d", def); + + /* + ** Get the buffer; use itself as the default. + */ + WWGetPrivateProfileString(section, entry, buffer, buffer, sizeof(buffer)-1, profile); + + /* + ** Convert to int & return. + */ + return(atoi(buffer)); +} + + +/*********************************************************************************************** + * WWWritePrivateProfileInt -- Write a profile int to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * * + * entry name of entry to write; if NULL, the entire section is deleted * + * * + * value value to write * + * * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileInt(char const * section, char const * entry, int value, char * profile) +{ + char buffer[250]; // Working section buffer. + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Generate string to save. + */ + sprintf(buffer, "%d", value); + + /* + ** Save the string. + */ + return(WWWritePrivateProfileString(section, entry, buffer, profile)); +} + + +/*********************************************************************************************** + * WWGetPrivateProfileString -- Fetch game override string. * + * * + * INPUT: * + * section section name to read from * + * * + * entry name of entry to read; if NULL, all entry names are returned * + * * + * def default string to use if not found; can be NULL * + * * + * retbuffer buffer to store result in * + * * + * retlen max length of return buffer * + * * + * profile INI buffer * + * * + * OUTPUT: * + * ptr to entry found in INI buf; NULL if not found * + * * + * WARNINGS: * + * On the PC, the "\n" (10) is translated to "\r\n" (13,10) when it's written * + * to disk. This routine must take this into consideration, by searching * + * for \n when scanning backward, and for \r when scanning forward. * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +char * WWGetPrivateProfileString(char const * section, char const * entry, char const * def, char * retbuffer, int retlen, char const * profile) +{ + char const * workptr; // Working pointer into profile block. + char * altworkptr; // Alternate work pointer. + char sec[50]; // Working section buffer. + char const * retval; // Start of section or entry pointer. + char * next; // Pointer to start of next section (or EOF). + char c,c2; // Working character values. + int len; // Working substring length value. + int entrylen; // Byte length of specified entry. + char * orig_retbuf; // original retbuffer ptr + +// if (!retlen) return(NULL); + + /* + ** Fill in the default value just in case the entry could not be found. + */ + if (retbuffer) { + retbuffer[0] = '\0'; + if (retlen > 1 || retlen == 0) retbuffer[1] = '\0'; + if (def) { + strncpy(retbuffer, def, retlen); + } + retbuffer[retlen-1] = '\0'; + orig_retbuf = retbuffer; + } + + /* + ** Make sure a profile string was passed in + */ + if (!profile || !section) { + return(retbuffer); + } + + /* + ** Build section string to match file image. + */ + sprintf(sec, "[%s]", section); // sec = section name including []'s + strupr(sec); + len = strlen(sec); // len = section name length, incl []'s + + /* + ** Scan for a matching section + */ + retval = profile; + workptr = profile; + for (;;) { + + /* + ** 'workptr' = start of next section + */ + workptr = strchr(workptr, '['); + + /* + ** If the end has been reached without finding the desired section + ** then abort with a failure flag. + */ + if (!workptr) { + return(NULL); + } + + /* + ** 'c' = character just before the '[' + */ + if (workptr==profile) { + c = '\n'; + } else { + c = *(workptr-1); + } + + /* + ** If this is the section name & the character before is a newline, + ** process this section + */ + if (memicmp(workptr, sec, len) == 0 && (c == '\n')) { + + /* + ** Skip work pointer to start of first valid entry. + */ + workptr += len; + while (isspace(*workptr)) { + workptr++; + } + + /* + ** If the section name is empty, we will have stepped onto the start + ** of the next section name; inserting new entries here will leave + ** a blank line between this section's name & 1st entry. So, check + ** for 2 newlines in a row & step backward. + */ + if (workptr - profile > 4) { + if ( *(workptr-1)=='\n' && *(workptr-3)=='\n') + workptr -= 2; + } + + /* + ** 'next = end of section or end of file. + */ + next = (char*)strchr(workptr, '['); + for (;;) { + if (next) { + + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if (*(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = (char*)workptr + strlen(workptr)-1; + break; + } + } + + /* + ** If a specific entry was specified then return with the associated + ** string. + */ + if (entry) { + retval = workptr; + entrylen = strlen(entry); + + for (;;) { + /* + ** Search for the 1st character of the entry + */ + workptr = strchr(workptr, *entry); + + /* + ** If the end of the file has been reached or we have spilled + ** into the next section, then abort + */ + if (!workptr || workptr >= next) { + return(NULL); + } + + /* + ** 'c' = character before possible entry; must be a newline + ** 'c2' = character after possible entry; must be '=' or space + */ + c = *(workptr-1); + c2 = *(workptr+entrylen); + + /* + ** Entry found; extract it + */ + if (memicmp(workptr, entry, entrylen) == 0 && (c == '\n') && + (c2 == '=' || isspace(c2))) { + retval = workptr; + workptr += entrylen; // skip entry name + workptr = strchr(workptr, '='); // find '=' + + /* + ** 'altworkptr' = next newline; \r is used here since we're + ** scanning forward! + */ + if (workptr) { + altworkptr = (char*)strchr(workptr, '\r'); // find next newline + } + + /* + ** Return if there was no '=', or if the newline is before + ** the next '=' + */ + if (workptr == NULL || altworkptr < workptr) { + return((char *)retval); + } + + /* + ** Skip any white space after the '=' and before the first + ** valid character of the parameter. + */ + workptr++; // Skip the '='. + while (isspace(*workptr)) { + + /* + ** Just return if there's no entry past the '='. + */ + if (workptr >= altworkptr) + return((char*)retval); + + workptr++; // Skip the whitespace + } + + /* + ** Copy the entry into the return buffer. + */ + len = (int)(altworkptr - workptr); + if (len > retlen-1) { + len = retlen-1; + } + + if (retbuffer) { + memcpy(retbuffer, workptr, len); + *(retbuffer + len) = '\0'; // Insert trailing null. + strtrim(retbuffer); + } + return((char*)retval); + } + + /* + ** Entry was not found; go to the next one + */ + workptr++; + } + } else { + + /* + ** No entry was specified, so build a list of all entries. + ** 'workptr' is at 1st entry after section name + ** 'next' is next bracket, or end of file + */ + retval = workptr; + + if (retbuffer) { + + /* + ** Keep accumulating the identifier strings in the retbuffer. + */ + while (workptr && workptr < next) { + altworkptr = (char*)strchr(workptr, '='); // find '=' + + if (altworkptr && altworkptr < next) { + int length; // Length of ID string. + + length = (int)(altworkptr - workptr); + + /* + ** Make sure we don't write past the end of the retbuffer; + ** add '3' for the 3 NULL's at the end + */ + if (retbuffer - orig_retbuf + length + 3 < retlen) { + memcpy(retbuffer, workptr, length); // copy entry name + *(retbuffer+length) = '\0'; // NULL-terminate it + strtrim(retbuffer); // trim spaces + retbuffer += strlen(retbuffer)+1; // next pos in dest buf + } else { + break; + } + + /* + ** Advance the work pointer to the start of the next line + ** by skipping the end of line character. + */ + workptr = strchr(altworkptr, '\n'); + if (!workptr) { + break; + } + workptr++; + } else { + + /* + ** If no '=', break out of loop + */ + break; + } + } + + /* + ** Final trailing terminator. Make double sure the double + ** trailing null is added. + */ + *retbuffer++ = '\0'; + *retbuffer++ = '\0'; + } + break; + } + } else { + + /* + ** Section name not found; go to the next bracket & try again + ** Advance past '[' and keep scanning. + */ + workptr++; + } + } + + return((char*)retval); +} + + +/*********************************************************************************************** + * WritePrivateProfileString -- Write a string to the profile data block. * + * * + * INPUT: * + * section section name to write to * + * entry name of entry to write; if NULL, the section is deleted * + * string string to write; if NULL, the entry is deleted * + * profile INI buffer * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * This function has to translate newlines into \r\n sequences. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +bool WWWritePrivateProfileString(char const * section, char const * entry, char const * string, char * profile) +{ + char buffer[250]; // Working section buffer + char const * offset; + char const * next; // ptr to next section + char c; // Working character value + + /* + ** Just return if nothing to do. + */ + if (!profile || !section) { + return(true); + } + + /* + ** Try to find the section. WWGetPrivateProfileString with NULL entry name + ** will return all entry names in the given buffer, truncated to the given + ** buffer length. 'offset' will point to 1st entry in the section, NULL if + ** section not found. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + + /* + ** If the section could not be found, then add it to the end. Don't add + ** anything if a removal of an entry is requested (it is obviously already + ** non-existent). Make sure two newlines precede the section name. + */ + if (!offset && entry) { + sprintf(buffer, "\r\n[%s]\r\n", section); + strcat(profile, buffer); + } + + /* + ** If the section is there and 'entry' is NULL, remove the entire section + */ + if (offset && !entry) { + + /* + ** 'next = end of section or end of file. + */ + next = strchr(offset, '['); + for (;;) { + if (next) { + c = *(next-1); + + /* + ** If character before '[' is newline, this is the start of the + ** next section + */ + if (c == '\n') { + if ( *(next-1)=='\n' && *(next-3)=='\n') { + next -= 2; + } + break; + } + + /* + ** This bracket was in the section; keep looking + */ + next = strchr(next+1, '['); + } else { + + /* + ** No bracket found; set 'next' to the end of the file + */ + next = offset + strlen(offset); + break; + } + } + + /* + ** Remove the section + */ + strcpy((char*)offset, (char*)next); + + return(true); + } + + /* + ** Find the matching entry within the desired section. A NULL return buffer + ** with 0 length will just return the offset of the found entry, NULL if + ** entry not found. + */ + offset = WWGetPrivateProfileString(section, entry, NULL, NULL, 0, profile); + + /* + ** Remove any existing entry + */ + if (offset) { + int eol; // Working EOL offset. + + /* + ** Get # characters up to newline; \n is used since we're after the end + ** of this line + */ + eol = strcspn(offset, "\n"); + + /* + ** Erase the entry by strcpy'ing the entire INI file over this entry + */ + if (eol) { + strcpy((char*)offset, offset + eol + 1); + } + } else { + + /* + ** Entry doesn't exist, so point 'offset' to the 1st entry position in + ** the section. + */ + offset = WWGetPrivateProfileString(section, NULL, NULL, NULL, 0, profile); + } + + /* + ** Add the desired entry. + */ + if (entry && string) { + + /* + ** Generate entry string. + */ + sprintf(buffer, "%s=%s\r\n", entry, string); + + /* + ** Make room for new entry. + */ + memmove((char*)offset+strlen(buffer), offset, strlen(offset)+1); + + /* + ** Copy the entry into the INI buffer. + */ + memcpy((char*)offset, buffer, strlen(buffer)); + } + + return(true); +} + + +char * Read_Bin_Buffer( void ) +{ + return( ReadBinBuffer ); +} + + +bool Read_Bin_Init( char * buffer, int length ) +{ + ReadBinBuffer = buffer; + ReadBinBufferLen = length; + ReadBinBufferPos = 0; + ReadBinBufferMax = 0; + return( true ); +} + + +int Read_Bin_Length( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferMax ); + } +} + + +bool Read_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != ReadBinBuffer || length <= 0 || length > 4 || + (ReadBinBufferPos + length) >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + memcpy( num, ptr, length ); + ReadBinBufferPos += length; + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } +} + + +int Read_Bin_Pos( char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + return( ReadBinBufferPos ); + } +} + + +int Read_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != ReadBinBuffer) { + return( -1 ); + } else { + ReadBinBufferPos = pos; + return( ReadBinBufferPos ); + } +} + + +bool Read_Bin_String( char * string, char * buffer ) +{ + char * ptr; + unsigned char length; + + if (buffer != ReadBinBuffer || + ReadBinBufferPos >= ReadBinBufferLen) { + return( false ); + } else { + ptr = ReadBinBuffer + ReadBinBufferPos; + length = (unsigned char)*ptr++; + if ( (ReadBinBufferPos + length + 2) <= ReadBinBufferLen) { + memcpy( string, ptr, (unsigned int)(length + 1) ); + ReadBinBufferPos += (length + 2); + + if (ReadBinBufferPos > ReadBinBufferMax) { + ReadBinBufferMax = ReadBinBufferPos; + } + + return( true ); + } else { + return( false ); + } + } +} + + +char * Write_Bin_Buffer( void ) +{ + return( WriteBinBuffer ); +} + + +bool Write_Bin_Init( char * buffer, int length ) +{ + WriteBinBuffer = buffer; + WriteBinBufferLen = length; + WriteBinBufferPos = 0; + WriteBinBufferMax = 0; + return( true ); +} + + +int Write_Bin_Length( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferMax ); + } +} + + +bool Write_Bin_Num( void * num, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length <= 0 || length > 4 || + (WriteBinBufferPos + length) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + memcpy( ptr, num, length ); + WriteBinBufferPos += length; + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + + +int Write_Bin_Pos( char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + return( WriteBinBufferPos ); + } +} + + +int Write_Bin_PosSet( unsigned int pos, char * buffer ) +{ + if (buffer != WriteBinBuffer) { + return( -1 ); + } else { + WriteBinBufferPos = pos; + return( WriteBinBufferPos ); + } +} + + +bool Write_Bin_String( char * string, int length, char * buffer ) +{ + char * ptr; + + if (buffer != WriteBinBuffer || length < 0 || length > 255 || + (WriteBinBufferPos + length + 2) > WriteBinBufferLen) { + return( false ); + } else { + ptr = WriteBinBuffer + WriteBinBufferPos; + *ptr++ = (char)length; + memcpy( ptr, string, (length + 1) ); + WriteBinBufferPos += (length + 2); + + if (WriteBinBufferPos > WriteBinBufferMax) { + WriteBinBufferMax = WriteBinBufferPos; + } + + return( true ); + } +} + diff --git a/code/redalert/queue.cpp b/code/redalert/queue.cpp new file mode 100644 index 0000000..1556d01 --- /dev/null +++ b/code/redalert/queue.cpp @@ -0,0 +1,4561 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/QUEUE.CPP 6 3/14/97 5:12p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/28/95 * + * * + * Last Update : October 14, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions for Queueing Events: * + * Queue_Mission -- Queue a mega mission event. * + * Queue_Options -- Queue the options event. * + * Queue_Exit -- Add the exit game event to the queue. * + * * + * Functions for processing Queued Events: * + * Queue_AI -- Process all queued events. * + * Queue_AI_Normal -- Process all queued events. * + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * Main Multiplayer Queue Logic: * + * Wait_For_Players -- Waits for other systems to come on-line * + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * Process_Send_Period -- timing for sending packets every 'n' frames * + * Send_Packets -- sends out events from the OutList * + * Send_FrameSync -- Sends a FRAMESYNC packet * + * Process_Receive_Packet -- processes an incoming packet * + * Process_Serial_Packet -- Handles an incoming serial packet * + * Can_Advance -- determines if it's OK to advance to the next frame * + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * Handle_Timeout -- attempts to reconnect; if fails, bails. * + * Stop_Game -- stops the game * + * * + * Packet Compression / Decompression: * + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * Add_Compressed_Events -- adds compressed events to a packet * + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * Extract_Uncompressed_Events -- extracts events from a packet * + * Extract_Compressed_Events -- extracts events from a packet * + * * + * DoList Management: * + * Execute_DoList -- Executes commands from the DoList * + * Clean_DoList -- Cleans out old events from the DoList * + * Queue_Record -- Records the DoList to disk * + * Queue_Playback -- plays back queue entries from a record file * + * * + * Debugging: * + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * Add_CRC -- Adds a value to a CRC * + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * Init_Queue_Mono -- inits mono display * + * Update_Queue_Mono -- updates mono display * + * Print_Framesync_Values -- displays frame-sync variables * + * Check_Mirror -- Checks mirror memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +bool bReconnectDialogCancelled; +#endif + + +/********************************** Defines *********************************/ +#define SHOW_MONO 0 + + +/********************************** Globals *********************************/ +//--------------------------------------------------------------------------- +// GameCRC is the current computed CRC value for this frame. +// CRC[] is a record of our last 32 game CRC's. +// ColorNames is for debug output in Print_CRCs +//--------------------------------------------------------------------------- +static unsigned long GameCRC; +static unsigned long CRC[32] = + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0}; +static char *ColorNames[8] = { + "Yellow", + "LtBlue", + "Red", + "Green", + "Orange", + "Grey", + "Blue", + "Brown" +}; + +//........................................................................... +// Mono debugging variables: +// NetMonoMode: 0 = show connection output, 1 = flowcount output +// NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen +// IsMono: used for taking control of Mono screen away from the engine +//........................................................................... +int NetMonoMode = 1; +int NewMonoMode = 1; +static int IsMono = 0; + +//--------------------------------------------------------------------------- +// Several routines return various codes; here's an enum for all of them. +//--------------------------------------------------------------------------- +typedef enum RetcodeEnum { + RC_NORMAL, // no news is good news + RC_PLAYER_READY, // a new player has been heard from + RC_SCENARIO_MISMATCH, // scenario mismatch + RC_DOLIST_FULL, // DoList is full + RC_SERIAL_PROCESSED, // modem: SERIAL packet was processed + RC_PLAYER_LEFT, // modem: other player left the game + RC_HUNG_UP, // modem has hung up + RC_NOT_RESPONDING, // other player not responding (timeout/hung up) + RC_CANCEL, // user cancelled +} RetcodeType; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern void Enable_Secret_Units(void); +#endif + +/********************************* Prototypes *******************************/ +//........................................................................... +// Main multiplayer queue logic +//........................................................................... +static void Queue_AI_Normal(void); +static void Queue_AI_Multiplayer(void); +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv); +static void Generate_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent); +static void Generate_Process_Time_Event(ConnManClass *net); +static int Process_Send_Period(ConnManClass *net); //, int init); +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent); +static void Send_FrameSync(ConnManClass *net, int cmd_count); +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time); +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh); +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv); +static void Stop_Game(void); + +//........................................................................... +// Packet compression/decompression: +//........................................................................... +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap); +int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size, + int cap); +static int Breakup_Receive_Packet(void *buf, int bufsize ); +int Extract_Uncompressed_Events(void *buf, int bufsize); +int Extract_Compressed_Events(void *buf, int bufsize); + +//........................................................................... +// DoList management: +//........................................................................... +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, +// ConnManClass *net, TCountDownTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +static void Clean_DoList(ConnManClass *net); +static void Queue_Record(void); +static void Queue_Playback(void); + +//........................................................................... +// Debugging: +//........................................................................... +static void Compute_Game_CRC(void); +void Add_CRC(unsigned long *crc, unsigned long val); +static void Print_CRCs(EventClass *ev); +static void Init_Queue_Mono(ConnManClass *net); +static void Update_Queue_Mono(ConnManClass *net, int flow_index); +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent); + +extern void Keyboard_Process(KeyNumType &input); +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv); +void Check_Mirror(void); + + +/*************************************************************************** + * Queue_Mission -- Queue a mega mission event. * + * * + * This routine is called when the player causes a change to a game unit. * + * The event that initiates the change is queued to as a result of a call * + * to this routine. * + * * + * INPUT: * + * whom Whom this mission request applies to (a friendly unit). * + * mission The mission to assign to this object. * + * target The target of this mission (if any). * + * dest The movement destination for this mission (if any). * + * * + * OUTPUT: * + * Was the mission request queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination)))) { + return(false); + } else { + return(true); + } +} + + +/*********************************************************************************************** + * Queue_Mission -- Queue a mega mission event, formation override for common speed. * + * * + * This routine is called when the player causes a change to a game unit. The event that * + * initiates the change is queued to as a result of a call to this routine. * + * * + * INPUT: whom -- Whom this mission request applies to (a friendly unit). * + * * + * mission -- The mission to assign to this object. * + * * + * target -- The target of this mission (if any). * + * * + * dest -- The movement destination for this mission (if any). * + * * + * speed -- The override speed for this unit. * + * * + * maxspeed -- The override maximum speed for this unit. * + * * + * OUTPUT: Was the mission request queued successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +bool Queue_Mission(TargetClass whom, MissionType mission, TARGET target, TARGET destination, SpeedType speed, MPHType maxspeed) +{ + if (! OutList.Add(EventClass(whom, mission, TargetClass(target), TargetClass(destination), speed, maxspeed))) { + return(false); + } else { + return(true); + } +} + + +/*************************************************************************** + * Queue_Options -- Queue the options event. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the options screen event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Options(void) +{ + if (! OutList.Add(EventClass(EventClass::OPTIONS))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Options */ + + +/*************************************************************************** + * Queue_Exit -- Add the exit game event to the queue. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Was the exit event queued successfully? * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +bool Queue_Exit(void) +{ + if (! OutList.Add(EventClass(EventClass::EXIT))) { + return(false); + } + else { + return(true); + } + +} /* end of Queue_Exit */ + + +/*************************************************************************** + * Queue_AI -- Process all queued events. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +void Queue_AI(void) +{ + if (Session.Play) { + Queue_Playback(); + } + + else { + + switch (Session.Type) { + + case GAME_SKIRMISH: + case GAME_NORMAL: + Queue_AI_Normal(); + break; + + case GAME_MODEM: + case GAME_NULL_MODEM: + case GAME_IPX: + case GAME_INTERNET: + case GAME_TEN: + case GAME_MPATH: + Queue_AI_Multiplayer(); + break; + } + } + +} /* end of Queue_AI */ + + +/*************************************************************************** + * Queue_AI_Normal -- Process all queued events. * + * * + * This is the "normal" version of the queue management routine. It does * + * the following: * + * - Transfers items in the OutList to the DoList * + * - Executes any commands in the DoList that are supposed to be done on * + * this frame # * + * - Cleans out the DoList * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=========================================================================*/ +static void Queue_AI_Normal(void) +{ + //------------------------------------------------------------------------ + // Move events from the OutList (events generated by this player) into the + // DoList (the list of events to execute). + //------------------------------------------------------------------------ + while (OutList.Count) { + OutList.First().IsExecuted = false; + if (!DoList.Add(OutList.First())) { + ; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + OutList.Next(); + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(1, PlayerPtr->Class->House, NULL, NULL, NULL, + NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_AI_Normal */ + + +/*************************************************************************** + * Queue_AI_Multiplayer -- Process all queued events. * + * * + * This is the network version of the queue management routine. It does * + * the following: * + * - If this is the 1st frame, waits for other systems to signal ready * + * - Generates a timing event, to allow the connection time to be dynamic * + * - Handles timing related to sending packets every 'n' frames * + * - Sends outgoing events * + * - Frame-syncs to the other systems (see below) * + * - Executes & cleans out the DoList * + * * + * The Frame-Sync'ing logic is the heart & soul of network play. It works * + * by ensuring that any system won't out-run the other system by more than * + * 'Session.MaxAhead' frames; this in turn ensures that a packet's * + * execution frame # won't have been passed by the time that packet is * + * received by all systems. * + * * + * To achieve this, the system must keep track of all other system's * + * current frame #'s; these are stored in an array called 'their_frame[]'. * + * However, because current frame #'s are sent in FRAMEINFO packets, which * + * don't require an ACK, and command packets are sent in packets requiring * + * an ACK, it's possible for a command packet to get lost, and the next * + * frame's FRAMEINFO packet to not get lost; the other system may then * + * advance past the frame # the command is to execute on! So, to prevent * + * this, all FRAMEINFO packets include a CommandCount field. This value * + * tells the other system how many events it should have received by this * + * time. This system can therefore keep track of how many commands it's * + * actually received, and compare it to the CommandCount field, to see if * + * it's missed an event packet. The # of events we've received from each * + * system is stored in 'their_recv[]', and the # events they say they've * + * sent is stored in 'their_sent[]'. * + * * + * Thus, two conditions must be met in order to advance to the next frame: * + * - Our current frame # must be < their_frame + Session.MaxAhead * + * - their_recv[i] must be >= their_sent[i] * + * * + * 'their_frame[] is updated by Process_Receive_Packet() * + * 'their_recv[] is updated by Process_Receive_Packet() * + * 'their_sent[] is updated by Process_Receive_Packet() * + * 'my_sent' is updated by this routine. * + * * + * The order of the arrays their_frame[] etc is the same order the * + * connections are created in. The Sender's ID is passed to * + * Connection_Index() to obtain the array index. * + * * + * The only routines allowed to pop up dialogs are: * + * Wait_For_Players() (only pops up the reconnect dialog) * + * Execute_DoList() (tells if out of sync, or packet recv'd too late) * + * * + * Sign-off's are detected by: * + * - Timing out while waiting for a packet * + * - Detecting that the other player is now at the score screen or * + * connection dialog (serial) * + * - If we see an EventClass::EXIT event on the private channel * + * * + * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has * + * the following properties: * + * - It compresses packets, so that the minimum number of bytes are * + * transmitted. Packets are compressed by extracting all info common to * + * the events into the packet header, and then sending only the bytes * + * relevant to each type of event. For instance, if 100 infantry guys * + * are told to move to the same location, the command itself & the * + * location will be included in the 1st movement command only; after * + * that, there will be a rep count then 99 infantry TARGET numbers, * + * identifying all the infantry told to move. * + * - The protocol also only sends packets out every 'n' frames. This cuts * + * the data rate dramatically. It means that 'Session.MaxAhead' must be * + * divisible by 'n'; also, the minimum value for 'Session.MaxAhead' is * + * 'n * 2', to give both sides some "breathing" room in case a FRAMEINFO * + * packet gets missed. * + * * + * Note: For synchronization-waiting loops (like waiting to hear from all * + * other players, waiting to advance to the next frame, etc), use * + * Net.Num_Connections() rather than Session.NumPlayers; this reflects the * + * actual # of connections, and can be "faked" into playing even when * + * there aren't any other players actually there. A typical example of * + * this is playing back a recorded game. For command-execution loops, use * + * Session.NumPlayers. This ensures all commands get executed, even if * + * there isn't a human generating those commands. * + * * + * The modem works a little differently from the network in this respect: * + * - The connection has to stay "alive" even if the other player exits to * + * the join dialog. This prevents each system from timing out & hanging * + * the modem up. Thus, packets are sent back & forth & just thrown away,* + * but each system knows the other is still there. Messages may be sent * + * between systems, though. * + * - Destroy_Null_Connection doesn't hang up the modem, so * + * Num_Connections() still reports a value of 1 even though the other * + * player has left. * + * - Any waits on Num_Connections() must also check for * + * Session.NumPlayers > 1, to keep from waiting forever if the other * + * guy has left * + * - Packets sent to a player who's left require no ACK * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_AI_Multiplayer(void) +{ + if(Session.Type == GAME_SKIRMISH) return; + + return; +#if (0)//PG + //........................................................................ + // Enums: + //........................................................................ + enum { + MIXFILE_RESEND_DELTA = 120, // ticks b/w resends + MIXFILE_TIMEOUT = 3600*2, // timeout waiting for mixfiles. + FRAMESYNC_DLG_TIME = (3*60), // time until displaying reconnect dialog + FRAMESYNC_TIMEOUT = (15*60), // timeout waiting for frame sync packet + }; + + int timeout_factor = (Session.Type == GAME_INTERNET) ? 6 : 1; + + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + ConnManClass *net; // ptr to access all multiplayer functions + EventClass packet; // for sending single frame-sync's + char *multi_packet_buf; // buffer for sending/receiving + int multi_packet_max; // max length of multi_packet_buf + + //........................................................................ + // Frame-sync'ing variables + //........................................................................ + static long + their_frame[MAX_PLAYERS - 1]; // other players' frame #'s + static unsigned short + their_sent[MAX_PLAYERS - 1]; // # cmds other player claims to have sent + static unsigned short + their_recv[MAX_PLAYERS - 1]; // # cmds actually received from others + static unsigned short + my_sent; // # cmds I've sent out + + //........................................................................ + // Timing variables + //........................................................................ + static CDTimerClass skip_crc; // to delay the CRC check +// static TCountDownTimerClass skip_crc; // to delay the CRC check + + //........................................................................ + // Other misc variables + //........................................................................ + int i; + RetcodeType rc; + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //------------------------------------------------------------------------ + // Initialize the packet buffer pointer & its max size + //------------------------------------------------------------------------ +#if (0)//PG + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + multi_packet_buf = NullModem.BuildBuf; + multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType); + net = &NullModem; + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + multi_packet_buf = Session.MetaPacket; + multi_packet_max = Session.MetaSize; + net = &Ipx; + } +#endif +#if(TEN) + else if (Session.Type == GAME_TEN) { + multi_packet_buf = Session.TenPacket; + multi_packet_max = Session.TenSize; + net = Ten; + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + multi_packet_buf = Session.MPathPacket; + multi_packet_max = Session.MPathSize; + net = MPath; + } +#endif + + //------------------------------------------------------------------------ + // Debug stuff + //------------------------------------------------------------------------ + Init_Queue_Mono(net); + Update_Queue_Mono (net, 0); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've just started a game, or loaded a multiplayer game, we must + // wait for all other systems to signal ready. + //------------------------------------------------------------------------ + if (Frame==0 || Session.LoadGame) { + //..................................................................... + // Initialize static locals + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) { + their_frame[i] = -1; + their_sent[i] = 0; + their_recv[i] = 0; + } + my_sent = 0; +#ifdef FIXIT_MULTI_SAVE + skip_crc = 32; +#else + skip_crc = Frame + 32; +#endif // FIXIT_MULTI_SAVE + for (i = 0; i < 32; i++) + CRC[i] = 0; + + //..................................................................... + // If we've loaded a saved game: + // - If this game was saved as the result of a lost connection, clear + // the CRC value so it will always match the other system's + // - Otherwise, use the GameCRC value, so we'll compare save-game files + // rather than scenario INI files + //..................................................................... + if (Session.LoadGame) { + if (Session.EmergencySave) + ScenarioCRC = 0; + else + ScenarioCRC = GameCRC; + } + + //..................................................................... + // Send our initial FRAMESYNC packet + //..................................................................... + Send_FrameSync(net, my_sent); + + //..................................................................... + // Wait for the other guys + //..................................................................... + rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor, + MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //..................................................................... + // Re-initialize frame numbers (in case somebody signed off while I was + // waiting for MIX files to load; we would have fallen through, but + // their frame # would still be -1). + //..................................................................... + for (i = 0; i < MAX_PLAYERS - 1; i++) + their_frame[i] = 0; + + //..................................................................... + // Reset the network response time computation, now that we're both + // sending data again (loading MIX files will have introduced + // deceptively large values). + //..................................................................... + net->Reset_Response_Time(); + + //..................................................................... + // Initialize the frame timers + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + Process_Send_Period(net);//, 1); + } + + //..................................................................... + // Turn off our special load-game flags + //..................................................................... + if (Session.LoadGame) { + Session.EmergencySave = false; + Session.LoadGame = false; + } + + } // end of Frame 0 wait + + //------------------------------------------------------------------------ + // Adjust connection timing parameters every 128 frames. + //------------------------------------------------------------------------ + + else if ( (Frame & 0x007f) == 0) { + // + // If we're using the new spiffy protocol, do proper timing handling. + // If we're the net "master", compute our desired frame rate & new + // 'MaxAhead' value. + // + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + + // + // All systems will transmit their required process time. + // + Generate_Process_Time_Event(net); + + // + // The game "host" will transmit timing adjustment events. + // + if (Session.Am_I_Master()) { + Generate_Real_Timing_Event(net, my_sent); + } + } else { + // + // For the older protocols, do the old broken timing handling. + // + Generate_Timing_Event(net, my_sent); + } + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (!Process_Send_Period(net)) { //, 0)) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + } + + //------------------------------------------------------------------------ + // Send our data packet(s); update my command-sent counter + //------------------------------------------------------------------------ + my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max, + Session.MaxAhead, my_sent); + + //------------------------------------------------------------------------ + // If this is our first time through, we're done. + //------------------------------------------------------------------------ + if (Frame==0) { + if (IsMono) { + MonoClass::Disable(); + } + return; + } + + //------------------------------------------------------------------------ + // Frame-sync'ing: wait until it's OK to advance to the next frame. + //------------------------------------------------------------------------ +#ifdef FIXIT_VERSION_3 + int iFramesyncTimeout; + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.iPlayerCount > 2 ) + // Shortened resync timeout for non-2 player games. + iFramesyncTimeout = 5 * 60; // One minute. + else + iFramesyncTimeout = FRAMESYNC_TIMEOUT; + + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + iFramesyncTimeout * (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#else + rc = Wait_For_Players (0, net, + (Session.MaxAhead << 3), + MAX ( net->Response_Time() * 3, FRAMESYNC_DLG_TIME*timeout_factor ), + FRAMESYNC_TIMEOUT* (2*timeout_factor), + multi_packet_buf, my_sent, their_frame, + their_sent, their_recv); +#endif + + if (rc != RC_NORMAL) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); +#ifdef WOLAPI_INTEGRATION + // New rule - if you cancel a waiting to reconnect dialog, you lose. + bReconnectDialogCancelled = ( rc == RC_CANCEL ); +#endif + } +#endif //WIN32 + if (rc == RC_NOT_RESPONDING) { + WWMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING); + } + else if (rc == RC_SCENARIO_MISMATCH) { + WWMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH); + } + else if (rc == RC_DOLIST_FULL) { + WWMessageBox().Process(TXT_QUEUE_FULL); + } + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Save the DoList to disk, if we're in "Record" mode + //------------------------------------------------------------------------ + if (Session.Record) { + Queue_Record(); + } + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (!Execute_DoList(Session.MaxPlayers, HOUSE_MULTI1, net, &skip_crc, + their_frame, their_sent, their_recv)) { +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + Register_Game_End_Time(); + } +#endif //WIN32 + Stop_Game(); + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(net); + + if (IsMono) { + MonoClass::Disable(); + } +#endif +} // end of Queue_AI_Multiplayer + + +/*************************************************************************** + * Wait_For_Players -- Waits for other systems to come on-line * + * * + * This routine performs the most critical logic in multiplayer; that of * + * synchronizing my frame number with those of the other systems. * + * * + * INPUT: * + * first_time 1 = 1st time this routine is called * + * net ptr to connection manager * + * resend_delta time (ticks) between FRAMESYNC resends * + * dialog_time time (ticks) until pop up a reconnect dialog * + * timeout time (ticks) until we give up the ghost * + * multi_packet_buf buffer to store packets in * + * my_sent # commands I've sent so far * + * their_frame array of their frame #'s * + * their_sent array of their CommandCount values * + * their_recv array of # cmds I've received from them * + * * + * OUTPUT: * + * RC_NORMAL OK to advance to the next frame * + * RC_CANCEL user hit 'Cancel' at the timeout countdown dlg * + * RC_NOT_RESPONDING other player(s) not responding * + * RC_SCENARIO_MISMATCH scenario's don't match (first_time only) * + * RC_DOLIST_FULL DoList was full * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Wait_For_Players(int first_time, ConnManClass *net, + int resend_delta, int dialog_time, int timeout, char *multi_packet_buf, + int my_sent, long *their_frame, unsigned short *their_sent, + unsigned short *their_recv) +{ + //........................................................................ + // Variables for sending, receiving & parsing packets: + //........................................................................ + EventClass *event; // event ptr for parsing incoming packets + int packetlen; // size of meta-packet sent, & received + int id; // id of other player + int messages_this_loop; // to limit # messages processed each loop + int message_limit; // max # messages we'll read each frame + + //........................................................................ + // Variables used only if 'first_time': + //........................................................................ + int num_ready; // # players signalling ready + + //........................................................................ + // Timing variables + //........................................................................ + CDTimerClass retry_timer; // time between FRAMESYNC packet resends + CDTimerClass dialog_timer; // time to pop up a dialog + CDTimerClass timeout_timer; // general-purpose timeout + + //........................................................................ + // Dialog variables + //........................................................................ + int reconnect_dlg = 0; // 1 = the reconnect dialog is displayed + + //........................................................................ + // Other misc variables + //........................................................................ + KeyNumType input; // for user input + int x,y; // for map input + RetcodeType rc; + + //------------------------------------------------------------------------ + // Wait to hear from all other players + //------------------------------------------------------------------------ + num_ready = 0; + retry_timer = resend_delta; // time to retry + dialog_timer = dialog_time; // time to show dlg + timeout_timer = timeout; // time to bail out + + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 2); + + //--------------------------------------------------------------------- + // Resend a frame-sync packet if longer than one propagation delay goes + // by; this prevents a "deadlock". If he's waiting for me to advance, + // but has missed my last few FRAMEINFO packets, I may be waiting for + // him to advance. Resending a FRAMESYNC ensures he knows what frame + // number I'm on. + //--------------------------------------------------------------------- + if (!retry_timer) { + retry_timer = resend_delta; // time to retry + Update_Queue_Mono (net, 3); + Send_FrameSync(net, my_sent); + } + + //--------------------------------------------------------------------- + // Service the connections + //--------------------------------------------------------------------- + net->Service(); + + //--------------------------------------------------------------------- + // Pop up a reconnect dialog if enough time goes by + //--------------------------------------------------------------------- + if (!dialog_timer && SpecialDialog==SDLG_NONE) { + if (reconnect_dlg == 0 && first_time == 0) { + FILE *fp; + int i; + HouseClass *housep; + + fp = fopen("recon.txt","wt"); + if (fp) { + fprintf(fp,"# Connections: %d\n",net->Num_Connections()); + fprintf(fp," My Frame #: %d\n",Frame); + for (i = 0; i < net->Num_Connections(); i++) { + housep = HouseClass::As_Pointer((HousesType)(net->Connection_ID(i))); + fprintf(fp,"%15s: Their Sent:%d Their Recv:%d Their Frame:%d\n", + housep->IniName, their_sent[i], their_recv[i], their_frame[i]); + } + fclose(fp); + } + +#ifdef WOLAPI_INTEGRATION + // "Reconnecting" dialog is about to be shown. + // At this point, begin wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->GameInfoCurrent.bTournament ) + pWolapi->Init_DisconnectPinging(); +#endif + } + + if (Process_Reconnect_Dialog(&timeout_timer, their_frame, // (Returns immediately.) + net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) { + return (RC_CANCEL); + } + reconnect_dlg = 1; + +#ifdef WOLAPI_INTEGRATION + // Continue wolapi "disconnect pinging", if appropriate. + if( Session.Type == GAME_INTERNET && pWolapi && pWolapi->bDoingDisconnectPinging ) + pWolapi->Pump_DisconnectPinging(); +#endif + } + + //--------------------------------------------------------------------- + // Exit if too much time goes by (the other system has crashed or + // bailed) + //--------------------------------------------------------------------- + if (!timeout_timer) { + //.................................................................. + // For the first-time run, just give up; something's wrong. + //.................................................................. + if (first_time) { + return (RC_NOT_RESPONDING); + } + //.................................................................. + // Otherwise, we're in the middle of a game; so, the modem & + // network must deal with a timeout differently. + //.................................................................. + else { + Update_Queue_Mono (net, 4); + + if (Handle_Timeout(net, their_frame, their_sent, their_recv)) { + Map.Flag_To_Redraw(true); // erase modem reconnect dialog + Map.Render(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + } +#ifdef FIXIT_MULTI_SAVE +#ifdef FIXIT_VERSION_3 + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) ) { +#else + else if ((Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) && + PlayingAgainstVersion != VERSION_RED_ALERT_104) { +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_NOT_RESPONDING, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; +//printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); +//Print_CRCs(NULL); +//printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Save_Game(-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); +//printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", +// Scen.RandomNumber.Count1, +// Scen.RandomNumber.Count2, +// Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); + } +#endif // FIXIT_MULTI_SAVE + else { + return (RC_NOT_RESPONDING); + } + } + } + + //--------------------------------------------------------------------- + // Check for an incoming message. We must still process commands + // even if 'first_time' is set, in case the other system got my 1st + // FRAMESYNC, but I didn't get his; he'll be at the next frame, and + // may be sending commands. + // We have to limit the number of incoming messages we handle; it's + // possible to go into an infinite loop processing modem messages. + // (This feature is disabled for Ten; we need to keep the TCP buffers + // clear, so we read all the packets we can every time.) + //--------------------------------------------------------------------- + messages_this_loop = 0; + message_limit = 5; + + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + message_limit = 9999; + } + + while ( (messages_this_loop++ < message_limit) && + net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) { + + Keyboard->Check(); + + Update_Queue_Mono (net, 5); + + /*.................................................................. + Get an event ptr to the incoming message + ..................................................................*/ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------ + // Special processing for a modem game: process SERIAL packets + //------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + rc = Process_Serial_Packet(multi_packet_buf, first_time); + //............................................................... + // SERIAL packet received & processed + //............................................................... + if (rc == RC_SERIAL_PROCESSED) { + net->Service(); + retry_timer = resend_delta; + dialog_timer = dialog_time; + timeout_timer = timeout; + continue; + } + //............................................................... + // other player has left the game + //............................................................... + else if (rc == RC_PLAYER_LEFT) { + if (first_time) { + num_ready++; + } + break; + } + //............................................................... + // Connection was lost + //............................................................... + else if (rc == RC_HUNG_UP) { +#ifdef FIXIT_MULTI_SAVE +#ifndef FIXIT_VERSION_3 + if (PlayingAgainstVersion != VERSION_RED_ALERT_104){ +#endif + if (WWMessageBox().Process (TXT_ASK_EMERGENCY_SAVE_HUNG_UP, + TXT_YES, TXT_NO, TXT_NONE) == 0) { + Session.EmergencySave = 1; + //printf("Saving emergency game; frame:%d, CRC:%d\n",Frame,GameCRC); + //Print_CRCs(NULL); + //printf("Before Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + //printf("After Save: Count1:%d, Count2:%d, Seed:%d\n", + // Scen.RandomNumber.Count1, + // Scen.RandomNumber.Count2, + // Scen.RandomNumber.Seed); + Session.EmergencySave = 0; + } + return (RC_CANCEL); +#ifndef FIXIT_VERSION_3 + }else{ + return (RC_NOT_RESPONDING); + } +#endif + +#else + return (RC_NOT_RESPONDING); +#endif // FIXIT_MULTI_SAVE + } + //............................................................... + // If it was any other type of serial packet, break + //............................................................... + else if (rc != RC_NORMAL) { + break; + } + } + + //------------------------------------------------------------------ + // Process the incoming packet + //------------------------------------------------------------------ + rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen, + their_frame, their_sent, their_recv); + //.................................................................. + // New player heard from + //.................................................................. + if (rc == RC_PLAYER_READY) { + num_ready++; + } + //.................................................................. + // Scenario's don't match + //.................................................................. + else if (rc == RC_SCENARIO_MISMATCH) { + return (RC_SCENARIO_MISMATCH); + } + //.................................................................. + // DoList was full + //.................................................................. + else if (rc == RC_DOLIST_FULL) { + return (RC_DOLIST_FULL); + } + + //.................................................................. + // Service the connection, to clean out the receive queues + //.................................................................. + net->Service(); + } + + //--------------------------------------------------------------------- + // Debug output + //--------------------------------------------------------------------- + Print_Framesync_Values(Frame, Session.MaxAhead, net->Num_Connections(), + their_recv, their_sent, my_sent); + + //--------------------------------------------------------------------- + // Attempt to advance to the next frame. + //--------------------------------------------------------------------- + //..................................................................... + // For the first-time run, just check to see if we've heard from + // everyone. + //..................................................................... + if (first_time) { + if (num_ready >= net->Num_Connections()) { + break; + } + } + //..................................................................... + // For in-game processing, we have to check their_sent, their_recv, + // their_frame, etc. + //..................................................................... + else { + if (Can_Advance(net, Session.MaxAhead, their_frame, their_sent, + their_recv)) { + break; + } + } + + //--------------------------------------------------------------------- + // Service game stuff. Servicing the map's input, and rendering the + // map, allows the map to scroll even though we're hung up waiting for + // packets. Don't do this if 'first_time' is set, since users could be + // waiting a very long time for all systems to load the scenario, and + // it gets frustrating being able to scroll around without doing + // anything. + //--------------------------------------------------------------------- + Call_Back(); + if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) { +#ifdef WIN32 + WWMouse->Erase_Mouse(&HidPage, TRUE); +#endif //WIN32 + Map.Input(input, x, y); + if (input) + Keyboard_Process(input); + Map.Render(); + } + + } /* end of while */ + + //------------------------------------------------------------------------ + // If the reconnect dialog was shown, force the map to redraw. + //------------------------------------------------------------------------ + if (reconnect_dlg) { + Map.Flag_To_Redraw(true); + Map.Render(); + } + + return (RC_NORMAL); + +} // end of Wait_For_Players + + +/*************************************************************************** + * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event * + * * + * This routine adjusts the connection timing on the local system; it also * + * optionally generates a RESPONSE_TIME event, to tell all systems to * + * dynamically adjust the current MaxAhead value. This allows both the * + * MaxAhead & the connection retry logic to have dynamic timing, to adjust * + * to varying line conditions. * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Generate_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + + // + // For now, TEN & MPATH don't measure the net's response time, so there's + // no point in adjusting our timing. Do nothing. + // + if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + return; + } + + //------------------------------------------------------------------------ + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, divide again by 4, assuming a game rate of 15 fps. + //------------------------------------------------------------------------ + resp_time = net->Response_Time(); + + //------------------------------------------------------------------------ + // Adjust my connection retry timing; only do this if I've sent out more + // than 5 commands, so I know I have a measure of the response time. + //------------------------------------------------------------------------ + if (my_sent > 5) { + + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + //..................................................................... + // If I'm the network "master", I'm also responsible for updating the + // MaxAhead value on all systems, so do that here too. + //..................................................................... + if (Session.Am_I_Master()) { + ev.Type = EventClass::RESPONSE_TIME; + //.................................................................. + // For multi-frame compressed events, the MaxAhead must be an even + // multiple of the FrameSendRate. + //.................................................................. + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + ev.Data.FrameInfo.Delay = max( ((((resp_time / 8) + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate), (Session.FrameSendRate * 2) ); + } + //.................................................................. + // For sending packets every frame, just use the 1-way connection + // response time. + //.................................................................. + else { + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + NETWORK_MIN_MAX_AHEAD ); + } + else if (Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + ev.Data.FrameInfo.Delay = max( (resp_time / 8), + MODEM_MIN_MAX_AHEAD ); + } + } + OutList.Add(ev); + } + } + +} // end of Generate_Timing_Event + + +/*************************************************************************** + * Generate_Real_Timing_Event -- Generates a TIMING event * + * * + * INPUT: * + * net ptr to connection manager * + * my_sent # commands I've sent out so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent) +{ + unsigned long resp_time; // connection response time, in ticks + EventClass ev; + int highest_ticks; + int i; + int specified_frame_rate; + int maxahead; + + // + // If we haven't sent out at least 5 guaranteed-delivery packets, don't + // bother trying to measure our connection response time; just return. + // + if (my_sent < 5) { + return; + } + + // + // Find the highest processing time we have stored + // + highest_ticks = 0; + for (i = 0; i < Session.Players.Count(); i++) { + + // + // If we haven't heard from all systems yet, bail out. + // + if (Session.Players[i]->Player.ProcessTime == -1) { + return; + } + if (Session.Players[i]->Player.ProcessTime > highest_ticks) { + highest_ticks = Session.Players[i]->Player.ProcessTime; + } + } + + // + // Compute our "desired" frame rate as the lower of: + // - What the user has dialed into the options screen + // - What we're really able to run at + // + if (highest_ticks == 0) { + Session.DesiredFrameRate = 60; + } else { + Session.DesiredFrameRate = 60 / highest_ticks; + } + + if (Options.GameSpeed == 0) { + specified_frame_rate = 60; + } else { + specified_frame_rate = 60 / Options.GameSpeed; + } + + Session.DesiredFrameRate = MIN (Session.DesiredFrameRate, specified_frame_rate); + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Compute our new 'MaxAhead' value, based upon the response time of our + // connection and our desired frame rate. + // 'MaxAhead' in frames is: + // + // (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec) + // + // resp_time is divided by 2 because, as reported, it represents a round- + // trip, and we only want to use a one-way trip. + // + maxahead = (resp_time * Session.DesiredFrameRate) / (2 * 60); + + // + // Now, we have to round 'maxahead' so it's an even multiple of our + // send rate. It also must be at least thrice the FrameSendRate. + // (Isn't "thrice" a cool word?) + // + maxahead = ((maxahead + Session.FrameSendRate - 1) / Session.FrameSendRate) * Session.FrameSendRate; + maxahead = MAX (maxahead, (int)Session.FrameSendRate * 3); + + ev.Type = EventClass::TIMING; + ev.Data.Timing.DesiredFrameRate = Session.DesiredFrameRate; + ev.Data.Timing.MaxAhead = maxahead; + + OutList.Add(ev); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + +} + + +/*************************************************************************** + * Generate_Process_Time_Event -- Generates a PROCESS_TIME event * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/02/1996 BRR : Created. * + *=========================================================================*/ +static void Generate_Process_Time_Event(ConnManClass *net) +{ + EventClass ev; + int avgticks; + unsigned long resp_time; // connection response time, in ticks + + // + // Measure the current connection response time. This time will be in + // 60ths of a second, and represents full round-trip time of a packet. + // To convert to one-way packet time, divide by 2; to convert to game + // frames, ....uh.... + // + resp_time = net->Response_Time(); + + // + // Adjust my connection retry timing. These values set the retry timeout + // to just over one round-trip time, the 'maxretries' to -1, and the + // connection timeout to allow for about 4 retries. + // + //net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + if (Session.Type == GAME_INTERNET) { + net->Set_Timing (resp_time + 10, -1, ((resp_time + 10)* 8)+15); + }else{ + net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15); + } + + + if (IsMono) { + MonoClass::Enable(); + Mono_Set_Cursor(0,23); + Mono_Printf("Processing Ticks:%03d Frames:%03d\n", Session.ProcessTicks,Session.ProcessFrames); + MonoClass::Disable(); + } + + avgticks = Session.ProcessTicks / Session.ProcessFrames; + + ev.Type = EventClass::PROCESS_TIME; + ev.Data.ProcessTime.AverageTicks = avgticks; + OutList.Add(ev); + + Session.ProcessTicks = 0; + Session.ProcessFrames = 0; +} + + +/*************************************************************************** + * Process_Send_Period -- timing for sending packets every 'n' frames * + * * + * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only. * + * It determines if it's time to send a packet or not. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * 1 = it's time to send a packet; 0 = don't send a packet this frame. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Send_Period(ConnManClass *net) //, int init) +{ + //------------------------------------------------------------------------ + // If the current frame # is not an even multiple of 'FrameSendRate', then + // it's not time to send a packet; just return. + //------------------------------------------------------------------------ + if (Frame != (((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate) ) { + + net->Service(); + + if (IsMono) { + MonoClass::Disable(); + } + + return (0); + } + + return (1); + + +} // end of Process_Send_Period + + +/*************************************************************************** + * Send_Packets -- sends out events from the OutList * + * * + * This routine computes how many events can be sent this frame, and then * + * builds the "meta-packet" & sends it. * + * * + * The 'cap' value is the max # of events we can send. Ideally, it should * + * be based upon the bandwidth of our connection. Currently, it's just * + * hardcoded to prevent the modem from having to resend "too much" data, * + * which is about 200 bytes per frame. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer to store packets in * + * multi_packet_max max size of multi_packet_buf * + * max_ahead current game MaxAhead value * + * my_sent # commands I've sent this game * + * * + * OUTPUT: * + * # events sent, NOT including the FRAMEINFO event * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Send_Packets(ConnManClass *net, char *multi_packet_buf, + int multi_packet_max, int max_ahead, int my_sent) +{ + int cap; // max # events to send, NOT including FRAMEINFO event + int do_once; // true: only go through packet loop once + int ack_req; // 0 = no ack required on outgoing packet + int packetlen; // size of meta-packet sent + + //------------------------------------------------------------------------ + // Determine how many events it's OK to send this frame. + //------------------------------------------------------------------------ + //........................................................................ + // If we have 4 or more packets queued for sending, don't add any more + // this frame. + //........................................................................ + if (net->Private_Num_Send() >= 4) { + cap = 0; + do_once = 1; + } + //........................................................................ + // If there are 2 or more packets queued, the entire packet we send must + // fit within a single ComQueue buffer, so limit # events to 5. + // (The Modem connection manager has a max buffer size of 200 bytes, which + // is large enough for 6 uncompressed events, which leaves room for 5 + // events plus a FRAMEINFO.) + //........................................................................ + else if (net->Private_Num_Send() >= 2) { + cap = 5; + do_once = 1; + + } + //........................................................................ + // Otherwise, just send all events in the OutList + //........................................................................ + else { + cap = OutList.Count; + do_once = 0; + } + + //........................................................................ + // Make sure we aren't sending more events than are in the OutList + //........................................................................ + if (cap > OutList.Count) { + cap = OutList.Count; + } + + //........................................................................ + // Make sure we don't send so many events that our DoList fills up + //........................................................................ + if (cap > (MAX_EVENTS * 64) - DoList.Count) { + cap = (MAX_EVENTS * 64) - DoList.Count; + } + + // + // 10/21/96 5:12PM - ST + // + if (Session.Type == GAME_INTERNET || Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM){ + cap = OutList.Count; + do_once = 0; + } + + //------------------------------------------------------------------------ + // Build our meta-packet & transmit it. + //------------------------------------------------------------------------ + while (1) { + Keyboard->Check(); + + Update_Queue_Mono (net, 1); + + //..................................................................... + // If there are no commands this frame, we'll just be sending a FRAMEINFO + // packet; no ack is required. For the modem's sake, check + // Session.NumPlayers; no ACK is needed if we're just sending to someone + // who's left the game. + //..................................................................... + if (cap == 0 || OutList.Count == 0 || Session.NumPlayers == 1) { + ack_req = 0; + } + else { + ack_req = 1; + } + + //..................................................................... + // Build & send out our message + //..................................................................... + packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max, + max_ahead, my_sent, cap); + net->Send_Private_Message (multi_packet_buf, packetlen, ack_req); + + //..................................................................... + // Call Service() to actually send the packet + //..................................................................... + net->Service(); + + //..................................................................... + // Stop if there's no more data to send, or if our send queue is + // filling up. + //..................................................................... + if (OutList.Count == 0 || do_once) { + break; + } + } + + return (cap); + +} // end of Send_Packets + + +/*************************************************************************** + * Send_FrameSync -- Sends a FRAMESYNC packet * + * * + * This routine is used to periodically remind the other systems that * + * we're still here, and to tell them what frame # we're on, in case * + * they've missed my FRAMEINFO packets. * + * * + * INPUT: * + * net ptr to connection manager * + * cmd_count # commands I've sent so far * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Send_FrameSync(ConnManClass *net, int cmd_count) +{ + EventClass packet; + + //------------------------------------------------------------------------ + // Build a frame-sync event to send. FRAMESYNC packets contain a + // scenario-based CRC rather than a game-state-based CRC, to let the + // games compare scenario CRC's on startup. + //------------------------------------------------------------------------ + memset (&packet, 0, sizeof(EventClass)); + packet.Type = EventClass::FRAMESYNC; + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + packet.Frame = ((Frame + Session.MaxAhead + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + packet.Frame = Frame + Session.MaxAhead; + } + packet.ID = PlayerPtr->ID; + packet.Data.FrameInfo.CRC = ScenarioCRC; + packet.Data.FrameInfo.CommandCount = cmd_count; + packet.Data.FrameInfo.Delay = Session.MaxAhead; + + //------------------------------------------------------------------------ + // Send the event. For modem, this just sends to the other player; + // for network, it sends to everyone we're connected to. + //------------------------------------------------------------------------ + + net->Send_Private_Message (&packet, (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)), 0 ); + + return; + +} // end of Send_FrameSync + + +/*************************************************************************** + * Process_Receive_Packet -- processes an incoming packet * + * * + * This routine receives a packet from another system, adds it to our * + * execution queue (the DoList), and updates my arrays of their frame #, * + * their commands-sent, and their commands-received. * + * * + * INPUT: * + * net ptr to connection manager * + * multi_packet_buf buffer containing packet(s) to parse * + * id id of sender * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * RC_NORMAL: nothing unusual happened, although * + * their_sent or their_recv may have been * + * altered * + * RC_PLAYER_READY: player has been heard from for the 1st time; * + * this presumes that his original * + * 'their_frame[]' value was -1 when this * + * routine was called * + * RC_SCENARIO_MISMATCH: FRAMEINFO scenario CRC doesn't match; * + * normally only applies after loading a new * + * scenario or save-game * + * RC_DOLIST_FULL: fatal error; unable to add events to DoList * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Receive_Packet(ConnManClass *net, + char *multi_packet_buf, int id, int packetlen, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + EventClass *event; + int index; + RetcodeType retcode = RC_NORMAL; + int i; + + //------------------------------------------------------------------------ + // Get an event ptr to the incoming message + //------------------------------------------------------------------------ + event = (EventClass *)multi_packet_buf; + + //------------------------------------------------------------------------ + // Get the index of the sender + //------------------------------------------------------------------------ + index = net->Connection_Index(id); + + //------------------------------------------------------------------------ + // Compute the other player's frame # (at the time this packet was sent) + //------------------------------------------------------------------------ + if (their_frame[index] < + (int)(event->Frame - event->Data.FrameInfo.Delay)) { + + //..................................................................... + // If the original frame # for this player is -1, it means we've heard + // from this player for the 1st time; return the appropriate value. + //..................................................................... + if (their_frame[index]==-1) { + retcode = RC_PLAYER_READY; + } + + their_frame[index] = event->Frame - event->Data.FrameInfo.Delay; + } + + //------------------------------------------------------------------------ + // Extract the other player's CommandCount. This count will include + // the commands in this packet, if there are any. + //------------------------------------------------------------------------ + if (event->Data.FrameInfo.CommandCount > their_sent[index]) { + + if ( abs(their_sent[index] - event->Data.FrameInfo.CommandCount) > 500) { + FILE *fp; + fp = fopen("badcount.txt","wt"); + if (fp) { + fprintf(fp,"Event Type:%s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame:%d ID:%d IsExec:%d\n", + event->Frame, + event->ID, + event->IsExecuted); + if (event->Type != EventClass::FRAMEINFO) { + fprintf(fp,"Wrong Event Type!\n"); + } else { + fprintf(fp,"CRC:%x CommandCount:%d Delay:%d\n", + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + } + } + + their_sent[index] = event->Data.FrameInfo.CommandCount; + } + + if (Debug_Print_Events) { + if (event->Type == EventClass::FRAMESYNC) { + printf("(%d) Received FRAMESYNC: ", Frame); + } else { + printf("(%d) Received FRAMEINFO: ", Frame); + } + printf("EvFrame:%d ID:%d CRC:%x CmdCount:%d Delay:%d\n", + event->Frame, + event->ID, + event->Data.FrameInfo.CRC, + event->Data.FrameInfo.CommandCount, + event->Data.FrameInfo.Delay); + } + + //------------------------------------------------------------------------ + // If this packet was not a FRAMESYNC packet: + // - Add the events in it to our DoList + // - Increment our commands-received counter by the number of non- + // FRAMEINFO packets received + //------------------------------------------------------------------------ + if (event->Type != EventClass::FRAMESYNC) { + //..................................................................... + // Break up the packet into its component events. A returned packet + // count of -1 indicates a fatal queue-full error. + //..................................................................... + i = Breakup_Receive_Packet( multi_packet_buf, packetlen); + if (i==-1) { + return (RC_DOLIST_FULL); + } + //..................................................................... + // Compute the actual # commands in the packet by subtracting off the + // FRAMEINFO event + //..................................................................... + if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) { + i--; + } + + their_recv[index] += i; + } + + //------------------------------------------------------------------------ + // If the event was a FRAMESYNC packet, there will be no commands to add, + // but we must check the ScenarioCRC value. + //------------------------------------------------------------------------ + else if (event->Data.FrameInfo.CRC != ScenarioCRC) { + return (RC_SCENARIO_MISMATCH); + } + + return (retcode); + +} // end of Process_Receive_Packet + + +/*************************************************************************** + * Process_Serial_Packet -- Handles an incoming serial packet * + * * + * This routine is needed because the modem classes don't support a * + * "global channel" like the network classes do, but that functionality is * + * still needed for modem communications. Specifically, the modem dialogs * + * transmit their own special packets back & forth, and messages are sent * + * using a special packet type. Thus, we have to call this routine when * + * we receive a modem packet, to allow it to process messages & dialog * + * packets. * + * * + * INPUT: * + * multi_packet_buf packet buffer to process * + * first_time 1 = this is the 1st game frame * + * * + * OUTPUT: * + * RC_NORMAL: this wasn't a SERIAL-type packet * + * RC_SERIAL_PROCESSED: this was a SERIAL-type packet, and was * + * processed; the other player is still connected, * + * even if he's not in the game. * + * RC_PLAYER_LEFT: other player has left the game * + * RC_HUNG_UP: we're getting our own packets back; thus, the * + * modem is mirroring our packets, which means the * + * modem hung up! * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static RetcodeType Process_Serial_Packet(char *multi_packet_buf, + int first_time) +{ + multi_packet_buf; + first_time; + return (RC_NORMAL); +#if (0) // ST - 5/13/2019 + SerialPacketType *serial_packet; // for parsing serial packets + int player_gone; + EventClass *event; + + //------------------------------------------------------------------------ + // Determine if this packet means that the other player has left the game + //------------------------------------------------------------------------ + serial_packet = (SerialPacketType *)multi_packet_buf; + player_gone = 0; + //........................................................................ + // On Frame 0, only a SIGN_OFF means the other player left; the other + // packet types may be left over from a previous session. + //........................................................................ + if (first_time) { + if (serial_packet->Command == SERIAL_SIGN_OFF) { + player_gone = 1; + } + } + //........................................................................ + // On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means + // the other player is gone. + //........................................................................ + else { + if (serial_packet->Command == SERIAL_SIGN_OFF || + serial_packet->Command == SERIAL_TIMING || + serial_packet->Command == SERIAL_SCORE_SCREEN ) { + player_gone = 1; + } + } + if (player_gone) { + Destroy_Null_Connection(serial_packet->ScenarioInfo.Color, 0); + return (RC_PLAYER_LEFT); + } + + //------------------------------------------------------------------------ + // Process an incoming message + //------------------------------------------------------------------------ + if (serial_packet->Command == SERIAL_MESSAGE) { + if (!Session.Messages.Concat_Message(serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, Rule.MessageDelay * TICKS_PER_MINUTE)) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Appears to do nothing + char *ptr = &serial_packet->Message.Message[0]; + if (!strncmp(ptr,"SECRET UNITS ON ",15) && NewUnitsEnabled) { + Enable_Secret_Units(); + } +#endif + Session.Messages.Add_Message (serial_packet->Name, + serial_packet->ID, serial_packet->Message.Message, + (PlayerColorType)serial_packet->ID, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + + Sound_Effect(VOC_INCOMING_MESSAGE); + } + + //..................................................................... + // Save this message in our last-message buffer + //..................................................................... + if (strlen (serial_packet->Message.Message)) { + strcpy (Session.LastMessage, serial_packet->Message.Message); + } + + //..................................................................... + // Tell the map to do a partial update (just to force the + // messages to redraw). + //..................................................................... + //Map.Flag_To_Redraw(false); + Map.Flag_To_Redraw(true); + return (RC_SERIAL_PROCESSED); + } + + //------------------------------------------------------------------------ + // Any other SERIAL-type packet means the other player is still there; + // throw them away, but let the caller know the connection is OK. + //------------------------------------------------------------------------ + if ( (serial_packet->Command >= SERIAL_CONNECT && + serial_packet->Command < SERIAL_LAST_COMMAND) || + (serial_packet->Command >= SERIAL_REQ_SCENARIO && + serial_packet->Command <= SERIAL_NO_SCENARIO) || + Session.NumPlayers == 1) { + return (RC_SERIAL_PROCESSED); + } + + //........................................................................ + // are we getting our own packets back?? + //........................................................................ + event = (EventClass *)multi_packet_buf; + + if (event->Type <= EventClass::EMPTY || event->Type >= EventClass::LAST_EVENT) return (RC_SERIAL_PROCESSED); + + if (event->ID == PlayerPtr->ID) { + return (RC_HUNG_UP); + } + + return (RC_NORMAL); +#endif +} // end of Process_Serial_Packet + + +/*************************************************************************** + * Can_Advance -- determines if it's OK to advance to the next frame * + * * + * This routine uses the current values stored in their_frame[], * + * their_send[], and their_recv[] to see if it's OK to advance to the next * + * game frame. We must not advance if: * + * - If our frame # would be too far ahead of the slowest player (the * + * lowest their_frame[] value). "Too far" means * + * (Frame >= their_frame + MaxAhead). * + * - our current command count doesn't match the sent command count of one * + * other player (meaning that we've missed a command packet from that * + * player, and thus the frame # we're receiving from him may be due to a * + * FRAMEINFO packet sent later than the command, so we shouldn't use * + * this frame # to see if we should advance; we should wait until we * + * have all the commands before we advance. * + * * + * Of course, this routine assumes the values in their_frame[] etc are * + * kept current by the caller. * + * * + * INPUT: * + * net ptr to connection manager * + * max_ahead max frames ahead * + * their_frame array of their frame #'s * + * their_sent array of their sent command count * + * their_recv array of their # received commands * + * * + * OUTPUT: * + * 1 = OK to advance; 0 = not OK * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + long their_oldest_frame; // other players' oldest frame # + int count_ok; // true = my cmd count matches theirs + int i; + + //------------------------------------------------------------------------ + // Special case for modem: if the other player has left, go ahead and + // advance to the next frame; don't wait on him. + //------------------------------------------------------------------------ + if (Session.NumPlayers == 1) { + return (1); + } + + //------------------------------------------------------------------------ + // Find the oldest frame # in 'their_frame' + //------------------------------------------------------------------------ + their_oldest_frame = Frame + 1000; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < their_oldest_frame) + their_oldest_frame = their_frame[i]; + } + + //------------------------------------------------------------------------ + // I can advance to the next frame IF: + // 1) I'm less than a one-way propagation delay ahead of the other + // players' frame numbers, AND + // 2) their_recv[i] >= their_sent[i] (ie I've received all the commands + // the other players have sent so far). + //------------------------------------------------------------------------ + count_ok = 1; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_recv[i] < their_sent[i]) { + count_ok = 0; + break; + } + } + if (count_ok && (Frame < (their_oldest_frame + max_ahead))) { + return (1); + } + + return (0); + +} // end of Can_Advance + + +/*************************************************************************** + * Process_Reconnect_Dialog -- processes the reconnection dialog * + * * + * This routine [re]draws the reconnection dialog; if 'reconn' is set, * + * it tells the user who we're trying to reconnect to; otherwise, is just * + * says something generic like "Waiting for connections". * + * * + * INPUT: * + * timeout_timer ptr to count down timer, showing time remaining * + * their_frame array of other players' frame #'s * + * num_conn # connections in 'their_frame' * + * reconn 1 = reconnect, 0 = waiting for first-time connection * + * fresh 1 = draw from scratch, 0 = only update time counter * + * * + * OUTPUT: * + * 1 = user wants to cancel, 0 = not * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Process_Reconnect_Dialog(CDTimerClass *timeout_timer, + long *their_frame, int num_conn, int reconn, int fresh) +{ + static int displayed_time = 0; // time value currently displayed + int new_time; + int oldest_index; // index of person requiring a reconnect + int i,j; + + //------------------------------------------------------------------------ + // Convert the timer to seconds + //------------------------------------------------------------------------ + new_time = *timeout_timer / 60; + + //------------------------------------------------------------------------ + // If the timer has changed, or 'fresh' is set, redraw the dialog + //------------------------------------------------------------------------ + if (fresh || (new_time != displayed_time)) { + //..................................................................... + // Find the index of the person we're trying to reconnect to + //..................................................................... + if (reconn) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < num_conn; i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + } + Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time); + } + displayed_time = new_time; + + //........................................................................ + // If user hits ESC, bail out + //........................................................................ + if (Keyboard->Check()) { + if (Keyboard->Get() == KN_ESC) { + return (1); + } + } + + return (0); + +} // end of Process_Reconnect_Dialog + + +/*************************************************************************** + * Handle_Timeout -- handles a timeout in the wait-for-players loop * + * * + * This routine "gracefully" handles a timeout in the frame-sync loop. * + * The timeout must be handled differently by a modem game or network * + * game. * + * * + * The modem game must detect if the other player is still connected * + * physically, even if he's not playing the game any more; if so, this * + * routine returns an OK status. If the other player isn't even * + * physically connected, an error is returned. * + * * + * The network game must find the connection that's causing the timeout, * + * and destroy it. The game continues, even if there are no more human * + * players left. * + * * + * INPUT: * + * net ptr to connection manager * + * their_frame array containing frame #'s of other players * + * their_sent array containing command count of other players * + * their_recv array containing # recv'd cmds from other players * + * * + * OUTPUT: * + * 1 = it's OK; reset timeout timers & keep processing * + * 0 = game over, man * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Handle_Timeout(ConnManClass *net, long *their_frame, + unsigned short *their_sent, unsigned short *their_recv) +{ + int oldest_index; // index of person requiring a reconnect + int i,j; + int id; + + //------------------------------------------------------------------------ + // For modem, attempt to reconnect; if that fails, save the game & bail. + //------------------------------------------------------------------------ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + if ( net->Num_Connections() ) { + if (!Reconnect_Modem()) { +#ifndef FIXIT_MULTI_SAVE + //............................................................... + // Set 'Session.EmergencySave', so when this game is loaded, we + // won't check the CRC of the game state (this system & the + // other may be on different frames, in which case the CRC + // won't match). + //............................................................... + Session.EmergencySave = 1; + //Save_Game (-1, (char *)Text_String(TXT_MULTIPLAYER_GAME)); + Session.EmergencySave = 0; +#endif // FIXIT_MULTI_SAVE + return (0); + } else { + return (1); + } + } + } + + //------------------------------------------------------------------------ + // For network, destroy the oldest connection + //------------------------------------------------------------------------ + else if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || Session.Type == GAME_MPATH) { + j = 0x7fffffff; + oldest_index = 0; + for (i = 0; i < net->Num_Connections(); i++) { + if (their_frame[i] < j) { + j = their_frame[i]; + oldest_index = i; + } + } + + id = net->Connection_ID(oldest_index); +#ifdef WIN32 + /* + ** Send the game statistics packet now if the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + ConnectionLost = true; + Send_Statistics_Packet(); // Disconnect, and I'll be the only one left. + } +#endif //WIN32 + + if (id != ConnManClass::CONNECTION_NONE) { + for (i = oldest_index; i < net->Num_Connections() - 1; i++) { + their_frame[i] = their_frame[i+1]; + their_sent[i] = their_sent[i+1]; + their_recv[i] = their_recv[i+1]; + } + if (Session.Type == GAME_IPX || Session.Type == GAME_INTERNET) { + Destroy_Connection(id,1); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(id,1); + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(id,1); + } +#endif + } + } + + return (1); + +} // end of Handle_Timeout + + +/*************************************************************************** + * Stop_Game -- stops the game * + * * + * This routine clears any global flags that need it, in preparation for * + * halting the game prematurely. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/22/1995 BRR : Created. * + *=========================================================================*/ +static void Stop_Game(void) +{ + Session.LoadGame = false; + Session.EmergencySave = false; + GameActive = 0; + if (IsMono) { + MonoClass::Disable(); + } +#ifdef WIN32 + if (Session.Type == GAME_INTERNET){ + ConnectionLost = true; + Send_Statistics_Packet(); // Stop_Game() + } +#endif //WIN32 + + return; + +} // end of Stop_Game + + +/*************************************************************************** + * Build_Send_Packet -- Builds a big packet from a bunch of little ones. * + * * + * This routine takes events from the OutList, and puts them into a * + * "meta-packet", which is transmitted to all systems we're connected to. * + * Also, these events are added to our own DoList. * + * * + * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this * + * tells the other systems what frame we're on, as well as serving as a * + * standard packet header. * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * num_cmds value to use for the CommandCount field * + * cap max # events to send * + * * + * OUTPUT: * + * new size of packet * + * * + * WARNINGS: * + * 'num_cmds' should be the total of of commands, including all those sent * + * this frame! * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Build_Send_Packet(void *buf, int bufsize, int frame_delay, + int num_cmds, int cap) +{ + int size = 0; + EventClass *finfo; + + //------------------------------------------------------------------------ + // All events start with a FRAMEINFO event; fill this part in. + //------------------------------------------------------------------------ + //........................................................................ + // Set the event type + //........................................................................ + finfo = (EventClass *)buf; + finfo->Type = EventClass::FRAMEINFO; + //........................................................................ + // Set the frame to execute this event on; this is protocol-specific + //........................................................................ + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + finfo->Frame = ((Frame + frame_delay + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + } + else { + finfo->Frame = Frame + frame_delay; + } + //........................................................................ + // Fill in the rest of the event + //........................................................................ + finfo->ID = PlayerPtr->ID; + finfo->Data.FrameInfo.CRC = GameCRC; + finfo->Data.FrameInfo.CommandCount = num_cmds; + finfo->Data.FrameInfo.Delay = frame_delay; + + //------------------------------------------------------------------------ + // Initialize the # of bytes processed; this is protocol-specific + //------------------------------------------------------------------------ + if (Session.CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) { + size += sizeof(EventClass); + } + else { + size += (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)); + } + + //------------------------------------------------------------------------ + // Transfer all events from the OutList into the DoList, building our + // packet while we go. + //------------------------------------------------------------------------ + switch (Session.CommProtocol) { + //..................................................................... + // COMM_PROTOCOL_SINGLE_NO_COMP: + // We'll send at least a FRAMEINFO every single frame, no compression + //..................................................................... + case (COMM_PROTOCOL_SINGLE_NO_COMP): + size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // COMM_PROTOCOL_SINGLE_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every frame. + // COMM_PROTOCOL_MULTI_E_COMP: + // Compress a group of packets into our send buffer; send out + // compressed packets every 'n' frames. + //..................................................................... + case (COMM_PROTOCOL_SINGLE_E_COMP): + case (COMM_PROTOCOL_MULTI_E_COMP): + size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap); + break; + + //..................................................................... + // Default: We have no idea what to do, so do nothing. + //..................................................................... + default: + size = 0; + break; + } + + return( size ); + +} /* end of Build_Send_Packet */ + + +/*************************************************************************** + * Add_Uncompressed_Events -- adds uncompressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + int ev_size; // size of event we're adding + + //------------------------------------------------------------------------ + // Loop until there are no more events, or we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + if (OutList.First().Type==EventClass::ADDPLAYER) { + ev_size = sizeof(EventClass) + OutList.First().Data.Variable.Size; + } + else { + ev_size = sizeof(EventClass); + } + //..................................................................... + // Will the next event exceed the size of the buffer? If so, break. + //..................................................................... + if ( (size + ev_size) > bufsize ) { + return (size); + } + + //..................................................................... + // Set the event's frame delay + //..................................................................... + OutList.First().Frame = Frame + frame_delay; + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList + // event. If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if (!DoList.Add(OutList.First())) { + return (size); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //..................................................................... + // Add event to the send packet + //..................................................................... + if (OutList.First().Type==EventClass::ADDPLAYER) { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + } + else { + memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) ); + size += sizeof(EventClass); + } + + //..................................................................... + // Increment our event counter; delete the last event from the queue + //..................................................................... + num++; + OutList.Next(); + } + + return (size); + +} // end of Add_Uncompressed_Events + + +/*************************************************************************** + * Add_Compressed_Events -- adds an compressed events to a packet * + * * + * INPUT: * + * buf buffer to store packet in * + * bufsize max size of buffer * + * frame_delay desired frame delay to attach to all outgoing packets * + * size reference to current packet size * + * cap max # events to process * + * * + * OUTPUT: * + * new size value * + * * + * WARNINGS: * + * This routine MUST check to be sure it doesn't overflow the buffer. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, + int size, int cap) +{ + int num = 0; // # of events processed + EventClass::EventType eventtype; // type of event being compressed + EventClass prevevent; // last event processed + int datasize; // size of element plucked from event union + int storedsize; // actual # bytes stored from event + unsigned char *unitsptr = NULL; // ptr to buffer pos to store mega. rep count + unsigned char numunits = 0; // megamission rep count value + bool missiondup = false; // flag: is this event a megamission repeat? + + //------------------------------------------------------------------------ + // clear previous event + //------------------------------------------------------------------------ + memset (&prevevent, 0, sizeof(EventClass)); + + if (Debug_Print_Events) { + printf("\n(%d) Building Send Packet\n", Frame); + } + + //------------------------------------------------------------------------ + // Loop until there are no more events, we've processed our max # of + // events, or the buffer is full. + //------------------------------------------------------------------------ + while (OutList.Count && (num < cap)) { + + Keyboard->Check(); + + eventtype = OutList.First().Type; + datasize = EventClass::EventLength[ eventtype ]; + //..................................................................... + // For a variable-sized event, pull the size from the event; otherwise, + // the size will be the data element size plus the event type value. + // (The other data elements in the event, Frame, ID, etc, are stored + // in the packet header.) + //..................................................................... + if (eventtype==EventClass::ADDPLAYER) { + storedsize = datasize + sizeof (EventClass::EventType) + + OutList.First().Data.Variable.Size; + } + else { + storedsize = datasize + sizeof (EventClass::EventType); + } + + //..................................................................... + // MegaMission compression: MegaMissions are stored as: + // EventType + // Rep Count + // MegaMission structure (event # 1 only) + // Whom #2 + // Whom #3 + // Whom #4 + // ... + // Whom #n + //..................................................................... + if (prevevent.Type == EventClass::MEGAMISSION) { + //.................................................................. + // If previous & current events are both MegaMissions: + //.................................................................. + if (eventtype == EventClass::MEGAMISSION) { + //............................................................... + // If the Mission, Target, & Destination are the same, compress + // the events into one: + // - Change datasize to the size of the 'Whom' field only + // - set total # bytes to store to the size of the 'Whom' only + // - increment the MegaMission rep count + // - set the MegaMission rep flag + //............................................................... + if (OutList.First().Data.MegaMission.Mission == + prevevent.Data.MegaMission.Mission && + OutList.First().Data.MegaMission.Target == + prevevent.Data.MegaMission.Target && + OutList.First().Data.MegaMission.Destination == + prevevent.Data.MegaMission.Destination) { +#if (0)//PG + if (Debug_Print_Events) { + printf(" adding Whom:%x (%x) Mission:%s Target:%x (%x) Dest:%x (%x)\n", + OutList.First().Data.MegaMission.Whom.As_TARGET(), + OutList.First().Data.MegaMission.Whom, + MissionClass::Mission_Name(OutList.First().Data.MegaMission.Mission), + OutList.First().Data.MegaMission.Target.As_TARGET(), + OutList.First().Data.MegaMission.Target, + OutList.First().Data.MegaMission.Destination.As_TARGET(), + OutList.First().Data.MegaMission.Destination); + } +#endif + datasize = sizeof(prevevent.Data.MegaMission.Whom); + storedsize = datasize; + numunits++; + missiondup = true; + } + //............................................................... + // Data doesn't match; start a new run of MegaMissions: + // - Store previous MegaMission rep count + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //............................................................... + else { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + *unitsptr = numunits; + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + } + //.................................................................. + // Previous event was a MegaMission, but this one isn't: end the + // run of MegaMissions: + // - Store previous MegaMission rep count + // - Clear variables + //.................................................................. + else { + *unitsptr = numunits; // save # events in our run + unitsptr = NULL; // init other values + numunits = 0; + missiondup = false; + } + } + + //..................................................................... + // The previous event is not a MEGAMISSION but the current event is: + // Set up a new run of MegaMissions: + // - Init 'unitsptr' to buffer pos after next EventType + // - set total # bytes to store to 'datasize' + sizeof(EventType) + + // sizeof (numunits) + // - init the MegaMission rep count to 1 + // - clear the MegaMission rep flag + //..................................................................... + else if (eventtype == EventClass::MEGAMISSION) { + + if (Debug_Print_Events) { + printf(" New MEGAMISSION run:\n"); + } + + unitsptr = ((unsigned char *)buf) + size + + sizeof(EventClass::EventType); + storedsize += sizeof(numunits); + numunits = 1; + missiondup = false; + } + + //..................................................................... + // Will the next event exceed the size of the buffer? If so, + // stop compressing. + //..................................................................... + if ( (size + storedsize) > bufsize ) + break; + + //..................................................................... + // Set the event's frame delay (this is protocol-dependent) + //..................................................................... + if (Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + OutList.First().Frame = ((Frame + frame_delay + + (Session.FrameSendRate - 1)) / Session.FrameSendRate) * + Session.FrameSendRate; + } + else { + OutList.First().Frame = Frame + frame_delay; + } + + //..................................................................... + // Set the event's ID + //..................................................................... + OutList.First().ID = PlayerPtr->ID; + + //..................................................................... + // Transfer the event in OutList to DoList, un-queue the OutList event. + // If the DoList is full, stop transferring immediately. + //..................................................................... + OutList.First().IsExecuted = 0; + if ( !DoList.Add( OutList.First() ) ) { + break; + } + #ifdef MIRROR_QUEUE + MirrorList.Add(OutList.First()); + #endif + + //--------------------------------------------------------------------- + // Compress the event into the send packet buffer + //--------------------------------------------------------------------- + switch ( eventtype ) { + //.................................................................. + // RESPONSE_TIME: just use the Delay field of the FrameInfo union + //.................................................................. + case (EventClass::RESPONSE_TIME): + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.FrameInfo.Delay, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + break; + + //.................................................................. + // MEGAMISSION: + //.................................................................. + case (EventClass::MEGAMISSION): + //............................................................... + // Repeated mission in a run: + // - Update the rep count (in case we break out) + // - Copy the Whom field only + //............................................................... + if (missiondup) { + *unitsptr = numunits; + + memcpy ( ((char *)buf) + size, + &OutList.First().Data.MegaMission.Whom, datasize ); + + size += datasize; + } + //............................................................... + // 1st mission in a run: + // - Init the rep count (in case we break out) + // - Set the EventType + // - Copy the MegaMission structure, leaving room for 'numunits' + //............................................................... + else { + *unitsptr = numunits; + + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + + sizeof(EventClass::EventType) + sizeof(numunits), + &OutList.First().Data.MegaMission, datasize ); + + size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits)); + } + break; + + //.................................................................. + // Variable-sized packets: Copy the packet Size & the buffer + //.................................................................. + case (EventClass::ADDPLAYER): + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data.Variable.Size, datasize ); + size += (datasize + sizeof(EventClass::EventType)); + + memcpy ( ((char *)buf) + size, + OutList.First().Data.Variable.Pointer, + OutList.First().Data.Variable.Size); + size += OutList.First().Data.Variable.Size; + + break; + + //.................................................................. + // Default case: Just copy over the data field from the union + //.................................................................. + default: + *(EventClass::EventType *)( ((char *)buf) + size) = eventtype; + + memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType), + &OutList.First().Data, datasize ); + + size += (datasize + sizeof(EventClass::EventType)); + + break; + } + + //--------------------------------------------------------------------- + // update # events processed + //--------------------------------------------------------------------- + num++; + + //--------------------------------------------------------------------- + // Update 'prevevent' + //--------------------------------------------------------------------- + memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) ); + + //--------------------------------------------------------------------- + // Go to the next event to process + //--------------------------------------------------------------------- + OutList.Next(); + } + + if (Debug_Print_Events) { + printf("\n"); + } + + return (size); + +} // end of Add_Compressed_Events + + +/*************************************************************************** + * Breakup_Receive_Packet -- Splits a big packet into little ones. * + * * + * INPUT: * + * buf buffer to break up * + * bufsize length of buffer * + * * + * OUTPUT: * + * # events added to queue, -1 if fatal error (queue is full) * + * (return value includes any FRAMEINFO packets encountered; * + * FRAMESYNC's are ignored) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Breakup_Receive_Packet(void *buf, int bufsize ) +{ + int count = 0; + + /* + ** is there enough leftover for another record + */ + switch (Session.CommProtocol) { + case (COMM_PROTOCOL_SINGLE_NO_COMP): + count = Extract_Uncompressed_Events(buf, bufsize); + break; + + default: + count = Extract_Compressed_Events(buf, bufsize); + break; + } + + return (count); + +} /* end of Breakup_Receive_Packet */ + + +/*************************************************************************** + * Extract_Uncompressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Uncompressed_Events(void *buf, int bufsize) +{ + int count = 0; + int pos = 0; + int leftover = bufsize; + EventClass *event; + + //------------------------------------------------------------------------ + // Loop until there are no more events in the packet + //------------------------------------------------------------------------ + while (leftover >= sizeof(EventClass) ) { + + Keyboard->Check(); + + event = (EventClass *)(((char *)buf) + pos); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + event->IsExecuted = 0; + + //.................................................................. + // Special processing for variable-sized events + //.................................................................. + if (event->Type == EventClass::ADDPLAYER) { + event->Data.Variable.Pointer = new char[event->Data.Variable.Size]; + memcpy (event->Data.Variable.Pointer, + ((char *)buf) + sizeof(EventClass), + event->Data.Variable.Size); + + pos += event->Data.Variable.Size; + leftover -= event->Data.Variable.Size; + } + + if (!DoList.Add( *event )) { + if (event->Type == EventClass::ADDPLAYER) { + delete [] event->Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add(*event); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + } + + //..................................................................... + // Point to the next position in the buffer; decrement our 'leftover' + //..................................................................... + pos += sizeof(EventClass); + leftover -= sizeof(EventClass); + } + + return (count); + +} // end of Extract_Uncompressed_Events + + +/*************************************************************************** + * Extract_Compressed_Events -- extracts events from a packet * + * * + * INPUT: * + * buf buffer containing events to extract * + * bufsize length of 'buf' * + * * + * OUTPUT: * + * # events extracted * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 DRD : Created. * + *=========================================================================*/ +static int Extract_Compressed_Events(void *buf, int bufsize) +{ + int pos = 0; // current buffer parsing position + int leftover = bufsize; // # bytes left to process + EventClass *event; // event ptr for parsing buffer + int count = 0; // # events processed + int datasize = 0; // size of data to copy + EventClass eventdata; // stores Frame, ID, etc + unsigned char numunits = 0; // # units stored in compressed MegaMissions + + //------------------------------------------------------------------------ + // Clear work event structure + //------------------------------------------------------------------------ + memset (&eventdata, 0, sizeof(EventClass)); + + //------------------------------------------------------------------------ + // Assume the first event is a FRAMEINFO event + // Init 'datasize' to the amount of data to copy, minus the EventType value + // For the 1st packet only, this will include all info before the Data + // union, plus the size of the FrameInfo structure, minus the EventType size. + //------------------------------------------------------------------------ + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType); + event = (EventClass *)(((char *)buf) + pos); + + while (leftover >= (datasize + (int)sizeof(EventClass::EventType)) ) { + + Keyboard->Check(); + + //..................................................................... + // add event to the DoList, only if it's not a FRAMESYNC + // (but FRAMEINFO's do get added.) + //..................................................................... + if (event->Type != EventClass::FRAMESYNC) { + //.................................................................. + // initialize the common data from the FRAMEINFO event + // keeping IsExecuted 0 + //.................................................................. + if (event->Type == EventClass::FRAMEINFO) { + eventdata.Frame = event->Frame; + eventdata.ID = event->ID; + + //............................................................... + // Adjust position past the common data + //............................................................... + pos += (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + leftover -= (offsetof(EventClass, Data) - + sizeof(EventClass::EventType)); + } + //.................................................................. + // if MEGAMISSION event get the number of units (events to generate) + //.................................................................. + else if (event->Type == EventClass::MEGAMISSION) { + numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type)); + pos += sizeof(numunits); + leftover -= sizeof(numunits); + } + + //.................................................................. + // clear the union data portion of the event + //.................................................................. + memset (&eventdata.Data, 0, sizeof(eventdata.Data)); + eventdata.Type = event->Type; + datasize = EventClass::EventLength[ eventdata.Type ]; + + switch (eventdata.Type) { + case (EventClass::RESPONSE_TIME): + memcpy ( &eventdata.Data.FrameInfo.Delay, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + + case (EventClass::ADDPLAYER): + + memcpy ( &eventdata.Data.Variable.Size, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + eventdata.Data.Variable.Pointer = + new char[eventdata.Data.Variable.Size]; + memcpy (eventdata.Data.Variable.Pointer, + ((char *)buf) + pos + sizeof(EventClass::EventType) + datasize, + eventdata.Data.Variable.Size); + + pos += eventdata.Data.Variable.Size; + leftover -= eventdata.Data.Variable.Size; + + break; + + case (EventClass::MEGAMISSION): + memcpy ( &eventdata.Data.MegaMission, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + + if (numunits > 1) { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + datasize = sizeof(eventdata.Data.MegaMission.Whom); + + while (numunits) { + + Keyboard->Check(); + + if ( !DoList.Add( eventdata ) ) { + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //...................................................... + // Keep count of how many events we add to the queue + //...................................................... + count++; + numunits--; + memcpy ( &eventdata.Data.MegaMission.Whom, + ((char *)buf) + pos, datasize ); + + //...................................................... + // if one unit left fall thru to normal code + //...................................................... + if (numunits == 1) { + datasize -= sizeof(EventClass::EventType); + break; + } + else { + pos += datasize; + leftover -= datasize; + } + } + } + break; + + default: + memcpy ( &eventdata.Data, + ((char *)buf) + pos + sizeof(EventClass::EventType), + datasize ); + break; + } + + if ( !DoList.Add( eventdata ) ) { + if (eventdata.Type == EventClass::ADDPLAYER) { + delete [] eventdata.Data.Variable.Pointer; + } + return (-1); + } + #ifdef MIRROR_QUEUE + MirrorList.Add( eventdata ); + #endif + + //.................................................................. + // Keep count of how many events we add to the queue + //.................................................................. + count++; + + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + + if (leftover) { + event = (EventClass *)(((char *)buf) + pos); + datasize = EventClass::EventLength[ event->Type ]; + if (event->Type == EventClass::MEGAMISSION) { + datasize += sizeof(numunits); + } + } + } + //..................................................................... + // FRAMESYNC event: This >should< be the only event in the buffer, + // and it will be uncompressed. + //..................................................................... + else { + pos += (datasize + sizeof(EventClass::EventType)); + leftover -= (datasize + sizeof(EventClass::EventType)); + event = (EventClass *)(((char *)buf) + pos); + + //.................................................................. + // size of FRAMESYNC event - EventType size + //.................................................................. + datasize = (offsetof(EventClass, Data) + + size_of(EventClass, Data.FrameInfo)) - + sizeof(EventClass::EventType); + } + } + + return (count); + +} // end of Extract_Compressed_Events + + +/*************************************************************************** + * Execute_DoList -- Executes commands from the DoList * + * * + * This routine executes any events in the DoList that need to be executed * + * on the current game frame. The events must be executed in a special * + * order, so that all systems execute all events in exactly the same * + * order. * + * * + * This routine also handles checking the Game CRC sent by other systems * + * against my own, to be sure we're still in sync. * + * * + * INPUT: * + * max_houses # houses to execute commands for * + * base_house HousesType to start with * + * net ptr to connection manager; NULL if none * + * skip_crc a frame-based countdown timer; if it's non-zero, the * + * CRC check will be skipped. Ignored if NULL. * + * their_frame array of their frame #'s * + * their_sent array of # commands they've sent * + * their_recv array of # commands I've received from them * + * * + * (their_xxx are ignored if 'net' is NULL.) * + * * + * OUTPUT: * + * 1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.) * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static int Execute_DoList(int max_houses, HousesType base_house, + ConnManClass *net, CDTimerClass *skip_crc, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + HousesType house; + HouseClass *hptr; + int i,j,k; + int index; + int check_crc; + + Check_Mirror(); + +#if(TIMING_FIX) + // + // If MPlayerMaxAhead is recomputed such that it increases, the systems + // may try to free-run to the new MaxAhead value. If so, they may miss + // an event that was generated after the TIMING event was created, but + // before it executed; this event will be scheduled with the older, + // shorter MaxAhead value. If a system doesn't receive this event, it + // may execute past the frame it's scheduled to execute on, creating + // a Packet-Recieved-Too-Late error. To prevent this, find any events + // that are scheduled to execute during this "period of vulnerability", + // and re-schedule for the end of that period. + // + for (j = 0; j < DoList.Count; j++) { + if (DoList[j].Type != EventClass::FRAMEINFO && + DoList[j].Frame > (unsigned)NewMaxAheadFrame1 && + DoList[j].Frame < (unsigned)NewMaxAheadFrame2) { + DoList[j].Frame = (unsigned)NewMaxAheadFrame2; + #ifdef MIRROR_QUEUE + MirrorList[j].Frame = NewMaxAheadFrame2; + #endif + } + } +#endif + + //------------------------------------------------------------------------ + // Execute the DoList. Events must be executed in the same order on all + // systems; so, execute them in the order of the HouseClass array. This + // array is stored in the same order on all systems. + //------------------------------------------------------------------------ + for (i = 0; i < max_houses; i++) { + //..................................................................... + // Convert our index into a HousesType value + //..................................................................... + house = (HousesType)(i + base_house); + hptr = HouseClass::As_Pointer(house); + + //..................................................................... + // If for some reason this house doesn't exist, skip it. + // Also, if this house has exited the game, skip it. (The user can + // generate events after he exits, because the exit event is scheduled + // at least FrameSendRate*3 frames ahead. If one system gets these + // packets & another system doesn't, they'll go out of sync because + // they aren't checking the CommandCount for that house, since that + // house isn't connected any more.) + //..................................................................... + if (!hptr) { + continue; + } + if (!hptr->IsHuman) { + continue; + } + + //..................................................................... + // Loop through all events + //..................................................................... + for (j = 0; j < DoList.Count; j++) { + + if (net) + Update_Queue_Mono (net, 6); + + //.................................................................. + // If this event was from the currently-executing player ID, and it's + // time to execute it, execute it. + //.................................................................. + if (DoList[j].ID == hptr->ID && (unsigned) Frame >= DoList[j].Frame && + !DoList[j].IsExecuted) { + + //............................................................... + // Error if it's too late to execute this packet! + // (Hack: disable this check for solo or skirmish mode.) + //............................................................... + if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type != + EventClass::FRAMEINFO && Session.Type != GAME_NORMAL && + Session.Type != GAME_SKIRMISH) { + +#if(TEN) + Send_TEN_Packet_Too_Late(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Packet_Too_Late(); +#endif // MPATH + + Dump_Packet_Too_Late_Stuff(&DoList[j], net, their_frame, + their_sent, their_recv); + WWMessageBox().Process (TXT_PACKET_TOO_LATE); + return (0); + } + + //............................................................... + // Only execute EXIT & OPTIONS commands if they're from myself. + //............................................................... + if (DoList[j].Type==EventClass::EXIT || + DoList[j].Type==EventClass::OPTIONS) { + +#ifdef WIN32 + if (DoList[j].Type==EventClass::EXIT) { + /* + ** Flag that this house lost because it quit. + */ + HousesType quithouse; + HouseClass *quithptr; + + for (int player = 0; player < max_houses ; player++) { + quithouse = (HousesType)(player + base_house); + quithptr = HouseClass::As_Pointer(quithouse); + if (!quithptr) { + continue; + } + if (quithptr->ID == DoList[j].ID) { + quithptr->IsGiverUpper = true; + break; + } + } + + /* + ** Send the game statistics packet now since the game is effectivly over + */ + if (Session.Players.Count() == 2 && + Session.Type == GAME_INTERNET && + !GameStatisticsPacketSent) { + Register_Game_End_Time(); + Send_Statistics_Packet(); // Event - player aborted, and there were only 2 left. + } + } +#endif //WIN32 + + if (Debug_Print_Events) { + if (DoList[j].Type==EventClass::EXIT) { + printf("(%d) Executing EXIT, ID:%d (%s), EvFrame:%d\n", + Frame, + DoList[j].ID, + (HouseClass::As_Pointer((HousesType)(DoList[j].ID)))->IniName, + DoList[j].Frame); + } + } + + if (DoList[j].ID == PlayerPtr->ID) { + DoList[j].Execute(); + } else if (DoList[j].Type==EventClass::EXIT) { + //............................................................ + // If this EXIT event isn't from myself, destroy the connection + // for that player. The HousesType for this event is the + // connection ID. + //............................................................ + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + //PG Destroy_Null_Connection( house, 0 ); + } else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET || + Session.Type == GAME_TEN || + Session.Type == GAME_MPATH) && net) { + index = net->Connection_Index (house); + if (index != -1) { + for (k = index; k < net->Num_Connections() - 1; k++) { + their_frame[k] = their_frame[k+1]; + their_sent[k] = their_sent[k+1]; + their_recv[k] = their_recv[k+1]; + } + if (Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) { + Destroy_Connection(house,0); + } +#if(TEN) + else if (Session.Type == GAME_TEN) { + Destroy_TEN_Connection(house,0); + } +#endif // TEN +#if(MPATH) + else if (Session.Type == GAME_MPATH) { + Destroy_MPATH_Connection(house,0); + } +#endif // MPATH + } + } + // + // Special case for recording playback: turn the house over + // to the computer. + // + if (Session.Play && DoList[j].Type==EventClass::EXIT) { + hptr->IsHuman = false; + hptr->IQ = Rule.MaxIQ; + hptr->Computer_Paranoid(); + strcpy (hptr->IniName,Text_String(TXT_COMPUTER)); + Session.NumPlayers--; + } + } + } + + //............................................................... + // For a FRAMEINFO event, check the CRC value. + //............................................................... + else if (DoList[j].Type == EventClass::FRAMEINFO) { + //............................................................ + // Skip the CRC check if we're less than 32 frames into the game; + // this will prevent a newly-loaded modem game from instantly + // going out of sync, if the games were saved at different + // frame numbers. + //............................................................ + if (!skip_crc || *skip_crc == 0) { + check_crc = 1; + } + else { + check_crc = 0; + } + if (check_crc + && DoList[j].Frame == Frame + && DoList[j].Data.FrameInfo.Delay < 32) { + index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) & + 0x001f); + if (CRC[index] != DoList[j].Data.FrameInfo.CRC) { + Print_CRCs(&DoList[j]); + +#if(TEN) + Send_TEN_Out_Of_Sync(); +#endif // TEN + +#if(MPATH) + //Send_MPATH_Out_Of_Sync(); +#endif // MPATH +#if (0)//PG + if (WWMessageBox().Process (TXT_OUT_OF_SYNC, + TXT_CONTINUE, TXT_STOP) == 0) { + if (Session.Type == GAME_MODEM || + Session.Type == GAME_NULL_MODEM) { + //PG Destroy_Null_Connection( house, -1 ); + Shutdown_Modem(); + Session.Type = GAME_NORMAL; + } + else if ((Session.Type == GAME_IPX || + Session.Type == GAME_INTERNET) && net) { + while (net->Num_Connections()) { + Keyboard->Check(); + Destroy_Connection (net->Connection_ID(0), -1); + } + } +#if(TEN) + else if (Session.Type == GAME_TEN && net) { + while (net->Num_Connections()) { + Destroy_TEN_Connection (net->Connection_ID(0), -1); + } + } +#endif +#if(MPATH) + else if (Session.Type == GAME_MPATH && net) { + while (net->Num_Connections()) { + Destroy_MPATH_Connection (net->Connection_ID(0), -1); + } + } +#endif + Map.Flag_To_Redraw(true); + } + else { + return (0); + } +#endif + return (1); + } + } + } + //............................................................... + // Execute other commands + //............................................................... + else { + DoList[j].Execute(); + } + + //............................................................... + // Mark this event as executed. + //............................................................... + DoList[j].IsExecuted = 1; + #ifdef MIRROR_QUEUE + MirrorList[j].IsExecuted = 1; + #endif + } + } + } + + return (1); + +} // end of Execute_DoList + + +/*************************************************************************** + * Clean_DoList -- Cleans out old events from the DoList * + * * + * Currently, an event can only be removed from the DoList if it's at the * + * head of the list; and event can't be removed from the middle. So, * + * this routine loops as long as the next event in the DoList has been * + * executed, it's removed. * + * * + * INPUT: * + * net ptr to connection manager; ignored if NULL * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Clean_DoList(ConnManClass *net) +{ + while (DoList.Count) { + + Keyboard->Check(); + + if (net) + Update_Queue_Mono (net, 7); + + //..................................................................... + // Discard events that have been executed, OR it's too late to execute. + // (This happens if another player exits the game; he'll leave FRAMEINFO + // events lying around in my queue. They won't have been "executed", + // because his IPX connection was destroyed.) + //..................................................................... + if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) { + DoList.Next(); + #ifdef MIRROR_QUEUE + MirrorList.Next(); + #endif + } + else { + break; + } + } + +} // end of Clean_DoList + + +/*************************************************************************** + * Queue_Record -- Records the DoList to disk * + * * + * This routine just saves any events in the DoList to disk; we can later * + * "play back" the recording just be pulling events from disk rather than * + * from the network! * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 08/14/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Record(void) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # of events to save this frame + //------------------------------------------------------------------------ + j = 0; + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + j++; + } + } + + //------------------------------------------------------------------------ + // Save the # of events, then all events. + //------------------------------------------------------------------------ + Session.RecordFile.Write (&j,sizeof(j)); + for (i = 0; i < DoList.Count; i++) { + if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) { + Session.RecordFile.Write (&DoList[i],sizeof (EventClass)); + j--; + } + } + +} /* end of Queue_Record */ + + +/*************************************************************************** + * Queue_Playback -- plays back queue entries from a record file * + * * + * This routine reads events from disk, putting them into the DoList; * + * it then executes the DoList just like the network version does. The * + * result is that the game "plays back" like a recording. * + * * + * This routine detects mouse motion and stops playback, so it can work * + * like an "attract" mode, showing a demo of the game itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/15/1995 BRR : Created. * + *=========================================================================*/ +static void Queue_Playback(void) +{ + int numevents; + EventClass event; + int i; + int ok; + static int mx,my; + int max_houses; + HousesType base_house; + int key; + int testframe; + + //------------------------------------------------------------------------ + // If the user hits ESC, stop the playback + //------------------------------------------------------------------------ + if (Keyboard->Check()) { + key = Keyboard->Get(); + if (key == KA_ESC || Session.Attract) { + GameActive = 0; + return; + } + } + + //------------------------------------------------------------------------ + // If we're in "Attract" mode, and the user moves the mouse, stop the + // playback. + //------------------------------------------------------------------------ + if (Session.Attract && Frame > 0 && + (mx != Get_Mouse_X() || my != Get_Mouse_Y())) { + GameActive = 0; + return; + } + mx = Get_Mouse_X(); + my = Get_Mouse_Y(); + + //------------------------------------------------------------------------ + // Compute the Game's CRC + //------------------------------------------------------------------------ + Compute_Game_CRC(); + CRC[Frame & 0x001f] = GameCRC; + + //------------------------------------------------------------------------ + // If we've reached the CRC print frame, do so & exit + //------------------------------------------------------------------------ + if (Frame >= Session.TrapPrintCRC) { + Print_CRCs(NULL); + Prog_End("Queue_Playback reached CRC print frame", true); + Emergency_Exit(0); + } + + //------------------------------------------------------------------------ + // Don't read anything the first time through (since the Queue_AI_Network + // routine didn't write anything the first time through); do this after the + // CRC is computed, since we'll still need a CRC for Frame 0. + //------------------------------------------------------------------------ + if (Frame==0 && Session.Type!=GAME_NORMAL) { + return; + } + + //------------------------------------------------------------------------ + // Only process every 'FrameSendRate' frames + //------------------------------------------------------------------------ + testframe = ((Frame + (Session.FrameSendRate - 1)) / + Session.FrameSendRate) * Session.FrameSendRate; + if ( (Session.Type != GAME_NORMAL && Session.Type != GAME_SKIRMISH) && + Session.CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) { + if (Frame != testframe) { + return; + } + } + + //------------------------------------------------------------------------ + // Read the DoList from disk + //------------------------------------------------------------------------ + ok = 1; + if (Session.RecordFile.Read (&numevents, sizeof(numevents)) == + sizeof(numevents)) { + for (i = 0; i < numevents; i++) { + if (Session.RecordFile.Read (&event, sizeof(EventClass)) == + sizeof(EventClass)) { + event.IsExecuted = 0; + DoList.Add (event); + #ifdef MIRROR_QUEUE + MirrorList.Add(event); + #endif + } + else { + ok = 0; + break; + } + } + } + else { + ok = 0; + } + + if (!ok) { + GameActive = 0; + return; + } + + + //------------------------------------------------------------------------ + // Execute the DoList; if an error occurs, bail out. + //------------------------------------------------------------------------ + if (Session.Type == GAME_NORMAL) { + max_houses = 1; + base_house = PlayerPtr->Class->House; + } + else { + max_houses = Session.MaxPlayers; + base_house = HOUSE_MULTI1; + } + if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) { + GameActive = 0; + return; + } + + //------------------------------------------------------------------------ + // Clean out the DoList + //------------------------------------------------------------------------ + Clean_DoList(NULL); + +} /* end of Queue_Playback */ + + +/*************************************************************************** + * Compute_Game_CRC -- Computes a CRC value of the entire game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Compute_Game_CRC(void) +{ + int i,j; + VesselClass *vessp; + InfantryClass *infp; + UnitClass *unitp; + BuildingClass *bldgp; + ObjectClass *objp; + HouseClass *housep; + + GameCRC = 0; + + //------------------------------------------------------------------------ + // Infantry + //------------------------------------------------------------------------ + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + } + + //------------------------------------------------------------------------ + // Units + //------------------------------------------------------------------------ + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + } + + //------------------------------------------------------------------------ + // Shippies + //------------------------------------------------------------------------ + for (i = 0; i < Vessels.Count(); i++) { + vessp = (VesselClass *)Vessels.Active_Ptr(i); + Add_CRC (&GameCRC, (int)vessp->Coord + (int)vessp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vessp->Speed + (int)vessp->NavCom); + Add_CRC (&GameCRC, (int)vessp->Strength); + Add_CRC (&GameCRC, (int)vessp->Mission + (int)vessp->TarCom); + } + + //------------------------------------------------------------------------ + // Buildings + //------------------------------------------------------------------------ + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + } + + //------------------------------------------------------------------------ + // Houses + //------------------------------------------------------------------------ + for (i = 0; i < Houses.Count(); i++) { + housep = (HouseClass *)Houses.Active_Ptr(i); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + for (i = 0; i < LAYER_COUNT; i++) { + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + } + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + } + + //------------------------------------------------------------------------ + // A random # + //------------------------------------------------------------------------ +// Add_CRC(&GameCRC, Scen.RandomNumber.Seed); + Add_CRC(&GameCRC, Scen.RandomNumber); + +} /* end of Compute_Game_CRC */ + + +/*************************************************************************** + * Add_CRC -- Adds a value to a CRC * + * * + * INPUT: * + * crc ptr to crc * + * val value to add * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +void Add_CRC(unsigned long *crc, unsigned long val) +{ + int hibit; + + if ( (*crc) & 0x80000000) { + hibit = 1; + } + else { + hibit = 0; + } + + (*crc) <<= 1; + (*crc) += val; + (*crc) += hibit; + +} /* end of Add_CRC */ + +/*************************************************************************** + * Print_CRCs -- Prints a data file for finding Sync Bugs * + * * + * INPUT: * + * ev -- event to display * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 05/09/1995 BRR : Created. * + *=========================================================================*/ +static void Print_CRCs(EventClass *ev) +{ + int i,j; + InfantryClass *infp; + UnitClass *unitp; + VesselClass *vesselp; + BuildingClass *bldgp; + ObjectClass *objp; + FILE *fp; + HouseClass *housep; + HousesType house; + int color; + + Mono_Clear_Screen(); + Mono_Set_Cursor (0,0); + fp = fopen("OUT.TXT","wt"); + if (fp==NULL) { + return; + } + + for (i = 0; i < 32; i++) { + fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]); + } + + // + // Houses + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + GameCRC = 0; + housep = HouseClass::As_Pointer (house); + if (housep) { + HousesType actlike = housep->ActLike; + color = housep->RemapColor; + fprintf(fp,"%s: IsHuman:%d Color:%s ID:%d ActLike:%s\n", + housep->IniName, + housep->IsHuman, + ColorNames[color], + housep->ID, + HouseClass::As_Pointer(actlike)->Class->Name()); + Add_CRC (&GameCRC, (int)housep->Credits + (int)housep->Power + + (int)housep->Drain); + Mono_Printf("House %s:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Infantry + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Infantry -------------------\n", + housep->Class->Name()); + for (i = 0; i < Infantry.Count(); i++) { + infp = (InfantryClass *)Infantry.Active_Ptr(i); + if (infp->Owner()==house) { + Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing); + Add_CRC (&GameCRC, (int)infp->Speed + (int)infp->NavCom); + Add_CRC (&GameCRC, (int)infp->Mission + (int)infp->TarCom); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x Speed:%d NavCom:%x\n", + infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(), + infp->Class->Type, infp->As_Target(), infp->Speed, infp->NavCom); + } + } + Mono_Printf("%s Infantry:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Units + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Units -------------------\n", + housep->Class->Name()); + for (i = 0; i < Units.Count(); i++) { + unitp = (UnitClass *)Units.Active_Ptr(i); + if (unitp->Owner()==house) { + Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing + + (int)unitp->SecondaryFacing); + fprintf(fp, + "COORD:%x Facing:%d Facing2:%d Mission:%d Type:%d Tgt:%x\n", + unitp->Coord,(int)unitp->PrimaryFacing, + (int)unitp->SecondaryFacing,unitp->Get_Mission(), + unitp->Class->Type, unitp->As_Target()); + } + } + Mono_Printf("%s Units:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Vessels + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Vessels -------------------\n", + housep->Class->Name()); + for (i = 0; i < Vessels.Count(); i++) { + vesselp = (VesselClass *)Vessels.Active_Ptr(i); + if (vesselp->Owner()==house) { + Add_CRC (&GameCRC, (int)vesselp->Coord + (int)vesselp->PrimaryFacing); + Add_CRC (&GameCRC, (int)vesselp->Speed + (int)vesselp->NavCom); + Add_CRC (&GameCRC, (int)vesselp->Strength); + Add_CRC (&GameCRC, (int)vesselp->Mission + (int)vesselp->TarCom); + fprintf(fp, + "COORD:%x Facing:%d Mission:%d Strength:%d Type:%d Tgt:%x\n", + vesselp->Coord,(int)vesselp->PrimaryFacing, + vesselp->Get_Mission(), vesselp->Strength, + vesselp->Class->Type, vesselp->As_Target()); + } + } + Mono_Printf("%s Vessels:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Buildings + // + for (house = HOUSE_MULTI1; house <= HOUSE_MULTI8; house++) { + housep = HouseClass::As_Pointer (house); + if (housep) { + GameCRC = 0; + fprintf(fp,"-------------------- %s Buildings -------------------\n", + housep->Class->Name()); + for (i = 0; i < Buildings.Count(); i++) { + bldgp = (BuildingClass *)Buildings.Active_Ptr(i); + if (bldgp->Owner()==house) { + Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing); + fprintf(fp,"COORD:%x Facing:%d Mission:%d Type:%d Tgt:%x\n", + bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(), + bldgp->Class->Type, bldgp->As_Target()); + } + } + Mono_Printf("%s Buildings:%x\n",housep->Class->Name(),GameCRC); + } + } + + // + // Animations + // + AnimClass *animp; + fprintf(fp,"-------------------- Animations -------------------\n"); + for (i = 0; i < Anims.Count(); i++) { + animp = (AnimClass *)Anims.Active_Ptr(i); + fprintf(fp,"Target:%x OwnerHouse:%d Loops:%d\n", + animp->xObject, + animp->OwnerHouse, + animp->Loops); + } + + //------------------------------------------------------------------------ + // Map Layers + //------------------------------------------------------------------------ + GameCRC = 0; + for (i = 0; i < LAYER_COUNT; i++) { + fprintf(fp,">>>> MAP LAYER %d <<<<\n",i); + for (j = 0; j < Map.Layer[i].Count(); j++) { + objp = Map.Layer[i][j]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",j,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + else if (objp->What_Am_I() == RTTI_VESSEL) + fprintf(fp,"Vessel (Type:%d) ", + (VesselType)(*((VesselClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + } + Mono_Printf("Map Layers:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Logic Layers + //------------------------------------------------------------------------ + GameCRC = 0; + fprintf(fp,">>>> LOGIC LAYER <<<<\n"); + for (i = 0; i < Logic.Count(); i++) { + objp = Logic[i]; + Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I()); + fprintf(fp,"Object %d: %x ",i,objp->Coord); + + if (objp->What_Am_I() == RTTI_AIRCRAFT) + fprintf(fp,"Aircraft (Type:%d) ", + (AircraftType)(*((AircraftClass *)objp))); + else if (objp->What_Am_I() == RTTI_ANIM) + fprintf(fp,"Anim (Type:%d) ", + (AnimType)(*((AnimClass *)objp))); + else if (objp->What_Am_I() == RTTI_BUILDING) + fprintf(fp,"Building (Type:%d) ", + (StructType)(*((BuildingClass *)objp))); + else if (objp->What_Am_I() == RTTI_BULLET) + fprintf(fp,"Bullet (Type:%d) ", + (BulletType)(*((BulletClass *)objp))); + else if (objp->What_Am_I() == RTTI_INFANTRY) + fprintf(fp,"Infantry (Type:%d) ", + (InfantryType)(*((InfantryClass *)objp))); + else if (objp->What_Am_I() == RTTI_OVERLAY) + fprintf(fp,"Overlay (Type:%d) ", + (OverlayType)(*((OverlayClass *)objp))); + else if (objp->What_Am_I() == RTTI_SMUDGE) + fprintf(fp,"Smudge (Type:%d) ", + (SmudgeType)(*((SmudgeClass *)objp))); + else if (objp->What_Am_I() == RTTI_TEMPLATE) + fprintf(fp,"Template (Type:%d) ", + (TemplateType)(*((TemplateClass *)objp))); + else if (objp->What_Am_I() == RTTI_TERRAIN) + fprintf(fp,"Terrain (Type:%d) ", + (TerrainType)(*((TerrainClass *)objp))); + else if (objp->What_Am_I() == RTTI_UNIT) + fprintf(fp,"Unit (Type:%d) ", + (UnitType)(*((UnitClass *)objp))); + + house = objp->Owner(); + if (house!=HOUSE_NONE) { + housep = HouseClass::As_Pointer (house); + fprintf(fp,"Owner: %s\n",housep->Class->IniName); + } + else { + fprintf(fp,"Owner: NONE\n"); + } + } + Mono_Printf("Logic:%x \n",GameCRC); + + //------------------------------------------------------------------------ + // Random # generator, frame # + //------------------------------------------------------------------------ + Mono_Printf("Random Number:%x \n",Scen.RandomNumber.Seed); +#ifdef RANDOM_COUNT + fprintf(fp,"\nRandom Number:%x (Count1:%d, Count2:%d)\n", + Scen.RandomNumber.Seed, + Scen.RandomNumber.Count1, + Scen.RandomNumber.Count2); +#else + fprintf(fp,"\nRandom Number:%x\n",Scen.RandomNumber.Seed); +#endif + + Mono_Printf("My Frame:%d \n",Frame); + fprintf(fp,"My Frame:%d\n",Frame); + + if (ev) { + fprintf(fp,"\n"); + fprintf(fp,"Offending event:\n"); + fprintf(fp," Type: %d\n",ev->Type); + fprintf(fp," Frame: %d\n",ev->Frame); + fprintf(fp," ID: %x\n",ev->ID); + fprintf(fp," CRC: %x\n",ev->Data.FrameInfo.CRC); + fprintf(fp," CommandCount: %d\n",ev->Data.FrameInfo.CommandCount); + fprintf(fp," Delay: %d\n",ev->Data.FrameInfo.Delay); + } + + fclose(fp); + +} /* end of Print_CRCs */ + + +/*************************************************************************** + * Init_Queue_Mono -- inits mono display * + * * + * This routine steals control of the mono screen away from the rest of * + * the engine, by setting the global IsMono; if IsMono is set, the other * + * routines in this module turn off the Mono display when they're done * + * with it, so the rest of the engine won't over-write what we're writing. * + * * + * INPUT: * + * net ptr to connection manager * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Init_Queue_Mono(ConnManClass *net) +{ +#if(SHOW_MONO) + //------------------------------------------------------------------------ + // Set 'IsMono' so we can steal the mono screen from the engine + //------------------------------------------------------------------------ + if ((Frame==0 || Session.LoadGame) && MonoClass::Is_Enabled()) { + IsMono = true; + } + + //------------------------------------------------------------------------ + // Enable mono output for our stuff; we must Disable it before we return + // control to the engine. + //------------------------------------------------------------------------ + if (IsMono) + MonoClass::Enable(); + + if (net->Num_Connections() > 0) { + //..................................................................... + // Network mono debugging screen + //..................................................................... + if (NetMonoMode==0) { + if (Frame==0 || Session.LoadGame || NewMonoMode) { + net->Configure_Debug (0, sizeof (CommHeaderType), + sizeof(EventClass::EventType), EventClass::EventNames, 0, 27); + net->Mono_Debug_Print (0,1); + NewMonoMode = 0; + } + else { + net->Mono_Debug_Print (0,0); + } + } + //..................................................................... + // Flow control debugging output + //..................................................................... + else { + if (NewMonoMode) { + Mono_Clear_Screen(); + Mono_Printf(" Queue AI:\n"); // flowcount[0] + Mono_Printf(" Build Packet Loop:\n"); // flowcount[1] + Mono_Printf(" Frame Sync:\n"); // flowcount[2] + Mono_Printf(" Frame Sync Resend:\n"); // flowcount[3] + Mono_Printf(" Frame Sync Timeout:\n"); // flowcount[4] + Mono_Printf(" Frame Sync New Message:\n"); // flowcount[5] + Mono_Printf(" DoList Execution:\n"); // flowcount[6] + Mono_Printf(" DoList Cleaning:\n"); // flowcount[7] + Mono_Printf("\n"); + Mono_Printf(" Frame:\n"); + Mono_Printf(" Session.MaxAhead:\n"); + Mono_Printf(" their_recv:\n"); + Mono_Printf(" their_sent:\n"); + Mono_Printf(" my_sent:\n"); + NewMonoMode = 0; + } + } + } +#else + net = net; +#endif +} // end of Init_Queue_Mono + + +/*************************************************************************** + * Update_Queue_Mono -- updates mono display * + * * + * INPUT: * + * net ptr to connection manager * + * flow_index index # for flow-count updates * + * -1: display * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Update_Queue_Mono(ConnManClass *net, int flow_index) +{ +#if(SHOW_MONO) + static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + //------------------------------------------------------------------------ + // If 'NetMonoMode' is 1, display flowcount info + //------------------------------------------------------------------------ + if (NetMonoMode==1) { + if (flow_index >= 0 && flow_index < 20) { + Mono_Set_Cursor(35,flow_index); + flowcount[flow_index]++; + Mono_Printf("%d",flowcount[flow_index]); + } + } + //------------------------------------------------------------------------ + // Otherwise, display the connection debug screen + //------------------------------------------------------------------------ + else { + net->Mono_Debug_Print (0,0); + } + +#else + flow_index = flow_index; + net = net; +#endif + +} // end of Update_Queue_Mono + + +/*************************************************************************** + * Print_Framesync_Values -- displays frame-sync variables * + * * + * INPUT: * + * curframe current game Frame # * + * max_ahead max-ahead value * + * num_connections # connections * + * their_recv # commands I've received from my connections * + * their_sent # commands each connection claims to have sent * + * my_sent # commands I've sent * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/21/1995 BRR : Created. * + *=========================================================================*/ +static void Print_Framesync_Values(long curframe, unsigned long max_ahead, + int num_connections, unsigned short *their_recv, + unsigned short *their_sent, unsigned short my_sent) +{ +#if(SHOW_MONO) + int i; + + if (NetMonoMode==1) { + Mono_Set_Cursor(35,9); + Mono_Printf("%d",curframe); + + Mono_Set_Cursor(35,10); + Mono_Printf("%d",max_ahead); + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,11); + Mono_Printf("%4d",(int)their_recv[i]); + } + + for (i = 0; i < num_connections; i++) { + Mono_Set_Cursor(35 + i*5,12); + Mono_Printf("%4d",(int)their_sent[i]); + } + + Mono_Set_Cursor(35,13); + Mono_Printf("%4d",(int)my_sent); + } +#else + curframe = curframe; + max_ahead = max_ahead; + num_connections = num_connections; + their_recv = their_recv; + their_sent = their_sent; + my_sent = my_sent; +#endif +} // end of Print_Framesync_Values + + +/*************************************************************************** + * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk * + * * + * INPUT: * + * event ptr to event to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/28/1996 BRR : Created. * + *=========================================================================*/ +void Dump_Packet_Too_Late_Stuff(EventClass *event, ConnManClass *net, + long *their_frame, unsigned short *their_sent, unsigned short *their_recv) +{ + FILE *fp; + int i; + HousesType house; + + fp = fopen("toolate.txt", "wt"); + if (!fp) { + return; + } + fprintf(fp,"----------------- Event data: ----------------------\n"); + fprintf(fp,"Type: %s\n",EventClass::EventNames[event->Type]); + fprintf(fp,"Frame: %d\n",event->Frame); + fprintf(fp,"ID: %d\n",event->ID); + + for (i = 0; i < Session.Players.Count(); i++) { + if (event->ID == Session.Players[i]->Player.ID) { + fprintf(fp,"Player's Name: %s",Session.Players[i]->Name); + } + } + fprintf(fp,"\n"); + + fprintf(fp,"--------------------- My data: ---------------------\n"); + fprintf(fp,"My Frame:%d\n",Frame); + fprintf(fp,"My MaxAhead:%d\n",Session.MaxAhead); + + if (net) { + fprintf(fp,"-------------------- Frame Stats: ------------------\n"); + fprintf(fp,"Name ID TheirFrame TheirSent TheirRecv\n"); + for (i = 0; i < net->Num_Connections(); i++) { + house = (HousesType)(net->Connection_ID(i)); + fprintf(fp,"%12s %2d %6d %6d %6d\n", + (HouseClass::As_Pointer(house))->IniName, + net->Connection_ID(i), + their_frame[i], + their_sent[i], + their_recv[i]); + } + } + + fclose(fp); +} + +/*************************************************************************** + * Check_Mirror -- Checks mirror memory * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/14/1996 BRR : Created. * + *=========================================================================*/ +void Check_Mirror(void) +{ +#ifdef MIRROR_QUEUE + int i; + char txt[80]; + unsigned long *ptr; + int found_5s = 0; + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0x55555555) { + sprintf(txt,"55555555 found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(DoList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in DoList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + ptr = (unsigned long *)(MirrorList.Get_Array()); + for (i = 0; i < (MAX_EVENTS * 64 * sizeof(EventClass)) / + sizeof(unsigned long); i++) { + if (ptr[i] == 0xAAAAAAAA) { + sprintf(txt,"AAAAAAAA found in MirrorList! Addr:%p", &(ptr[i])); + WWMessageBox().Process (txt); + found_5s = 1; + } + } + + for (i = 0; i < DoList.Count; i++) { + if (memcmp(&DoList[i], &MirrorList[i], sizeof(EventClass)) != 0) { + sprintf(txt,"Queue Memory Trashed! Head:%d Tail:%d, Addr:%p or %p", + DoList.Get_Head(), + DoList.Get_Tail(), + DoList.Get_Array() + i, + MirrorList.Get_Array() + i); + WWMessageBox().Process (txt); + Prog_End("Check_Mirror", true); + Emergency_Exit(0); + } + } + + if (found_5s) { + //Prog_End(); + Emergency_Exit(0); + } + +#endif +} // end of Check_Mirror + + +/*************************** end of queue.cpp ******************************/ \ No newline at end of file diff --git a/code/redalert/queue.h b/code/redalert/queue.h new file mode 100644 index 0000000..3206ae5 --- /dev/null +++ b/code/redalert/queue.h @@ -0,0 +1,292 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/QUEUE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : QUEUE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/08/94 * + * * + * Last Update : December 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * QueueClass::Add -- Add object to queue. * + * QueueClass::First -- Fetches reference to first object in list. * + * QueueClass::Init -- Initializes queue to empty state. * + * QueueClass::Next -- Throws out the head of the line. * + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef QUEUE_H +#define QUEUE_H + +#include "mission.h" +#include "target.h" +#include "defines.h" + +//#pragma warn -inl + +/* +** This class implements a classic FIFO queue (also known as - standing in line). Objects +** are added to the end (tail) of the line. Objects are removed from the start (first) of +** the line. A keyboard buffer is a good example of a common computer use of a queue. There +** is no provision for "taking cuts" or leaving the line once an object has been added. +** +** The implementation uses a circular list of objects. This allows adding and deleting of +** elements without any maintenance moves of remaining objects that would otherwise be +** necessary in a simple array storage method. A side effect of this means that accessing the +** internal array directly is not allowed. Supporting functions are provided for this purpose. +** +** WARNING WARNING WARNING WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +** The size parameter MUST be an exact power of two (2, 4, 8, 16, etc.) otherwise the internal +** indexing algorithm will fail. +*/ +template +class QueueClass +{ + public: + /* + ** This is the count of the number of objects in the queue. If this count is zero, + ** then the operator[], First(), and Next() functions are undefined. Check this + ** value BEFORE calling these functions. + */ + const int Count; + + //-------------- Functions -------------------- + QueueClass(void); // Default constructor. + + /* + ** The bracket subscript operator functions similarly to the way a normal subscript + ** operator works except that entry [0] matches the first-in-line and entry + ** [Count-1] matches the last-in-line. This is ensured regardless of the actual position + ** of the object in the circular internal list. + */ + T & operator[](int); + + /* + ** This function will return a reference to the "head of the line" object. + */ + T & First(void); + + /* + ** This function clears the list of objects. + */ + void Init(void); + + /* + ** This function discards the head-of-the-line object and advances all the remaining + ** objects up by one. Mnemonic: Imagine a broadway audition and the director yells + ** "NEXT!" + */ + int Next(void); + + /* + ** This will add an object to the tail of the line. If there is no more room to add + ** the object, then false will be returned. + */ + int Add(T const &); + + int Get_Head(void); + int Get_Tail(void); + T * Get_Array(void); + + private: + int Head; // Index of element in list the longest. + int Tail; // Index where next new addition will go. + + T Array[size]; // Raw array of objects. +}; + + +/*********************************************************************************************** + * QueueClass::QueueClass -- Default constructor for QueueClass objects. * + * * + * This default constructor for QueueClass objects initializes the queue to an empty * + * state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline QueueClass::QueueClass(void) : Count(0) +{ + Init(); +} + + +/*********************************************************************************************** + * QueueClass::Init -- Initializes queue to empty state. * + * * + * This function resets the queue to an empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline void QueueClass::Init(void) +{ + ((int &)Count) = 0; + Head = 0; + Tail = 0; +} + + +/*********************************************************************************************** + * QueueClass::Add -- Add object to queue. * + * * + * This function is used to add an object to the tail of the line. If the queue cannot * + * accept any more entries, then the object won't be added and false will be returned. * + * * + * INPUT: object -- The object that is to be added to the queue. * + * * + * OUTPUT: bool; Was the object added successfully? * + * * + * WARNINGS: If the queue is full, then the object won't be added. Be sure to check for this.* + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Add(T const &q) +{ + if (Count < size) { + Array[Tail] = q; + Tail = (Tail + 1) & (size-1); + ((int &)Count) = Count + 1; + return(true); + } + return (false); +} + + +/*********************************************************************************************** + * QueueClass::Next -- Throws out the head of the line. * + * * + * This routine is used to discard the object at the head of the line. All remaining * + * objects "move up" one. No actual movement occurs, merely the index is adjusted, but * + * the affect is that the next oldest object in the queue will now be returned with the * + * next call to the First() function. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of object remaining in the queue. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline int QueueClass::Next(void) +{ + if (Count) { + Head = (Head + 1) & (size-1); + ((int &)Count) = Count - 1; + } + return (Count); +} + + +/*********************************************************************************************** + * QueueClass::operator[] -- Fetches reference to sub object in queue. * + * * + * Use this routine to examine individual objects within the queue. The oldest object in * + * the queue is referenced by an index value of zero. The newest object in the queue is * + * referenced by a value of Count-1. If there are no objects in the queue, then this * + * operator is undefined. Although this operator allows examination of the queue, there is * + * no corresponding ability to insert or delete objects from the middle of the queue. * + * * + * INPUT: index -- The index into the queue of objects. Valid values range from zero to * + * Count-1. All other values return an undefined reference! * + * * + * OUTPUT: Returns with a reference to the object indicated by the index. * + * * + * WARNINGS: Check to make sure that Count is not zero before using this operator. Failure * + * to do so will return a reference to an undefined object. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::operator[](int index) +{ + return Array[(Head + index) & (size-1)]; +} + + +/*********************************************************************************************** + * QueueClass::First -- Fetches reference to first object in list. * + * * + * This routine is used to fetch the first object in the list (head of the line). This * + * object is the oldest in the list. Typical use of this function is to get and process * + * the first object so that it may be discarded with the Next() function in order to bring * + * subsequent objects to the first position. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the oldest object in the queue. * + * * + * WARNINGS: If there are no objects in the queue, then this function returns an undefined * + * reference. Be sure to check Count against zero before calling this function. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +template +inline T & QueueClass::First(void) +{ + return Array[Head]; +} + +template +inline int QueueClass::Get_Head(void) +{ + return Head; +} + +template +inline int QueueClass::Get_Tail(void) +{ + return Tail; +} + +template +inline T * QueueClass::Get_Array(void) +{ + return Array; +} + +#endif diff --git a/code/redalert/radar.cpp b/code/redalert/radar.cpp new file mode 100644 index 0000000..1680711 --- /dev/null +++ b/code/redalert/radar.cpp @@ -0,0 +1,2735 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADAR.CPP 3 3/12/97 2:35p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Get_Multi_Color -- Get the multi color offset number * + * RadarClass::AI -- Processes radar input (non-tactical). * + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * RadarClass::Click_In_Radar -- Check to see if a click is in radar map * + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * RadarClass::Draw_Names -- draws players' names on the radar map * + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * RadarClass::Init_Clear -- Sets the radar map to a known state * + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * RadarClass::Radar_Activate -- Controls radar activation. * + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell.* + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Called when setting the tactical display position. * + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to* + * RadarClass::Zoom_Mode(void) -- Handles toggling zoom on the map * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "Image.h" + + //void const * RadarClass::CoverShape; +RadarClass::RTacticalClass RadarClass::RadarButton; + +void const* RadarClass::RadarAnim = NULL; +void const* RadarClass::RadarPulse = NULL; +void const* RadarClass::RadarFrame = NULL; + +static bool FullRedraw = false; + +static GraphicBufferClass _IconStage(3, 3); +static GraphicBufferClass _TileStage(24, 24); + + +/*********************************************************************************************** + * RadarClass::RadarClass -- Default constructor for RadarClass object. * + * * + * This default constructor merely sets the radar specific values to default settings. The * + * radar must be deliberately activated in order for it to be displayed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/16/1994 JLB : Created. * + *=============================================================================================*/ +RadarClass::RadarClass(void) : + IsToRedraw(false), + RadarCursorRedraw(false), + IsPulseActive(false), + RadarPulseFrame(0), + DoesRadarExist(false), + IsRadarActive(false), + IsRadarActivating(false), + IsRadarDeactivating(false), + IsRadarJammedByPlayerMask(0U), + SpecialRadarFrame(0), + RadarAnimFrame(0), + RadarX(0), + RadarY(0), + RadarCellWidth(0), + RadarCellHeight(0), + RadarCell(0), + BaseX(0), + BaseY(0), + RadarWidth(0), + RadarHeight(0), + IsZoomed(true), + ZoomFactor(0), + IsPlayerNames(false), + IsHouseSpy(false), + SpyingOn(HOUSE_SPAIN), + PixelPtr(0) +{ +} + + +/*********************************************************************************************** + * RadarClass::One_Time -- Handles one time processing for the radar map. * + * * + * This routine handles any one time processing required in order for the radar map to * + * function. This actually only requires an allocation of the radar staging buffer. This * + * buffer is needed for those cases where the radar area of the page is being destroyed * + * and it needs to be destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to call this routine only ONCE. * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::One_Time(void) +{ + RadWidth = 80 * RESFACTOR; + RadHeight = 70 * RESFACTOR; + RadX = SeenBuff.Get_Width() - RadWidth; + RadY = 7 * RESFACTOR; + RadPWidth = 64 * RESFACTOR; + RadPHeight = 64 * RESFACTOR; +#ifdef WIN32 + RadOffX = 6; + RadOffY = 7; + RadIWidth = 128 + 18;//************ + RadIHeight = 128 + 2;//************ +#else + RadOffX = 4; + RadOffY = 1; + RadIWidth = 72; + RadIHeight = 69; +#endif + + DisplayClass::One_Time(); +#ifdef OBSOLETE + RadarButton.X = RadX + RadOffX; + RadarButton.Y = RadY + RadOffY; + RadarButton.Width = RadIWidth; + RadarButton.Height = RadIHeight; +#else + RadarButton.X = RadX; + RadarButton.Y = RadY; + RadarButton.Width = RadWidth; + RadarButton.Height = RadHeight; +#endif +} + + +/*********************************************************************************************** + * RadarClass::Init_Clear -- Sets the radar map to a known state. * + * * + * This routine is used to initialize the radar map at the start of the scenario. It * + * sets the radar map position and starts it in the disabled state. * + * * + * INPUT: theater -- The theater that the scenario is starting (unused by this routine). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Init_Clear(void) +{ + DisplayClass::Init_Clear(); + IsRadarActive = false; + IsToRedraw = true; + RadarCursorRedraw = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + PixelPtr = 0; + IsPlayerNames = false; + + /* + ** If we have a valid map lets make sure that we set it correctly + */ + if (MapCellWidth || MapCellHeight) { +#ifdef WIN32 + IsZoomed = false; +#else + IsZoomed = true; +#endif + Zoom_Mode(Coord_Cell(Map.TacticalCoord)); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Activate -- Controls radar activation. * + * * + * Use this routine to turn the radar map on or off. * + * * + * INPUT: control -- What to do with the radar map: * + * 0 = Turn radar off. * + * 1 = Turn radar on. * + * 2 = Remove Radar Gadgets * + * 3 = Add Radar Gadgets * + * 4 = Remove radar. * + * -1= Toggle radar on or off. * + * * + * OUTPUT: bool; Was the radar map already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Radar_Activate(int control) +{ + bool old = IsRadarActive; + + switch (control) { + + /* + ** Toggle the state of the radar map on or off. + */ + case -1: + { + int temp = (IsRadarActive == false); + if (temp) { + Radar_Activate(1); + } + else { + Radar_Activate(0); + } + } + break; + + /* + ** Turn the radar map off properly. + */ + case 0: + if (Map.IsSidebarActive) { + if (IsRadarActive && !IsRadarDeactivating) { + Sound_Effect(VOC_RADAR_OFF); + IsRadarDeactivating = true; + IsRadarActive = false; + if (IsRadarActivating == true) { + IsRadarActivating = false; + } + else { + RadarAnimFrame = RADAR_ACTIVATED_FRAME; + } + } + } + else { + Radar_Activate(2); + } + return(old); + + case 1: + if (Map.IsSidebarActive) { + if (!IsRadarActivating && !IsRadarActive) { + Sound_Effect(VOC_RADAR_ON); + IsRadarActivating = true; + if (IsRadarDeactivating == true) { + IsRadarDeactivating = false; + } + else { + if (DoesRadarExist) { + RadarAnimFrame = MAX_RADAR_FRAMES; + } + else { + RadarAnimFrame = 0; + } + } + } + } + else { + Radar_Activate(3); + } + return(old); + + case 2: + if (Session.Type == GAME_NORMAL) { + SidebarClass::Zoom.Disable(); + } + else { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 3: + if (Session.Type == GAME_NORMAL && Is_Zoomable()) { + SidebarClass::Zoom.Enable(); + } + IsRadarActive = true; + IsRadarActivating = false; + IsRadarDeactivating = false; + break; + + case 4: + IsRadarActive = false; + IsRadarActivating = false; + IsRadarDeactivating = false; + DoesRadarExist = false; + Flag_To_Redraw(false); + IsToRedraw = true; + break; + + default: + break; + } + + if (IsRadarActive != old) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + FullRedraw = IsRadarActive; + return(old); +} + + +/*********************************************************************************************** + * RadarClass::Draw_It -- Displays the radar map of the terrain. * + * * + * This is used to display the radar map that appears in the lower * + * right corner. The main changes to this map are the vehicles and * + * structure pixels. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/24/1991 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Draw_It(bool forced) +{ + DisplayClass::Draw_It(forced); + + static char* _hiresradarnames[] = { + "natoradr.shp", //HOUSE_SPAIN, + "natoradr.shp", //HOUSE_GREECE, + "ussrradr.shp", //HOUSE_USSR, + "natoradr.shp", //HOUSE_ENGLAND, + "ussrradr.shp", //HOUSE_UKRAINE, + "natoradr.shp", //HOUSE_GERMANY, + "natoradr.shp", //HOUSE_FRANCE, + "natoradr.shp", //HOUSE_TURKEY, + "natoradr.shp", //HOUSE_GOOD + "ussrradr.shp", //HOUSE_BAD + }; + static char* _frames[] = { + "nradrfrm.shp", //HOUSE_SPAIN, + "nradrfrm.shp", //HOUSE_GREECE, + "uradrfrm.shp", //HOUSE_USSR, + "nradrfrm.shp", //HOUSE_ENGLAND, + "uradrfrm.shp", //HOUSE_UKRAINE, + "nradrfrm.shp", //HOUSE_GERMANY, + "nradrfrm.shp", //HOUSE_FRANCE, + "nradrfrm.shp", //HOUSE_TURKEY, + "nradrfrm.shp", //HOUSE_GOOD + "uradrfrm.shp", //HOUSE_BAD + }; + + int radarforced = 0; + + /* + ** Don't perform any rendering if none is requested. + */ + if (!forced && !IsToRedraw && !FullRedraw) return; + + BStart(BENCH_RADAR); + + static HousesType _house = HOUSE_NONE; + + if (PlayerPtr->ActLike != _house) { + char name[_MAX_FNAME + _MAX_EXT]; + + // strcpy(name, "NATORADR.SHP" ); + // if (Session.Type == GAME_NORMAL) { + strcpy(name, _hiresradarnames[PlayerPtr->ActLike]); + // } +#ifndef NDEBUG + RawFileClass file(name); + if (file.Is_Available()) { + RadarAnim = Load_Alloc_Data(file); + } + else { + RadarAnim = MFCD::Retrieve(name); + } + strcpy(name, "PULSE.SHP"); + RawFileClass file2(name); + if (file2.Is_Available()) { + RadarPulse = Load_Alloc_Data(file2); + } + else { + RadarPulse = MFCD::Retrieve(name); + } + strcpy(name, _frames[PlayerPtr->ActLike]); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarFrame = Load_Alloc_Data(file3); + } + else { + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); + } +#else + RadarAnim = MFCD::Retrieve(name); + strcpy(name, "PULSE.SHP"); + RawFileClass file3(name); + if (file3.Is_Available()) { + RadarPulse = Load_Alloc_Data(file3); + } + else { + RadarPulse = MFCD::Retrieve(name); + } + RadarFrame = MFCD::Retrieve(_frames[PlayerPtr->ActLike]); +#endif + _house = PlayerPtr->ActLike; + } + + /* + ** If in player name mode, just draw player names + */ + if (IsPlayerNames) { + Draw_Names(); + // IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + /* + ** If in spy-on-radar facility mode, draw the appropriate info. + */ + if (IsHouseSpy) { + //IsToRedraw = false; + if (Draw_House_Info()) { + BEnd(BENCH_RADAR); + return; + } + } + + if (IsRadarActivating || IsRadarDeactivating /*|| IsRadarJammed*/) { + Radar_Anim(); + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + //IsToRedraw = false; + BEnd(BENCH_RADAR); + return; + } + + if (Map.IsSidebarActive) { + if (IsRadarActive) { + + if (RunningAsDLL) { + BEnd(BENCH_RADAR); + return; + } + + /* + ** If only a few of the radar pixels need to be redrawn, then find and redraw + ** only these. + */ + if (!forced && IsToRedraw && !FullRedraw && !IsPulseActive) { + //IsToRedraw = false; + + if (PixelPtr) { + + /* + ** Render all pixels in the "to redraw" stack. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < (int)PixelPtr; index++) { + CELL cell = PixelStack[index]; + if (Cell_On_Radar(cell)) { + (*this)[cell].IsPlot = false; + Plot_Radar_Pixel(cell); + RadarCursorRedraw |= (*this)[cell].IsRadarCursor; + } + } + LogicPage->Unlock(); + } + + /* + ** Refill the stack if there is pending pixels yet to be plotted. + ** This should only process in sections for speed reasons + */ + if (PixelPtr == PIXELSTACK) { + PixelPtr = 0; + + for (int y = 0; y < MapCellHeight; y++) { + for (int x = 0; x < MapCellWidth; x++) { + CELL cell = XY_Cell(MapCellX + x, MapCellY + y); + if (Cell_On_Radar(cell)) { + + if ((*this)[cell].IsPlot) { + PixelStack[PixelPtr++] = cell; + IsToRedraw = true; + if (PixelPtr == PIXELSTACK) break; + } + } + } + if (PixelPtr == PIXELSTACK) break; + } + } + else { + PixelPtr = 0; + } + } + + Radar_Cursor(RadarCursorRedraw); + + } + else { + +#ifdef WIN32 + GraphicViewPortClass* oldpage = Set_Logic_Page(HidPage); +#else + GraphicBufferClass* oldpage = Set_Logic_Page(HidPage); +#endif + + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + if (BaseX || BaseY) { + + if (!IsZoomed && BaseX && BaseY && (int)RadarWidth < (RadIWidth - 1) && (int)RadarHeight < (RadIHeight - 1)) { +#ifdef WIN32 + LogicPage->Draw_Rect(RadX + RadOffX + BaseX - 1, + RadY + RadOffY + BaseY - 1, + RadX + RadOffX + BaseX + RadarWidth, + // RadX + RadOffX + BaseX + RadarWidth +1, + RadY + RadOffY + BaseY + RadarHeight, + // RadY + RadOffY + BaseY + RadarHeight +1, + WHITE); +#endif + } + } + else { + LogicPage->Fill_Rect(RadX + RadOffX, + RadY + RadOffY, + RadX + RadOffX + RadIWidth - 1, + RadY + RadOffY + RadIHeight - 1, + BLACK); + } + + /* + ** Draw the entire radar map. + */ + if (LogicPage->Lock()) { + for (int index = 0; index < MAP_CELL_TOTAL; index++) { + if (In_Radar(index) && Cell_On_Radar(index)) { + Plot_Radar_Pixel(index); + } + } + if (IsPulseActive) { + CC_Draw_Shape(RadarPulse, RadarPulseFrame++, RadX + RadOffX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + } + LogicPage->Unlock(); + } + + Radar_Cursor(true); + //FullRedraw = false; + //IsToRedraw = false; + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + if (oldpage == &SeenBuff) { + Hide_Mouse(); + LogicPage->Blit(SeenBuff, RadX, RadY, RadX, RadY, RadWidth, RadHeight); + Show_Mouse(); + } + + Set_Logic_Page(oldpage); + } + + } + else { + + /* + ** If the radar is not active, then only draw the cover plate if forced to do so. + */ + int val = (DoesRadarExist) ? MAX_RADAR_FRAMES : 0; + CC_Draw_Shape(RadarAnim, val, RadX, RadY + (1 * RESFACTOR), WINDOW_MAIN, SHAPE_NORMAL); + //FullRedraw = false; + //IsToRedraw = false; + + if (RunningAsDLL) { + BEnd(BENCH_RADAR); + return; + } + + /* + ** Display the country name on the cover plate when in multi play only. + */ + if (Session.Type != GAME_NORMAL) { + Fancy_Text_Print(Text_String(HouseTypeClass::As_Reference(PlayerPtr->ActLike).Full_Name()), RadX + RadWidth / 2, RadY + RadHeight - 10 * RESFACTOR, &ColorRemaps[PlayerPtr->RemapColor], TBLACK, TPF_CENTER | TPF_TEXT | TPF_DROPSHADOW); + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + } + + } + BEnd(BENCH_RADAR); +} + + +/*************************************************************************** + * RadarClass::Render_Terrain -- Render the terrain over the given cell * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/12/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Terrain(CELL cell, int x, int y, int size) +{ + TerrainClass* list[4] = { 0,0,0,0 }; + int listidx = 0; + int lp, lp2; + + + ObjectClass* obj = Map[cell].Cell_Occupier(); + + /* + ** If the cell is occupied by a terrain type, add it to the sortable + ** list. + */ + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass*)obj; + + /* + ** Now loop through all the occupiers and add them to the list if they + ** are terrain type. + */ + for (lp = 0; lp < ARRAY_SIZE(Map[cell].Overlapper); lp++) { + obj = Map[cell].Overlapper[lp]; + if (obj && obj->What_Am_I() == RTTI_TERRAIN) + list[listidx++] = (TerrainClass*)obj; + } + + /* + ** If there are no entries in our list then just get out. + */ + if (!listidx) return; + + /* + ** If there is terrain in this cell then draw a dark pixel to + ** represent it. + */ + if (size == 1) { + LogicPage->Put_Pixel(x, y, 21); + // LogicPage->Put_Pixel(x, y, 60); + return; + } + + /* + ** Sort the list by its sort Y value so that we can render in the proper + ** order. + */ + for (lp = 0; lp < listidx - 1; lp++) { + for (lp2 = lp + 1; lp2 < listidx; lp2++) { + if (list[lp]->Sort_Y() > list[lp2]->Sort_Y()) { + TerrainClass* terrain = list[lp]; + list[lp] = list[lp2]; + list[lp2] = terrain; + } + } + } + + /* + ** loop through the list and take care of rendering the correct icon. + */ + for (lp = 0; lp < listidx; lp++) { + unsigned char* icon = list[lp]->Radar_Icon(cell); + if (!icon) continue; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, ZoomFactor, ZoomFactor, TRUE, (char*)&FadingBrighten[0]); +#else + for (int lpy = 0; lpy < 3; lpy++) { + for (int lpx = 0; lpx < 3; lpx++) { + if (*icon) { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + icon++; + } + } +#endif //WIN32 + } +} + + +/*********************************************************************************************** + * RadarClass::Render_Infantry -- Displays objects on the radar map. * + * * + * This routine will display an object imagery at the location specified according to the * + * condition of the specified cell. * + * * + * INPUT: cell -- The cell to use as reference when drawing the radar pixel. * + * * + * x,y -- The pixel coordinate to render the radar "pixel" at. * + * * + * size -- The size of the "pixel". When zoomed in, this value will be "3". * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Render_Infantry(CELL cell, int x, int y, int size) +{ + ObjectClass* obj; + + obj = (ObjectClass*)Map[cell].Cell_Occupier(); + while (obj) { + if (obj->Is_Techno() && ((TechnoClass*)obj)->Is_Visible_On_Radar()) { + int color = ColorRemaps[((InfantryClass*)obj)->House->RemapColor].Bar; + // int color = ColorRemaps[((InfantryClass *)obj)->House->RemapColor].BrightColor; + int xoff; + int yoff; + int subsize = max(1, size / 3); + + switch (obj->What_Am_I()) { + case RTTI_INFANTRY: + xoff = (Coord_XLepton(obj->Coord) / (CELL_LEPTON_W / (size + 1))) - subsize / 2; + xoff = max(xoff, 0); + xoff = min(xoff, size - subsize); + yoff = (Coord_YLepton(obj->Coord) / (CELL_LEPTON_H / (size + 1))) - subsize / 2; + yoff = max(yoff, 0); + yoff = min(yoff, size - subsize); + + /* + ** Draw the infantryman's pixel. If he's a spy, draw in my house color + */ + if (*(InfantryClass*)obj == INFANTRY_SPY) { + color = ColorRemaps[PlayerPtr->RemapColor].Bar; + // color = ColorRemaps[PlayerPtr->RemapColor].BrightColor; + } + LogicPage->Fill_Rect(x + xoff, y + yoff, x + xoff + (subsize - 1), y + yoff + (subsize - 1), color); + break; + + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + LogicPage->Fill_Rect(x, y, x + size - 1, y + size - 1, color); + break; + + default: + break; + } + } + obj = obj->Next; + } + +} + + +/*************************************************************************** + * RadarClass::Render_Overlay -- Renders an icon for given overlay * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/18/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Render_Overlay(CELL cell, int x, int y, int size) +{ + //int lpx,lpy; + + OverlayType overlay = (*this)[cell].Overlay; + if (overlay != OVERLAY_NONE) { + OverlayTypeClass const* otype = &OverlayTypeClass::As_Reference(overlay); + + if (otype->IsRadarVisible) { + unsigned char* icon = otype->Radar_Icon((*this)[cell].OverlayData); + if (!icon) return; +#ifdef WIN32 + Buffer_To_Page(0, 0, 3, 3, icon, _IconStage); + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x, y, DKGREY); + + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingShade[0]); + } + else { + _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char*)&FadingYellow[0]); + } + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingGreen[0]); + // } else { + // _IconStage.Scale(*LogicPage, 0, 0, x, y, 3, 3, size, size, TRUE, (char *)&FadingBrighten[0]); + } + +#else + for (int lpy = 0; lpy < size; lpy++) { + for (int lpx = 0; lpx < size; lpx++) { + if (size == 1) icon += 4; + if (*icon) { + if (otype->IsTiberium) { + if (size == 1) { + LogicPage->Put_Pixel(x + lpx, y + lpy, DKGREY); + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingShade[*icon]); + } + else { + LogicPage->Put_Pixel(x + lpx, y + lpy, FadingYellow[*icon]); + } + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingGreen[*icon]); + // } else { + // LogicPage->Put_Pixel(x + lpx, y + lpy, FadingBrighten[*icon]); + } + } + if (size == 1) { + icon += 5; + } + else { + icon++; + } + icon++; + } + } +#endif //WIN32 + } + } +} + + +/*************************************************************************** + * RadarClass::Zoom_Mode -- Handles toggling zoom on the map * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/29/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Zoom_Mode(CELL cell) +{ + int map_c_width; + int map_c_height; + + /* + ** Set all of the initial zoom mode variables to the correct + ** setting. + */ +#ifdef WIN32 + if (Is_Zoomable()) { + IsZoomed = !IsZoomed; + } + else { + IsZoomed = true; + } +#else + IsZoomed = false; +#endif + BaseX = 0; + BaseY = 0; + + /* + ** Figure out exactly what size we need to zoom the map to. + */ + if (!IsZoomed) { + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + ZoomFactor = max(min(xfactor, yfactor), 1); + map_c_width = MapCellWidth; + map_c_height = MapCellHeight; + } + else { + ZoomFactor = 3; + // ZoomFactor = 6; + map_c_width = RadIWidth / ZoomFactor; + map_c_height = RadIHeight / ZoomFactor; + } + + /* + ** Make sure we do not show more cells than are on the map. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + map_c_width = min(map_c_width, RadIWidth); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, RadIHeight); + map_c_height = min(map_c_height, MapCellHeight); +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif +#else + map_c_width = min(map_c_width, 62 * RESFACTOR); + map_c_width = min(map_c_width, MapCellWidth); + map_c_height = min(map_c_height, 62 * RESFACTOR); + map_c_height = min(map_c_height, MapCellHeight); +#endif + + /* + ** Find the amount of remainder because this will let us calculate + ** how to center the thing. + */ + int rem_x = RadIWidth - (map_c_width * ZoomFactor); + int rem_y = RadIHeight - (map_c_height * ZoomFactor); + + /* + ** Finally mark the map so it shows just as much as it is supposed + ** to. + */ + BaseX = rem_x / 2; + BaseY = rem_y / 2; + RadarCellWidth = map_c_width; + RadarCellHeight = map_c_height; + RadarWidth = RadIWidth - rem_x; + RadarHeight = RadIHeight - rem_y; + + /* + ** Set the radar position to the current cell. + */ + Set_Radar_Position(cell); + + /* + ** When zoom mode changes then we need to redraw the radar + ** area. + */ + IsToRedraw = true; + + /* + ** Notify the map that we need to redraw a portion + */ + Flag_To_Redraw(false); + + /* + ** Since we have made a vast change we must redraw everything + */ + FullRedraw = true; +} + + +/*********************************************************************************************** + * RadarClass::Is_Zoomable -- Determines if the map can be zoomed. * + * * + * This will check to see if the zoomed mode of the map would be just the same size as * + * the non-zoomed mode. If this is true, then zooming would have no effect, so return * + * false indicating that zooming is not allowed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is zooming allowed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Zoomable(void) const +{ + int xfactor = RadIWidth / MapCellWidth; + int yfactor = RadIHeight / MapCellHeight; + int factor = max(min(xfactor, yfactor), 1); + if (factor == 3) { + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Plot_Radar_Pixel -- Updates the radar map with a terrain pixel. * + * * + * This will update the radar map with a pixel. It is used to display * + * vehicle positions on the radar map. * + * * + * INPUT: unit -- Pointer to unit to render at the given position. If * + * NULL is passed in, then the underlying terrain is * + * displayed instead. * + * * + * pos -- Position on the map to update. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine does NOT hide the mouse. It is up to you to * + * do so. * + * * + * HISTORY: * + * 06/04/1991 JLB : Created. * + * 06/21/1991 JLB : Large blips for units & buildings. * + * 02/14/1994 JLB : Revamped. * + * 04/17/1995 PWG : Created. * + * 04/18/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Plot_Radar_Pixel(CELL cell) +{ + if (cell == -1) cell = 1; + + int x, y; // Coordinate of cell location. + + /* + ** Perform any clipping on the cell coordinate. + */ + if (!IsRadarActive || (unsigned)cell > MAP_CELL_TOTAL) { + return; + } + + if (!In_Radar(cell) || !Cell_On_Radar(cell)) { + return; + } + + /* + ** If we are zoomed in then calculate the pixel based off of the portion + ** of the map the radar is viewing. + */ + x = Cell_X(cell) - RadarX; + y = Cell_Y(cell) - RadarY; + if ((unsigned)x >= RadarCellWidth || (unsigned)y >= RadarCellHeight) { + return; + } + + bool usjamming = false; + if (LogicPage->Lock()) { + CellClass* cellptr = &(*this)[cell]; + x = RadX + RadOffX + BaseX + (x * ZoomFactor); + y = RadY + RadOffY + BaseY + (y * ZoomFactor); + + /* + ** Determine what (if any) vehicle or unit should be rendered in this blip. + */ + int color = TBLACK; // Color of the pixel to plot. + int housebit = (1 << PlayerPtr->Class->House); + int celljammed = (*this)[cell].Jammed; + int jammed = celljammed & (0xFFFF - housebit); + if (!jammed && ((*this)[cell].IsMapped || Debug_Unshroud)) { + // if (!jammed && ((*this)[cell].IsVisible || Debug_Unshroud)) { + color = cellptr->Cell_Color(true); + if ((celljammed & housebit) && (color == TBLACK)) { + color = BLACK;//FadingWayDark[color]; + usjamming = true; + } + } + else { + color = BLACK; + } + + /* + ** If no color override occurs for this cell, then render the underlying + ** terrain. + */ + if (color == TBLACK) { + if (ZoomFactor > 1) { + const TemplateTypeClass* ptr = NULL; + int icon; + + /* + ** Fetch the template pointer and template icon number for the + ** specified cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != 255) { + ptr = &TemplateTypeClass::As_Reference(cellptr->TType); + icon = cellptr->TIcon; + } + + /* + ** If the template pointer is still NULL, then this means either a clear + ** template or an illegal one. Setup for a clear template. + */ + if (ptr == NULL) { + ptr = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = cellptr->Clear_Icon(); + } + + IconsetClass const* iconset = (IconsetClass const*)ptr->Get_Image_Data(); + unsigned char const* icondata = iconset->Icon_Data(); + + + /* + ** Convert the logical icon number into the actual icon number. + */ + icon &= 0x00FF; + icon = *(iconset->Map_Data() + icon); + // jmarshall + unsigned char* data = (unsigned char*)icondata + icon * (24 * 24); + Image_t* image; + { + char tmp[512]; + sprintf(tmp, "radar_%d_%d", ptr->Type, icon); + image = Image_CreateImageFrom8Bit(tmp, 24, 24, (unsigned char*)data); + } + GL_RenderImage(image, x, y, ZoomFactor, ZoomFactor); + // jmarshall end + //Buffer_To_Page(0, 0, 24, 24, data, _TileStage); + //_TileStage.Scale(*LogicPage, 0, 0, x, y, 24, 24, ZoomFactor, ZoomFactor, TRUE); + } + else { + // LogicPage->Fill_Rect(x, y, x+ZoomFactor-1, y+ZoomFactor-1, cellptr->Cell_Color(false)); + /*BG*/ LogicPage->Put_Pixel(x, y, cellptr->Cell_Color(false)); + } + } + else { + LogicPage->Fill_Rect(x, y, x + ZoomFactor - 1, y + ZoomFactor - 1, color); + ///*BG*/ LogicPage->Put_Pixel(x, y, color); + } + if (color != BLACK) { + Render_Overlay(cell, x, y, ZoomFactor); + Render_Terrain(cell, x, y, ZoomFactor); + Render_Infantry(cell, x, y, ZoomFactor); + } + else { + if (usjamming) { + Render_Infantry(cell, x, y, ZoomFactor); + } + } + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * RadarClass::Radar_Pixel -- Mark a cell to be rerendered on the radar map. * + * * + * This routine is used to inform the system that a pixel needs to be * + * rerendered on the radar map. The pixel(s) will be rendered the * + * next time the map is refreshed. * + * * + * INPUT: cell -- The map cell to be rerendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/12/1992 JLB : Created. * + * 05/08/1994 JLB : Converted to member function. * + *=============================================================================================*/ +void RadarClass::Radar_Pixel(CELL cell) +{ + if (IsRadarActive && Map.IsSidebarActive && Cell_On_Radar(cell)) { + IsToRedraw = true; + (*this)[cell].IsPlot = true; + if (PixelPtr < PIXELSTACK) { + PixelStack[PixelPtr++] = cell; + } + } +} + + +/*********************************************************************************************** + * RadarClass::Click_In_Radar -- Converts a radar click into cell X and Y coordinate. * + * * + * This routine will examine the X and Y coordinate and convert them into the X and Y * + * cell coordinate value that corresponds to the location. * + * * + * INPUT: x,y -- The X and Y mouse coordinate already normalized to the radar upper left * + * corner. * + * * + * OUTPUT: Returns with success rating in addition, the X and Y values will now hold the * + * cell coordinates of the cell the pixel offsets indicated. * + * Result 1 = click was in radar region * + * Result 0 = click was outside radar region completely * + * Result-1 = click in radar area but not on clickable region of radar. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1995 PWG : Created. * + * 07/16/1995 JLB : Recognizes when sidebar is closed now. * + *=============================================================================================*/ +int RadarClass::Click_In_Radar(int& ptr_x, int& ptr_y, bool change) const +{ + int x = ptr_x; + int y = ptr_y; + + /* + ** If radar is not active the click could have been on a radar point + */ + if (!IsRadarActive || !Map.IsSidebarActive) return(0); + + x -= (RadX + RadOffX); + y -= (RadY + RadOffY); + if (x < RadIWidth && y < RadIHeight) { + x -= BaseX; + y -= BaseY; + + if ((unsigned)x < RadarWidth + (ZoomFactor - 1) && (unsigned)y < RadarHeight + (ZoomFactor - 1)) { + // if ((unsigned)x < RadarWidth && (unsigned)y < RadarHeight) { + x = RadarX + (x / ZoomFactor); + y = RadarY + (y / ZoomFactor); + if (change) { + ptr_x = x; + ptr_y = y; + } + return(1); + } + return(-1); + } + return(0); +} + + +/*********************************************************************************************** + * RadarClass::Click_Cell_Calc -- Determines what cell the pixel coordinate is over. * + * * + * This routine will examine the pixel coordinate provided and determine what cell it * + * represents. If the radar map is not active or the coordinates are not positioned over * + * the radar map, then it will fall into the base class corresponding routine. * + * * + * INPUT: x,y -- The pixel coordinate to convert into a cell number. * + * * + * OUTPUT: Returns with the cell number that the coordinate is over or -1 if not over any * + * cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Click_Cell_Calc(int x, int y) const +{ + int result = Click_In_Radar(x, y, true); + switch (result) { + case 1: + return(XY_Cell(x, y)); + + case -1: + return(-1); + + default: + break; + } + return(DisplayClass::Click_Cell_Calc(x, y)); +} + + +/*********************************************************************************************** + * RadarClass::Map_Cell -- Updates radar map when a cell becomes mapped. * + * * + * This routine will update the radar map if a cell becomes mapped. * + * * + * INPUT: cell -- The cell that is being mapped. * + * * + * house -- The house that is doing the mapping. * + * * + * OUTPUT: bool; Was the cell mapped (for the first time) by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Map_Cell(CELL cell, HouseClass* house, bool check_radar_spied, bool and_for_allies) +{ + if (DisplayClass::Map_Cell(cell, house, check_radar_spied, and_for_allies)) { + Radar_Pixel(cell); + return(true); + } + return(false); +} + + + +void RadarClass::Cursor_Cell(CELL cell, int value) +{ + /* + ** If this cell is not on the radar don't bother doing anything. + */ + if (Cell_On_Radar(cell)) { + + int temp = (*this)[cell].IsRadarCursor; + + if (temp != value) { + + /* + ** Record the new state of this cell. + */ + (*this)[cell].IsRadarCursor = value; + + /* + ** If we are erasing then erase the cell. + */ + if (value == FALSE) { + Plot_Radar_Pixel(cell); + } + } + } +} + + +void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen) +{ + int x, y; + /* + ** First step is to convert pixel coordinates back to a CellX and CellY. + */ + x1 = RadarX + (x1 / ZoomFactor); + y1 = RadarY + (y1 / ZoomFactor); + x2 = RadarX + (x2 / ZoomFactor); + y2 = RadarY + (y2 / ZoomFactor); + + /* + ** Now we need to convert the Pixel length to a cell length. + */ + barlen = (barlen / ZoomFactor) + 1; + + /* + ** Now lets loop through and mark the map with the proper value. + */ + for (int lp = 0; lp <= barlen; lp++) { + /* + ** Do Horizontal action to upper and lower left corners. + */ + x = x1 + lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Horizontal Action to upper and lower right corners + */ + x = x2 - lp; + Cursor_Cell(XY_Cell(x, y1), value); + Cursor_Cell(XY_Cell(x, y2), value); + /* + ** Do Vertical Action to left and right upper corners + */ + y = y1 + lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + + /* + ** Do Vertical action to left and right lower corners. + */ + y = y2 - lp; + Cursor_Cell(XY_Cell(x1, y), value); + Cursor_Cell(XY_Cell(x2, y), value); + } +} + + + +/*********************************************************************************************** + * RadarClass::Cell_XY_To_Radar_Pixel-- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ +void RadarClass::Cell_XY_To_Radar_Pixel(int cellx, int celly, int& x, int& y) +{ + x = (cellx - RadarX) * ZoomFactor; + y = (celly - RadarY) * ZoomFactor; +} + + +/*********************************************************************************************** + * RadarClass::Jam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Jam_Cell(CELL cell, HouseClass* house/*KO, bool shadeit*/) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Jammed |= jam; + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 11:00AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (house != PlayerPtr) Shroud_Cell(cell, PlayerPtr/*KO, shadeit*/); + + } + else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass* player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != house) { + Shroud_Cell(cell, player_house); + } + } + } + + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::UnJam_Cell -- Updates radar map when a cell becomes jammed. * + * * + * This routine will update the radar map if a cell becomes jammed. * + * * + * INPUT: cell -- The cell that is being jammed. * + * * + * house -- The house that is doing the jamming. * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/09/1995 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::UnJam_Cell(CELL cell, HouseClass* house) +{ + unsigned short jam = 1 << house->Class->House; + (*this)[cell].Redraw_Objects(); + (*this)[cell].Jammed &= (0xFFFF - jam); + Radar_Pixel(cell); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Radar_Cursor -- Adjust the position of the radar map cursor. * + * * + * This routine will adjust the location (and visibility) of the radar * + * map cursor. It handles all restoration, drawing, and flashing. * + * * + * INPUT: pos - Cell position for the cursor. If the value is -1 then * + * the cursor will be hidden. If the value is equal to * + * the last value passed in then cursor flashing will * + * be maintained. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + * 11/17/1995 PWG : Created. * + *=============================================================================================*/ + //#pragma argsused +void RadarClass::Radar_Cursor(int forced) +{ + static int _last_pos = -1; + static int _last_frame = -1; +#ifdef WIN32 + GraphicViewPortClass* oldpage; +#else + GraphicBufferClass* oldpage; +#endif + int x1, y1, x2, y2; + + /* + ** figure out these function calls as we will need to call them multiple times. + */ + int tac_cell = Coord_Cell(TacticalCoord); + int tac_cell_x = Cell_X(tac_cell); + int tac_cell_y = Cell_Y(tac_cell); + int barlen = 6; + + /* + ** If the current tactical cell is invalid or we haven't moved and we are not forced to redraw then + ** just skip the redraw process. + */ + if (tac_cell != -1 && _last_pos == tac_cell && _last_frame == SpecialRadarFrame && !forced) return; + + if (_last_pos != -1) { + /* + ** The first thing we need to do is take care of erasing the last radar cell position. We do this + ** by converting to pixel coordinates, then adjusting for the pixel coords for the current frame and + ** finally taking care of calling the erase procedure which will convert the pixel coordinates back + ** to the cells that need to be redraw. + **/ + int last_cell_x = Cell_X(_last_pos); + int last_cell_y = Cell_Y(_last_pos); + + Cell_XY_To_Radar_Pixel(last_cell_x, last_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(last_cell_x + Lepton_To_Cell(TacLeptonWidth), last_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the current coordinates based on the last animation frame. + */ + x1 -= _last_frame; + y1 -= _last_frame; + x2 += _last_frame; + y2 += _last_frame; + + /* + ** Finally mark the map (actually remove the marks that indicate the radar cursor was there + */ + Mark_Radar(x1, y1, x2, y2, FALSE, barlen); + } + + + /* + ** Find the upper left and lower right corners of the radar cursor. + ** Remember to adjust x2 and y2 back by one pixel as they will not be + ** pointing to the right value otherwise. They point one cell ahead + ** of where they should. + */ + Cell_XY_To_Radar_Pixel(tac_cell_x, tac_cell_y, x1, y1); + Cell_XY_To_Radar_Pixel(tac_cell_x + Lepton_To_Cell(TacLeptonWidth), tac_cell_y + Lepton_To_Cell(TacLeptonHeight), x2, y2); + x2--; + y2--; + + /* + ** Adjust the coordinates based on the current frame of radar animation. + */ + x1 -= SpecialRadarFrame; + y1 -= SpecialRadarFrame; + x2 += SpecialRadarFrame; + y2 += SpecialRadarFrame; + + Mark_Radar(x1, y1, x2, y2, TRUE, barlen); + + /* + ** setup a graphic view port class so we can write all the pixels relative + ** to 0,0 rather than relative to full screen coordinates. + */ + oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + BaseX + LogicPage->Get_XPos(), + RadY + RadOffY + BaseY + LogicPage->Get_YPos(), + RadarWidth, + RadarHeight); + + draw_window.Draw_Line(x1, y1, x1 + barlen, y1, LTGREEN); + draw_window.Draw_Line(x1, y1, x1, y1 + barlen, LTGREEN); + + // Draw upper right hand corner + draw_window.Draw_Line(x2 - barlen, y1, x2, y1, LTGREEN); + draw_window.Draw_Line(x2, y1, x2, y1 + barlen, LTGREEN); + + // Draw lower left hand corner + draw_window.Draw_Line(x1, y2 - barlen, x1, y2, LTGREEN); + draw_window.Draw_Line(x1, y2, x1 + barlen, y2, LTGREEN); + + // Draw lower right hand corner + draw_window.Draw_Line(x2, y2 - barlen, x2, y2, LTGREEN); + draw_window.Draw_Line(x2 - barlen, y2, x2, y2, LTGREEN); + + Set_Logic_Page(oldpage); + _last_pos = tac_cell; + _last_frame = SpecialRadarFrame; + RadarCursorRedraw = FALSE; +} + + +/*************************************************************************** + * RadarClass::Radar_Anim -- Renders current frame of radar animation * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1995 PWG : Created. * + *=========================================================================*/ +void RadarClass::Radar_Anim(void) +{ + /* + ** Do nothing if we're in player-name mode + */ + if (IsPlayerNames) + return; + + if (!Map.IsSidebarActive) return; + +#ifdef WIN32 + GraphicViewPortClass* oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage->Get_Graphic_Buffer(), + RadX + RadOffX + LogicPage->Get_XPos(), + RadY + RadOffY + LogicPage->Get_YPos(), + RadIWidth, + RadIHeight); +#else + GraphicBufferClass* oldpage = Set_Logic_Page(HidPage); + GraphicViewPortClass draw_window(LogicPage, + RadX + RadOffX, + RadY + RadOffY, + RadIWidth, + RadIHeight - 2); +#endif + //Mono_Set_Cursor(0,0); +#ifdef WIN32 + Draw_Box(RadX + RadOffX - 1, RadY + RadOffY - 1, RadIWidth + 2, RadIHeight + 2, BOXSTYLE_RAISED, true); +#endif + draw_window.Clear(); + CC_Draw_Shape(RadarAnim, RadarAnimFrame, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + Flag_To_Redraw(false); + Set_Logic_Page(oldpage); +} + + +/*********************************************************************************************** + * RadarClass::AI -- Processes radar input (non-tactical). * + * * + * This routine intercepts any player input that concerns the radar map, but not those * + * areas that represent the tactical map. These are handled by the tactical map AI * + * processor. Primarily, this routine handles the little buttons that border the radar * + * map. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate parameters to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + * 12/26/1994 JLB : Moves tactical map with click or drag. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +void RadarClass::AI(KeyNumType& input, int x, int y) +{ + /* + ** Check to see if we need to animate the radar cursor + */ + if (IsRadarActive && Map.IsSidebarActive && SpecialRadarFrame) { + SpecialRadarFrame--; + RadarCursorRedraw = TRUE; + IsToRedraw = TRUE; + Flag_To_Redraw(FALSE); + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarActivating) { + if (!DoesRadarExist) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + else { + DoesRadarExist = true; + Radar_Activate(3); + } + } + else { + RadarAnimFrame--; + if (RadarAnimFrame > RADAR_ACTIVATED_FRAME) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + else { + Radar_Activate(3); + } + } + } + + /* + ** Check goes here to see if there is enough power to run the radar + */ + if (IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame == MAX_RADAR_FRAMES) { + IsRadarDeactivating = false; + } + else { + IsToRedraw = true; + Flag_To_Redraw(false); + } + } + + /* + ** Check here to see if radar is being jammed, so we can update the + ** animation with snow. + */ + if (!IsRadarActivating && !IsRadarDeactivating) { + RadarAnimFrame++; + if (RadarAnimFrame < RADAR_ACTIVATED_FRAME) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + if (RadarAnimFrame > (3 + RADAR_ACTIVATED_FRAME)) RadarAnimFrame = RADAR_ACTIVATED_FRAME; + IsToRedraw = true; + Flag_To_Redraw(false); + } + + /* + ** Check here to see if the sonar pulse is active, and if it is, flag the + ** radar to redraw so the pulse ping will display. + */ + if (IsPulseActive) { + Flag_To_Redraw(true); + IsToRedraw = true; + if (RadarPulseFrame >= 8) { + RadarPulseFrame = 0; + IsPulseActive = false; + } + } + + DisplayClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * RadarClass::RTacticalClass::Action -- I/O function for the radar map. * + * * + * This is the main action function for handling player I/O on the radar map. It processes * + * mouse clicks as well as mouse moves. * + * * + * INPUT: flags -- The event flags that trigger this function call. * + * * + * key -- Reference the keyboard event that applies to the trigger event. * + * * + * OUTPUT: Should further processing of the input list be aborted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +int RadarClass::RTacticalClass::Action(unsigned flags, KeyNumType& key) +{ + CELL cell; // cell num click happened over + int x, y; // Sub cell pixel coordinates. + int cellx, celly; // Sub cell pixel coordinates. + bool shadow; // is the cell in shadow or not + ObjectClass* object = 0; // what object is in the cell + ActionType action = ACTION_NONE; // Action possible with currently selected object. + + /* + ** Force any help label to disappear when the mouse is held over the + ** radar map. + */ + if (Map.IsSidebarActive) { + Map.Help_Text(TXT_NONE); + } + + if (!Map.IsRadarActive) { + if (Map.IsSidebarActive) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); + // Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + return(false); + } + + /* + ** Disable processing if the player names are up + */ + if (Map.Is_Player_Names()) { + GadgetClass::Action(0, key); + return(true); + } + + /* + ** Set some working variables that depend on the mouse position. For the press + ** or release event, special mouse queuing storage variables are used. Other + ** events must use the current mouse position globals. + */ + if (flags & (LEFTPRESS | LEFTRELEASE | RIGHTPRESS | RIGHTRELEASE)) { + x = Keyboard->MouseQX; + y = Keyboard->MouseQY; + } + else { + x = Get_Mouse_X(); + y = Get_Mouse_Y(); + } + + /* + ** See if the mouse is over the radar general area, but not yet + ** over the active region of the radar map. In such a case, the + ** mouse is overridden to be the normal cursor and no other + ** action is performed. + */ + if (x < Map.RadX + Map.RadOffX || x >= Map.RadX + Map.RadIWidth || y < Map.RadY + Map.RadOffY || y >= Map.RadY + Map.RadIHeight) { + Map.Override_Mouse_Shape(MOUSE_NORMAL); + return(false); + } + + int result = Map.RadarClass::Click_In_Radar(x, y, false); + + if (result == 1) { + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1 && Map.In_Radar(cell)) { + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + // shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + cellx = 12; + celly = 12; + + /* + ** Determine the object that the mouse is currently over. + */ + if (!shadow) { + object = Map.Cell_Object(cell, cellx, celly); + } + + /* + ** If there is a currently selected object, then the action to perform if + ** the left mouse button were clicked must be determined. + */ + if (CurrentObject.Count()) { + if (object) { + action = CurrentObject[0]->What_Action(object); + } + else { + action = CurrentObject[0]->What_Action(cell); + } + + /* + ** If this is not a valid radar map action then we are not going to do + ** anything. + */ + switch (action) { + case ACTION_MOVE: + case ACTION_NOMOVE: + case ACTION_ATTACK: + case ACTION_ENTER: + case ACTION_CAPTURE: + case ACTION_SABOTAGE: + break; + + default: + action = ACTION_NONE; + object = NULL; + break; + } + + /* + ** On the radar map the only reason we would want the normal cursor to + ** appear is if we were over one of our own selected units. Otherwise + ** we can't move there. + **/ + if (action == ACTION_NONE) { + if (object && object->Is_Selected_By_Player()) { + object = NULL; + } + else { + action = ACTION_NOMOVE; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Mouse_Right_Press(); + } + + /* + ** When the mouse buttons aren't pressed, only the mouse cursor shape is processed. + ** The shape changes depending on what object the mouse is currently over and what + ** object is currently selected. + */ + if (flags & LEFTUP) { + Map.Mouse_Left_Up(-1, shadow, object, action, true); + } + + /* + ** Normal actions occur when the mouse button is released. The press event is + ** intercepted and possible rubber-band mode is flagged. + */ + if (flags & LEFTPRESS) { + Map.Mouse_Left_Release(cell, cellx, celly, object, action, true); + } + + } + else { + + Map.Set_Default_Mouse(MOUSE_RADAR_CURSOR, !Map.IsZoomed); + + if (flags & LEFTPRESS) { + + cell = Map.RadarClass::Click_Cell_Calc(x, y); + if (cell != -1) { + int cellx = Cell_X(cell); + int celly = Cell_Y(cell); + cellx -= Lepton_To_Cell(Map.TacLeptonWidth) / 2; + cellx = max(cellx, Map.MapCellX); + celly -= Lepton_To_Cell(Map.TacLeptonHeight) / 2; + celly = max(celly, Map.MapCellY); + cell = XY_Cell(cellx, celly); + shadow = (!Map[cell].IsMapped && !Debug_Unshroud); + // shadow = (!Map[cell].IsVisible && !Debug_Unshroud); + Map.Set_Tactical_Position(Cell_Coord(cell)); + cell = Coord_Cell(Map.DesiredTacticalCoord); + Map.DisplayClass::IsToRedraw = true; + Map.Flag_To_Redraw(true); + Map.SpecialRadarFrame = 4; + } + } + + /* + ** A right mouse button press toggles the zoom mode. + */ + if (flags & RIGHTPRESS) { + Map.Zoom_Mode(cell); + } + } + } + } + if (result == -1) { + Map.Override_Mouse_Shape(MOUSE_NORMAL, true); + } + GadgetClass::Action(0, key); + return(true); +} + + +/*********************************************************************************************** + * RadarClass::Refresh_Cells -- Intercepts refresh request and updates radar if needed * + * * + * This routine intercepts the refresh cells request and if it detects that the sidebar * + * should be rerendered, it flags the radar map to redraw during the next draw operation. * + * * + * INPUT: cell -- The origin cell that the refresh cell offset list is based upon. * + * * + * list -- Pointer to the list of offsets from the origin cell that specifies the * + * cells to be flagged for redraw. If the list starts with the special * + * code to refresh the sidebar, then this routine recognizes it and flags * + * the radar map to be redrawn accordingly. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Refresh_Cells(CELL cell, short const* list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + DisplayClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * RadarClass::Set_Radar_Position -- Sets the radar position to center around specified cell. * + * * + * This routine will try to center the radar map around the cell position specified. * + * * + * INPUT: cell -- The cell to try and position the radar map around. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Radar_Position(CELL cell) +{ +#ifdef WIN32 + int oldx, oldy; + int newx, newy; + int newcell; + + if (ZoomFactor != 1) { + oldx = (Cell_X(cell) - MapCellX); + oldy = (Cell_Y(cell) - MapCellY); + } + else { + oldx = 0; + oldy = 0; + } + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + + newx = oldx + MapCellX; + newy = oldy + MapCellY; + newcell = XY_Cell(newx, newy); + + if (RadarCell != newcell) { + int forced = FALSE; + int xmod = newx; + int ymod = newy; + + int radx = (Cell_X(RadarCell)) - xmod; + int rady = (Cell_Y(RadarCell)) - ymod; + + RadarX = newx; + RadarY = newy; + RadarCell = newcell; + + if (Map.IsSidebarActive && Map.IsRadarActive) { + int radw = RadarCellWidth - ABS(radx); // Replicable width. + int radh = RadarCellHeight - ABS(rady); // Replicable height. + + if (radw < 1) forced = true; + if (radh < 1) forced = true; + + if (!forced && (radw != RadarWidth || radh != RadarHeight)) { + /* + ** Blit the section that is actually overlapping. + ** + ** If the video card isnt able to blit overlapped regions then we have + ** to do the blit in two stages via an intermediate buffer. The test to allow + ** overlapped blits is done in the library at the time of setting the video mode. + */ + if (OverlappedVideoBlits || !HidPage.Get_IsDirectDraw()) { + + /* + ** Overlapped blits are OK or we dont have a video memory hid page so blits are + ** always done in software by the library anyway. + */ + HidPage.Blit(HidPage, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + + } + else { + /* + ** Create a temporary intermediate surface + */ + GraphicBufferClass temp_surface; + temp_surface.Init((RadarWidth + 16) & 0xfffffff0, + (RadarHeight + 16) & 0xfffffff0, + NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + /* + ** Do the blit in 2 stages. + */ + HidPage.Blit(temp_surface, + (((radx < 0) ? -radx : 0) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? -rady : 0) * ZoomFactor) + RadY + RadOffY + BaseY, + 0, + 0, + RadarWidth, + RadarHeight); + + temp_surface.Blit(HidPage, + 0, + 0, + (((radx < 0) ? 0 : radx) * ZoomFactor) + RadX + RadOffX + BaseX, + (((rady < 0) ? 0 : rady) * ZoomFactor) + RadY + RadOffY + BaseY, + radw * ZoomFactor, + radh * ZoomFactor); + } + + /* + ** Now we need to flag the section of the map that is going to redraw. + */ + if (radx != 0) { + int min; + int max; + if (radx < 0) { // this mean regen the right edge + min = radw; + max = radw + ABS(radx); + } + else { // this mean regen the left edge + min = 0; + max = radx; + } + for (int x = min; x < max; x++) { + for (int y = 0; y < (int)RadarCellHeight; y++) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + if (newy != 0) { + int min; + int max; + if (rady < 0) { // this mean regen the bottom edge + min = radh; + max = radh + ABS(rady); + } + else { // this mean regen the top edge + min = 0; + max = rady; + } + for (int y = min; y < max; y++) { + for (int x = 0; x < (int)RadarCellWidth; x++) { + Radar_Pixel(XY_Cell(newx + x, newy + y)); + } + } + } + } + } + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + if (ZoomFactor > 4) { + FullRedraw = forced; + } + } + else { + RadarCursorRedraw = IsRadarActive; + IsToRedraw = IsRadarActive; + Flag_To_Redraw(false); + } +#else + + if (cell != RadarCell) { + int oldx = RadarX; + int oldy = RadarY; + CELL oldcell = RadarCell; + int x = Cell_X(cell); + int y = Cell_Y(cell); + + /* + ** If the new radar position is not too close to the edge of the + ** current radar display, then don't bother to change the radar position. + */ + if ((unsigned)(x - (RadarX + 10)) > RadarCellWidth - 20) { + oldx = (Cell_X(cell) - MapCellX) - RadarCellWidth / 2; + } + else { + oldx = Cell_X(RadarCell) - MapCellX; + } + if ((unsigned)(y - (RadarY + 10)) > RadarCellHeight - 20) { + oldy = (Cell_Y(cell) - MapCellY) - RadarCellHeight / 2; + } + else { + oldy = Cell_Y(RadarCell) - MapCellY; + } + +#ifdef NEVER + if ((unsigned)(x - (RadarX + 10)) > RadarWidth - 20 || (unsigned)(y - (RadarY + 10)) > RadarHeight - 20) { + oldx = (Cell_X(cell) - MapCellX) - RadarCellWidth / 2; + oldy = (Cell_Y(cell) - MapCellY) - RadarCellHeight / 2; + } + else { + oldx = Cell_X(RadarCell) - MapCellX; + oldy = Cell_Y(RadarCell) - MapCellY; + } +#endif + + Confine_Rect(&oldx, &oldy, RadarCellWidth, RadarCellHeight, MapCellWidth, MapCellHeight); + RadarX = oldx + MapCellX; + RadarY = oldy + MapCellY; + RadarCell = XY_Cell(RadarX, RadarY); + IsToRedraw = true; + Flag_To_Redraw(false); + if (oldcell != RadarCell) { + FullRedraw = IsRadarActive; + } + } +#endif +} + + +/*********************************************************************************************** + * RadarClass::Radar_Position -- Returns with the current position of the radar map. * + * * + * This returns the cell number of the upper left corner of the radar map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the radar map upper left corner cell position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +CELL RadarClass::Radar_Position(void) +{ + return(RadarCell); +} + + +/*********************************************************************************************** + * RadarClass::Set_Map_Dimensions -- Sets the tactical map dimensions. * + * * + * This routine is called when the tactical map changes its dimensions. This occurs when * + * the tactical map moves and when the sidebar pops on or off. * + * * + * INPUT: x,y -- The cell coordinate of the upper left corner of the tactical map. * + * * + * w,y -- The cell width and height of the tactical map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Map_Dimensions(int x, int y, int w, int h) +{ + Set_Radar_Position(XY_Cell(x, y)); + DisplayClass::Set_Map_Dimensions(x, y, w, h); +} + + +/*********************************************************************************************** + * RadarClass::Set_Tactical_Position -- Sets the map's tactical position and adjusts radar to * + * * + * This routine is called when the tactical map is to change position. The radar map might * + * be adjusted as well by this routine. * + * * + * INPUT: coord -- The new coordinate to use for the upper left corner of the tactical * + * map. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/17/1995 JLB : Created. * + *=============================================================================================*/ +void RadarClass::Set_Tactical_Position(COORDINATE coord) +{ + DisplayClass::Set_Tactical_Position(coord); + Set_Radar_Position(Coord_Cell(TacticalCoord)); +} + + +/*********************************************************************************************** + * RadarClass::Cell_On_Radar -- Determines if a cell is currently visible on radar. * + * * + * This routine will examine the specified cell number and return whether it is visible * + * on the radar map. This depends on the radar map position. * + * * + * INPUT: cell -- The cell number to check. * + * * + * OUTPUT: Is the specified cell visible on the radar map currently? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/03/1995 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Cell_On_Radar(CELL cell) +{ + if ((unsigned)cell > MAP_CELL_TOTAL) + return(false); + + if (!IsZoomed) { + return(true); + } + return(!(((Cell_X(cell) - RadarX) > RadarCellWidth) || ((Cell_Y(cell) - RadarY) > RadarCellHeight))); +} + + +/*********************************************************************************************** + * RadarClass::Player_Names -- toggles the Player-Names mode of the radar map * + * * + * INPUT: * + * on true = turn on; false = turn off * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Player_Names(bool on) +{ + IsPlayerNames = on; + IsToRedraw = true; + if (on) { + Flag_To_Redraw(true); + // Flag_To_Redraw(false); + } + else { + Flag_To_Redraw(true); // force drawing of the plate + } +} + + +/*********************************************************************************************** + * RadarClass::Spy_Next_House -- advances to the next house we're spying on, or returns NULL * + * * + * INPUT: * + * * + * OUTPUT: * + * 0 = no house to spy on, 1 = found house to spy on * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Spy_Next_House(void) +{ + bool tospy = false; + int spiedby = (1 << (PlayerPtr->Class->House)); + + IsPlayerNames = false; + IsToRedraw = true; + + HousesType maxhouse; + HousesType firsthouse; + HousesType house; + + if (Session.Type == GAME_NORMAL) { + firsthouse = HOUSE_SPAIN; + maxhouse = HOUSE_GOOD; + } + else { + firsthouse = HOUSE_MULTI1; + maxhouse = HOUSE_COUNT; + } + + if (IsHouseSpy) { + house = (HousesType)(SpyingOn + 1); + } + else { + house = firsthouse; + } + + if (house < firsthouse) house = firsthouse; + + while (house < maxhouse && !tospy) { + HouseClass* hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive && hptr != PlayerPtr) { + if (hptr->RadarSpied & spiedby) { + tospy = true; + SpyingOn = house; + break; + } + } + house++; + } + + IsHouseSpy = tospy; + + Flag_To_Redraw(true); // force drawing of the plate + return(tospy); +} + + +/*********************************************************************************************** + * Draw_House_Info -- Print house statistics on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/20/1996 BWG : Created. * + *=============================================================================================*/ +bool RadarClass::Draw_House_Info(void) +{ + int y; + char txt[40]; + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return false; + } + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + y = RadY + RadOffY + (2 * RESFACTOR); + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); + + Fancy_Text_Print(TXT_SPY_INFO, RadX + RadOffX + (6 * RESFACTOR), y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += 7 * RESFACTOR; + + HouseClass* ptr = HouseClass::As_Pointer(SpyingOn); + if (ptr && (ptr->RadarSpied & (1 << (PlayerPtr->Class->House)))) { + PlayerColorType c_idx; + RemapControlType* color; + TextPrintType style; + + c_idx = ptr->RemapColor; + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + + /* + ** Print house's name below 'spy report' + */ + txt[0] = 0; + sprintf(txt, "%s", ptr->IniName);//Text_String(ptr->Class->FullName)); +// sprintf(txt, "%s", ptr->Name());//Text_String(ptr->Class->FullName)); + if (strlen(txt)) { + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + } + else { + strcpy(txt, "________"); + } + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_BUILDNGS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + + // count & print buildings + itoa(ptr->CurBuildings, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_UNITS, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + // count & print units + itoa(ptr->CurUnits, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + 6 * RESFACTOR, y, color, BLACK, style); + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_INFANTRY, RadX + RadOffX + (6 * RESFACTOR), y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + // count & print infantry + itoa(ptr->CurInfantry, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX + (6 * RESFACTOR), y, color, BLACK, style); +#if(0) + y += (6 * RESFACTOR) + 1; + + Fancy_Text_Print(TXT_AIRCRAFT, RADAR_X + RADAR_OFF_X + 6, y, + &ColorRemaps[PCOLOR_GREY], TBLACK, + TPF_6PT_GRAD | TPF_NOSHADOW); + y += (6 * RESFACTOR) + 1; + // count & print aircraft + for (i = AIRCRAFT_NONE + 1, count = 0; i < AIRCRAFT_COUNT; i++) { + count += ptr->AQuantity[i]; + } + itoa(count, txt, 10); + Fancy_Text_Print(txt, RadX + RadOffX, y, + color, BLACK, style); +#endif + return(true); + } + else { + return(false); + } +} + + +/*********************************************************************************************** + * Draw_Names -- draws players' names on the radar map * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/07/1995 BRR : Created. * + *=============================================================================================*/ +void RadarClass::Draw_Names(void) +{ + PlayerColorType c_idx; + HousesType house; + HouseClass* ptr; + int y; + char txt[40]; + HousesType h; + int kills; + RemapControlType* color; + TextPrintType style; + + /* + ** Do nothing if the sidebar isn't there + */ + if (!Map.IsSidebarActive) { + return; + } + + // CC_Draw_Shape(RadarAnim, RADAR_ACTIVATED_FRAME, RADAR_X, RADAR_Y+1, + // WINDOW_MAIN, SHAPE_NORMAL); + CC_Draw_Shape(RadarFrame, 1, RadX, RadY + 1 * RESFACTOR, WINDOW_MAIN, SHAPE_NORMAL); + + y = RadY + RadOffY + (2 * RESFACTOR); + + Fancy_Text_Print(TXT_NAME_COLON, RadX + RadOffX, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW); + Fancy_Text_Print(TXT_KILLS_COLON, RadX + RadOffX + RadIWidth - 2, y, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_RIGHT | TPF_6PT_GRAD | TPF_NOSHADOW); + y += 6 * RESFACTOR + 1; + + LogicPage->Draw_Line(RadX + RadOffX, y, RadX + RadOffX + RadIWidth - 1, y, LTGREY); + y += 2 * RESFACTOR; + + for (house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + ptr = HouseClass::As_Pointer(house); + + if (!ptr) continue; + + /* + ** Decode this house's color + */ + c_idx = ptr->RemapColor; + + if (ptr->IsDefeated) { + color = &GreyScheme; + style = TPF_6PT_GRAD | TPF_NOSHADOW; + } + else { + color = &ColorRemaps[c_idx]; + style = TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW; + } + + /* + ** Initialize our message + */ + txt[0] = 0; + // sprintf(txt, "%s", ptr->Name()); + sprintf(txt, "%s", ptr->IsHuman ? ptr->IniName : Text_String(TXT_COMPUTER)); + + if (strlen(txt) == 0) { + strcpy(txt, "________"); + } + + /* + ** Print the player name, and the # of kills + */ +#ifdef WIN32 + if (strlen(txt) > 9) { + txt[9] = '.'; + txt[10] = '\0'; + } +#else + if (strlen(txt) > 8) { + txt[8] = '.'; + txt[9] = '\0'; + } +#endif + Fancy_Text_Print(txt, RadX + RadOffX, y, color, TBLACK, style); + + kills = 0; + for (h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + kills += ptr->UnitsKilled[h]; + kills += ptr->BuildingsKilled[h]; + } + sprintf(txt, "%2d", kills); + Fancy_Text_Print(txt, RadX + RadOffX + RadIWidth - 2, y, color, TBLACK, style | TPF_RIGHT); + + y += 6 * RESFACTOR + 1; + + } + + Map.Repair.Draw_Me(true); + Map.Upgrade.Draw_Me(true); + Map.Zoom.Draw_Me(true); +} + + +void RadarClass::Activate_Pulse(void) +{ + if (IsRadarActive || PlayerPtr->IsGPSActive) { + IsPulseActive = true; + RadarPulseFrame = 0; + } +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Active -- Determines if the radar map is currently being displayed. * + * * + * Determines if the radar map is currently being displayed. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map currently being displayed as active? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Active(void) +{ + return(IsRadarActive || PlayerPtr->IsGPSActive); + // return IsRadarActive || PlayerPtr->IsGPSActive; +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Activating -- Determines if the radar map is being activated. * + * * + * Determines if the radar map is being activated. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map currently being activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/2019 SKY : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Activating(void) +{ + return IsRadarActivating; +} + + +/*********************************************************************************************** + * RadarClass::Is_Radar_Existing -- Queries to see if radar map is available. * + * * + * This will determine if the radar map is available. If available, the radar will show * + * representations of terrain, units, and buildings. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar map available to be displayed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Is_Radar_Existing(void) +{ + return(DoesRadarExist || PlayerPtr->IsGPSActive); +} + + +/*********************************************************************************************** + * RadarClass::Get_Jammed -- Fetch the current radar jammed state for the player. * + * * + * This will fetch the current state of the radar jamming for the player. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the radar currently jammed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +bool RadarClass::Get_Jammed(HousesType house) const +{ + for (int i = 0; i < Houses.Count(); ++i) { + HouseClass* player = Houses.Ptr(i); + if (player && player->Class->House == house) { + return Get_Jammed(player); + } + } + return false; +} + +bool RadarClass::Get_Jammed(HouseClass* player) const +{ + assert(player); + if (player->IsGPSActive) return(false); + int shift = (int)player->Class->House; + return (IsRadarJammedByPlayerMask & (1 << shift)) ? true : false; +} + +void RadarClass::Set_Jammed(HousesType house, bool jam) +{ + int shift = (int)house; + if (jam) { + IsRadarJammedByPlayerMask |= (1 << shift); + } + else { + IsRadarJammedByPlayerMask &= ~(1 << shift); + } +} + +void RadarClass::Set_Jammed(HouseClass* player, bool jam) +{ + if (player && player->Class) { + Set_Jammed(player->Class->House, jam); + } +} + +void RadarClass::Flag_Cell(CELL cell) +{ + // Radar_Pixel(cell); + DisplayClass::Flag_Cell(cell); +} \ No newline at end of file diff --git a/code/redalert/radar.h b/code/redalert/radar.h new file mode 100644 index 0000000..47bb603 --- /dev/null +++ b/code/redalert/radar.h @@ -0,0 +1,250 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADAR_H +#define RADAR_H + +#include "display.h" + +class RadarClass: public DisplayClass +{ + public: + RadarClass(void); + RadarClass(NoInitClass const & x) : DisplayClass(x) {}; + + /* + ** The dimensions and coordinates of the radar map. + */ + int RadX; + int RadOffX; + int RadY; + int RadOffY; + int RadWidth; + int RadHeight; + int RadIWidth; + int RadIHeight; + int RadPWidth; + int RadPHeight; + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + + virtual void Flag_Cell(CELL cell); + virtual bool Map_Cell(CELL cell, HouseClass *house, bool check_radar_spied = true, bool and_for_allies = true); // Added check_radar_spied parameter to prevent recursion. ST - 8/6/2019 10:16AM. Added and_for_allies ST - 10/31/2019 1:18PM + virtual bool Jam_Cell(CELL cell, HouseClass * house); + virtual bool UnJam_Cell(CELL cell, HouseClass * house); + virtual CELL Click_Cell_Calc(int x, int y) const; + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + virtual void Refresh_Cells(CELL cell, short const *list); + virtual void Set_Map_Dimensions(int x, int y, int w, int h); + virtual void Set_Tactical_Position(COORDINATE coord); + void Zoom_Mode(CELL cell); + int Click_In_Radar(int &x, int &y, bool change=false) const; + void Cell_XY_To_Radar_Pixel(int cellx, int celly, int &x, int &y); + + bool Is_Zoomable(void) const; + void Set_Radar_Position(CELL cell); + CELL Radar_Position(void); + bool Radar_Activate(int control); + void Plot_Radar_Pixel(CELL cell); + void Radar_Pixel(CELL cell); + void Coord_To_Radar_Pixel(COORDINATE coord, int &x, int &y); + void Cursor_Cell(CELL cell, int value); + void RadarClass::Mark_Radar(int x1, int y1, int x2, int y2, int value, int barlen); + void Radar_Cursor(int forced = false); + void Render_Terrain(CELL cell, int x, int y, int size); + bool Cell_On_Radar(CELL cell); + void Render_Infantry(CELL cell, int x, int y, int size); + void Render_Overlay(CELL cell, int x, int y, int size); + void Radar_Anim(void); + bool Is_Radar_Active(void); + bool Is_Radar_Activating(void); + bool Is_Radar_Existing(void); + + /* + ** Toggles player names on & off + */ + void Player_Names(bool on); + int Is_Player_Names(void) {return IsPlayerNames;} + bool Spying_On_House(void) {return IsHouseSpy;} + void Draw_Names(void); + bool Draw_House_Info(void); + int Is_Zoomed(void) {return IsZoomed;} + bool Get_Jammed(HousesType house) const; + bool Get_Jammed(HouseClass *player) const; + void Set_Jammed(HousesType house, bool jam); + void Set_Jammed(HouseClass *player, bool jam); + bool Spy_Next_House(void); + void Activate_Pulse(void); + + protected: + + /* + ** Radar map constant values. + */ + enum RadarClassEnums { + RADAR_ACTIVATED_FRAME=22, + MAX_RADAR_FRAMES = 41 + }; + + /* + ** If the radar map must be completely redrawn, then this flag will be true. + ** Typical causes of this would be when the radar first appears, or when the + ** screen has been damaged. + */ + unsigned IsToRedraw:1; + unsigned RadarCursorRedraw:1; + + /* + ** If the radar map is visible then this flag is true. + */ + unsigned DoesRadarExist:1; + unsigned IsRadarActive:1; + unsigned IsRadarActivating:1; + unsigned IsRadarDeactivating:1; + + /* + ** Per-player radar jammed flag. + */ + unsigned int IsRadarJammedByPlayerMask; + + /* + ** Flag to tell whether sonar pulse should be displayed on radar map + */ + unsigned IsPulseActive:1; + int RadarPulseFrame; + + /* + ** Special radar frame is set when a new location is selected on the + ** radar map. It counts down through the special radar cursors until + ** either the radar cursor becomes normal or the radar cursor is moved + ** again. + */ + int SpecialRadarFrame; + int RadarAnimFrame; + + static void const * RadarAnim; + static void const * RadarPulse; + static void const * RadarFrame; + + /* + ** This gadget class is used for capturing input to the tactical map. All mouse input + ** will be routed through this gadget. + */ + class RTacticalClass : public GadgetClass { + public: + RTacticalClass(void) : GadgetClass(0,0,0,0,LEFTPRESS|LEFTRELEASE|LEFTHELD|LEFTUP|RIGHTPRESS,true) {}; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + friend class RadarClass; + }; + friend class RTacticalClass; + + /* + ** This is the "button" that tracks all input to the tactical map. + ** It must be available to derived classes, for Save/Load purposes. + */ + static RTacticalClass RadarButton; + + private: + + /* + ** The current radar position as the upper left corner cell for the + ** radar map display. The width and height is controlled by the + ** actual dimensions of the radar map display box (in pixels). + */ + unsigned RadarX; + unsigned RadarY; + unsigned RadarCellWidth; + unsigned RadarCellHeight; + unsigned RadarCell; + + /* + ** This is the origin (pixel offsets) for the upper left corner + ** of the radar map within the full radar map area of the screen. + ** This is biased so that the radar map, when smaller than full + ** size will appear centered. + */ + unsigned BaseX; + unsigned BaseY; + + unsigned RadarWidth; + unsigned RadarHeight; + + /* + ** If the radar map is in zoom mode, then this value will be true. + */ + unsigned IsZoomed:1; + + /* + ** This flag is true if the radar map is in its special show-the-player + ** names mode. + */ + unsigned IsPlayerNames:1; + + /* + ** This flag is true if the radar map is in its special show-the-units + ** of-another-house mode. + */ + unsigned IsHouseSpy:1; + + /* + ** This is the zoom factor to use. This value is the number of pixels wide + ** each cell will occupy on the radar map. Completely zoomed out would be a + ** value of 1. + */ + int ZoomFactor; + + /* + ** If we're spying on a house's radar facility, this field shows the + ** name of the house we're spying on. + */ + HousesType SpyingOn; + + /* + ** This is the list of radar pixels that need to be updated. Only a partial + ** list is maintained for maximum speed. + */ + unsigned PixelPtr; + enum PixelStackEnums {PIXELSTACK=400}; + CELL PixelStack[PIXELSTACK]; +}; + + +#endif diff --git a/code/redalert/radio.cpp b/code/redalert/radio.cpp new file mode 100644 index 0000000..8c82910 --- /dev/null +++ b/code/redalert/radio.cpp @@ -0,0 +1,290 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADIO.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/* +** These are the text representations of the radio messages that can be transmitted. +*/ +char const * RadioClass::Messages[RADIO_COUNT] = { + "static (no message)", + "Roger.", + "Come in.", + "Over and out.", + "Requesting transport.", + "Attach to transport.", + "I've got a delivery for you.", + "I'm performing load/unload maneuver. Be careful.", + "I'm clear.", + "You are clear to unload. Driving away now.", + "Am unable to comply.", + "I'm starting construction now... act busy.", + "I've finished construction. You are free.", + "We bumped, redraw yourself please.", + "I'm trying to load up now.", + "May I become a passenger?", + "Are you ready to receive shipment?", + "Are you trying to become a passenger?", + "Move to location X.", + "Do you need to move?", + "All right already. Now what?", + "I'm a passenger now.", + "Backup into refinery now.", + "Run away!", + "Tether established.", + "Tether broken.", + "Repair one step.", + "Are you prepared to fight?", + "Attack this target please.", + "Reload one step.", + "Circumstances prevent success.", + "All done with the request.", + "Do you need service depot work?", + "Are you sitting on service depot?" +}; + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * RadioClass::Debug_Dump -- Displays the current status of the radio to the mono monitor. * + * * + * This displays the radio connection value to the monochrome monitor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void RadioClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + + mono->Set_Cursor(29, 7);mono->Printf("0-%-47s", Messages[Old[0]]); + mono->Set_Cursor(29, 8);mono->Printf("1-%-47s", Messages[Old[1]]); + mono->Set_Cursor(29, 9);mono->Printf("2-%-47s", Messages[Old[2]]); + if (Radio) { + mono->Set_Cursor(20, 7);mono->Printf("%08X", Radio->As_Target()); + } + MissionClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * RadioClass::Receive_Message -- Handles receipt of a radio message. * + * * + * This is the base version of what should happen when a radio message is received. It * + * turns the radio off when the "OVER_OUT" message is received. All other messages are * + * merely acknowledged with a "ROGER". * + * * + * INPUT: from -- The object that is initiating this radio message (always valid). * + * * + * message -- The radio message received. * + * * + * param -- Reference to optional value that might be used to return more * + * information than can be conveyed in the simple radio response * + * messages. * + * * + * OUTPUT: Returns with the response radio message. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 09/24/1994 JLB : Streamlined to be only a communications carrier. * + * 05/22/1995 JLB : Recognized who is sending the message * + * 06/05/1996 JLB : Radio message history tracking. * + *=============================================================================================*/ +RadioMessageType RadioClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + /* + ** Keep a record of the last message received by this radio. + */ + if (message != Old[0]) { + Old[2] = Old[1]; + Old[1] = Old[0]; + Old[0] = message; + } + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + ** This only applies if the message is coming from the object that + ** has an established conversation with this object. + */ + if (from == Radio && message == RADIO_OVER_OUT) { + MissionClass::Receive_Message(from, message, param); + Radio_Off(); + return(RADIO_ROGER); + } + + /* + ** The "hello" message is an attempt to establish contact. If this radio + ** is already in an established conversation with another object, then + ** return with "negative". If all is well, return with "roger". + */ + if (message == RADIO_HELLO && Strength) { + if (Radio == from || Radio == NULL) { + Radio = from; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(MissionClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmit message from one object to another. * + * * + * This routine is used to transmit a radio message from this object to another. Most * + * inter object coordination is handled through this mechanism. * + * * + * INPUT: to -- Pointer to the object that will receive the radio message. * + * * + * message -- The message itself (see RadioType). * + * * + * param -- Optional reference to parameter that might be used to pass or * + * receive additional information. * + * * + * OUTPUT: Returns with the response radio message from the receiving object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, long & param, RadioClass * to) +{ + assert(IsActive); + + if (to == NULL) { + to = Contact_With_Whom(); + } + + /* + ** If there is no target for the radio message, then always return static. + */ + if (to == NULL) return(RADIO_STATIC); + + /* + ** Handle some special case processing that occurs when certain messages + ** are transmitted. + */ + if (to == Radio && message == RADIO_OVER_OUT) { + Radio = 0; + } + + /* + ** If this object is not in radio contact but the message + ** indicates that radio contact should be established, then + ** try to do so. If the other party agrees then contact + ** is established. + */ + if (message == RADIO_HELLO) { + Transmit_Message(RADIO_OVER_OUT); + if (to->Receive_Message(this, message, param) == RADIO_ROGER) { + Radio = to; + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + } + + return(to->Receive_Message(this, message, param)); +} + + +/*********************************************************************************************** + * RadioClass::Limbo -- When limboing a unit will always break radio contact. * + * * + * This routine will break radio contact as the object is entering limbo state. * + * * + * INPUT: none * + * * + * OUTPUT: Was the object successfully limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +bool RadioClass::Limbo(void) +{ + assert(IsActive); + + if (!IsInLimbo) { + Transmit_Message(RADIO_OVER_OUT); + } + return(MissionClass::Limbo()); +} + + +/*********************************************************************************************** + * RadioClass::Transmit_Message -- Transmits a message to the object specified. * + * * + * This routine will transmit the specified message to the object. This routine differs * + * from the normal Transmit_Message in that the LParam value is "faked" into the * + * parameter list. It is presumed that the message sent with this function does not * + * require the LParam. * + * * + * INPUT: message -- The message to transmit. * + * * + * to -- The requested receiver of this message. * + * * + * OUTPUT: Returns with the radio response from the receiver. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +RadioMessageType RadioClass::Transmit_Message(RadioMessageType message, RadioClass * to) +{ + assert(IsActive); + + return(Transmit_Message(message, LParam, to)); +} diff --git a/code/redalert/radio.h b/code/redalert/radio.h new file mode 100644 index 0000000..011d8c6 --- /dev/null +++ b/code/redalert/radio.h @@ -0,0 +1,104 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RADIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RADIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 23, 1994 * + * * + * Last Update : April 23, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RADIO_H +#define RADIO_H + +#include "mission.h" + +class ObjectClass; +class TechnoClass; + + +/**************************************************************************** +** Radio contact is controlled by this class. It handles the mundane chore +** of keeping the radio contact alive as well as broadcasting messages +** to the receiving radio. Radio contact is primarily used when one object +** is in "command" of another. +*/ +class RadioClass : public MissionClass { + private: + + /* + ** This is a record of the last message received by this receiver. + */ + RadioMessageType Old[3]; + + /* + ** This is the object that radio communication has been established + ** with. Although is is only a one-way reference, it is required that + ** the receiving radio is also tuned to the object that contains this + ** radio set. + */ + RadioClass * Radio; + + /* + ** This is a text representation of all the possible radio messages. This + ** text is used for monochrome debug printing. + */ + static char const * Messages[RADIO_COUNT]; + + public: + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + RadioClass(RTTIType rtti, int id) : MissionClass(rtti, id), Radio(0) {}; + RadioClass(NoInitClass const & x) : MissionClass(x) {}; + virtual ~RadioClass(void) {Radio=0;}; + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + bool In_Radio_Contact(void) const {return (Radio != 0);}; + void Radio_Off(void) {Radio = 0;}; + TechnoClass * Contact_With_Whom(void) const {return (TechnoClass *)Radio;}; + + // Inherited from base class(es). + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual RadioMessageType Transmit_Message(RadioMessageType message, long & param=LParam, RadioClass * to=NULL); + virtual RadioMessageType Transmit_Message(RadioMessageType message, RadioClass * to); + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + virtual bool Limbo(void); + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); +}; + +#endif diff --git a/code/redalert/ramfile.cpp b/code/redalert/ramfile.cpp new file mode 100644 index 0000000..7a6ff0e --- /dev/null +++ b/code/redalert/ramfile.cpp @@ -0,0 +1,472 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAMFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAMFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RAMFileClass::Close -- This will 'close' the ram file. * + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * RAMFileClass::Is_Open -- Is the file open? * + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * RAMFileClass::Open -- Opens the RAM based file. * + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * RAMFileClass::Read -- Read data from the file. * + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * RAMFileClass::Size -- Returns with the size of the ram file. * + * RAMFileClass::Write -- Copies data to the ram file. * + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "FUNCTION.H" +#include "ramfile.h" +#include + + +/*********************************************************************************************** + * RAMFileClass::RAMFileClass -- Construct a RAM buffer based "file" object. * + * * + * This routine will construct a "file" object that actually is just a front end processor * + * for a buffer. Access to the buffer will appear as if it was accessing a file. This * + * is different from the caching ability of the buffered file class in that this file * + * class has no real file counterpart. Typical use of this is for algorithms that were * + * originally designed for file processing, but are now desired to work with a buffer. * + * * + * INPUT: buffer -- Pointer to the buffer to use for this file. The buffer will already * + * contain data if the file is opened for READ. It will be considered * + * a scratch buffer if opened for WRITE. If the buffer pointer is NULL * + * but the length parameter is not, then a buffer will be allocated * + * of the specified length. This case is only useful for opening the * + * file for WRITE. * + * * + * length -- The length of the buffer submitted to this routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::RAMFileClass(void * buffer, int len) : + Buffer((char *)buffer), + MaxLength(len), + Length(len), + Offset(0), + Access(READ), + IsOpen(false), + IsAllocated(false) +{ + if (buffer == NULL && len > 0) { + Buffer = new char[len]; + IsAllocated = true; + } +} + + +/*********************************************************************************************** + * RAMFileClass::~RAMFileClass -- Destructor for the RAM file class. * + * * + * The destructor will deallocate any buffer that it allocated. Otherwise it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +RAMFileClass::~RAMFileClass(void) +{ + Close(); + if (IsAllocated) { + delete [] Buffer; + Buffer = NULL; + IsAllocated = false; + } +} + + +/*********************************************************************************************** + * RAMFileClass::Create -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Create(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Delete -- Effectively clears the buffer of data. * + * * + * This routine "clears" the buffer of data. It only makes the buffer appear empty by * + * resetting the internal length to zero. * + * * + * INPUT: none * + * * + * OUTPUT: Was the file reset in this fashion? * + * * + * WARNINGS: If the file was open, then resetting by this routine is not allowed. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Delete(void) +{ + if (!Is_Open()) { + Length = 0; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Available -- Determines if the "file" is available. * + * * + * RAM files are always available. * + * * + * INPUT: none * + * * + * OUTPUT: TRUE * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Available(int ) +{ + return(true); +} + + +/*********************************************************************************************** + * RAMFileClass::Is_Open -- Is the file open? * + * * + * This answers the question whether the file is open or not. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Is_Open(void) const +{ + return(IsOpen); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens a RAM based file for read or write. * + * * + * This routine will open the ram file. The name is meaningless so that parameter is * + * ignored. If the access mode is for write, then the pseudo-file can be written until the * + * buffer is full. If the file is opened for read, then the buffer is presumed to be full * + * of the data to be read. * + * * + * INPUT: name -- ignored. * + * * + * access-- The access method to use for the data buffer -- either READ or WRITE. * + * * + * OUTPUT: bool; Was the open successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(char const * , int access) +{ + return(Open(access)); +} + + +/*********************************************************************************************** + * RAMFileClass::Open -- Opens the RAM based file. * + * * + * This will open the ram based file for read or write. If the file is opened for write, * + * the the 'file' can be written up to the limit of the buffer's size. If the file is * + * opened for read, then the buffer is presumed to hold the data to be read. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int RAMFileClass::Open(int access) +{ + if (Buffer == NULL || Is_Open()) { + return(false); + } + + Offset = 0; + Access = access; + IsOpen = true; + + switch (access) { + default: + case READ: + break; + + case WRITE: + Length = 0; + break; + + case READ|WRITE: + break; + } + + return(Is_Open()); +} + + +/*********************************************************************************************** + * RAMFileClass::Read -- Read data from the file. * + * * + * Use this routine just like a normal file read. It will copy the bytes from the ram * + * buffer to the destination specified. When the ram buffer is exhausted, less bytes than * + * requested will be read. * + * * + * INPUT: buffer -- Pointer to the buffer to store the data to. * + * * + * size -- The number of bytes to 'read' into the specified buffer. * + * * + * OUTPUT: Returns with the number of bytes copied to the destination buffer. If the number * + * of bytes returned is less than requested, then this indicates that the source * + * buffer is exhausted. * + * * + * WARNINGS: The read function only applies to ram 'files' opened for read access. * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Read(void * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(READ); + hasopened = true; + } else { + if ((Access & READ) == 0) { + return(0); + } + } + + int tocopy = (size < (Length-Offset)) ? size : (Length-Offset); + memmove(buffer, &Buffer[Offset], tocopy); + Offset += tocopy; + + if (hasopened) { + Close(); + } + + return(tocopy); +} + + +/*********************************************************************************************** + * RAMFileClass::Seek -- Controls the ram file virtual read position. * + * * + * This routine will move the read/write position of the ram file to the location specified * + * by the offset and direction parameters. It functions similarly to the regular file * + * seek method. * + * * + * INPUT: pos -- The signed offset from the home position specified by the "dir" * + * parameter. * + * * + * dir -- The home position to base the position offset on. This will either be * + * the start of the file, the end of the file, or the current read/write * + * position. * + * * + * OUTPUT: Returns with the new file position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Seek(long pos, int dir) +{ + if (Buffer == NULL || !Is_Open()) { + return(Offset); + } + + int maxoffset = Length; + if ((Access & WRITE) != 0) { + maxoffset = MaxLength; + } + + switch (dir) { + case SEEK_CUR: + Offset += pos; + break; + + case SEEK_SET: + Offset = 0 + pos; + break; + + case SEEK_END: + Offset = maxoffset + pos; + break; + } + + if (Offset < 0) Offset = 0; + if (Offset > maxoffset) Offset = maxoffset; + + if (Offset > Length) { + Length = Offset; + } + + return(Offset); +} + + +/*********************************************************************************************** + * RAMFileClass::Size -- Returns with the size of the ram file. * + * * + * This will return the size of the 'real' data in the ram file. The real data is either * + * the entire buffer, if opened for READ, or just the written data if opened for WRITE. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes that the ram file system considers to be valid * + * data of the 'file'. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Size(void) +{ + return(Length); +} + + +/*********************************************************************************************** + * RAMFileClass::Write -- Copies data to the ram file. * + * * + * This function similarly to the regular write operation supported for files. It copies * + * the data specified to the current write position in the ram file. * + * * + * INPUT: buffer -- Pointer to the data to be written. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the actual number of bytes written. This will be less than requested * + * if the buffer is exhausted of space prematurely. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +long RAMFileClass::Write(void const * buffer, long size) +{ + if (Buffer == NULL || buffer == NULL || size == 0) { + return(0); + } + + bool hasopened = false; + if (!Is_Open()) { + Open(WRITE); + hasopened = true; + } else { + if ((Access & WRITE) == 0) { + return(0); + } + } + + int maxwrite = MaxLength - Offset; + int towrite = (size < maxwrite) ? size : maxwrite; + memmove(&Buffer[Offset], buffer, towrite); + Offset += towrite; + + if (Offset > Length) { + Length = Offset; + } + + if (hasopened) { + Close(); + } + + return(towrite); +} + + +/*********************************************************************************************** + * RAMFileClass::Close -- This will 'close' the ram file. * + * * + * Closing a ram file actually does nothing but record that it is now closed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void RAMFileClass::Close(void) +{ + IsOpen = false; +} diff --git a/code/redalert/ramfile.h b/code/redalert/ramfile.h new file mode 100644 index 0000000..b05efdc --- /dev/null +++ b/code/redalert/ramfile.h @@ -0,0 +1,110 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAMFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAMFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RAMFILE_H +#define RAMFILE_H + + +#include "wwfile.h" + + +class RAMFileClass : public FileClass +{ + public: + RAMFileClass(void * buffer, int len); + virtual ~RAMFileClass(void); + + virtual char const * File_Name(void) const {return("UNKNOWN");} + virtual char const * Set_Name(char const * ) {return(File_Name());} + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const * filename, int access=READ); + virtual int Open(int access=READ); + virtual long Read(void * buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const * buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(true);} + virtual void Error(int , int = false, char const * =NULL) {} + + operator char const * () {return File_Name();} + + private: + + /* + ** Pointer to the buffer that the "file" will reside in. + */ + char * Buffer; + + /* + ** The maximum size of the buffer. The file occupying the buffer + ** may be smaller than this size. + */ + int MaxLength; + + /* + ** The number of bytes in the sub-file occupying the buffer. + */ + int Length; + + /* + ** The current file position offset within the buffer. + */ + int Offset; + + /* + ** The file was opened with this access mode. + */ + int Access; + + /* + ** Is the file currently open? + */ + bool IsOpen; + + /* + ** Was the file buffer allocated during construction of this object? + */ + bool IsAllocated; +}; + + + + +#endif diff --git a/code/redalert/rand.cpp b/code/redalert/rand.cpp new file mode 100644 index 0000000..d46bfdd --- /dev/null +++ b/code/redalert/rand.cpp @@ -0,0 +1,170 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\rand.cpv 4.35 04 Jul 1996 16:14:56 JOE_BOSTIC $ */ +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 04/25/95 * + * * + * Last Update : June 17, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Sim_IRandom -- Returns minval to maxval, inclusive * + * Sim_Random -- Returns 0 - 255 * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "COORDA.h" +int SimRandIndex = 0; + +/*************************************************************************** + * Sim_Random -- Returns 0 - 255 * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0 - 255, at random * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Takes advantage of character math wrap. * + *=========================================================================*/ +int Sim_Random(void) +{ + static unsigned char _randvals[] = { + 0x47, 0xce, 0xc6, 0x6e, 0xd7, 0x9f, 0x98, 0x29, 0x92, 0x0c, 0x74, 0xa2, + 0x65, 0x20, 0x4b, 0x4f, 0x1e, 0xed, 0x3a, 0xdf, 0xa5, 0x7d, 0xb5, 0xc8, + 0x86, 0x01, 0x81, 0xca, 0xf1, 0x17, 0xd6, 0x23, 0xe1, 0xbd, 0x0e, 0xe4, + 0x62, 0xfa, 0xd9, 0x5c, 0x68, 0xf5, 0x7f, 0xdc, 0xe7, 0xb9, 0xc4, 0xb3, + 0x7a, 0xd8, 0x06, 0x3e, 0xeb, 0x09, 0x1a, 0x31, 0x3f, 0x46, 0x28, 0x12, + 0xf0, 0x10, 0x84, 0x76, 0x3b, 0xc5, 0x53, 0x18, 0x14, 0x73, 0x7e, 0x59, + 0x48, 0x93, 0xaa, 0x1d, 0x5d, 0x79, 0x24, 0x61, 0x1b, 0xfd, 0x2b, 0xa8, + 0xc2, 0xdb, 0xe8, 0x2a, 0xb0, 0x25, 0x95, 0xab, 0x96, 0x83, 0xfc, 0x5f, + 0x9c, 0x32, 0x78, 0x9a, 0x9e, 0xe2, 0x8e, 0x35, 0x4c, 0x41, 0xa1, 0x69, + 0x5a, 0xfe, 0xa7, 0xa4, 0xf6, 0x6d, 0xc1, 0x58, 0x0a, 0xcf, 0xea, 0xc3, + 0xba, 0x85, 0x99, 0x8d, 0x36, 0xb6, 0xdd, 0xd3, 0x04, 0xe6, 0x45, 0x0d, + 0x60, 0xae, 0xa3, 0x22, 0x4d, 0xe9, 0xc9, 0x9b, 0xb7, 0x0f, 0x02, 0x42, + 0xf9, 0x0b, 0x8f, 0x43, 0x44, 0x87, 0x70, 0xbe, 0xe3, 0xf8, 0xee, 0xa9, + 0xbc, 0xc0, 0x67, 0x33, 0x16, 0x37, 0x57, 0xad, 0x5e, 0x9d, 0x64, 0x40, + 0x54, 0x05, 0x2c, 0xe0, 0xb2, 0x97, 0x08, 0xaf, 0x75, 0x8a, 0x5b, 0xfb, + 0x4e, 0xbf, 0x91, 0xf3, 0xcb, 0x7c, 0x63, 0xef, 0x89, 0x52, 0x6c, 0x2f, + 0x21, 0x4a, 0xf7, 0xcd, 0x2e, 0xf4, 0xc7, 0x6f, 0x19, 0xb1, 0x66, 0xcc, + 0x90, 0x8c, 0x50, 0x51, 0x26, 0x7b, 0xda, 0x49, 0x80, 0x30, 0x55, 0x1f, + 0xd2, 0xb4, 0xd1, 0xd5, 0x6b, 0xf2, 0x72, 0xbb, 0x13, 0x3d, 0xff, 0x15, + 0x38, 0xe5, 0xd4, 0xde, 0x2d, 0x27, 0x94, 0xa0, 0xd0, 0x39, 0x82, 0x8b, + 0x03, 0xac, 0x3c, 0x34, 0x77, 0xb8, 0xec, 0x00, 0x07, 0x1c, 0x88, 0xa6, + 0x56, 0x11, 0x71, 0x6a, + }; + + ((unsigned char&)SimRandIndex)++; +// SimRandIndex &= 0xff; + return(_randvals[SimRandIndex]); +} + + +/*************************************************************************** + * Sim_IRandom -- returns minval to maxval, inclusive * + * * + * INPUT: * + * minval,maxval range limits * + * * + * OUTPUT: * + * random value * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 04/25/1995 BRR : Created. * + * 06/17/1995 JLB : Uses fixed point math helper routine. * + *=========================================================================*/ +int Sim_IRandom(int minval, int maxval) +{ + return(Fixed_To_Cardinal((maxval-minval), Sim_Random()) + minval); +} + + +/* +** This routine can be used to create the _RandVals[] table. It will be +** different every time it's run. +*/ +#if 0 +#include +#include +#include +#include + +void main(void); + +void main(void) +{ + int i; + FILE *fp; + int r; + char is_used[256]; + char rand_val[256]; + + srand ( time ( NULL ) ) ; + + for (i = 0; i < 256; i++) { + is_used[i] = 0; + } + + /* + ** Store the digits 0 to 255 in a random order within the 'rand_val' array + ** This ensures our random number sequence represents every value. + */ + for (i = 0; i < 256; i++) { + if (kbhit()!=0) { + printf("ABORTED!\n"); + break; + } + r = (rand() * 256) / RAND_MAX; // r is the index into array + if (is_used[r]==0) { + is_used[r] = 1; + rand_val[r] = i; + } else { + i--; + } + } + + fp = fopen("rand.cpp","wt"); + if (fp==NULL) { + printf("File error\n"); + exit(0); + } + + fprintf(fp, "unsigned char _RandVals[] = {\n"); + for (i = 0; i < 256; i++) { + fprintf(fp,"0x%02x,\n",rand_val[i]); + } + fprintf(fp,"};\n"); + fclose(fp); + +} + +#endif + diff --git a/code/redalert/random.cpp b/code/redalert/random.cpp new file mode 100644 index 0000000..337c412 --- /dev/null +++ b/code/redalert/random.cpp @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RANDOM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RAND.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/28/96 * + * * + * Last Update : February 28, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomClass::RandomClass -- Constructor for the random number class. * + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * RandomClass::operator() -- Ranged random number generator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "random.h" +#ifdef RANDOM_COUNT +#include +extern long Frame; +#endif + +/*********************************************************************************************** + * RandomClass::RandomClass -- Constructor for the random number class. * + * * + * This constructor can take an integer as a parameter. This allows the class to be * + * constructed by assigning an integer to an existing object. The compiler creates a * + * temporary with the constructor and then performs a copy constructor operation. * + * * + * INPUT: seed -- The optional starting seed value to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +RandomClass::RandomClass(unsigned seed) : + Seed(seed) +{ +#ifdef RANDOM_COUNT + Count1 = 0; + Count2 = 0; +#endif +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Fetches the next random number in the sequence. * + * * + * This routine will fetch the next random number in the sequence. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the next random number. * + * * + * WARNINGS: This routine modifies the seed value so that subsequent calls will not return * + * the same value. Take note that this routine only returns 15 bits of * + * random number. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator ()(void) +{ +#ifdef RANDOM_COUNT + Count1++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Transform the seed value into the next number in the sequence. + */ + Seed = (Seed * MULT_CONSTANT) + ADD_CONSTANT; + + /* + ** Extract the 'random' bits from the seed and return that value as the + ** random number result. + */ + return((Seed >> THROW_AWAY_BITS) & (~((~0) << SIGNIFICANT_BITS))); +} + + +/*********************************************************************************************** + * RandomClass::operator() -- Ranged random number generator. * + * * + * This function will return with a random number within the range specified. This replaces * + * the functionality of IRandom() in the old library. * + * * + * INPUT: minval -- The minimum value to return from the function. * + * * + * maxval -- The maximum value to return from the function. * + * * + * OUTPUT: Returns with a random number that falls between the minval and maxval (inclusive). * + * * + * WARNINGS: The range specified must fall within the maximum bit significance of the * + * random number algorithm (15 bits), otherwise the value returned will be * + * decidedly non-random. * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +int RandomClass::operator() (int minval, int maxval) +{ +#ifdef RANDOM_COUNT + Count2++; + printf("Frame %d: Random Count1:%d Count2:%d (%x)\n",Frame,Count1,Count2,Seed); +#endif + + /* + ** Test for shortcut case where the range is null and thus + ** the number to return is actually implicit from the + ** parameters. + */ + if (minval == maxval) return(minval); + + /* + ** Ensure that the min and max range values are in proper order. + */ + if (minval > maxval) { + int temp = minval; + minval = maxval; + maxval = temp; + } + + /* + ** Find the highest bit that fits within the magnitude of the + ** range of random numbers desired. Notice that the scan is + ** limited to the range of significant bits returned by the + ** random number algorithm. + */ + int magnitude = maxval - minval; + int highbit = SIGNIFICANT_BITS-1; + while ((magnitude & (1 << highbit)) == 0 && highbit > 0) { + highbit--; + } + + /* + ** Create a full bit mask pattern that has all bits set that just + ** barely covers the magnitude of the number range desired. + */ + int mask = ~( (~0L) << (highbit+1) ); + + /* + ** Keep picking random numbers until it fits within the magnitude desired. + */ + int pick = magnitude+1; + while (pick > magnitude) { + pick = operator()() & mask; + } + + /* + ** Finally, bias the random number pick to the start of the range + ** requested. + */ + return(pick + minval); +} + diff --git a/code/redalert/random.h b/code/redalert/random.h new file mode 100644 index 0000000..f13c542 --- /dev/null +++ b/code/redalert/random.h @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RANDOM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RANDOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/96 * + * * + * Last Update : February 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RANDOM_H +#define RANDOM_H + +/********************************************************************** +** Enable this define to turn on the random # counters. This must not +** be defined for the release version, or saved games won't work! +*/ +//#define RANDOM_COUNT + + +/* +** This class functions like a 'magic' int value that returns a random number +** every time it is read. To set the "random seed" for this, just assign a number +** to the object (just as you would if it were a true 'int' value). Take note that although +** this class will return an 'int', the actual significance of the random number is +** limited to 15 bits (0..32767). +*/ +class RandomClass { + public: + RandomClass(unsigned seed=0); + + operator int(void) {return(operator()());}; + int operator() (void); + int operator() (int minval, int maxval); + + enum { + SIGNIFICANT_BITS=15 // Random number bit significance. + }; + + unsigned long Seed; + +#ifdef RANDOM_COUNT + unsigned long Count1; + unsigned long Count2; +#endif + + protected: + /* + ** Internal working constants that are used to generate the next + ** random number. + */ + enum { + MULT_CONSTANT=0x41C64E6D, // K multiplier value. + ADD_CONSTANT=0x00003039, // K additive value. + THROW_AWAY_BITS=10 // Low bits to throw away. + }; +}; + +#endif diff --git a/code/redalert/rawfile.cpp b/code/redalert/rawfile.cpp new file mode 100644 index 0000000..8e9629d --- /dev/null +++ b/code/redalert/rawfile.cpp @@ -0,0 +1,1345 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * RawFileClass::Close -- Perform a closure of the file. * + * RawFileClass::Create -- Creates an empty file. * + * RawFileClass::Delete -- Deletes the file object from the disk. * + * RawFileClass::Error -- Handles displaying a file error message. * + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * RawFileClass::Open -- Opens the file object with the rights specified. * + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * RawFileClass::Size -- Determines size of file (in bytes). * + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include +#include +#include +#include +#include + +#include "rawfile.h" + +#ifndef WIN32 +#include +#include +#include +extern short Hard_Error_Occured; +#endif + + +/*********************************************************************************************** + * RawFileClass::Error -- Handles displaying a file error message. * + * * + * Display an error message as indicated. If it is allowed to retry, then pressing a key * + * will return from this function. Otherwise, it will exit the program with "exit()". * + * * + * INPUT: error -- The error number (same as the DOSERR.H error numbers). * + * * + * canretry -- Can this routine exit normally so that retrying can occur? If this is * + * false, then the program WILL exit in this routine. * + * * + * filename -- Optional filename to report with this error. If no filename is * + * supplied, then no filename is listed in the error message. * + * * + * OUTPUT: none, but this routine might not return at all if the "canretry" parameter is * + * false or the player pressed ESC. * + * * + * WARNINGS: This routine may not return at all. It handles being in text mode as well as * + * if in a graphic mode. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Error(int , int , char const * ) +{ +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Simple constructor for a file object. * + * * + * This constructor is called when a file object is created with a supplied filename, but * + * not opened at the same time. In this case, an assumption is made that the supplied * + * filename is a constant string. A duplicate of the filename string is not created since * + * it would be wasteful in that case. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +RawFileClass::RawFileClass(char const * filename) : + Rights(0), + BiasStart(0), + BiasLength(-1), + Handle(NULL_HANDLE), + Filename(filename), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::Set_Name -- Manually sets the name for a file object. * + * * + * This routine will set the name for the file object to the name specified. This name is * + * duplicated in free store. This allows the supplied name to be a temporarily constructed * + * text string. Setting the name in this fashion doesn't affect the closed or opened state * + * of the file. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * OUTPUT: Returns with a pointer to the allocated copy of this filename. This pointer is * + * guaranteed to remain valid for the duration of this file object or until the name * + * is changed -- whichever is sooner. * + * * + * WARNINGS: Because of the allocation this routine must perform, memory could become * + * fragmented. * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +char const * RawFileClass::Set_Name(char const * filename) +{ + if (Filename != NULL && Allocated) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } + + if (filename == NULL) return(NULL); + + Bias(0); + + ((char *&)Filename) = strdup(filename); + if (Filename == NULL) { + Error(ENOMEM, false, filename); + } + Allocated = true; + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Assigns name and opens file in one operation. * + * * + * This routine will assign the specified filename to the file object and open it at the * + * same time. If the file object was already open, then it will be closed first. If the * + * file object was previously assigned a filename, then it will be replaced with the new * + * name. Typically, this routine is used when an anonymous file object has been crated and * + * now it needs to be assigned a name and opened. * + * * + * INPUT: filename -- The filename to assign to this file object. * + * * + * rights -- The open file access rights to use. * + * * + * OUTPUT: bool; Was the file opened? The return value of this is moot, since the open file * + * is designed to never return unless it succeeded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(char const * filename, int rights) +{ + Set_Name(filename); + return(Open(rights)); +} + + +/*********************************************************************************************** + * RawFileClass::Open -- Opens the file object with the rights specified. * + * * + * This routine is used to open the specified file object with the access rights indicated. * + * This only works if the file has already been assigned a filename. It is guaranteed, by * + * the error handler, that this routine will always return with success. * + * * + * INPUT: rights -- The file access rights to use when opening this file. This is a * + * combination of READ and/or WRITE bit flags. * + * * + * OUTPUT: bool; Was the file opened successfully? This will always return true by reason of * + * the error handler. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Open(int rights) +{ + Close(); + + /* + ** Verify that there is a filename associated with this file object. If not, then this is a + ** big error condition. + */ + if (Filename == NULL) { + Error(ENOENT, false); + } + + /* + ** Record the access rights used for this open call. These rights will be used if the + ** file object is duplicated. + */ + Rights = rights; + + /* + ** Repetitively try to open the file. Abort if a fatal error condition occurs. + */ + for (;;) { + + /* + ** Try to open the file according to the access rights specified. + */ + #ifndef WIN32 + Hard_Error_Occured = 0; + #endif + switch (rights) { + + /* + ** If the access rights are not recognized, then report this as + ** an invalid access code. + */ + default: + errno = EINVAL; + break; + + case READ: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + #endif + break; + + case WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_creat(Filename, 0, &Handle); + #endif + break; + + case READ|WRITE: + #ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + #else + _dos_open(Filename, O_RDWR|O_CREAT|SH_DENYWR, &Handle); + #endif + break; + } + + /* + ** Biased files must be positioned past the bias start position. + */ + if (BiasStart != 0 || BiasLength != -1) { + Seek(0, SEEK_SET); + } + + /* + ** If the handle indicates the file is not open, then this is an error condition. + ** For the case of the file cannot be found, then allow a retry. All other cases + ** are fatal. + */ + if (Handle == NULL_HANDLE) { + +#ifdef WIN32 +// return(false); + Error(GetLastError(), false, Filename); +// continue; +#else + /* + ** If this flag is set, then some hard error occurred. Just assume that the error + ** is probably a removed CD-ROM and allow a retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + } else { + if (errno == ENOENT) { + Error(ENOENT, true, Filename); + } else { + Error(errno, false, Filename); + } + } + continue; +#endif + } + break; + } + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Is_Available -- Checks to see if the specified file is available to open. * + * * + * This routine will examine the disk system to see if the specified file can be opened * + * or not. Use this routine before opening a file in order to make sure that is available * + * or to perform other necessary actions. * + * * + * INPUT: force -- Should this routine keep retrying until the file becomes available? If * + * in this case it doesn't become available, then the program will abort. * + * * + * OUTPUT: bool; Is the file available to be opened? * + * * + * WARNINGS: Depending on the parameter passed in, this routine may never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Is_Available(int forced) +{ +#ifndef WIN32 + bool open_failed; +#endif + + if (Filename == NULL) return(false); + + /* + ** If the file is already open, then is must have already passed the availability check. + ** Return true in this case. + */ + if (Is_Open()) return(true); + + /* + ** If this is a forced check, then go through the normal open channels, since those + ** channels ensure that the file must exist. + */ + if (forced) { + RawFileClass::Open(READ); + RawFileClass::Close(); + return(true); + } + + /* + ** Perform a raw open of the file. If this open fails for ANY REASON, including a missing + ** CD-ROM, this routine will return a failure condition. In all but the missing file + ** condition, go through the normal error recover channels. + */ + for (;;) { + +#ifdef WIN32 + Handle = CreateFile(Filename, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (Handle == NULL_HANDLE) { + return(false); + } + break; +#else + + Hard_Error_Occured = 0; + open_failed = _dos_open(Filename, O_RDONLY|SH_DENYNO, &Handle); + + /* + ** If DOS reports that everything is just fine except that the file is not present, + ** then return with this fact. Any other case will fall through to the error handler + ** routine. + */ + if (open_failed && errno == ENOENT) return(false); + + /* + ** If we got an access error it could be because there is no cd in + ** the drive. Call the error handler but allow a continue if it + ** returns. + */ + if (open_failed && errno == EACCES) { + Error(errno, true, Filename); + continue; + } + + /* + ** If the file could not be found, then return with this information. If a more + ** serious error occurred, then display the error message and abort. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (open_failed) { + /* + ** An unhandled error condition is fatal. Display the error message and then + ** abort. + */ + Error(errno, false, Filename); + } + } + if (!open_failed) break; +#endif + } + + /* + ** Since the file could be opened, then close it and return that the file exists. + */ +#ifdef WIN32 + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + if (_dos_close(Handle)) { + Error(errno, false, Filename); + } +#endif + Handle = NULL_HANDLE; + + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Close -- Perform a closure of the file. * + * * + * Close the file object. In the rare case of an error, handle it as appropriate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Some rare error conditions may cause this routine to abort the program. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Close(void) +{ + /* + ** If the file is open, then close it. If the file is already closed, then just return. This + ** isn't considered an error condition. + */ + if (Is_Open()) { + +#ifdef WIN32 + /* + ** Try to close the file. If there was an error (who knows what that could be), then + ** call the error routine. + */ + if (!CloseHandle(Handle)) { + Error(GetLastError(), false, Filename); + } +#else + for (;;) { + /* + ** Close the file. If there was an error in the close operation -- abort. + */ + Hard_Error_Occured = 0; + if (_dos_close(Handle)) { + + /* + ** By definition, this error can only be a bad file handle. This a fatal condition + ** of course, so abort with an error message. + */ + Error(errno, false, Filename); + } + + /* + ** In the condition (if it is even possible) of a hard error occurring, then + ** assume it is the case of missing media. Display an error message and try + ** again if indicated. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + break; + } + +#endif + + /* + ** At this point the file must have been closed. Mark the file as empty and return. + */ + Handle = NULL_HANDLE; + } +} + + +/*********************************************************************************************** + * RawFileClass::Read -- Reads the specified number of bytes into a memory buffer. * + * * + * This routine will read the specified number of bytes and place the data into the buffer * + * indicated. It is legal to call this routine with a request for more bytes than are in * + * the file. This condition can result in fewer bytes being read than requested. Determine * + * this by examining the return value. * + * * + * INPUT: buffer -- Pointer to the buffer to read data into. If NULL is passed, no read * + * is performed. * + * * + * size -- The number of bytes to read. If NULL is passed, then no read is * + * performed. * + * * + * OUTPUT: Returns with the number of bytes read into the buffer. If this number is less * + * than requested, it indicates that the file has been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Read(void * buffer, long size) +{ + long bytesread = 0; // Running count of the number of bytes read into the buffer. + int opened = false; // Was the file opened by this routine? + + /* + ** If the file isn't opened, open it. This serves as a convenience + ** for the programmer. + */ + if (!Is_Open()) { + + /* + ** The error check here is moot. Open will never return unless it succeeded. + */ + if (!Open(READ)) { + return(0); + } + opened = true; + } + + /* + ** A biased file has the requested read length limited to the bias length of + ** the file. + */ + if (BiasLength != -1) { + int remainder = BiasLength - Seek(0); + size = size < remainder ? size : remainder; + } + +#ifdef WIN32 + long total = 0; + while (size > 0) { + bytesread = 0; + + SetErrorMode(SEM_FAILCRITICALERRORS); + if (!ReadFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + size -= bytesread; + total += bytesread; + Error(GetLastError(), true, Filename); + SetErrorMode(0); + continue; + } + SetErrorMode(0); + size -= bytesread; + total += bytesread; + if (bytesread == 0) break; + } + bytesread = total; + +#else + + int readresult; + + /* + ** Read the file in convenient chunk sizes. When the actual number + ** of bytes read does not match the desired, then assume that the file + ** is exhausted and bail. This loop was adjusted to take into + ** consideration the fact that "read" returns a SIGNED value whereas + ** it takes an UNSIGNED value as the byte count. + */ + while (size) { + unsigned desired; // Bytes desired to be read this pass. + unsigned actual; // Actual number of bytes read. + + /* + ** Break the read request into chunks no bigger than the low level DOS read + ** can handle. + */ + desired = size < 32000L ? size : 32000L; + + Hard_Error_Occured = 0; + readresult = _dos_read(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume that it is the case of the CD-ROM or + ** floppy media having been removed. Display the error and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned from the read operation, then this indicates + ** either a bad file number or invalid access. These are fatal conditions, so + ** display the error and then abort. + */ + if (readresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** No error occurred during the read. Adjust the pointers and size counters and + ** loop again if more data is needed to be read. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + if (actual != desired) break; // No more data? + } + } + } +#endif //WIN32 + + /* + ** Close the file if it was opened by this routine and return + ** the actual number of bytes read into the buffer. + */ + if (opened) Close(); + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Write -- Writes the specified data to the buffer specified. * + * * + * This routine will write the data specified to the file. * + * * + * INPUT: buffer -- The buffer that holds the data to write. * + * * + * size -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. This routine catches the * + * case of a disk full condition, so this routine will always return with the number * + * matching the size request. * + * * + * WARNINGS: A fatal file condition could cause this routine to never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Write(void const * buffer, long size) +{ + long bytesread = 0; + int opened = false; // Was the file manually opened? + + /* + ** Check to open status of the file. If the file is open, then merely write to + ** it. Otherwise, open the file for writing and then close the file when the + ** output is finished. + */ + if (!Is_Open()) { + if (!Open(WRITE)) { + return(0); + } + opened = true; + } + +#ifdef WIN32 + if (!WriteFile(Handle, buffer, size, &(unsigned long&)bytesread, NULL)) { + Error(GetLastError(), false, Filename); + } + +#else + + int writeresult; + /* + ** Write the data to the file in chunks no bigger than what the low level DOS write + ** can handle. + */ + while (size) { + unsigned desired; // Bytes desired to be write this pass. + unsigned actual; // Actual number of bytes written. + + Hard_Error_Occured = 0; +// desired = (unsigned)MIN(size, Transfer_Block_Size()); + desired = size; + writeresult = _dos_write(Handle, buffer, desired, &actual); + + /* + ** If a hard error occurred, then assume it is the case of the media being + ** removed. Print the error message an retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; // Not technically needed, but to be consistent... + } else { + + /* + ** If negative one is returned by the DOS read, then this indicates a bad file + ** handle or invalid access. Either condition is fatal -- display error condition + ** and abort. + */ + if (writeresult != 0) { + Error(errno, false, Filename); + } else { + + /* + ** A successful write occurred. Update pointers and byte counter as appropriate. + */ + buffer = (char *)buffer + actual; + bytesread += actual; + size -= actual; + + /* + ** If the actual bytes written is less than requested, assume this is a case of + ** the disk being full. Consider this a fatal error condition. + */ + if (actual != desired) { + Error(ENOSPC, false, Filename); + } + } + } + } +#endif //WIN32 + + /* + ** Fixup the bias length if necessary. + */ + if (BiasLength != -1) { + if (Raw_Seek(0) > BiasStart+BiasLength) { + BiasLength = Raw_Seek(0) - BiasStart; + } + } + + /* + ** If this routine had to open the file, then close it before returning. + */ + if (opened) { + Close(); + } + + /* + ** Return with the number of bytes written. This will always be the number of bytes + ** requested, since the case of the disk being full is caught by this routine. + */ + return(bytesread); +} + + +/*********************************************************************************************** + * RawFileClass::Seek -- Reposition the file pointer as indicated. * + * * + * Use this routine to move the filepointer to the position indicated. It can move either * + * relative to current position or absolute from the beginning or ending of the file. This * + * routine will only return if it successfully performed the seek. * + * * + * INPUT: pos -- The position to seek to. This is interpreted as relative to the position * + * indicated by the "dir" parameter. * + * * + * dir -- The relative position to relate the seek to. This can be either SEEK_SET * + * for the beginning of the file, SEEK_CUR for the current position, or * + * SEEK_END for the end of the file. * + * * + * OUTPUT: This routine returns the position that the seek ended up at. * + * * + * WARNINGS: If there was a file error, then this routine might never return. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Seek(long pos, int dir) +{ + + /* + ** A file that is biased will have a seek operation modified so that the file appears to + ** exist only within the bias range. All bytes outside of this range appear to be + ** non-existant. + */ + if (BiasLength != -1) { + switch (dir) { + case SEEK_SET: + if (pos > BiasLength) { + pos = BiasLength; + } + pos += BiasStart; + break; + + case SEEK_CUR: + break; + + case SEEK_END: + dir = SEEK_SET; + pos += BiasStart + BiasLength; +// pos = (pos <= BiasStart+BiasLength) ? pos : BiasStart+BiasLength; +// pos = (pos >= BiasStart) ? pos : BiasStart; + break; + } + + /* + ** Perform the modified raw seek into the file. + */ + long newpos = Raw_Seek(pos, dir) - BiasStart; + + /* + ** Perform a final double check to make sure the file position fits with the bias range. + */ + if (newpos < 0) { + newpos = Raw_Seek(BiasStart, SEEK_SET) - BiasStart; + } + if (newpos > BiasLength) { + newpos = Raw_Seek(BiasStart+BiasLength, SEEK_SET) - BiasStart; + } + return(newpos); + } + + /* + ** If the file is not biased in any fashion, then the normal seek logic will + ** work just fine. + */ + return(Raw_Seek(pos, dir)); +} + + +/*********************************************************************************************** + * RawFileClass::Size -- Determines size of file (in bytes). * + * * + * Use this routine to determine the size of the file. The file must exist or this is an * + * error condition. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes in the file. * + * * + * WARNINGS: This routine handles error conditions and will not return unless the file * + * exists and can successfully be queried for file length. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Size(void) +{ + long size = 0; + + /* + ** A biased file already has its length determined. + */ + if (BiasLength != -1) { + return(BiasLength); + } + + /* + ** If the file is open, then proceed normally. + */ + if (Is_Open()) { + +#ifdef WIN32 + size = GetFileSize(Handle, NULL); + + /* + ** If there was in internal error, then call the error function. + */ + if (size == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Repetitively try to determine the file size until a fatal error condition or success + ** is achieved. + */ + for (;;) { + Hard_Error_Occured = 0; + size = filelength(Handle); + + /* + ** If a hard error occurred, then assume it is the case of removed media. Display an + ** error condition and allow retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + if (size == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + } else { + + /* + ** If the file wasn't open, then open the file and call this routine again. Count on + ** the fact that the open function must succeed. + */ + if (Open()) { + size = Size(); + + /* + ** Since we needed to open the file we must remember to close the file when the + ** size has been determined. + */ + Close(); + } + } + + BiasLength = size-BiasStart; + return(BiasLength); +} + + +/*********************************************************************************************** + * RawFileClass::Create -- Creates an empty file. * + * * + * This routine will create an empty file from the file object. The file object's filename * + * must already have been assigned before this routine will function. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file successfully created? This routine will always return true. * + * * + * WARNINGS: A fatal error condition could occur with this routine. Especially if the disk * + * is full or a read-only media was selected. * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Create(void) +{ + Close(); + if (Open(WRITE)) { + + /* + ** A biased file must be at least as long as the bias offset. Seeking to the + ** appropriate start offset has the effect of lengthening the file to the + ** correct length. + */ + if (BiasLength != -1) { + Seek(0, SEEK_SET); + } + + Close(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RawFileClass::Delete -- Deletes the file object from the disk. * + * * + * This routine will delete the file object from the disk. If the file object doesn't * + * exist, then this routine will return as if it had succeeded (since the effect is the * + * same). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the file deleted? If the file was already missing, the this value will * + * be false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +int RawFileClass::Delete(void) +{ + /* + ** If the file was open, then it must be closed first. + */ + Close(); + + /* + ** If there is no filename associated with this object, then this indicates a fatal error + ** condition. Report this and abort. + */ + if (!Filename) { + Error(ENOENT, false); + } + + /* + ** Repetitively try to delete the file if possible. Either return with success, or + ** abort the program with an error. + */ + for (;;) { + + /* + ** If the file is already missing, then return with this fact. No action is necessary. + ** This can occur as this section loops if the file exists on a floppy and the floppy + ** was removed, the file deleted on another machine, and then the floppy was + ** reinserted. Admittedly, this is a rare case, but is handled here. + */ + if (!Is_Available()) { + return(false); + } + +#ifdef WIN32 + if (!DeleteFile(Filename)) { + Error(GetLastError(), false, Filename); + return(false); + } +#else + Hard_Error_Occured = 0; + if (remove(Filename) == -1) { + + /* + ** If a hard error occurred, then assume that the media has been removed. Display + ** error message and retry as directed. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } + + /* + ** If at this point, DOS says the file doesn't exist, then just exit with this + ** fact. It should have been caught earlier, but in any case, this is a legal + ** condition. + */ + if (errno == ENOENT) break; + + /* + ** The only way it can reach this point is if DOS indicates that access is denied + ** on the file. This occurs when trying to delete a file on a read-only media such + ** as a CD-ROM. Report this as a fatal error and then abort. + */ + Error(errno, false, Filename); + } +#endif + break; + } + + /* + ** DOS reports that the file was successfully deleted. Return with this fact. + */ + return(true); +} + + +/*********************************************************************************************** + * RawFileClass::Get_Date_Time -- Gets the date and time the file was last modified. * + * * + * Use this routine to get the date and time of the file. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the file date and time as a long. * + * Use the YEAR(long), MONTH(),.... * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win32 method. * + *=============================================================================================*/ +unsigned long RawFileClass::Get_Date_Time(void) +{ +#ifdef WIN32 + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + WORD dosdate; + WORD dostime; + FileTimeToDosDateTime(&info.ftLastWriteTime, &dosdate, &dostime); + return((dosdate << 16) | dostime); + } + return(0); +#else + unsigned short time; + unsigned short date; + unsigned long datetime = 0; + + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + if ( _dos_getftime( Handle, &date, &time ) ) { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + if ( _dos_getftime( Handle, &date, &time ) ) { + RawFileClass::Close(); + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + + RawFileClass::Close(); + } else { + // + // return 0 indicating error with no date and time + // + return( datetime ); + } + } + + // + // combine the date and time as a long + // + datetime = (date << 16) + time; + + return( datetime ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Set_Date_Time -- Sets the date and time the file was last modified. * + * * + * Use this routine to set the date and time of the file. * + * * + * INPUT: the file date and time as a long * + * * + * OUTPUT: successful or not if the file date and time was changed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1995 DRD : Created. * + * 07/13/1996 JLB : Handles win 32 method * + *=============================================================================================*/ +bool RawFileClass::Set_Date_Time(unsigned long datetime) +{ +#ifdef WIN32 + if (RawFileClass::Is_Open()) { + BY_HANDLE_FILE_INFORMATION info; + + if (GetFileInformationByHandle(Handle, &info)) { + FILETIME filetime; + if (DosDateTimeToFileTime((WORD)(datetime >> 16), (WORD)(datetime & 0x0FFFF), &filetime)) { + return(SetFileTime(Handle, &info.ftCreationTime, &filetime, &filetime)); + } + } + } + return(false); +#else + unsigned short time; + unsigned short date; + + // + // If the file is open, then proceed normally. + // + if ( RawFileClass::Is_Open() ) { + // + // only set the date and time if open for READ only + // + if ( Rights == READ ) { + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + // + // return true indicating success + // + return( true ); + } + } + } else { + + // + // If the file wasn't open, then see if the file exists. + // + if ( RawFileClass::Is_Available() ) { + RawFileClass::Open(); + + time = (unsigned short)(datetime & 0xFFFF); + date = (unsigned short)((datetime >> 16) & 0xFFFF); + + if ( !_dos_setftime( Handle, date, time ) ) { + RawFileClass::Close(); + // + // return true indicating success + // + return( true ); + } + + RawFileClass::Close(); + } + } + + // + // return false indicating error + // + return( false ); +#endif +} + + +/*********************************************************************************************** + * RawFileClass::Bias -- Bias a file with a specific starting position and length. * + * * + * This will bias a file by giving it an artificial starting position and length. By * + * using this routine, it is possible to 'fool' the file into ignoring a header and * + * trailing extra data. An example of this would be a file inside of a mixfile. * + * * + * INPUT: start -- The starting offset that will now be considered the start of the * + * file. * + * * + * length -- The forced length of the file. For files that are opened for write, * + * this serves as the artificial constraint on the file's length. For * + * files opened for read, this limits the usable file size. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1996 JLB : Created. * + *=============================================================================================*/ +void RawFileClass::Bias(int start, int length) +{ + if (start == 0) { + BiasStart = 0; + BiasLength = -1; + return; + } + + BiasLength = RawFileClass::Size(); + BiasStart += start; + if (length != -1) { + BiasLength = BiasLength < length ? BiasLength : length; + } + BiasLength = BiasLength > 0 ? BiasLength : 0; + + /* + ** Move the current file offset to a legal position if necessary and the + ** file was open. + */ + if (Is_Open()) { + RawFileClass::Seek(0, SEEK_SET); + } +} + + +/*********************************************************************************************** + * RawFileClass::Raw_Seek -- Performs a seek on the unbiased file * + * * + * This will perform a seek on the file as if it were unbiased. This is in spite of any * + * bias setting the file may have. The ability to perform a raw seek in this fasion is * + * necessary to maintain the bias ability. * + * * + * INPUT: pos -- The position to seek the file relative to the "dir" parameter. * + * * + * dir -- The origin of the seek operation. * + * * + * OUTPUT: Returns with the new position of the seek operation. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +long RawFileClass::Raw_Seek(long pos, int dir) +{ + /* + ** If the file isn't opened, then this is a fatal error condition. + */ + if (!Is_Open()) { + Error(EBADF, false, Filename); + } + +#ifdef WIN32 + switch (dir) { + case SEEK_SET: + dir = FILE_BEGIN; + break; + + case SEEK_CUR: + dir = FILE_CURRENT; + break; + + case SEEK_END: + dir = FILE_END; + break; + } + + pos = SetFilePointer(Handle, pos, NULL, dir); + + /* + ** If there was an error in the seek, then bail with an error condition. + */ + if (pos == 0xFFFFFFFF) { + Error(GetLastError(), false, Filename); + } +#else + + /* + ** Keep trying to seek until a non-retry condition occurs. + */ + for (;;) { + + /* + ** Perform the low level seek on the file. + */ + Hard_Error_Occured = 0; + pos = lseek(Handle, pos, dir); + + /* + ** If a hard error occurred, then assume that it is the case of removed media. Display + ** error message and retry. + */ + if (Hard_Error_Occured) { + Error(Hard_Error_Occured, true, Filename); + continue; + } else { + + /* + ** A negative one indicates a fatal error with the seek operation. Display error + ** condition and then abort. + */ + if (pos == -1) { + Error(errno, false, Filename); + } + } + break; + } +#endif + + /* + ** Return with the new position of the file. This will range between zero and the number of + ** bytes the file contains. + */ + return(pos); +} diff --git a/code/redalert/rawfile.h b/code/redalert/rawfile.h new file mode 100644 index 0000000..e33b535 --- /dev/null +++ b/code/redalert/rawfile.h @@ -0,0 +1,332 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RAWFILE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_Hx +#define RAWFILE_Hx + +#include +#include +#include +#include + +#ifdef WIN32 +#include + +#define NULL_HANDLE INVALID_HANDLE_VALUE +#define HANDLE_TYPE HANDLE +#else +#define NULL_HANDLE -1 +#define HANDLE_TYPE int +#endif + +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +#ifndef WWERROR +#define WWERROR -1 +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void); + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual unsigned long Get_Date_Time(void); + virtual bool Set_Date_Time(unsigned long datetime); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + + void Bias(int start, int length=-1); + + HANDLE_TYPE Get_File_Handle(void) { return (Handle); }; + + /* + ** These bias values enable a sub-portion of a file to appear as if it + ** were the whole file. This comes in very handy for multi-part files such as + ** mixfiles. + */ + int BiasStart; + int BiasLength; + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + long Raw_Seek(long pos, int dir=SEEK_CUR); + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + HANDLE_TYPE Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + // + // file date and time are in the following formats: + // + // date bits 0-4 day (0-31) + // bits 5-8 month (1-12) + // bits 9-15 year (0-119 representing 1980-2099) + // + // time bits 0-4 second/2 (0-29) + // bits 5-10 minutes (0-59) + // bits 11-15 hours (0-23) + // + unsigned short Date; + unsigned short Time; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return(Filename); +} + + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : + Rights(READ), + BiasStart(0), + BiasLength(-1), + #ifdef WIN32 + Handle(INVALID_HANDLE_VALUE), + #else + Handle(-1), + #endif + Filename(0), + Date(0), + Time(0), + Allocated(false) +{ +} + + +/*********************************************************************************************** + * RawFileClass::~RawFileClass -- Default deconstructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::~RawFileClass(void) +{ + Close(); + if (Allocated && Filename) { + free((char *)Filename); + ((char *&)Filename) = 0; + Allocated = false; + } +} + + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ +#ifdef WIN32 + return(Handle != INVALID_HANDLE_VALUE); +#else + return (Handle >= 0); +#endif +} + +#endif diff --git a/code/redalert/rawolapi.cpp b/code/redalert/rawolapi.cpp new file mode 100644 index 0000000..a62a1f7 --- /dev/null +++ b/code/redalert/rawolapi.cpp @@ -0,0 +1,2099 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// rawolapi.cpp - Core WOLAPI interface functions stuff. +// Definitions for RAChatEventSink, RADownloadEventSink, RANetUtilEventSink. +// ajw 07/10/98 + +#include "RAWolapi.h" +#define IID_DEFINED +#include "wolapi\wolapi_i.c" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "Wol_gsup.h" +#include "wolapi\netutildefs.h" + +#include "WolDebug.h" + +bool operator<( const User& u1, const User& u2 ); + +const char* Game_Registry_Key(); + +// The definitions of QueryInterface, AddRef, and Release are needed because we are not including +// files that ordinarily (under MSVC) would define these for us, as part of CComObjectRoot, I believe. +// This Watcom has no equivalent we can use, so we do it manually... + +//*********************************************************************************************** +RAChatEventSink::RAChatEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + bRequestServerListWait( false ), + pOwner( pOwnerIn ), + pServer( NULL ), + bConnected( false ), + hresRequestConnectionError( 0 ), + pChannelList( NULL ), + pUserList( NULL ), + pUserTail( NULL ), + szMotd( NULL ), + bJoined( false ), + bGotKickedTrigger( false ), + bIgnoreChannelLists( false ), + bRequestChannelListForLobbiesWait( false ), + pGameUserList( NULL ), + bRequestGameStartWait( false ), + pUserIPList( NULL ), + pUserIPListTail( NULL ), + iGameID( 0 ) +{ +} + +//*********************************************************************************************** +RAChatEventSink::~RAChatEventSink() +{ +// debugprint( "RAChatEventSink destructor\n" ); + delete pServer; + delete [] szMotd; + DeleteChannelList(); + DeleteUserList(); + DeleteUserIPList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RAChatEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RAChatEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_IChatEvent)) + { + *ppv = (IChatEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RAChatEventSink::AddRef() +{ +// debugprint( "RAChatEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RAChatEventSink::Release() +{ +// debugprint( "RAChatEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerList( HRESULT hRes, Server* pServerHead ) +{ + //strcpy( szLadderServerHost, "games.westwood.com" ); + //iLadderServerPort = 3840; + //strcpy( szGameResServerHost, "games.westwood.com" ); + +// debugprint( ">>> OnServerList got: %i ", hRes ); + DebugChatDef( hRes ); + + if( pServer ) + { + delete pServer; + pServer = NULL; + } + + if( SUCCEEDED( hRes ) ) + { + while( pServerHead ) + { + // Copy the first IRC Server to use in the RequestConnection() call. + if( !pServer && ( strcmp( (char*)pServerHead->connlabel, "IRC" ) == 0 ) ) + { + pServer = new Server; + *pServer = *pServerHead; + } + else if( !*pOwner->szLadderServerHost && ( strcmp( (char*)pServerHead->connlabel, "LAD" ) == 0 ) ) + { +// debugprint( "Scanning '%s'\n", (char*)pServerHead->conndata ); + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szLadderServerHost, token ); + token = strtok( NULL, ";" ); + pOwner->iLadderServerPort = atoi( token ); +// debugprint( "Ladder is at: %s, port %i\n", pOwner->szLadderServerHost, pOwner->iLadderServerPort ); + } + else if( !*pOwner->szGameResServerHost1 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Red Alert game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost1, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort1 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + else if( !*pOwner->szGameResServerHost2 && ( strcmp( (char*)pServerHead->connlabel, "GAM" ) == 0 ) ) + { + // This is the Aftermath game results port. + char* token; + token = strtok( (char*)pServerHead->conndata, ";" ); + token = strtok( NULL, ";" ); + strcpy( pOwner->szGameResServerHost2, token ); + token = strtok( NULL, ";" ); + pOwner->iGameResServerPort2 = atoi( token ); +// debugprint( "GameRes is at: %s, port %i\n", pOwner->szGameResServerHost, pOwner->iGameResServerPort ); + } + pServerHead = pServerHead->next; + } + } + + bRequestServerListWait = false; + return(S_OK); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPageSend( HRESULT hRes ) +{ +// debugprint( ">>> OnPageSend got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_PAGE_NOTHERE && hRes != CHAT_S_PAGE_OFF && hRes != S_OK ) + hRes = E_FAIL; + + hresRequestPageResult = hRes; + + bRequestPageWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPaged( HRESULT, User* pUser, LPCSTR szMessage ) +{ +// debugprint( ">>> OnPaged got: %s ", szMessage ); + + char* szPrint = new char[ strlen( (char*)pUser->name ) + strlen( szMessage ) + strlen( TXT_WOL_ONPAGE ) ]; + sprintf( szPrint, TXT_WOL_ONPAGE, (char*)pUser->name, szMessage ); + if( !pOwner->bInGame ) + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PAGE ); + else + { + Session.Messages.Add_Message( NULL, 0, szPrint, PCOLOR_GOLD, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE ); + if( !pOwner->bFreezeExternalPager ) + strcpy( pOwner->szExternalPager, (char*)pUser->name ); + Map.Flag_To_Redraw(true); + } + + delete [] szPrint; + + Sound_Effect( WOLSOUND_ONPAGE ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnFind( HRESULT hRes, Channel* pChannel ) +{ +// debugprint( ">>> OnFind got: %i ", hRes ); + DebugChatDef( hRes ); + + if( hRes != CHAT_S_FIND_NOTHERE && hRes != CHAT_S_FIND_NOCHAN && hRes != CHAT_S_FIND_OFF && hRes != S_OK ) + hRes = E_FAIL; + + if( hRes == S_OK ) + OnFindChannel = *pChannel; + + hresRequestFindResult = hRes; + + bRequestFindWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnLogout( HRESULT hRes, User* pUser ) +{ +// debugprint( ">>> OnLogout got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone has been logged out by the chat server due to inactivity. + // Fake a call to OnChannelLeave(), as the processing is identical. +// debugprint( "OnLogout calling OnChannelLeave for %s, owner=%i\n", (char*)pUser->name, ( pUser->flags & CHAT_USER_CHANNELOWNER ) ); + OnChannelLeave( S_OK, NULL, pUser ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnBusy(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnIdle(HRESULT) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnConnection( HRESULT hRes, LPCSTR motd ) +{ +// debugprint( ">>> OnConnection got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + + // Prepare a new string for a modified version of motd. + szMotd = new char[ strlen( motd ) + 1 ]; + // Replace single line breaks with a space. + // Replace double line breaks with double carriage returns. + + bool bJustDidBreak = false; + const char* szIn = motd; + char* szOut = szMotd; + + while( *szIn ) + { + if( *szIn == '\r' && *( szIn + 1 ) == '\n' ) + { + if( !bJustDidBreak ) + { + *szOut++ = ' '; + bJustDidBreak = true; + } + else + { + szOut--; + *szOut++ = '\r'; + *szOut++ = '\r'; + bJustDidBreak = false; +// debugprint( "^" ); + } + szIn += 2; + } + else + { + *szOut++ = *szIn++; + bJustDidBreak = false; + } +// debugprint( "%c", *( szOut - 1 ) ); + } + *szOut = 0; // Null-terminate. +// debugprint( "\n" ); + +// pOwner->PrintMessage( szMotd ); + + bConnected = true; + } + else + { + hresRequestConnectionError = hRes; + + char szError[150]; + ChatDefAsText( szError, hRes ); + strcat( szError, " (Connect Error)" ); +// debugprint( szError ); + } + + bRequestConnectionWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelCreate( HRESULT hRes, Channel* ) +{ +// debugprint( ">>> OnChannelCreate got: %i ", hRes ); + DebugChatDef( hRes ); + +// if( bJoined ) +// { +// WWMessageBox().Process( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// Fatal( "RAChatEventSink::OnChannelCreate called when bJoined is true!" ); +// } + + if( SUCCEEDED( hRes ) ) + { + bJoined = true; + } + bRequestChannelCreateWait = false; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelModify(HRESULT, Channel *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelJoin( HRESULT hRes, Channel* /*pChannel*/, User* pUser ) +{ +// if( SUCCEEDED( hRes ) ) +// debugprint( ">>> OnChannelJoin got: channel '%s', user '%s', %i ", (char*)pChannel->name, (char*)pUser->name, hRes ); +// else +// debugprint( ">>> OnChannelJoin got: %i ", hRes ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelJoin when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring join.\n" ); +// return S_OK; +// } + + hresRequestJoinResult = hRes; + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = true; + bRequestChannelJoinWait = false; + if( pUserList ) + { + // Should never happen. +// debugprint( "pUserList should be NULL (as I am the joiner)!!! Deleting user list...\n" ); + DeleteUserList(); + } + } + else + { + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We can ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + // Add user to our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelJoin - ignoring %s join... \n", (char*)pUser->name ); + return S_OK; + } +// if( !pUserList ) +// { +// // There has to be at least one user there - you. +// debugprint( "pUserList is null in OnChannelJoin!!! users: %s\n", (char*)pUser->name ); +// Fatal( "pUserList is null in OnChannelJoin!!!\n" ); +// } + + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + + // Insert user into list alphabetically. + InsertUserSorted( pUserNew ); + + // Update the shown list. + pOwner->ListChannelUsers(); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + _ASSERTE( pOwner->pGSupDlg ); + pOwner->pGSupDlg->OnGuestJoin( pUser ); + + // Ask for this player's IP address. + pOwner->RequestIPs( (char*)pUser->name ); + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL || pOwner->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Request ladder results for new user. + pOwner->RequestLadders( (char*)pUser->name ); + } + } + } + else + { + bRequestChannelJoinWait = false; + } + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::InsertUserSorted( User* pUserNew ) +{ + if( !pUserList ) + { + pUserList = pUserNew; + pUserTail = pUserNew; + } + else + { + if( *pUserNew < *pUserList ) + { + // Insert user at beginning. + pUserNew->next = pUserList; + pUserList = pUserNew; + } + else + { + User* pUserCheck = pUserList; + User* pUserInsertAfter = NULL; + while( pUserCheck->next ) + { + if( *pUserNew < *pUserCheck->next ) + { + pUserInsertAfter = pUserCheck; + break; + } + pUserCheck = pUserCheck->next; + } + if( pUserInsertAfter ) + { + pUserNew->next = pUserInsertAfter->next; + pUserInsertAfter->next = pUserNew; + } + else + { + // Add user to end. + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + } + } +} + +//*********************************************************************************************** +bool operator<( const User& u1, const User& u2 ) +{ + if( u1.flags & CHAT_USER_CHANNELOWNER && !( u2.flags & CHAT_USER_CHANNELOWNER ) ) + return true; + if( !( u1.flags & CHAT_USER_CHANNELOWNER ) && u2.flags & CHAT_USER_CHANNELOWNER ) + return false; + if( u1.flags & CHAT_USER_VOICE && !( u2.flags & CHAT_USER_VOICE ) ) + return true; + if( !( u1.flags & CHAT_USER_VOICE ) && u2.flags & CHAT_USER_VOICE ) + return false; + return ( _stricmp( (char*)u1.name, (char*)u2.name ) < 0 ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelLeave( HRESULT hRes, Channel*, User* pUser ) +{ + // Note: This is also called directly from OnUserKick(), below, when someone is kicked from a channel. + // Also now from OnLogout(). + +// debugprint( ">>> OnChannelLeave got %s: ", (char*)pUser->name ); + DebugChatDef( hRes ); + +// // Special case - ignore OnChannelLeave when waiting for a UserList. +// if( bRequestUserListWait ) +// { +// debugprint( "bRequestUserListWait is true - ignoring leave.\n" ); +// return S_OK; +// } + + if( SUCCEEDED( hRes ) ) + { + if( pUser->flags & CHAT_USER_MYSELF ) + { + bJoined = false; + bRequestChannelLeaveWait = false; + } + else + { + // Remove user from our current channel users list. + if( !pUserList ) + { +// debugprint( "pUserList is null in OnChannelLeave - ignoring %s leave... \n", (char*)pUser->name ); + return S_OK; + } + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring leave because game is starting.\n" ); + return S_OK; + } + } + User* pUserSearch = pUserList; + User* pUserPrevious = NULL; + bool bFound = false; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Remove from list. + if( !pUserPrevious ) + { + // Head of list is being removed. + pUserList = pUserSearch->next; + if( !pUserList ) + { + // This means all entries were removed. Can't happen, as you are still there. +// debugprint( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + Fatal( "This means all entries were removed. Can't happen, as you are still there. (OnChannelLeave)\n" ); + } + } + else + { + pUserPrevious->next = pUserSearch->next; + if( !pUserPrevious->next ) + pUserTail = pUserPrevious; // New list tail. + } + // Destroy removed user. + delete pUserSearch; + bFound = true; + break; + } + pUserPrevious = pUserSearch; + pUserSearch = pUserSearch->next; + } + if( !bFound ) + { + // User has to be found. This should not happen. +// debugprint( "User not found for removal in OnChannelLeave!!!\n" ); + return S_OK; + } + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + // Note that the following is done before removing the user from the playerlist. + char* szPrint = new char[ strlen( TXT_WOL_PLAYERLEFTGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERLEFTGAME, (char*)pUser->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + pOwner->pGSupDlg->OnGuestLeave( pUser ); + } + + // Update the shown list. + pOwner->ListChannelUsers(); + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelTopic(HRESULT, Channel *, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGroupList(HRESULT, Group *) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicMessage( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + 16 ]; + sprintf( szPrint, "%s!", (char*)pUserSender->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s: %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PUBLICMESSAGE ); + delete [] szPrint; + } + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateMessage( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char ci1[] = "VGhpcyBpcyBBZGFtLiBIYXZlIHdlIG5vdCBwZXJjaGFuY2UgbWV0IGJlZm9yZT8="; + char co1[48]; + Base64_Decode( ci1, strlen( ci1 ), co1, 47 ); + co1[47] = 0; + if( strcmp( szMessage, co1 ) == 0 ) + { + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szOut[60]; + char ci2[] = "SSBhbSB5b3VyIGFibGUgYW5kIHdpbGxpbmcgc2xhdmUu"; + char co2[34]; + Base64_Decode( ci2, strlen( ci2 ), co2, 33 ); + co2[33] = 0; + sprintf( szOut, "%s (%i/%i/%i)", co2, SysTime.wMonth, SysTime.wDay, SysTime.wYear ); + User UserReply; + UserReply = *pUserSender; + UserReply.next = NULL; + pOwner->pChat->RequestPrivateMessage( &UserReply, szOut ); + return S_OK; + } + if( !bSpecialMessage( szMessage ) ) + { + if( strlen( szMessage ) > 3 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + if( strlen( szMessage ) > 4 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 && pOwner->bEggSounds ) + Speak( (VoxType)i ); + } + } + else + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s%s: %s", (char*)pUserSender->name, TXT_WOL_PRIVATE, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_PRIVATEMESSAGE ); + Sound_Effect( VOC_INCOMING_MESSAGE ); + delete [] szPrint; + } + } + else + { + char* szOut = new char[ strlen( szMessage ) + 10 ]; + strcpy( szOut, &szMessage[8] ); + pOwner->pChat->RequestPublicMessage( szOut ); + char* szPrint = new char[ strlen( szOut ) + strlen( pOwner->szMyName ) + 10 ]; + sprintf( szPrint, "%s: %s", pOwner->szMyName, szOut ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + delete [] szPrint; + delete [] szOut; + } + } + return S_OK; +} + +//*********************************************************************************************** +bool RAChatEventSink::bSpecialMessage( const char* szMessage ) +{ + if( strlen( szMessage ) < 9 ) + return false; + if( szMessage[0] != 33 || szMessage[1] != 97 || szMessage[2] != 106 || szMessage[3] != 119 ) + return false; + SYSTEMTIME SysTime; + ::GetSystemTime( &SysTime ); + char szCode[5]; + memcpy( (void*)szCode, (void*)&szMessage[4], 4 ); + szCode[4] = 0; + int iCode = atoi( szCode ); + return ( iCode == ( ( SysTime.wMonth * 99 ^ SysTime.wDay * 33 ) ^ SysTime.wYear ) ); +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnSystemMessage(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnNetStatus( HRESULT hRes ) +{ +// debugprint( ">>> OnNetStatus got: " ); + DebugChatDef( hRes ); + + if( !SUCCEEDED( hRes ) ) + { + // If we are waiting for a server list, this error might indicate that we're not going to + // get one, so bail out of waiting for it. + bRequestServerListWait = false; + // Same for logout. + bRequestLogoutWait = false; + } + + if( hRes == CHAT_S_CON_DISCONNECTED ) + { + if( bRequestLogoutWait || !bConnected ) + { + // Ok. We are waiting to logout or already have. + bRequestLogoutWait = false; + } + else + { + // Uh oh. We got disconnected unexpectedly. + if( pOwner->bInGame ) + // Set flag for wolapi destruction if connection is lost during game. + pOwner->bConnectionDown = true; + else + { + if( !pOwner->bSelfDestruct ) + { + // Set flag for wolapi destruction. + WWMessageBox().Process( TXT_WOL_WOLAPIGONE ); + pOwner->bSelfDestruct = true; + } + } + } + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelList( HRESULT, Channel* pChannelListIn ) +{ + if( bIgnoreChannelLists ) // Response to channel lists has been temporarily turned off. + { +// debugprint( ">>> IGNORED OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + return S_OK; + } + + // Special case for modal GetLobbyChannels(). Because we want to be sure this OnChannelList is one that was caused + // by a Request for gametype 0, and not one arriving from an earlier Request for games. + // This OnChannelList might not actually match the Request in GetLobbyChannels(), but as long as it's type 0 it'll do. + if( bRequestChannelListForLobbiesWait ) + { + if( pChannelListIn && pChannelListIn->type != 0 ) + { +// debugprint( ">>> IGNORED OnChannelList, bRequestChannelListForLobbiesWait if\n" ); + return S_OK; + } + // Note: if no channels in list, can't tell what kind of Request call gave us this list. + // (In our case assume it was the one asking for lobbies and allow to fail later naturally due to no lobbies available.) + } + + DeleteChannelList(); +// debugprint( ">>> OnChannelList, filter = %i, WO's LastUpdateChannelCallLevel = %i \n", ChannelFilter, pOwner->LastUpdateChannelCallLevel ); + + int iLobbyCur = iChannelLobbyNumber( (unsigned char*)pOwner->szChannelNameCurrent ); + + Channel* pChannelListTail = NULL; + + // Copy channel list to our own list. + while( pChannelListIn ) + { +// debugprint( "OnChannelList got %s\n", pChannelListIn->name ); + switch( ChannelFilter ) + { + case CHANNELFILTER_OFFICIAL: + if( pChannelListIn->official != 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_UNOFFICIAL: + if( pChannelListIn->official == 1 || iChannelLobbyNumber( pChannelListIn->name ) != -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + case CHANNELFILTER_LOBBIES: + { + int iLobby = iChannelLobbyNumber( pChannelListIn->name ); + if( iLobby == -1 ) + { +// debugprint( "(OnChannelList filtered this one.)\n", pChannelListIn->name ); + pChannelListIn = pChannelListIn->next; + continue; + } + break; + } + case CHANNELFILTER_LOCALLOBBYGAMES: + // We are listing games of our type, and may have to filter out non-local-lobby games. + if( !pOwner->bAllGamesShown ) + { + int iGameSourceLobby = pChannelListIn->reserved & 0x00FFFFFF; + if( iLobbyCur == -1 || iGameSourceLobby != iLobbyCur ) + { + pChannelListIn = pChannelListIn->next; + continue; + } + } + break; + } + Channel* pChannelNew = new Channel; + *pChannelNew = *pChannelListIn; + pChannelNew->next = NULL; // (We don't want the value that was just copied!) + if( !pChannelListTail ) + { + // First channel in list. + pChannelList = pChannelNew; // This is the head of our channel list. + pChannelListTail = pChannelNew; + } + else + { + pChannelListTail->next = pChannelNew; + pChannelListTail = pChannelNew; + } + pChannelListIn = pChannelListIn->next; + } + +// bRequestChannelListWait = false; + + if( bRequestChannelListForLobbiesWait ) + bRequestChannelListForLobbiesWait = false; + else + pOwner->OnChannelList(); + + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteChannelList() +{ + // Delete all channels allocated on the heap. + // pChannelList points to the head element of a linked list of channels, copied during OnChannelList(). +// debugprint( "DeleteChannelList\n" ); + while( pChannelList ) + { + Channel* pChannelHead = pChannelList; + pChannelList = pChannelHead->next; + delete pChannelHead; + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserList(HRESULT, Channel*, User* pUserListIn ) +{ + // Maintenance of users list is like that for channels list. +// debugprint( ">>> OnUserList\n" ); + DeleteUserList(); + + // Copy channel list to our own list. + while( pUserListIn ) + { +// debugprint( "OnUserList got %s\n", pUserListIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserListIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserList = pUserNew; // This is the head of our User list. + pUserTail = pUserNew; + } + else + { + pUserTail->next = pUserNew; + pUserTail = pUserNew; + } + pUserListIn = pUserListIn->next; + } + +// bRequestUserListWait = false; + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserList() +{ + // Delete all Users allocated on the heap. + // pUserList points to the head element of a linked list of Users, copied during OnUserList(). +// debugprint( "DeleteUserList\n" ); + while( pUserList ) + { + User* pUserHead = pUserList; + pUserList = pUserHead->next; + delete pUserHead; + } + pUserTail = NULL; +} + + +//*********************************************************************************************** +// We got a list of updates to apply +// +STDMETHODIMP RAChatEventSink::OnUpdateList( HRESULT hRes, Update* pUpdateList ) +{ +// debugprint( ">>> OnUpdateList got: " ); + DebugChatDef( hRes ); + + if( !pUpdateList ) // Shouldn't happen. + return S_OK; + + // Count the updates. + int iUpdates = 0; + Update* pUpdate = pUpdateList; + + while( pUpdate != NULL ) + { + pUpdate = pUpdate->next; + ++iUpdates; + } +// debugprint( "%i updates\n", iUpdates ); + + if( WWMessageBox().Process( TXT_WOL_PATCHQUESTION, TXT_YES, TXT_NO ) == 0 ) + { + // Get the updates. (I ignore the concept of "optional" downloads here.) + if( DownloadUpdates( pUpdateList, iUpdates ) ) + pOwner->hresPatchResults = PATCHDOWNLOADED; + else + pOwner->hresPatchResults = PATCHAVOIDED; + } + else + // User says don't do the download. + // Set flag to tell WolapiObject what has happened. + pOwner->hresPatchResults = PATCHAVOIDED; + + return S_OK; +} + +extern bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ); +//*********************************************************************************************** +bool RAChatEventSink::DownloadUpdates( Update* pUpdateList, int iUpdates ) +{ + // First we create a Download and Download Sink interface object, like Chat and ChatSink. + bool bReturn = true; + // This is all like WolapiObject::bSetupCOMStuff(). +//debugprint( "Do all the COM stuff.\n" ); + IDownload* pDownload; + CoCreateInstance( CLSID_Download, NULL, CLSCTX_INPROC_SERVER, IID_IDownload, (void**)&pDownload ); + _ASSERTE( pDownload ); + RADownloadEventSink* pDownloadSink = new RADownloadEventSink(); + pDownloadSink->AddRef(); + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + HRESULT hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + DWORD dwDownloadAdvise; + hRes = pConnectionPoint->Advise( (IDownloadEvent*)pDownloadSink, &dwDownloadAdvise ); + _ASSERTE(SUCCEEDED(hRes)); + // Presumably the above calls will succeed, because they did so when we did bSetupComStuff(). + + pContainer->Release(); + pConnectionPoint->Release(); + + Update* pUpdate = pUpdateList; + int iUpdateCurrent = 0; + // Save current directory. + char szCurDirSave[_MAX_PATH]; + ::GetCurrentDirectory( _MAX_PATH, szCurDirSave ); + while( pUpdate ) + { + ++iUpdateCurrent; + char szTitle[ 120 ]; + sprintf( szTitle, TXT_WOL_DOWNLOADING, iUpdateCurrent, iUpdates ); + char fullpath[ _MAX_PATH ]; + sprintf( fullpath, "%s\\%s", pUpdate->patchpath, pUpdate->patchfile ); + // Downloading in WOLAPI is in a state of disarray somewhat. + // Make sure the destination directory exists, and make it the current directory during the download. +//debugprint( "Switching to %s dir.\n", (char*)pUpdate->localpath ); + if( !::SetCurrentDirectory( (char*)pUpdate->localpath ) ) + { + // Create the destination directory. +// debugprint( "Creating dir.\n" ); + ::CreateDirectory( (char*)pUpdate->localpath, NULL ); + ::SetCurrentDirectory( (char*)pUpdate->localpath ); + } + // Note: Unknown what the reg key value is actually used for... +//debugprint( "Asking to download %s to %s. Server '%s', login '%s', password '%s'\n", fullpath, (char*)pUpdate->patchfile, +// (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password ); + pDownload->DownloadFile( (char*)pUpdate->server, (char*)pUpdate->login, (char*)pUpdate->password, fullpath, + (char*)pUpdate->patchfile, Game_Registry_Key() ); +// debugprint( "Call WOL_Download_Dialog()\n" ); + if( !WOL_Download_Dialog( pDownload, pDownloadSink, szTitle ) ) + { + bReturn = false; + break; + } + pUpdate = pUpdate->next; + } + ::SetCurrentDirectory( szCurDirSave ); + + // Undo all the COM stuff. +//debugprint( "Undo all the COM stuff.\n" ); + pConnectionPoint = NULL; + pContainer = NULL; + hRes = pDownload->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); + hRes = pContainer->FindConnectionPoint( IID_IDownloadEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); + pConnectionPoint->Unadvise( dwDownloadAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pDownload->Release(); + pDownloadSink->Release(); // This results in pDownloadSink deleting itself for us. + + return bReturn; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError( HRESULT hRes ) +{ +// debugprint( ">>> OnServerError got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnMessageOfTheDay(HRESULT, LPCSTR) +{ + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::ActionEggSound( const char* szMessage ) +{ + // Easter egg related. + if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) ) + { + int i = rand() % 30; + if( i == 0 ) + Sound_Effect( VOC_DOG_HURT ); + else if( i == 1 ) + Sound_Effect( VOC_ANTDIE ); + else + Sound_Effect( (VocType)( VOC_SCREAM1 + rand() % 9 ) ); + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) + || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_WHINE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 6 ) + { + case 0: Sound_Effect( VOC_CANNON1 ); break; + case 1: Sound_Effect( VOC_CANNON2 ); break; + case 2: Sound_Effect( VOC_GUN_RIFLE ); break; + case 3: Sound_Effect( VOC_SILENCER ); break; + case 4: Sound_Effect( VOC_CANNON6 ); break; + case 5: Sound_Effect( VOC_CANNON8 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_KABOOM1 ); break; + case 1: Sound_Effect( VOC_KABOOM12 ); break; + case 2: Sound_Effect( VOC_KABOOM15 ); break; + case 3: Sound_Effect( VOC_KABOOM30 ); break; + case 4: Sound_Effect( VOC_KABOOM25 ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 8 ) + { + case 0: Sound_Effect( VOC_E_AH ); break; + case 1: Sound_Effect( VOC_E_YES ); break; + case 2: Sound_Effect( VOC_THIEF_YEA ); break; + case 3: Sound_Effect( VOC_SPY_YESSIR ); break; + case 4: Sound_Effect( VOC_SPY_INDEED ); break; + case 5: Sound_Effect( VOC_ENG_YES ); break; + case 6: Sound_Effect( VOC_MED_YESSIR ); break; + case 7: Sound_Effect( VOC_MED_AFFIRM ); break; + } + } + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_E_OK ); + else if( strstr( szMessage, "<>" )|| strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + { + switch( rand() % 5 ) + { + case 0: Sound_Effect( VOC_SPY_ONWAY ); break; + case 1: Sound_Effect( VOC_ENG_MOVEOUT ); break; + case 2: Sound_Effect( VOC_SPY_KING ); break; + case 3: Sound_Effect( VOC_MED_MOVEOUT ); break; + case 4: Sound_Effect( VOC_THIEF_MOVEOUT ); break; + } + } + else if( strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_SPLASH ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_POWER_UP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TESLA_ZAP ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_TORPEDO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || strstr( szMessage, "<>" )) + Sound_Effect( VOC_SUBSHOW ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_BARK ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_DOG_GROWL2 ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CHRONO ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CRUMBLE ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) || + strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_CASHTURN ); + else if( strstr( szMessage, "<>" ) || strstr( szMessage, "<>" ) ) + Sound_Effect( VOC_HEAL ); + else if( strstr( szMessage, "<>" ) ) + { + switch( rand() % 3 ) + { + case 0: Sound_Effect( VOC_MISSILE_1 ); break; + case 1: Sound_Effect( VOC_MISSILE_2 ); break; + case 2: Sound_Effect( VOC_MISSILE_3 ); break; + } + } +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateAction( HRESULT, User* pUserSender, LPCSTR szMessage ) +{ + // Ignore private messages sent to myself by myself. + if( pUserSender->flags & CHAT_USER_MYSELF ) + return S_OK; + + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 116 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATE, (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicAction( HRESULT, Channel*, User* pUserSender, LPCSTR szMessage ) +{ + if( *szMessage ) + { + char* szPrint = new char[ strlen( (char*)pUserSender->name ) + strlen( szMessage ) + 110 ]; + sprintf( szPrint, "%s %s", (char*)pUserSender->name, szMessage ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + delete [] szPrint; + // Easter egg related. + if( pOwner->bEggSounds ) + ActionEggSound( szMessage ); + } + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPrivateGameOptions( HRESULT, User* pUser, LPCSTR szRequest ) +{ +// debugprint( ">>> OnPrivateGameOptions\n" ); +// DebugChatDef( hRes ); + + char szRequestCopy[ 600 ]; + strcpy( szRequestCopy, szRequest ); + + if( pOwner->pGSupDlg ) + { + if( pOwner->pGSupDlg->bHost ) + pOwner->pGSupDlg->ProcessGuestRequest( pUser, szRequestCopy ); + else + pOwner->pGSupDlg->ProcessInform( szRequestCopy ); // Must be private message to guest from game host. + } +// else +// debugprint( "OnPrivateGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnPublicGameOptions( HRESULT, Channel*, User*, LPCSTR szInform ) +{ +// debugprint( ">>> OnPublicGameOptions: %s\n", szInform ); + + char szInformCopy[ 600 ]; + strcpy( szInformCopy, szInform ); + + if( pOwner->pGSupDlg ) + { + pOwner->pGSupDlg->ProcessInform( szInformCopy ); + } +// else +// debugprint( "OnPublicGameOptions bizarreness.\n" ); + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnGameStart( HRESULT hRes, Channel*, User* pUserIn, int iGameID ) +{ + // Note: All players receive this, not just the host that requested it. + +// debugprint( ">>> OnGameStart got: " ); + DebugChatDef( hRes ); + +// if( bRequestGameStartWait ) // Implies user is the host that did RequestGameStart(). +// { + + // Create the list of users that are actually involved in a game. + // Most likely will always match pUserList, but there is a chance of someone leaving or joining + // at the wrong moment, so from this point on, the pGameUserList is used. + + // Note: pUserIPList was added later, for pre-start pinging. It duplicates pGameUserList ip information, + // strictly speaking. + + // Delete any existing list. + while( pGameUserList ) + { + User* pGameUserHead = pGameUserList; + pGameUserList = pGameUserList->next; + delete pGameUserHead; + } + // Copy incoming user list. + User* pGameUserListTail = NULL; + while( pUserIn ) + { +// debugprint( "OnGameStart got %s\n", (char*)pUserIn->name ); + User* pUserNew = new User; + *pUserNew = *pUserIn; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pGameUserListTail ) + { + // First User in list. + pGameUserList = pUserNew; // This is the head of our User list. + pGameUserListTail = pUserNew; + } + else + { + pGameUserListTail->next = pUserNew; + pGameUserListTail = pUserNew; + } + pUserIn = pUserIn->next; + } + + bRequestGameStartWait = false; +// } + +// debugprint( "iGameID is %i\n", iGameID ); + this->iGameID = iGameID; + + return S_OK; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetPlayerGameIP( const char* szPlayerName ) const +{ + // Returns ipaddr value of player if found in pGameUserList, else 0. + User* pUser = pGameUserList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szPlayerName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserKick( HRESULT hRes, Channel*, User* pUserKicked, User* pUserKicker ) +{ +// debugprint( ">>> OnUserKick got: " ); + DebugChatDef( hRes ); + + if( hRes == S_OK ) + { + // Someone was kicked. + // Fake a call to OnChannelLeave(), as the processing is identical. + OnChannelLeave( S_OK, NULL, pUserKicked ); + if( pUserKicked->flags & CHAT_USER_MYSELF ) + { + // Trigger a channel exit later on, when we have left this callback. + bGotKickedTrigger = true; + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( TXT_WOL_USERKICKEDYOU ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDYOU, (char*)pUserKicker->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + // Ensure that the bGotKickedTrigger is acted upon immediately... + pOwner->dwTimeNextWolapiPump = ::timeGetTime(); + } + else + { + char* szPrint = new char[ strlen( (char*)pUserKicker->name ) + strlen( (char*)pUserKicked->name ) + + strlen( TXT_WOL_USERKICKEDUSER ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERKICKEDUSER, (char*)pUserKicker->name, (char*)pUserKicked->name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + switch( rand() % 4 ) + { + case 0: + Sound_Effect( VOC_TANYA_CHEW ); + break; + case 1: + Sound_Effect( VOC_TANYA_LAUGH ); + break; + case 2: + Sound_Effect( VOC_TANYA_CHING ); + break; + case 3: + Sound_Effect( VOC_TANYA_KISS ); + break; + } + } + else + { + // You tried to kick someone, but the user wasn't found. + // Ignore. +// debugprint( "OnUserKick non S_OK value\n" ); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserIP( HRESULT hRes, User* pUser ) +{ + // A list of users is kept, separate from other user lists, to preserve the ipaddr's we've found through this + // callback. OnUserList (for some dumb reason) doesn't hold valid ipaddr's, so we have to go through + // all this rigamarole... + // (List is cleared when entering game channel. Users are added initially and on joins, not removed on leaves.) +// debugprint( ">>> OnUserIP got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { + // Look for user in our current users list. + User* pUserSearch = pUserIPList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, (char*)pUser->name ) == 0 ) + { + // Found matching user. Replace it's ipaddr value, in case it changed.(?) + pUserSearch->ipaddr = pUser->ipaddr; + return S_OK; + } + pUserSearch = pUserSearch->next; + } + // User not found in current list. Add. + User* pUserNew = new User; + *pUserNew = *pUser; + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserIPListTail ) + { + // First user in list. + pUserIPList = pUserNew; // This is the head of our list. + pUserIPListTail = pUserNew; + } + else + { + pUserIPListTail->next = pUserNew; + pUserIPListTail = pUserNew; + } + } + return S_OK; +} + +//*********************************************************************************************** +void RAChatEventSink::DeleteUserIPList() +{ + // Same as DeleteUserList but for pUserIPList. +// debugprint( "DeleteUserIPList\n" ); + while( pUserIPList ) + { + User* pUserHead = pUserIPList; + pUserIPList = pUserHead->next; + delete pUserHead; + } + pUserIPListTail = NULL; +} + +//*********************************************************************************************** +unsigned long RAChatEventSink::GetUserIP( const char* szName ) const +{ + // Looks in pUserIPList for the ipaddr of user with name szName. + // This is used only while in game channels. + // This is for step 2 in acquiring fellow player ping times. To get the IP addresses into pUserIPList + // we had to go through request/callbacks. Now pings are requested on these addresses, and the results + // tallied in NetUtilSink for our retrieval later. + // Returns 0 if not found. + + // Find szName in list. + User* pUser = pUserIPList; + while( pUser ) + { + if( _stricmp( (char*)pUser->name, szName ) == 0 ) + return pUser->ipaddr; + pUser = pUser->next; + } + return 0; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerError(HRESULT , LPCSTR ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnServerBannedYou(HRESULT , time_t ) +{ + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnUserFlags( HRESULT hRes, LPCSTR name, unsigned int flags, unsigned int ) +{ +// debugprint( ">>> OnUserFlags got: " ); + DebugChatDef( hRes ); + + if( pOwner->CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pOwner->pGSupDlg && + ( pOwner->pGSupDlg->bHostSayGo || pOwner->pGSupDlg->bHostWaitingForGoTrigger || + pOwner->pGSupDlg->bExitForGameTrigger || iGameID ) ) + { + // A game has this moment entered the "must start" phase. We must ignore the fact that others are leaving the channel. +// debugprint( "Ignoring OnUserFlags because game is starting.\n" ); // (Shouldn't ever happen.) + return S_OK; + } + } + + // Find user in our current users list. + User* pUserPrior = NULL; + User* pUserSearch = pUserList; + while( pUserSearch ) + { + if( _stricmp( (char*)pUserSearch->name, name ) == 0 ) + { + // Set user's flags to new value. + pUserSearch->flags = flags; + + // Remove user from userlist and reinsert appropriately sorted. + if( !pUserPrior ) + { + // User was head of list. + pUserList = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was also tail of list. + pUserTail = NULL; + else + pUserSearch->next = NULL; + } + else + { + // User was not head of list. + pUserPrior->next = pUserSearch->next; + if( pUserSearch == pUserTail ) + // User was tail of list. + pUserTail = pUserPrior; + else + pUserSearch->next = NULL; + } + InsertUserSorted( pUserSearch ); + + // Update shown list. + pOwner->ListChannelUsers(); + break; + } + pUserPrior = pUserSearch; + pUserSearch = pUserSearch->next; + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RAChatEventSink::OnChannelBan( HRESULT , LPCSTR name, int banned ) +{ + if( banned && strcmp( name, "*" ) != 0 ) + { + char* szPrint = new char[ strlen( name ) + strlen( TXT_WOL_USERWASBANNED ) + 5 ]; + sprintf( szPrint, TXT_WOL_USERWASBANNED, name ); + pOwner->PrintMessage( szPrint, WOLCOLORREMAP_KICKORBAN ); + delete [] szPrint; + } + + return S_OK; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RADownloadEventSink::RADownloadEventSink() : + bFlagEnd( false ), + bFlagError( false ), + bFlagProgressUpdate( false ), + bFlagStatusUpdate( false ), + bFlagQueryResume( false ) +{ + m_cRef=0; // Ref counter +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RADownloadEventSink::QueryInterface(const IID& iid, void** ppv) +{ + if ((iid == IID_IUnknown) ||(iid == IID_IDownloadEvent)) + { + *ppv = (IDownloadEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RADownloadEventSink::AddRef() +{ + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RADownloadEventSink::Release() +{ + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnEnd(void) +{ +// debugprint( ">>> OnEnd\n" ); + bFlagEnd = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnError( int /*iCode*/ ) +{ +// debugprint( ">>> OnError got: %i\n", iCode ); +//#define DOWNLOADEVENT_NOSUCHSERVER 1 +//#define DOWNLOADEVENT_COULDNOTCONNECT 2 +//#define DOWNLOADEVENT_LOGINFAILED 3 +//#define DOWNLOADEVENT_NOSUCHFILE 4 +//#define DOWNLOADEVENT_LOCALFILEOPENFAILED 5 +//#define DOWNLOADEVENT_TCPERROR 6 +//#define DOWNLOADEVENT_DISCONNECTERROR 7 + + bFlagError = true; + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnProgressUpdate( int bytesread, int totalsize, int timetaken, int timeleft ) +{ +// debugprint( ">>> OnProgressUpdate\n" ); + bFlagProgressUpdate = true; + + iBytesRead = bytesread; + iTotalSize = totalsize; + iTimeTaken = timetaken; + iTimeLeft = timeleft; + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RADownloadEventSink::OnStatusUpdate( int status ) +{ +// debugprint( ">>> OnStatusUpdate, status = %i\n", status ); + bFlagStatusUpdate = true; + + iStatus = status; + + return S_OK; +} + +//*********************************************************************************************** +// Just tell the FTP module to go ahead and resume +// +STDMETHODIMP RADownloadEventSink::OnQueryResume() +{ +// debugprint( ">>> OnQueryResume\n" ); + bFlagQueryResume = true; + + bResumed = true; + + return DOWNLOADEVENT_RESUME; +} + + +//*********************************************************************************************** +//*********************************************************************************************** +RANetUtilEventSink::RANetUtilEventSink( WolapiObject* pOwnerIn ) : m_cRef( 0 ), // init the reference count + pOwner( pOwnerIn ), + pLadderList( NULL ), + pLadderTail( NULL ), + pLadderListAM( NULL ), + pLadderTailAM( NULL ) +{ +// debugprint( "RANetUtilEventSink constructor\n" ); +} + +//*********************************************************************************************** +RANetUtilEventSink::~RANetUtilEventSink() +{ +// debugprint( "RANetUtilEventSink destructor\n" ); + DeleteLadderList(); +} + +// Interface IUnknown Methods +//*********************************************************************************************** +// QueryInterface +// +HRESULT __stdcall +RANetUtilEventSink::QueryInterface(const IID& iid, void** ppv) +{ +// debugprint( "RANetUtilEventSink::QueryInterface\n" ); + if ((iid == IID_IUnknown) ||(iid == IID_INetUtilEvent)) + { + *ppv = (INetUtilEvent*)this; // Removed static_cast<> ajw + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + ((IUnknown*)(*ppv))->AddRef(); // Removed reinterpret_cast<> ajw + return S_OK ; +} + +//*********************************************************************************************** +// AddRef +// +ULONG __stdcall +RANetUtilEventSink::AddRef() +{ +// debugprint( "RANetUtilEventSink::AddRef\n" ); + return InterlockedIncrement(&m_cRef) ; +} + +//*********************************************************************************************** +// Release +// +ULONG __stdcall +RANetUtilEventSink::Release() +{ +// debugprint( "RANetUtilEventSink::Release\n" ); + if (InterlockedDecrement(&m_cRef) == 0) + { + delete this ; + return 0 ; + } + return m_cRef; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnGameresSent( HRESULT hRes ) +{ +// debugprint( ">>> OnGameresSent got: " ); + DebugChatDef( hRes ); + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnLadderList( HRESULT hRes, Ladder* pLadderListIn, int /*totalCount*/, long /*timeStamp*/, int /*keyRung*/ ) +{ + // Maintenance of ladders list is like that for channels list above. + // DeleteLadderList(); -> This is done once, before a set of RequestLadderList() calls are made. +// debugprint( ">>> OnLadderList got: " ); + DebugChatDef( hRes ); + + if( SUCCEEDED( hRes ) ) + { +// debugprint( "(SUCCEEDED)\n" ); + // Copy ladder list to our own list. + while( pLadderListIn ) + { +// debugprint( "OnLadderList got %s, rung %u\n", pLadderListIn->login_name, pLadderListIn->rung ); + if( *pLadderListIn->login_name != 0 && pLadderListIn->rung != -1 ) + { + Ladder* pLadderNew = new Ladder; + *pLadderNew = *pLadderListIn; + pLadderNew->next = NULL; // (We don't want the value that was just copied!) + if( pLadderNew->sku == LADDER_CODE_RA ) + { + if( !pLadderTail ) + { + // First Ladder in list. + pLadderList = pLadderNew; // This is the head of our Ladder list. + pLadderTail = pLadderNew; + } + else + { + pLadderTail->next = pLadderNew; + pLadderTail = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string. + sprintf( pOwner->szMyRecord, TXT_WOL_PERSONALWINLOSSRECORD, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + else // sku must be LADDER_CODE_AM + { + if( !pLadderTailAM ) + { + // First Ladder in list. + pLadderListAM = pLadderNew; // This is the head of our Ladder list. + pLadderTailAM = pLadderNew; + } + else + { + pLadderTailAM->next = pLadderNew; + pLadderTailAM = pLadderNew; + } + if( _stricmp( (char*)pLadderNew->login_name, pOwner->szMyName ) == 0 ) + { + // Set up local player's win/loss string for Aftermath. + sprintf( pOwner->szMyRecordAM, TXT_WOL_PERSONALWINLOSSRECORDAM, pOwner->szMyName, + pLadderNew->rung, pLadderNew->wins, pLadderNew->losses, pLadderNew->points ); + pOwner->bMyRecordUpdated = true; + } + } + } + pLadderListIn = pLadderListIn->next; + } + // Update shown list. + pOwner->ListChannelUsers(); + } + + return S_OK; +} + +//*********************************************************************************************** +STDMETHODIMP RANetUtilEventSink::OnPing( HRESULT hRes, int time, unsigned long ip, int /*handle*/ ) +{ + if( pOwner->bDoingDisconnectPinging ) + { +// debugprint( ">>> OnPing got : ip %i, time %i, ", ip, time ); + DebugChatDef( hRes ); + + if( ip == pOwner->TournamentOpponentIP ) + { + // This is the result of the opponent ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Opponent[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Opponent\n", pOwner->iDisconnectPingCurrent ); + } + else + { + // This is the result of the game server ping. + if( time != -1 ) + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_GOOD; + else + pOwner->DisconnectPingResult_Server[ pOwner->iDisconnectPingCurrent ] = PING_BAD; +// debugprint( "Set ping #%i for Server\n", pOwner->iDisconnectPingCurrent ); + } + } + return S_OK; +} + +//*********************************************************************************************** +void RANetUtilEventSink::DeleteLadderList() +{ +// debugprint( "DeleteLadderList()\n" ); + // Delete all Ladders allocated on the heap. + while( pLadderList ) + { + Ladder* pLadderHead = pLadderList; + pLadderList = pLadderHead->next; + delete pLadderHead; + } + pLadderTail = NULL; +} + +//*********************************************************************************************** +unsigned int RANetUtilEventSink::GetUserRank( const char* szName, bool bRankRA ) +{ + // Searches for szName in ladder list, returns player rank if found, else 0. + // Slow linear search. + // If bRankRA, returns RA rank, else returns AM rank. +// debugprint( "GetUserRank: Asked for %s, ", szName ); + Ladder* pLad; + if( bRankRA ) + pLad = pLadderList; + else + pLad = pLadderListAM; + + while( pLad ) + { +// debugprint( " comparing %s\n", (char*)pLad->login_name ); + if( _stricmp( (char*)pLad->login_name, szName ) == 0 ) + { +// debugprint( "found rung value %u\n", pLad->rung ); + return pLad->rung; + } + pLad = pLad->next; + } +// debugprint( "couldn't find in my ladder list.\n", szName ); + + return 0; +} + +//*********************************************************************************************** +//*********************************************************************************************** +void ChatDefAsText( char* szDesc, HRESULT hRes ) +{ + // Sets szDesc to the text meaning of hRes. + // Make sure szDesc is as long as the longest of the below. + switch( hRes ) + { + case CHAT_E_NICKINUSE : + sprintf( szDesc, "Your nick is still logged into chat" ); + break; + case CHAT_E_BADPASS : + sprintf( szDesc, "Your password is incorrect during login" ); + break; + case CHAT_E_NONESUCH : + sprintf( szDesc, "Reference made to non-existant user or channel" ); + break; + case CHAT_E_CON_NETDOWN : + sprintf( szDesc, "The network layer is down or cannot be initialized for some reason" ); + break; + case CHAT_E_CON_LOOKUP_FAILED : + sprintf( szDesc, "Name lookup (e.g DNS) failed for some reason" ); + break; + case CHAT_E_CON_ERROR : + sprintf( szDesc, "Some fatal error occured with the net connection" ); + break; + case CHAT_E_TIMEOUT : + sprintf( szDesc, "General request timeout for a request" ); + break; + case CHAT_E_MUSTPATCH : + sprintf( szDesc, "Must patch before continuing" ); + break; + case CHAT_E_STATUSERROR : + sprintf( szDesc, "Miscellaneous internal status error" ); + break; + case CHAT_E_UNKNOWNRESPONSE : + sprintf( szDesc, "Server has returned something we don't recognise" ); + break; + case CHAT_E_CHANNELFULL : + sprintf( szDesc, "Tried to join a channel that has enough players already" ); + break; + case CHAT_E_CHANNELEXISTS : + sprintf( szDesc, "Tried to create a channel that already exists" ); + break; + case CHAT_E_CHANNELDOESNOTEXIST : + sprintf( szDesc, "Tried to join a channel that does not exist" ); + break; + case CHAT_E_BADCHANNELPASSWORD : + sprintf( szDesc, "Tried to join a channel with the wrong password" ); + break; + case CHAT_E_BANNED : + sprintf( szDesc, "You've been banned from the server / channel" ); + break; + case CHAT_E_NOT_OPER : + sprintf( szDesc, "You tried to do something that required operator status" ); + break; + + case CHAT_S_CON_CONNECTING : + sprintf( szDesc, "A network connection is underway" ); + break; + case CHAT_S_CON_CONNECTED : + sprintf( szDesc, "A network connection is complete" ); + break; + case CHAT_S_CON_DISCONNECTING : + sprintf( szDesc, "A network connection is going down" ); + break; + case CHAT_S_CON_DISCONNECTED : + sprintf( szDesc, "A network connection is closed" ); + break; + case CHAT_S_FIND_NOTHERE : + sprintf( szDesc, "Find - Nick not in system" ); + break; + case CHAT_S_FIND_NOCHAN : + sprintf( szDesc, "Find - Not in any channels" ); + break; + case CHAT_S_FIND_OFF : + sprintf( szDesc, "Find - user has find turned off" ); + break; + case CHAT_S_PAGE_NOTHERE : + sprintf( szDesc, "Page - Nick not in system" ); + break; + case CHAT_S_PAGE_OFF : + sprintf( szDesc, "Page - user has page turned off" ); + break; + case CHAT_E_NOTCONNECTED : + sprintf( szDesc, "You are not connected to the chat server" ); + break; + case CHAT_E_NOCHANNEL : + sprintf( szDesc, "You are not in a channel" ); + break; + case CHAT_E_NOTIMPLEMENTED : + sprintf( szDesc, "Feature is not implemented" ); + break; + case CHAT_E_PENDINGREQUEST : + sprintf( szDesc, "The request was made while while a conflicting request was still pending" ); + break; + case CHAT_E_PARAMERROR : + sprintf( szDesc, "Invalid parameter passed - usually a NULL pointer" ); + break; + case CHAT_E_LEAVECHANNEL : + sprintf( szDesc, "Tried to create or join a channel before leaving the previous one" ); + break; + case CHAT_E_JOINCHANNEL : + sprintf( szDesc, "Tried to send something to a channel when not a member of any channel" ); + break; + case CHAT_E_UNKNOWNCHANNEL : + sprintf( szDesc, "Tried to join a non-existant channel" ); + break; + case S_OK: + sprintf( szDesc, "S_OK" ); + break; + case E_FAIL: + sprintf( szDesc, "E_FAIL" ); + break; + default: + sprintf( szDesc, "ERROR - Value not recognized!" ); + break; + } + // Append NetUtil errors. + switch( hRes ) + { + case NETUTIL_E_ERROR: + strcat( szDesc, " NetUtil: NETUTIL_E_ERROR" ); + break; + case NETUTIL_E_BUSY: + strcat( szDesc, " NetUtil: NETUTIL_E_BUSY" ); + break; + case NETUTIL_S_FINISHED: + strcat( szDesc, " NetUtil: NETUTIL_S_FINISHED" ); + break; + } +} + +//*********************************************************************************************** +void DebugChatDef( HRESULT hRes ) +{ + char szText[200]; + ChatDefAsText( szText, hRes ); +// debugprint( "%s\n", szText ); +} + +//*********************************************************************************************** +int iChannelLobbyNumber( const unsigned char* szChannelName ) +{ + // Returns lobby number of channel, or -1 for "channel is not a lobby". + if( strncmp( (char*)szChannelName, LOB_PREFIX, strlen( LOB_PREFIX ) ) == 0 ) + { + char szNum[10]; + strcpy( szNum, (char*)szChannelName + strlen( LOB_PREFIX ) ); +// debugprint( " ^ iChannelLobbyNumber returning atoi of %s\n", szNum ); + return atoi( szNum ); + } + else + return -1; +} + +//*********************************************************************************************** +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ) +{ + // Hard-coded translation of lobby number to apparent lobby name. + switch( iLobby ) + { + case 0: + strcpy( szLobbyNameToSet, "Combat Alley" ); + break; + case 1: + strcpy( szLobbyNameToSet, "No Man's Land" ); + break; + case 2: + strcpy( szLobbyNameToSet, "Hell's Pass" ); + break; + case 3: + strcpy( szLobbyNameToSet, "Lost Vegas" ); + break; + case 4: + strcpy( szLobbyNameToSet, "Death Valley" ); + break; + case 5: + strcpy( szLobbyNameToSet, "The Wastelands" ); + break; + case 6: + strcpy( szLobbyNameToSet, "Isle of Fury" ); + break; + case 7: + strcpy( szLobbyNameToSet, "Armourgarden" ); + break; + case 8: + strcpy( szLobbyNameToSet, "The Hive" ); + break; + case 9: + strcpy( szLobbyNameToSet, "North by Northwest" ); + break; + case 10: + strcpy( szLobbyNameToSet, "Decatur High" ); + break; + case 11: + strcpy( szLobbyNameToSet, "Damnation Alley" ); + break; + default: + sprintf( szLobbyNameToSet, "%ith Division", iLobby ); + break; + } +} + +#endif \ No newline at end of file diff --git a/code/redalert/rawolapi.h b/code/redalert/rawolapi.h new file mode 100644 index 0000000..3bad368 --- /dev/null +++ b/code/redalert/rawolapi.h @@ -0,0 +1,339 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +#ifndef WIN32 +#error WOLAPI_INTEGRATION can't be specified for non WIN32 version! +#endif + +// rawolapi.h - WOLAPI sinks declarations. +// ajw 07/10/98 + +// Based somewhat on Neal's Borlandized version, "chatapi.h". + +#ifndef RAWOLAPI_H +#define RAWOLAPI_H + +#include "function.h" + +//#include "cominit.h" +#include + +// From OBJBASE.H +#define interface struct + +// From RPCNDR.H +#define DECLSPEC_UUID(x) + +#include + +//namespace WOL // namespace is workaround due to the use of "Server" as a global in Red Alert. +// ajw - Can't use namespaces in Watcom 10.5 it seems... +//{ + #include "wolapi\wolapi.h" + #define IID_DEFINED + //#include "wlib/wdebug.h" + #include "wolapi\chatdefs.h" + #include "wolapi\downloaddefs.h" + #include "wolapi\ftpdefs.h" +//}; +//using namespace WOL; +#include +//#include +#include + +//*********************************************************************************************** +// For debugging chat defined hresults... +void ChatDefAsText( char* szDesc, HRESULT hRes ); +void DebugChatDef( HRESULT hRes ); + +int iChannelLobbyNumber( const unsigned char* szChannelName ); +#define REASONABLELOBBYINTERPRETEDNAMELEN 50 +void InterpretLobbyNumber( char* szLobbyNameToSet, int iLobby ); + +class WolapiObject; + +#define MAXCHATSENDLENGTH 71 // Mainly aesthetic, and because of the length of edit line. + +enum CHANNELFILTER +{ + CHANNELFILTER_NO, + CHANNELFILTER_OFFICIAL, + CHANNELFILTER_UNOFFICIAL, + CHANNELFILTER_LOBBIES, + CHANNELFILTER_LOCALLOBBYGAMES, +}; + +#define WOLCOLORREMAP_ACTION PCOLOR_GREY +#define WOLCOLORREMAP_SELFSPEAKING PCOLOR_RED +#define WOLCOLORREMAP_LOCALMACHINEMESS PCOLOR_REALLY_BLUE // Color of system messages that originate locally. +#define WOLCOLORREMAP_PAGE PCOLOR_GOLD +#define WOLCOLORREMAP_KICKORBAN PCOLOR_GREEN //LTBLUE +#define WOLCOLORREMAP_PUBLICMESSAGE PCOLOR_NONE +#define WOLCOLORREMAP_PRIVATEMESSAGE PCOLOR_ORANGE + +#define WOLSOUND_ERROR VOC_SYS_ERROR +#define WOLSOUND_LOGIN VOC_RADAR_ON +#define WOLSOUND_LOGOUT VOC_RADAR_OFF +#define WOLSOUND_ENTERCHAN VOC_PLAYER_JOINED +#define WOLSOUND_EXITCHAN VOC_PLAYER_LEFT +#define WOLSOUND_ONPAGE VOC_INCOMING_MESSAGE +#define WOLSOUND_KICKORBAN VOC_TANYA_KISS +#define WOLSOUND_ENTERGAME VOC_INVULNERABLE +#define WOLSOUND_EXITGAME VOC_DOOR + +enum DISCONNECT_PING_STATUS +{ + PING_UNSTARTED, + PING_WAITING, + PING_GOOD, + PING_BAD, +}; +#define DISCONNECT_PING_COUNT 5 + +//*********************************************************************************************** +class RAChatEventSink : /////public CComObjectRoot, /////public IConnectionPoint, + public IChatEvent +{ + public: + RAChatEventSink( WolapiObject* pOwner ); + virtual ~RAChatEventSink(); + +// BEGIN_COM_MAP(RAChatEventSink) +// COM_INTERFACE_ENTRY(IChatEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IChatEvent + STDMETHOD(OnServerList)(HRESULT res, Server* servers); + STDMETHOD(OnLogout)(HRESULT r, User *user); + STDMETHOD(OnBusy)(HRESULT r); + STDMETHOD(OnIdle)(HRESULT r); + STDMETHOD(OnPageSend)(HRESULT r); + STDMETHOD(OnPaged)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnFind)(HRESULT r, Channel *); + STDMETHOD(OnConnection)(HRESULT r,LPCSTR motd); + STDMETHOD(OnChannelCreate)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelModify)(HRESULT r, Channel *channel); + STDMETHOD(OnChannelJoin)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelLeave)(HRESULT r, Channel *channel, User *user); + STDMETHOD(OnChannelTopic)(HRESULT r, Channel *channel, LPCSTR topic); + STDMETHOD(OnGroupList)(HRESULT r, Group *); + STDMETHOD(OnPublicMessage)(HRESULT r, Channel *channel, User *user, LPCSTR text); + STDMETHOD(OnPrivateMessage)(HRESULT r, User *user,LPCSTR text); + STDMETHOD(OnSystemMessage)(HRESULT r, LPCSTR); + STDMETHOD(OnNetStatus)(HRESULT r); + STDMETHOD(OnChannelList)(HRESULT r, Channel* channels); + STDMETHOD(OnUserList)(HRESULT r, Channel* channel, User* users); + STDMETHOD(OnUpdateList)(HRESULT res, Update *); + STDMETHOD(OnServerError)(HRESULT res); + STDMETHOD(OnMessageOfTheDay)(HRESULT res, LPCSTR); + STDMETHOD(OnPrivateAction)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicAction)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnPrivateGameOptions)(HRESULT r, User *, LPCSTR); + STDMETHOD(OnPublicGameOptions)(HRESULT r, Channel *, User *, LPCSTR); + STDMETHOD(OnGameStart)(HRESULT r, Channel *, User *, int); + STDMETHOD(OnUserKick)(HRESULT r, Channel *, User *, User *); + STDMETHOD(OnUserIP)(HRESULT r, User *); + STDMETHOD(OnServerError)(HRESULT res, LPCSTR ircmsg); + STDMETHOD(OnServerBannedYou)(HRESULT r, time_t bannedTill); + STDMETHOD(OnUserFlags)(HRESULT r, LPCSTR name, unsigned int flags, unsigned int mask); + STDMETHOD(OnChannelBan)(HRESULT r, LPCSTR name, int banned); + + unsigned long GetPlayerGameIP( const char* szPlayerName ) const; + void DeleteUserList(); // Deletes from heap all users pointed to through pUserList. + void DeleteUserIPList(); + unsigned long GetUserIP( const char* szName ) const; + + void ActionEggSound( const char* szMessage ); + +public: + // These vars are rather hackish. Basically, they are set before a callback is expected to be fired, and + // then checked immediately afterwards. The rest of the time, their values are meaningless. + // The idea is to force wolapi act in a modal way. In many places I "block" until a callback response to a + // wolapi request has been received. + bool bRequestServerListWait; + bool bRequestConnectionWait; + bool bRequestLogoutWait; +// bool bRequestChannelListWait; + bool bRequestChannelJoinWait; + bool bRequestChannelLeaveWait; + bool bRequestUserListWait; + bool bRequestChannelCreateWait; + bool bRequestFindWait; + bool bRequestPageWait; + + bool bRequestChannelListForLobbiesWait; + + bool bIgnoreChannelLists; // Used to temporarily turn off response to channel lists, when we are in the midst + // of some processing that depends on pChannelList remaining constant. + + bool bRequestGameStartWait; + + Server* pServer; // Server to connect to, acquired from OnServerList. + bool bConnected; // True when user is logged in to chat server. + bool bJoined; // True when user has joined a channel. + + Channel* pChannelList; // First element of channel list, or null. + CHANNELFILTER ChannelFilter; // Affects what channels are included in channel list when built. + + User* pUserList; // First element of user list, or null. + User* pUserTail; // Last element of user list, or null. + + char* szMotd; // Message of the day. + HRESULT hresRequestConnectionError; // Used to pass error hresult. + + HRESULT hresRequestFindResult; // Used to pass hresult. + Channel OnFindChannel; + + HRESULT hresRequestPageResult; // Used to pass hresult. + + HRESULT hresRequestJoinResult; // Used to pass hresult. + + bool bGotKickedTrigger; // Special flag meaning do some more processing after callback has exited. + + User* pGameUserList; // First element of start game user list, or null. + int iGameID; // WW Online game id received from OnGameStart. + // Is also a flag indicating "OnGameStart() called, TriggerGameStart() not yet called". + + User* pUserIPList; // List that holds user IP's, used for pinging in game channel. + User* pUserIPListTail; + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + + void DeleteChannelList(); // Deletes from heap all channels pointed to through pChannelList. + bool DownloadUpdates( Update* pUpdateList, int iUpdates ); + bool bSpecialMessage( const char* szMessage ); + void InsertUserSorted( User* pUserNew ); + +private: + long m_cRef; // Reference Count + +}; + +//*********************************************************************************************** +class RADownloadEventSink : +/////////// public CComObjectRoot, + public IDownloadEvent +{ +public: + RADownloadEventSink(); + virtual ~RADownloadEventSink() {}; + +// BEGIN_COM_MAP(RADownloadEventSink) +// COM_INTERFACE_ENTRY(IDownloadEvent) +// END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // IDownloadEvent + STDMETHOD(OnEnd)(void); + STDMETHOD(OnError)(int error); + STDMETHOD(OnProgressUpdate)(int bytesread, int totalsize, int timetaken, int timeleft); + STDMETHOD(OnStatusUpdate)(int status); + STDMETHOD(OnQueryResume)(void); + +public: + bool bFlagEnd; + bool bFlagError; + bool bFlagProgressUpdate; + bool bFlagStatusUpdate; + bool bFlagQueryResume; + int iBytesRead; + int iTotalSize; + int iTimeTaken; + int iTimeLeft; + int iStatus; + bool bResumed; + +private: + long m_cRef; // Ref count +}; + +//*********************************************************************************************** +class RANetUtilEventSink : +// public CComObjectRoot, + public INetUtilEvent +{ +public: + RANetUtilEventSink( WolapiObject* pOwner ); + virtual ~RANetUtilEventSink(); + +//BEGIN_COM_MAP(CNetUtilEventSink) +// COM_INTERFACE_ENTRY(INetUtilEvent) +//END_COM_MAP() + + // IUnknown + STDMETHOD(QueryInterface)(const IID& iid, void **ppv); + STDMETHOD_(ULONG,AddRef)(); + STDMETHOD_(ULONG,Release)(); + + // INetUtilEvent + + STDMETHOD(OnGameresSent)(HRESULT res); + STDMETHOD(OnLadderList)(HRESULT res, Ladder *list, int totalCount, long timeStamp, int keyRung); + STDMETHOD(OnPing)(HRESULT res, int time, unsigned long ip, int handle); + + + void DeleteLadderList(); // Deletes from heap all users pointed to through pUserList. + unsigned int GetUserRank( const char* szName, bool bRankRA ); + + Ladder* pLadderList; // First element of Ladder list, or null. + Ladder* pLadderTail; // Last element of Ladder list, or null. + Ladder* pLadderListAM; // First element of Aftermath Ladder list, or null. + Ladder* pLadderTailAM; // Last element of Aftermath Ladder list, or null. + +protected: + WolapiObject* pOwner; // Link back to the object that contains me. + +private: + long m_cRef; // Reference Count +}; + +//*********************************************************************************************** + +// SKU, reported to WOLAPI for the purpose of finding patches. +#ifdef ENGLISH +#define GAME_SKU 0x1500 +#else +#ifdef GERMAN +#define GAME_SKU 0x1502 +#else +#define GAME_SKU 0x1503 +#endif +#endif + +#define GAME_VERSION 0x00030003 +#define GAME_TYPE 21 +#define LOB_PREFIX "Lob_21_" + +// Sent to gameres server in order to receive Red Alert or Aftermath ladder rankings. (Sent in RequestLadderList.) +#define LADDER_CODE_RA 1005 +#define LADDER_CODE_AM 500 + +#endif + +#endif + +#define GAME_VERSION 0x00030003 \ No newline at end of file diff --git a/code/redalert/readline.cpp b/code/redalert/readline.cpp new file mode 100644 index 0000000..62f0b60 --- /dev/null +++ b/code/redalert/readline.cpp @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "function.h" +#include +#include +#include "wwfile.h" +#include "xstraw.h" +#include "readline.h" + + +// Disable the "temporary object used to initialize a non-constant reference" warning. +//#pragma warning 665 9 + + +void strtrimcpp(char * buffer) +{ + if (buffer) { + + /* + ** Strip leading white space from the string. + */ + char * source = buffer; + while (isspace(*source)) { + source++; + } + if (source != buffer) { + strcpy(buffer, source); + } + + /* + ** Clip trailing white space from the string. + */ + for (int index = strlen(buffer)-1; index >= 0; index--) { + if (isspace(buffer[index])) { + buffer[index] = '\0'; + } else { + break; + } + } + } +} + + + +int Read_Line(FileClass & file, char * buffer, int len, bool & eof) +{ + return(Read_Line(FileStraw(file), buffer, len, eof)); +} + + +int Read_Line(Straw & file, char * buffer, int len, bool & eof) +{ + if (len == 0 || buffer == NULL) return(0); + + int count = 0; + for (;;) { + char c; + if (file.Get(&c, sizeof(c)) != sizeof(c)) { + eof = true; + buffer[0] = '\0'; + break; + } + + if (c == '\x0A') break; + if (c != '\x0D' && count+1 < len) { + buffer[count++] = c; + } + } + buffer[count] = '\0'; + + strtrimcpp(buffer); + return(strlen(buffer)); +} diff --git a/code/redalert/readline.h b/code/redalert/readline.h new file mode 100644 index 0000000..b0a8281 --- /dev/null +++ b/code/redalert/readline.h @@ -0,0 +1,23 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#include "wwfile.h" +#include "straw.h" + +void strtrim(char * buffer); +int Read_Line(FileClass & file, char * buffer, int len, bool & eof); +int Read_Line(Straw & file, char * buffer, int len, bool & eof); + diff --git a/code/redalert/rect.cpp b/code/redalert/rect.cpp new file mode 100644 index 0000000..d51f42a --- /dev/null +++ b/code/redalert/rect.cpp @@ -0,0 +1,197 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RECT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RECT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/22/96 * + * * + * Last Update : July 22, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Rect::Rect -- Constructs a rectangle object. * + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * Rect::Intersect -- Find the intersection between two rectangles. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "rect.h" + + +/*********************************************************************************************** + * Rect::Rect -- Constructs a rectangle object. * + * * + * This will construct a rectangle object according to the parameters specified. * + * * + * INPUT: x,y -- The X and Y values of the upper left corner of the rectangle. * + * * + * w,h -- The width and height values of the rectangle. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect::Rect(int x, int y, int w, int h) : + X(x), + Y(y), + Width(w), + Height(h) +{ +} + + +/*********************************************************************************************** + * Rect::Is_Valid -- Determines if the rectangle is valid. * + * * + * An invalid rectangle has values that do not make any sense. This is a useful state since * + * this can be used to determine if a rectangle has been initialized correctly or for * + * detecting an error return condition for rectangle manipulation routines. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Does this rectangle appear valid? * + * * + * WARNINGS: An invalid rectangle is one that has a width or height of less than one. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +bool Rect::Is_Valid(void) const +{ + return(Width > 0 && Height > 0); +} + + +/*********************************************************************************************** + * Rect::Intersect -- Find the intersection between two rectangles. * + * * + * This routine will take the specified rectangle and use it like a "cookie cutter" on this * + * rectangle. The intersection of these two rectangles is returned. An optional X and * + * Y parameter is supplied so that an absolute coordinate can be maintained to the new * + * rectangle. * + * * + * INPUT: rectangle -- Reference to the rectangle to use as a cookie cutter. * + * * + * x,y -- Optional pointer to a coordinate that will be adjusted to stay * + * in an absolute position in coordinates even though it is a * + * relative offset. * + * * + * OUTPUT: Returns with the rectangle that is the intersection of the one specified and * + * this rectangle. * + * * + * WARNINGS: The rectangle returned may be invalid. This can occur if there is no legal * + * intersection between the rectangles. * + * * + * HISTORY: * + * 07/22/1996 JLB : Created. * + *=============================================================================================*/ +Rect const Rect::Intersect(Rect const & rectangle, int * x, int * y) const +{ + Rect rect(0, 0, 0, 0); // Dummy (illegal) rectangle. + Rect r = rectangle; // Working rectangle. + + /* + ** Both rectangles must be valid or else no intersection can occur. In such + ** a case, return an illegal rectangle. + */ + if (!Is_Valid() || !rectangle.Is_Valid()) return(rect); + + /* + ** The rectangle spills past the left edge. + */ + if (r.X < X) { + r.Width -= X - r.X; + r.X = X; + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past top edge. + */ + if (r.Y < Y) { + r.Height -= Y - r.Y; + r.Y = Y; + } + if (r.Height < 1) return(rect); + + /* + ** The rectangle spills past the right edge. + */ + if (r.X + r.Width > X + Width) { + r.Width -= (r.X + r.Width) - (X + Width); + } + if (r.Width < 1) return(rect); + + /* + ** The rectangle spills past the bottom edge. + */ + if (r.Y + r.Height > Y + Height) { + r.Height -= (r.Y + r.Height) - (Y + Height); + } + if (r.Height < 1) return(rect); + + /* + ** Adjust Height relative draw position according to Height new rectangle + ** union. + */ + if (x != NULL) { + *x -= (r.X-X); + } + if (y != NULL) { + *y -= (r.Y-Y); + } + + return(r); +} + + +Rect const Union(Rect const & rect1, Rect const & rect2) +{ + if (rect1.Is_Valid()) { + if (rect2.Is_Valid()) { + Rect result = rect1; + + if (result.X > rect2.X) { + result.Width += result.X-rect2.X; + result.X = rect2.X; + } + if (result.Y > rect2.Y) { + result.Height += result.Y-rect2.Y; + result.Y = rect2.Y; + } + if (result.X+result.Width < rect2.X+rect2.Width) { + result.Width = ((rect2.X+rect2.Width)-result.X)+1; + } + if (result.Y+result.Height < rect2.Y+rect2.Height) { + result.Height = ((rect2.Y+rect2.Height)-result.Y)+1; + } + return(result); + } + return(rect1); + } + return(rect2); +} diff --git a/code/redalert/rect.h b/code/redalert/rect.h new file mode 100644 index 0000000..8f1a77b --- /dev/null +++ b/code/redalert/rect.h @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RECT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RECT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/21/96 * + * * + * Last Update : July 21, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RECT_H +#define RECT_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#include + +class Rect +{ + public: + Rect(int x=0, int y=0, int w=0, int h=0); + + Rect const Intersect(Rect const & rectangle, int * x=NULL, int * y=NULL) const; + friend Rect const Union(Rect const & rect1, Rect const & rect2); + + bool Is_Valid(void) const; + int Size(void) const {return(Width*Height);} + +// private: + int X; + int Y; + int Width; + int Height; +}; + + +#endif + diff --git a/code/redalert/region.h b/code/redalert/region.h new file mode 100644 index 0000000..33fdba6 --- /dev/null +++ b/code/redalert/region.h @@ -0,0 +1,56 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/REGION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REGION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/09/95 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef REGION_H +#define REGION_H + + +class RegionClass { + public: + RegionClass(void) {Threat = 0;}; + ~RegionClass(void) {}; + int operator != (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass));}; + int operator == (RegionClass const & region) {return !memcmp(this, ®ion, sizeof(RegionClass));}; + int operator > (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) > 0;}; + int operator < (RegionClass const & region) {return memcmp(this, ®ion, sizeof(RegionClass)) < 0;}; + + void Reset_Threat(void) {Threat = 0;}; + void Adjust_Threat(int threat, int neg) {if (neg) Threat -= threat; else Threat+= threat;}; + int Threat_Value(void) const {return Threat;}; + + protected: + long Threat; +}; + +#endif diff --git a/code/redalert/reinf.cpp b/code/redalert/reinf.cpp new file mode 100644 index 0000000..bdd2cb1 --- /dev/null +++ b/code/redalert/reinf.cpp @@ -0,0 +1,748 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/REINF.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : REINF.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 24, 1994 * + * * + * Last Update : July 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * Do_Reinforcements -- Create and place a reinforcement team. * + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * _Create_Group -- Create a group given team specification. * + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * _Pop_Group_Out_Of_Object -- Process popping the group out of the object. * + * * + * This routine will cause the group to pop out of the object specified. * + * * + * INPUT: group -- Pointer to the first object in the group to be popped out. * + * * + * object -- Pointer to the object that the group is to pop out of. * + * * + * OUTPUT: bool; Was the group popped out of the specified object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Pop_Group_Out_Of_Object(FootClass * group, TechnoClass * object) +{ + assert(group != NULL && object != NULL); + int quantity = 0; + + /* + ** Take every infantry member of this group and detach it from the group list + ** and then make it pop out of the candidate source. + */ + while (group != NULL) { + TechnoClass * todo = group; + group = (FootClass *)(ObjectClass *)group->Next; + todo->Next = NULL; + + switch (object->What_Am_I()) { + + /* + ** The infantry just walks out of a building. + */ + case RTTI_BUILDING: + if (object->Exit_Object(todo) != 2) { + delete todo; + } else { + ++quantity; + } + break; + + /* + ** Infantry get attached to transport vehicles and then unload. + */ + case RTTI_UNIT: + case RTTI_VESSEL: + case RTTI_AIRCRAFT: + object->Attach((FootClass *)todo); + object->Assign_Mission(MISSION_UNLOAD); + ++quantity; + break; + + default: + delete todo; + break; + } + } + + return (quantity != 0); +} + + +/*********************************************************************************************** + * _Need_To_Take -- Examines unit to determine if it should be confiscated. * + * * + * The unit is examined and if the owning house needs to confiscate it, then this routine * + * will return TRUE. In other cases, the unit should be left to its own devices. * + * * + * INPUT: unit -- Pointer to the object to examine. * + * * + * OUTPUT: bool; Should the object be confiscated by the player so that it becomes one of * + * his normal game objects? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool _Need_To_Take(AircraftClass const * air) +{ + if (*air == AIRCRAFT_YAK || *air == AIRCRAFT_MIG) { + int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP); +// int deficit = air->House->Get_Quantity(STRUCT_AIRSTRIP) - (air->House->Get_Quantity(AIRCRAFT_YAK)+air->House->Get_Quantity(AIRCRAFT_MIG)); + + /* + ** Loop through all aircraft and subtract all the ones that are NOT loaners. + */ + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * airptr = Aircraft.Ptr(index); + if ((*airptr == AIRCRAFT_YAK || *airptr == AIRCRAFT_MIG) && airptr->IsOwnedByPlayer && !airptr->IsALoaner && airptr != air) { + deficit -= 1; + if (deficit == 0) break; + } + } + + if (deficit > 0) return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Create_Group -- Create a group given team specification. * + * * + * This routine will create all members of the group as specified by the team type. * + * * + * INPUT: teamtype -- Pointer to the team type that specifies what objects should be * + * created in this group. * + * * + * OUTPUT: Returns with a pointer to the first member of the group created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static FootClass * _Create_Group(TeamTypeClass const * teamtype) +{ + assert(teamtype != NULL); + + TeamClass * team = new TeamClass(teamtype); + if (team != NULL) { + team->Force_Active(); + } + + bool hasunload = false; + for (int tm = 0; tm < teamtype->MissionCount; tm++) { + if (teamtype->MissionList[tm].Mission == TMISSION_UNLOAD) { + hasunload = true; + break; + } + } + + /* + ** Now that the official source for the reinforcement has been determined, the + ** objects themselves must be created. + */ + FootClass * transport = NULL; + FootClass * object = NULL; + for (int index = 0; index < teamtype->ClassCount; index++) { + TechnoTypeClass const * tclass = teamtype->Members[index].Class; + + for (int sub = 0; sub < teamtype->Members[index].Quantity; sub++) { + ScenarioInit++; + FootClass * temp = (FootClass *)tclass->Create_One_Of(HouseClass::As_Pointer(teamtype->House)); + ScenarioInit--; + + if (temp != NULL) { + + /* + ** Add the member to the team. + */ + if (team != NULL) { + ScenarioInit++; + bool ok = team->Add(temp); +//Mono_Printf("Added to team = %d.\n", ok);Keyboard->Get(); + + ScenarioInit--; + temp->IsInitiated = true; + } + + if (temp->What_Am_I() == RTTI_AIRCRAFT && !_Need_To_Take((AircraftClass const *)temp)) { + temp->IsALoaner = true; + } + + /* + ** Build the list of transporters and passengers. + */ + if (tclass->Max_Passengers() > 0) { + + /* + ** Link to the list of transports. + */ + temp->Next = transport; + transport = temp; + + } else { + + /* + ** Link to the list of normal objects. + */ + temp->Next = object; + object = temp; + } + } + } + } + + /* + ** If the group consists of transports and normal objects, then assign the normal + ** objects to be passengers on the transport. + */ + if (transport != NULL && object != NULL) { + transport->Attach(object); + + /* + ** HACK ALERT! If the this team has an unload mission, then flag the transport + ** as a loaner so that it will exit from the map when the unload process is + ** complete, but only if the transport is an aircraft type. + */ + if (hasunload && (transport->What_Am_I() == RTTI_AIRCRAFT || transport->What_Am_I() == RTTI_VESSEL)) { + transport->IsALoaner = true; + } + } + + /* + ** For JUST transport helicopters, consider the loaner a gift if there are + ** no passengers. + */ + if (transport != NULL && object == NULL && transport->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass *)transport) == AIRCRAFT_TRANSPORT) { + transport->IsALoaner = false; + } + + if (transport == 0 && object == 0) { + if (team != NULL) delete team; + return(NULL); + } + + /* + ** If this group consists only of non-transport object, then just return with a pointer + ** to the first member of the group. + */ + if (transport == NULL) { + return(object); + } + + return(transport); +} + + +/*********************************************************************************************** + * _Consists_Only_Of_Infantry -- Determine if this group consists only of infantry. * + * * + * Use this to determine if the specified group only contains infantry. Such a reinforcement* + * group is a candidate for popping out of a building or transport vehicle rather than * + * driving/walking/sailing/flying onto the map under its own power. * + * * + * INPUT: first -- Pointer to the first object in the group to examine. * + * * + * OUTPUT: bool; Is the entire group composed of infantry type units? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static bool _Consists_Only_Of_Infantry(FootClass const * first) +{ + while (first != NULL) { + if (first->What_Am_I() != RTTI_INFANTRY) { + return(false); + } + first = (FootClass const *)((ObjectClass *)first->Next); + } + return(true); +} + + +/*********************************************************************************************** + * _Who_Can_Pop_Out_Of -- Find a suitable host for these reinforcements. * + * * + * This routine is used to scan nearby locations to determine if there is a suitable host * + * for these reinforcements to "pop out of" (apologies to Aliens). Typical hosts include * + * buildings and transport vehicles (of any kind). * + * * + * INPUT: origin -- The cell that should be scanned from. Only this location and immediate * + * adjacent locations will be scanned. * + * * + * OUTPUT: Returns with a pointer to a suitable host. If none could be found then NULL is * + * returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1996 JLB : Created. * + *=============================================================================================*/ +static TechnoClass * _Who_Can_Pop_Out_Of(CELL origin) +{ + CellClass * cellptr = &Map[origin]; + TechnoClass * candidate = NULL; + + for (int f = -1; f < 8; f++) { + CellClass * ptr = cellptr; + if (f != -1) { + ptr = &ptr->Adjacent_Cell(FacingType(f)); + } + + BuildingClass * building = ptr->Cell_Building(); + if (building && building->Strength > 0) { + candidate = building; + } + + UnitClass * unit = ptr->Cell_Unit(); + if (unit && unit->Strength && unit->Class->Max_Passengers() > 0) { + return(unit); + } + } + return(candidate); +} + + +/*********************************************************************************************** + * Do_Reinforcements -- Create and place a reinforcement team. * + * * + * This routine is called when a reinforcement team must be created and placed on the map. * + * It will create all members of the team and place them at the location determined from * + * the team composition. The reinforcement team should follow team orders until overridden * + * by AI or player intervention. * + * * + * INPUT: teamtype -- Pointer to the team type to create as a reinforcement. * + * * + * OUTPUT: Was the reinforcement successfully created and placed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/18/1995 JLB : Returns success or failure condition. * + * 06/19/1995 JLB : Announces reinforcements. * + * 02/15/1996 JLB : Recognizes team reinforcement location. * + *=============================================================================================*/ +bool Do_Reinforcements(TeamTypeClass const * teamtype) +{ + assert(teamtype != 0); + + /* + ** perform some preliminary checks for validity. + */ + if (!teamtype || !teamtype->ClassCount) return(false); + + /* + ** HACK ALERT! + ** Give this team an attack waypoint mission that will attack the waypoint location of this + ** team if there are no team missions previously assigned. + */ + if (teamtype->MissionCount == 0) { + TeamTypeClass * tt = (TeamTypeClass *)teamtype; + tt->MissionCount = 1; + tt->MissionList[0].Mission = TMISSION_ATT_WAYPT; + tt->MissionList[0].Data.Value = teamtype->Origin; + } + + FootClass * object = _Create_Group(teamtype); + + +//Mono_Printf("%d-%s (object=%p, team=%d).\n", __LINE__, __FILE__, object, object->Team.Is_Valid());Keyboard->Get(); + + + /* + ** Bail on this reinforcement if no reinforcements could be created. + ** This is probably because the object maximum was reached. + */ + if (!object) { + return(false); + } + + /* + ** Special case code to handle infantry types that run from a building. This presumes + ** that infantry are never a transport (which is safe to do). + */ + if (object != NULL && teamtype->Origin != -1 && _Consists_Only_Of_Infantry(object)) { + + /* + ** Search for an object that these infantry can pop out of. + */ + TechnoClass * candidate = _Who_Can_Pop_Out_Of(Scen.Waypoint[teamtype->Origin]); + + if (candidate != NULL) { + return(_Pop_Group_Out_Of_Object(object, candidate)); + } + } + + /* + ** The reinforcements must be delivered the old fashioned way -- by moving onto the + ** map using their own power. First order of business is to determine where they + ** should arrive from. + */ + SourceType source = HouseClass::As_Pointer(teamtype->House)->Control.Edge; + if (source == SOURCE_NONE) { + source = SOURCE_NORTH; + } + + /* + ** Pick the location where the reinforcements appear and then place + ** them there. + */ + bool placed = false; + + FacingType eface = (FacingType)(source << 1); // Facing to enter map. + + CELL cell = Map.Calculated_Cell(source, teamtype->Origin, -1, object->Techno_Type_Class()->Speed); +#ifdef FIXIT_ANTS + /* + ** For the ants, they will pop out of the ant hill directly. + */ + if (teamtype->Origin != -1 && object->What_Am_I() == RTTI_UNIT && + (*((UnitClass*)object) == UNIT_ANT1 || + *((UnitClass*)object) == UNIT_ANT2 || + *((UnitClass*)object) == UNIT_ANT3)) { + CELL newcell = Scen.Waypoint[teamtype->Origin]; + if (newcell != -1) { + if (Map[newcell].TType == TEMPLATE_HILL01) { + cell = newcell; + } + } + } +#endif + + CELL newcell = cell; + + FootClass * o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + bool okvoice = false; + while (newcell > 0 && object != NULL) { + DirType desiredfacing = Facing_Dir(eface); + if (object->What_Am_I() == RTTI_AIRCRAFT) { + desiredfacing = Random_Pick(DIR_N, DIR_MAX); + } + + ScenarioInit++; + if (object->Unlimbo(Cell_Coord(newcell), desiredfacing)) { + okvoice = true; + + /* + ** If this object is part of a team, then the mission for this + ** object will be guard. The team handler will assign the proper + ** mission that it should follow. + */ + if (object->What_Am_I() != RTTI_AIRCRAFT) { + object->Assign_Mission(MISSION_GUARD); + object->Commence(); + } + + } else { + + /* + ** Could not unlimbo at location specified so find an adjacent location that it can + ** be unlimboed at. If this fails, then abort the whole placement process. + */ + FacingType adj; + for (adj = FACING_N; adj < FACING_COUNT; adj++) { + CELL trycell = Adjacent_Cell(newcell, adj); + if (!Map.In_Radar(trycell) && object->Can_Enter_Cell(trycell, adj) == MOVE_OK) { + newcell = trycell; + break; + } + } + if (adj < FACING_COUNT) continue; + newcell = 0; + } + ScenarioInit--; + + object = o; + if (object != NULL) { + o = (FootClass *)(ObjectClass *)object->Next; + object->Next = 0; + } + } + + /* + ** If there are still objects that could not be placed, then delete them. + */ + if (o != NULL) { + while (o != NULL) { + FootClass * old = o; + o = (FootClass *)(ObjectClass *)o->Next; + old->Next = 0; + + delete old; + } + } + + /* + ** Announce when the reinforcements have arrived. + */ + if (okvoice && teamtype->House == PlayerPtr->Class->House) { + Speak(VOX_REINFORCEMENTS, NULL, newcell ? Cell_Coord(newcell) : 0); + } + + return(true); +} + + +/*********************************************************************************************** + * Create_Special_Reinforcement -- Ad hoc reinforcement handler. * + * * + * Use this routine to bring on a reinforcement that hasn't been anticipated by the trigger * + * system. An example of this would be replacement harvesters or airfield ordered units. * + * The appropriate transport is created (if necessary) and a mission is assigned such that * + * the object will legally bring itself onto the playing field. * + * * + * INPUT: house -- The owner of this reinforcement. * + * * + * type -- The object to bring on. * + * * + * another -- This is reserved for the transport class in those cases where the * + * transport MUST be forced to a specific type. * + * * + * mission -- The mission to assign this reinforcement team. * + * * + * argument -- Optional team mission argument (usually a waypoint). * + * * + * OUTPUT: Was the special reinforcement created without error? * + * * + * WARNINGS: This routine will fail if a team type cannot be created. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Create_Special_Reinforcement(HouseClass * house, TechnoTypeClass const * type, TechnoTypeClass const * another, TeamMissionType mission, int argument) +{ + assert(house != 0); + assert(type != 0); + + if (house && type) { + TeamTypeClass * team = new TeamTypeClass(); + + if (team) { + + /* + ** If there is no overridden mission assign to this special reinforcement, then + ** we must assign something. If not, the reinforcement will just sit at the edge + ** of the map. + */ + if (!another && mission == TMISSION_NONE) { + mission = TMISSION_MOVECELL; + argument = Map.Calculated_Cell(house->Control.Edge); + } + + /* + ** Fill in the team characteristics. + */ + strcpy((char *)&team->IniName[0], "TEMP"); + team->IsReinforcable = false; + team->IsTransient = true; + team->ClassCount = 1; + team->Members[0].Class = type; + team->Members[0].Quantity = 1; + team->MissionCount = 1; + if (mission == TMISSION_NONE) { + team->MissionList[0].Mission = TMISSION_UNLOAD; + team->MissionList[0].Data.Value = WAYPT_REINF; + } else { + team->MissionList[0].Mission = mission; + team->MissionList[0].Data.Value = argument; + } + team->House = house->Class->House; + if (another) { + team->ClassCount++; + team->Members[1].Class = another; + team->Members[1].Quantity = 1; + } + + bool ok = Do_Reinforcements(team); + if (!ok) delete team; + return(ok); + } + } + return(false); +} + + +/*********************************************************************************************** + * Create_Air_Reinforcement -- Creates air strike reinforcement * + * * + * This routine is used to launch an airstrike. It will create the necessary aircraft and * + * assign them to attack the target specified. This routine bypasses the normal * + * reinforcement logic since it doesn't need the sophistication of unloading and following * + * team mission lists. * + * * + * INPUT: house -- The perpetrator of this air strike. * + * * + * air -- The type of aircraft to make up this airstrike. * + * * + * number -- The number of aircraft in this airstrike. * + * * + * mission -- The mission to assign the aircraft. * + * * + * tarcom -- The target to assign these aircraft. * + * * + * navcom -- The navigation target to assign (if necessary). * + * * + * OUTPUT: Returns the number of aircraft created for this airstrike. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Commented. * + *=============================================================================================*/ +int Create_Air_Reinforcement(HouseClass * house, AircraftType air, int number, MissionType mission, TARGET tarcom, TARGET navcom, InfantryType passenger) +{ + assert(house != 0); + assert((unsigned)air < AIRCRAFT_COUNT); + assert(number != 0); + assert((unsigned)mission < MISSION_COUNT); + /* + ** Get a pointer to the class of the object that we are going to create. + */ + TechnoTypeClass const * type = (TechnoTypeClass *)&AircraftTypeClass::As_Reference(air); + + /* + ** Abort the airstrike if Tanya is the passenger and she's dead. + */ + if (passenger == INFANTRY_TANYA && IsTanyaDead) { + number = 0; + } + + /* + ** Loop through the number of objects we are supposed to create and + ** create and place them on the map. + */ + int sub; + for (sub = 0; sub < number; sub++) { + + /* + ** Create one of the required objects. If this fails we could have + ** a real problem. + */ + ScenarioInit++; + TechnoClass * obj = (TechnoClass *)type->Create_One_Of(house); + ScenarioInit--; + if (!obj) return(sub); + + /* + ** Flying objects always have the IsALoaner bit set. + */ + obj->IsALoaner = true; + + /* + ** Find a cell for the object to come in on. This is stolen from the + ** the code that handles a SOURCE_AIR in the normal logic. + */ + SourceType source = house->Control.Edge; + switch (source) { + case SOURCE_NORTH: + case SOURCE_EAST: + case SOURCE_SOUTH: + case SOURCE_WEST: + break; + + default: + source = SOURCE_NORTH; + break; + } + CELL newcell = Map.Calculated_Cell(source, -1, -1, SPEED_WINGED); + + /* + ** Try and place the object onto the map. + */ + ScenarioInit++; + int placed = obj->Unlimbo(Cell_Coord(newcell), DIR_N); + ScenarioInit--; + if (placed) { + + /* + ** If we succeeded in placing the obj onto the map then + ** now we need to give it a mission and destination. + */ + obj->Assign_Mission(mission); + + /* + ** If a navcom was specified then set it. + */ + if (navcom != TARGET_NONE) { + obj->Assign_Destination(navcom); + } + + /* + ** If a tarcom was specified then set it. + */ + if (tarcom != TARGET_NONE) { + obj->Assign_Target(tarcom); + } + + /* + ** Assign generic passenger value here. This value is used to determine + ** if this aircraft should drop parachute reinforcements. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass * aircraft = (AircraftClass *)obj; + if (passenger != INFANTRY_NONE) { + aircraft->Passenger = passenger; + } +// if (Passenger == INFANTRY_TANYA) { +// aircraft->Ammo = 1; + //aircraft->AttacksRemaining = 1; +// } + } + + /* + ** Start the object into action. + */ + obj->Commence(); + } else { + delete obj; + sub--; + return(sub); + } + } + return(sub); +} diff --git a/code/redalert/resource/redalert.rc b/code/redalert/resource/redalert.rc new file mode 100644 index 0000000..e7ddb71 --- /dev/null +++ b/code/redalert/resource/redalert.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Petroglyph Games Inc." + VALUE "FileDescription", "Command & Conquer(TM) Remastered Collection RedAlert DLL" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "Command & Conquer(TM) Remastered Collection" + VALUE "LegalCopyright", "(C) 2020 Electronic Arts Inc. All rights reserved." + VALUE "OriginalFilename", "RedAlert.DLL" + VALUE "ProductName", "Command & Conquer(TM) Remastered Collection" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/code/redalert/resource/resource.h b/code/redalert/resource/resource.h new file mode 100644 index 0000000..d0c028e --- /dev/null +++ b/code/redalert/resource/resource.h @@ -0,0 +1,30 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by RedAlert.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/code/redalert/rgb.cpp b/code/redalert/rgb.cpp new file mode 100644 index 0000000..265d47d --- /dev/null +++ b/code/redalert/rgb.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RGB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RGB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : February 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * RGBClass::Difference -- Determines the "distance" between two colors. * + * RGBClass::Set -- Set the color index to the RGB object specified. * + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "watcom.h" +#include "rgb.h" +#include "hsv.h" +#include "palettec.h" + +RGBClass const BlackColor(0, 0, 0); + + +/*********************************************************************************************** + * RGBClass::Adjust -- Adjust one RGB value toward another. * + * * + * This routine is used to modify an RGB value to proportionately match another RGB value * + * according to the ratio parameter specified. Typical use of this routine is in palette * + * fading from one palette to another or to black. * + * * + * INPUT: ratio -- The ration of transformation. This value is in the form of 0 to 255, * + * with 0 being no change, and 255 being 100% transformed into the * + * destination color. * + * * + * rgb -- Reference to the destination RGB color to transform this color into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Adjust(int ratio, RGBClass const & rgb) +{ + /* + ** Ratio conversion is limited to 0 through 100%. This is + ** the range of 0 to 255. + */ + ratio &= 0x00FF; + + /* + ** Adjust the color guns by the ratio specified toward the + ** destination color. + */ + int value = (int)rgb.Red - (int)Red; + Red = (int)Red + (value * ratio) / 256; + + value = (int)rgb.Green - (int)Green; + Green = (int)Green + (value * ratio) / 256; + + value = (int)rgb.Blue - (int)Blue; + Blue = (int)Blue + (value * ratio) / 256; +} + + +/*********************************************************************************************** + * RGBClass::Difference -- Determines the "distance" between two colors. * + * * + * This routine is used to calculate a relative distance between two colors. The value is * + * relative only to itself and thus is useful only for determining the magnitude of * + * color difference rather than the nature of the color difference. Palette remapping * + * code uses this routine to find closest matches for colors. * + * * + * INPUT: rgb -- Reference to the color to be compared to this color. * + * * + * OUTPUT: Returns the difference between the two colors. The value returned is zero if the * + * colors exactly match. The greater the positive value the greater the difference * + * between the colors. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/02/1995 JLB : Created. * + *=============================================================================================*/ +int RGBClass::Difference(RGBClass const & rgb) const +{ + int r = (int)Red - (int)rgb.Red; + if (r < 0) r = -r; + + int g = (int)Green - (int)rgb.Green; + if (g < 0) g = -g; + + int b = (int)Blue - (int)rgb.Blue; + if (b < 0) b = -b; + + return(r*r + g*g + b*b); +} + + +/*********************************************************************************************** + * RGBClass::operator HSVClass -- Conversion operator for RGB to HSV object. * + * * + * This conversion operator will convert an RGBClass object into an HSVClass object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference (implicit) to the HSVClass object that most closely * + * represents the RGBClass object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +RGBClass::operator HSVClass (void) const +{ + int hue; + int saturation; + int value; + + /* + ** Fetch working component values for the color guns. + */ + int red = Red_Component(); + int green = Green_Component(); + int blue = Blue_Component(); + + /* + ** The hue defaults to none. Only if there is a saturation value will the + ** hue be calculated. + */ + hue = 0; + + /* + ** Set the value (brightness) to match the brightest color gun. + */ + value = (red > green) ? red : green; + if (blue > value) value = blue; + + /* + ** Determine the amount of true white present in the color. This is the + ** minimum color gun value. The white component is used to determine + ** color saturation. + */ + int white = (red < green) ? red : green; + if (blue < white) white = blue; + + /* + ** Determine the saturation (intensity) of the color by comparing the + ** ratio of true white as a component of the overall color. The more + ** white component, the less saturation. + */ + saturation = 0; + if (value) { + saturation = ((value - white) * 255) / value; + } + + /* + ** If there is any saturation at all, then the hue must be calculated. The + ** hue is based on a six sided color wheel. + */ + if (saturation != 0) { + unsigned int tmp = value - white; + unsigned int r1 = ((value - red) * 255) / tmp; + unsigned int g1 = ((value - green) * 255) / tmp; + unsigned int b1 = ((value - blue) * 255) / tmp; + + // Find effect of second most predominant color. + // In which section of the hexagon of colors does the color lie? + if (value == red) { + if (white == green) { + tmp = 5 * 256 + b1; + } else { + tmp = 1 * 256 - g1; + } + } else { + if (value == green) { + if (white == blue) { + tmp = 1 * 256 + r1; + } else { + tmp = 3 * 256 - b1; + } + } else { + if (white == red) { + tmp = 3 * 256 + g1; + } else { + tmp = 5 * 256 - r1; + } + } + } + + // Divide by six and round. + hue = tmp / 6; + } + + return(HSVClass(hue, saturation, value)); +} + + +/*********************************************************************************************** + * RGBClass::Set -- Set the color index to the RGB object specified. * + * * + * This routine will set the specified color index with this RGBClass object. Use this * + * routine to set one color. * + * * + * INPUT: color -- The color index to set with this RGBClass object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/20/1996 JLB : Created. * + *=============================================================================================*/ +void RGBClass::Set(int color) const +{ + Raw_Color_Prep(color); + Raw_Set(); + + ((RGBClass &)PaletteClass::CurrentPalette[color]) = *this; +} + diff --git a/code/redalert/rgb.h b/code/redalert/rgb.h new file mode 100644 index 0000000..a91f2d1 --- /dev/null +++ b/code/redalert/rgb.h @@ -0,0 +1,140 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RGB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RGB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/02/95 * + * * + * Last Update : December 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RGB_H +#define RGB_H + +class PaletteClass; +class HSVClass; + +#if (0) + +#ifndef OUTPORTB +#define OUTPORTB +extern void outportb(int port, unsigned char data); +#pragma aux outportb parm [edx] [al] = \ + "out dx,al" + +extern void outport(int port, unsigned short data); +#pragma aux outport parm [edx] [ax] = \ + "out dx,al" \ + "inc dx" \ + "mov al,ah" \ + "out dx,al" +#endif + +extern void outrgb(unsigned char red, unsigned char green, unsigned char blue); +#pragma aux outrgb parm [al] [bl] [cl] \ + modify [dx al] = \ + "mov dx,03C9h" \ + "out dx,al" \ + "jmp e1" \ + "e1:" \ + "mov al,bl" \ + "out dx,al" \ + "jmp e2" \ + "e2:" \ + "mov al,cl" \ + "out dx,al" \ + "jmp e3" \ + "e3:" + +#endif + +/* +** Each color entry is represented by this class. It holds the values for the color +** guns. The gun values are recorded in device dependant format, but the interface +** uses gun values from 0 to 255. +*/ +class RGBClass +{ +// static RGBClass const BlackColor; + + public: + RGBClass(void) : Red(0), Green(0), Blue(0) {}; + RGBClass(unsigned char red, unsigned char green, unsigned char blue) : + Red((unsigned char)(red>>2)), + Green((unsigned char)(green>>2)), + Blue((unsigned char)(blue>>2)) + {}; + operator HSVClass (void) const; + RGBClass & operator = (RGBClass const & rgb) { + if (this == &rgb) return(*this); + + Red = rgb.Red; + Green = rgb.Green; + Blue = rgb.Blue; + return(*this); + }; + + enum { + MAX_VALUE=255 + }; + + void Adjust(int ratio, RGBClass const & rgb); +// void Adjust(int ratio, RGBClass const & rgb = BlackColor); + int Difference(RGBClass const & rgb) const; + int Red_Component(void) const {return ((Red << 2) | (Red >> 6));}; + int Green_Component(void) const {return((Green << 2) | (Green >> 6));}; + int Blue_Component(void) const {return((Blue << 2) | (Blue >> 6));}; + void Set(int color) const; + + private: + + friend class PaletteClass; + + void Raw_Set(void) const { + //outrgb(Red, Green, Blue); + +// outportb(0x03C9, Red); +// outportb(0x03C9, Green); +// outportb(0x03C9, Blue); + }; + + static void Raw_Color_Prep(unsigned char color) { + //outportb(0x03C8, color); + }; + + /* + ** These hold the actual color gun values in machine dependant scale. This + ** means the values range from 0 to 63. + */ + unsigned char Red; + unsigned char Green; + unsigned char Blue; +}; + +extern RGBClass const BlackColor; + +#endif diff --git a/code/redalert/rndstraw.cpp b/code/redalert/rndstraw.cpp new file mode 100644 index 0000000..c9dc7bc --- /dev/null +++ b/code/redalert/rndstraw.cpp @@ -0,0 +1,317 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNDSTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNDSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RandomStraw::Get -- Fetch random data. * + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * RandomStraw::Reset -- Reset the data to known initial state. * + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "rndstraw.h" +#include "sha.h" + + +/*********************************************************************************************** + * RandomStraw::RandomStraw -- Constructor for the random straw class. * + * * + * This will initialize the random straw into a known state. The initial state is useless * + * for cryptographic purposes. It must be seeded before it should be used. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::RandomStraw(void) : + SeedBits(0), + Current(0) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::~RandomStraw -- Destructor for random straw class. * + * * + * This destructor will clear out the seed data so that there won't be any sensitive * + * information left over in the memory this object occupied. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +RandomStraw::~RandomStraw(void) +{ + Reset(); +} + + +/*********************************************************************************************** + * RandomStraw::Reset -- Reset the data to known initial state. * + * * + * This routine is functionally equivalent to performing an placement new on the object. It * + * clears out all the seed data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must reseed it before fetching random numbers. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Reset(void) +{ + SeedBits = 0; + Current = 0; + memset(Random, '\0', sizeof(Random)); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bits_Needed -- Fetches the number of seed bits needed. * + * * + * This routine is used when seeding the random straw. Keep feeding random bits to this * + * class until the Seed_Bits_Needed value returns zero. Random bits should be gathered from * + * sources external to the immediate program. Examples would be the system clock (good for * + * a few bits), the delay between player keystrokes (good for a few bits per keystroke), * + * etc. When gathering random bits from integers, use the low order bits (since they * + * characteristically are less predictable and more 'random' than the others). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bits required in order to fully seed this random * + * number generator. * + * * + * WARNINGS: Even if this routine returns zero, you are still allowed and encouraged to feed * + * more random bits when the opportunity arrises. * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +int RandomStraw::Seed_Bits_Needed(void) const +{ + const int total = sizeof(Random) * CHAR_BIT; + if (SeedBits < total) { + return(total - SeedBits); + } + return(0); +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Bit -- Add a random bit to the accumulated seed value. * + * * + * This routine should be called to feed a single random bit to the random straw object. * + * You must feed as many bits as requested by the Seed_Bits_Needed() function. Submitting * + * additional bits is not only allowed, but encouraged. * + * * + * INPUT: seed -- The bit (lowest order bit) to feed to the random number seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Bit(int seed) +{ + char * ptr = ((char *)&Random[0]) + ((SeedBits / CHAR_BIT) % sizeof(Random)); + char frac = (char)(1 << (SeedBits & (CHAR_BIT-1))); + + if (seed & 0x01) { + *ptr ^= frac; + } + SeedBits++; + + if (SeedBits == (sizeof(Random) * CHAR_BIT)) { + Scramble_Seed(); + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Byte -- Submit 8 bits to the random number seed. * + * * + * This will submit 8 bits (as specified) to the random number seed. * + * * + * INPUT: seed -- The seed bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Byte(char seed) +{ + for (int index = 0; index < CHAR_BIT; index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Short -- Submit 16 bits to the random number seed. * + * * + * This will submit 16 bits to the accumulated seed. * + * * + * INPUT: seed -- The 16 bits to submit to the seed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Short(short seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Seed_Long -- Submit 32 bits to the random number seed. * + * * + * This will submit a full 32 bits to the accumulated seed value. * + * * + * INPUT: seed -- The 32 bits to submit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Seed_Long(long seed) +{ + for (int index = 0; index < (sizeof(seed)*CHAR_BIT); index++) { + Seed_Bit(seed); + seed >>= 1; + } +} + + +/*********************************************************************************************** + * RandomStraw::Scramble_Seed -- Masks any coorelation between the seed bits. * + * * + * This routine is called when a full set of seed bits has been accumulated. It will take * + * the existing seed and scramble the bits. An effective way of doing this is to use the * + * Secure Hash Algorithm. It is necessary to have this routine because there might be * + * some coorelation in the seed bits. Even more important is that this routine will result * + * in every bit of the seed having a scrambling effect on every other bit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/10/1996 JLB : Created. * + *=============================================================================================*/ +void RandomStraw::Scramble_Seed(void) +{ + SHAEngine sha; + + for (int index = 0; index < sizeof(Random); index++) { + char digest[20]; + + sha.Hash(&Random[0], sizeof(Random)); + sha.Result(digest); + + int tocopy = sizeof(digest) < (sizeof(Random)-index) ? sizeof(digest) : (sizeof(Random)-index); + memmove(((char *)&Random[0]) + index, digest, tocopy); + } +} + + +/*********************************************************************************************** + * RandomStraw::Get -- Fetch random data. * + * * + * This routine will fetch random data and store it into the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to fill with random data. * + * * + * length -- The number of bytes to store into the buffer. * + * * + * OUTPUT: Returns with the actual number of bytes stored into the buffer. This will always * + * be the number of bytes requested since random numbers are unexhaustible. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1996 JLB : Created. * + * 07/10/1996 JLB : Revamped to make cryptographically secure. * + *=============================================================================================*/ +int RandomStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(Straw::Get(source, slen)); + } + + int total = 0; + while (slen > 0) { + *(char *)source = (char)Random[Current++]; + Current = Current % (sizeof(Random) / sizeof(Random[0])); + source = (char*)source + sizeof(char); + slen--; + total++; + } + return(total); +} diff --git a/code/redalert/rndstraw.h b/code/redalert/rndstraw.h new file mode 100644 index 0000000..c19f678 --- /dev/null +++ b/code/redalert/rndstraw.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNDSTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNDSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RNDSTRAW_H +#define RNDSTRAW_H + + +#include "straw.h" +#include "random.h" + +/* +** This is a straw terminator class. It will generate random numbers to fill the data request. +** Unlike regular straw terminators, this class will never run out of "data". +*/ +class RandomStraw : public Straw +{ + public: + RandomStraw(void); + virtual ~RandomStraw(void); + + virtual int Get(void * source, int slen); + + void Reset(void); + void Seed_Bit(int seed); + void Seed_Byte(char seed); + void Seed_Short(short seed); + void Seed_Long(long seed); + + int Seed_Bits_Needed(void) const; + + private: + /* + ** Counter of the number of seed bits stored to this random number + ** generator. + */ + int SeedBits; + + /* + ** The current random generator to use when fetching the next random + ** byte of data. + */ + int Current; + + /* + ** Array of generators. There must be at least 448 bits of random number seed + ** in order to be reasonably secure, however, using 1024 bits would be best. + */ + RandomClass Random[32]; + + void Scramble_Seed(void); + + RandomStraw(RandomStraw & rvalue); + RandomStraw & operator = (RandomStraw const & pipe); +}; + + +#endif diff --git a/code/redalert/rng.h b/code/redalert/rng.h new file mode 100644 index 0000000..5016169 --- /dev/null +++ b/code/redalert/rng.h @@ -0,0 +1,51 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RNG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RNG.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RNG_H +#define RNG_H + +/* +** This is an abstract interface class for a random number generator. It serves only to +** provide random numbers. +*/ +class RandomNumberGenerator { + public: + virtual ~RandomNumberGenerator() {} + + virtual void Get_Block(void * output, unsigned int size) = 0; +}; + + +#endif + diff --git a/code/redalert/rotbmp.cpp b/code/redalert/rotbmp.cpp new file mode 100644 index 0000000..10ac8ce --- /dev/null +++ b/code/redalert/rotbmp.cpp @@ -0,0 +1,414 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/ROTBMP.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ROTBMP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/05/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0)//PG +//#include "function.h" +#include "watcom.h" +#include "rotbmp.h" +#define FILE_H +#define WWMEM_H +#include + + +int Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int angle); + +struct WPPOINT { + int x; + int y; +}; + +/*************************************************************************** + * Rotate_bitmap -- rotate a bitmap from srcvp to destvp * + * * + * INPUT: note that angles are 0 - 255 (best if used in increments of 4!) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * destvp should be a square. width and height should be equal to : * + * MAX(srcvp->width,srcvp->height) * 1.7 (square root of 2!!) * + * * + * * + * HISTORY: * + * 01/02/1996 BP : Created. * + *=========================================================================*/ +Rotate_Bitmap(GraphicViewPortClass * srcvp, GraphicViewPortClass * destvp, int a) +{ + int shift = 7; + int fixpoint1 = 1 << shift; // this is a fixed point 1 + int Deltax; + int Deltay; + int sx,sy,dx,dy; + int Error = 0; + int Decimal = 0; + // this is used if I walk in Y + int buffwidth = srcvp->Get_Width() + srcvp->Get_XAdd(); + int buffwid2 = destvp->Get_Width() + destvp->Get_XAdd(); + char * dest; + char * src; + int sa,ca; + int t; + int x,y; + int rx,ry; + int w,h; + int dw = destvp->Get_Width(); + int dh = destvp->Get_Height(); + WPPOINT sp[4]; + WPPOINT dp[4]; + sa = Sin256[a]; + ca = Cos256[a]; + // get rectangle size + x = 0; + y = 0; + w = srcvp->Get_Width(); + h = srcvp->Get_Height(); + + int halfws = w >> 1; + int halfhs = h >> 1; + int halfwd = dw >> 1; + int halfhd = dh >> 1; + + + // make the src rectangle + sp[0].x = x; + sp[0].y = y; + + sp[1].x = x + w; + sp[1].y = y; + + // now calculate the rotated rectangle + dp[0].x = ( ((sp[0].x - halfws) * ca) - ((sp[0].y - halfhs) * sa) ) >> shift; + dp[0].x += halfwd; + + dp[0].y = ( ((sp[0].x - halfws) * sa) + ((sp[0].y - halfhs) * ca) ) >> shift; + dp[0].y += halfhd; + + dp[1].x = ( ((sp[1].x - halfws) * ca) - ((sp[1].y - halfhs) * sa) ) >> shift; + dp[1].x += halfwd; + + dp[1].y = ( ((sp[1].x - halfws) * sa) + ((sp[1].y - halfhs) * ca) ) >> shift; + dp[1].y += halfhd; + + rx = dp[0].x; + ry = dp[0].y; + // now calculate slope + + // diff from new to old x + + Deltax = (dp[1].x - dp[0].x); + + // diff from new to old y + + Deltay = (dp[1].y - dp[0].y); + + // handle the easy cases + + // no change in x + int r; + if (!Deltax) { // must be 90 or 270 degree transpose! + if (Deltay < 0) { + // walk across source in the x + dir + // walk across dest in the y - dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + dx, ry - dy); + for (r = 0; r< w; r++) { + // transparency + if (* src) + *dest = *src; + src++; + dest -= buffwid2; + } + y++; + dx++; + } + } else { + // walk across source in the x + dir + // walk across dest in the y + dir + + x = 0; + dy = 0; + dx = 0; + for (t = 0; t< h; t++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - dx, ry + dy); + for (r = 0; r< w; r++) { + // transparency + if (*src) + *dest = *src; + src++; + dest += buffwid2; + } + y++; + dx++; + } + } + return 0; + } + // no change in y + + if (!Deltay) { // must be 0 or 180 degree transpose ! + if (Deltax < 0) { + y = 0; + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx - x , ry - y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest--; + src++; + } + } + } else { + for (y = 0; y< h; y++) { + x = 0; + src = MAKE_PTR(srcvp, x, y); + dest = MAKE_PTR(destvp, rx + x, ry + y); + for (x = 0 ; x< w; x++) { + // transparency + if (*src) + *dest = *src; + dest++; + src++; + } + } + } + return 0; + } + + + // ok now the hard part + + // make them 16.16 + if ( ABS(Deltax) < ABS(Deltay)) { // ok this means we want to walk in y + + // walk in + x in the src and + // walk in + y in the dest + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + int yinc1 = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + } + Decimal = ( Deltax << shift) / Deltay ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratio between the source height and the dest height + // as the rectangle rotates the height and width change + int DeltaH = (w << shift) / Deltay; + int Error2 = 0; + sy = 0; + for (int r = 0; r< h ;r++) { + // now we walk across the top calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start in dest + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + + // this is the end point of the line + + int y2 = ( ( ((w) - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + y2 += halfhd; + + // length of line + + int diff = ABS(y2 - ry); + + // if walking backwards reveres diff to reflect sign + + src = MAKE_PTR(srcvp, x, y + sy); + + dest = MAKE_PTR(destvp, rx, ry); + + Error = 0; + Error2 = 0; + char * baseptr = src; + // while walking line + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaH; + dest += buffwid2; + Error += Decimal; + src = baseptr + (Error2 >> shift) ; + // this is time to inc x in src y in dest + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += xinc; + } + } + sy += yinc; + } + return 0; + } else { // else we walk in X + int lasterror = 0; + Error = 0; + // start at left top corner in src and dest + + sx = 0; + sy = 0; + + dx = 0; + dy = 0; + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + // this gets added to error each inc of x + // when error is > 1 then inc y! + int xinc = 1; + if (Deltax < 0) { + Deltax = -Deltax; + xinc = -1; + } + int yinc = 1; + if (Deltay < 0) { + Deltay = - Deltay; + buffwid2 = - buffwid2; + sy = sy + h - 1; + yinc = -1; + } + + + Decimal = ( Deltay << shift) / Deltax ; + // walk in X + + int Deltax2 = Deltax << shift; + int Deltay2 = Deltay << shift; + + // this is the ratios between the source width and the dest width + // as the rectangle rotates the actual size changes! + + int DeltaW = (w << shift) / Deltax; + int Error2 = 0; + for (int r = 0; r< h ;r++) { + sx = 0; + + // now we walk across the side calculating each rotated point + // along the side + // the use delta formula to walk in x and y in destination space + // always walking in the x in the source! + // figure out rotated location to start + + rx = ( ( (sx - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + rx += halfwd; + ry = ( ( (sx - halfws) * sa) + ( (sy - halfhs) * ca) ) >> shift; + ry += halfhd; + // this is the other side of the box + + int x2 = ( ( ((sx + w) - halfws) * ca) - ( (sy - halfhs) * sa) ) >> shift; + + x2 += halfwd; + + int diff = x2 - rx; + + + dx = 0; + dy = 0; + + if (xinc == -1) { + diff = -diff; + } + + src = MAKE_PTR(srcvp, x + sx, y + sy); + + dest = MAKE_PTR(destvp, rx + dx, ry + dy); + Error = 0; + Error2 = 0; + char * baseptr = src; + + while (diff--) { + char c = *src; + // transparency + if (c) + *dest = *src; + Error2 += DeltaW; + rx++; + dest += xinc; + Error += Decimal; + src = baseptr + (Error2 >> shift); + if (Error >= fixpoint1) { + Error -= fixpoint1; + if (*src) + *dest = *src; + dest += buffwid2; + } + } + sy += yinc; + } + } + return 0; +} +#endif \ No newline at end of file diff --git a/code/redalert/rotbmp.h b/code/redalert/rotbmp.h new file mode 100644 index 0000000..88158d4 --- /dev/null +++ b/code/redalert/rotbmp.h @@ -0,0 +1,112 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// list of 256 sines + +#define MAKE_PTR(vp,x,y) (char *) (vp->Get_Offset() + ((y) * (vp->Get_Width() + vp->Get_XAdd())) + (x)) +#define MAKE_PTR2(vp,x,y) (char *) (vp->Get_Offset() + (((y)<<1) * (vp->Get_Width() + vp->Get_XAdd())) + ((x)<<1)) + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +long Cos256[] = { + 128, 127, 127, 127, 127, 127, 126, 126, + 125, 124, 124, 123, 122, 121, 120, 119, + 118, 116, 115, 114, 112, 111, 109, 107, + 106, 104, 102, 100, 98, 96, 94, 92, + 90, 87, 85, 83, 80, 78, 75, 73, + 70, 68, 65, 62, 59, 57, 54, 51, + 48, 45, 42, 39, 36, 33, 30, 27, + 24, 21, 18, 14, 11, 8, 5, 2, + 0, -3, -7, -10, -13, -16, -19, -22, + -25, -28, -31, -35, -38, -41, -44, -46, + -49, -52, -55, -58, -61, -63, -66, -69, + -72, -74, -77, -79, -82, -84, -86, -89, + -91, -93, -95, -97, -99, -101, -103, -105, + -107, -108, -110, -112, -113, -114, -116, -117, + -118, -119, -120, -121, -122, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -116, -114, -113, -112, -110, -108, -107, + -105, -103, -101, -99, -97, -95, -93, -91, + -89, -86, -84, -82, -79, -77, -74, -72, + -69, -66, -64, -61, -58, -55, -52, -49, + -46, -44, -41, -38, -35, -31, -28, -25, + -22, -19, -16, -13, -10, -7, -3, 0, + 2, 5, 8, 11, 14, 18, 21, 24, + 27, 30, 33, 36, 39, 42, 45, 48, + 51, 54, 57, 59, 62, 65, 68, 70, + 73, 75, 78, 80, 83, 85, 87, 90, + 92, 94, 96, 98, 100, 102, 104, 106, + 107, 109, 111, 112, 114, 115, 116, 118, + 119, 120, 121, 122, 123, 124, 124, 125, + 126, 126, 127, 127, 127, 127, 127, 127, +}; +long Sin256[] = { + 0, 3, 6, 9, 12, 15, 18, 21, + 25, 28, 31, 34, 37, 40, 43, 46, + 49, 52, 54, 57, 60, 63, 66, 68, + 71, 73, 76, 79, 81, 83, 86, 88, + 90, 92, 95, 97, 99, 101, 103, 104, + 106, 108, 110, 111, 113, 114, 115, 117, + 118, 119, 120, 121, 122, 123, 124, 125, + 125, 126, 126, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 126, 126, 125, + 125, 124, 123, 123, 122, 121, 120, 119, + 117, 116, 115, 113, 112, 110, 109, 107, + 105, 104, 102, 100, 98, 96, 94, 91, + 89, 87, 85, 82, 80, 77, 75, 72, + 70, 67, 64, 61, 59, 56, 53, 50, + 47, 44, 41, 38, 35, 32, 29, 26, + 23, 20, 17, 14, 11, 7, 4, 1, + -1, -4, -7, -11, -14, -17, -20, -23, + -26, -29, -32, -35, -38, -41, -44, -47, + -50, -53, -56, -59, -61, -64, -67, -70, + -72, -75, -77, -80, -82, -85, -87, -89, + -91, -94, -96, -98, -100, -102, -104, -105, + -107, -109, -110, -112, -113, -115, -116, -117, + -119, -120, -121, -122, -123, -123, -124, -125, + -125, -126, -126, -127, -127, -127, -127, -127, + -127, -127, -127, -127, -127, -126, -126, -125, + -125, -124, -123, -122, -121, -120, -119, -118, + -117, -115, -114, -113, -111, -110, -108, -106, + -104, -103, -101, -99, -97, -95, -92, -90, + -88, -86, -83, -81, -79, -76, -73, -71, + -68, -66, -63, -60, -57, -54, -52, -49, + -46, -43, -40, -37, -34, -31, -28, -25, + -21, -18, -15, -12, -9, -6, -3, 0, +}; diff --git a/code/redalert/rules.cpp b/code/redalert/rules.cpp new file mode 100644 index 0000000..7c49413 --- /dev/null +++ b/code/redalert/rules.cpp @@ -0,0 +1,1072 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Difficulty_Get -- Fetch the difficulty bias values. * + * RulesClass::AI -- Processes the AI control constants from the database. * + * RulesClass::General -- Process the general main game rules. * + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * RulesClass::Land_Types -- Inits the land type values. * + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * RulesClass::Powerups -- Process the powerup values from the database. * + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * RulesClass::RulesClass -- Default constructor for rules class object. * + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * Techno_Get -- Get rule data common for all techno type objects. * + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * RulesClass::Objects -- Fetch all the object characteristic values. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "vortex.h" + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = fixed(100, 256) * val; + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * RulesClass::RulesClass -- Default constructor for rules class object. * + * * + * This is the default constructor for the rules class object. Although it initializes the * + * rule data with default values, it is expected that they will all be overridden by the * + * rules control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +RulesClass::RulesClass(void) : + TurboBoost("1.5"), + AttackInterval(3), + AttackDelay(5), + PowerEmergencyFraction(fixed::_3_4), + BadgerBombCount(1), + AirstripRatio(".12"), + AirstripLimit(5), + HelipadRatio(".12"), + HelipadLimit(5), + TeslaRatio(".16"), + TeslaLimit(10), + AARatio(".14"), + AALimit(10), + DefenseRatio(".5"), + DefenseLimit(40), + WarRatio(".1"), + WarLimit(2), + BarracksRatio(".16"), + BarracksLimit(2), + RefineryLimit(4), + RefineryRatio(".16"), + BaseSizeAdd(3), + PowerSurplus(50), + InfantryReserve(2000), + InfantryBaseMult(2), + ChronoDuration(3), + WaterCrateChance(".2"), + SoloCrateMoney(2000), + GPSTechLevel(0), + UnitCrateType(UNIT_NONE), + PatrolTime(".016"), + TeamDelay(".6"), + CloakDelay(0), + GameSpeedBias(1), + NervousBias(1), + VortexRange(10*CELL_LEPTON_W), + VortexSpeed((MPHType)10), + VortexDamage(200), + VortexChance(".2"), + ExplosionSpread(fixed::_1_2), + SupressRadius(CELL_LEPTON_W), + ParaInfantryTechLevel(10), + SpyPlaneTechLevel(10), + ParaBombTechLevel(10), + MaxIQ(5), + IQSuperWeapons(4), + IQProduction(5), + IQGuardArea(4), + IQRepairSell(3), + IQCrush(2), + IQScatter(3), + IQContentScan(4), + IQAircraft(4), + IQHarvester(3), + IQSellBack(2), + SilverCrate(CRATE_HEAL_BASE), + WoodCrate(CRATE_MONEY), + WaterCrate(CRATE_MONEY), + CrateMinimum(1), + CrateMaximum(255), + LZScanRadius(16*CELL_LEPTON_W), + MPDefaultMoney(3000), + MPMaxMoney(10000), + IsMPShadowGrow(true), + IsMPBasesOn(true), + IsMPTiberiumGrow(true), + IsMPCrates(true), + IsMPAIPlayers(false), + IsMPCaptureTheFlag(false), + DropZoneRadius(4*CELL_LEPTON_W), + MessageDelay(".6"), + SavourDelay(".03"), + AVMineDamage(1200), + APMineDamage(1000), + MaxPlayers(8), + BaseDefenseDelay(fixed::_1_4), + SuspendPriority(20), + SuspendDelay(2), + SurvivorFraction(fixed::_1_2), + ReloadRate(".05"), + AutocreateTime(5), + BuildupTime(".05"), + OreDumpRate(2), + AtomDamage(1000), + IsComputerParanoid(true), + IsCurleyShuffle(false), + IsFlashLowPower(true), + IsCompEasyBonus(true), + IsFineDifficulty(false), + IsExplosiveHarvester(false), + IsMCVDeploy(false), + IsAllyReveal(true), + IsSeparate(false), + IsTreeTarget(false), + IsMineAware(true), + IsTGrowth(true), + IsTSpread(true), + IsNamed(false), + IsAutoCrush(false), + IsSmartDefense(false), + IsScatter(false), + IsChronoKill(true), + ProneDamageBias(fixed::_1_2), + QuakeDamagePercent(".33"), + QuakeChance(".2"), + GrowthRate(2), + ShroudRate(4), + CrateTime(10), + TimerWarning(2), + ChronoTechLevel(1), + SonarTime(14), + ChronoTime(3), + ParaBombTime(14), + ParaInfantryTime(2), + ParaSaboteurTime(14), + SpyTime(2), + IronCurtainTime(14), + GPSTime(1), + NukeTime(14), + SpeakDelay(2), + DamageDelay(1), + Gravity(3), + GapShroudRadius(10), + GapRegenInterval(".1"), + RadarJamRadius(10*CELL_LEPTON_W), + Incoming(MPH_IMMOBILE), + MinDamage(1), + MaxDamage(1000), + RepairStep(5), + RepairPercent(fixed::_1_4), + URepairStep(5), + URepairPercent(fixed::_1_4), + RepairRate(".016"), + ConditionGreen(1), + ConditionYellow(fixed::_1_2), + ConditionRed(fixed::_1_4), + RandomAnimateTime(".083"), + BailCount(28), + GoldValue(35), + GemValue(110), + AircraftMax(100), + AnimMax(100), + BuildingMax(500), + BulletMax(40), + FactoryMax(20), + InfantryMax(500), + OverlayMax(1), + SmudgeMax(1), + TeamMax(60), + TeamTypeMax(60), + TemplateMax(1), + TerrainMax(500), + TriggerMax(60), + UnitMax(500), + VesselMax(100), + ProjectileMax(20), + WeaponMax(20), + WarheadMax(20), + TrigTypeMax(80), + CloseEnoughDistance(0x0280), + StrayDistance(0x0200), + CrushDistance(0x0180), + CrateRadius(0x0280), + HomingScatter(0x0200), + BallisticScatter(0x0100), + RefundPercent(fixed::_1_2), + IronCurtainDuration(fixed::_1_2), + BridgeStrength(1000), + BuildSpeedBias(1), + C4Delay(".03"), + RepairThreshhold(1000), + PathDelay(".016"), + MovieTime(fixed::_1_4), + TiberiumShortScan(0x0600), + TiberiumLongScan(0x2000), + HealthBarDisplayMode(HB_SELECTED), + ResourceBarDisplayMode(RB_SELECTED) +{ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + NewUnitsEnabled = SecretUnitsEnabled = 0; + MTankDistance = 30; + QuakeUnitDamage = 0x080; + QuakeBuildingDamage = 0x040; + QuakeInfantryDamage = 0; + QuakeDelay = 120; + ChronoTankDuration = 0x300; +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + EngineerDamage=(fixed)1 / (fixed)3; // Amount of damage an engineer does + EngineerCaptureLevel=ConditionRed; // Building damage level before engineer can capture +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = 60; +#endif +#endif +} + + +/*********************************************************************************************** + * Difficulty_Get -- Fetch the difficulty bias values. * + * * + * This will fetch the difficulty bias values for the section specified. * + * * + * INPUT: ini -- Reference the INI database to fetch the values from. * + * * + * diff -- Reference to the difficulty class object to fill in with the values. * + * * + * section -- The section identifier to lift the values from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 JLB : Created. * + *=============================================================================================*/ +static void Difficulty_Get(CCINIClass & ini, DifficultyClass & diff, char const * section) +{ + if (ini.Is_Present(section)) { + diff.FirepowerBias = ini.Get_Fixed(section, "FirePower", 1); + diff.GroundspeedBias = ini.Get_Fixed(section, "Groundspeed", 1); + diff.AirspeedBias = ini.Get_Fixed(section, "Airspeed", 1); + diff.ArmorBias = ini.Get_Fixed(section, "Armor", 1); + diff.ROFBias = ini.Get_Fixed(section, "ROF", 1); + diff.CostBias = ini.Get_Fixed(section, "Cost", 1); + diff.RepairDelay = ini.Get_Fixed(section, "RepairDelay", ".02"); + diff.BuildDelay = ini.Get_Fixed(section, "BuildDelay", ".03"); + diff.IsBuildSlowdown = ini.Get_Bool(section, "BuildSlowdown", false); + diff.BuildSpeedBias = ini.Get_Fixed(section, "BuildTime", 1); + diff.IsWallDestroyer = ini.Get_Bool(section, "DestroyWalls", true); + diff.IsContentScan = ini.Get_Bool(section, "ContentScan", false); + } +} + + +/*********************************************************************************************** + * RulesClass::Process -- Fetch the bulk of the rule data from the control file. * + * * + * This routine will fetch the rule data from the control file. * + * * + * INPUT: file -- Reference to the rule file to process. * + * * + * OUTPUT: bool; Was the rule file processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Process(CCINIClass & ini) +{ + BStart(BENCH_RULES); + + General(ini); + MPlayer(ini); + Recharge(ini); + Heap_Maximums(ini); + AI(ini); + Powerups(ini); + Land_Types(ini); + Themes(ini); + IQ(ini); + Objects(ini); + Difficulty(ini); + + BEnd(BENCH_RULES); + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::General -- Process the general main game rules. * + * * + * This fetches the control constants uses for regular game processing. Any game behavior * + * controlling values that don't properly fit in any of the other catagories will be * + * stored here. * + * * + * INPUT: ini -- Reference to the database to fetch the values from. * + * * + * OUTPUT: bool; Was the general section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::General(CCINIClass & ini) +{ + static char const * const GENERAL = "General"; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + static char const * const AFTERMATH = "Aftermath"; + + if(ini.Is_Present(AFTERMATH)) { +//debugprint( "NewUnitsEnabled previously %i\n", NewUnitsEnabled ); + NewUnitsEnabled = ini.Get_Int(AFTERMATH, "NewUnitsEnabled", 0); +//debugprint( "NewUnitsEnabled set to %i by Rules\n", NewUnitsEnabled ); + MTankDistance = ini.Get_Int(AFTERMATH,"MTankDistance",MTankDistance); + QuakeUnitDamage = ini.Get_Fixed(AFTERMATH, "QuakeUnitDamage", QuakeUnitDamage); + QuakeBuildingDamage = ini.Get_Fixed(AFTERMATH, "QuakeBuildingDamage", QuakeBuildingDamage); + QuakeInfantryDamage = ini.Get_Int(AFTERMATH,"QuakeInfantryDamage",QuakeInfantryDamage); + QuakeDelay = ini.Get_Int(AFTERMATH,"QuakeDelay",QuakeDelay); + ChronoTankDuration = ini.Get_Fixed(AFTERMATH, "ChronoTankDuration", ChronoTankDuration); +//Mono_Set_Cursor(0,0);Mono_Printf("Chrono duration: %08x \n",ChronoTankDuration);Keyboard->Get();Keyboard->Get(); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + CarrierLaunchDelay = ini.Get_Int(AFTERMATH,"CarrierLaunchDelay",120); +#endif +#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 + // Engineer changing fields were specifically left out of Aftrmath.ini, thus these values are not found to set. ajw + // Implies interesting security hole if user creates a separate Aftrmath.ini file! + EngineerDamage = ini.Get_Fixed(AFTERMATH, "EngineerDamage", EngineerDamage); // Amount of damage an engineer does + EngineerCaptureLevel = ini.Get_Fixed(AFTERMATH, "EngineerCaptureLevel", EngineerCaptureLevel); // Building damage level before engineer can capture +#endif + } + +#endif + + if (ini.Is_Present(GENERAL)) { + TurboBoost = ini.Get_Fixed(GENERAL, "TurboBoost", TurboBoost); + BadgerBombCount = ini.Get_Int(GENERAL, "BadgerBombCount", BadgerBombCount); + IsCurleyShuffle = ini.Get_Bool(GENERAL, "CurleyShuffle", IsCurleyShuffle); + IsFlashLowPower = ini.Get_Bool(GENERAL, "FlashLowPower", IsFlashLowPower); + IsChronoKill = ini.Get_Bool(GENERAL, "ChronoKillCargo", IsChronoKill); + ChronoDuration = ini.Get_Fixed(GENERAL, "ChronoDuration", ChronoDuration); + IsFineDifficulty = ini.Get_Bool(GENERAL, "FineDiffControl", IsFineDifficulty); + WaterCrateChance = ini.Get_Fixed(GENERAL, "WaterCrateChance", WaterCrateChance); + SoloCrateMoney = ini.Get_Int(GENERAL, "SoloCrateMoney", SoloCrateMoney); + ParaBombTechLevel = ini.Get_Int(GENERAL, "ParabombTech", ParaBombTechLevel); + GPSTechLevel = ini.Get_Int(GENERAL, "GPSTechLevel", GPSTechLevel); + UnitCrateType = ini.Get_UnitType(GENERAL, "UnitCrateType", UnitCrateType); + IsExplosiveHarvester = ini.Get_Fixed(GENERAL, "OreExplosive", IsExplosiveHarvester); + GapRegenInterval = ini.Get_Fixed(GENERAL, "GapRegenInterval", GapRegenInterval); + TeamDelay = ini.Get_Fixed(GENERAL, "TeamDelay", TeamDelay); + CloakDelay = ini.Get_Fixed(GENERAL, "SubmergeDelay", CloakDelay); + GameSpeedBias = ini.Get_Fixed(GENERAL, "GameSpeedBias", GameSpeedBias); + NervousBias = ini.Get_Fixed(GENERAL, "BaseBias", NervousBias); + ExplosionSpread = ini.Get_Fixed(GENERAL, "ExpSpread", ExplosionSpread); + SupressRadius = ini.Get_Lepton(GENERAL, "FireSupress", SupressRadius); + ParaInfantryTechLevel = ini.Get_Int(GENERAL, "ParaTech", ParaInfantryTechLevel); + SpyPlaneTechLevel = ini.Get_Int(GENERAL, "SpyPlaneTech", SpyPlaneTechLevel); + SilverCrate = ini.Get_CrateType(GENERAL, "SilverCrate", SilverCrate); + WoodCrate = ini.Get_CrateType(GENERAL, "WoodCrate", WoodCrate); + WaterCrate = ini.Get_CrateType(GENERAL, "WaterCrate", WaterCrate); + CrateMinimum = ini.Get_Int(GENERAL, "CrateMinimum", CrateMinimum); + CrateMaximum = ini.Get_Int(GENERAL, "CrateMaximum", CrateMaximum); + IsScatter = ini.Get_Bool(GENERAL, "PlayerScatter", IsScatter); + IsSmartDefense = ini.Get_Bool(GENERAL, "PlayerReturnFire", IsSmartDefense); + IsAutoCrush = ini.Get_Bool(GENERAL, "PlayerAutoCrush", IsAutoCrush); + IsNamed = ini.Get_Bool(GENERAL, "NamedCivilians", IsNamed); + IsTGrowth = ini.Get_Bool(GENERAL, "OreGrows", IsTGrowth); + IsTSpread = ini.Get_Bool(GENERAL, "OreSpreads", IsTSpread); + IsMineAware = ini.Get_Bool(GENERAL, "MineAware", IsMineAware); + IsTreeTarget = ini.Get_Bool(GENERAL, "TreeTargeting", IsTreeTarget); + IsSeparate = ini.Get_Bool(GENERAL, "SeparateAircraft", IsSeparate); + DropZoneRadius = ini.Get_Lepton(GENERAL, "DropZoneRadius", DropZoneRadius); + MessageDelay = ini.Get_Fixed(GENERAL, "MessageDelay", MessageDelay); + SavourDelay = ini.Get_Fixed(GENERAL, "SavourDelay", SavourDelay); + AVMineDamage = ini.Get_Int(GENERAL, "AVMineDamage", AVMineDamage); + APMineDamage = ini.Get_Int(GENERAL, "APMineDamage", APMineDamage); + BaseDefenseDelay = ini.Get_Fixed(GENERAL, "BaseDefenseDelay", BaseDefenseDelay); + SuspendPriority = ini.Get_Int(GENERAL, "SuspendPriority", SuspendPriority); + SuspendDelay = ini.Get_Fixed(GENERAL, "SuspendDelay", SuspendDelay); + SurvivorFraction = ini.Get_Fixed(GENERAL, "SurvivorRate", SurvivorFraction); + RadarJamRadius = ini.Get_Lepton(GENERAL, "RadarJamRadius", RadarJamRadius); + ReloadRate = ini.Get_Fixed(GENERAL, "ReloadRate", ReloadRate); + RandomAnimateTime = ini.Get_Fixed(GENERAL, "IdleActionFrequency", RandomAnimateTime); + BuildupTime = ini.Get_Fixed(GENERAL, "BuildupTime", BuildupTime); + OreDumpRate = ini.Get_Int(GENERAL, "OreTruckRate", OreDumpRate); + AtomDamage = ini.Get_Int(GENERAL, "AtomDamage", AtomDamage); + BailCount = ini.Get_Int(GENERAL, "BailCount", BailCount); + BallisticScatter = ini.Get_Lepton(GENERAL, "BallisticScatter", BallisticScatter); + BridgeStrength = ini.Get_Int(GENERAL, "BridgeStrength", BridgeStrength); + BuildSpeedBias = ini.Get_Fixed(GENERAL, "BuildSpeed", BuildSpeedBias); + ConditionGreen = 1; + ConditionRed = ini.Get_Fixed(GENERAL, "ConditionRed", ConditionRed); + ConditionYellow = ini.Get_Fixed(GENERAL, "ConditionYellow", ConditionYellow); + CrateRadius = ini.Get_Lepton(GENERAL, "CrateRadius", CrateRadius); + CrushDistance = ini.Get_Lepton(GENERAL, "Crush", CrushDistance); + DamageDelay = ini.Get_Fixed(GENERAL, "DamageDelay", DamageDelay); + GapShroudRadius = ini.Get_Int(GENERAL, "GapRadius", GapShroudRadius); + GemValue = ini.Get_Int(GENERAL, "GemValue", GemValue); + GoldValue = ini.Get_Int(GENERAL, "GoldValue", GoldValue); + Gravity = ini.Get_Int(GENERAL, "Gravity", Gravity); + GrowthRate = ini.Get_Fixed(GENERAL, "GrowthRate", GrowthRate); + HomingScatter = ini.Get_Lepton(GENERAL, "HomingScatter", HomingScatter); + Incoming = ini.Get_MPHType(GENERAL, "Incoming", MPH_IMMOBILE); + IronCurtainDuration = ini.Get_Fixed(GENERAL, "IronCurtain", IronCurtainDuration); + IsAllyReveal = ini.Get_Bool(GENERAL, "AllyReveal", IsAllyReveal); + IsMCVDeploy = ini.Get_Bool(GENERAL, "MCVUndeploy", IsMCVDeploy); + MaxDamage = ini.Get_Int(GENERAL, "MaxDamage", MaxDamage); + MinDamage = ini.Get_Int(GENERAL, "MinDamage", MinDamage); + ProneDamageBias = ini.Get_Fixed(GENERAL, "ProneDamage", ProneDamageBias); + QuakeDamagePercent = ini.Get_Fixed(GENERAL, "QuakeDamage", QuakeDamagePercent); + QuakeChance = ini.Get_Fixed(GENERAL, "QuakeChance", QuakeChance); + RefundPercent = ini.Get_Fixed(GENERAL, "RefundPercent", RefundPercent); + RepairPercent = ini.Get_Fixed(GENERAL, "RepairPercent", RepairPercent); + RepairStep = ini.Get_Int(GENERAL, "RepairStep", RepairStep); + URepairPercent = ini.Get_Fixed(GENERAL, "URepairPercent", URepairPercent); + URepairStep = ini.Get_Int(GENERAL, "URepairStep", URepairStep); + RepairRate = ini.Get_Fixed(GENERAL, "RepairRate", RepairRate); + ShroudRate = ini.Get_Fixed(GENERAL, "ShroudRate", ShroudRate); + SpeakDelay = ini.Get_Fixed(GENERAL, "SpeakDelay", SpeakDelay); + StrayDistance = ini.Get_Lepton(GENERAL, "Stray", StrayDistance); + CloseEnoughDistance = ini.Get_Lepton(GENERAL, "CloseEnough", CloseEnoughDistance); + TimerWarning = ini.Get_Fixed(GENERAL, "TimerWarning", TimerWarning); + MovieTime = ini.Get_Fixed(GENERAL, "MovieTime", MovieTime); + C4Delay = ini.Get_Fixed(GENERAL, "C4Delay", C4Delay); + ChronoTechLevel = ini.Get_Int(GENERAL, "ChronoTechLevel", ChronoTechLevel); + CrateTime = ini.Get_Fixed(GENERAL, "CrateRegen", CrateTime); + VortexRange = ini.Get_Lepton(GENERAL, "VortexRange", VortexRange); + VortexSpeed = MPHType(_Scale_To_256(ini.Get_Int(GENERAL, "VortexSpeed", VortexSpeed))); + VortexDamage = ini.Get_Int(GENERAL, "VortexDamage", VortexDamage); + VortexChance = ini.Get_Fixed(GENERAL, "VortexChance", VortexChance); + + ChronalVortex.Set_Range(VortexRange / CELL_LEPTON_W); + ChronalVortex.Set_Speed(VortexSpeed); + ChronalVortex.Set_Damage(VortexDamage); + + //ChronalVortex.Set_Range ( ini.Get_Int (GENERAL, "VortexRange", ChronalVortex.Get_Range() ) ); + //ChronalVortex.Set_Speed ( ini.Get_Int (GENERAL, "VortexSpeed", ChronalVortex.Get_Speed() ) ); + //ChronalVortex.Set_Damage ( ini.Get_Int (GENERAL, "VortexDamage", ChronalVortex.Get_Damage() ) ); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::MPlayer -- Fetch and process the multiplayer default settings. * + * * + * This is used to set the default settings for the multiplayer system. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the multiplayer default override section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::MPlayer(CCINIClass & ini) +{ + static char const * const MPLAYER = "MultiplayerDefaults"; + if (ini.Is_Present(MPLAYER)) { + MPDefaultMoney = ini.Get_Int(MPLAYER, "Money", MPDefaultMoney); + MPMaxMoney = ini.Get_Int(MPLAYER, "MaxMoney", MPMaxMoney); + IsMPShadowGrow = ini.Get_Bool(MPLAYER, "ShadowGrow", IsMPShadowGrow); + IsMPBasesOn = ini.Get_Bool(MPLAYER, "Bases", IsMPBasesOn); + IsMPTiberiumGrow = ini.Get_Bool(MPLAYER, "OreGrows", IsMPTiberiumGrow); + IsMPCrates = ini.Get_Bool(MPLAYER, "Crates", IsMPCrates); + IsMPCaptureTheFlag = ini.Get_Bool(MPLAYER, "CaptureTheFlag", IsMPCaptureTheFlag); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Recharge -- Process the super weapon recharge statistics. * + * * + * Use this to set the recharge times for the various super weapons available. * + * * + * INPUT: ini -- Reference to the database. * + * * + * OUTPUT: bool; Was the recharge section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Recharge(CCINIClass & ini) +{ + static char const * const RECHARGE = "Recharge"; + if (ini.Is_Present(RECHARGE)) { + SonarTime = ini.Get_Fixed(RECHARGE, "Sonar", SonarTime); + ChronoTime = ini.Get_Fixed(RECHARGE, "Chrono", ChronoTime); + ParaBombTime = ini.Get_Fixed(RECHARGE, "ParaBomb", ParaBombTime); + ParaInfantryTime = ini.Get_Fixed(RECHARGE, "Paratrooper", ParaInfantryTime); + SpyTime = ini.Get_Fixed(RECHARGE, "SpyPlane", SpyTime); + IronCurtainTime = ini.Get_Fixed(RECHARGE, "IronCurtain", IronCurtainTime); + GPSTime = ini.Get_Fixed(RECHARGE, "GPS", GPSTime); + NukeTime = ini.Get_Fixed(RECHARGE, "Nuke", NukeTime); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Heap_Maximums -- Fetch and process the heap override values. * + * * + * This fetches the maximum heap sizes from the database specified. The heaps will be * + * initialized by this routine as indicated. * + * * + * INPUT: ini -- Reference to the INI database. * + * * + * OUTPUT: bool; Was the maximum section found and processed? * + * * + * WARNINGS: This process is catastrophic to any data currently existing in the heaps * + * modified. This should only be processed during the game initialization stage. * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Heap_Maximums(CCINIClass & ini) +{ + /* + ** Heap maximum values. + */ + static char const * const MAXIMUMS = "Maximums"; + if (ini.Is_Present(MAXIMUMS)) { + MaxPlayers = ini.Get_Int(MAXIMUMS, "Players", MaxPlayers); + AircraftMax = ini.Get_Int(MAXIMUMS, "Aircraft", AircraftMax); + AnimMax = ini.Get_Int(MAXIMUMS, "Anim", AnimMax); + BuildingMax = ini.Get_Int(MAXIMUMS, "Building", BuildingMax); + BulletMax = ini.Get_Int(MAXIMUMS, "Bullet", BulletMax); + FactoryMax = ini.Get_Int(MAXIMUMS, "Factory", FactoryMax); + InfantryMax = ini.Get_Int(MAXIMUMS, "Infantry", InfantryMax); + OverlayMax = ini.Get_Int(MAXIMUMS, "Overlay", OverlayMax); + SmudgeMax = ini.Get_Int(MAXIMUMS, "Smudge", SmudgeMax); + TeamMax = ini.Get_Int(MAXIMUMS, "Team", TeamMax); + TeamTypeMax = ini.Get_Int(MAXIMUMS, "TeamType", TeamTypeMax); + TemplateMax = ini.Get_Int(MAXIMUMS, "Template", TemplateMax); + TerrainMax = ini.Get_Int(MAXIMUMS, "Terrain", TerrainMax); + TriggerMax = ini.Get_Int(MAXIMUMS, "Trigger", TriggerMax); + UnitMax = ini.Get_Int(MAXIMUMS, "Unit", UnitMax); + VesselMax = ini.Get_Int(MAXIMUMS, "Vessel", VesselMax); + ProjectileMax = ini.Get_Int(MAXIMUMS, "Projectile", ProjectileMax); + WeaponMax = ini.Get_Int(MAXIMUMS, "Weapon", WeaponMax); + WarheadMax = ini.Get_Int(MAXIMUMS, "Warhead", WarheadMax); + TrigTypeMax = ini.Get_Int(MAXIMUMS, "TrigType", TrigTypeMax); + } + + /* + ** Special case: double maximum animations to accomodate lots of action + */ + AnimMax = max(AnimMax, 200); + + /* + ** Any heaps that use the maximums that were just loaded, must + ** be initialized as necessary. + */ + Warheads.Set_Heap(WarheadMax); + new WarheadTypeClass("SA"); + new WarheadTypeClass("HE"); + new WarheadTypeClass("AP"); + new WarheadTypeClass("Fire"); + new WarheadTypeClass("HollowPoint"); + new WarheadTypeClass("Super"); + new WarheadTypeClass("Organic"); + new WarheadTypeClass("Nuke"); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WarheadTypeClass("Mechanical"); +#endif + + Weapons.Set_Heap(WeaponMax); + new WeaponTypeClass("Colt45"); + new WeaponTypeClass("ZSU-23"); + new WeaponTypeClass("Vulcan"); + new WeaponTypeClass("Maverick"); + new WeaponTypeClass("Camera"); + new WeaponTypeClass("FireballLauncher"); + new WeaponTypeClass("Sniper"); + new WeaponTypeClass("ChainGun"); + new WeaponTypeClass("Pistol"); + new WeaponTypeClass("M1Carbine"); + new WeaponTypeClass("Dragon"); + new WeaponTypeClass("Hellfire"); + new WeaponTypeClass("Grenade"); + new WeaponTypeClass("75mm"); + new WeaponTypeClass("90mm"); + new WeaponTypeClass("105mm"); + new WeaponTypeClass("120mm"); + new WeaponTypeClass("TurretGun"); + new WeaponTypeClass("MammothTusk"); + new WeaponTypeClass("155mm"); + new WeaponTypeClass("M60mg"); + new WeaponTypeClass("Napalm"); + new WeaponTypeClass("TeslaZap"); + new WeaponTypeClass("Nike"); + new WeaponTypeClass("8Inch"); + new WeaponTypeClass("Stinger"); + new WeaponTypeClass("TorpTube"); + new WeaponTypeClass("2Inch"); + new WeaponTypeClass("DepthCharge"); + new WeaponTypeClass("ParaBomb"); + new WeaponTypeClass("DogJaw"); + new WeaponTypeClass("Heal"); + new WeaponTypeClass("SCUD"); + new WeaponTypeClass("Flamer"); + new WeaponTypeClass("RedEye"); + +#ifdef FIXIT_ANTS + new WeaponTypeClass("Mandible"); +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new WeaponTypeClass("PortaTesla"); + new WeaponTypeClass("GoodWrench"); + new WeaponTypeClass("SubSCUD"); + new WeaponTypeClass("TTankZap"); + new WeaponTypeClass("APTusk"); + new WeaponTypeClass("Democharge"); +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new WeaponTypeClass("AirAssault"); +#endif + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::AI -- Processes the AI control constants from the database. * + * * + * This will examine the database specified and set the AI override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database that holds the AI overrides. * + * * + * OUTPUT: bool; Was the AI section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::AI(CCINIClass & ini) +{ + static char const * const AI = "AI"; + if (ini.Is_Present(AI)) { + AttackInterval = ini.Get_Fixed(AI, "AttackInterval", AttackInterval); + AttackDelay = ini.Get_Fixed(AI, "AttackDelay", AttackDelay); + PatrolTime = ini.Get_Fixed(AI, "PatrolScan", PatrolTime); + RepairThreshhold = ini.Get_Int(AI, "CreditReserve", RepairThreshhold); + PathDelay = ini.Get_Fixed(AI, "PathDelay", PathDelay); + TiberiumShortScan = ini.Get_Lepton(AI, "OreNearScan", TiberiumShortScan); + TiberiumLongScan = ini.Get_Lepton(AI, "OreFarScan", TiberiumLongScan); + AutocreateTime = ini.Get_Fixed(AI, "AutocreateTime", AutocreateTime); + InfantryReserve = ini.Get_Int(AI, "InfantryReserve", InfantryReserve); + InfantryBaseMult = ini.Get_Int(AI, "InfantryBaseMult", InfantryBaseMult); + PowerSurplus = ini.Get_Int(AI, "PowerSurplus", PowerSurplus); + BaseSizeAdd = ini.Get_Int(AI, "BaseSizeAdd", BaseSizeAdd); + RefineryRatio = ini.Get_Fixed(AI, "RefineryRatio", RefineryRatio); + RefineryLimit = ini.Get_Int(AI, "RefineryLimit", RefineryLimit); + BarracksRatio = ini.Get_Fixed(AI, "BarracksRatio", BarracksRatio); + BarracksLimit = ini.Get_Int(AI, "BarracksLimit", BarracksLimit); + WarRatio = ini.Get_Fixed(AI, "WarRatio", WarRatio); + WarLimit = ini.Get_Int(AI, "WarLimit", WarLimit); + DefenseRatio = ini.Get_Fixed(AI, "DefenseRatio", DefenseRatio); + DefenseLimit = ini.Get_Int(AI, "DefenseLimit", DefenseLimit); + AARatio = ini.Get_Fixed(AI, "AARatio", AARatio); + AALimit = ini.Get_Int(AI, "AALimit", AALimit); + TeslaRatio = ini.Get_Fixed(AI, "TeslaRatio", TeslaRatio); + TeslaLimit = ini.Get_Int(AI, "TeslaLimit", TeslaLimit); + HelipadRatio = ini.Get_Fixed(AI, "HelipadRatio", HelipadRatio); + HelipadLimit = ini.Get_Int(AI, "HelipadLimit", HelipadLimit); + AirstripRatio = ini.Get_Fixed(AI, "AirstripRatio", AirstripRatio); + AirstripLimit = ini.Get_Int(AI, "AirstripLimit", AirstripLimit); + IsCompEasyBonus = ini.Get_Bool(AI, "CompEasyBonus", IsCompEasyBonus); + IsComputerParanoid = ini.Get_Bool(AI, "Paranoid", IsComputerParanoid); + PowerEmergencyFraction = ini.Get_Fixed(AI, "PowerEmergency", PowerEmergencyFraction); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Powerups -- Process the powerup values from the database. * + * * + * This will examine the database and initialize the powerup override values accordingly. * + * * + * INPUT: ini -- Reference to the INI database the the powerup values are to be * + * initialized from. * + * * + * OUTPUT: bool; Was the powerup section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Powerups(CCINIClass & ini) +{ + static char const * const POWERUPS = "Powerups"; + if (ini.Is_Present(POWERUPS)) { + for (CrateType crate = CRATE_FIRST; crate < CRATE_COUNT; crate++) { + char buffer[128]; + if (ini.Get_String(POWERUPS, CrateNames[crate], "0,NONE", buffer, sizeof(buffer))) { + + /* + ** Share odds. + */ + char * token = strtok(buffer, ","); + if (token) { + strtrim(token); + CrateShares[crate] = atoi(token); + } + + /* + ** Animation to use. + */ + token = strtok(NULL, ","); + if (token) { + strtrim(token); + CrateAnims[crate] = Anim_From_Name(token); + } + + /* + ** Optional data number. + */ + token = strtok(NULL, ","); + if (token != NULL) { + if (strpbrk(token, ".%") != NULL) { + CrateData[crate] = fixed(token) * 256; + } else { + strtrim(token); + CrateData[crate] = atoi(token); + } + } + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Land_Types -- Inits the land type values. * + * * + * This will set the land movement attributes from the database specified. * + * * + * INPUT: ini -- Reference to the database that has the land value overrides. * + * * + * OUTPUT: bool; Was the land type sections found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Land_Types(CCINIClass & ini) +{ + /* + ** Fetch the movement characteristic data for terrain types. + */ + for (LandType land = LAND_FIRST; land < LAND_COUNT; land++) { + static char const * _lands[LAND_COUNT] = { + "Clear", + "Road", + "Water", + "Rock", + "Wall", + "Ore", + "Beach", + "Rough", + "River" + }; + + GroundType * gptr = &Ground[land]; + + if (ini.Is_Present(_lands[land])) { + gptr->Cost[SPEED_FOOT] = ini.Get_Fixed(_lands[land], "Foot", 1); + gptr->Cost[SPEED_TRACK] = ini.Get_Fixed(_lands[land], "Track", 1); + gptr->Cost[SPEED_WHEEL] = ini.Get_Fixed(_lands[land], "Wheel", 1); + gptr->Cost[SPEED_WINGED] = fixed(1); + gptr->Cost[SPEED_FLOAT] = ini.Get_Fixed(_lands[land], "Float", 1); + gptr->Build = ini.Get_Bool(_lands[land], "Buildable", false); + } + } + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Themes -- Fetches the theme control values from the INI database. * + * * + * The musical theme availability is controlled by the scenario and the player's house * + * choice. These controls can be specified in the theme control section of the INI * + * database. * + * * + * INPUT: ini -- Reference to the INI database to process. * + * * + * OUTPUT: bool; Was the theme section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Themes(CCINIClass & ini) +{ + static char const * const THEMECONTROL = "ThemeControl"; + + if (ini.Is_Present(THEMECONTROL)) { + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (ini.Is_Present(THEMECONTROL, Theme.Base_Name(theme))) { + + char buffer[128]; + int scen = 1; + int owners = HOUSEF_ALLIES | HOUSEF_SOVIET | HOUSEF_OTHERS; + + ini.Get_String(THEMECONTROL, Theme.Base_Name(theme), "", buffer, sizeof(buffer)); + char const * token = strtok(buffer, ","); + if (token != NULL) { + scen = atoi(token); + } + + token = strtok(NULL, ","); + if (token != NULL) { + owners = Owner_From_Name(token); + } + + Theme.Set_Theme_Data(theme, scen, owners); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::IQ -- Fetches the IQ control values from the INI database. * + * * + * This will scan the database specified and retrieve the IQ control values from it. These * + * IQ control values are what gives the IQ rating meaning. It fundimentally controls how * + * the computer behaves. * + * * + * INPUT: ini -- Reference to the INI database to read the IQ controls from. * + * * + * OUTPUT: bool; Was the IQ section found and processed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/11/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::IQ(CCINIClass & ini) +{ + static char const * const IQCONTROL = "IQ"; + if (ini.Is_Present(IQCONTROL)) { + MaxIQ = ini.Get_Int(IQCONTROL, "MaxIQLevels", MaxIQ); + IQSuperWeapons = ini.Get_Int(IQCONTROL, "SuperWeapons", IQSuperWeapons); + IQProduction = ini.Get_Int(IQCONTROL, "Production", IQProduction); + IQGuardArea = ini.Get_Int(IQCONTROL, "GuardArea", IQGuardArea); + IQRepairSell = ini.Get_Int(IQCONTROL, "RepairSell", IQRepairSell); + IQCrush = ini.Get_Int(IQCONTROL, "AutoCrush", IQCrush); + IQScatter = ini.Get_Int(IQCONTROL, "Scatter", IQScatter); + IQContentScan = ini.Get_Int(IQCONTROL, "ContentScan", IQContentScan); + IQAircraft = ini.Get_Int(IQCONTROL, "Aircraft", IQAircraft); + IQHarvester = ini.Get_Int(IQCONTROL, "Harvester", IQHarvester); + IQSellBack = ini.Get_Int(IQCONTROL, "SellBack", IQSellBack); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * RulesClass::Objects -- Fetch all the object characteristic values. * + * * + * This will parse the specified INI database and fetch all the object characteristic * + * values specified therein. * + * * + * INPUT: ini -- Reference to the ini database to scan. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Objects(CCINIClass & ini) +{ + /* + ** Fetch the game object values from the rules file. + */ + for (int index = 0; index < Warheads.Count(); index++) { + Warheads.Ptr(index)->Read_INI(ini); + } + + for (int proj = 0; proj < BulletTypes.Count(); proj++) { + BulletTypes.Ptr(proj)->Read_INI(ini); + } + + for (int windex = 0; windex < Weapons.Count(); windex++) { + Weapons.Ptr(windex)->Read_INI(ini); + } + + for (int uindex = 0; uindex < UnitTypes.Count(); uindex++) { + UnitTypes.Ptr(uindex)->Read_INI(ini); + } + + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypes.Ptr(iindex)->Read_INI(ini); + } + + for (int vindex = 0; vindex < VesselTypes.Count(); vindex++) { + VesselTypes.Ptr(vindex)->Read_INI(ini); + } + + for (int aindex = 0; aindex < AircraftTypes.Count(); aindex++) { + AircraftTypes.Ptr(aindex)->Read_INI(ini); + } + + for (int bindex = 0; bindex < BuildingTypes.Count(); bindex++) { + BuildingTypes.Ptr(bindex)->Read_INI(ini); + } + + /* + ** Fetch the house attribute override values. + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTypeClass::As_Reference(house).Read_INI(ini); + } + + /* + ** Fetch the mission control values. + */ + for (MissionType mission = MISSION_FIRST; mission < MISSION_COUNT; mission++) { + MissionControlClass * miss = &MissionControl[mission]; + miss->Mission = mission; + miss->Read_INI(ini); + } + + return(true); +} + + +/*********************************************************************************************** + * RulesClass::Difficulty -- Fetch the various difficulty group settings. * + * * + * This routine is used to fetch the various group settings for the difficulty levels. * + * * + * INPUT: ini -- Reference to the INI database that has the difficulty setting values. * + * * + * OUTPUT: bool; Was the difficulty section found and processed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +bool RulesClass::Difficulty(CCINIClass & ini) +{ + Difficulty_Get(ini, Diff[DIFF_EASY], "Easy"); + Difficulty_Get(ini, Diff[DIFF_NORMAL], "Normal"); + Difficulty_Get(ini, Diff[DIFF_HARD], "Difficult"); + return(true); +} + + +/*********************************************************************************************** + * Is_MCV_Deploy -- Check if MCV can be deployed. * + * * + * This routine is used to check if the Construction Yard can revert back into an MCV. * + * It allows the special variables to override anything set by the rules. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can the Construction Yard revert back into an MCV. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/24/2019 SKY : Created. * + *=============================================================================================*/ +bool Is_MCV_Deploy() +{ + return Special.UseMCVDeploy ? Special.IsMCVDeploy : Rule.IsMCVDeploy; +} \ No newline at end of file diff --git a/code/redalert/rules.h b/code/redalert/rules.h new file mode 100644 index 0000000..b4ce1c0 --- /dev/null +++ b/code/redalert/rules.h @@ -0,0 +1,896 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/RULES.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : RULES.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/12/96 * + * * + * Last Update : May 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef RULES_H +#define RULES_H + +#include "ccini.h" + +class DifficultyClass +{ + public: + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + fixed RepairDelay; + fixed BuildDelay; + + unsigned IsBuildSlowdown:1; + unsigned IsWallDestroyer:1; + unsigned IsContentScan:1; +}; + +class RulesClass { + public: + + RulesClass(void); + + bool Process(CCINIClass & file); + bool General(CCINIClass & ini); + bool MPlayer(CCINIClass & ini); + bool Recharge(CCINIClass & ini); + bool Heap_Maximums(CCINIClass & ini); + bool AI(CCINIClass & ini); + bool Powerups(CCINIClass & ini); + bool Land_Types(CCINIClass & ini); + bool Themes(CCINIClass & ini); + bool IQ(CCINIClass & ini); + bool Objects(CCINIClass & ini); + bool Difficulty(CCINIClass & ini); + + /* + ** This specifies the turbo boost speed for missiles when they are fired upon + ** aircraft and the weapon is specified as having a turbo boost bonus. + */ + fixed TurboBoost; + + /* + ** This specifies the average number of minutes between each computer attack. + */ + fixed AttackInterval; + + /* + ** This specifies the average minutes delay before the computer will begin + ** its first attack upon the player. The duration is also modified by the + ** difficulty level. + */ + fixed AttackDelay; + + /* + ** If the power ratio falls below this percentage, then a power emergency is + ** in effect. At such times, the computer might decide to sell off some + ** power hungry buildings in order to alleviate the situation. + */ + fixed PowerEmergencyFraction; + + /* + ** The number of badgers that arrive when the parabomb option is used. + */ + int BadgerBombCount; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of airstrips. + */ + fixed AirstripRatio; + + /* + ** Limit the number of airstrips to this amount. + */ + int AirstripLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of helipads. + */ + fixed HelipadRatio; + + /* + ** Limit the number of helipads to this amount. + */ + int HelipadLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of Tesla Coils. + */ + fixed TeslaRatio; + + /* + ** Limit tesla coil production to this maximum. + */ + int TeslaLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of anti-aircraft defense. + */ + fixed AARatio; + + /* + ** Limit anti-aircraft building quantity to this amount. + */ + int AALimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of defensive structures. + */ + fixed DefenseRatio; + + /* + ** This is the limit to the number of defensive building that can be built. + */ + int DefenseLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of war factories. + */ + fixed WarRatio; + + /* + ** War factories are limited to this quantity for the computer controlled player. + */ + int WarLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of infantry producing structures. + */ + fixed BarracksRatio; + + /* + ** No more than this many barracks can be built. + */ + int BarracksLimit; + + /* + ** Refinery building is limited to this many refineries. + */ + int RefineryLimit; + + /* + ** This specifies the percentage of the base (by building quantity) that should + ** be composed of refineries. + */ + fixed RefineryRatio; + + /* + ** The computer is limited in the size of the base it can build. It is limited to the + ** size of the largest human opponent base plus this surplus count. + */ + int BaseSizeAdd; + + /* + ** If the power surplus is less than this amount, then the computer will + ** build power plants. + */ + int PowerSurplus; + + /* + ** The computer will build infantry if their cash reserve is greater than this amount. + */ + int InfantryReserve; + + /* + ** This factor is multiplied by the number of buildings in the computer's base and infantry + ** are always built until it matches that number. + */ + int InfantryBaseMult; + + /* + ** This specifies the duration that a unit will remain chronoshifted before it + ** will be returned to its starting location. + */ + fixed ChronoDuration; + + /* + ** Percent chance that a water crate will be generated instead of a land + ** crate when crates are on and in a multiplay game. + */ + fixed WaterCrateChance; + + /* + ** Solo play has money crate amount fixed according to this rule value. + */ + int SoloCrateMoney; + + /* + ** GPS tech level control. + */ + int GPSTechLevel; + + /* + ** If a unit type is specified here, then the unit crate will generate + ** a unit of this type (always). + */ + UnitType UnitCrateType; + + /* + ** This is the time to delay between patrol-to-waypoint target scanning. + */ + fixed PatrolTime; + + /* + ** This is the time interval that checking to create teams will span. The + ** smaller this number, the more often checking for team creation will occur. + */ + fixed TeamDelay; + + /* + ** This is the arbitrary delay to make all cloaking objects remain uncloaked + ** before having it recloak. + */ + fixed CloakDelay; + + /* + ** This is an overall game apparent speed bias to use for object + ** movement purposes. + */ + fixed GameSpeedBias; + + /* + ** If a potential target is close to the base then increase + ** the likelyhood of attacking it by this bias factor. + */ + fixed NervousBias; + + /* + ** Controls the Chronal vortex characteristics. + */ + LEPTON VortexRange; + MPHType VortexSpeed; + int VortexDamage; + fixed VortexChance; + + /* + ** When an explosive object explodes, the damage will spread out + ** by this factor. The value represents the number of cells radius + ** that the damage will spread for every 100 points of raw damage at + ** the explosion center point. + */ + fixed ExplosionSpread; + + /* + ** For weapons specially marked to check for nearby friendly buildings + ** when scanning for good targets, this indicates the scan radius. Such + ** weapons will supress firing on enemies if they are in close proximity + ** to allied buildings. + */ + LEPTON SupressRadius; + + /* + ** This is the tech level that para infantry are granted free to the owner + ** of an airstrip. + */ + int ParaInfantryTechLevel; + + /* + ** This is the tech level that spy planes are granted free to the owner of + ** an airstrip. + */ + int SpyPlaneTechLevel; + + /* + ** This is the tech level that the parabombs are granted free to the owner + ** of an airstrip. + */ + int ParaBombTechLevel; + + /* + ** This is the maximum number of IQ settings available. The human player is + ** presumed to be at IQ level zero. + */ + int MaxIQ; + + /* + ** The IQ level at which super weapons will be automatically fired by the computer. + */ + int IQSuperWeapons; + + /* + ** The IQ level at which production is automatically controlled by the computer. + */ + int IQProduction; + + /* + ** The IQ level at which newly produced units start out in guard area mode instead + ** of normal guard mode. + */ + int IQGuardArea; + + /* + ** The IQ level at which the computer will be able to decide what gets repaired + ** or sold. + */ + int IQRepairSell; + + /* + ** At this IQ level or higher, a unit is allowed to automatically try to crush + ** an atagonist if possible. + */ + int IQCrush; + + /* + ** The unit/infantry will try to scatter if an incoming threat + ** is detected. + */ + int IQScatter; + + /* + ** Tech level at which the computer will scan the contents of a transport + ** in order to pick the best target to fire upon. + */ + int IQContentScan; + + /* + ** Aircraft replacement production occurs at this IQ level or higher. + */ + int IQAircraft; + + /* + ** Checks for and replaces lost harvesters. + */ + int IQHarvester; + + /* + ** Is allowed to sell a structure being damaged. + */ + int IQSellBack; + + /* + ** The silver and wood crates in solo play will have these powerups. + */ + CrateType SilverCrate; + CrateType WoodCrate; + CrateType WaterCrate; + + /* + ** This specifies the minimum number of crates to place on the map in spite + ** of the number of actual human players. + */ + int CrateMinimum; + + /* + ** This specifies the crate maximum quantity to use. + */ + int CrateMaximum; + + /* + ** Landing zone maximum alternate zone scan radius. + */ + LEPTON LZScanRadius; + + /* + ** Multiplayer default settings. + */ + int MPDefaultMoney; + int MPMaxMoney; + unsigned IsMPShadowGrow:1; + unsigned IsMPBasesOn:1; + unsigned IsMPTiberiumGrow:1; + unsigned IsMPCrates:1; + unsigned IsMPAIPlayers:1; + unsigned IsMPCaptureTheFlag:1; + + /* + ** Drop zone reveal radius. + */ + LEPTON DropZoneRadius; + + /* + ** This is the delay that multiplayer messages will remain on the screen. + */ + fixed MessageDelay; + + /* + ** Savour delay between when scenario detects end and the actual + ** end of the play. + */ + fixed SavourDelay; + + /* + ** This specifies the damage to inflict for two differnt styles of + ** land mine. + */ + int AVMineDamage; + int APMineDamage; + + /* + ** This is the maximum number of multiplayers allowed. + */ + int MaxPlayers; + + /* + ** This is the delay between 'panic attacks' when the computer's base is under + ** attack. This delay gives the previously assigned units a chance to affect the + ** attacker before the computer sends more. + */ + fixed BaseDefenseDelay; + + /* + ** These values control the team suspension logic for dealing with immedate base threats. + ** When the base is attacked, all teams with less than the specified priority will be + ** temporarily put on hold for the number of minutes specified. + */ + int SuspendPriority; + fixed SuspendDelay; + + /* + ** This serves as the fraction of a building's original cost that is converted + ** into survivors (of some fashion). There are rounding and other marginal + ** fudge effects, but this value is the greatest control over the survivor rate. + */ + fixed SurvivorFraction; + + /* + ** This is the aircraft reload rate expressed in minutes per ammo load. + */ + fixed ReloadRate; + + /* + ** The average time (in minutes) between the computer autocreating a team + ** from the team's autocreate list. + */ + fixed AutocreateTime; + + /* + ** Build up time for buildings (minutes). + */ + fixed BuildupTime; + + /* + ** Ore truck speed for dumping. + */ + int OreDumpRate; + + /* + ** This is the amount of damage done by the atom bomb in solo missions. The + ** damage done during multiplay will be 1/5th this value. + */ + int AtomDamage; + + /* + ** This array controls the difficulty affects on the game. There is one + ** difficulty class object for each difficulty level. + */ + DifficultyClass Diff[DIFF_COUNT]; + + /* + ** Is the computer paranoid? If so, then it will band together with other computer + ** paranoid players when the situation looks rough. + */ + bool IsComputerParanoid:1; + + /* + ** Should helicopters shuffle their position between firing on their + ** target? + */ + bool IsCurleyShuffle:1; + + /* + ** Flash the power bar when the power goes below 100%. + */ + bool IsFlashLowPower:1; + + /* + ** If the computer players will go to easy mode if there is more + ** than one human player, this flag will be true. + */ + bool IsCompEasyBonus:1; + + /* + ** If fine control of difficulty settings is desired, then set this value to true. + ** Fine control allows 5 settings. The coarse control only allows three settings. + */ + bool IsFineDifficulty:1; + + /* + ** If the harvester is to explode more violently than normal + ** if it is carrying cargo, then this flag will be true. + */ + unsigned IsExplosiveHarvester:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + */ + unsigned IsMCVDeploy:1; + + /* + ** If the base is to be revealed to a new ally, then this + ** flag will be true. + */ + unsigned IsAllyReveal:1; + + /* + ** Can the helipad (and airfield) be purchased separately from the associated + ** aircraft. + */ + unsigned IsSeparate:1; + + /* + ** Give target cursor for trees? Doing this will make targetting of trees easier. + */ + unsigned IsTreeTarget:1; + + /* + ** Are friendly units automatically aware of mines so that they can avoid them? + */ + unsigned IsMineAware:1; + + /* + ** If Tiberium is allowed to grow, then this flag will be true. + */ + unsigned IsTGrowth:1; + + /* + ** If Tiberium is allowed to spread, then this flag will be true. + */ + unsigned IsTSpread:1; + + /* + ** Should civilan buildings and civilians display their true name rather than + ** the generic "Civilian Building" and "Civilain"? + */ + unsigned IsNamed:1; + + /* + ** Should player controlled vehicles automatically try to crush nearby infantry + ** instead of required the player to manually direct them to crush. + */ + unsigned IsAutoCrush:1; + + /* + ** Should the player controlled buildings and units automatically return fire when + ** fired upon? + */ + unsigned IsSmartDefense:1; + + /* + ** Should player controlled units try to scatter more easily in order to + ** avoid damage or threats? + */ + unsigned IsScatter:1; + + /* + ** If the chronoshift effect should kill all cargo, then this flag will + ** be set to true. + */ + unsigned IsChronoKill:1; + + /* + ** When infantry are prone or when civilians are running around like crazy, + ** they are less prone to damage. This specifies the multiplier to the damage + ** (as a fixed point number). + */ + fixed ProneDamageBias; + + /* + ** The time quake will do this percentage of damage to all units and buildings + ** in the game. The number is expressed as a fixed point percentage. + */ + fixed QuakeDamagePercent; + + /* + ** Percentage chance that a time quake will occur with each chronoshift use. + */ + fixed QuakeChance; + + /* + ** Ore (Tiberium) growth rate. The value is the number of minutes between + ** growth steps. + */ + fixed GrowthRate; + + /* + ** This specifies the number of minutes between each shroud regrowth process. + */ + fixed ShroudRate; + + /* + ** This is the average minutes between each generation of a random crate + ** to be placed on the map if generating of random crates is indicated. + */ + fixed CrateTime; + + /* + ** This specifies the number of minutes remaining before that if the mission timer + ** gets to this level or below, it will be displayed in red. + */ + fixed TimerWarning; + + /* + ** This specifies the minutes of delay between recharges for these + ** special weapon types. + */ + fixed SonarTime; + fixed ChronoTime; + fixed ParaBombTime; + fixed ParaInfantryTime; + fixed ParaSaboteurTime; + fixed SpyTime; + fixed IronCurtainTime; + fixed GPSTime; + fixed NukeTime; + + /* + ** Other miscellaneous delay times. + */ + fixed SpeakDelay; + fixed DamageDelay; + + /* + ** This is the gravity constant used to control the arcing and descent of ballistic + ** object such as grenades and artillery. + */ + int Gravity; + + /* + ** Gap generators have a shroud radius of this many cells. + */ + int GapShroudRadius; + + /* + ** This is the minute interval between the gap generators refreshing + ** their zones of gapping. + */ + fixed GapRegenInterval; + + /* + ** Mobile radar jammer radius of effect. + */ + LEPTON RadarJamRadius; + + /* + ** The speed at which a projectile that travels at or slower will cause + ** objects in the target location to scatter. This simulates the ability + ** of targets to run for cover if the projectile gives them enough time + ** to react. + */ + MPHType Incoming; + + /* + ** Minimum and maximum damage allowed per shot. + */ + int MinDamage; + int MaxDamage; + + /* + ** This is the rate of repair for units and buildings. The rate is the + ** number of strength points repaired per repair clock tick. The cost of + ** repair is the (fixed point) fractional cost to repair the object based + ** on the full price of the object. Example; a value of 50% means that to + ** repair the object from 1 damage point to full strength would cost 50% of + ** the cost to build it from scratch. + */ + int RepairStep; + fixed RepairPercent; + int URepairStep; + fixed URepairPercent; + + /* + ** This is the rate that objects with self healing will heal. They will repair a bit + ** every 'this' number of minutes. + */ + fixed RepairRate; + + /* + ** These fixed point values are used to determine the status (health bar + ** color) of the game objects. Objects in the 'yellow' are in a cautionary + ** state. Object in the 'red' are in a danger state. + */ + fixed ConditionGreen; + fixed ConditionYellow; + fixed ConditionRed; + + /* + ** Average number of minutes between infantry random idle animations. + */ + fixed RandomAnimateTime; + + /* + ** These control the capacity and value of the ore types that a harvester + ** may carry. The harvester carries a maximum discrete number of 'bails'. + ** The value of each bail depends on the ore it is composed of. + */ + int BailCount; // was STEP_COUNT + int GoldValue; // was GOLD_WORTH + int GemValue; // was GEM_WORTH + + /* + ** This specifies the heap maximum for the various game objects. + */ + int AircraftMax; + int AnimMax; + int BuildingMax; + int BulletMax; + int FactoryMax; + int InfantryMax; + int OverlayMax; + int SmudgeMax; + int TeamMax; + int TeamTypeMax; + int TemplateMax; + int TerrainMax; + int TriggerMax; + int UnitMax; + int VesselMax; + int ProjectileMax; + int WeaponMax; + int WarheadMax; + int TrigTypeMax; + + /* + ** Close enough distance that is used to determine if the object should + ** stop movement when blocked. If the distance to the desired destination + ** is equal to this distance or less, but the path is blocked, then consider + ** the object to have gotten "close enough" to the destination to stop. + */ + LEPTON CloseEnoughDistance; + + /* + ** Stray distance to group team members within. The larger the distance, + ** the looser the teams will move. + */ + LEPTON StrayDistance; + + /* + ** If a vehicle is closer than this range to a target that it can crush + ** by driving over it, then it will try to drive over it instead of firing + ** upon it. The larger the value, the greater the 'bigfoot crush syndrome' is + ** has. + */ + LEPTON CrushDistance; + + /* + ** For area effect crate bonus items will affect all objects within this radius. + */ + LEPTON CrateRadius; + + /* + ** Maximum scatter distances for homing and non-homing projectiles. + */ + LEPTON HomingScatter; + LEPTON BallisticScatter; + + /* + ** This is the refund percentage when selling off buildings and units + ** on the repair pad (service depot). + */ + fixed RefundPercent; + + /* + ** The Iron Curtain invulnerability effect lasts for this many minutes. + */ + fixed IronCurtainDuration; + + /* + ** The strength of bridges is held here. By corollary, the strength of the + ** demolition charge carried by Tanya is equal to this value as well. + */ + int BridgeStrength; + + /* + ** This is the overall build speed bias. Multiply this value by the normal build + ** delay to get the effective build delay. + */ + fixed BuildSpeedBias; + + /* + ** Weapon type array pointer should go here. Dynamic type. + */ + + + /* + ** Warhead type class array pointer should go here. Dynamic type. + */ + + + /* + ** Ground type and speed affect data should go here. + */ + + + /* + ** This is the delay between the time a C4 bomb is planted and the time it will + ** explode. The longer the delay, the greater safety margin for a demolitioner + ** type. The short the delay, the less time the victim has to sell the building + ** off. + */ + fixed C4Delay; + + /* + ** The computer will only repair a structure if it has spare money greater than this + ** amount. The thinking is that this will prevent the computer from frittering away + ** all it's cash on repairing and thus leaving nothing for production of defenses. + */ + int RepairThreshhold; + + /* + ** This is the delay (in minutes) between retries of a failed path. The longer the + ** delay the faster the system, but the longer the units take to react to a blocked + ** terrain event. + */ + fixed PathDelay; + + /* + ** This is the special (debug version only) movie recorder timeout value. Each second + ** results in about 2-3 megabytes. + */ + fixed MovieTime; + + /* + ** This is the level at or above which the chronosphere facility can + ** actually produce the chronosphere effect. Below this tech level, + ** the facility is merely a showpiece and has no effect. + */ + int ChronoTechLevel; + + /* + ** These are the Tiberium scan distances. The short range scan is used to determine if the + ** current field has been exhausted. The long range scan is used when finding a Tiberium + ** field to harvest. Keep these ranges as small as possible. + */ + LEPTON TiberiumShortScan; + LEPTON TiberiumLongScan; + + /* + ** Health bar display mode + */ + enum eHealthBarDisplayMode + { + HB_DAMAGED = 0, + HB_ALWAYS, + HB_SELECTED + } HealthBarDisplayMode; + + /* + ** Resource bar display mode + */ + enum eResourceBarDisplayMode + { + RB_SELECTED = 0, + RB_ALWAYS, + } ResourceBarDisplayMode; +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/savedlg.h b/code/redalert/savedlg.h new file mode 100644 index 0000000..a8e2991 --- /dev/null +++ b/code/redalert/savedlg.h @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SAVEDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVEDLG.H * + * * + * Programmer : Maria del Mar McCready Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SAVEDLG_H +#define SAVEDLG_H + +#include "gadget.h" + +class SaveOptionsClass +{ + private: + + enum SaveOptionsClassEnums { + BUTTON_CANCEL=200, + BUTTON_SAVE, + OPTION_WIDTH=216, + OPTION_HEIGHT=122, + OPTION_X=((320 - OPTION_WIDTH) / 2) & ~7, + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + NUMBER_OF_BUTTONS=2, + CAPTION_Y_POS=5, + BORDER1_LEN=49, + BUTTON_CANCEL_X=90, + BUTTON_CANCEL_Y=103, + LISTBOX_X=40, + LISTBOX_Y=24, + LISTBOX_W=136, + LISTBOX_H=72 + }; + + public: + SaveOptionsClass (void) { }; + void Process (void); +}; + + +#endif diff --git a/code/redalert/saveload.cpp b/code/redalert/saveload.cpp new file mode 100644 index 0000000..5e4d836 --- /dev/null +++ b/code/redalert/saveload.cpp @@ -0,0 +1,1654 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SAVELOAD.CPP 9 3/17/97 1:04a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SAVELOAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 23, 1994 * + * * + * Last Update : July 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Code_All_Pointers -- Code all pointers. * + * Decode_All_Pointers -- Decodes all pointers. * + * Get_Savefile_Info -- gets description, scenario #, house * + * Load_Game -- loads a saved game * + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * Load_Misc_Values -- loads miscellaneous variables * + * MPlayer_Save_Message -- pops up a "saving..." message * + * Put_All -- Store all save game data to the pipe. * + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * Save_Game -- saves a game to disk * + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * Save_Misc_Values -- saves miscellaneous variables * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +//#include "WolDebug.h" + +extern bool DLLSave(Pipe &file); +extern bool DLLLoad(Straw &file); + +extern bool SpawnedFromWChat; +#endif + +//#define SAVE_BLOCK_SIZE 512 +#define SAVE_BLOCK_SIZE 4096 +//#define SAVE_BLOCK_SIZE 1024 + +/* +********************************** Defines ********************************** +*/ +#define SAVEGAME_VERSION (DESCRIP_MAX + \ + 0x01000006 + ( \ + sizeof(AircraftClass) + \ + sizeof(AircraftTypeClass) + \ + sizeof(AnimClass) + \ + sizeof(AnimTypeClass) + \ + sizeof(BaseClass) + \ + sizeof(BuildingClass) + \ + sizeof(BuildingTypeClass) + \ + sizeof(BulletClass) + \ + sizeof(BulletTypeClass) + \ + sizeof(CellClass) + \ + sizeof(FactoryClass) + \ + sizeof(HouseClass) + \ + sizeof(HouseTypeClass) + \ + sizeof(InfantryClass) + \ + sizeof(InfantryTypeClass) + \ + sizeof(LayerClass) + \ + sizeof(MouseClass) + \ + sizeof(OverlayClass) + \ + sizeof(OverlayTypeClass) + \ + sizeof(SmudgeClass) + \ + sizeof(SmudgeTypeClass) + \ + sizeof(TeamClass) + \ + sizeof(TeamTypeClass) + \ + sizeof(TemplateClass) + \ + sizeof(TemplateTypeClass) + \ + sizeof(TerrainClass) + \ + sizeof(TerrainTypeClass) + \ + sizeof(TriggerClass) + \ + sizeof(TriggerTypeClass) + \ + sizeof(UnitClass) + \ + sizeof(UnitTypeClass) + \ + sizeof(VesselClass) + \ + sizeof(ScenarioClass) + \ + sizeof(ChronalVortexClass))) +// sizeof(Waypoint))) + + +static int Reconcile_Players(void); +extern bool Is_Mission_Counterstrike (char *file_name); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +/*********************************************************************************************** + * Put_All -- Store all save game data to the pipe. * + * * + * This is the bulk processor of the game related save game data. All the game object * + * and state data is stored to the pipe specified. * + * * + * INPUT: pipe -- Reference to the pipe that will receive the save game data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1996 JLB : Created. * + *=============================================================================================*/ +static void Put_All(Pipe & pipe, int save_net) +{ + /* + ** Save the scenario global information. + */ + pipe.Put(&Scen, sizeof(Scen)); + + /* + ** Save the map. The map must be saved first, since it saves the Theater. + */ + if (!save_net) Call_Back(); + Map.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save all game objects. This code saves every object that's stored in a + ** TFixedIHeap class. + */ + Houses.Save(pipe); + if (!save_net) Call_Back(); + TeamTypes.Save(pipe); + if (!save_net) Call_Back(); + Teams.Save(pipe); + if (!save_net) Call_Back(); + TriggerTypes.Save(pipe); + if (!save_net) Call_Back(); + Triggers.Save(pipe); + if (!save_net) Call_Back(); + Aircraft.Save(pipe); + if (!save_net) Call_Back(); + Anims.Save(pipe); + + if (!save_net) Call_Back(); + + Buildings.Save(pipe); + if (!save_net) Call_Back(); + Bullets.Save(pipe); + if (!save_net) Call_Back(); + Infantry.Save(pipe); + if (!save_net) Call_Back(); + Overlays.Save(pipe); + if (!save_net) Call_Back(); + Smudges.Save(pipe); + if (!save_net) Call_Back(); + Templates.Save(pipe); + if (!save_net) Call_Back(); + Terrains.Save(pipe); + if (!save_net) Call_Back(); + Units.Save(pipe); + if (!save_net) Call_Back(); + Factories.Save(pipe); + if (!save_net) Call_Back(); + Vessels.Save(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save the Logic & Map layers + */ + Logic.Save(pipe); + + int count = MapTriggers.Count(); + pipe.Put(&count, sizeof(count)); + int index; + for (int index = 0; index < MapTriggers.Count(); index++) { + TARGET target = MapTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + count = LogicTriggers.Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < LogicTriggers.Count(); index++) { + TARGET target = LogicTriggers[index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + if (!save_net) Call_Back(); + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + count = HouseTriggers[h].Count(); + pipe.Put(&count, sizeof(count)); + for (index = 0; index < HouseTriggers[h].Count(); index++) { + TARGET target = HouseTriggers[h][index]->As_Target(); + pipe.Put(&target, sizeof(target)); + } + } + if (!save_net) Call_Back(); + + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Save(pipe); + } + + if (!save_net) Call_Back(); + + /* + ** Save the Score + */ + pipe.Put(&Score, sizeof(Score)); + if (!save_net) Call_Back(); + + /* + ** Save the AI Base + */ + Base.Save(pipe); + if (!save_net) Call_Back(); + + /* + ** Save out the carry over list (if present). First see how + ** many carry over objects are in the list. + */ + int carry_count = 0; + CarryoverClass const * cptr = Carryover; + while (cptr != NULL) { + carry_count++; + cptr = (CarryoverClass const *)cptr->Get_Next(); + } + + if (!save_net) Call_Back(); + + /* + ** Save out the number of objects in the list. + */ + pipe.Put(&carry_count, sizeof(carry_count)); + if (!save_net) Call_Back(); + + /* + ** Now write out the objects themselves. + */ + CarryoverClass const * object_to_write = Carryover; + while (object_to_write != NULL) { + pipe.Put(object_to_write, sizeof(*object_to_write)); + object_to_write = (CarryoverClass const *)object_to_write->Get_Next(); + } + if (!save_net) Call_Back(); + + /* + ** Save miscellaneous variables. + */ + Save_Misc_Values(pipe); + + if (!save_net) Call_Back(); + + /* + ** Save multiplayer values + */ + pipe.Put(&save_net, sizeof(save_net)); // Write out whether we saved the net values so we know if we have to load them again. ST - 10/22/2019 2:10PM + if (save_net) { + Save_MPlayer_Values(pipe); + } + + pipe.Flush(); +} + + +/*************************************************************************** + * Save_Game -- saves a game to disk * + * * + * Saving the Map: * + * DisplayClass::Save() invokes CellClass's Write() for every cell * + * that needs to be saved. A cell needs to be saved if it contains * + * any special data at all, such as a TIcon, or an Occupier. * + * The cell saves its own CellTrigger pointer, converted to a TARGET. * + * * + * Saving game objects: * + * - Any object stored in an ArrayOf class needs to be saved. The ArrayOf* + * Save() routine invokes each object's Write() routine, if that * + * object's IsActive is set. * + * * + * Saving the layers: * + * The Map's Layers (Ground, Air, etc) of things that are on the map, * + * and the Logic's Layer of things to process both need to be saved. * + * LayerClass::Save() writes the entire layer array to disk * + * * + * Saving the houses: * + * Each house needs to be saved, to record its Credits, Power, etc. * + * * + * Saving miscellaneous data: * + * There are a lot of miscellaneous variables to save, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 02/27/1996 JLB : Uses simpler game control value save operation. * + *=========================================================================*/ +bool Save_Game(int id, char const * descr, bool ) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to save. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id==-1) { + strcpy(name, NET_SAVE_FILE_NAME); + //save_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + + return Save_Game(name, descr); +} + + + +/* +** Version that takes file name. ST - 9/9/2019 11:10AM +*/ +bool NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve +bool Save_Game(const char *file_name, const char *descr) +{ + NowSavingGame = true; // TEMP MBL: Need to discuss better solution with Steve + + int save_net = 0; // 1 = save network/modem game + + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + save_net = 1; + } + + unsigned scenario; + HousesType house; + + scenario = Scen.Scenario; // get current scenario # + house = PlayerPtr->Class->House; // get current house + + /* + ** Code everybody's pointers + */ + Code_All_Pointers(); + + /* + ** Open the file + */ + BufferIOFileClass file(file_name); + + FilePipe fpipe(&file); + + /* + ** Save the DLLs variables first, so we can do a version check in the DLL when we begin the load + */ + if (RunningAsDLL) { + DLLSave(fpipe); + } + + /* + ** Save the description, scenario #, and house + ** (scenario # & house are saved separately from the actual Scenario & + ** PlayerPtr globals for convenience; we can quickly find out which + ** house & scenario this save-game file is for by reading these values. + ** Also, PlayerPtr is stored in a coded form in Save_Misc_Values(), + ** which may or may not be a HousesType number; so, saving 'house' + ** here ensures we can always pull out the house for this file.) + */ + char descr_buf[DESCRIP_MAX]; + memset(descr_buf, '\0', sizeof(descr_buf)); + sprintf(descr_buf, "%s\r\n", descr); // put CR-LF after text + //descr_buf[strlen(descr_buf) + 1] = 26; // put CTRL-Z after NULL + fpipe.Put(descr_buf, DESCRIP_MAX); + + fpipe.Put(&scenario, sizeof(scenario)); + + fpipe.Put(&house, sizeof(house)); + + /* + ** Save the save-game version, for loading verification + */ + unsigned long version = SAVEGAME_VERSION; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + version++; +#endif + fpipe.Put(&version, sizeof(version)); + + int pos = file.Seek(0, SEEK_CUR); + + /* + ** Store a dummy message digest. + */ + char digest[20]; + fpipe.Put(digest, sizeof(digest)); + + + /* + ** Dump the save game data to the file. The data is compressed + ** and then encrypted. The message digest is calculated in the + ** process by using the data just as it is written to disk. + */ + SHAPipe sha; + BlowPipe bpipe(BlowPipe::ENCRYPT); + LZOPipe pipe(LZOPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LZWPipe pipe(LZWPipe::COMPRESS, SAVE_BLOCK_SIZE); +// LCWPipe pipe(LCWPipe::COMPRESS, SAVE_BLOCK_SIZE); + bpipe.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + + sha.Put_To(fpipe); + bpipe.Put_To(sha); + pipe.Put_To(bpipe); + Put_All(pipe, save_net); + + /* + ** Output the real final message digest. This is the one that is of + ** the data image as it exists on the disk. + */ + pipe.Flush(); + file.Seek(pos, SEEK_SET); + sha.Result(digest); + fpipe.Put(digest, sizeof(digest)); + + pipe.End(); + + Decode_All_Pointers(); + + NowSavingGame = false; // TEMP MBL: Need to discuss better solution with Steve + + return(true); +} + + +/*************************************************************************** + * Load_Game -- loads a saved game * + * * + * This routine loads the data in the same way it was saved out. * + * * + * Loading the Map: * + * - DisplayClass::Load() invokes CellClass's Load() for every cell * + * that was saved. * + * - The cell loads its own CellTrigger pointer. * + * * + * Loading game objects: * + * - IHeap's Load() routine loads the # of objects stored, and loads * + * each object. * + * - Triggers: Add themselves to the HouseTriggers if they're associated * + * with a house * + * * + * Loading the layers: * + * LayerClass::Load() reads the entire layer array to disk * + * * + * Loading the houses: * + * Each house is loaded in its entirety. * + * * + * Loading miscellaneous data: * + * There are a lot of miscellaneous variables to load, such as the * + * map's dimensions, the player's house, etc. * + * * + * INPUT: * + * id numerical ID, for the file extension * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * If this routine returns false, the entire game will be in an * + * unknown state, so the scenario will have to be re-initialized. * + * * + * HISTORY: * + * 12/28/1994 BR : Created. * + * 1/20/97 V.Grippi Added expansion CD check * + *=========================================================================*/ +bool Load_Game(int id) +{ + char name[_MAX_FNAME+_MAX_EXT]; + + /* + ** Generate the filename to load. If 'id' is -1, it means save a + ** network/modem game; otherwise, use 'id' as the file extension. + */ + if (id == -1) { + strcpy(name, NET_SAVE_FILE_NAME); + //load_net = 1; + } else { + sprintf(name, "SAVEGAME.%03d", id); + } + return Load_Game(name); +} + + +/* +** Version that takes a file name instead. ST - 9/9/2019 11:13AM +*/ +bool Load_Game(const char *file_name) +{ + int i; + unsigned scenario; + HousesType house; + char descr_buf[DESCRIP_MAX]; + int load_net = 0; // 1 = save network/modem game + + /* + ** Open the file + */ + RawFileClass file(file_name); + if (!file.Is_Available()) { + return(false); + } + + FileStraw fstraw(file); + + Call_Back(); + + /* + ** Load the DLLs variables first, in case we need to do something different based on version + */ + if (RunningAsDLL) { + DLLLoad(fstraw); + } + + /* + ** Read & discard the save-game's header info + */ + if (fstraw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + if (fstraw.Get(&scenario, sizeof(scenario)) != sizeof(scenario)) { + return(false); + } + + if (fstraw.Get(&house, sizeof(house)) != sizeof(house)) { + return(false); + } + + /* + ** Read in & verify the save-game ID code + */ + unsigned long version; + if (fstraw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } + GameVersion = version; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1) != SAVEGAME_VERSION) ) { + return(false); + } +#else + if (version != SAVEGAME_VERSION /*&& version != 0x0100616D*/){ + return(false); + } +#endif + /* + ** Get the message digest that is embedded in the file. + */ + char digest[20]; + fstraw.Get(digest, sizeof(digest)); + + /* + ** Remember the file position since we must seek back here to + ** perform the real saved game read. + */ + long pos = file.Seek(0, SEEK_CUR); + + /* + ** Pass the rest of the file through the hash straw so that + ** the digest can be compaired to the one in the file. + */ + SHAStraw sha; + sha.Get_From(fstraw); + for (;;) { + if (sha.Get(_staging_buffer, sizeof(_staging_buffer)) != sizeof(_staging_buffer)) break; + } + char actual[20]; + sha.Result(actual); + sha.Get_From(NULL); + + Call_Back(); + + /* + ** Compare the two digests. If they differ then return a failure condition + ** before any damage could be done. + */ + if (memcmp(actual, digest, sizeof(digest)) != 0) { + return(false); + } + + /* + ** Set up the pipe so that the scenario data can be read. + */ + file.Seek(pos, SEEK_SET); + BlowStraw bstraw(BlowStraw::DECRYPT); + LZOStraw straw(LZOStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LZWStraw straw(LZWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); +// LCWStraw straw(LCWStraw::DECOMPRESS, SAVE_BLOCK_SIZE); + + bstraw.Key(&FastKey, BlowfishEngine::MAX_KEY_LENGTH); + bstraw.Get_From(fstraw); + straw.Get_From(bstraw); + + /* + ** Clear the scenario so we start fresh; this calls the Init_Clear() routine + ** for the Map, and all object arrays. It has the following important + ** effects: + ** - Every cell is cleared to 0's, via MapClass::Init_Clear() + ** - All heap elements' are cleared + ** - The Houses are Initialized, which also clears their HouseTriggers + ** array + ** - The map's Layers & Logic Layer are cleared to empty + ** - The list of currently-selected objects is cleared + */ + Clear_Scenario(); + + /* + ** Load the scenario global information. + */ + straw.Get(&Scen, sizeof(Scen)); + + /* + ** Fixup the Sessionclass scenario info so we can work out which + ** CD to request later + */ + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + if ( !scenario_file.Is_Available() ){ + + int cd = -1; + if (Is_Mission_Counterstrike (Scen.ScenarioName)) { + cd = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Expansion_AM_Present()) { + int current_drive = CCFileClass::Get_CD_Drive(); + int index = Get_CD_Index(current_drive, 1*60); + if (index == 3) cd = 3; + } +#endif + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Mission_Aftermath (Scen.ScenarioName)) { + cd = 3; +#ifdef BOGUSCD + cd = -1; +#endif + } +#endif + RequiredCD = cd; + if (!Force_CD_Available (RequiredCD)) { + Emergency_Exit(EXIT_FAILURE); + } + + /* + ** Update the internal list of scenarios to include the counterstrike + ** list. + */ + Session.Read_Scenario_Descriptions(); + } else { + /* + ** The scenario is available so set RequiredCD to whatever is currently + ** in the drive. + */ + int current_drive = CCFileClass::Get_CD_Drive(); + RequiredCD = Get_CD_Index(current_drive, 1*60); + } + } + + /* + ** Load the map. The map comes first, since it loads the Theater & init's + ** mixfiles. The map calls all the type-class's Init routines, telling them + ** what the Theater is; this must be done before any objects are created, so + ** they'll be properly created. + */ + Map.Load(straw); + + Call_Back(); + + /* + ** Load the object data. + */ + Houses.Load(straw); + TeamTypes.Load(straw); + Teams.Load(straw); + TriggerTypes.Load(straw); + Triggers.Load(straw); + Aircraft.Load(straw); + Anims.Load(straw); + Buildings.Load(straw); + Bullets.Load(straw); + + Call_Back(); + + Infantry.Load(straw); + Overlays.Load(straw); + Smudges.Load(straw); + Templates.Load(straw); + Terrains.Load(straw); + Units.Load(straw); + Factories.Load(straw); + Vessels.Load(straw); + + /* + ** Load the Logic & Map Layers + */ + Logic.Load(straw); + + int count; + straw.Get(&count, sizeof(count)); + MapTriggers.Clear(); + int index; + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + MapTriggers.Add(As_Trigger(target)); + } + + straw.Get(&count, sizeof(count)); + LogicTriggers.Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + LogicTriggers.Add(As_Trigger(target)); + } + + for (HousesType h = HOUSE_FIRST; h < HOUSE_COUNT; h++) { + straw.Get(&count, sizeof(count)); + HouseTriggers[h].Clear(); + for (index = 0; index < count; index++) { + TARGET target; + straw.Get(&target, sizeof(target)); + HouseTriggers[h].Add(As_Trigger(target)); + } + } + + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Load(straw); + } + + Call_Back(); + + /* + ** Load the Score + */ + straw.Get(&Score, sizeof(Score)); + new(&Score) ScoreClass(NoInitClass()); + + /* + ** Load the AI Base + */ + Base.Load(straw); + + /* + ** Delete any carryover pseudo-saved game list. + */ + while (Carryover != NULL) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Load any carryover pseudo-saved game list. + */ + int carry_count = 0; + straw.Get(&carry_count, sizeof(carry_count)); + while (carry_count) { + CarryoverClass * cptr = new CarryoverClass; + assert(cptr != NULL); + + straw.Get(cptr, sizeof(CarryoverClass)); + new (cptr) CarryoverClass(NoInitClass()); + cptr->Zap(); + + if (!Carryover) { + Carryover = cptr; + } else { + cptr->Add_Tail(*Carryover); + } + carry_count--; + } + + Call_Back(); + + /* + ** Load miscellaneous variables, including the map size & the Theater + */ + Load_Misc_Values(straw); + + /* + ** Load multiplayer values + */ + straw.Get(&load_net, sizeof(load_net)); + if (load_net) { + Load_MPlayer_Values(straw); + } + + file.Close(); + Decode_All_Pointers(); + Map.Init_IO(); + Map.Flag_To_Redraw(true); + + /* + ** Fixup any expediency data that can be inferred from the physical + ** data loaded. + */ + Post_Load_Game(load_net); + + /* + ** Re-init unit trackers. They will be garbage pointers after the load + */ + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr && hptr->IsActive) { + hptr->Init_Unit_Trackers(); + } + } + + Call_Back(); + + /* + ** Set the required CD to be in the drive according to the scenario + ** loaded. + */ + if (RequiredCD != -2 && !load_net) { + +#ifdef FIXIT_ANTS + /* + ** Determines if this an ant mission. Since the ant mission looks no different from + ** a regular mission, examining of the scenario name is the only way to tell. + */ + if (toupper(Scen.ScenarioName[0]) == 'S' && + toupper(Scen.ScenarioName[1]) == 'C' && + toupper(Scen.ScenarioName[2]) == 'A' && + toupper(Scen.ScenarioName[3]) == '0' && + toupper(Scen.ScenarioName[5]) == 'E' && + toupper(Scen.ScenarioName[6]) == 'A') { + + AntsEnabled = true; + } else{ + AntsEnabled = false; + } +#endif + + if (Scen.Scenario == 1) { + RequiredCD = -1; + } else { +#ifdef FIXIT_ANTS + if (Scen.Scenario > 19 || AntsEnabled) { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(Scen.Scenario >= 36) { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { +#endif //FIXIT_ANTS + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + RequiredCD = 0; + } else { + RequiredCD = 1; + } +#ifdef FIXIT_ANTS + } +#endif //FIXIT_ANTS + } + + }else{ + + if ( load_net ){ + + CCFileClass scenario_file (Scen.ScenarioName); + + /* + ** Fix up the session class variables + */ + for ( int s=0 ; sDescription() == Scen.Description){ + + memcpy (Session.Options.ScenarioDescription, Scen.Description, + sizeof (Session.Options.ScenarioDescription)); + memcpy (Session.ScenarioFileName, Scen.ScenarioName, + sizeof (Session.ScenarioFileName)); + Session.ScenarioFileLength = scenario_file.Size(); + memcpy (Session.ScenarioDigest, Session.Scenarios[s]->Get_Digest(), + sizeof (Session.ScenarioDigest)); + Session.ScenarioIsOfficial = Session.Scenarios[s]->Get_Official(); + Scen.Scenario = s; + Session.Options.ScenarioIndex = s; + break; + } + } + } + } + + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Load_Game Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + + ScenarioInit = 0; + + if (load_net) { + + // Removed as this is ensured by the GlyphX & DLL save/load code. ST - 10/22/2019 5:20PM + //if (!Reconcile_Players()) { // (must do after Decode pointers) + // return(false); + //} + + //!!!!!!!!!! put Fixup_Player_Units() here + Session.LoadGame = true; + } + + Map.Reload_Sidebar(); // re-load sidebar art. + + /* + ** Rescan the scenario file for any rules updates. + */ + CCINIClass ini; + int result = ini.Load(CCFileClass(Scen.ScenarioName), true); + + /* + ** Reset the rules values to their initial settings. + */ + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - But does this incorporate *changes*? - NO. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); \ + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode. + if (load_net) { + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: + readini = Is_Aftermath_Installed(); + break; + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + if (!Force_CD_Available(3)) { // force Aftermath CD in drive. + Prog_End("Load_Game Force_CD_Available(3) failed", true); +#ifndef FIXIT_VERSION_3 // WChat eliminated. +#ifdef WIN32 +if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); +} +#endif +#endif + Emergency_Exit(EXIT_FAILURE); + } + } + CCINIClass mpini; + if (mpini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(mpini); + Rule.Recharge(mpini); + Rule.AI(mpini); + Rule.Powerups(mpini); + Rule.Land_Types(mpini); + Rule.Themes(mpini); + Rule.IQ(mpini); + Rule.Objects(mpini); + Rule.Difficulty(mpini); + } + } + + } +#endif + + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } else { + Theme.Queue_Song(Scen.TransitTheme); + } + + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + Rule.IsSmartDefense = true; + } + + return(true); +} + + +/*************************************************************************** + * Save_Misc_Values -- saves miscellaneous variables * + * * + * INPUT: * + * file file to use for writing * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/29/1994 BR : Created. * + * 03/12/1996 JLB : Simplified. * + *=========================================================================*/ +bool Save_Misc_Values(Pipe & file) +{ + int i, j; + int count; // # ptrs in 'CurrentObject' + ObjectClass * ptr; // for saving 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x = PlayerPtr->Class->House; + file.Put(&x, sizeof(x)); + + /* + ** Save frame #. + */ + file.Put(&Frame, sizeof(Frame)); + + /* + ** Save currently-selected objects list. + ** Save the # of ptrs in the list. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + count = selection.Count(); + file.Put(&count, sizeof(count)); + + /* + ** Save the pointers. + */ + for (j = 0; j < count; j++) { + ptr = selection[j]; + file.Put(&ptr, sizeof(ptr)); + } + } + + /* + ** Save the chronal vortex + */ + ChronalVortex.Save(file); + + /* + ** Save Tanya flags. + */ + file.Put(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Put(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*********************************************************************************************** + * Load_Misc_Values -- Loads miscellaneous variables. * + * * + * INPUT: file -- The file to load the misc values from. * + * * + * OUTPUT: Was the misc load process successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + * 03/12/1996 JLB : Simplified. * + *=============================================================================================*/ +bool Load_Misc_Values(Straw & file) +{ + ObjectClass * ptr; // for loading 'CurrentObject' ptrs + + /* + ** Player's House. + */ + int x; + file.Get(&x, sizeof(x)); +// file.Get(&PlayerPtr, sizeof(PlayerPtr)); + PlayerPtr = HouseClass::As_Pointer((HousesType)x); + + /* + ** Load frame #. + */ + file.Get(&Frame, sizeof(Frame)); + + for (int i = 0; i < SelectedObjectsType::COUNT; i++) { + /* + ** Load currently-selected objects list. + ** Load the # of ptrs in the list. + */ + DynamicVectorClass& selection = CurrentObject.Raw(i); + int count; // # ptrs in 'CurrentObject' + file.Get(&count, sizeof(count)); + + /* + ** Load the pointers. + */ + for (int j = 0; j < count; j++) { + file.Get(&ptr, sizeof(ptr)); + selection.Add(ptr); // add to the list + } + } + + /* + ** Load the chronal vortex + */ + ChronalVortex.Load(file); + + /* + ** Save Tanya flags. + */ + file.Get(&IsTanyaDead, sizeof(IsTanyaDead)); + file.Get(&SaveTanya, sizeof(SaveTanya)); + + return(true); +} + + +/*************************************************************************** + * Save_MPlayer_Values -- Saves multiplayer-specific values * + * * + * This routine saves multiplayer values that need to be restored for a * + * save game. In addition to saving the random # seed for this scenario, * + * it saves the contents of the actual random number generator; this * + * ensures that the random # sequencer will pick up where it left off when * + * the game was saved. * + * This routine also saves the header for a Recording file, so it must * + * save some data not needed specifically by a save-game file (ie Seed). * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Save_MPlayer_Values(Pipe & file) +{ + Session.Save(file); + file.Put(&BuildLevel, sizeof(BuildLevel)); + file.Put(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Put(&Seed, sizeof(Seed)); + file.Put(&Whom, sizeof(Whom)); + file.Put(&Special, sizeof(SpecialClass)); + file.Put(&Options, sizeof(GameOptionsClass)); + + return (true); +} + + +/*************************************************************************** + * Load_MPlayer_Values -- Loads multiplayer-specific values * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * true = success, false = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/28/1995 BRR : Created. * + *=========================================================================*/ +bool Load_MPlayer_Values(Straw & file) +{ + Session.Load(file); + file.Get(&BuildLevel, sizeof(BuildLevel)); + file.Get(&Debug_Unshroud, sizeof(Debug_Unshroud)); + file.Get(&Seed, sizeof(Seed)); + file.Get(&Whom, sizeof(Whom)); + file.Get(&Special, sizeof(SpecialClass)); + file.Get(&Options, sizeof(GameOptionsClass)); + + return (true); +} + +/* +** ST - 9/26/2019 11:43AM +*/ +extern void DLL_Code_Pointers(void); +extern void DLL_Decode_Pointers(void); + +/*********************************************************************************************** + * Code_All_Pointers -- Code all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Code_All_Pointers(void) +{ + int i, j; + + /* + ** The Map. + */ + Map.Code_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Code_Pointers(); + Teams.Code_Pointers(); + Triggers.Code_Pointers(); + Aircraft.Code_Pointers(); + Anims.Code_Pointers(); + Buildings.Code_Pointers(); + Bullets.Code_Pointers(); + Infantry.Code_Pointers(); + Overlays.Code_Pointers(); + Smudges.Code_Pointers(); + Templates.Code_Pointers(); + Terrains.Code_Pointers(); + Units.Code_Pointers(); + Factories.Code_Pointers(); + Vessels.Code_Pointers(); + + /* + ** The Layers. + */ + Logic.Code_Pointers(); + for (i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Code_Pointers(); + } + + /* + ** The Score. + */ + Score.Code_Pointers(); + + /* + ** The Base. + */ + Base.Code_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = (HouseClass *)(PlayerPtr->Class->House); + + /* + ** Currently-selected objects. + */ + for (i = 0; i < SelectedObjectsType::COUNT; i++) { + DynamicVectorClass& selection = CurrentObject.Raw(i); + for (j = 0; j < selection.Count(); j++) { + selection[j] = (ObjectClass *)selection[j]->As_Target(); + } + } + + /* + ** DLL data + */ + DLL_Code_Pointers(); + + /* + ** Houses must be coded last, because the Class->House member of the HouseClass + ** is used to code HouseClass pointers for all other objects, and if Class is + ** coded, it will point to a meaningless value. + */ + Houses.Code_Pointers(); +} + + +/*********************************************************************************************** + * Decode_All_Pointers -- Decodes all pointers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1995 BRR : Created. * + *=============================================================================================*/ +void Decode_All_Pointers(void) +{ + /* + ** The Map. + */ + Map.Decode_Pointers(); + + /* + ** Decode houses first, so we can properly decode all other objects' + ** House pointers + */ + Houses.Decode_Pointers(); + + /* + ** DLL data + */ + DLL_Decode_Pointers(); + + /* + ** The ArrayOf's. + */ + TeamTypes.Decode_Pointers(); + Teams.Decode_Pointers(); + Triggers.Decode_Pointers(); + Aircraft.Decode_Pointers(); + Anims.Decode_Pointers(); + Buildings.Decode_Pointers(); + Bullets.Decode_Pointers(); + Infantry.Decode_Pointers(); + Overlays.Decode_Pointers(); + Smudges.Decode_Pointers(); + Templates.Decode_Pointers(); + Terrains.Decode_Pointers(); + Units.Decode_Pointers(); + Factories.Decode_Pointers(); + Vessels.Decode_Pointers(); + + /* + ** The Layers. + */ + Logic.Decode_Pointers(); + for (int i = 0; i < LAYER_COUNT; i++) { + Map.Layer[i].Decode_Pointers(); + } + + /* + ** The Score. + */ + Score.Decode_Pointers(); + + /* + ** The Base. + */ + Base.Decode_Pointers(); + + /* + ** PlayerPtr. + */ +// PlayerPtr = HouseClass::As_Pointer((HousesType)PlayerPtr); + Whom = PlayerPtr->Class->House; + assert(PlayerPtr != NULL); + + /* + ** Currently-selected objects. + */ + for (int index = 0; index < SelectedObjectsType::COUNT; index++) { + DynamicVectorClass& selection = CurrentObject.Raw(index); + for (int j = 0; j < selection.Count(); j++) { + selection[j] = As_Object((TARGET)selection[j]); + assert(selection[j] != NULL); + } + } + + /* + ** Last-Minute Fixups; to resolve these pointers properly requires all other + ** pointers to be loaded & decoded. + */ + if (Map.PendingObjectPtr) { + Map.PendingObject = &Map.PendingObjectPtr->Class_Of(); + assert(Map.PendingObject != NULL); + Map.Set_Cursor_Shape(Map.PendingObject->Occupy_List(true)); +#ifdef BG + Map.Set_Placement_List(Map.PendingObject->Placement_List(true)); +#endif + } else { + Map.PendingObject = 0; + Map.Set_Cursor_Shape(0); + } +} + + +/*************************************************************************** + * Get_Savefile_Info -- gets description, scenario #, house * + * * + * INPUT: * + * id numerical ID, for the file extension * + * buf buffer to store description in * + * scenp ptr to variable to hold scenario * + * housep ptr to variable to hold house * + * * + * OUTPUT: * + * true = OK, false = error (save-game file invalid) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 01/12/1995 BR : Created. * + *=========================================================================*/ +bool Get_Savefile_Info(int id, char * buf, unsigned * scenp, HousesType * housep) +{ + char name[_MAX_FNAME+_MAX_EXT]; + unsigned long version; + char descr_buf[DESCRIP_MAX]; + + /* + ** Generate the filename to load + */ + sprintf(name, "SAVEGAME.%03d", id); + BufferIOFileClass file(name); + + FileStraw straw(file); + + /* + ** Read in the description, scenario #, and the house + */ + if (straw.Get(descr_buf, DESCRIP_MAX) != DESCRIP_MAX) { + return(false); + } + + descr_buf[strlen(descr_buf) - 2] = '\0'; // trim off CR/LF + strcpy(buf, descr_buf); + + if (straw.Get(scenp, sizeof(unsigned)) != sizeof(unsigned)) { + return(false); + } + + if (straw.Get(housep, sizeof(HousesType)) != sizeof(HousesType)) { + return(false); + } + + /* + ** Read & verify the save-game version # + */ + if (straw.Get(&version, sizeof(version)) != sizeof(version)) { + return(false); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (version != SAVEGAME_VERSION && ((version-1 != SAVEGAME_VERSION)) ) { +#else + if (version != SAVEGAME_VERSION) { +#endif + return(false); + } + return(true); +} + + +/*************************************************************************** + * Reconcile_Players -- Reconciles loaded data with the 'Players' vector * + * * + * This function is for supporting loading a saved multiplayer game. * + * When the game is loaded, we have to figure out which house goes with * + * which entry in the Players vector. We also have to figure out if * + * everyone who was originally in the game is still with us, and if not, * + * turn their stuff over to the computer. * + * * + * So, this function does the following: * + * - For every name in 'Players', makes sure that name is in the House * + * array; if not, it's a fatal error. * + * - For every human-controlled house, makes sure there's a player * + * with that name; if not, it turns that house over to the computer. * + * - Fills in the Player's house ID * + * * + * This assumes that each player MUST keep their name the same as it was * + * when the game was saved! It's also assumed that the network * + * connections have not been formed yet, since Player[i]->Player.ID will * + * be invalid until this routine has been called. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * true = OK, false = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 09/29/1995 BRR : Created. * + *=========================================================================*/ +static int Reconcile_Players(void) +{ + int i; + int found; + HousesType house; + HouseClass * housep; + + /* + ** If there are no players, there's nothing to do. + */ + if (Session.Players.Count()==0) + return (true); + + /* + ** Make sure every name we're connected to can be found in a House + */ + for (i = 0; i < Session.Players.Count(); i++) { + found = 0; + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + break; + } + } + if (!found) + return (false); + } + + // + // Loop through all Houses; if we find a human-owned house that we're + // not connected to, turn it over to the computer. + // + for (house = HOUSE_MULTI1; house < HOUSE_MULTI1 + + Session.MaxPlayers; house++) { + housep = HouseClass::As_Pointer(house); + if (!housep) { + continue; + } + + // + // Skip this house if it wasn't human to start with. + // + if (!housep->IsHuman) { + continue; + } + + // + // Try to find this name in the Players vector; if it's found, set + // its ID to this house. + // + found = 0; + for (i = 0; i < Session.Players.Count(); i++) { + if (!stricmp(Session.Players[i]->Name, housep->IniName)) { + found = 1; + Session.Players[i]->Player.ID = house; + break; + } + } + + /* + ** If this name wasn't found, remove it + */ + if (!found) { + + /* + ** Turn the player's house over to the computer's AI + */ + housep->IsHuman = false; + housep->IsStarted = true; +// housep->Smartness = IQ_MENSA; + housep->IQ = Rule.MaxIQ; + strcpy (housep->IniName, Text_String(TXT_COMPUTER)); + + Session.NumPlayers--; + } + } + + // + // If all went well, our Session.NumPlayers value should now equal the value + // from the saved game, minus any players we removed. + // + if (Session.NumPlayers == Session.Players.Count()) { + return (true); + } else { + return (false); + } +} + + +/*************************************************************************** + * MPlayer_Save_Message -- pops up a "saving..." message * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +void MPlayer_Save_Message(void) +{ + //char *txt = Text_String( +} \ No newline at end of file diff --git a/code/redalert/scenario.cpp b/code/redalert/scenario.cpp new file mode 100644 index 0000000..f6943e9 --- /dev/null +++ b/code/redalert/scenario.cpp @@ -0,0 +1,3770 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +//Mono_Printf("%d %s\n",__LINE__,__FILE__); +/* $Header: /CounterStrike/SCENARIO.CPP 15 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : October 21, 1996 [JLB] * + * * + * This module handles the scenario reading and writing. Scenario related * + * code that is executed between scenario play can also be here. * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Assign_Houses -- Assigns multiplayer houses to various players * + * Clear_Flag_Spots -- Clears flag overlays off the map * + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * Clip_Move -- moves in given direction from given cell; clips to map * + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * Do_Lose -- Display losing comments. * + * Do_Restart -- Handle the restart mission process. * + * Do_Win -- Display winning congratulations. * + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * Post_Load_Game -- Fill in an inferred data from the game state. * + * Read_Scenario -- Reads a scenario from disk. * + * Read_Scenario_INI -- Read specified scenario INI file. * + * Remove_AI_Players -- Removes the computer AI houses & their units * + * Restate_Mission -- Handles restating the mission objective. * + * Scan_Place_Object -- places an object >near< the given cell * + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * Set_Scenario_Name -- Creates the INI scenario name string. * + * Start_Scenario -- Starts the scenario. * + * Write_Scenario_INI -- Write the scenario INI file. * + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WIN32 +#include "tcpip.h" +#include "ccdde.h" + +extern bool SpawnedFromWChat; +#endif +extern int PreserveVQAScreen; + +void Display_Briefing_Text_GlyphX(); + +extern void GlyphX_Assign_Houses(void); //ST - 8/8/2019 12:35PM + +//#include "WolDebug.h" + +#ifdef FIXIT_VERSION_3 // Stalemate games. +#include "WolStrng.h" +#endif + +static void Remove_AI_Players(void); +static void Create_Units(bool official); +static CELL Clip_Scatter(CELL cell, int maxdist); +static CELL Clip_Move(CELL cell, FacingType facing, int dist); + +// Made this non-static so we can access it from the updated assign players function. ST - 8/9/2019 10:35AM +int _build_tech[11] = { + 2,2, // Tech level 0 and 1 are the same (tech 0 is never used). + 4, + 5, + 7, + 8, + 9, + 10, + 11, + 12, + 13 +}; + + +#ifdef FRENCH +#define TXT_HACKHACK "Accomplie" +#endif +#if defined(ENGLISH) || defined(GERMAN) +#define TXT_HACKHACK Text_String(TXT_ACCOMPLISHED) +#endif + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Counterstrike (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +#endif + +bool mapScriptPlayStart = false; + +void Cmd_Map(void); + +/*********************************************************************************************** + * ScenarioClass::ScenarioClass -- Constructor for the scenario control object. * + * * + * This constructs the default scenario control object. Normally, all the default values * + * are meaningless since the act of starting a scenario will fill in all of the values with * + * settings retrieved from the scenario control file. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +ScenarioClass::ScenarioClass(void) : + Difficulty(DIFF_NORMAL), + CDifficulty(DIFF_NORMAL), + Timer(0), + MissionTimer(0), + ShroudTimer(TICKS_PER_MINUTE * Rule.ShroudRate), + Scenario(1), + Theater(THEATER_TEMPERATE), + IntroMovie(VQ_NONE), + BriefMovie(VQ_NONE), + WinMovie(VQ_NONE), + WinMovie2(VQ_NONE), + WinMovie3(VQ_NONE), + WinMovie4(VQ_NONE), + LoseMovie(VQ_NONE), + ActionMovie(VQ_NONE), + TransitTheme(THEME_NONE), + PlayerHouse(HOUSE_GREECE), + CarryOverPercent(0), + CarryOverMoney(0), + CarryOverCap(0), + Percent(0), + BridgeCount(0), + CarryOverTimer(0), + IsBridgeChanged(false), + IsGlobalChanged(false), + IsToCarryOver(false), + IsToInherit(false), + IsTanyaEvac(false), + IsFadingBW(false), + IsFadingColor(false), + IsEndOfGame(false), + IsInheritTimer(false), + IsNoSpyPlane(false), + IsSkipScore(false), + IsOneTimeOnly(false), + IsNoMapSel(false), + IsTruckCrate(false), + IsMoneyTiberium(false), +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. +#define AUTOSONAR_PERIOD TICKS_PER_SECOND * 40 + AutoSonarTimer( AUTOSONAR_PERIOD ), +#endif + FadeTimer(0) +{ + for (int index = 0; index < ARRAY_SIZE(Waypoint); index++) { + Waypoint[index] = -1; + } + strcpy(Description, ""); + strcpy(ScenarioName, ""); + strcpy(BriefingText, ""); + memset(GlobalFlags, '\0', sizeof(GlobalFlags)); + memset(Views, '\0', sizeof(Views)); +// jmarshall + warpIntoMap = false; + Cmd_AddCommand("map", Cmd_Map); +// jmarshall end +} + + +/*********************************************************************************************** + * ScenarioClass::Do_BW_Fade -- Cause the palette to temporarily shift to B/W. * + * * + * This routine will start the palette to fade to B/W for a brief moment. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_BW_Fade(void) +{ + IsFadingBW = true; + IsFadingColor = false; + FadeTimer = GRAYFADETIME; +} + + +/*********************************************************************************************** + * ScenarioClass::Do_Fade_AI -- Process the palette fading effect. * + * * + * This routine will handle the maintenance of the palette fading effect. It should be * + * called once per game frame. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/21/1996 JLB : Created. * + *=============================================================================================*/ +void ScenarioClass::Do_Fade_AI(void) +{ + if (IsFadingColor) { + if (FadeTimer == 0) { + IsFadingColor = false; + } + fixed newsat = Options.Get_Saturation() * fixed(GRAYFADETIME-FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + } + if (IsFadingBW) { + if (FadeTimer == 0) { + IsFadingBW = false; + } + fixed newsat = Options.Get_Saturation() * fixed(FadeTimer, GRAYFADETIME); + Options.Adjust_Palette(OriginalPalette, GamePalette, Options.Get_Brightness(), newsat, Options.Get_Tint(), Options.Get_Contrast()); + GamePalette.Set(); + if (!IsFadingBW) { + IsFadingColor = true; + FadeTimer = GRAYFADETIME; + } + } +} + + +/*********************************************************************************************** + * ScenarioClass::Set_Global_To -- Set scenario global to value specified. * + * * + * This routine will set the global flag to the falue (true/false) specified. It will * + * also scan for and spring any triggers that are dependant upon that global. * + * * + * INPUT: global -- The global flag to change. * + * * + * value -- The value to change the global flag to. * + * * + * OUTPUT: Returns with the previous value of the flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool ScenarioClass::Set_Global_To(int global, bool value) +{ + if ((unsigned)global < ARRAY_SIZE(Scen.GlobalFlags)) { + + bool previous = GlobalFlags[global]; + if (previous != value) { + GlobalFlags[global] = value; + IsGlobalChanged = true; + + /* + ** Special case to scan through all triggers and if any are found that depend on this + ** global being set/cleared, then if there is an elapsed time event associated, it + ** will be reset at this time. + */ + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + if ((tp->Class->Event1.Event == TEVENT_GLOBAL_SET || tp->Class->Event1.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event1.Data.Value == global) { + tp->Class->Event2.Reset(tp->Event1); + } + if ((tp->Class->Event2.Event == TEVENT_GLOBAL_SET || tp->Class->Event2.Event == TEVENT_GLOBAL_CLEAR) && tp->Class->Event2.Data.Value == global) { + tp->Class->Event1.Reset(tp->Event1); + } + } + } + return(previous); + } + return(false); +} + + +/*********************************************************************************************** + * Start_Scenario -- Starts the scenario. * + * * + * This routine will start the scenario. In addition to loading the scenario data, it will * + * play the briefing and action movies. * + * * + * INPUT: root -- Pointer to the filename root for this scenario (e.g., "SCG01EA"). * + * * + * briefing -- Should the briefing be played? Normally this is true except when the * + * scenario is restarting. * + * * + * OUTPUT: Was the scenario started without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +bool Start_Scenario(char * name, bool briefing) +{ +//BG Theme.Queue_Song(THEME_QUIET); + Console_Printf("Start_Scenario: %s\n", name); + + Theme.Stop(); + IsTanyaDead = SaveTanya; + if (!Read_Scenario(name)) { + Console_Printf("Failed to load scenario!\n"); + return(false); + } + +// jmarshall: map scripts + if (Scen.mapScript) { + delete Scen.mapScript; + } + Scen.mapScript = new MapScript(); + if (!Scen.mapScript->Init(name)) { + delete Scen.mapScript; + Scen.mapScript = nullptr; + } + else { + mapScriptPlayStart = true; + } +// jmarshall end + + /* + ** Play the winning movie and then start the next scenario. + */ + RequiredCD = -1; +// if (RequiredCD != -2 && Session.Type == GAME_NORMAL) { +// if (Scen.Scenario == 1) +// RequiredCD = -1; +// else { +// if((Scen.Scenario >= 20 && Scen.ScenarioName[2] == 'G' || Scen.ScenarioName[2] == 'U') || Scen.ScenarioName[2] == 'A' +// || (Scen.ScenarioName[2] == 'M' && Scen.Scenario >= 25)) +// RequiredCD = 2; +// else if(Scen.ScenarioName[2] == 'U') +// RequiredCD = 1; +// else if(Scen.ScenarioName[2] == 'G') +// RequiredCD = 0; +// } +// +//#ifdef FIXIT_FORCE_CD +// Forces the CD to be inserted according to the scenario being loaded. +//Hide_Mouse(); +//VisiblePage.Clear(); +//Show_Mouse(); +//GamePalette.Set(); +//if (!Force_CD_Available(RequiredCD)) { +// Prog_End(); +// exit(EXIT_FAILURE); +//} +//#endif + + + +// } + Theme.Stop(); +// jmarshall - map scripts + if (Scen.mapScript) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Scen.mapScript->CallFunction("map_briefing"); + } + else if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.IntroMovie); + Play_Movie(Scen.BriefMovie); + } +// jmarshall end + /* + ** If there's no briefing movie, restate the mission at the beginning. + */ + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + if (Session.Type == GAME_NORMAL && ( Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available() ) ) { + /* + ** Make sure the mouse is visible before showing the restatement. + */ + while(Get_Mouse_State()) { + Show_Mouse(); + } + Restate_Mission(Scen.ScenarioName, TXT_OK, TXT_NONE); + } + + if (briefing) { + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.ActionMovie, Scen.TransitTheme); + } + + if (Scen.TransitTheme == THEME_NONE) { + Theme.Queue_Song(THEME_FIRST); + } + + /* + ** Set the options values, since the palette has been initialized by Read_Scenario + */ + Options.Set(); + + return(true); +} + +void Scenario_MapScriptFrame(void) { + if (!Scen.mapScript) + return; + + if (mapScriptPlayStart) { + Scen.mapScript->CallFunction("map_start"); + mapScriptPlayStart = false; + return; + } + + Scen.mapScript->CallFunction("map_frame"); +} + + +/*********************************************************************************************** + * Set_Scenario_Difficulty -- Sets the difficulty of the scenario. * + * * + * Updates the player's difficulty in single-player mode. * + * * + * INPUT: difficulty -- Scenario difficulty * + * * + * OUTPUT: none * + * * + * WARNINGS: Only works in single-player. * + * Must call Start_Scenario first to initialize the player. * + * * + * HISTORY: * + * 10/02/2019 SKY : Created. * + *=============================================================================================*/ +void Set_Scenario_Difficulty(int difficulty) +{ + if (Session.Type == GAME_NORMAL) { + switch (difficulty) { + case 0: + PlayerPtr->Assign_Handicap(DIFF_EASY); + break; + case 1: + PlayerPtr->Assign_Handicap(DIFF_NORMAL); + break; + case 2: + PlayerPtr->Assign_Handicap(DIFF_HARD); + break; + default: + break; + } + } +} + + +/*********************************************************************************************** + * Read_Scenario -- Reads a scenario from disk. * + * * + * This will read a scenario from disk. Use this to begin a scenario. * + * It doesn't perform any rendering, it merely sets up the system * + * with the proper data. Setting of the right game state will start * + * the scenario running. * + * * + * INPUT: root -- Scenario root filename * + * * + * OUTPUT: none * + * * + * WARNINGS: You must clear out the system variables before calling * + * this function. Use the Clear_Scenario() function. * + * It is assumed that Scenario is set to the current scenario number. * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 02/03/1992 JLB : Uses house identification. * + *=============================================================================================*/ +bool Read_Scenario(char * name) +{ + BStart(BENCH_SCENARIO); + Clear_Scenario(); + ScenarioInit++; + if (Read_Scenario_INI(name)) { +#ifdef FIXIT_CSII // ajw - Added runtime check for Aftermath to skirmish mode case. + bool readini = false; + switch(Session.Type) { + case GAME_NORMAL: + readini = false; + break; + case GAME_SKIRMISH: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#endif + break; + case GAME_INTERNET: +#ifndef FIXIT_VERSION_3 // Loading of Aftermath rules depends on bAftermathMultiplayer now. + if (Is_Mission_Counterstrike(name)) { + readini = false; // Don't allow AM units on a CS map in WChat + break; + } +#endif // ( Note lack of break; ) + default: +#ifdef FIXIT_VERSION_3 + readini = bAftermathMultiplayer; +#else + if (PlayingAgainstVersion >= VERSION_AFTERMATH_CS) { + readini = true; + } +#endif + break; + } + if(readini) { + /* + ** Find out if the CD in the current drive is the Aftermath disc. + */ +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != 3 ) { +#else + if(Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != 3) { +#endif + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + RequiredCD = 3; + if (!Force_CD_Available(RequiredCD)) { // force Aftermath CD in drive. +#ifndef WOLAPI_INTEGRATION + #ifdef WIN32 + if(Special.IsFromWChat || SpawnedFromWChat) { + char packet[10] = {"Hello"}; + Send_Data_To_DDE_Server (packet, strlen(packet), DDEServerClass::DDE_CONNECTION_FAILED); + } + #endif +#endif + Emergency_Exit(EXIT_FAILURE); + } + } + CCINIClass ini; + if (ini.Load(CCFileClass("MPLAYER.INI"), false)) { + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + } + } +#endif + Fill_In_Data(); + + // BL 2020-06-15 : not needed !! + //Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); + + } else { + +#if (1) + char message[200]; + if (name) { + sprintf(message, "Failed to load scenario %s", name); + GlyphX_Debug_Print(message); + } else { + GlyphX_Debug_Print("Failed to load scenario"); + } +#else + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); + WWMessageBox().Process(TXT_UNABLE_READ_SCENARIO); + Hide_Mouse(); +#endif + + BEnd(BENCH_SCENARIO); + return(false); + } + ScenarioInit--; + BEnd(BENCH_SCENARIO); + return(true); +} + + +/*********************************************************************************************** + * Fill_In_Data -- Recreate all data that is not loaded with scenario. * + * * + * This routine is called after the INI file for the scenario has been processed. It will * + * infer the game state from the scenario INI data. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + *=============================================================================================*/ +void Fill_In_Data(void) +{ + /* + ** The basic scenario data load does not contain the full set of + ** game data. We now must fill in the missing pieces. + */ + ScenarioInit++; + int index; + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Update_Buildables(); + } + + Map.Flag_To_Redraw(true); + + /* + ** Reset the movement zones according to the terrain passability. + */ + Map.Zone_Reset(MZONEF_ALL); + + +#ifdef WIN32 + /* + ** Since the sidebar starts up activated, adjust the home start position so that + ** the right edge of the map will still be visible. + */ + if (!Debug_Map) { + Map.SidebarClass::Activate(1); + if (Session.Type == GAME_NORMAL) { + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = Scen.Waypoint[WAYPT_HOME]; + Map.Set_Tactical_Position(Cell_Coord((Scen.Waypoint[WAYPT_HOME] - (MAP_CELL_W * 4 * RESFACTOR)) - (5*RESFACTOR))); + } + if (Session.Type == GAME_SKIRMISH) { + CELL PlayerCenterCell = XY_Cell( Coord_XCell(PlayerPtr->Center), Coord_YCell( PlayerPtr->Center) ); + Scen.Views[0] = Scen.Views[1] = Scen.Views[2] = Scen.Views[3] = PlayerCenterCell; + Map.Set_Tactical_Position( Cell_Coord( (PlayerCenterCell - (MAP_CELL_W * 4 * RESFACTOR) ) - (5 * RESFACTOR) ) ); + } + } +#endif + + /* + ** Handle any data resetting that can be safely inferred from the actual + ** data that has been loaded. + */ + /* + ** Distribute the trigger pointers to the appropriate working lists. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + + assert(tp != NULL); + + if (tp->Attaches_To() & ATTACH_MAP) { + MapTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_GENERAL) { + LogicTriggers.Add(Find_Or_Make(tp)); + } + if (tp->Attaches_To() & ATTACH_HOUSE) { + HouseTriggers[tp->House].Add(Find_Or_Make(tp)); + } + } + + ScenarioInit--; + + /* + ** Now go through and set all the cells ringing the map to be visible, so + ** we won't get the wall of shadow at the edge of the map. + */ + int x,y; + for (x = Map.MapCellX-1; x < ((Map.MapCellX + Map.MapCellWidth + 1)); x++) { + Map[XY_Cell(x, Map.MapCellY-1)].IsVisible = + Map[XY_Cell(x, Map.MapCellY-1)].IsMapped = true; + + Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsVisible = + Map[XY_Cell(x, Map.MapCellY+Map.MapCellHeight)].IsMapped = true; + } + for (y = Map.MapCellY; y < (Map.MapCellY + Map.MapCellHeight); y++) { + Map[XY_Cell(Map.MapCellX-1, y)].IsVisible = + Map[XY_Cell(Map.MapCellX-1, y)].IsMapped = true; + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsVisible = + Map[XY_Cell(Map.MapCellX+Map.MapCellWidth, y)].IsMapped = true; + } + + /* + ** If inheriting from a previous scenario was indicated, then create the carry over + ** objects at this time. + */ + if (Scen.IsToInherit) { + CarryoverClass * cptr = Carryover; + while (cptr != NULL) { + cptr->Create(); + cptr = (CarryoverClass *)cptr->Get_Next(); + } + } + + /* + ** The "allow win" action is a special case that is handled here. The total number + ** of triggers that have this action must be recorded. + */ + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypeClass * tp = TriggerTypes.Ptr(index); + if (tp->Action1.Action == TACTION_ALLOWWIN || + (tp->ActionControl != MULTI_ONLY && tp->Action2.Action == TACTION_ALLOWWIN)) { + + HouseClass::As_Pointer(tp->House)->Blockage++; + } + } + + /* + ** Move available money to silos, if the scenario flag so indicates. + */ + if (Scen.IsMoneyTiberium) { + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr != NULL) { + int tomove = hptr->Capacity - hptr->Tiberium; + hptr->Credits -= tomove; + hptr->Tiberium += tomove; + } + } + } + + /* + ** Count all non-destroyed bridges on the map. + */ + Scen.BridgeCount = Map.Intact_Bridge_Count(); + + Map.All_To_Look(PlayerPtr, true); +} + + +/*********************************************************************************************** + * Post_Load_Game -- Fill in an inferred data from the game state. * + * * + * This routine is typically called after a game has been loaded. Some working data lists * + * can be rebuild from the game state. This working data is rebuilt rather than being * + * stored with the game data file. * + * * + * INPUT: load_multi -- true if we're loading a multiplayer game * + * * + * OUTPUT: none * + * * + * WARNINGS: Although it is safe to call this routine whenever, it is only needed after a * + * game load. * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +void Post_Load_Game(int load_multi) +{ + // BL 2020-06-15 : not needed !! + //Map.Set_View_Dimensions(0, 0, Map.MapCellWidth, Map.MapCellHeight); + + // + // Do NOT call Overpass if we're loading a multiplayer game; it calls the + // random # generator, which throws the games out of sync if they were + // saved on different frame #'s. + // + if (!load_multi) { + Map.Overpass(); + } + Scen.BridgeCount = Map.Intact_Bridge_Count(); + Map.Zone_Reset(MZONEF_ALL); +} + + +/*********************************************************************************************** + * Clear_Scenario -- Clears all data in preparation for scenario load. * + * * + * This routine will clear out all data specific to a scenario in * + * preparation for a subsequent scenario data load. This will free * + * all units, animations, and icon maps. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/22/1991 : Created. * + * 03/21/1992 JLB : Changed buffer allocations, so changes memset code. * + * 07/13/1995 JLB : End count down moved here. * + *=============================================================================================*/ +void Clear_Scenario(void) +{ +// TCTCTC -- possibly just use in-place new of scenario object? + + Scen.MissionTimer = 0; + Scen.MissionTimer.Stop(); + Scen.Timer = 0; + Scen.ShroudTimer = 0; + Scen.IntroMovie = VQ_NONE; + Scen.BriefMovie = VQ_NONE; + Scen.WinMovie = VQ_NONE; + Scen.WinMovie2 = VQ_NONE; + Scen.WinMovie3 = VQ_NONE; + Scen.WinMovie4 = VQ_NONE; + Scen.LoseMovie = VQ_NONE; + Scen.ActionMovie = VQ_NONE; + Scen.IsNoSpyPlane = false; + Scen.IsTanyaEvac = false; + Scen.IsEndOfGame = false; + Scen.IsInheritTimer = false; + Scen.IsToCarryOver = false; + Scen.IsSkipScore = false; + Scen.IsOneTimeOnly = false; + Scen.IsTruckCrate = false; + Scen.IsMoneyTiberium = false; + Scen.IsNoMapSel = false; + Scen.CarryOverCap = 0; + Scen.CarryOverPercent = 0; + Scen.TransitTheme = THEME_NONE; + Scen.Percent = 0; + + memset(Scen.GlobalFlags, 0, sizeof(Scen.GlobalFlags)); + + MapTriggers.Clear(); + LogicTriggers.Clear(); + + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + HouseTriggers[house].Clear(); + } + + /* + ** Call everyone's Init routine, except the Map's; for the Map, only call + ** MapClass::Init, which clears the Cell array. The Display::Init requires + ** a Theater argument, and the theater is not known at this point; also, it + ** would reload MixFiles, which isn't desired. Display::Read_INI calls its + ** own Init, which will Init the entire Map hierarchy. + */ + Map.Init_Clear(); + Score.Init(); + Logic.Init(); + + HouseClass::Init(); + ObjectClass::Init(); + TeamTypeClass::Init(); + TeamClass::Init(); + TriggerClass::Init(); + TriggerTypeClass::Init(); + AircraftClass::Init(); + AnimClass::Init(); + BuildingClass::Init(); + BulletClass::Init(); + InfantryClass::Init(); + OverlayClass::Init(); + SmudgeClass::Init(); + TemplateClass::Init(); + TerrainClass::Init(); + UnitClass::Init(); + VesselClass::Init(); + + FactoryClass::Init(); + + Base.Init(); + + CurrentObject.Clear_All(); + + for (int index = 0; index < WAYPT_COUNT; index++) { + Scen.Waypoint[index] = -1; + } + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + bAutoSonarPulse = false; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + Scen.bLocalProposesDraw = false; + Scen.bOtherProposesDraw = false; +#endif + +} + + +/*********************************************************************************************** + * Do_Win -- Display winning congratulations. * + * * + * Perform the win the mission process. This will display any winning movies and the score * + * screen. Followed by the map selection screen and then the load of the new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + * 01/01/1995 JLB : Carries money forward into next scenario. * + *=============================================================================================*/ +void Do_Win(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ +#ifdef FIXIT_ANTS + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore || AntsEnabled) { +#else + if (Session.Type != GAME_NORMAL || !Scen.IsSkipScore ) { +#endif //FIXIT_ANTS + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Map.Flag_To_Redraw (true); + Map.Render(); +#ifdef WIN32 + Fancy_Text_Print(TXT_SCENARIO_WON, (SeenBuff.Get_Width() / 2), 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#else + Fancy_Text_Print(TXT_MISSION, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_HACKHACK, x, 110*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); +#endif + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) { + Device_Present(); + }; + Speak(VOX_ACCOMPLISHED); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + Device_Present(); + } + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); + Play_Movie(Scen.WinMovie); + Play_Movie(Scen.WinMovie2); + Play_Movie(Scen.WinMovie3); + Play_Movie(Scen.WinMovie4); + + Keyboard->Clear(); + + SaveTanya = IsTanyaDead; + Scen.CarryOverTimer = Scen.MissionTimer; +// int timer = Scen.MissionTimer; + + /* + ** Do the ending screens only if not playing back a recorded game. + */ + if (!Session.Play) { + /* + ** If the score presentation should be performed, then do + ** so now. + */ + Keyboard->Clear(); + if (!Scen.IsSkipScore) { + Score.Presentation(); + } + + + if (Scen.IsOneTimeOnly) { + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +// Mono_Printf("Scenario.cpp one time only antsenabled is false\n"); +#endif + return; + } + + /* + ** If this scenario is flagged as ending the game then print the credits and exit. + */ + if (Scen.IsEndOfGame) { + if (PlayerPtr->ActLike == HOUSE_USSR) { + Play_Movie(VQ_SOVFINAL); + } else { + Play_Movie(VQ_ALLYEND); + } + Show_Who_Was_Responsible(); + GameActive = false; + Show_Mouse(); +#ifdef FIXIT_ANTS + AntsEnabled = false; +#endif + return; + } + + /* + ** Hack section. If it's allied scenario 10, variation A, then skip the + ** score and map selection, don't increment scenario, and set it to + ** variation B. + */ + if (Scen.IsNoMapSel) { + // force it to play the second half of scenario 10 +#ifdef FIXIT_ANTS + if (AntsEnabled) { + char scenarioname[24]; + strcpy(scenarioname, Scen.ScenarioName); + char buf[10]; + Scen.Scenario++; + sprintf(buf, "%02d", Scen.Scenario); + memcpy(&scenarioname[3], buf, 2); + Scen.Set_Scenario_Name(scenarioname); + } else { + Scen.ScenarioName[6] = 'B'; + } + +#else + Scen.ScenarioName[6] = 'B'; +#endif + + } else { + Scen.Set_Scenario_Name(Map_Selection()); + } + + Keyboard->Clear(); + + UserInput.Process_Input(); + } + + Scen.CarryOverMoney = PlayerPtr->Credits; + + /* + ** If requested, record the scenario's objects in the carry over list + ** for possible use in a future scenario. + */ + if (Scen.IsToCarryOver) { + + /* + ** First delete any existing carry over list. Any old list will be + ** blasted over by the new list -- there is only one logic carryover + ** list to be maintained. + */ + while (Carryover) { + CarryoverClass * cptr = (CarryoverClass *)Carryover->Get_Next(); + Carryover->Remove(); + delete Carryover; + Carryover = cptr; + } + + /* + ** Record all objects, that are to be part of the carry over set, into + ** the carry over list. + */ + for (int building_index = 0; building_index < Buildings.Count(); building_index++) { + BuildingClass * building = Buildings.Ptr(building_index); + + if (building && !building->IsInLimbo && building->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(building); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + UnitClass * unit = Units.Ptr(unit_index); + + if (unit && !unit->IsInLimbo && unit->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(unit); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + InfantryClass * infantry = Infantry.Ptr(infantry_index); + + if (infantry && !infantry->IsInLimbo && infantry->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(infantry); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + VesselClass * vessel = Vessels.Ptr(vessel_index); + + if (vessel && !vessel->IsInLimbo && vessel->Strength > 0) { + CarryoverClass * cptr = new CarryoverClass(vessel); + + if (cptr) { + if (Carryover) { + cptr->Add_Tail(*Carryover); + } else { + Carryover = cptr; + } + } + } + } + } + + /* + ** Generate a new scenario filename + */ +// Scen.Set_Scenario_Name(Scen.Scenario, Scen.ScenPlayer, Scen.ScenDir, Scen.ScenVar); + Start_Scenario(Scen.ScenarioName); + + /* + ** If the mission timer is to be inheriteded from the previous scenario then do it now. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + +// PlayerPtr->NukePieces = nukes; + + Map.Render(); + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); +// Fade_Palette_To(GamePalette, FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + + +/*********************************************************************************************** + * Do_Lose -- Display losing comments. * + * * + * Performs the lose mission processing. This will generally display a "would you like * + * to replay" dialog and then either reload the scenario or set flags such that the main * + * menu will appear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/05/1992 JLB : Created. * + *=============================================================================================*/ +void Do_Lose(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_SCENARIO_LOST, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_FAIL); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (Session.Type != GAME_NORMAL) { + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); + return; + } + + Hide_Mouse(); + VisiblePage.Clear(); + Show_Mouse(); +#ifdef CHEAT_KEYS +// Mono_Printf("Trying to play lose movie\n"); +#endif //CHEAT_KEYS + Play_Movie(Scen.LoseMovie); + + /* + ** Start same scenario again + */ + GamePalette.Set(); + Show_Mouse(); + if (!Session.Play && !WWMessageBox().Process(TXT_TO_REPLAY, TXT_YES, TXT_NO)) { + Hide_Mouse(); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + Map.Render(); + } else { + Hide_Mouse(); + GameActive = 0; + } + + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + Show_Mouse(); +} + +#ifdef FIXIT_VERSION_3 // Stalemate games. +/*********************************************************************************************** + * Do_Draw -- Parallels Do_Win and Do_Lose, for multiplayer games that end in a draw. + *=============================================================================================*/ +void Do_Draw(void) +{ + Map.Set_Default_Mouse(MOUSE_NORMAL); + Hide_Mouse(); + + Theme.Queue_Song(THEME_QUIET); + + /* + ** If this is a multiplayer game, clear the game's name so we won't respond + ** to game queries any more (in Call_Back) + */ + if (Session.Type != GAME_NORMAL) { + Session.GameName[0] = 0; + } + + /* + ** Determine a cosmetic center point for the text. + */ + int x = Map.TacPixelX + (Lepton_To_Pixel(Map.TacLeptonWidth)/2); + + /* + ** Announce win to player. + */ + Set_Logic_Page(SeenBuff); + Fancy_Text_Print(TXT_WOL_DRAW, x, 90*RESFACTOR, &ColorRemaps[PCOLOR_RED], TBLACK, TPF_CENTER|TPF_VCR|TPF_USE_GRAD_PAL|TPF_DROPSHADOW); + CountDownTimer = TIMER_SECOND * 3; + while (Is_Speaking()) {}; + Speak(VOX_CONTROL_EXIT); + while (CountDownTimer || Is_Speaking()) { + Call_Back(); + } + + /* + ** Stop here if this is a multiplayer game. + */ + if (!Session.Play) { + Session.GamesPlayed++; + Multi_Score_Presentation(); + Session.CurGame++; + if (Session.CurGame >= MAX_MULTI_GAMES) { + Session.CurGame = MAX_MULTI_GAMES - 1; + } + } + GameActive = 0; + Show_Mouse(); +} +#endif + +/*********************************************************************************************** + * Do_Restart -- Handle the restart mission process. * + * * + * This routine is called in the main game loop when the mission must be restarted. This * + * routine will throw away the current game and reload the appropriate mission. The * + * game will "resume" at the start of the mission. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/24/1995 JLB : Created. * + *=============================================================================================*/ +void Do_Restart(void) +{ + /* + ** Start a timer going, before we restart the scenario + */ + CDTimerClass timer; + timer = TICKS_PER_SECOND * 4; + Theme.Queue_Song(THEME_QUIET); + + WWMessageBox().Process(TXT_RESTARTING, TXT_NONE); + + Map.Set_Default_Mouse(MOUSE_NORMAL); + Keyboard->Clear(); + Start_Scenario(Scen.ScenarioName, false); + + /* + ** Start the scenario timer with the carried over value if necessary. + */ + if (Scen.IsInheritTimer) { + Scen.MissionTimer = Scen.CarryOverTimer; + Scen.MissionTimer.Start(); + } + + /* + ** Make sure the message stays displayed for at least 1 second + */ + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + Map.Render(); +} + + +/*********************************************************************************************** + * Restate_Mission -- Handles restating the mission objective. * + * * + * This routine will display the mission objective (as text). It will also give the * + * option to redisplay the mission briefing video. * + * * + * INPUT: name -- The scenario name. This is the unique identifier for the scenario * + * briefing text as it appears in the "MISSION.INI" file. * + * * + * OUTPUT: Returns the response from the dialog. This will either be 1 if the video was * + * requested, or 0 if the return to game options button was selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/23/1995 JLB : Created. * + * 08/06/1995 JLB : Uses preloaded briefing text. * + *=============================================================================================*/ +bool Restate_Mission(char const * name, int button1, int button2) +{ + if (name) { + + bool brief = true; + char buffer[25]; + if (Scen.BriefMovie != VQ_NONE) { + sprintf(buffer, "%s.VQA", VQName[Scen.BriefMovie]); + } + + if (Scen.BriefMovie == VQ_NONE || !CCFileClass(buffer).Is_Available()) { + button2 = TXT_OK; + button1 = TXT_NONE; + brief = false; + } + + /* + ** If mission object text was found, then display it. + */ + if (strlen(Scen.BriefingText)) { + strcpy(_ShapeBuffer, Scen.BriefingText); + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + if (BGMessageBox(_ShapeBuffer, button2, button1)) { + return(true); + } + if (!brief) return(true); + return(false); + } + } + return(false); +} + + +#define BUTTON_1 1 +#define BUTTON_2 2 +#define BUTTON_3 3 +#define BUTTON_FLAG 0x8000 +int BGMessageBox(char const * msg, int btn1, int btn2) +{ +#define BUFFSIZE 511 + char buffer[BUFFSIZE]; + int retval; + bool process; // loop while true + KeyNumType input; // user input + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[3]; + BOOL display = 1; // display level + int realval[5]; + int morebutton = 3; // which button says "more": 2 or 3? + + const char * b1txt = Text_String(btn1); + const char * b2txt = Text_String(btn2); +#ifdef FRENCH + const char * b3txt = "SUITE"; +#else +#ifdef GERMAN + const char * b3txt = "MEHR"; +#else + const char * b3txt = "MORE"; +#endif +#endif + + const void *briefsnd = MFCD::Retrieve("BRIEFING.AUD"); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_TYPE]); + + /* + ** If the message won't be needing the 'more' button, get rid of it. + */ + if (strlen(msg) <= BUFFSIZE-1) { + b3txt = ""; + } + +#ifdef WIN32 + GraphicBufferClass seen_buff_save(VisiblePage.Get_Width(), VisiblePage.Get_Height(), (void*)NULL); +#endif + + /* + ** If there's no text for button one, zero it out. + */ + if (*b1txt == '\0') { + b1txt = b2txt; + b2txt = ""; + if(*b1txt == '\0') { + b1txt=0; + } + } + + /* + ** If there's no text for button two, zero it out. However, if there + ** is text for button three, move its text (always "MORE") to button two, + ** and set the morebutton flag to point to button two. Then, clear out + ** button 3. + */ + if (*b2txt == '\0') { + b2txt = 0; + if (*b3txt != '\0') { + b2txt = b3txt; + b3txt = ""; + morebutton = 1; + } + } + + /* + ** If there's no text for button three, zero it out. + */ + if (*b3txt == '\0') b3txt = 0; + + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + char b1char, b2char, b3char; // 1st char of each string + int bwidth, bheight; // button width and height + int numbuttons = 0; + if (b1txt) { + b1char = toupper(b1txt[0]); + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2; + bwidth = max((String_Pixel_Width(b1txt) + 8), 80); + if (b2txt) { + numbuttons = 2; + b2char = toupper(b2txt[0]); + bwidth = max(((int)String_Pixel_Width( b2txt ) + 8), bwidth); +// b1x = x + 10; // left side + + if (b3txt) { + numbuttons = 3; + b3char = toupper(b3txt[0]); + bwidth = max(((int)String_Pixel_Width( b3txt ) + 8), bwidth); + } + + } else { + numbuttons = 1; +// b1x = x + ((width - bwidth) >> 1); // centered + } + } + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + int buffend = BUFFSIZE-1; + strncpy(buffer, msg, BUFFSIZE-1); + /* + ** Scan through the string to see if it got clipped, and if so, we'll + ** trim it back to the last space so it'll clip on a word. + */ + if (strlen(buffer) != strlen(msg)) { + while (buffer[buffend] != ' ') buffend--; + buffer[buffend]=0; + } + Fancy_Text_Print(TXT_NONE, 0, 0, &ColorRemaps[PCOLOR_TYPE], TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL); + int width; + int height; + Format_Window_String(buffer, 300, width, height); + height += (numbuttons == 0) ? 30 : 60; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Other inits. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Initialize the button structures. All are initialized, even though one (or none) may + ** actually be added to the button list. + */ + TextButtonClass button1(BUTTON_1, b1txt, TPF_BUTTON, + x + ((numbuttons == 1) ? ((width - bwidth) >> 1) : 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button2(BUTTON_2, b2txt, TPF_BUTTON, + x + width - (bwidth + 10), y + height - (bheight + 5), bwidth); + + TextButtonClass button3(BUTTON_3, b3txt, TPF_BUTTON, + 0, y + height - (bheight + 5)); + button3.X = x + ((width - button3.Width) >> 1); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + if (numbuttons) { + buttonlist = &button1; + buttons[0] = &button1; + realval[0] = BUTTON_1; + if (numbuttons > 2) { + button3.Add(*buttonlist); + buttons[1] = &button3; + realval[1] = BUTTON_3; + button2.Add(*buttonlist); + buttons[2] = &button2; + realval[2] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } else if (numbuttons == 2) { + button2.Add(*buttonlist); + buttons[1] = &button2; + realval[1] = BUTTON_2; + buttons[curbutton]->Turn_On(); + } + } + + /* + ** Draw the dialog. + */ + Hide_Mouse(); + + Show_OldFrameBuffer(false); + + PaletteClass temp; +#ifdef WIN32 + char *filename = "SOVPAPER.PCX"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.PCX"; + } + Load_Title_Screen(filename, &HidPage, (unsigned char*)temp.Get_Data()); +#else + char *filename = "SOVPAPER.CPS"; + if (PlayerPtr->Class->House != HOUSE_USSR && PlayerPtr->Class->House != HOUSE_UKRAINE) { + filename = "ALIPAPER.CPS"; + } + Load_Uncompress(CCFileClass(filename), HidPage, HidPage, temp); +#endif + HidPage.Blit(SeenPage); + + #ifdef WIN32 + //VisiblePage.Blit(seen_buff_save); + #endif + + static unsigned char _scorepal[]={0,1,12,13,4,5,6,7,8,9,10,255,252,253,14,248}; + Set_Font_Palette(_scorepal); + temp.Set(FADE_PALETTE_MEDIUM, Call_Back); + + /* + ** Main Processing Loop. + */ + + int bufindex = 0; + + Keyboard->Clear(); + + Set_Font_Palette(_scorepal); + int xprint = x + 20; + int yprint = y + 25; + do { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + char bufprint[2]; + bufprint[1]=0; + bufprint[0] = buffer[bufindex]; + if (bufprint[0] == '\r' || bufprint[0] == '@') { + xprint = x + 20; + yprint += FontHeight + FontYSpacing; + + } else { + if (bufprint[0] != 20) { + SeenPage.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#ifdef WIN32 + seen_buff_save.Print(bufprint, xprint, yprint, TBLACK, TBLACK); +#endif + xprint += Char_Pixel_Width(bufprint[0]); + } + } + if (bufprint[0] == '\r' || bufprint[0] == '@') { +#ifdef WIN32 + Play_Sample(briefsnd, 255, Options.Normalize_Volume(135)); +#else + Play_Sample(briefsnd, 255, Options.Normalize_Volume(45)); +#endif + CDTimerClass cd; + cd = 5; + do { + Call_Back(); + } while(!Keyboard->Check() && cd); + } + } while (buffer[++bufindex]); + + Show_Mouse(); + Keyboard->Clear(); + + if (buttonlist) { + process = true; + pressed = false; + while (process) { + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored = false; + Hide_Mouse(); + seen_buff_save.Blit(VisiblePage); + display = true; + Show_Mouse(); + } + #endif + + { + display = false; + + Hide_Mouse(); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + switch (input) { + case (BUTTON_1|BUTTON_FLAG): + selection = realval[0]; + pressed = true; + break; + + case (KN_ESC): + if (numbuttons > 2) { + selection = realval[1]; + pressed = true; + } else { + selection = realval[2]; + pressed = true; + } + break; + + case (BUTTON_2|BUTTON_FLAG): + selection = BUTTON_2; + pressed = true; + break; + + case (BUTTON_3|BUTTON_FLAG): + selection = realval[1]; + pressed = true; + break; + + case (KN_LEFT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < 0) { + curbutton = numbuttons - 1; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (numbuttons > 1) { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (numbuttons - 1) ) { + curbutton = 0; + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_1; + pressed = true; + break; + + /* + ** Check 'input' to see if it's the 1st char of button text + */ + default: + if (b1char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_1; + pressed = true; + } else if (b2txt!=NULL && + b2char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_2; + pressed = true; + } else if (b3txt!=NULL && + b3char == toupper(Keyboard->To_ASCII((KeyNumType)(input & 0xFF)))) { + selection = BUTTON_3; + pressed = true; + } + break; + } + + if (pressed) { + switch (selection) { + case (BUTTON_1): + retval = 1; + process = false; + break; + + case (BUTTON_2): + retval = 0; + process = false; + break; + + case BUTTON_3: + retval = 2; + process = false; + break; + } + + pressed = false; + } + } + + } else { + + Keyboard->Clear(); + } + + if (retval == (morebutton-1) && strlen(msg) > BUFFSIZE-1) { + retval = BGMessageBox(msg + buffend + 1, btn1, btn2); + } + /* + ** Restore the screen. + */ + Hide_Mouse(); + /* + ** Now set the palette, depending on if we're going to show the video or + ** go back to the main menu. + */ + switch (retval) { + case 0: +// BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); +// SeenPage.Clear(); +//// CCPalette.Set(); +// break; + case 1: + BlackPalette.Set(FADE_PALETTE_MEDIUM, Call_Back); + SeenPage.Clear(); + break; + default: + break; + } + Show_Mouse(); + + GadgetClass::Set_Color_Scheme(&ColorRemaps[PCOLOR_DIALOG_BLUE]); + + Show_OldFrameBuffer(true); + + return(retval); +} + + +/*********************************************************************************************** + * Set_Scenario_Name -- Creates the INI scenario name string. * + * * + * This routine is used by the scenario loading and saving code. It generates the scenario * + * INI root file name for the specified scenario parameters. * + * * + * INPUT: * + * buf buffer to store filename in; must be long enough for root.ext * + * scenario scenario number * + * player player type for this game (GDI, NOD, multi-player, ...) * + * dir directional parameter for this game (East/West) * + * var variation of this game (Lose, A/B/C/D, etc) * + * * + * OUTPUT: none. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + * 05/01/1995 BRR : 2-player scenarios use same names as multiplayer * + *=============================================================================================*/ +void ScenarioClass::Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir, ScenarioVarType var) +{ + Scenario = scenario; +// ScenPlayer = player; +// ScenDir = dir; +// ScenVar = var; + + char c_player; // character representing player type + char c_dir; // character representing direction type + char c_var; // character representing variation type + ScenarioVarType i; + char fname[_MAX_FNAME+_MAX_EXT]; + + /* + ** Set the player-type value. + */ + switch (player) { + case SCEN_PLAYER_SPAIN: + c_player = HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix; + break; + + case SCEN_PLAYER_GREECE: + c_player = HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix; + break; + + case SCEN_PLAYER_USSR: + c_player = HouseTypeClass::As_Reference(HOUSE_USSR).Prefix; + break; + + case SCEN_PLAYER_JP: + c_player = HouseTypeClass::As_Reference(HOUSE_JP).Prefix; + break; + + /* + ** Multi player scenario. + */ + default: + c_player = HouseTypeClass::As_Reference(HOUSE_MULTI1).Prefix; + break; + } + + /* + ** Set the directional character value. + ** If SCEN_DIR_NONE is specified, randomly pick a direction; otherwise, use 'E' or 'W' + */ + switch (dir) { + case SCEN_DIR_EAST: + c_dir = 'E'; + break; + + case SCEN_DIR_WEST: + c_dir = 'W'; + break; + + default: + case SCEN_DIR_NONE: + c_dir = Percent_Chance(50) ? 'W' : 'E'; + break; + } + + /* + ** Set the variation value. + */ + if (var == SCEN_VAR_NONE) { + + /* + ** Find which variations are available for this scenario + */ + for (i = SCEN_VAR_FIRST; i < SCEN_VAR_COUNT; i++) { + sprintf(fname, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, 'A' + i); + if (!CCFileClass(fname).Is_Available()) { + break; + } + } + + if (i==SCEN_VAR_FIRST) { + c_var = 'X'; // indicates an error + } else { + c_var = 'A' + Random_Pick(0, i-1); +// ScenVar = (ScenarioVarType)i; + } + } else { + switch (var) { + case SCEN_VAR_A: + c_var = 'A'; + break; + + case SCEN_VAR_B: + c_var = 'B'; + break; + + case SCEN_VAR_C: + c_var = 'C'; + break; + + case SCEN_VAR_D: + c_var = 'D'; + break; + + default: + c_var = 'L'; + break; + + } + } + + /* + ** generate the filename + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +//Mono_Printf("In set_scenario_name, scenario # = %d\n",scenario);Keyboard->Get();Keyboard->Get(); + if (scenario < 100) { + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); + } else { + char first = (scenario / 36) + 'A'; + char second = scenario % 36; + + if (second < 10) { + second += '0'; + } else { + second = (second - 10) + 'A'; + } + + sprintf(ScenarioName, "SC%c%c%c%c%c.INI", c_player, first, second, c_dir, c_var); + } +#else + sprintf(ScenarioName, "SC%c%02d%c%c.INI", c_player, scenario, c_dir, c_var); +#endif +} + + +void ScenarioClass::Set_Scenario_Name(char const * name) +{ + if (name != NULL) { + strncpy(ScenarioName, name, sizeof(ScenarioName)); + ScenarioName[ARRAY_SIZE(ScenarioName)-1] = '\0'; + + char buf[3]; + memcpy(buf, &ScenarioName[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (buf[0] > '9' || buf[1] > '9') { + char first = buf[0]; + char second = buf[1]; + if (first <= '9') { + first -= '0'; + } else { + first -= 'A'; + } + + if (second <= '9') { + second -= '0'; + } else { + second = (second - 'A') + 10; + } + + Scenario = (36 * first) + second; + } else { + Scenario = atoi(buf); + } +#else + Scenario = atoi(buf); +#endif + } +} + + + +/*********************************************************************************************** + * Read_Scenario_INI -- Read specified scenario INI file. * + * * + * Read in the scenario INI file. This routine only sets the game * + * globals with that data that is explicitly defined in the INI file. * + * The remaining necessary interpolated data is generated elsewhere. * + * * + * INPUT: * + * root root filename for scenario file to read * + * * + * fresh true = should the current scenario be cleared? * + * * + * OUTPUT: bool; Was the scenario read successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. V.Grippi added CS check 2/5/97 * + *=============================================================================================*/ +bool Read_Scenario_INI(char * fname, bool ) +{ +// char fname[_MAX_FNAME+_MAX_EXT]; // full INI filename + + + ScenarioInit++; + + Clear_Scenario(); +#ifdef OBSOLETE + /* + ** If we are not dealing with scenario 1, or a multi player scenario + ** then make sure the correct disk is in the drive. + */ + if (RequiredCD != -2) { + RequiredCD = -1; + } +#endif + + /* + ** Only force a CD check if this is a single player game or if its + ** a multiplayer game on an official scenario. If its non-official + ** (a user scenario) then we dont care which CD is in because the + ** scenario is stored locally on the hard drive. In this case, we + ** have already verified its existance. ST 3/1/97 4:52PM. + */ +#ifdef FIXIT_VERSION_3 // Avoid CD check if official scenario was downloaded. + if( ( Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial ) && _stricmp( Scen.ScenarioName, "download.tmp" ) ){ +#else + if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial){ +#endif + + /* + ** If this is scenario 1 then it should be on all CDs unless its an ant scenario + */ + if (Scen.Scenario == 1 && Scen.ScenarioName[2] != 'A') { + RequiredCD = -1; + } else { +// Mono_Printf("Read_SCen_INI scenario is: %s\n", Scen.ScenarioName); + /* + ** If this is a multiplayer scenario we need to find out if its a counterstrike + ** scenario. If so then we need CD 2. The original multiplayer scenarios are on + ** all CDs. + */ + if (Session.Type != GAME_NORMAL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + RequiredCD = -1; // default that any CD will do. +// If it's a counterstrike mission, require the counterstrike CD, unless the +// Aftermath CD is already in the drive, in which case, leave it there. +// Note, this works because this section only tests for multiplayer scenarios. + if (Is_Mission_Counterstrike(Scen.ScenarioName)) { + RequiredCD = 2; + if( Is_Aftermath_Installed() || Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) == 3 ) + { + RequiredCD = 3; + } + } + if(Is_Mission_Aftermath(Scen.ScenarioName)) { + RequiredCD = 3; + } +#else + if (Scen.Scenario > 24) { + RequiredCD = 2; + } else { + RequiredCD = -1; + } +#endif + } else { + + /* + ** This is a solo game. If the scenario number is >= 20 or its an ant mission + ** then we need the counterstrike CD (2) + */ + if (Scen.Scenario >= 20 || Scen.ScenarioName[2] == 'A') { + RequiredCD = 2; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Scen.Scenario >= 36 && Scen.ScenarioName[2] != 'A') { + RequiredCD = 3; +#ifdef BOGUSCD + RequiredCD = -1; +#endif + } +#endif + } else { + + /* + ** This is a solo mission from the original Red Alert. Choose the Soviet or + ** allied CD depending on the scenario name. + */ + if (Scen.ScenarioName[2] == 'U') { + RequiredCD = 1; + } else { + if (Scen.ScenarioName[2] == 'G') { +// Mono_Printf("We are setting REquiredCD to 0"); + RequiredCD = 0; + + } + } + } + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're asking for a CD swap, check to see if we need to set the palette +// to avoid a black screen. If this is a normal RA game, and the CD being +// requested is an RA CD, then don't set the palette, leave the map screen up. + +#ifdef FIXIT_VERSION_3 + int cd_index = Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60); + if( !( Using_DVD() && cd_index == 5 ) && cd_index != RequiredCD ) { +#else + if (Get_CD_Index(CCFileClass::Get_CD_Drive(), 1*60) != RequiredCD) { +#endif + if ((RequiredCD == 0 || RequiredCD == 1) && Session.Type == GAME_NORMAL) { + SeenPage.Clear(); + } + GamePalette.Set(FADE_PALETTE_FAST, Call_Back); + } +#endif + if (!Force_CD_Available(RequiredCD)) { + Prog_End("Read_Scenario_INI Force_CD_Available failed", true); + Emergency_Exit(EXIT_FAILURE); + } + } else { + /* + ** This is a user scenario so any old CD will do. + */ + RequiredCD = -1; + } + + + /* + ** Create scenario filename and read the file. + */ +// sprintf(fname, "%s.INI", root); + CCINIClass ini; + CCFileClass file(fname); +// file.Cache(); + + int result = ini.Load(file, true); + if (result == 0) { +// Mono_Printf("ini.Load failed"); + GlyphX_Debug_Print("Failed to load scenario file"); + GlyphX_Debug_Print(fname); + return(false); + } + + GlyphX_Debug_Print("Loaded scenario file"); + GlyphX_Debug_Print(file); + + /* + ** If the scenario digest is wrong then the return code will be a 2. + */ + if (result == 2) { +// if (Session.Type == GAME_NORMAL || Session.ScenarioIsOfficial) { + /* + ** Make a special exception so that multiplayer maps from 1 through + ** 24 will not care if the message digest is in error. All other + ** maps will abort the scenario load. + */ + if (Scen.ScenarioName[2] != 'M' || Scen.Scenario >= 25) { +#if (1) + GlyphX_Debug_Print("Scenario digest is wrong"); +#else + GamePalette.Set(); + WWMessageBox().Process(TXT_SCENARIO_ERROR, TXT_OK); +#endif +#ifdef RELEASE_VERSION + return(false); +#endif + } +// } + } + + /* + ** Reset the rules values to their initial settings. + */ +#ifdef FIXIT_NAME_OVERRIDE + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameOverride[index] != NULL) free((void*)NameOverride[index]); + NameOverride[index] = NULL; + NameIDOverride[index] = 0; + } + if (Session.Type == GAME_NORMAL) { + Special.IsShadowGrow = false; + } +#endif + +#ifdef FIXIT_ANTS + Session.Messages.Reset(); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); +// Session.Messages.Add_Message(NULL, 0, NULL, PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, 1); + WeaponTypeClass::As_Pointer(WEAPON_FLAMER)->Sound = VOC_NONE; + InfantryTypeClass::As_Reference(INFANTRY_THIEF).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_E4).IsDoubleOwned = false; + InfantryTypeClass::As_Reference(INFANTRY_SPY).PrimaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_SPY).SecondaryWeapon = NULL; + InfantryTypeClass::As_Reference(INFANTRY_GENERAL).IsBomber = false; + UnitTypeClass::As_Reference(UNIT_HARVESTER).IsExploding = false; + UnitTypeClass::As_Reference(UNIT_ANT1).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT2).Level = -1; + UnitTypeClass::As_Reference(UNIT_ANT3).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_QUEEN).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA1).Level = -1; + BuildingTypeClass::As_Reference(STRUCT_LARVA2).Level = -1; +#endif + + + Rule.General(RuleINI); + Rule.Recharge(RuleINI); + Rule.AI(RuleINI); + Rule.Powerups(RuleINI); + Rule.Land_Types(RuleINI); + Rule.Themes(RuleINI); + Rule.IQ(RuleINI); + Rule.Objects(RuleINI); + Rule.Difficulty(RuleINI); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Except does this _change_ any rules, or just add to them? - Just adds. + Rule.General(AftermathINI); + Rule.Recharge(AftermathINI); + Rule.AI(AftermathINI); + Rule.Powerups(AftermathINI); + Rule.Land_Types(AftermathINI); + Rule.Themes(AftermathINI); + Rule.IQ(AftermathINI); + Rule.Objects(AftermathINI); + Rule.Difficulty(AftermathINI); +#endif + + /* + ** For civilians, remove the graphics name override from the base rules (can still be overridden in scenario-specific INI). + */ + for (int iindex = 0; iindex < InfantryTypes.Count(); iindex++) { + InfantryTypeClass* itype = InfantryTypes.Ptr(iindex); + if (itype->IsCivilian) { + itype->GraphicName[0] = '\0'; + } + } + + /* + ** Override any rules values specified in this + ** particular scenario file. + */ + Rule.General(ini); + Rule.Recharge(ini); + Rule.AI(ini); + Rule.Powerups(ini); + Rule.Land_Types(ini); + Rule.Themes(ini); + Rule.IQ(ini); + Rule.Objects(ini); + Rule.Difficulty(ini); + + /* + ** Fix a legacy bug with England and France country bonuses + */ + if (Session.Type != GAME_NORMAL) { + HouseTypeClass::As_Reference(HOUSE_ENGLAND).ArmorBias = fixed(9, 10); + HouseTypeClass::As_Reference(HOUSE_FRANCE).ROFBias = fixed(9, 10); + } + + /* + ** Init the Scenario CRC value + */ + ScenarioCRC = 0; +#ifdef TOFIX + len = strlen(buffer); + for (int i = 0; i < len; i++) { + val = (unsigned char)buffer[i]; + Add_CRC(&ScenarioCRC, (unsigned long)val); + } +#endif + + /* + ** Fetch the appropriate movie names from the INI file. + */ + const char * const BASIC = "Basic"; + ini.Get_String(BASIC, "Name", "", Scen.Description, sizeof(Scen.Description)); + Scen.IntroMovie = ini.Get_VQType(BASIC, "Intro", Scen.IntroMovie); + Scen.BriefMovie = ini.Get_VQType(BASIC, "Brief", Scen.BriefMovie); + Scen.WinMovie = ini.Get_VQType(BASIC, "Win", Scen.WinMovie); + Scen.WinMovie2 = ini.Get_VQType(BASIC, "Win2", Scen.WinMovie2); + Scen.WinMovie3 = ini.Get_VQType(BASIC, "Win3", Scen.WinMovie3); + Scen.WinMovie4 = ini.Get_VQType(BASIC, "Win4", Scen.WinMovie4); + Scen.LoseMovie = ini.Get_VQType(BASIC, "Lose", Scen.LoseMovie); + Scen.ActionMovie = ini.Get_VQType(BASIC, "Action", Scen.ActionMovie); + Scen.IsToCarryOver = ini.Get_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + Scen.IsToInherit = ini.Get_Bool(BASIC, "ToInherit", Scen.IsToInherit); + Scen.IsInheritTimer = ini.Get_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + Scen.IsEndOfGame = ini.Get_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + Scen.IsTanyaEvac = ini.Get_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + Scen.TransitTheme = ini.Get_ThemeType(BASIC, "Theme", THEME_NONE); + NewINIFormat = ini.Get_Int(BASIC, "NewINIFormat", 0); + Scen.CarryOverPercent = ini.Get_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + Scen.CarryOverPercent = Saturate(Scen.CarryOverPercent, 1); + Scen.CarryOverCap = ini.Get_Int(BASIC, "CarryOverCap", Scen.CarryOverCap); + Scen.IsNoSpyPlane = ini.Get_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + Scen.IsSkipScore = ini.Get_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + Scen.IsOneTimeOnly = ini.Get_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + Scen.IsNoMapSel = ini.Get_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + Scen.IsTruckCrate = ini.Get_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + Scen.IsMoneyTiberium = ini.Get_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + Scen.Percent = ini.Get_Int(BASIC, "Percent", Scen.Percent); + + /* + ** Read in the specific information for each of the house types. This creates + ** the houses of different types. + */ + HouseClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the team-type data. The team types must be created before any + ** triggers can be created. + */ + TeamTypeClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the trigger data. The triggers must be created before any other + ** objects can be initialized. + */ + TriggerTypeClass::Read_INI(ini); + Call_Back(); + + + /* + ** Read in the map control values. This includes dimensions + ** as well as theater information. + */ + Map.Read_INI(ini); + Call_Back(); + + /* + ** Assign PlayerPtr by reading the player's house from the INI; + ** Must be done before any TechnoClass objects are created. + */ + if (Session.Type == GAME_NORMAL) { + PlayerPtr = HouseClass::As_Pointer(ini.Get_HousesType(BASIC, "Player", HOUSE_GREECE)); + PlayerPtr->Assign_Handicap(Scen.Difficulty); + int carryover; + if (Scen.CarryOverCap != -1) { + carryover = min(Scen.CarryOverMoney * Scen.CarryOverPercent, Scen.CarryOverCap); + } else { + carryover = Scen.CarryOverMoney * Scen.CarryOverPercent; + } + PlayerPtr->Credits += carryover; + PlayerPtr->Control.InitialCredits += carryover; + } else { +// jmarshall - Thanks Iran and OmniBlade from the Choronoshift Discord! +// This fixes ai building player controlled factions. + Assign_Houses(); + //GlyphX_Assign_Houses(); +// jmarshall end + } + PlayerPtr->IsHuman = true; + PlayerPtr->IsPlayerControl = true; + +// if (NewINIFormat < 2 || !ini.Is_Present("MapPack")) { +// Map.Read_Binary(root, &ScenarioCRC); +// } + + + /* + ** Read in and place the 3D terrain objects. + */ + TerrainClass::Read_INI(ini); + Call_Back(); + /* + ** Read in and place the units (all sides). + */ + UnitClass::Read_INI(ini); + Call_Back(); + + VesselClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in and place the infantry units (all sides). + */ + InfantryClass::Read_INI(ini); + Call_Back(); + + + + /* + ** Read in and place all the buildings on the map. + */ + BuildingClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in the AI's base information. + */ + Base.Read_INI(ini); + Call_Back(); + + /* + ** Read in any normal overlay objects. + */ + OverlayClass::Read_INI(ini); + Call_Back(); + + /* + ** Read in any smudge overlays. + */ + SmudgeClass::Read_INI(ini); + Call_Back(); + + /* Moved above ini.Get_TextBlock(...) so Xlat mission.ini could be loaded + ** If the briefing text could not be found in the INI file, then search + ** the mission.ini file. VG 10/17/96 + */ + INIClass mini; + mini.Load(CCFileClass("MISSION.INI")); + mini.Get_TextBlock(fname, Scen.BriefingText, sizeof(Scen.BriefingText)); + + /* + ** Read in any briefing text. + */ + if (Scen.BriefingText[0] == '\0') { + ini.Get_TextBlock("Briefing", Scen.BriefingText, sizeof(Scen.BriefingText)); + } + /* + ** Perform a final overpass of the map. This handles smoothing of certain + ** types of terrain (tiberium). + */ + Map.Overpass(); + Call_Back(); + + /* + ** Special cases: + ** Gold Rush multiplayer map cell 9033 - LAND_ROCK + ** USSR mission 13 - fixup trigger action + ** Allied mission 9A - fail mission if tech center is destroyed before being spied + ** Aftermath: Brother in Arms - have transports move to separate waypoints + ** Aftermath: Let's Make a Steal - Make the pillboxes un-capturable + ** Counterstrike: Soviet Soldier Volkov and Chitzkoi / Deus Ex Machina - Sniper burst fix + */ + if (_stricmp(Scen.ScenarioName, "scmh8ea.ini") == 0) { + Map[(CELL)9033].Override_Land_Type(LAND_ROCK); + } + + if (_stricmp(Scen.ScenarioName, "scu13ea.ini") == 0) { + TriggerTypeClass* trigger = TriggerTypes.Ptr(11); + trigger->Action1.Trigger.Set_Raw(39); + } + + if (_stricmp(Scen.ScenarioName, "scg09ea.ini") == 0) { + TriggerTypeClass* spyd_trigger = TriggerTypeClass::From_Name("Spyd"); + assert(spyd_trigger != NULL); + + TriggerTypeClass* kos_trigger = new TriggerTypeClass(); + kos_trigger->IsPersistant = spyd_trigger->IsPersistant; + kos_trigger->House = spyd_trigger->House; + kos_trigger->EventControl = MULTI_LINKED; + kos_trigger->ActionControl = MULTI_ONLY; + kos_trigger->Event1.Event = spyd_trigger->Event1.Event; + kos_trigger->Event1.Data = spyd_trigger->Event1.Data; + kos_trigger->Event2.Event = TEVENT_DESTROYED; + kos_trigger->Event2.Data.Value = 0; + kos_trigger->Action1.Action = TACTION_SET_GLOBAL; + kos_trigger->Action1.Data.Value = 22; + kos_trigger->Action2.Action = TACTION_SET_GLOBAL; + kos_trigger->Action2.Data.Value = 23; + + spyd_trigger->Event1.Event = TEVENT_GLOBAL_SET; + spyd_trigger->Event1.Data.Value = 22; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass* building = Buildings.Ptr(index); + if (building->Trigger.Is_Valid() && (building->Trigger->Class == spyd_trigger)) { + building->Attach_Trigger(Find_Or_Make(kos_trigger)); + } + } + + TriggerTypeClass* los3_trigger = new TriggerTypeClass(); + los3_trigger->IsPersistant = spyd_trigger->IsPersistant; + los3_trigger->House = spyd_trigger->House; + los3_trigger->EventControl = MULTI_AND; + los3_trigger->ActionControl = MULTI_AND; + los3_trigger->Event1.Event = TEVENT_GLOBAL_SET; + los3_trigger->Event1.Data.Value = 23; + los3_trigger->Event2.Event = TEVENT_GLOBAL_CLEAR; + los3_trigger->Event2.Data.Value = 22; + los3_trigger->Action1.Action = TACTION_LOSE; + los3_trigger->Action1.Data.Value = -255; + los3_trigger->Action2.Action = TACTION_TEXT_TRIGGER; + los3_trigger->Action2.Data.Value = 54; + } + + if (_stricmp(Scen.ScenarioName, "scu46ea.ini") == 0) { + Scen.Waypoint[20] = 9915; + Scen.Waypoint[21] = 9919; + Map[Scen.Waypoint[20]].IsWaypoint = 1; + Map[Scen.Waypoint[21]].IsWaypoint = 1; + + TeamTypeClass* rnf1_team = TeamTypeClass::From_Name("rnf1"); + assert(rnf1_team != NULL); + rnf1_team->MissionList[0].Data.Value = 20; + + TeamTypeClass* rnf2_team = TeamTypeClass::From_Name("rnf2"); + assert(rnf2_team != NULL); + rnf2_team->MissionList[0].Data.Value = 21; + } + + if (_stricmp(Scen.ScenarioName, "scu42ea.ini") == 0) { + BuildingTypeClass::As_Reference(STRUCT_PILLBOX).IsCaptureable = false; + } + + if ((_stricmp(Scen.ScenarioName, "scu35ea.ini") == 0) || (_stricmp(Scen.ScenarioName, "scu47ea.ini") == 0)) { + WeaponTypeClass::As_Pointer(Weapon_From_Name("Sniper"))->Burst = 2; + } + + /* + ** Multi-player last-minute fixups: + ** - If computer players are disabled, remove all computer-owned houses + ** - If bases are disabled, create the scenario dynamically + ** - Remove any flag spot overlays lying around + ** - If capture-the-flag is enabled, assign flags to cells. + */ + if (Session.Type != GAME_NORMAL /*|| Scen.ScenPlayer == SCEN_PLAYER_2PLAYER || Scen.ScenPlayer == SCEN_PLAYER_MPLAYER*/) { + + /* + ** If Ghosts are disabled and we're not editing, remove computer players + ** (Must be done after all objects are read in from the INI) + */ + if ( (Session.Options.AIPlayers + Session.Players.Count() < Rule.MaxPlayers) && !Debug_Map) { + Remove_AI_Players(); + } + + /* + ** Units must be created for each house. If bases are ON, this routine + ** will create an MCV along with the units; otherwise, it will just create + ** a whole bunch of units. Session.Options.UnitCount is the total # of units + ** to create. + */ + if (!Debug_Map) { + int save_init = ScenarioInit; // turn ScenarioInit off + ScenarioInit = 0; + Create_Units(ini.Get_Bool("Basic", "Official", false)); + ScenarioInit = save_init; // turn ScenarioInit back on + } + + /* + ** Place crates if random crates are enabled for + ** this scenario. + */ + if (Session.Options.Goodies) { + int count = max(Rule.CrateMinimum, Session.NumPlayers); + count = min(count, Rule.CrateMaximum); + for (int index = 0; index < count; index++) { + Map.Place_Random_Crate(); + } + } + } + + Call_Back(); + + /* + ** Return with flag saying that the scenario file was read. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 - Added runtime check. + if( Is_Aftermath_Installed() ) + { + if (Session.Type == GAME_SKIRMISH || Session.Type == GAME_GLYPHX_MULTIPLAYER) { + bAftermathMultiplayer = NewUnitsEnabled = OverrideNewUnitsEnabled; + } + } +#endif + ScenarioInit--; + return(true); +} + + +/*********************************************************************************************** + * Write_Scenario_INI -- Write the scenario INI file. * + * * + * INPUT: * + * root root filename for the scenario * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/07/1992 JLB : Created. * + * 05/11/1995 JLB : Updates movie data. * + *=============================================================================================*/ +void Write_Scenario_INI(char * fname) +{ +#ifndef CHEAT_KEYS + fname = fname; +#else +// CCFileClass file(fname); + + CCINIClass ini; + + /* + ** Preload the old scenario if it is present because there may + ** be some fields in the INI that are processed but not written + ** out. Preloading the scenario will preserve these manually + ** maintained entries. + */ + if (CCFileClass(fname).Is_Available()) { + ini.Load(CCFileClass(fname), true); + } + + static char const * const BASIC = "Basic"; + ini.Clear(BASIC); + ini.Put_String(BASIC, "Name", Scen.Description); + ini.Put_VQType(BASIC, "Intro", Scen.IntroMovie); + ini.Put_VQType(BASIC, "Brief", Scen.BriefMovie); + ini.Put_VQType(BASIC, "Win", Scen.WinMovie); + ini.Put_VQType(BASIC, "Lose", Scen.LoseMovie); + ini.Put_VQType(BASIC, "Action", Scen.ActionMovie); + ini.Put_HousesType(BASIC, "Player", PlayerPtr->Class->House); + ini.Put_ThemeType(BASIC, "Theme", Scen.TransitTheme); + ini.Put_Fixed(BASIC, "CarryOverMoney", Scen.CarryOverPercent); + ini.Put_Bool(BASIC, "ToCarryOver", Scen.IsToCarryOver); + ini.Put_Bool(BASIC, "ToInherit", Scen.IsToInherit); + ini.Put_Bool(BASIC, "TimerInherit", Scen.IsInheritTimer); + ini.Put_Bool(BASIC, "CivEvac", Scen.IsTanyaEvac); + ini.Put_Int(BASIC, "NewINIFormat", 3); + ini.Put_Int(BASIC, "CarryOverCap", Scen.CarryOverCap/100); + ini.Put_Bool(BASIC, "EndOfGame", Scen.IsEndOfGame); + ini.Put_Bool(BASIC, "NoSpyPlane", Scen.IsNoSpyPlane); + ini.Put_Bool(BASIC, "SkipScore", Scen.IsSkipScore); + ini.Put_Bool(BASIC, "OneTimeOnly", Scen.IsOneTimeOnly); + ini.Put_Bool(BASIC, "SkipMapSelect", Scen.IsNoMapSel); + ini.Put_Bool(BASIC, "Official", true); + ini.Put_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); + ini.Put_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); + ini.Put_Int(BASIC, "Percent", Scen.Percent); + + HouseClass::Write_INI(ini); + TeamTypeClass::Write_INI(ini); + TriggerTypeClass::Write_INI(ini); + Map.Write_INI(ini); + TerrainClass::Write_INI(ini); + UnitClass::Write_INI(ini); + VesselClass::Write_INI(ini); + InfantryClass::Write_INI(ini); + BuildingClass::Write_INI(ini); + Base.Write_INI(ini); + OverlayClass::Write_INI(ini); + SmudgeClass::Write_INI(ini); + + if (strlen(Scen.BriefingText)) { + ini.Put_TextBlock("Briefing", Scen.BriefingText); + } +// sprintf(fname, "%s.INI", root); + RawFileClass rawfile(fname); + ini.Save(rawfile, true); +#endif +} + + +/*********************************************************************************************** + * Assign_Houses -- Assigns multiplayer houses to various players * + * * + * This routine assigns all players to a multiplayer house slot; it forms network connections * + * to each player. The Connection ID used is the value for that player's HousesType. * + * * + * PlayerPtr is also set here. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This routine assumes the 'Players' vector has been properly filled in with players' * + * names, addresses, color, etc. * + * Also, it's assumed that the HouseClass's have all been created & initialized. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + * 07/14/1995 JLB : Records name of player in house structure. * + *=============================================================================================*/ +void Assign_Houses(void) +{ + int assigned[MAX_PLAYERS]; + int color_used[8]; + int i,j; + HousesType house; + HouseClass * housep; + int lowest_color; + int index; + HousesType pref_house; + int color; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + for (i = 0; i < MAX_PLAYERS; i++) { + assigned[i] = 0; + color_used[i] = 0; + } + +// debugprint( "Assign_Houses()\n" ); + //------------------------------------------------------------------------ + // Assign each player in 'Players' to a multiplayer house. Players will + // be sorted by their chosen color value (this value must be unique among + // all the players). + //------------------------------------------------------------------------ + for (i = 0; i < Session.Players.Count(); i++) { + + //..................................................................... + // Find the player with the lowest color index + //..................................................................... + index = 0; + lowest_color = 255; + for (j = 0; j < Session.Players.Count(); j++) { + //.................................................................. + // If we've already assigned this house, skip it. + //.................................................................. + if (assigned[j]) { + continue; + } + if (Session.Players[j]->Player.Color < lowest_color) { + lowest_color = Session.Players[j]->Player.Color; + index = j; + } + } + + //..................................................................... + // Mark this player as having been assigned. + //..................................................................... + assigned[index] = 1; + color_used[Session.Players[index]->Player.Color] = 1; + + //..................................................................... + // Assign the lowest-color'd player to the next available slot in the + // HouseClass array. + //..................................................................... + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + memset((char *)housep->IniName, 0, MPLAYER_NAME_MAX); + strncpy((char *)housep->IniName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#ifdef WOLAPI_INTEGRATION + // Make another copy of name, permanent throughout entire game. + strncpy((char *)housep->InitialName, Session.Players[index]->Name, MPLAYER_NAME_MAX - 1); +#endif + housep->IsHuman = true; + housep->Init_Data((PlayerColorType)(Session.Players[index]->Player.Color), + Session.Players[index]->Player.House, Session.Options.Credits); + if (index == 0) { + PlayerPtr = housep; + } + /* + ** Convert the build level into an actual tech level to assign to the house. + ** There isn't a one-to-one correspondence. + */ + housep->Control.TechLevel = _build_tech[BuildLevel]; + + housep->Assign_Handicap(Scen.Difficulty); + + //..................................................................... + // Record where we placed this player + //..................................................................... + Session.Players[index]->Player.ID = house; + +// debugprint( "Assigned ID of %i to %s\n", house, Session.Players[index]->Name ); + } + + //------------------------------------------------------------------------ + // Now assign computer players to the remaining houses. + //------------------------------------------------------------------------ + for (i = Session.Players.Count(); i < Session.Players.Count() + Session.Options.AIPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (Percent_Chance(50)) { + pref_house = HOUSE_GREECE; + } else { + pref_house = HOUSE_USSR; + } + + //..................................................................... + // Pick a color for this house; keep looping until we find one. + //..................................................................... + while (1) { + color = Random_Pick(0, 7); + if (color_used[color] == false) { + break; + } + } + color_used[color] = true; + + //..................................................................... + // Set up the house + //..................................................................... +// housep->Control.MaxUnit = 80; +// housep->Control.MaxInfantry = 60; +// housep->Control.MaxBuilding = 60; +// housep->Control.MaxVessel = 60; + housep->IsHuman = false; + housep->IsStarted = true; + + strcpy(housep->IniName, Text_String(TXT_COMPUTER)); + + if (Session.Type != GAME_NORMAL) { + housep->IQ = Rule.MaxIQ; + } + + housep->Init_Data((PlayerColorType)color, pref_house, Session.Options.Credits); + housep->Control.TechLevel = _build_tech[BuildLevel]; +// housep->Control.TechLevel = BuildLevel; + + DiffType difficulty = Scen.CDifficulty; + + if (Session.Players.Count() > 1 && Rule.IsCompEasyBonus && difficulty > DIFF_EASY) { + difficulty = (DiffType)(difficulty - 1); + } + housep->Assign_Handicap(difficulty); + } + + for (i = Session.Players.Count()+Session.Options.AIPlayers; i < Rule.MaxPlayers; i++) { + house = (HousesType)(i + HOUSE_MULTI1); + housep = HouseClass::As_Pointer(house); + if (housep != NULL) { + housep->IsDefeated = true; + } + } +} + + +/*********************************************************************************************** + * Remove_AI_Players -- Removes the computer AI houses & their units * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static void Remove_AI_Players(void) +{ + int i; + int aicount = 0; + HousesType house; + HouseClass * housep; + + for (i = 0; i < MAX_PLAYERS; i++) { + house = (HousesType)(i + (int)HOUSE_MULTI1); + housep = HouseClass::As_Pointer (house); + if (housep->IsHuman == false) { + aicount++; + if(aicount > Session.Options.AIPlayers) { + housep->Clobber_All(); + } + } + } +} + + +#define USE_GLYPHX_START_LOCATIONS 1 + + +/*********************************************************************************************** + * Create_Units -- Creates infantry & units, for non-base multiplayer * + * * + * This routine uses data tables to determine which units to create for either * + * a GDI or NOD house, and how many of each. * + * * + * It also sets each house's FlagHome & FlagLocation to the Waypoint selected * + * as that house's "home" cell. * + * * + * INPUT: official -- Directs the placement logic to use the full set of waypoints rather * + * than biasing toward the first four. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +static int ReserveInfantryIndex = 0; +static void Reserve_Infantry() +{ + if (Infantry.Count() == Infantry.Length()) { + delete Infantry.Ptr(ReserveInfantryIndex); + ReserveInfantryIndex = (ReserveInfantryIndex + 1) % Infantry.Length(); + } +} + +static int ReserveUnitIndex = 0; +static void Reserve_Unit() +{ + if (Units.Count() == Units.Length()) { + delete Units.Ptr(ReserveUnitIndex); + ReserveUnitIndex = (ReserveUnitIndex + 1) % Units.Length(); + } +} + +static void Create_Units(bool official) +{ + static struct { + int MinLevel; + UnitType AllyType[2]; + UnitType SovietType[2]; + } utable[] = { + {4, {UNIT_MTANK2, UNIT_LTANK}, {UNIT_MTANK, UNIT_NONE}}, + {5, {UNIT_APC, UNIT_NONE}, {UNIT_V2_LAUNCHER, UNIT_NONE}}, + {8, {UNIT_ARTY, UNIT_JEEP}, {UNIT_MTANK, UNIT_NONE}}, + {10, {UNIT_MTANK2, UNIT_MTANK2}, {UNIT_HTANK, UNIT_NONE}} + }; + static int num_units[ARRAY_SIZE(utable)]; // # of each type of unit to create + int tot_units; // total # units to create + + static struct { + int MinLevel; + int AllyCount; + InfantryType AllyType; + int SovietCount; + InfantryType SovietType; + } itable[] = { + {0, 1,INFANTRY_E1, 1,INFANTRY_E1}, + {2, 1,INFANTRY_E3, 1,INFANTRY_E2}, + {4, 1,INFANTRY_E3, 1,INFANTRY_E4}, + +// removed because of bug B478 (inappropriate infantry given in a bases off scenario). +// {5, 1,INFANTRY_RENOVATOR, 1,INFANTRY_RENOVATOR}, +// {6, 1,INFANTRY_SPY, 1,INFANTRY_DOG}, +// {10, 1,INFANTRY_THIEF, 1,INFANTRY_DOG}, +// {12, 1,INFANTRY_MEDIC, 2,INFANTRY_DOG} + }; + static int num_infantry[ARRAY_SIZE(itable)];// # of each type of infantry to create + int tot_infantry; // total # infantry to create + + + CELL centroid; // centroid of this house's stuff + CELL centerpt; // centroid for a category of objects, as a CELL + + int u_limit=0; // last allowable index of units for this BuildLevel + int i_limit=0; // last allowable index of infantry for this BuildLevel + TechnoClass * obj; // newly-created object + int i,j,k; // loop counters + int scaleval; // value to scale # units or infantry + + ReserveInfantryIndex = ReserveUnitIndex = 0; + + /* + ** For the current BuildLevel, find the max allowable index into the tables + */ + for (i = 0; i < ARRAY_SIZE(utable); i++) { + if (PlayerPtr->Control.TechLevel >= utable[i].MinLevel) { + u_limit = i+1; + } + } + for (i = 0; i < ARRAY_SIZE(itable); i++) { + if (PlayerPtr->Control.TechLevel >= itable[i].MinLevel) { + i_limit = i+1; + } + } + + /* + ** Compute how many of each buildable category to create + */ + /* + ** Compute allowed # units + */ + tot_units = (Session.Options.UnitCount * 2) / 3; + if (u_limit == 0) tot_units = 0; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < u_limit; i++) { + num_units[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all units + */ + j = 0; + for (i = 0; i < tot_units; i++) { + num_units[j]++; + j++; + if (j >= u_limit) { + j = 0; + } + } + + /* + ** Compute allowed # infantry + */ + tot_infantry = Session.Options.UnitCount - tot_units; + + /* + ** Init # of each category to 0 + */ + for (i = 0; i < i_limit; i++) { + num_infantry[i] = 0; + } + + /* + ** Increment # of each category, until we've used up all infantry + */ + j = 0; + for (i = 0; i < tot_infantry; i++) { + num_infantry[j]++; + j++; + if (j >= i_limit) { + j = 0; + } + } + + /* + ** Build a list of the valid waypoints. This normally shouldn't be + ** necessary because the scenario level designer should have assigned + ** valid locations to the first N waypoints, but just in case, this + ** loop verifies that. + */ + + const unsigned int MAX_STORED_WAYPOINTS = 26; + + bool taken[MAX_STORED_WAYPOINTS]; + CELL waypts[MAX_STORED_WAYPOINTS]; + assert(Rule.MaxPlayers < ARRAY_SIZE(waypts)); + int num_waypts = 0; + + /* + ** Calculate the number of waypoints (as a minimum) that will be lifted from the + ** mission file. Bias this number so that only the first 4 waypoints are used + ** if there are 4 or fewer players. Unofficial maps will pick from all the + ** available waypoints. + */ + int look_for = max(4, Session.Players.Count()+Session.Options.AIPlayers); + if (!official) { + look_for = 8; + } + + + for (int waycount = 0; waycount < 26; waycount++) { +// for (int waycount = 0; waycount < max(4, Session.Players.Count()+Session.Options.AIPlayers); waycount++) { + if (Scen.Waypoint[waycount] != -1) { + waypts[num_waypts] = Scen.Waypoint[waycount]; + taken[num_waypts] = false; + num_waypts++; + + if (num_waypts >= MAX_STORED_WAYPOINTS) + { + break; + } + } + } + + /* + ** If there are insufficient waypoints to account for all players, then randomly assign + ** starting points until there is enough. + */ + int deficiency = look_for - num_waypts; +// int deficiency = (Session.Players.Count() + Session.Options.AIPlayers) - num_waypts; + if (deficiency > 0) { + for (int index = 0; index < deficiency; index++) { + CELL trycell = XY_Cell(Map.MapCellX + Random_Pick(0, Map.MapCellWidth-1), Map.MapCellY + Random_Pick(0, Map.MapCellHeight-1)); + + trycell = Map.Nearby_Location(trycell, SPEED_TRACK); + waypts[num_waypts] = trycell; + taken[num_waypts] = false; + num_waypts++; + } + } + + /* + ** Loop through all houses. Computer-controlled houses, with Session.Options.Bases + ** ON, are treated as though bases are OFF (since we have no base-building + ** AI logic.) + */ + int numtaken = 0; + for (HousesType house = HOUSE_MULTI1; house < (HOUSE_MULTI1 + Session.MaxPlayers); house++) { + + /* + ** Get a pointer to this house; if there is none, go to the next house + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + if (hptr == NULL) { + continue; + } + + /* + ** Pick the starting location for this house. The first house just picks + ** one of the valid locations at random. The other houses pick the furthest + ** wapoint from the existing houses. + */ + if (numtaken == 0) { + int pick = Random_Pick(0, num_waypts - 1); + centroid = waypts[pick]; + taken[pick] = true; + numtaken++; + } + else { + + /* + ** Set all waypoints to have a score of zero in preparation for giving + ** a distance score to all waypoints. + */ + int score[26]; + memset(score, '\0', sizeof(score)); + + /* + ** Scan through all waypoints and give a score as a value of the sum + ** of the distances from this waypoint to all taken waypoints. + */ + for (int index = 0; index < num_waypts; index++) { + + /* + ** If this waypoint has not already been taken, then accumulate the + ** sum of the distance between this waypoint and all other taken + ** waypoints. + */ + if (!taken[index]) { + for (int trypoint = 0; trypoint < num_waypts; trypoint++) { + + if (taken[trypoint]) { + score[index] += Distance(Cell_Coord(waypts[index]), Cell_Coord(waypts[trypoint])); + } + } + } + } + + /* + ** Now find the waypoint with the largest score. This waypoint is the one + ** that is furthest from all other taken waypoints. + */ + int best = 0; + int bestvalue = 0; + for (int searchindex = 0; searchindex < num_waypts; searchindex++) { + if (score[searchindex] > bestvalue || bestvalue == 0) { + bestvalue = score[searchindex]; + best = searchindex; + } + } + + /* + ** Assign this best position to the house. + */ + centroid = waypts[best]; + taken[best] = true; + numtaken++; + } + + /* + ** Assign the center of this house to the waypoint location. + */ + hptr->Center = Cell_Coord(centroid); + + /* + ** If Bases are ON, human & computer houses are treated differently + */ + if (Session.Options.Bases) { + + /* + ** - For a human-controlled house: + ** - Set 'scaleval' to 1 + ** - Create an MCV + ** - Attach a flag to it for capture-the-flag mode + */ + scaleval = 1; + Reserve_Unit(); + obj = new UnitClass (UNIT_MCV, house); + if (!obj->Unlimbo(Cell_Coord(centroid), DIR_N)) { + if (!Scan_Place_Object(obj, centroid)) { + delete obj; + obj = NULL; + } + } + if (obj != NULL) { + hptr->FlagHome = 0; + hptr->FlagLocation = 0; + if (Special.IsCaptureTheFlag) { + hptr->Flag_Attach((UnitClass *)obj, true); + } + } + } else { + + /* + ** If bases are OFF, set 'scaleval' to 1 & create a Mobile HQ for + ** capture-the-flag mode. + */ + scaleval = 1; +#ifdef TOFIX + if (Special.IsCaptureTheFlag) { + obj = new UnitClass (UNIT_TRUCK, house); + obj->Unlimbo(Cell_Coord(centroid), DIR_N); + hptr->FlagHome = 0; // turn house's flag off + hptr->FlagLocation = 0; + } +#endif + } + + /* + ** Create units for this house + */ + for (i = 0; i < u_limit; i++) { + + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_units[i] * scaleval; j++) { + + /* + ** Create an Ally unit + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < 2; k++) if(utable[i].AllyType[k] != UNIT_NONE) { + Reserve_Unit(); + obj = new UnitClass (utable[i].AllyType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create a Soviet unit + */ + for (k = 0; k < 2; k++) if(utable[i].SovietType[k] != UNIT_NONE) { + Reserve_Unit(); + obj = new UnitClass (utable[i].SovietType[k], house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + + /* + ** Create infantry + */ + for (i = 0; i < i_limit; i++) { + /* + ** Find the center point for this category. + */ + centerpt = Clip_Scatter(centroid, 4); + + /* + ** Place objects; loop through all unit in this category + */ + for (j = 0; j < num_infantry[i] * scaleval; j++) { + + /* + ** Create Ally infantry (Note: Unlimbo calls Enter_Idle_Mode(), which + ** assigns the infantry to HUNT; we must use Set_Mission() to override + ** this state.) + */ + if (hptr->ActLike != HOUSE_USSR && hptr->ActLike != HOUSE_UKRAINE) { + for (k = 0; k < itable[i].AllyCount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].AllyType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } else { + + /* + ** Create Soviet infantry + */ + for (k = 0; k < itable[i].SovietCount; k++) { + Reserve_Infantry(); + obj = new InfantryClass (itable[i].SovietType, house); + if (!Scan_Place_Object(obj, centerpt)) { + delete obj; + } else { + if (!hptr->IsHuman) { + obj->Set_Mission(MISSION_GUARD_AREA); + } else { + obj->Set_Mission(MISSION_GUARD); + } + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * Scan_Place_Object -- places an object >near< the given cell * + * * + * INPUT: * + * obj ptr to object to Unlimbo * + * cell center of search area * + * * + * OUTPUT: * + * true = object was placed; false = it wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/09/1995 BRR : Created. * + *=============================================================================================*/ +int Scan_Place_Object(ObjectClass * obj, CELL cell) +{ + int dist; // for object placement + FacingType rot; // for object placement + FacingType fcounter; // for object placement + int tryval; + CELL newcell; + TechnoClass * techno; + int skipit; + + /* + ** First try to unlimbo the object in the given cell. + */ + if (Map.In_Radar(cell)) { + techno = Map[cell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(cell), DIR_N)) { + return(true); + } + } + } + + /* + ** Loop through distances from the given center cell; skip the center cell. + ** For each distance, try placing the object along each rotational direction; + ** if none are available, try each direction with a random scatter value. + ** If that fails, go to the next distance. + ** This ensures that the closest coordinates are filled first. + */ + for (dist = 1; dist < 32; dist++) { + + /* + ** Pick a random starting direction + */ + rot = Random_Pick(FACING_N, FACING_NW); + + /* + ** Try all directions twice + */ + for (tryval = 0 ; tryval < 2; tryval++) { + + /* + ** Loop through all directions, at this distance. + */ + for (fcounter = FACING_N; fcounter <= FACING_NW; fcounter++) { + + skipit = false; + + /* + ** Pick a coordinate along this directional axis + */ + newcell = Clip_Move(cell, rot, dist); + + /* + ** If this is our second try at this distance, add a random scatter + ** to the desired cell, so our units aren't all aligned along spokes. + */ + if (tryval > 0) { + newcell = Clip_Scatter (newcell, 1); + } + + /* + ** If, by randomly scattering, we've chosen the exact center, skip + ** it & try another direction. + */ + if (newcell==cell) { + skipit = true; + } + + if (!skipit) { + /* + ** Only attempt to Unlimbo the object if: + ** - there is no techno in the cell + ** - the techno in the cell & the object are both infantry + */ + techno = Map[newcell].Cell_Techno(); + if (!techno || (techno->What_Am_I()==RTTI_INFANTRY && + obj->What_Am_I()==RTTI_INFANTRY)) { + if (obj->Unlimbo(Cell_Coord(newcell), DIR_N)) { + return(true); + } + } + } + + rot++; + if (rot > FACING_NW) { + rot = FACING_N; + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * Clip_Scatter -- randomly scatters from given cell; won't fall off map * + * * + * INPUT: * + * cell cell to scatter from * + * maxdist max distance to scatter * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Scatter(CELL cell, int maxdist) +{ + int x,y; + int xdist; + int ydist; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + xdist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + x += xdist; + if (x > xmax) { + x = xmax; + } + } else { + x -= xdist; + if (x < xmin) { + x = xmin; + } + } + + /* + ** Adjust the y-coordinate + */ + ydist = Random_Pick(0, maxdist); + if (Percent_Chance(50)) { + y += ydist; + if (y > ymax) { + y = ymax; + } + } else { + y -= ydist; + if (y < ymin) { + y = ymin; + } + } + + return (XY_Cell(x, y)); +} + + +/*********************************************************************************************** + * Clip_Move -- moves in given direction from given cell; clips to map * + * * + * INPUT: * + * cell cell to start from * + * facing direction to move * + * dist distance to move * + * * + * OUTPUT: * + * new cell number * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/30/1995 BRR : Created. * + *=============================================================================================*/ +static CELL Clip_Move(CELL cell, FacingType facing, int dist) +{ + int x,y; + int xmin,xmax; + int ymin,ymax; + + /* + ** Get X & Y coords of given starting cell + */ + x = Cell_X(cell); + y = Cell_Y(cell); + + /* + ** Compute our x & y limits + */ + xmin = Map.MapCellX; + xmax = xmin + Map.MapCellWidth - 1; + ymin = Map.MapCellY; + ymax = ymin + Map.MapCellHeight - 1; + + /* + ** Adjust the x-coordinate + */ + switch (facing) { + case FACING_N: + y -= dist; + break; + + case FACING_NE: + x += dist; + y -= dist; + break; + + case FACING_E: + x += dist; + break; + + case FACING_SE: + x += dist; + y += dist; + break; + + case FACING_S: + y += dist; + break; + + case FACING_SW: + x -= dist; + y += dist; + break; + + case FACING_W: + x -= dist; + break; + + case FACING_NW: + x -= dist; + y -= dist; + break; + } + + /* + ** Clip to the map + */ + if (x > xmax) + x = xmax; + if (x < xmin) + x = xmin; + + if (y > ymax) + y = ymax; + if (y < ymin) + y = ymin; + + return (XY_Cell(x, y)); +} + + +void Disect_Scenario_Name(char const * name, int & scenario, ScenarioPlayerType & player, ScenarioDirType & dir, ScenarioVarType & var) +{ + if (name == NULL) return; + + /* + ** Fetch the scenario number. + */ + char buf[3]; + memcpy(buf, &name[3], 2); + buf[2] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + char first = buf[0]; + char second = buf[1]; + if (first <= '9' && second <= '9') { + scenario = atoi(buf); + } else { + if (first <= '9') { + first -= '0'; + } else { + if (first >= 'a' && first <= 'z') { + first -= 'a'; + } else { + first -= 'A'; + } + } + if (second <= '9') { + second -= '0'; + } else { + if (second >= 'a' && second <= 'z') { + second = (second - 'a') + 10; + } else { + second = (second - 'A') + 10; + } + } + scenario = (36 * first) + second; + } +#else + scenario = atoi(buf); +#endif + + /* + ** Fetch the scenario player (side). + */ + player = SCEN_PLAYER_GREECE; + if (name[2] == HouseTypeClass::As_Reference(HOUSE_SPAIN).Prefix) { + player = SCEN_PLAYER_SPAIN; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_GREECE).Prefix) { + player = SCEN_PLAYER_GREECE; + } + if (name[2] == HouseTypeClass::As_Reference(HOUSE_USSR).Prefix) { + player = SCEN_PLAYER_USSR; + } + + /* + ** Fetch the direction. + */ + dir = SCEN_DIR_EAST; + if (name[5] == 'E') { + dir = SCEN_DIR_EAST; + } else { + dir = SCEN_DIR_WEST; + } + + /* + ** Fetch the variation. + */ + var = SCEN_VAR_A; + var = ScenarioVarType((name[6] - 'A') + SCEN_VAR_A); +} + +/* +============= +Cmd_Map +============= +*/ +void Cmd_Map(void) { + if (Cmd_Argc() != 2) { + Console_Printf("usage: map \n"); + return; + } + Scen.warpIntoMap = true; + Start_Scenario(Cmd_Argv(1)); + longjmp(select_game_jump, 1); +} \ No newline at end of file diff --git a/code/redalert/scenario.h b/code/redalert/scenario.h new file mode 100644 index 0000000..6012b64 --- /dev/null +++ b/code/redalert/scenario.h @@ -0,0 +1,342 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCENARIO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCENARIO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/26/96 * + * * + * Last Update : February 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SCENARIO_H +#define SCENARIO_H + +#include "MapScript.h" + + +/* +** This class holds the information about the current game being played. This information is +** global to the scenario and is generally of a similar nature to the information that was held +** in the controlling scenario INI file. It is safe to write this structure out as a whole since +** it doesn't contain any embedded pointers. +*/ +class ScenarioClass { + public: + + // Constructor. + ScenarioClass(void); + void Set_Scenario_Name(int scenario, ScenarioPlayerType player, ScenarioDirType dir = SCEN_DIR_NONE, ScenarioVarType var = SCEN_VAR_NONE); + void Set_Scenario_Name(char const * name); + + bool Set_Global_To(int global, bool value); + void Do_BW_Fade(void); + void Do_Fade_AI(void); + + /* + ** This is the source of the random numbers used in the game. This controls + ** the game logic and thus must be in sync with any networked machines. + */ + RandomClass RandomNumber; + + /* + ** This is the difficulty setting of the game. + */ + DiffType Difficulty; // For human player. + DiffType CDifficulty; // For computer players. + + /* + ** This is the main mission timer. This is the timer that is reset at the + ** start of the mission. It, effectively, holds the elapsed time of the + ** mission. + */ + TTimerClass Timer; + + /* + ** This is an array of waypoints; each waypoint corresponds to a letter of + ** the alphabet, and points to a cell number. -1 means unassigned. + ** The CellClass has a bit that tells if that cell has a waypoint attached to + ** it; the only way to find which waypoint it is, is to scan this array. This + ** shouldn't be needed often; usually, you know the waypoint & you want the CELL. + */ + CELL Waypoint[WAYPT_COUNT]; + + /* + ** This holds the system wide mission countdown timer. Time based missions + ** are governed by this timer. Various trigger events can modify and examine + ** this timer. The current value of this timer will display on the game + ** screen. + */ + CDTimerClass MissionTimer; + + /* + ** The shroud regrowth (if enabled) is regulated by this timer. When the + ** timer expires, the shroud will regrow one step. + */ + CDTimerClass ShroudTimer; + + /* + ** The scenario number. + */ + int Scenario; + + /* + ** The theater of the current scenario. + */ + TheaterType Theater; + + /* + ** The full name of the scenario (as it exists on disk). + */ + char ScenarioName[_MAX_FNAME+_MAX_EXT]; + + /* + ** Description of the scenario. + */ + char Description[DESCRIP_MAX]; + + /* + ** The filename of the introduction movie. + */ + VQType IntroMovie; + + /* + ** The filename of the briefing movie. + */ + VQType BriefMovie; + + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie2; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie3; + /* + ** The filename of the movie to play if the scenario is won. + */ + VQType WinMovie4; + + /* + ** The filename of the movie to play if the scenario is lost. + */ + VQType LoseMovie; + + /* + ** The filename of the movie to play right after the briefing and + ** just before the game. + */ + VQType ActionMovie; + + /* + ** This is the full text of the briefing. This text will be + ** displayed when the player commands the "restate mission + ** objectives" operation. + */ + char BriefingText[1024]; + + /* + ** This is the theme to start playing at the beginning of the action + ** movie. A score started in this fashion will continue to play as + ** the game progresses. + */ + ThemeType TransitTheme; + + /* + ** The house that the player is to be (obsolete). + */ + HousesType PlayerHouse; + + /* + ** The percentage of money that is allowed to be carried over into the + ** following scenario. + */ + fixed CarryOverPercent; + + /* + ** This is the amount of money that was left over in the previous + ** scenario. + */ + int CarryOverMoney; + + /* + ** This specifies the maximum amount of money that is allowed to be + ** carried over from the previous scenario. This limits the amount + ** regardless of what the carry over percentage is set to. + */ + int CarryOverCap; + + /* + ** This is the percent that the computer controlled base is to be + ** built up to at the scenario start. + */ + int Percent; + + /* + ** Global flags that are used in the trigger system and are persistent + ** over the course of the game. + */ + bool GlobalFlags[30]; + + /* + ** This records the bookmark view locations the player has recorded. + */ + CELL Views[4]; + + /* + ** This is the number of active passable bridges in the current game. + */ + int BridgeCount; + + /* + ** This records the carry over timer value that is used when the mission + ** starts (presuming the appropriate flag is set) and also used when the + ** scenario restarts. + */ + int CarryOverTimer; + + /* + ** If a bridge has been destroyed, then this flag will be set to true. + ** If there is a trigger that depends on this, it might be triggered. + */ + unsigned IsBridgeChanged:1; + + /* + ** If a global has changed and global change trigger events must be + ** processed, then this flag will be set to true. + */ + unsigned IsGlobalChanged:1; + + /* + ** Are the buildings and units in this scenario to carry over into + ** some (unspecified) later scenario and thus have to be recorded + ** at the end? + */ + unsigned IsToCarryOver:1; + + /* + ** Is this scenario to begin by taking the previously recorded + ** carryover objects and creating them onto the map? + */ + unsigned IsToInherit:1; + + /* + ** If Tanya or a civilian is to be automatically evacuated when they enter + ** a transport vehicle, then this flag will be true. + */ + unsigned IsTanyaEvac:1; + + /* + ** These variables are assigned to the chronosphere effect, and control + ** whether the palette should be fading towards b&w or towards color. + */ + unsigned IsFadingBW:1; + unsigned IsFadingColor:1; + + /* + ** If this scenario is to be the last mission of the game (for this side), then + ** this flag will be true. + */ + unsigned IsEndOfGame:1; + + /* + ** If the mission countdown timer is to be inherited from the previous + ** scenario, then this flag will be set to true. + */ + unsigned IsInheritTimer:1; + + /* + ** If the spy plane is to be disabled in this scenario even though circumstances + ** might otherwise indicate that it should appear, then this flag will be true. + */ + unsigned IsNoSpyPlane:1; + + /* + ** If the score screen (and "mission accomplished" voice) is to be skipped when + ** this scenario is finished, then this flag will be true. + */ + unsigned IsSkipScore:1; + + /* + ** If this is to be a one time only mission such that when it is completed, the game + ** will return to the main menu, then this flag will be set to true. + */ + unsigned IsOneTimeOnly:1; + + /* + ** If the map selection is to be skipped then this flag will be true. If this + ** ins't a one time only scenario, then the next scenario will have the same + ** name as the current one but will be for variation "B". + */ + unsigned IsNoMapSel:1; + + /* + ** If trucks are supposed to drop wood crates when they explode, then this flag + ** will be set to true. + */ + unsigned IsTruckCrate:1; + + /* + ** If the initial money is to be assigned as ore in available silos, then + ** this flag will be set to true. + */ + unsigned IsMoneyTiberium:1; + + /* + ** This is the fading countdown timer. As this timer counts down, the + ** fading to b&w or color will progress. This timer represents a + ** percentage of the Options.Get_Saturation() to fade towards. + */ + CDTimerClass FadeTimer; + +#ifdef FIXIT_VERSION_3 // For endgame auto-sonar pulse. + // Timer to set the period for checking if an auto-sonar pulse should be performed. + // This will take place if a player has nothing but subs left in the game. + CDTimerClass AutoSonarTimer; +#endif + +#ifdef FIXIT_VERSION_3 // Stalemate games. + bool bLocalProposesDraw; // True if the local player in a 2-player game has a draw offer extended. + bool bOtherProposesDraw; // True if the other player in a 2-player game has a draw offer extended. +#endif + + bool warpIntoMap; + + + /* + ** MapScript storage, to be accessed by triggers etc. + */ + MapScript* mapScript = nullptr; +}; + + +#endif diff --git a/code/redalert/score.cpp b/code/redalert/score.cpp new file mode 100644 index 0000000..ea2a50c --- /dev/null +++ b/code/redalert/score.cpp @@ -0,0 +1,1949 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SCORE.CPP 3 3/14/97 12:02a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : May 3, 1995 [BWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * Draw_Infantrymen -- Draw all the guys on the score screen * + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen * + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly * + * ScoreClass::DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen * + * ScoreClass::Delay -- Pauses waiting for keypress. * + * ScoreClass::Presentation -- Main routine to display score screen. * + * ScoreClass::Print_Graph_Title -- Prints title on score screen. * + * ScoreClass::Print_Minutes -- Print out hours/minutes up to max * + * ScoreClass::Pulse_Bar_Graph -- Pulses the bargraph color. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WIN32 +extern short StreamLowImpact; +#endif + +#include "function.h" + +#define SCORETEXT_X 184 +#define SCORETEXT_Y 8 +#define CASUALTY_Y 88 +#define BUILDING_X 256 +#define BUILDING_Y 128 +#define BARGRAPH_X 266 +#define MAX_BAR_X 318 // max possible is 319 because of bar's right shadow +#define SIZEGBAR 118 +#define HALLFAME_X 11 +#define HALLFAME_Y 120 + +#define MULTISCOREX 30 + +#define TEDIT_FAME 1 +#define NUMINFANTRYMEN 10 +#define NUMFAMENAMES 7 +#define MAX_FAMENAME_LENGTH 11 + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +GraphicBufferClass *PseudoSeenBuff; + +struct InfantryAnim { + int xpos; + int ypos; + void const *shapefile; + void const *remap; + int anim; + int stage; + char delay; + InfantryTypeClass const *Class; +} InfantryMan[NUMINFANTRYMEN]; +void Draw_InfantryMen(void); +void Draw_InfantryMan(int index); +void New_Infantry_Anim(int index, int anim); +void Draw_Bar_Graphs(int i, int gkilled, int nkilled); +void Animate_Cursor(int pos, int ypos); +void Animate_Score_Objs(void); +void Cycle_Wait_Click(bool cycle=true); + +#ifdef FIXIT_SCORE_CRASH +//void Disable_Uncompressed_Shapes (void); +//void Enable_Uncompressed_Shapes (void); +#endif //FIXIT + +void const * Beepy6; +int ControlQ; // cheat key to skip past score/mapsel screens +bool StillUpdating; + +#ifdef WIN32 +char *ScreenNames[2]={"ALIBACKH.PCX", "SOVBACKH.PCX"}; +#else +char *ScreenNames[2]={"ALI-TRAN.WSA", "SOV-TRAN.WSA"}; +#endif + +//#ifdef WIN32 +//TextBlitClass BlitList; +//#endif + + +struct Fame { + char name[MAX_FAMENAME_LENGTH]; + int score; + int level; + int side; +}; + +ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + + +ScoreAnimClass::ScoreAnimClass(int x, int y, void const * data) +{ + XPos = x * RESFACTOR; + YPos = y * RESFACTOR; + Timer = 0; + DataPtr = data; +} + + +ScoreTimeClass::ScoreTimeClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; +} + +void ScoreTimeClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + +ScoreCredsClass::ScoreCredsClass(int xpos, int ypos, void const * data, int maxval, int xtimer) : + ScoreAnimClass(xpos, ypos, data) +{ + Stage = 0; + MaxStage = maxval; + TimerReset = xtimer; + Clock1 = MFCD::Retrieve("CLOCK1.AUD"); + CashTurn = MFCD::Retrieve("CASHTURN.AUD"); +} + + +void ScoreCredsClass::Update(void) +{ +#ifdef WIN32 + GraphicViewPortClass *oldpage; +#else + GraphicBufferClass *oldpage; +#endif + if (!Timer) { + Timer = TimerReset; + if (++Stage >= MaxStage) Stage = 0; + oldpage = LogicPage; + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(Clock1, 255, Options.Normalize_Volume(130)); +#else + Play_Sample(Clock1, 255, Options.Normalize_Volume(50)); +#endif + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(DataPtr, Stage, XPos, YPos, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#endif + Set_Logic_Page(oldpage); + } +} + + +ScorePrintClass::ScorePrintClass(int string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, Text_String(string)) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +ScorePrintClass::ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background) : + ScoreAnimClass(xpos, ypos, string) +{ + Background = background; + PrimaryPalette = palette; + Stage = 0; +} + + +void ScorePrintClass::Update(void) +{ + static char localstr[2]={0,0}; + static char _whitepal[]={0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F}; + + if (Stage && (((char *)DataPtr)[Stage-1]==0) ) { + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i] == this) { + ScoreObjs[i] = 0; + } + } + delete this; + return; + } + +#ifdef WIN32 + StillUpdating = true; +#endif + if (!Timer) { + Timer = 1; + + int pos = XPos+(Stage*(6*RESFACTOR)); +// print the letter properly + if (Stage) { + Set_Font_Palette(PrimaryPalette); + localstr[0]=((char *)DataPtr)[Stage-1]; + HidPage.Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, pos-6*RESFACTOR, YPos-1*RESFACTOR, pos-6*RESFACTOR, YPos-1*RESFACTOR, 7*RESFACTOR, 8*RESFACTOR); + PseudoSeenBuff->Print(localstr, pos-6*RESFACTOR, YPos, TBLACK, TBLACK); +#endif + } + if (((char *)DataPtr)[Stage]) { + localstr[0]=((char *)DataPtr)[Stage]; + Set_Font_Palette(_whitepal); + SeenPage.Print(localstr, pos, YPos-1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos, YPos+1, TBLACK, TBLACK); + SeenPage.Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(localstr, pos, YPos-1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos, YPos+1, TBLACK, TBLACK); + PseudoSeenBuff->Print(localstr, pos+1, YPos , TBLACK, TBLACK); +#endif + } + Stage++; + } +} + + +ScoreScaleClass::ScoreScaleClass(void const * string, int xpos, int ypos, char const palette[]) : + ScoreAnimClass(xpos, ypos, string) +{ + Palette = &palette[0]; +#ifdef WIN32 + Stage = 0; +#else + Stage = 5; +#endif +} + + +void ScoreScaleClass::Update(void) +{ + static int _destx[]={0,80,107,134,180,228}; + static int _destw[]={6,20, 30, 40, 60, 80}; + + /* + ** Restore the background for the scaled-up letter + */ + if (!Timer) { + Timer = 1; +#ifndef WIN32 + if (Stage != 5) { + int destx = _destx[Stage+1]*RESFACTOR; + int destw = _destw[Stage+1]*RESFACTOR; + HidPage.Blit(SeenPage, destx, YPos, destx, YPos, (destx + destw) <= 320 * RESFACTOR ? destw : (320 * RESFACTOR) - destx, (YPos + destw) <= 200 * RESFACTOR ? destw : (200 * RESFACTOR) - YPos); + } +#endif + if (Stage) { + Set_Font_Palette(Palette); + HidPage.Fill_Rect(0, 0, 7*RESFACTOR, 7*RESFACTOR, TBLACK); + HidPage.Print((char *)DataPtr, 0, 0, TBLACK, TBLACK); + HidPage.Scale(SeenPage, 0, 0, _destx[Stage]*RESFACTOR, YPos, 5*RESFACTOR, 6*RESFACTOR, _destw[Stage]*RESFACTOR, _destw[Stage]*RESFACTOR, true); + Stage--; + } else { + Set_Font_Palette(Palette); + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]==this) ScoreObjs[i] = 0; + } + HidPage.Print((char *)DataPtr, XPos, YPos, TBLACK, TBLACK); + HidPage.Blit(SeenPage, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, XPos, YPos, XPos, YPos, 6*RESFACTOR, 6*RESFACTOR); +#endif + delete this; + return; + } + } +} + +int Alloc_Object(ScoreAnimClass *obj) +{ + int i,ret; + + for (i = ret = 0; i < MAXSCOREOBJS; i++) { + if (!ScoreObjs[i]) { + ScoreObjs[i] = obj; + ret = i; + break; + } + } + return(ret); +} + + + +/*********************************************************************************************** + * ScoreClass::Presentation -- Main routine to display score screen. * + * * + * This is the main routine that displays the score screen graphics. * + * It gets called at the end of each scenario and is used to present * + * the results and a rating of the player's battle. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 : Created. * + *=============================================================================================*/ +static unsigned char const _bluepal[]={0xC0,0xC1,0xC1,0xC3,0xC2,0xC5,0xC3,0xC7,0xC4,0xC9,0xCA,0xCB,0xCC,0xCD,0xC0,0xCF}; +static unsigned char const _greenpal[]={0x70,0x71,0x7C,0x73,0x7D,0x75,0x7E,0x77,0x7F,0x79,0x7A,0x7B,0x7C,0x7D,0x7C,0x7F}; +static unsigned char const _redpal[]={0xD0,0xD1,0xD7,0xD3,0xD9,0xD5,0xDA,0xD7,0xDB,0xD9,0xDA,0xDB,0xDC,0xDD,0xD6,0xDF}; +static unsigned char const _yellowpal[]={0x0,0x0,0xEC,0x0,0xEB,0x0,0xEA,0x0,0xE9,0x0,0x0,0x0,0x0,0x0,0xED,0x0}; +void ScoreClass::Presentation(void) +{ +#ifdef WIN32 +// if (Keyboard != NULL) return; +#endif + static int const _casuax[2]={144,150}; + static int const _casuay[2]={ 78, 78}; + static int const _gditxy[2]={ 90, 90}; + +#if defined(FRENCH) || defined(GERMAN) + static int const _gditxx[2]={130,150}; + static int const _nodtxx[2]={130,150}; +#else + static int const _gditxx[2]={135,150}; + static int const _nodtxx[2]={135,150}; +#endif + static int const _nodtxy[2]={102,102}; + static int const _bldggy[2]={138,138}; + static int const _bldgny[2]={150,150}; + +#ifdef WIN32 +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Disable_Uncompressed_Shapes(); +#endif //FIXIT + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + int i; + void const * yellowptr; + void const * redptr; + CCFileClass file(FAME_FILE_NAME); + struct Fame hallfame[NUMFAMENAMES]; + void *oldfont; + int oldfontxspacing = FontXSpacing; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 +#ifdef WIN32 + char inter_pal[15]; + sprintf(inter_pal, "SCORPAL1.PAL"); +#endif + + ControlQ = 0; + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Theme.Queue_Song(THEME_SCORE); + +#ifdef WIN32 + VisiblePage.Clear(); + SysMemPage.Clear(); + WWMouse->Erase_Mouse(&HidPage, TRUE); + HiddenPage.Clear(); + Set_Logic_Page(SysMemPage); +#else + SeenPage.Clear(); + HidPage.Clear(); + Set_Logic_Page(HidPage); +#endif + BlackPalette.Set(); + + Show_OldFrameBuffer(false); + + void const * country4 = MFCD::Retrieve("COUNTRY4.AUD"); + void const * sfx4 = MFCD::Retrieve("SFX4.AUD"); + Beepy6 = MFCD::Retrieve("BEEPY6.AUD"); + + /* + ** Load the background for the score screen + */ +#ifndef WIN32 + void *anim = Open_Animation(ScreenNames[house], NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); +#endif + + unsigned minutes = (unsigned)((ElapsedTime / (long)TIMER_MINUTE))+1; + +// Load up the shapes for the Nod score screen +#ifdef WIN32 + yellowptr = MFCD::Retrieve("BAR3BHR.SHP"); + redptr = MFCD::Retrieve("BAR3RHR.SHP"); +#else + if (!house) { + yellowptr = MFCD::Retrieve("BAR3BLU.SHP"); + redptr = MFCD::Retrieve("BAR3RED.SHP"); + } +#endif + +/* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + +/* --- Now display the background animation --- */ + Hide_Mouse(); +#ifdef WIN32 + Load_Title_Screen(ScreenNames[house], &HidPage, (unsigned char *)ScorePalette.Get_Data()); + Increase_Palette_Luminance ((unsigned char*)ScorePalette.Get_Data(), 30, 30, 30, 63); + HidPage.Blit(SeenPage); + HidPage.Blit(*PseudoSeenBuff); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); +#ifdef WIN32 + Play_Sample(country4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(country4, 255, Options.Normalize_Volume(60)); +#endif + +#ifndef WIN32 + int frame = 1; + StreamLowImpact = true; + while (frame < Get_Animation_Frame_Count(anim)) { + Animate_Frame(anim, SeenPage, frame++); + Call_Back_Delay(2); + } + StreamLowImpact = false; + Call_Back(); + Close_Animation(anim); +#endif + + /* + ** Background's up, so now load various shapes and animations + */ +#ifdef WIN32 + void const * timeshape = MFCD::Retrieve("TIMEHR.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISC1-HR.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISC2-HR.SHP"); +#else + void const * timeshape = MFCD::Retrieve("TIME.SHP"); + void const * hiscore1shape = MFCD::Retrieve("HISCORE1.SHP"); + void const * hiscore2shape = MFCD::Retrieve("HISCORE2.SHP"); +#endif + ScoreObjs[0] = new ScoreTimeClass(238, 2, timeshape, 30, 4); + ScoreObjs[1] = new ScoreTimeClass(4, 89, hiscore1shape, 10, 4); + ScoreObjs[2] = new ScoreTimeClass(4, 180, hiscore2shape, 10, 4); + + /* Now display the stuff */ +#ifdef WIN32 +#else + SeenPage.Blit(HidPage); +#endif + Set_Logic_Page(SeenBuff); + + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 198, 9, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TIME, 204, 9, _greenpal)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_LEAD, 164, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_EFFI, 164, 38, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOTA, 164, 50, _greenpal)); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(13); + + int scorecounter = 0; + + Keyboard->Clear(); + + /* + ** Determine leadership rating. + */ + int leadership = 0; + for (int index = 0; index < Logic.Count(); index++) { + ObjectClass * object = Logic[index]; + HousesType owner = object->Owner(); + if ( (house) && (owner == HOUSE_USSR || owner == HOUSE_BAD || owner == HOUSE_UKRAINE) ) { + leadership++; + } else { + if ( (!house) && (object->Owner() == HOUSE_GREECE) ) { + leadership++; + } + } + } + int uspoints = 0; + + for (HousesType hous = HOUSE_SPAIN; hous <= HOUSE_BAD; hous++) { + HouseClass *hows = HouseClass::As_Pointer(hous); + if (hous == HOUSE_USSR || hous == HOUSE_BAD || hous == HOUSE_UKRAINE) { + NKilled += hows->UnitsLost; + NBKilled += hows->BuildingsLost; + } else { + GKilled += hows->UnitsLost; + GBKilled += hows->BuildingsLost; + } + if (PlayerPtr->Is_Ally(hous) ) { + uspoints += hows->PointTotal; + } + } +// if(uspoints < 0) uspoints = 0; +// uspoints += 1000; //BG 1000 bonus points for winning mission + + /* + ** Bias the base score upward according to the difficulty level. + */ + switch (PlayerPtr->Difficulty) { + case DIFF_EASY: + uspoints += 500; + break; + + case DIFF_NORMAL: + uspoints += 1500; + break; + + case DIFF_HARD: + uspoints += 3500; + break; + } + + + if (!leadership) leadership++; + leadership = 100*fixed(leadership, (house ? NKilled+NBKilled+leadership : GKilled+GBKilled+leadership)); + leadership = min(150,leadership); + + /* + ** Determine economy rating. + */ + int init = PlayerPtr->Control.InitialCredits; + int cred = PlayerPtr->Available_Money(); + + int economy = 100*fixed((unsigned)PlayerPtr->Available_Money()+1+PlayerPtr->StolenBuildingsCredits, PlayerPtr->HarvestedCredits + (unsigned)PlayerPtr->Control.InitialCredits+1); + economy=min(economy,150); + + int total = ((uspoints * leadership) / 100) + ((uspoints * economy) / 100); + if (total < -9999) total = -9999; + total = min(total, 99999); + +Keyboard->Clear(); + for (i = 0; i <= 130; i++) { + Set_Font_Palette(_greenpal); + int lead = (leadership * i) / 100; + Count_Up_Print("%3d%%", lead, leadership, 244, 26); + if (i>=30) { + int econo = (economy * (i-30)) / 100; + Count_Up_Print("%3d%%", econo, economy, 244, 38); + } + Print_Minutes(minutes); + Call_Back_Delay(1); +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(100)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(40)); +#endif + if ( (i >= 30) && (i >= leadership) && ((i-30) >= economy) ) break; +//BG if (Keyboard->Check()) break; + } + Count_Up_Print("%3d%%", leadership, leadership, 244, 26); + Count_Up_Print("%3d%%", economy, economy, 244, 38); + + char buffer[16]; + sprintf(buffer, "x %5d",uspoints); + Alloc_Object(new ScorePrintClass(buffer, 274, 26, _greenpal)); + Alloc_Object(new ScorePrintClass(buffer, 274, 38, _greenpal)); + Call_Back_Delay(8); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, WHITE); + Call_Back_Delay(1); + SeenBuff.Draw_Line(274*RESFACTOR, 48*RESFACTOR, 313*RESFACTOR, 48*RESFACTOR, GREEN); + + sprintf(buffer,"%5d", total); + Alloc_Object(new ScorePrintClass(buffer, 286, 50, _greenpal)); + +//BG if (!Keyboard->Check()) { + Call_Back_Delay(60); +//BG } + + if (house) Show_Credits(house, (const char *)&_greenpal[0]); + +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(60); + + /* + ** Show stats on # of units killed + */ + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + int indx = house; +#ifdef WIN32 + indx = 0; +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_CASU, _casuax[indx], _casuay[indx], _greenpal)); + Call_Back_Delay(9); + if (house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _gditxy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _nodtxy[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _gditxy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _nodtxx[indx], _nodtxy[indx], _redpal)); + } + Call_Back_Delay(6); + + Set_Font_Palette(_redpal); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); +#else + if (house) { + Do_Nod_Casualties_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GKilled + CKilled, NKilled, 89); + } +#endif + + Set_Logic_Page(SeenBuff); + + /* + ** Print out stats on buildings destroyed + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif +#ifdef WIN32 + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); +#else + if (!house) { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL, 144, 126, _greenpal)); + Call_Back_Delay(9); + } else { + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL1, 150, 118, _greenpal)); + Alloc_Object(new ScorePrintClass(TXT_SCORE_BUIL2, 150, 126, _greenpal)); + Call_Back_Delay(13); + } +#endif + if(house) { + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldggy[indx], _redpal)); + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldgny[indx], _bluepal)); + } else { + Alloc_Object(new ScorePrintClass(TXT_ALLIES, _gditxx[indx], _bldggy[indx], _bluepal)); + Alloc_Object(new ScorePrintClass(TXT_SOVIET, _gditxx[indx], _bldgny[indx], _redpal)); + } + Call_Back_Delay(7); +#ifdef WIN32 + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); +#else + if (house) { + Call_Back_Delay(6); + Set_Font_Palette(_greenpal); + Do_Nod_Buildings_Graph(); + } else { + Do_GDI_Graph(yellowptr, redptr, GBKilled + CBKilled, NBKilled, 137); + } +#endif + +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + + Keyboard->Clear(); + + if (!house) Show_Credits(house, (const char*)&_greenpal[0]); + /* + ** Hall of fame display and processing + */ +#ifdef WIN32 + Play_Sample(sfx4, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(sfx4, 255, Options.Normalize_Volume(60)); +#endif + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 28, 110, _greenpal)); + Call_Back_Delay(9); + + /* + ** First check for the existence of the file, and if there isn't one, + ** make a new one filled with blanks. + */ + if (!file.Is_Available()) { + + // hall of fame doesn't exist, so blank it out & write it + file.Open(WRITE); + + for (i = 0; i < NUMFAMENAMES; i++) { + hallfame[i].name[0] = + hallfame[i].score = + hallfame[i].level = 0; + hallfame[i].side = 0; + file.Write(&hallfame[i], sizeof(struct Fame)); + } + + file.Close(); + } + + file.Open(READ); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Read(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + + /* + ** If the player's score is good enough to bump someone off the list, + ** remove their data, move everyone down a notch, and set index = where + ** their info goes + */ + if (hallfame[NUMFAMENAMES-1].score >= total) + hallfame[NUMFAMENAMES-1].score = 0; + int index; + for (index = 0; index < NUMFAMENAMES; index++) { + if (total > hallfame[index].score) { + if (index < (NUMFAMENAMES-1)) for (i = (NUMFAMENAMES-1); i > index; i--) hallfame[i] = hallfame[i-1]; + hallfame[index].score = total; + hallfame[index].level = Scen.Scenario; + hallfame[index].name[0] = 0; // blank out the name + hallfame[index].side = house; + break; + } + } + + /* + ** Now display the hall of fame + */ + Set_Logic_Page(SeenBuff); + +#ifdef WIN32 + char maststr[NUMFAMENAMES*32]; +#endif + const char *pal; + for (i = 0; i < NUMFAMENAMES; i++) { + pal = (const char* )(hallfame[i].side ? _redpal : _bluepal); + Alloc_Object(new ScorePrintClass(hallfame[i].name, HALLFAME_X, HALLFAME_Y + (i*8), pal)); + if (hallfame[i].score) { +#ifdef WIN32 + char *str = maststr + i*32; +#else + char *str = (char *)(HidPage.Get_Buffer()) + i*32; +#endif + sprintf(str, "%d", hallfame[i].score); + Alloc_Object(new ScorePrintClass(str, HALLFAME_X+(6*14), HALLFAME_Y + (i*8), pal, BLACK)); + if (hallfame[i].level < 20) { + sprintf(str+16, "%d", hallfame[i].level); + } else { + strcpy(str+16, "**"); + } + Alloc_Object(new ScorePrintClass(str+16, HALLFAME_X+(6*11), HALLFAME_Y + (i*8), pal, BLACK)); + Call_Back_Delay(13); + } + } +#ifdef WIN32 + // Wait for text printing to complete + while (StillUpdating) { + Call_Back_Delay(1); + } +#endif + /* + ** If the player's on the hall of fame, have him enter his name now + */ + Keyboard->Clear(); + + if (index < NUMFAMENAMES) { + pal = (const char *)(hallfame[index].side ? _redpal : _bluepal); + Input_Name(hallfame[index].name, HALLFAME_X, HALLFAME_Y + (index*8), pal); + + file.Open(WRITE); + for (i = 0; i < NUMFAMENAMES; i++) { + file.Write(&hallfame[i], sizeof(struct Fame)); + } + file.Close(); + } else { + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 149, 190, _yellowpal)); + ControlQ = false; + Cycle_Wait_Click(); + } + + Keyboard->Clear(); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + Show_Mouse(); +// Map_Selection(); +// Scen.ScenVar = SCEN_VAR_A; +// Scen.ScenDir = SCEN_DIR_EAST; + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); +#ifdef WIN32 + VisiblePage.Clear(); +#else + SeenPage.Clear(); +#endif + GamePalette.Set(); + + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + +#ifdef WIN32 + delete PseudoSeenBuff; +#ifdef FIXIT_SCORE_CRASH + /* + ** Fix for the score screen crash due to uncompressed shape buffer overflow. + */ + Enable_Uncompressed_Shapes(); +#endif //FIXIT + +#endif + + Show_OldFrameBuffer(true); +} + + +void Cycle_Wait_Click(bool cycle) +{ + int counter = 0; + int minclicks = 20; + unsigned long timingtime = TickCount; + //PG SerialPacketType sendpacket; + //PG SerialPacketType receivepacket; + //PG int packetlen; + + + Keyboard->Clear(); + while (minclicks || (!Keyboard->Check() && !ControlQ) ) { +#if (0) //PG + if (Session.Type == GAME_NULL_MODEM || + Session.Type == GAME_MODEM) { + + // + // send a timing packet if enough time has gone by. + // + if ( (TickCount - timingtime) > PACKET_TIMING_TIMEOUT) { + memset (&sendpacket, 0, sizeof(SerialPacketType)); + sendpacket.Command = SERIAL_SCORE_SCREEN; + sendpacket.ScenarioInfo.ResponseTime = NullModem.Response_Time(); + sendpacket.ID = Session.ModemType; + + NullModem.Send_Message (&sendpacket, sizeof(sendpacket), 0); + timingtime = TickCount; + } + + if (NullModem.Get_Message (&receivepacket, &packetlen) > 0) { + // throw packet away + packetlen = packetlen; + } + + NullModem.Service(); + } +#endif + Call_Back_Delay(1); + if (minclicks) { + minclicks--; + Keyboard->Clear(); + } + + if(cycle) { + counter = ((++counter) & 7); + if (counter == 0 && Options.IsPaletteScroll) { + RGBClass rgb = ScorePalette[233]; + for (int i = 233; i < 237; i++) { + ScorePalette[i] = ScorePalette[i+1]; + } + ScorePalette[237] = rgb; + ScorePalette.Set(); + } + } + } + Keyboard->Clear(); +} + +void ScoreClass::Do_Nod_Buildings_Graph(void) +{ + int shapenum; + InfantryTypeClass const *ramboclass; + + void const * factptr = MFCD::Retrieve("POWR.SHP"); + void const * rmboptr = MFCD::Retrieve("E7.SHP"); + void const * fball1ptr = MFCD::Retrieve("FBALL1.SHP"); + ramboclass = &InfantryTypeClass::As_Reference(INFANTRY_TANYA); + + /* + ** Print the # of buildings on the hidpage so we only need to do it once + */ + SeenPage.Blit(HidPage); + Set_Logic_Page(HidPage); + Call_Back_Delay(30); + Set_Font_Palette(_redpal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 10, TBLACK, TBLACK); + Set_Font_Palette(_bluepal); + HidPage.Print( 0, BUILDING_X + 16, BUILDING_Y + 22, TBLACK, TBLACK); + + /* + ** Here's the animation/draw loop for blowing up the factory + */ + int i; + for (i=0; i<98; i++) { + HidPage.Blit(HidPage, BUILDING_X, BUILDING_Y, 0, 0, 320-BUILDING_X, 48); + shapenum = 0; // no damage + if (i >= 60) { + shapenum = Extract_Shape_Count(factptr) - 2; // some damage + if (i == 60) { + Shake_The_Screen(6); + Sound_Effect(VOC_CRUMBLE); + } + if (i > 65) { + shapenum = Extract_Shape_Count(factptr) - 1; // mega damage + } + } + + /* + ** Draw the building before Rambo + */ + if (i < 68) { + CC_Draw_Shape(factptr, shapenum, 0, 0, WINDOW_MAIN, + SHAPE_GHOST|SHAPE_FADING|SHAPE_WIN_REL, ColorRemaps[PCOLOR_GOLD].RemapTable, DisplayClass::UnitShadow); + + } + + /* + ** Now draw some fires, if appropriate + */ + if (i >= 61) { + int firecount = Extract_Shape_Count(fball1ptr); + int shapeindex = (i-61) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 10, 10, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + if (i > 64) { + shapeindex = (i-64) / 2; + if (shapeindex < firecount) { + CC_Draw_Shape(fball1ptr, shapeindex, 50, 30, WINDOW_MAIN, + SHAPE_CENTER|SHAPE_WIN_REL); + } + } + } + /* + ** Draw the Tanya character running away from the building + */ + CC_Draw_Shape(rmboptr, (ramboclass->DoControls[DO_WALK].Frame + ramboclass->DoControls[DO_WALK].Jump*6) + ((unsigned(i)>>1)%ramboclass->DoControls[DO_WALK].Count), + i+32, 40, WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + ColorRemaps[PCOLOR_RED].RemapTable, DisplayClass::UnitShadow); + HidPage.Blit(SeenPage, 0, 0, BUILDING_X, BUILDING_Y, 320-BUILDING_X, 48); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(1); + } + + i = max(GBKilled, NBKilled); + for (int q = 0; q <= i; q++) { + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", q, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", q, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(1); +//BG } + } + Set_Font_Palette(_redpal); + Count_Up_Print( "%d", NBKilled, NBKilled, BUILDING_X + 16, BUILDING_Y + 10); + Set_Font_Palette(_bluepal); + Count_Up_Print( "%d", GBKilled, GBKilled, BUILDING_X + 16, BUILDING_Y + 22); +} + + +/*************************************************************************** + * DO_GDI_GRAPH -- Show # of people or buildings killed on GDI score screen* + * * + * * + * * + * INPUT: yellowptr, redptr = pointers to shape file for graphs * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 BWG : Created. * + *=========================================================================*/ + +void ScoreClass::Do_GDI_Graph(void const * yellowptr, void const * redptr, int gkilled, int nkilled, int ypos) +{ + int i, maxval; +#ifdef WIN32 + int xpos = 174; + int house = (PlayerPtr->Class->House == HOUSE_USSR || PlayerPtr->Class->House == HOUSE_UKRAINE); // 0 or 1 + if(house) { + int temp = gkilled; + gkilled = nkilled; + nkilled = temp; + void const *tempptr = yellowptr; + yellowptr = redptr; + redptr = tempptr; + } +#else + int xpos = 173; +#endif + int gdikilled = gkilled, nodkilled=nkilled; + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + gdikilled = (gdikilled * SIZEGBAR) / maxval; + nodkilled = (nodkilled * SIZEGBAR) / maxval; + if (maxval < 20) { + gdikilled = gkilled * 5; + nodkilled = nkilled * 5; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + // Draw the white-flash shape on the hidpage + Set_Logic_Page(HidPage); + HidPage.Fill_Rect(0, 0, 124*RESFACTOR, 9*RESFACTOR, TBLACK); + CC_Draw_Shape(redptr, 119, 0, 0, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#ifdef WIN32 + Set_Font_Palette(house ? _redpal : _bluepal); +#else + Set_Font_Palette(_bluepal); +#endif + + for (i = 1; i <= gdikilled; i++) { + if (i != gdikilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(yellowptr, i, xpos*RESFACTOR, ypos*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, ypos*RESFACTOR, (3+gdikilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*gkilled) / maxval, gkilled, 297, ypos+2); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(yellowptr, gdikilled, xpos*RESFACTOR, ypos*RESFACTOR , WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + Count_Up_Print("%d", gkilled, gkilled, 297, ypos+ 2); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); + +#ifdef WIN32 + Set_Font_Palette(house ? _bluepal : _redpal); +#else + Set_Font_Palette(_redpal); +#endif + for (i = 1; i <= nodkilled; i++) { + if (i != nodkilled) { +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape(redptr, i, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + } else { + HidPage.Blit(SeenPage, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, 0, 0, xpos*RESFACTOR, (ypos+12)*RESFACTOR, (3+nodkilled)*RESFACTOR, 8*RESFACTOR); +#endif + } + + Count_Up_Print("%d", (i*nkilled) / maxval, nkilled, 297, ypos+14); +//BG if (!Keyboard->Check()) { +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + Call_Back_Delay(2); +//BG } + } + +// if (Keyboard::Check()) Keyboard::Clear(); + + /* + ** Make sure accurate count is printed at end + */ +#ifdef WIN32 + Set_Logic_Page(*PseudoSeenBuff); + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Set_Logic_Page(SeenBuff); +#endif + CC_Draw_Shape( redptr, nodkilled, xpos*RESFACTOR, (ypos+12)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL, 0, 0); + Count_Up_Print("%d", nkilled, nkilled, 297, ypos+14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(40); +} + + +void ScoreClass::Do_Nod_Casualties_Graph(void) +{ + int i, gdikilled, nodkilled, maxval; + + void const * e1ptr = MFCD::Retrieve("E1.SHP"); + + gdikilled = GKilled; + nodkilled = NKilled; + maxval = max(gdikilled, nodkilled); + + if (!maxval) maxval=1; + if ((gdikilled > (MAX_BAR_X - BARGRAPH_X)) || (nodkilled > (MAX_BAR_X - BARGRAPH_X)) ) { + gdikilled = (gdikilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + nodkilled = (nodkilled * (MAX_BAR_X - BARGRAPH_X)) / maxval; + } + + maxval = max(gdikilled, nodkilled); + if (!maxval) maxval=1; + + /* + ** Initialize a bunch of objects for the infantrymen who pose for the bar + ** graphs of casualties. + */ + int r = NUMINFANTRYMEN/2; + for (i = 0; i < NUMINFANTRYMEN/2; i++) { + InfantryMan[i+0].xpos = + InfantryMan[i+r].xpos = (i*10) + 7; + InfantryMan[i+0].ypos = 11; + InfantryMan[i+r].ypos = 21; + InfantryMan[i+0].shapefile = + InfantryMan[i+r].shapefile = e1ptr; + InfantryMan[i+0].remap = ColorRemaps[PCOLOR_RED].RemapTable; + InfantryMan[i+r].remap = ColorRemaps[PCOLOR_BLUE].RemapTable; + InfantryMan[i+0].anim = + InfantryMan[i+r].anim = 0; + InfantryMan[i+0].stage = + InfantryMan[i+r].stage = 0; + InfantryMan[i+0].delay = + InfantryMan[i+r].delay = NonCriticalRandomNumber & 0x1F; + InfantryMan[i+0].Class = + InfantryMan[i+r].Class = &InfantryTypeClass::As_Reference(INFANTRY_E1); + } + + /* + ** Draw the infantrymen and pause briefly before running the graph + */ + Draw_InfantryMen(); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(40); + + for (i = 1; i <= maxval; i++) { + // Draw & update infantrymen 3 times for every tick on the graph (i) + for (int index = 0; index < 3; index++) { + Draw_InfantryMen(); + Draw_Bar_Graphs(i, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + + Set_Font_Palette(_redpal); + Count_Up_Print("%d", (i*NKilled) / maxval, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", (i*GKilled) / maxval, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); +/*BG if (!Keyboard->Check()) */ Call_Back_Delay(3); + } +#ifdef WIN32 + Play_Sample(Beepy6, 255, Options.Normalize_Volume(150)); +#else + Play_Sample(Beepy6, 255, Options.Normalize_Volume(60)); +#endif + } +//BG if (Keyboard->Check()) Keyboard->Clear(); + + /* + ** Make sure accurate count is printed at end + */ + Set_Font_Palette(_redpal); + Count_Up_Print("%d", NKilled, NKilled, SCORETEXT_X+64, CASUALTY_Y + 2); + Set_Font_Palette(_bluepal); + Count_Up_Print("%d", GKilled, GKilled, SCORETEXT_X+64, CASUALTY_Y + 14); + + /* + ** Finish up death animations, if there are any active + */ + int k = 1; + while (k) { + for (i=k=0; i= DO_GUN_DEATH) { + k=1; + } + } + if (k) { + Draw_InfantryMen(); + } + Draw_Bar_Graphs(maxval, nodkilled, gdikilled); + HidPage.Blit(SeenPage, 0, 0, BARGRAPH_X, CASUALTY_Y, 320-BARGRAPH_X, 34); + Call_Back_Delay(1); + } +} + + +void ScoreClass::Show_Credits(int house, char const pal[]) +{ + static int _credsx[2]={276,276}; + static int _credsy[2]={173,58}; + static int _credpx[2]={228,236}; +#ifdef GERMAN + static int _credpy[2]={181, 74}; + static int _credtx[2]={162,162}; + static int _credty[2]={173, 62}; +#else + static int _credpy[2]={189-12, 74}; + static int _credtx[2]={182,182}; + static int _credty[2]={179-12, 62}; +#endif + + int credobj,i; + int minval,add; + +#ifdef WIN32 + void const * credshape = MFCD::Retrieve(house ? "CREDSUHR.SHP" : "CREDSAHR.SHP"); +#else + void const * credshape = MFCD::Retrieve(house ? "CREDSU.SHP" : "CREDSA.SHP"); +#endif + + Alloc_Object(new ScorePrintClass(TXT_SCORE_ENDCRED, _credtx[house], _credty[house], pal)); + Call_Back_Delay(15); + + credobj = Alloc_Object(new ScoreCredsClass(_credsx[house], _credsy[house], credshape, 32, 2)); + minval = PlayerPtr->Available_Money() / 100; + + /* + ** Print out total credits left at end of scenario + */ + i = -50; + + do { + add = 5; + if ((PlayerPtr->Available_Money() - i) > 100 ) add += 15; + if ((PlayerPtr->Available_Money() - i) > 500 ) add += 30; + if ((PlayerPtr->Available_Money() - i) > 1000) add += PlayerPtr->Available_Money() / 40; + if (add < minval) add = minval; + i += add; + + if (i < 0) i=0; + + Set_Font_Palette(pal); + Count_Up_Print("%d", i, PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Call_Back_Delay(2); +/*BG if (Keyboard->Check()) { + Count_Up_Print("%d", PlayerPtr->Available_Money(), PlayerPtr->Available_Money(), _credpx[house], _credpy[house]); + Keyboard->Clear(); + break; + }*/ + } while (i < PlayerPtr->Available_Money()) ; + + delete ScoreObjs[credobj]; + ScoreObjs[credobj] = 0; +} + + +/*************************************************************************** + * SCORECLASS::PRINT_MINUTES -- Print out hours/minutes up to max * + * * + * Same as count-up-print, but for the time * + * * + * INPUT: current minute count and maximum * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void ScoreClass::Print_Minutes(int minutes) +{ + char str[20]; + if (minutes >= 60) { + if ((minutes/60) > 9) minutes = (9*60 + 59); + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT1), (minutes / 60), (minutes % 60)); + } else { + sprintf(str, Text_String(TXT_SCORE_TIMEFORMAT2), minutes); + } + SeenPage.Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Print(str, 275*RESFACTOR, 9*RESFACTOR, TBLACK, TBLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Count_Up_Print -- Prints a number (up to its max) into a string, cleanly. * + * * + * This routine prints out a number (like 70) or its maximum number, into a string, onto * + * the screen, on a clean section of the screen, and blits it forward to the seenpage so you* + * can print without flashing and can print over something (to count up %'s). * + * * + * INPUT: str = string to print into * + * percent = # to print * + * max = # to print if percent > max * + * xpos = x pixel coord * + * ypos = y pixel coord * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/07/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Count_Up_Print(char *str, int percent, int maxval, int xpos, int ypos) +{ + char destbuf[64]; + + sprintf(destbuf, str, percent <= maxval ? percent : maxval); + SeenPage.Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#ifdef WIN32 + PseudoSeenBuff->Print( destbuf, xpos * RESFACTOR, ypos * RESFACTOR, TBLACK, BLACK); +#endif +} + + +/*********************************************************************************************** + * ScoreClass::Input_Name -- Gets the name from the keyboard * + * * + * This routine handles keyboard input, and does a nifty zooming letter effect too. * + * * + * INPUT: str = string to put user's typing into * + * xpos = x pixel coord * + * ypos = y pixel coord * + * pal = text remapping palette to print using * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/15/1995 BWG : Created. * + *=============================================================================================*/ +void ScoreClass::Input_Name(char str[], int xpos, int ypos, char const pal[]) +{ + int key = 0; + int ascii, index=0; + + void const * keystrok = MFCD::Retrieve("KEYSTROK.AUD"); + + /* + ** Ready the hidpage so it can restore background under zoomed letters + */ + SeenPage.Blit(HidPage); + + /* + ** Put a copy of the high score area on a spare area of the hidpage, so + ** we can use it to restore the letter's background instead of filling + ** with black. + */ + HidPage.Blit(HidPage, 0, 100*RESFACTOR, 0, 0, 100*RESFACTOR, 100*RESFACTOR); +// jmarshall - temp disable high school writing. +// do { +//// jmarshall +// g_globalKeyNumType = KN_NONE; +// g_globalKeyFlags = 0; +// UserInput.Process_Input(); +//// jmarshall end +// Call_Back(); +// Animate_Score_Objs(); +// Animate_Cursor(index, ypos); +// if (Keyboard->Check()) { +// key = Keyboard->To_ASCII(Keyboard->Get()) & 0xFF; +// Call_Back(); +// +// if (index == MAX_FAMENAME_LENGTH-2) { +// while (Keyboard->Check()) { +// Keyboard->Get(); +// } +// } +// +// /* +// ** If they hit 'backspace' when they're on the last letter, +// ** turn it into a space instead. +// */ +// if ((key == KA_BACKSPACE) && (index == MAX_FAMENAME_LENGTH-2) ) { +// if (str[index] && str[index]!=32) key = 32; +// } +// if (key == KA_BACKSPACE) { //if (key == KN_BACKSPACE) { +// if (index) { +// str[--index] = 0; +// +// int xposindex6 = (xpos+(index*6))*RESFACTOR; +// HidPage.Blit(SeenPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// #ifdef WIN32 +// HidPage.Blit(*PseudoSeenBuff, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// #endif +// HidPage.Blit(HidPage, xposindex6, (ypos-100)*RESFACTOR, xposindex6, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// } +// +// } else if (key != KA_RETURN) { //else if (key != KN_RETURN && key!=KN_KEYPAD_RETURN) { +// ascii = key; //ascii = KN_To_KA(key); +// if (ascii >= 'a' && ascii <= 'z') ascii -= ('a' - 'A'); +// if ( (ascii >= '!' && ascii <= KA_TILDA) || ascii == ' ') { +// HidPage.Blit(SeenPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// #ifdef WIN32 +// HidPage.Blit(*PseudoSeenBuff, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// #endif +// HidPage.Blit(HidPage, (xpos + (index*6))*RESFACTOR, (ypos-100)*RESFACTOR, (xpos + (index*6))*RESFACTOR, ypos*RESFACTOR, 6*RESFACTOR, 6*RESFACTOR); +// str[index] = ascii; +// str[index+1] = 0; +// +// int objindex; +//#ifdef WIN32 +// Play_Sample(keystrok, 255, Options.Normalize_Volume(150)); +//#else +// Play_Sample(keystrok, 255, Options.Normalize_Volume(105)); +//#endif +// objindex = Alloc_Object(new ScoreScaleClass(str+index, xpos+(index*6), ypos, pal)); +// while (ScoreObjs[objindex]) Call_Back_Delay(1); +// +// if (index < (MAX_FAMENAME_LENGTH-2) ) index++; +// } +// } +// } +// } while (key != KA_RETURN); // } while(key != KN_RETURN && key!=KN_KEYPAD_RETURN); +// jmarshall end +} + + +void Animate_Cursor(int pos, int ypos) +{ + static int _lastpos = 0, _state; + static CDTimerClass _timer; + + ypos += 6; // move cursor to bottom of letter + + ypos *= RESFACTOR; + + // If they moved the cursor, erase old one and force state=0, to make green draw right away + if (pos != _lastpos) { + HidPage.Blit(SeenPage, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#ifdef WIN32 + HidPage.Blit(*PseudoSeenBuff, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos-100*RESFACTOR, (HALLFAME_X + (_lastpos*6))*RESFACTOR, ypos, 6*RESFACTOR, 1*RESFACTOR); +#endif + _lastpos = pos; + _state = 0; + } + SeenBuff.Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#ifdef WIN32 + PseudoSeenBuff->Draw_Line((HALLFAME_X + (pos*6))*RESFACTOR, ypos, (HALLFAME_X + (pos*6)+5)*RESFACTOR, ypos, _state ? LTBLUE : TBLACK); +#endif + /* + ** Toggle the color of the cursor, green or black, if it's time to do so. + */ + if (!_timer) { + _state ^= 1; + _timer = 5; + } +} + + +/*************************************************************************** + * Draw_InfantryMen -- Draw all the guys on the score screen * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMen() +{ + int k; + +// Only draw the infantrymen if we're playing USSR... Allies wouldn't execute +// people like that. + + /* + ** First restore the background + */ + HidPage.Blit(HidPage, BARGRAPH_X, CASUALTY_Y, 0, 0, 320-BARGRAPH_X, 34); + Set_Logic_Page(HidPage); + + /* + ** Then draw all the infantrymen on the clean hidpage + */ + for (k = 0; k < NUMINFANTRYMEN; k++) Draw_InfantryMan(k); + /* + ** They'll all be blitted over to the seenpage after the graphs are drawn + */ +} + +/*************************************************************************** + * Draw_InfantryMan -- Draw one guy in score screen, update animation * + * * + * This routine draws one of the infantrymen in the "Casualties" area * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Draw_InfantryMan(int index) +{ + int stage; + + /* If the infantryman's dead, just abort this function */ + if (InfantryMan[index].anim == -1) return; + + stage = InfantryMan[index].stage + InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Frame; + + CC_Draw_Shape(InfantryMan[index].shapefile, + stage, + InfantryMan[index].xpos, + InfantryMan[index].ypos, + WINDOW_MAIN, + SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + InfantryMan[index].remap, + DisplayClass::UnitShadow); + /* + ** see if it's time to run a new anim + */ + if (--InfantryMan[index].delay <= 0) { + InfantryMan[index].delay = 3; + if (++InfantryMan[index].stage >= InfantryMan[index].Class->DoControls[InfantryMan[index].anim].Count) { + + /* + ** was he playing a death anim? If so, and it's done, erase him + */ + if (InfantryMan[index].anim >= DO_GUN_DEATH) { + InfantryMan[index].anim = -1; + } else { + New_Infantry_Anim(index, DO_STAND_READY); + } + } + } +} + + +/*************************************************************************** + * New_Infantry_Anim -- Start up a new animation for one of the infantrymen* + * * + * * + * * + * INPUT: index: which of the 30 infantrymen to affect * + * anim: which animation sequence to start him into * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void New_Infantry_Anim(int index, int anim) +{ + InfantryMan[index].anim = anim; + InfantryMan[index].stage = 0; + if (anim >= DO_GUN_DEATH) { + InfantryMan[index].delay = 1; // start right away + } else { + InfantryMan[index].delay = NonCriticalRandomNumber & 15; + } +} + + +/*************************************************************************** + * Draw_Bar_Graphs -- Draw "Casualties" bar graphs * + * * + * * + * * + * INPUT: i = current count of how far to draw graph * + * gkilled = # of GDI forces killed (adjusted to fit in space) * + * nkilled = # of Nod forces killed (adjusted to fit in space) * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + * 07/02/1996 BWG : Removed references to civilians. * + *=========================================================================*/ +void Draw_Bar_Graphs(int i, int gkilled, int nkilled) +{ + + if (gkilled) { + LogicPage->Fill_Rect(0, 0+4*RESFACTOR, 0+min(i, gkilled)*RESFACTOR, 0+5*RESFACTOR, RED); + LogicPage->Draw_Line(0+1*RESFACTOR, 0+6*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+6*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, (0+min(i, gkilled)+1)*RESFACTOR, 0+5*RESFACTOR, TBLACK); + if (i <= gkilled) { + int anim = InfantryMan[i/11].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim(i/11, DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim(i/11, DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } + if (nkilled) { + LogicPage->Fill_Rect( 0, 0+16*RESFACTOR, 0+min(i, nkilled)*RESFACTOR, 0+17*RESFACTOR, LTCYAN); + LogicPage->Draw_Line( 0+1*RESFACTOR, 0+18*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+18*RESFACTOR, TBLACK); + LogicPage->Draw_Line((0+MIN(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, (0+min(i, nkilled)+1)*RESFACTOR, 0+17*RESFACTOR, TBLACK); + if (i <= nkilled) { + int anim = InfantryMan[(NUMINFANTRYMEN/2)+(i/11)].anim; + if (anim!=-1 && anim < DO_GUN_DEATH) { + if (i/11) { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH + (NonCriticalRandomNumber & 3)); + } else { + New_Infantry_Anim((NUMINFANTRYMEN/2)+(i/11), DO_GUN_DEATH); + } +// Sound_Effect(Random_Pick(VOC_SCREAM1, VOC_SCREAM5)); + } + } + } +} + + +/*************************************************************************** + * Call_Back_Delay -- Combines Call_Back() and Delay() functions * + * * + * This is just to cut down on code size and typing a little. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/13/1995 BWG : Created. * + *=========================================================================*/ +void Call_Back_Delay(int time) +{ + Device_Present(); + + if (time < 0 ) time = 0; + if (time > 60) time = 60; + CDTimerClass cd; + CDTimerClass callbackcd = 0; + + if (!ControlQ) { + if (Keyboard->Down(KN_LCTRL) && Keyboard->Down(KN_Q)) { + ControlQ = 1; + Keyboard->Clear(); + } + } + if (ControlQ) time=0; + + cd = time; +// StreamLowImpact = true; + do { + if (callbackcd == 0) { + Call_Back(); + callbackcd = TIMER_SECOND/4; + } + Animate_Score_Objs(); + Device_Present(); + } while (cd); + //StreamLowImpact = false; +} + + +void Animate_Score_Objs() +{ +#ifdef WIN32 + StillUpdating = false; + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + PseudoSeenBuff->Blit(SeenPage); + } +#endif + for (int i = 0; i < MAXSCOREOBJS; i++) { + if (ScoreObjs[i]) { + ScoreObjs[i]->Update(); + } + } +} + +char *Int_Print(int a) +{ + static char str[10]; + + sprintf(str, "%d", a); + return str; +} + + +/*********************************************************************************************** + * Multi_Score_Presentation -- Multiplayer routine to display score screen. * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/11/1995 BWG: Created. * + *=============================================================================================*/ +extern int CopyType; + +void Multi_Score_Presentation(void) +{ +#if (0)//PG + char remap[16]; +#ifdef WIN32 + GraphicBufferClass *pseudoseenbuff = new GraphicBufferClass(320, 200, (void*)NULL); + PseudoSeenBuff = new GraphicBufferClass(SeenBuff.Get_Width(),SeenBuff.Get_Height(),(void*)NULL); +#endif + + int i,k; + void *oldfont; + int oldfontxspacing = FontXSpacing; + + FontXSpacing = 0; + Map.Override_Mouse_Shape(MOUSE_NORMAL); +// Theme.Queue_Song(THEME_WIN); + + BlackPalette.Set(); + SeenPage.Clear(); + HidPage.Clear(); + Hide_Mouse(); + void *anim = Open_Animation("MLTIPLYR.WSA", NULL, 0L, (WSAOpenType)(WSA_OPEN_FROM_MEM | WSA_OPEN_TO_PAGE), ScorePalette); + /* + ** Display the background animation + */ +#ifdef WIN32 + pseudoseenbuff->Clear(); + Animate_Frame(anim, *pseudoseenbuff, 1); +for(int x=0; x<256; x++) memset(&PaletteInterpolationTable[x][0],x,256); +CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , 0); +#else + Animate_Frame(anim, HidPage, 1); + HidPage.Blit(SeenPage); +#endif + ScorePalette.Set(FADE_PALETTE_FAST, Call_Back); + + int frame = 1; + while (frame < Get_Animation_Frame_Count(anim)) { +#ifdef WIN32 + Animate_Frame(anim, *pseudoseenbuff, frame++); + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , &SeenBuff , NULL); + CopyType = 0; +#else + Animate_Frame(anim, SeenPage, frame++); +#endif + Call_Back_Delay(2); + } + Close_Animation(anim); + +#ifdef WIN32 + CopyType = 1; + Interpolate_2X_Scale(pseudoseenbuff , PseudoSeenBuff , NULL); + CopyType = 0; +#endif + + /* Change to the six-point font for Text_Print */ + oldfont = Set_Font(ScoreFontPtr); + Call_Back(); + + Set_Logic_Page(SeenBuff); + +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 113, 13, _greenpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_SCORE_TOP, 130, 13, _greenpal)); +#endif + Call_Back_Delay(5); + Alloc_Object(new ScorePrintClass(TXT_COMMANDER, 27, 31, _greenpal)); + Call_Back_Delay(10); +#ifdef FRENCH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 113, 31, _greenpal)); +#endif +#ifdef GERMAN + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 118, 31, _greenpal)); +#endif +#ifdef ENGLISH + Alloc_Object(new ScorePrintClass(TXT_BATTLES_WON, 126, 31, _greenpal)); +#endif + Call_Back_Delay(13); + Alloc_Object(new ScorePrintClass(TXT_KILLS_COLON, 249, 31, _greenpal)); + Call_Back_Delay(6); + + /* + ** Move all the scores over a notch if there's more games than can be + ** shown (which is known by Session.CurGame == MAX_MULTI_GAMES-1); + */ + if (Session.CurGame == MAX_MULTI_GAMES-1) { + for (i = 0; i < MAX_MULTI_NAMES; i++) { + for (k = 0; k < MAX_MULTI_GAMES-1; k++) { + Session.Score[i].Kills[k] = Session.Score[i].Kills[k+1]; + } + } + } + + int y = 41; + for (i = 0; i < MAX_MULTI_NAMES; i++) { + if (strlen(Session.Score[i].Name)) { + int color = Session.Score[i].Color; + remap[ 8] = ColorRemaps[color].FontRemap[11]; + remap[ 6] = ColorRemaps[color].FontRemap[12]; + remap[ 4] = ColorRemaps[color].FontRemap[13]; + remap[ 2] = ColorRemaps[color].FontRemap[14]; + remap[14] = ColorRemaps[color].FontRemap[15]; + + Alloc_Object(new ScorePrintClass(Session.Score[i].Name, 15, y, remap)); + Call_Back_Delay(20); + + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Wins), 118, y, remap)); + Call_Back_Delay(6); + + for (k = 0; k <= min(Session.CurGame, MAX_MULTI_GAMES-2); k++) { + if (Session.Score[i].Kills[k] >= 0) { + Alloc_Object(new ScorePrintClass(Int_Print(Session.Score[i].Kills[k]), 225+(24*k), y, remap)); + Call_Back_Delay(6); + } + } + y += 12; + } + } + +#if defined(GERMAN) || defined(FRENCH) + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 95 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#else + Alloc_Object(new ScorePrintClass(TXT_CLICK_CONTINUE, 109 /*(320-strlen(Text_String(TXT_MAP_CLICK2)))/2*/, 190, _yellowpal)); +#endif + Cycle_Wait_Click(false); + +/* get rid of all the animating objects */ + for (i = 0; i < MAXSCOREOBJS; i++) if (ScoreObjs[i]) { + delete ScoreObjs[i]; + ScoreObjs[i] = 0; + } + + Theme.Queue_Song(THEME_NONE); + + BlackPalette.Set(FADE_PALETTE_FAST, NULL); + SeenPage.Clear(); + GamePalette.Set(); +#ifdef WIN32 + delete PseudoSeenBuff; +#endif + Set_Font(oldfont); + FontXSpacing = oldfontxspacing; + ControlQ = 0; + Show_Mouse(); +#endif +} + +void ScoreClass::Init(void) +{ + Score = 0; + NKilled = 0; + GKilled = 0; + CKilled = 0; + NBKilled = 0; + GBKilled = 0; + CBKilled = 0; + NHarvested = 0; + GHarvested = 0; + CHarvested = 0; + ElapsedTime = 0; + RealTime = 0; + ChangingGun = 0; +} diff --git a/code/redalert/score.h b/code/redalert/score.h new file mode 100644 index 0000000..e5d6f95 --- /dev/null +++ b/code/redalert/score.h @@ -0,0 +1,145 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCORE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCORE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 19, 1994 * + * * + * Last Update : April 19, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCORE_H +#define SCORE_H + +#include "unit.h" +#include "building.h" + +class ScoreClass { + public: + ScoreClass(void) {}; + ScoreClass(NoInitClass const &) {}; + + int Score; + int NKilled; + int GKilled; + int CKilled; + int NBKilled; + int GBKilled; + int CBKilled; + int NHarvested; + int GHarvested; + int CHarvested; + unsigned long ElapsedTime; + TTimerClass RealTime; + + void Init(void); + void Presentation(void); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + private: + unsigned char *ChangingGun; + + void ScoreDelay(int ticks); + void Pulse_Bar_Graph(void); + void Print_Graph_Title(int,int); + void Print_Minutes(int minutes); + void Count_Up_Print(char *str, int percent, int max, int xpos, int ypos); + void Show_Credits(int house, char const pal[]); + void Do_GDI_Graph(void const * yellowptr, void const * redptr, int gdikilled, int nodkilled, int ypos); + void Do_Nod_Casualties_Graph(void); + void Do_Nod_Buildings_Graph(void); + void Input_Name(char str[], int xpos, int ypos, char const pal[]); +}; + +class ScoreAnimClass { + public: + ScoreAnimClass(int x, int y, void const * data); + int XPos; + int YPos; + CDTimerClass Timer; + void const * DataPtr; + virtual void Update(void) {} ; + virtual ~ScoreAnimClass(void) {DataPtr=0;} ; +}; + +class ScoreCredsClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + void const * CashTurn; + void const * Clock1; + + virtual void Update(void); + ScoreCredsClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreCredsClass(void) {CashTurn=0;Clock1=0;}; +}; + +class ScoreTimeClass : public ScoreAnimClass { + public: + int Stage; + int MaxStage; + int TimerReset; + virtual void Update(void); + ScoreTimeClass(int xpos, int ypos, void const * data, int max, int timer); + virtual ~ScoreTimeClass(void) {}; +}; + +class ScorePrintClass : public ScoreAnimClass { + public: + int Background; + int Stage; + void const * PrimaryPalette; + virtual void Update(void); + ScorePrintClass(void const * string, int xpos, int ypos, void const * palette, int background=TBLACK); + ScorePrintClass( int string, int xpos, int ypos, void const * palette, int background=TBLACK); + virtual ~ScorePrintClass(void) {PrimaryPalette=0;}; +}; + +class ScoreScaleClass : public ScoreAnimClass { + public: + int Stage; + char const * Palette; + virtual void Update(void); + ScoreScaleClass(void const * data, int xpos, int ypos, char const pal[]); + virtual ~ScoreScaleClass(void) {Palette=0;}; + +}; + +#define MAXSCOREOBJS 8 +extern ScoreAnimClass *ScoreObjs[MAXSCOREOBJS]; + +void Multi_Score_Presentation(void); + +#endif diff --git a/code/redalert/screen.h b/code/redalert/screen.h new file mode 100644 index 0000000..9913640 --- /dev/null +++ b/code/redalert/screen.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCREEN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCREEN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 2, 1994 * + * * + * Last Update : June 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCREEN_H +#define SCREEN_H + + +class ScreenClass +{ + /* + ** The mouse shape is controlled by these variables. These + ** hold the current mouse shape (so resetting won't be needlessly performed) and + ** the normal default mouse shape (when arrow shapes are needed). + */ + MouseShapeType CurrentMouseShape; + MouseShapeType NormalMouseShape; + + public: + + ScreenClass(void) { + CurrentMouseShape = SHP_NONE; + NormalMouseShape = SHP_MOUSE; + }; + + + Init(void); + Set_Default_Mouse(MouseShapeType mouse); + Force_Mouse_Shape(MouseShapeType mouse); + + unsigned char *GamePalette; + unsigned char *BlackPalette; +}; + +#endif diff --git a/code/redalert/scroll.cpp b/code/redalert/scroll.cpp new file mode 100644 index 0000000..0ad225f --- /dev/null +++ b/code/redalert/scroll.cpp @@ -0,0 +1,266 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCROLL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/08/95 * + * * + * Last Update : August 25, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ScrollClass::AI -- Handles scroll AI processing. * + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define SCROLL_DELAY 1 +#else +#define SCROLL_DELAY 2 +#endif + +CDTimerClass ScrollClass::Counter; + + +/*********************************************************************************************** + * ScrollClass::ScrollClass -- Constructor for the scroll class object. * + * * + * This is the constructor for the scroll class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +ScrollClass::ScrollClass(void) : + IsAutoScroll(true) +{ + Counter = SCROLL_DELAY; + Inertia = 0; +} + + +/*********************************************************************************************** + * ScrollClass::AI -- Handles scroll AI processing. * + * * + * This routine is called every game frame for purposes of input processing. * + * * + * INPUT: input -- Reference to the keyboard/mouse event that just occurred. * + * * + * x,y -- The mouse coordinates. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + * 08/10/1995 JLB : Revamped for free smooth scrolling. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +void ScrollClass::AI(KeyNumType &input, int x, int y) +{ + bool player_scrolled=false; + static DirType direction; + int rate; + + + /* + ** If rubber band mode is in progress, then don't allow scrolling of the tactical map. + */ + if (!IsRubberBand /*&& !IsTentative*/) { + + /* + ** Special check to not scroll within the special no-scroll regions. + */ + bool noscroll = false; + + if (!noscroll) { + bool at_screen_edge = (y == 0 || x == 0 || x >= SeenBuff.Get_Width()-1 || y >= SeenBuff.Get_Height()-1); + + /* + ** Verify that the mouse is over a scroll region. + */ + if (Inertia || at_screen_edge) { + if (at_screen_edge) { + + player_scrolled=true; + + /* + ** Adjust the mouse coordinates to emphasize the + ** cardinal directions over the diagonals. + */ + int altx = x; + if (altx < 50 * RESFACTOR) altx -= ((50 * RESFACTOR)-altx); + altx = max(altx, 0); + if (altx > ((320-50) * RESFACTOR)) altx += altx-((320-50) * RESFACTOR); + altx = min(altx, (320 * RESFACTOR)); + if (altx > (50 * RESFACTOR) && altx < ((320-50) * RESFACTOR)) { + altx += (((320/2) * RESFACTOR)-altx)/2; + } + + int alty = y; + if (alty < (50 * RESFACTOR)) alty -= (50 * RESFACTOR)-alty; + alty = max(alty, 0); + if (alty > (150 * RESFACTOR)) alty += alty-(150 * RESFACTOR); + alty = min(alty, 200 * RESFACTOR); + + direction = (DirType)Desired_Facing256((320/2) * RESFACTOR, (200/2) * RESFACTOR, altx, alty); + } + + int control = Dir_Facing(direction); + + /* + ** The mouse is over a scroll region so set the mouse shape accordingly if the map + ** can be scrolled in the direction indicated. + */ + static int _rate[9] = { + 0x00E0*RESFACTOR, + 0x00C0*RESFACTOR, + 0x00A0*RESFACTOR, + 0x0080*RESFACTOR, + 0x0060*RESFACTOR, + 0x0040*RESFACTOR, + 0x0020*RESFACTOR, + 0x0010*RESFACTOR, + 0x0008*RESFACTOR + }; + if (Debug_Map) { + rate = Options.ScrollRate+1; + } else { + rate = 8-Inertia; + } + + if (rate < Options.ScrollRate+1) { + rate = Options.ScrollRate+1; + Inertia = 8-rate; + } + + /* + ** Increase the scroll rate if the mouse button is held down. + */ + // if (Keyboard->Down(KN_LMOUSE)) { + // rate = Bound(rate-3, 0, 4); + // } + if (Keyboard->Down(KN_RMOUSE)) { + rate = Bound(rate+1, 4, (int)(sizeof(_rate)/sizeof(_rate[0]))-1); + } + + /* + ** If options indicate that scrolling should be forced to + ** one of the 8 facings, then adjust the direction value + ** accordingly. + */ + direction = Facing_Dir(Dir_Facing(direction)); + + int distance = _rate[rate]/2; + + if (!Scroll_Map(direction, distance, false)) { + Override_Mouse_Shape((MouseType)(MOUSE_NO_N+control), false); + } else { + Override_Mouse_Shape((MouseType)(MOUSE_N+control), false); + + /* + ** If the mouse button is pressed or auto scrolling is active, then scroll + ** the map if the delay counter indicates. + */ + if (Keyboard->Down(KN_LMOUSE) || IsAutoScroll) { + distance = _rate[rate]; + + if (Debug_Map) { + Scroll_Map(direction, distance, true); + Counter = SCROLL_DELAY; + } else { + distance = _rate[rate]; + Scroll_Map(direction, distance, true); + + if (Counter == 0 && player_scrolled) { + Counter = SCROLL_DELAY; + Inertia++; + } + } + } + } + + } + + if (!Debug_Map && !player_scrolled) { + if (!Counter) { + Inertia--; + if (Inertia<0) Inertia++; + Counter = SCROLL_DELAY; + } + } + + } + } + + HelpClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * ScrollClass::Set_Autoscroll -- Turns autoscrolling on or off. * + * * + * This routine controls the autoscrolling setting. Autoscroll, when active, will cause the * + * map to scroll if the mouse is held over the scroll region. This is regardless of whether * + * any mouse button is held down or not. * + * * + * INPUT: control -- Should the autoscroll be turned on? * + * 0 = turn off * + * 1 = turn on * + * -1 = toggle current setting * + * * + * OUTPUT: Returns with the old setting of the autoscroll flag. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/10/1995 JLB : Created. * + *=============================================================================================*/ +bool ScrollClass::Set_Autoscroll(int control) +{ + bool old = IsAutoScroll; + + switch (control) { + case -1: + IsAutoScroll = !IsAutoScroll; + break; + + default: + IsAutoScroll = control; + break; + } + return(old); +} + + diff --git a/code/redalert/scroll.h b/code/redalert/scroll.h new file mode 100644 index 0000000..b363493 --- /dev/null +++ b/code/redalert/scroll.h @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SCROLL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SCROLL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/18/94 * + * * + * Last Update : November 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SCROLL_H +#define SCROLL_H + +#include "help.h" + + +class ScrollClass: public HelpClass +{ + /* + ** If map scrolling is automatic, then this flag is true. Automatic scrolling will + ** cause the map to scroll if the mouse is in the scroll region, regardless of + ** whether or not the mouse button is held down. + */ + unsigned IsAutoScroll:1; + + /* + ** Scroll speed is regulated by this count down timer. When this value reaches zero, + ** scroll the map in the direction required and reset this timer. + */ + static CDTimerClass Counter; + + /* + ** Inertia control for scrolling + */ + int Inertia; + + public: + ScrollClass(void); + ScrollClass(NoInitClass const & x) : HelpClass(x) {}; + + bool Set_Autoscroll(int control); + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Init_IO(void) {Counter = 0;HelpClass::Init_IO();}; +}; + +#endif diff --git a/code/redalert/sdata.cpp b/code/redalert/sdata.cpp new file mode 100644 index 0000000..65488c4 --- /dev/null +++ b/code/redalert/sdata.cpp @@ -0,0 +1,568 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SDATA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "type.h" + + +static SmudgeTypeClass const Crater1 ( + SMUDGE_CRATER1, + "CR1", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater2 ( + SMUDGE_CRATER2, + "CR2", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater3 ( + SMUDGE_CRATER3, + "CR3", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater4 ( + SMUDGE_CRATER4, + "CR4", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater5 ( + SMUDGE_CRATER5, + "CR5", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Crater6 ( + SMUDGE_CRATER6, + "CR6", + TXT_CRATER, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + true // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch1 ( + SMUDGE_SCORCH1, + "SC1", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch2 ( + SMUDGE_SCORCH2, + "SC2", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch3 ( + SMUDGE_SCORCH3, + "SC3", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch4 ( + SMUDGE_SCORCH4, + "SC4", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch5 ( + SMUDGE_SCORCH5, + "SC5", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Scorch6 ( + SMUDGE_SCORCH6, + "SC6", + TXT_SCORCH, + 1,1, // Width and height of smudge (in icons). + false, // Is this a building bib? + false // Is this a crater smudge? +); + +static SmudgeTypeClass const Bibx1 ( + SMUDGE_BIB1, + "BIB1", + TXT_BIB, + 4,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx2 ( + SMUDGE_BIB2, + "BIB2", + TXT_BIB, + 3,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); +static SmudgeTypeClass const Bibx3 ( + SMUDGE_BIB3, + "BIB3", + TXT_BIB, + 2,2, // Width and height of smudge (in icons). + true, // Is this a building bib? + false // Is this a crater smudge? +); + + +/*********************************************************************************************** + * SmudgeTypeClass::SmudgeTypeClass -- Constructor for smudge type objects. * + * * + * This constructor is used to create the smudge type objects. These type objects contain * + * static information about the various smudge types supported in the game. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass::SmudgeTypeClass( + SmudgeType smudge, + char const * ininame, + int fullname, + int width, + int height, + bool isbib, + bool iscrater) : + ObjectTypeClass( + RTTI_SMUDGETYPE, + int(smudge), + false, + true, + false, + false, + true, + true, + false, + fullname, + ininame), + Type(smudge), + Width(width), + Height(height), + IsCrater(iscrater), + IsBib(isbib) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator new -- Allocate a smudge type object from the memory pool. * + * * + * This will allocate a smudge type class object from the special memory pool for that * + * purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the newly allocated smudge type class object. If there is insufficient* + * memory in the pool to fulfill the request, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * SmudgeTypeClass::operator new(size_t) +{ + return(SmudgeTypes.Alloc()); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::operator delete -- Returns a smudge type class object to the pool. * + * * + * This will return the smudge type class object back to the memory pool from whence it * + * was originally allocated. * + * * + * INPUT: pointer -- Pointer to the smudge type class object to return the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::operator delete(void * pointer) +{ + SmudgeTypes.Free((SmudgeTypeClass *)pointer); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init_Heap -- Initialize the smudge type class object heap. * + * * + * This will initialize the special heap for smudge type class objects, by pre-allocated * + * all known smudge types. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once and before the rules.ini file is processed. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init_Heap(void) +{ + /* + ** These smudge type class objects must be allocated in the exact order that they + ** are specified in the SmudgeType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new SmudgeTypeClass(Crater1); // SMUDGE_CRATER1 + new SmudgeTypeClass(Crater2); // SMUDGE_CRATER2 + new SmudgeTypeClass(Crater3); // SMUDGE_CRATER3 + new SmudgeTypeClass(Crater4); // SMUDGE_CRATER4 + new SmudgeTypeClass(Crater5); // SMUDGE_CRATER5 + new SmudgeTypeClass(Crater6); // SMUDGE_CRATER6 + new SmudgeTypeClass(Scorch1); // SMUDGE_SCORCH1 + new SmudgeTypeClass(Scorch2); // SMUDGE_SCORCH2 + new SmudgeTypeClass(Scorch3); // SMUDGE_SCORCH3 + new SmudgeTypeClass(Scorch4); // SMUDGE_SCORCH4 + new SmudgeTypeClass(Scorch5); // SMUDGE_SCORCH5 + new SmudgeTypeClass(Scorch6); // SMUDGE_SCORCH6 + new SmudgeTypeClass(Bibx1); // SMUDGE_BIB1 + new SmudgeTypeClass(Bibx2); // SMUDGE_BIB2 + new SmudgeTypeClass(Bibx3); // SMUDGE_BIB3 +} + + +/*********************************************************************************************** + * SmudgeTypeClass::From_Name -- Converts an ASCII name into a smudge type. * + * * + * This converts an ASCII name into a smudge type number. This is typically necessary * + * when processing scenario INI files and not used otherwise. * + * * + * INPUT: name -- Pointer to the name to convert. * + * * + * OUTPUT: Returns with the SmudgeType number that matches the name supplied. If no match * + * was found, then SMUDGE_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeType SmudgeTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (stricmp(As_Reference(index).IniName, name) == 0) { + return(index); + } + } + } + return(SMUDGE_NONE); +} + + +/*********************************************************************************************** + * SmudgetypeClass::Occupy_List -- Determines occupation list for smudge object. * + * * + * Smudges are always only one icon in dimension, so this routine always returns a cell * + * occupation offset list of the center cell. * + * * + * INPUT: placement -- Is this for placement legality checking only? The normal condition * + * is for marking occupation flags. * + * * + * OUTPUT: Returns occupation list specifying all the cells that the overlay occupies. This * + * is just the center cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +short const * SmudgeTypeClass::Occupy_List(bool) const +{ + static short _occupy[4*4]; + short * ptr = &_occupy[0]; + + for (int x = 0; x < Width; x++) { + for (int y = 0; y < Height; y++) { + *ptr++ = x + (y*MAP_CELL_W); + } + } + *ptr = REFRESH_EOL; + return(_occupy); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Init -- Performs theater specific initializations. * + * * + * Smudge object imagery varies between theaters. This routine will load the appropriate * + * imagery for the theater specified. * + * * + * INPUT: theater -- The theater to prepare for. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Init(TheaterType theater) +{ + if (theater != LastTheater) { + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + SmudgeTypeClass const & smudge = As_Reference(index); + char fullname[_MAX_FNAME+_MAX_EXT]; // Fully constructed smudge data set name. + + _makepath(fullname, NULL, NULL, smudge.IniName, Theaters[theater].Suffix); + ((void const *&)smudge.ImageData) = MFCD::Retrieve(fullname); + } + } +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * SmudgeTypeClass::Display -- Draws a generic version of this smudge type. * + * * + * The scenario object editor will call this routine to display a typical imagery of this * + * smudge object for graphical identification purposes. * + * * + * INPUT: x,y -- Coordinate to render the smudge at. * + * * + * window-- The window to base the coordinate rendering upon. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + void const * ptr = Get_Image_Data(); + + x += WindowList[window][WINDOWX]; + y += WindowList[window][WINDOWY]; + + IsTheaterShape = true; // Smudges are theater specific + if (ptr != NULL) { + for (int w = 0; w < Width; w++) { + for (int h = 0; h < Height; h++) { + CC_Draw_Shape(ptr, w + (h*Width), x + w*ICON_PIXEL_W, y + h*ICON_PIXEL_H, WINDOW_TACTICAL, SHAPE_WIN_REL); + } + } + } + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Prep_For_Add -- Prepares the scenario editor for adding a smudge object. * + * * + * This routine adds smudge objects to the list of objects that the scenario editor can * + * place upon the ground. It is only called from the scenario editor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Prep_For_Add(void) +{ + for (SmudgeType index = SMUDGE_FIRST; index < SMUDGE_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_And_Place -- Creates and places on map, a smudge object. * + * * + * This routine will, in one motion, create a smudge object and place it upon the map. * + * Since placing a smudge on the map will destroy the object, this routine will leave the * + * smudge object count unchanged. Typically, this routine is used by the scenario editor * + * for creating smudges and placing them on the map. * + * * + * INPUT: cell -- The cell to place the smudge object. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +bool SmudgeTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new SmudgeClass(Type, Cell_Coord(cell))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Create_One_Of -- Creates a smudge object of this type. * + * * + * This routine will create a smudge object of the appropriate type. Smudge objects are * + * transitory in nature. They exist only from the point of creation until they are given * + * a spot on the map to reside. At that time the map data is updated and the smudge * + * object is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a created smudge object. If none could be created, then * + * NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * SmudgeTypeClass::Create_One_Of(HouseClass *) const +{ + return(new SmudgeClass(Type, -1)); +} + + +/*********************************************************************************************** + * SmudgeTypeClass::Draw_It -- Renders the smudge image at the coordinate specified. * + * * + * This routine will draw the smudge overlay image at the coordinate (upper left) * + * specified. The underlying terrain icon is presumed to have already been rendered. * + * * + * INPUT: x,y -- Coordinate of the upper left corner of icon to render the smudge object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::Draw_It(int x, int y, int data) const +{ + void const * ptr = Get_Image_Data(); + if (ptr != NULL) { + IsTheaterShape = true; // Smudges are theater specific + CC_Draw_Shape(ptr, data, x, y, WINDOW_TACTICAL, SHAPE_WIN_REL); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * SmudgeTypeClass::One_Time -- Performs one-time initialization * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeTypeClass::One_Time(void) +{ +} + + +/*********************************************************************************************** + * SmudgeTypeClass::As_Reference -- Fetches a reference to the smudge type specified. * + * * + * Use this routine to get a reference to the smudge type class object when given just * + * the smudge type identifier. * + * * + * INPUT: type -- The smudge type identifier to convert into a reference. * + * * + * OUTPUT: Returns with a reference to the smudge type class object. * + * * + * WARNINGS: Be sure that the smudge type specified is legal. An illegal type value will * + * produce undefined results. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +SmudgeTypeClass & SmudgeTypeClass::As_Reference(SmudgeType type) +{ + return(*SmudgeTypes.Ptr(type)); +} diff --git a/code/redalert/search.h b/code/redalert/search.h new file mode 100644 index 0000000..083f9cc --- /dev/null +++ b/code/redalert/search.h @@ -0,0 +1,686 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SEARCH.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEARCH.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/02/96 * + * * + * Last Update : November 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * IndexClass::Add_Index -- Add element to index tracking system. * + * IndexClass::Clear -- Clear index handler to empty state. * + * IndexClass::Count -- Fetch the number of index entries recorded. * + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * IndexClass::IndexClass -- Constructor for index handler. * + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * IndexClass::Is_Present -- Checks for presense of index entry. * + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * IndexClass::Sort_Nodes -- Sorts nodes in preparation for a binary search. * + * IndexClass::~IndexClass -- Destructor for index handler object. * + * compfunc -- Support function for bsearch and bsort. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SEARCH_H +#define SEARCH_H + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#ifndef __BORLANDC__ +#define _USERENTRY +#endif + +/* +** This class is used to create and maintain an index. It does this by assigning a unique +** identifier number to the type objects that it is indexing. The regular binary sort and search +** function are used for speedy index retreival. Typical use of this would be to index pointers to +** normal data objects, but it can be used to hold the data objects themselves. Keep in mind that +** the data object "T" is copied around by this routine in the internal tables so not only must +** it have a valid copy constructor, it must also be efficient. The internal algorithm will +** create an arbitrary number of default constructed objects of type "T". Make sure it has a +** default constructor that is effecient. The constructor need not perform any actual +** initialization since this class has prior knowledge about the legality of these temporary +** objects and doesn't use them until after the copy constructor is used to initialize them. +*/ + +template +class IndexClass +{ + public: + IndexClass(void); + ~IndexClass(void); + + /* + ** Add element to index table. + */ + bool Add_Index(int id, T data); + + /* + ** Removes an index entry from the index table. + */ + bool Remove_Index(int id); + + /* + ** Check to see if index is present. + */ + bool Is_Present(int id) const; + + /* + ** Fetch number of indexes in the table. + */ + int Count(void) const; + + /* + ** Actually a fetch an index data element from the table. + */ + T Fetch_Index(int id) const; + + /* + ** Clear out the index table to null (empty) state. + */ + void Clear(void); + + private: + /* + ** This node object is used to keep track of the connection between the data + ** object and the index identifier number. + */ + struct NodeElement { + int ID; // ID number (must be first element in this structure). + T Data; // Data element assigned to this ID number. + }; + + /* + ** This is the pointer to the allocated index table. It contains all valid nodes in + ** a sorted order. + */ + NodeElement * IndexTable; + + /* + ** This records the number of valid nodes within the index table. + */ + int IndexCount; + + /* + ** The total size (in nodes) of the index table is recorded here. If adding a node + ** would cause the index count to exceed this value, the index table must be resized + ** to make room. + */ + int IndexSize; + + /* + ** If the index table is sorted and ready for searching, this flag will be true. Sorting + ** of the table only occurs when absolutely necessary. + */ + bool IsSorted; + + /* + ** This records a pointer to the last element found by the Is_Present() function. Using + ** this last recorded value can allow quick fetches of data whenever possible. + */ + NodeElement const * Archive; + + //------------------------------------------------------------------------------------- + IndexClass(IndexClass const & rvalue); + IndexClass * operator = (IndexClass const & rvalue); + + /* + ** Increase size of internal index table by amount specified. + */ + bool Increase_Table_Size(int amount); + + /* + ** Check if archive pointer is the same as that requested. + */ + bool Is_Archive_Same(int id) const; + + /* + ** Invalidate the archive pointer. + */ + void Invalidate_Archive(void); + + /* + ** Set archive to specified value. + */ + void Set_Archive(NodeElement const * node); + + /* + ** Search for the node in the index table. + */ + NodeElement const * Search_For_Node(int id) const; + + static int _USERENTRY search_compfunc(void const * ptr, void const * ptr2); +}; + + +/*********************************************************************************************** + * IndexClass::IndexClass -- Constructor for index handler. * + * * + * This constructs an empty index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::IndexClass(void) : + IndexTable(0), + IndexCount(0), + IndexSize(0), + IsSorted(false), + Archive(0) +{ + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::~IndexClass -- Destructor for index handler object. * + * * + * This will destruct and free any resources managed by this index handler. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +IndexClass::~IndexClass(void) +{ + Clear(); +} + + +/*********************************************************************************************** + * IndexClass::Clear -- Clear index handler to empty state. * + * * + * This routine will free all internal resources and bring the index handler into a * + * known empty state. After this routine, the index handler is free to be reused. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Clear(void) +{ + delete [] IndexTable; + IndexTable = 0; + IndexCount = 0; + IndexSize = 0; + IsSorted = false; + Invalidate_Archive(); +} + + +/*********************************************************************************************** + * IndexClass::Increase_Table_Size -- Increase the internal index table capacity. * + * * + * This helper function will increase the capacity of the internal index table without * + * performing any alterations to the index data. Use this routine prior to adding a new * + * element if the existing capacity is insufficient. * + * * + * INPUT: amount -- The number of index element spaces to add to its capacity. * + * * + * OUTPUT: bool; Was the internal capacity increased without error? * + * * + * WARNINGS: If there is insufficient RAM, then this routine will fail. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Increase_Table_Size(int amount) +{ + /* + ** Check size increase parameter for legality. + */ + if (amount < 0) return(false); + + NodeElement * table = new NodeElement[IndexSize + amount]; + if (table != NULL) { + + /* + ** Copy all valid nodes into the new table. + */ + for (int index = 0; index < IndexCount; index++) { + table[index] = IndexTable[index]; + } + + /* + ** Make the new table the current one (and delete the old one). + */ + delete [] IndexTable; + IndexTable = table; + IndexSize += amount; + Invalidate_Archive(); + + /* + ** Return with success flag. + */ + return(true); + } + + /* + ** Failure to allocate the memory results in a failure to increase + ** the size of the index table. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Count -- Fetch the number of index entries recorded. * + * * + * This will return the quantity of index entries that have been recored by this index * + * handler. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with number of recored indecies present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +int IndexClass::Count(void) const +{ + return(IndexCount); +} + + +/*********************************************************************************************** + * IndexClass::Is_Present -- Checks for presense of index entry. * + * * + * This routine will scan for the specified index entry. If it was found, then 'true' is * + * returned. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: bool; Was the index entry found in this index handler object? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Present(int id) const +{ + /* + ** If there are no data elements in the index table, then it can + ** never find the specified index. Check for and return failure + ** in this case. + */ + if (IndexCount == 0) { + return(false); + } + + /* + ** Check to see if this same index element was previously searched for. If + ** so and it was previously found, then there is no need to search for it + ** again -- just return true. + */ + if (Is_Archive_Same(id)) { + return(true); + } + + /* + ** Perform a binary search on the index nodes in order to look for a + ** matching index value. + */ + NodeElement const * nodeptr = Search_For_Node(id); + + /* + ** If a matching index was found, then record it for future reference and return success. + */ + if (nodeptr != 0) { + ((IndexClass *)this)->Set_Archive(nodeptr); + return(true); + } + + /* + ** Could not find element so return failure condition. + */ + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Fetch_Index -- Fetch data from specified index. * + * * + * This routine will find the specified index and return the data value associated with it. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with the data value associated with the index value. * + * * + * WARNINGS: This routine presumes that the index exists. If it doesn't exist, then the * + * default constructed object "T" is returned instead. To avoid this problem, * + * always verfiy the existance of the index by calling Is_Present() first. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +T IndexClass::Fetch_Index(int id) const +{ + if (Is_Present(id)) { + + /* + ** Count on the fact that the archive pointer is always valid after a call to Is_Present + ** that returns "true". + */ + return(Archive->Data); + } + return(T()); +} + + +/*********************************************************************************************** + * IndexClass::Is_Archive_Same -- Checks to see if archive pointer is same as index. * + * * + * This routine compares the specified index ID with the previously recorded index archive * + * pointer in order to determine if they match. * + * * + * INPUT: id -- The index ID to compare to the archive index object pointer. * + * * + * OUTPUT: bool; Does the specified index match the archive pointer? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Is_Archive_Same(int id) const +{ + if (Archive != 0 && Archive->ID == id) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * IndexClass::Invalidate_Archive -- Invalidate the archive pointer. * + * * + * This routine will set the archive pointer to an invalid state. This must be performed * + * if ever the archive pointer would become illegal (such as when the element it refers to * + * is deleted). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Invalidate_Archive(void) +{ + Archive = 0; +} + + +/*********************************************************************************************** + * IndexClass::Set_Archive -- Records the node pointer into the archive. * + * * + * This routine records the specified node pointer. Use this routine when there is a * + * good chance that the specified node will be requested in the immediate future. * + * * + * INPUT: node -- Pointer to the node to assign to the archive. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +void IndexClass::Set_Archive(NodeElement const * node) +{ + Archive = node; +} + + +/*********************************************************************************************** + * IndexClass::Add_Index -- Add element to index tracking system. * + * * + * This routine will record the index information into this index manager object. It * + * associates the index number with the data specified. The data is copied to an internal * + * storage location. * + * * + * INPUT: id -- The ID number to associate with the data. * + * * + * data -- The data to store. * + * * + * OUTPUT: bool; Was the element added without error? Failure indicates that RAM has been * + * exhausted. * + * * + * WARNINGS: The data is COPIED to internal storage. This means that the data object must * + * have a functional and efficient copy constructor and assignment operator. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Add_Index(int id, T data) +{ + /* + ** Ensure that there is enough room to add this index. If not, then increase the + ** capacity of the internal index table. + */ + if (IndexCount + 1 > IndexSize) { + if (!Increase_Table_Size(IndexSize == 0 ? 10 : IndexSize)) { + + /* + ** Failure to increase the size of the index table means failure to add + ** the index element. + */ + return(false); + } + } + + /* + ** Add the data to the end of the index data and then sort the index table. + */ + IndexTable[IndexCount].ID = id; + IndexTable[IndexCount].Data = data; + IndexCount++; + IsSorted = false; + + return(true); +} + + +/*********************************************************************************************** + * IndexClass::Remove_Index -- Find matching index and remove it from system. * + * * + * This will scan through the previously added index elements and if a match was found, it * + * will be removed. * + * * + * INPUT: id -- The index ID to search for and remove. * + * * + * OUTPUT: bool; Was the index element found and removed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +bool IndexClass::Remove_Index(int id) +{ + /* + ** Find the array index into the table that matches the specified id value. + */ + int found_index = -1; + for (int index = 0; index < IndexCount; index++) { + if (IndexTable[index].ID == id) { + found_index = index; + break; + } + } + + /* + ** If the array index was found, then copy all higher index entries + ** downward to fill the vacated location. We cannot use memcpy here because the type + ** object may not support raw copies. C++ defines the assignment operator to deal + ** with this, so that is what we use. + */ + if (found_index != -1) { + + for (int index = found_index+1; index < IndexCount; index++) { + IndexTable[index-1] = IndexTable[index]; + } + IndexCount--; + + NodeElement fake; + fake.ID = 0; + fake.Data = T(); + IndexTable[IndexCount] = fake; // zap last (now unused) element + + Invalidate_Archive(); + return(true); + } + + return(false); +} + + +/*********************************************************************************************** + * compfunc -- Support function for bsearch and bsort. * + * * + * This compare function presumes that its parameters are pointing to NodeElements and that * + * the first "int" in the node is the index ID number to be used for comparison. * + * * + * INPUT: ptr1 -- Pointer to first node. * + * * + * ptr2 -- Pointer to second node. * + * * + * OUTPUT: Returns with the comparision value between the two nodes. * + * * + * WARNINGS: This is highly dependant upon the layout of the NodeElement structure. * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +int _USERENTRY IndexClass::search_compfunc(void const * ptr1, void const * ptr2) +{ + if (*(int const *)ptr1 == *(int const *)ptr2) { + return(0); + } + if (*(int const *)ptr1 < *(int const *)ptr2) { + return(-1); + } + return(1); +} + + +/*********************************************************************************************** + * IndexClass::Search_For_Node -- Perform a search for the specified node ID * + * * + * This routine will perform a binary search on the previously recorded index values and * + * if a match was found, it will return a pointer to the NodeElement. * + * * + * INPUT: id -- The index ID to search for. * + * * + * OUTPUT: Returns with a pointer to the NodeElement that matches the index ID specified. If * + * no matching index could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1996 JLB : Created. * + *=============================================================================================*/ +template +typename IndexClass::NodeElement const * IndexClass::Search_For_Node(int id) const +{ + /* + ** If there are no elements in the list, then it certainly can't find any matches. + */ + if (IndexCount == 0) { + return(0); + } + + /* + ** If the list has not yet been sorted, then do so now. Binary searching requires + ** the list to be sorted. + */ + if (!IsSorted) { + qsort(&IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc); + ((IndexClass *)this)->Invalidate_Archive(); + ((IndexClass *)this)->IsSorted = true; + } + + /* + ** This list is sorted and ready to perform a binary search upon it. + */ + NodeElement node; + node.ID = id; + return((NodeElement const *)bsearch(&node, &IndexTable[0], IndexCount, sizeof(IndexTable[0]), search_compfunc)); +} + + +#endif + + diff --git a/code/redalert/seditdlg.cpp b/code/redalert/seditdlg.cpp new file mode 100644 index 0000000..6b90c0b --- /dev/null +++ b/code/redalert/seditdlg.cpp @@ -0,0 +1,383 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.cpp - "SimpleEditDlgClass": An ok/cancel type dialog with 1 or 2 edit boxes. +// Mostly a hack for what I need right now - not necessarily very flexible. +// Still - I can't believe there isn't a set of dialog classes in here already. +// ajw 07/21/98 + +#include "SEditDlg.h" +#include "WOLEdit.h" + +extern bool cancel_current_msgbox; +bool disable_current_msgbox = false; + +//*********************************************************************************************** +SimpleEditDlgClass::SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 /* = NULL */, int iEditCharsAccept2 /* = 0 */ ) + : iDialogWidth( iDialogWidth ), iEditCharsAccept( iEditCharsAccept ), iEditCharsAccept2( iEditCharsAccept2 ) +{ + // Copy strings. + if( szTitle ) + { + this->szTitle = new char[ strlen( szTitle ) + 1 ]; + strcpy( this->szTitle, szTitle ); + } + else + this->szTitle = NULL; + + if( szPrompt ) + { + this->szPrompt = new char[ strlen( szPrompt ) + 1 ]; + strcpy( this->szPrompt, szPrompt ); + } + else + this->szPrompt = NULL; // I wouldn't try this ... not totally implemented. + + if( szPrompt2 ) + { + this->szPrompt2 = new char[ strlen( szPrompt2 ) + 1 ]; + strcpy( this->szPrompt2, szPrompt2 ); + } + else + this->szPrompt2 = NULL; // This is the flag for whether or not there is a second edit box. + + *szEdit = 0; + *szEdit2 = 0; + + szOkButton = Text_String( TXT_OK ); + szCancelButton = Text_String( TXT_CANCEL ); + szMiddleButton = NULL; +} + +//*********************************************************************************************** +SimpleEditDlgClass::~SimpleEditDlgClass() +{ + delete [] szTitle; + delete [] szPrompt; + delete [] szPrompt2; +} + +//*********************************************************************************************** +void SimpleEditDlgClass::SetButtons( const char* szOk, const char* szCancel, const char* szMiddle /*= NULL*/ ) +{ + szOkButton = szOk; + szCancelButton = szCancel; + szMiddleButton = szMiddle; +} + +//*********************************************************************************************** +const char* SimpleEditDlgClass::Show() +{ + // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int x_margin = 18 * RESFACTOR; // margin width/height + int y_margin = 10 * RESFACTOR; // margin width/height + int d_gap_y = 5 * RESFACTOR; + + int d_dialog_w = iDialogWidth; + int d_dialog_h = szPrompt2 ? ( 29 * RESFACTOR ) + 2 * d_gap_y + 2 * y_margin : ( 19 * RESFACTOR ) + d_gap_y + 2 * y_margin; + if( szTitle ) + d_dialog_h += 10 * RESFACTOR + 2 * d_gap_y; + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + +/* + if( szTitle ) + { + d_title_w = String_Pixel_Width( szTitle ); + d_title_h = 10 * RESFACTOR; + d_title_x = d_dialog_cx - d_title_w / 2; + d_title_y = d_dialog_y + d_gap_y; + } +*/ + + int d_prompt_w = String_Pixel_Width( szPrompt ); + int d_prompt_h = 10 * RESFACTOR; + int d_prompt_x = d_dialog_x + x_margin; + int d_prompt_y = szTitle ? ( d_dialog_y + 3 * d_gap_y + 10 * RESFACTOR ) : ( d_dialog_y + d_gap_y ); + + int d_edit_w = d_dialog_w - d_prompt_w - 2 * x_margin; + int d_edit_h = 10 * RESFACTOR; + int d_edit_x = d_dialog_x + d_prompt_w + x_margin; + int d_edit_y = d_prompt_y; + + int d_prompt2_w = szPrompt2 ? String_Pixel_Width( szPrompt2 ) : 0; + int d_prompt2_h = 10 * RESFACTOR; + int d_prompt2_x = d_dialog_x + x_margin; + int d_prompt2_y = d_prompt_y + d_prompt2_h + d_gap_y; + + int d_edit2_w = d_dialog_w - d_prompt2_w - 2 * x_margin; + int d_edit2_h = 10 * RESFACTOR; + int d_edit2_x = d_dialog_x + d_prompt2_w + x_margin; + int d_edit2_y = d_prompt2_y; + + int d_ok_w, d_ok_h, d_ok_x, d_ok_y, d_cancel_w, d_cancel_h, d_cancel_x, d_cancel_y, d_mid_x, d_mid_y, d_mid_w, d_mid_h; + + if( !szMiddleButton ) + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 10 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 10 * RESFACTOR; + d_cancel_y = d_ok_y; + } + else + { + d_ok_w = 40 * RESFACTOR; + d_ok_h = 9 * RESFACTOR; + d_ok_x = d_dialog_cx - d_ok_w - 30 * RESFACTOR; + d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - y_margin; + + d_mid_w = 40 * RESFACTOR; + d_mid_h = 9 * RESFACTOR; + d_mid_x = d_dialog_cx - ( d_mid_w / 2 ); + d_mid_y = d_ok_y; + + d_cancel_w = 40 * RESFACTOR; + d_cancel_h = 9 * RESFACTOR; + d_cancel_x = d_dialog_cx + 30 * RESFACTOR; + d_cancel_y = d_ok_y; + } + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + BUTTON_MIDDLE, + BUTTON_EDIT, + BUTTON_EDIT2 + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + const char* szReturn = NULL; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, szOkButton, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, szCancelButton, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + TextButtonClass MiddleBtn( BUTTON_MIDDLE, szMiddleButton, TPF_BUTTON, d_mid_x, d_mid_y, d_mid_w ); + + WOLEditClass EditBox( BUTTON_EDIT, szEdit, min( sizeof(szEdit), iEditCharsAccept ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit_x, d_edit_y, d_edit_w, -1, EditClass::ALPHANUMERIC ); + WOLEditClass EditBox2( BUTTON_EDIT2, szEdit2, min( sizeof(szEdit2), iEditCharsAccept2 ), TPF_6PT_GRAD|TPF_NOSHADOW, + d_edit2_x, d_edit2_y, d_edit2_w, -1, EditClass::ALPHANUMERIC ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + if( szMiddleButton ) + MiddleBtn.Add_Tail(*commands); + EditBox.Add_Tail(*commands); + if( szPrompt2 ) + EditBox2.Add_Tail(*commands); + EditBox.Set_Focus(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + if( szTitle ) + Draw_Caption( szTitle, d_dialog_x, d_dialog_y, d_dialog_w ); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( szPrompt, d_prompt_x, d_prompt_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + if( szPrompt2 ) + Fancy_Text_Print( szPrompt2, d_prompt2_x, d_prompt2_y, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT ); + + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + EditBox.Set_Focus(); + EditBox.Flag_To_Redraw(); + } + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + // I really hate to do this, but... ajw + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + } + + if( disable_current_msgbox ) + { + disable_current_msgbox = false; + EditBox.Disable(); + // These do not actually draw. I am actually clearing the "draw" flag! + // Problem is Disable sets them to redraw, and I don't want to, and there is no Flag_To_Redraw( false ). + EditBox.GadgetClass::Draw_Me( true ); + if( szPrompt2 ) + { + EditBox2.Disable(); + EditBox2.GadgetClass::Draw_Me( true ); + } + OkBtn.Disable(); + OkBtn.GadgetClass::Draw_Me( true ); + CancelBtn.Disable(); + CancelBtn.GadgetClass::Draw_Me( true ); + if( szMiddleButton ) + { + MiddleBtn.Disable(); + MiddleBtn.GadgetClass::Draw_Me( true ); + } + } + + /* + ** Process input. + */ + switch( input ) + { +// case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + szReturn = szCancelButton; + process = false; + break; + +// case KN_RETURN: + case ( BUTTON_EDIT | KN_BUTTON ): // (Return pressed while on edit.) + case ( BUTTON_OK | KN_BUTTON ): + szReturn = szOkButton; + process = false; + break; + + case ( BUTTON_MIDDLE | KN_BUTTON ): + szReturn = szMiddleButton; + process = false; + break; + + default: + break; + } + } + + return szReturn; +} + +#endif diff --git a/code/redalert/seditdlg.h b/code/redalert/seditdlg.h new file mode 100644 index 0000000..7db7d7c --- /dev/null +++ b/code/redalert/seditdlg.h @@ -0,0 +1,53 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// SEditDlg.h - "SimpleEditDlgClass": An ok/cancel type dialog with a single edit box. +// ajw 07/21/98 + +#include "function.h" + +class SimpleEditDlgClass +{ +public: + SimpleEditDlgClass( int iDialogWidth, const char* szTitle, const char* szPrompt, int iEditCharsAccept, + const char* szPrompt2 = NULL, int iEditCharsAccept2 = 0 ); + virtual ~SimpleEditDlgClass(); + + const char* Show(); // Shows dialog, returns text of button pressed. + // Unless SetButtons() is used, value will be TXT_OK or TXT_CANCEL string values. + + void SetButtons( const char* szOk, const char* szCancel, const char* szMiddle = NULL ); + + char szEdit[ 300 ]; // iEditCharsAccept upper limit. + char szEdit2[ 300 ]; + +protected: + int iDialogWidth; // X pixels width of entire dialog. + char* szTitle; // Title of dialog, or NULL for no title. + + char* szPrompt; // Text appearing to the left of edit box. + int iEditCharsAccept; // Max length of string allowed in edit, includes null-terminator. + + char* szPrompt2; + int iEditCharsAccept2; + + const char* szOkButton; // Text of button that acts like an Ok button. Appears on left. + const char* szCancelButton; // Text of button that acts like an Cancel button. Appears on right. + const char* szMiddleButton; // Optional middle button text. Null = no middle button. +}; + +#endif diff --git a/code/redalert/sendfile.cpp b/code/redalert/sendfile.cpp new file mode 100644 index 0000000..d5913f0 --- /dev/null +++ b/code/redalert/sendfile.cpp @@ -0,0 +1,877 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : SENDFILE.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : Audust 20th, 1996 * + * * + * Last Update : August 20th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Functions for scenario file transfer between machines * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#if (0)//PG +#include "function.h" + +//#include "WolDebug.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else + +#ifdef WIN32 +#include "tcpip.h" +#else +#include "fakesock.h" +#endif //WIN32 + +#endif //WINSOCK_IPX + +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype); +bool Send_Remote_File ( char *file_name, int gametype ); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +extern bool Is_Mission_Counterstrike (char *file_name); +extern bool Is_Mission_Aftermath (char *file_name); +#endif + +#define RESPONSE_TIMEOUT 60*60 + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; +#endif + +/*********************************************************************************************** + * Get_Scenario_File_From_Host -- Initiates download of scenario file from game host * + * * + * * + * * + * INPUT: ptr to buffer to copy file name into * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file sucessfully downloaded * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:06PM ST : Created * + *=============================================================================================*/ +bool Get_Scenario_File_From_Host(char *return_name, int gametype) +{ + //WWDebugString ("RA95 - In Get_Scenario_From_Host\n"); + + unsigned int file_length; + + SerialPacketType send_packet; + SerialPacketType receive_packet; + GlobalPacketType net_send_packet; + GlobalPacketType net_receive_packet; + unsigned int packet_len; + unsigned short product_id; + + IPXAddressClass sender_address; + + CDTimerClass response_timer; // timeout timer for waiting for responses + + /* + ** Send the scenario request using guaranteed delivery. + */ + if (!gametype) { + memset ((void*)&send_packet, 0, sizeof (send_packet)); + send_packet.Command = SERIAL_REQ_SCENARIO; + NullModem.Send_Message (&send_packet, sizeof(send_packet), 1); + } else { + memset ((void*)&net_send_packet, 0, sizeof (net_send_packet)); + net_send_packet.Command = NET_REQ_SCENARIO; + Ipx.Send_Global_Message (&net_send_packet, sizeof (net_send_packet), + 1, &(Session.HostAddress) ); + } + + + + //WWDebugString ("RA95 - Waiting for response from host\n"); + + /* + ** Wait for host to respond with a file info packet + */ + response_timer = RESPONSE_TIMEOUT; + if (!gametype){ + do { + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == SERIAL_FILE_INFO){ + strcpy (return_name, receive_packet.ScenarioInfo.ShortFileName); + file_length = receive_packet.ScenarioInfo.FileLength; + break; + } + } + } while ( response_timer ); + }else{ + do { + Ipx.Service(); + int receive_packet_length = sizeof (net_receive_packet); + if (Ipx.Get_Global_Message (&net_receive_packet, &receive_packet_length, + &sender_address, &product_id)){ + +//WWDebugString ("RA95 - Got packet from host\n"); +#ifdef WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && sender_address == Session.HostAddress) { +#else //WINSOCK_IPX + if (net_receive_packet.Command == NET_FILE_INFO && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + strcpy (return_name, net_receive_packet.ScenarioInfo.ShortFileName); + file_length = net_receive_packet.ScenarioInfo.FileLength; +//WWDebugString ("RA95 - Got file info packet from host\n"); + break; + } + } + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + } while ( response_timer ); + } + +//char rt[80]; +//sprintf (rt, "RA95 - response_timer = %d\n", response_timer ); +//WWDebugString (rt); + + /* + ** If we timed out then something horrible has happened to the other player so just + ** return failure. + */ + if (!response_timer) return (false); + +// debugprint( "about to download '%s'\n", return_name ); + + /* + ** Receive the file from the host + */ + return (Receive_Remote_File ( return_name, file_length, gametype)); +} + + + + + + + +/*********************************************************************************************** + * Receive_Remote_File -- Handles incoming file download packets from the game host * + * * + * * + * * + * INPUT: file name to save as * + * length of file to expect * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * OUTPUT: true if file downloaded was completed * + * * + * WARNINGS: This fuction can modify the file name passed in * + * * + * HISTORY: * + * 8/22/96 3:07PM ST : Created * + *=============================================================================================*/ +bool Receive_Remote_File ( char *file_name, unsigned int file_length, int gametype) +{ + + //WWDebugString ("RA95 - In Receive_Remote_File\n"); + unsigned short product_id; + IPXAddressClass sender_address; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200 *RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_RECEIVING_SCENARIO); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + + RemoteFileTransferType receive_packet; + + int last_received_block = -1; //No blocks received yet + unsigned int total_length = 0; + unsigned int packet_len; + + /* + ** If the file name is already in use, use the temp file name + */ + CCFileClass test_file (file_name); + if (test_file.Is_Available()){ + strcpy (file_name, "DOWNLOAD.TMP"); + } + + RawFileClass save_file (file_name); + + /* + ** If the file already exists then delete it and re-create it. + */ + if (save_file.Is_Available()) save_file.Delete(); + + /* + ** Open the file for write + */ + save_file.Open ( WRITE ); + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + /* + ** Wait for all the blocks to arrive + */ + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + if (NullModem.Get_Message ((void*)&receive_packet, (int*)&packet_len) > 0) { + + if (receive_packet.Command == NET_FILE_CHUNK){ + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + }else{ + Ipx.Service(); + + int receive_packet_len = sizeof (receive_packet); + if (Ipx.Get_Global_Message (&receive_packet, &receive_packet_len, + &sender_address, &product_id)){ + +#ifdef WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && sender_address == Session.HostAddress){ +#else //WINSOCK_IPX + if (receive_packet.Command == NET_FILE_CHUNK && + (Winsock.Get_Connected() || sender_address == Session.HostAddress)){ +#endif //WINSOCK_IPX + + if (receive_packet.BlockNumber == last_received_block + 1){ + + save_file.Write ( receive_packet.RawData, receive_packet.BlockLength ); + total_length += receive_packet.BlockLength; + last_received_block ++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (total_length*100) / file_length ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + if (total_length >= file_length){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + } + } + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + + } while ( process ); + + save_file.Close(); + + /* + ** Update the internal list of scenarios to include the downloaded one so we know about it + ** for the next game. + */ + Session.Read_Scenario_Descriptions(); + + return (return_code); +} + + + + + + +/*********************************************************************************************** + * Send_Remote_File -- Sends a file to game clients * + * * + * * + * * + * INPUT: File name * + * * + * OUTPUT: true if file transfer was successfully completed * + * game type - 0 for modem/null modem, 1 otherwise * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/22/96 3:09PM ST : Created * + *=============================================================================================*/ +bool Send_Remote_File ( char *file_name, int gametype ) +{ + //WWDebugString ("RA95 - In Send_Remote_File\n"); + + /* + ** Dialog & button dimensions + */ + int factor = (SeenBuff.Get_Width() == 320) ? 1 : 2; + + int d_dialog_w = 240 *factor; // dialog width + int d_dialog_h = 90*factor; // dialog height + int d_dialog_x = ((320*factor - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*factor - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin1=10; + int d_txt6_h=15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*factor; +#else + int d_cancel_w = 40*factor; +#endif + int d_cancel_h = 9*factor; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*factor; + + int d_progress_w = 100*factor; + int d_progress_h = 10*factor; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*factor; + + int width; + int height; + + char *info_string = (char*)Text_String (TXT_SENDING_SCENARIO); + + CDTimerClass response_timer; // timeout timer for waiting for responses + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + Format_Window_String(info_string, SeenBuff.Get_Height(), width, height); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + //TextButtonClass *buttons; // button list + + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, + TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); +#endif + + GaugeClass progress_meter (BUTTON_PROGRESS, + d_progress_x, d_progress_y, d_progress_w, d_progress_h); + + Fancy_Text_Print(TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass *commands; // button list + bool return_code; + int update_time = 0; + + + int file_length; + int block_number; + int max_chunk_size; + int total_blocks; + int bytes_left; + + void *read_ptr; + + RemoteFileTransferType send_packet; + SerialPacketType file_info; + GlobalPacketType net_file_info; + + + CCFileClass send_file (file_name); + + if ( !send_file.Is_Available() ){ + //WWDebugString ("RA95 - Error - could not find file to send to client\n"); +// debugprint("RA95 - Error - could not find file to send to client\n"); + return (false); + } + file_length = send_file.Size(); + + response_timer = RESPONSE_TIMEOUT; + + /* + ** Send the file info to the remote machine(s) + */ + if (!gametype){ + file_info.Command = SERIAL_FILE_INFO; + strcpy (&file_info.ScenarioInfo.ShortFileName[0], file_name); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#endif +#endif + file_info.ScenarioInfo.FileLength = file_length; + NullModem.Send_Message (&file_info, sizeof (file_info), 1); + while (NullModem.Num_Send() > 0 && response_timer){ + NullModem.Service(); + } + } else { + net_file_info.Command = NET_FILE_INFO; + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], file_name); +// debugprint( "Uploading '%s'\n", file_name ); +#ifdef FIXIT_VERSION_3 + // If we're sending an official map, always send it to 'download.tmp'. + if( Is_Mission_Counterstrike( file_name ) || Is_Mission_Aftermath( file_name ) ) { + strcpy (&net_file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + } +#else +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If we're sending an Aftermath map, always send it to 'download.tmp'. + if (Is_Mission_Aftermath(file_name)) { + strcpy (&file_info.ScenarioInfo.ShortFileName[0], "DOWNLOAD.TMP"); + // There was a bug here: s/b net_file_info. This means that players that don't have Aftermath could have been + // accumulating Aftermath maps all this time!!! (File wasn't getting renamed to "DOWNLOAD.TMP".) + } +#endif +#endif +// debugprint( "ShortFileName is '%s'\n", net_file_info.ScenarioInfo.ShortFileName ); + net_file_info.ScenarioInfo.FileLength = file_length; + + for (int i=0 ; iAddress) ); + } + + while (Ipx.Global_Num_Send() > 0 && response_timer) { + Ipx.Service(); + } + } + + + max_chunk_size = MAX_SEND_FILE_PACKET_SIZE; + total_blocks = (file_length + max_chunk_size-1) / max_chunk_size; + bytes_left = file_length; + + send_file.Open ( READ ); + + + commands = &cancelbtn; + commands->Add_Tail (progress_meter); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + + block_number = 0; + + while ( process ){ + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + +#ifdef WOLAPI_INTEGRATION + if( Session.Type == GAME_INTERNET && pWolapi && ( ::timeGetTime() > pWolapi->dwTimeNextWolapiPump ) ) + { + pWolapi->pChat->PumpMessages(); + pWolapi->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + + Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*factor, + GadgetClass::Get_Color_Scheme(), TBLACK, + TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + + if (!gametype){ + NullModem.Service(); + + + if (block_number < total_blocks){ + + if ( NullModem.Num_Send() <2 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + NullModem.Send_Message ((void*)&send_packet, sizeof(send_packet), 1); + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (NullModem.Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + + } + } + + + }else{ + Ipx.Service(); + + if (block_number < total_blocks){ + + if ( Ipx.Global_Num_Send() == 0 ){ + + send_packet.Command = SERIAL_FILE_CHUNK; + send_packet.BlockNumber = block_number; + send_packet.BlockLength = MIN (file_length, max_chunk_size); + + file_length -= send_packet.BlockLength; + + read_ptr = &send_packet.RawData[0]; + + if (send_file.Read (read_ptr , send_packet.BlockLength) == send_packet.BlockLength){ + for (int i=0 ; iAddress) ); + } + } + + block_number++; + + update_time++; + if (update_time >7){ + progress_meter.Set_Value ( (block_number*100) / total_blocks ); + display = REDRAW_PROGRESS; + update_time = 0;; + } + + } + }else{ + if (Ipx.Global_Num_Send() == 0){ + process = false; + return_code = true; + progress_meter.Set_Value ( 100 ); + progress_meter.Draw_Me(true); + } + } + + + + } + + + if (process){ + input = cancelbtn.Input(); + + /* + ---------------------------- Process input ---------------------------- + */ + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return_code = false; + break; + } + } + + } + + return (return_code); +} +#endif \ No newline at end of file diff --git a/code/redalert/seqconn.cpp b/code/redalert/seqconn.cpp new file mode 100644 index 0000000..496d6b1 --- /dev/null +++ b/code/redalert/seqconn.cpp @@ -0,0 +1,560 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.cpv 1.10 01 Mar 1996 18:29:54 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.CPP * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 20, 1994 * + * * + * Last Update : April 9, 1995 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * SequencedConnClass::SequencedConnClass -- class constructor * + * SequencedConnClass::~SequencedConnClass -- class destructor * + * SequencedConnClass::Init -- Initializes connection queue to empty * + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * SequencedConnClass::Receive_Packet -- adds packet to receive queue * + * SequencedConnClass::Get_Packet -- gets a packet from receive queue * + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * SequencedConnClass::Service_Receive_Queue -- services recieve queue * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "function.h" + +#include "WolDebug.h" + +/*************************************************************************** + * SequencedConnClass::SequencedConnClass -- class constructor * + * * + * INPUT: * + * numsend desired # of entries for the send queue * + * numreceive desired # of entries for the recieve queue * + * maxlen max length of an application packet * + * magicnum the packet "magic number" for this connection * + * retry_delta the time to wait between sends * + * max_retries the max # of retries allowed for a packet * + * (-1 means retry forever, based on this parameter) * + * timeout the max amount of time before we give up on a packet * + * (-1 means retry forever, based on this parameter) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::SequencedConnClass (int numsend, int numreceive, + int maxlen, unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout) : + ConnectionClass (maxlen, magicnum, retry_delta, max_retries, timeout) +{ + NumRecNoAck = 0; + NumRecAck = 0; + NumSendNoAck = 0; + NumSendAck = 0; + + /*------------------------------------------------------------------------ + Allocate the packet Queue. This will store incoming packets (which will + be placed there by the Connection Manager), and outgoing packets (which + are placed there by this class when it "sends" a packet). + ------------------------------------------------------------------------*/ + Queue = new CommQueueClass (numsend, numreceive, MaxPacketLen); + +} /* end of SequencedConnClass */ + + +/*************************************************************************** + * SequencedConnClass::~SequencedConnClass -- class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +SequencedConnClass::~SequencedConnClass () +{ + delete Queue; +} + + +/*************************************************************************** + * SequencedConnClass::Init -- Initializes connection queue to empty * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +void SequencedConnClass::Init (void) +{ + Queue->Init(); + +} /* end of Init */ + + +/*************************************************************************** + * SequencedConnClass::Send_Packet -- adds a packet to the send queue * + * * + * This routine prefixes the given buffer with a CommHeaderType and * + * queues the resulting packet into the Send Queue. (It's actually the * + * Service() routine that handles the hardware-dependent Send of the data).* + * The packet's MagicNumber, Code, and PacketID are set here. * + * * + * INPUT: * + * buf buffer to send * + * buflen length of buffer * + * ack_req true = ACK is required for this packet; false = isn't * + * * + * OUTPUT: * + * 1 = packet was queue'd OK, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Send_Packet (void * buf, int buflen, int ack_req) +{ + /*........................................................................ + Set the magic # for the packet + ........................................................................*/ + ((CommHeaderType *)PacketBuf)->MagicNumber = MagicNum; + + /*........................................................................ + Set the packet Code: DATA_ACK if it requires an ACK, NOACK if it doesn't + Set the packet ID to the appropriate counter value. + ........................................................................*/ + if (ack_req) { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_ACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendAck; + } else { + ((CommHeaderType *)PacketBuf)->Code = PACKET_DATA_NOACK; + ((CommHeaderType *)PacketBuf)->PacketID = NumSendNoAck; + } + + /*........................................................................ + Now build the packet + ........................................................................*/ + memcpy(PacketBuf + sizeof(CommHeaderType), buf, buflen); + + /*........................................................................ + Add it to the queue. + ........................................................................*/ + if (Queue->Queue_Send(PacketBuf,buflen + sizeof(CommHeaderType))) { + if (ack_req) { + NumSendAck++; + } else { + NumSendNoAck++; + } + return(true); + + } else { + return(false); + } +} + + +/*************************************************************************** + * SequencedConnClass::Receive_Packet -- adds packet to the receive queue * + * * + * INPUT: * + * buf buffer to process (already includes CommHeaderType) * + * buflen length of buffer to process * + * * + * OUTPUT: * + * 1 = packet was processed OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Receive_Packet (void * buf, int buflen) +{ + CommHeaderType *packet; // ptr to this packet + SendQueueType *send_entry; // ptr to send entry header + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *entry_data; // ptr to queue entry data + int save_packet = 1; // 0 = this is a resend, or + // out-of-order packet + + /* + --------------------------- Check the magic # ---------------------------- + */ + packet = (CommHeaderType *)buf; + if (packet->MagicNumber!=MagicNum) + return(false); + + /*------------------------------------------------------------------------ + Process the packet based on its Code + ------------------------------------------------------------------------*/ + switch (packet->Code) { + /*..................................................................... + DATA: If this is a No-Ack packet, always save it. Otherwise, only + save it if it's received in the proper sequence. + .....................................................................*/ + case PACKET_DATA_ACK: + case PACKET_DATA_NOACK: + if (packet->Code == PACKET_DATA_NOACK) { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_NOACK received (%d)\n",packet->PacketID); +#endif + save_packet = 1; + } else { +#ifdef DEBUG_SEQ +printf("PACKET_DATA_ACK received (%d)\n",packet->PacketID); +#endif + if ((packet->PacketID == NumRecAck)) { + save_packet = 1; + } else { + save_packet = 0; + /*............................................................... + If this is a resend of our next-available received message, it + means the other app didn't get our ACK, so mark it as + non-acknowledged to tell Service_Receive_Queue to send an ACK. + ...............................................................*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry) { + entry_data = (CommHeaderType *)rec_entry->Buffer; + if (entry_data->PacketID==packet->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + rec_entry->IsACK = 0; +#ifdef DEBUG_SEQ +printf("(Resend)\n"); +#endif + } + } + } + } + + /* + ...................... queue this packet ........................ + */ + if (save_packet) { + if (!Queue->Queue_Receive(buf, buflen)) { + return(false); + } + if (packet->Code == PACKET_DATA_ACK) { + NumRecAck++; + } else { + NumRecNoAck++; + } + } + break; + + /*..................................................................... + ACK: If this ACK is for the latest-sent packet, mark that packet as + acknowledged, then throw this packet away. Otherwise, ignore the ACK + (if we re-sent before we received the other system's first ACK, this + ACK will be a leftover) + .....................................................................*/ + case PACKET_ACK: +#ifdef DEBUG_SEQ +printf("ACK received (%d)\n",packet->PacketID); +#endif + /* + ....................... Get queue entry ptr ........................ + */ + send_entry = Queue->Next_Send(); + /* + ............... If ptr is valid, get ptr to its data ............... + */ + if (send_entry!=NULL) { + entry_data = (CommHeaderType *)send_entry->Buffer; + + /* + .............. If ACK is for this entry, mark it ................ + */ + if (packet->PacketID==entry_data->PacketID && + entry_data->Code == PACKET_DATA_ACK) { + send_entry->IsACK = 1; + } + } + break; + + /*..................................................................... + Default: ignore the packet + .....................................................................*/ + default: + break; + + } /* end of switch */ + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Get_Packet -- gets a packet from the receive queue * + * * + * INPUT: * + * buf location to store buffer * + * buflen filled in with length of 'buf' * + * * + * OUTPUT: * + * 1 = packet was read, 0 = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Get_Packet (void * buf, int *buflen) +{ + ReceiveQueueType *rec_entry; // ptr to receive entry header + int packetlen; // size of received packet + + /* + ------------------ Get ptr to the next available entry ------------------- + */ + rec_entry = Queue->Next_Receive(); + + /* + ------------------------ Read it if it's un-read ------------------------- + */ + if (rec_entry!=NULL && rec_entry->IsRead==0) { + /* + ........................... Mark as read .............................. + */ + rec_entry->IsRead = 1; + + /* + .......................... Copy data packet ........................... + */ + packetlen = rec_entry->BufLen - sizeof(CommHeaderType); + if (packetlen > 0) { + memcpy(buf, rec_entry->Buffer + sizeof(CommHeaderType), packetlen); + } + (*buflen) = packetlen; + return(true); + } + + return(false); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Send_Queue -- services the send queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Send_Queue (void) +{ + SendQueueType *send_entry; // ptr to send queue entry + CommHeaderType *packet_hdr; // packet header + unsigned long curtime; // current time + + /*------------------------------------------------------------------------ + - If the next packet is ACK'd remove it from the queue + - If the next packet isn't ACK'd, [re-]send it + ------------------------------------------------------------------------*/ + /* + ......................... Get ptr to data to send ........................ + */ + send_entry = Queue->Next_Send(); + if (send_entry==NULL) + return(true); + + /* + ------------------ If ACK has been received, unqueue it ------------------ + */ + if (send_entry->IsACK) { + + /* + .................. Update this queue's response time .................. + */ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_ACK) { + Queue->Add_Delay(Time() - send_entry->FirstTime); + } + + /* + ......................... unqueue the packet .......................... + */ +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Receive packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Send(NULL,NULL); + } else { + + /* + ----------------- ACK not received yet, [re-]send packet ----------------- + */ + /*..................................................................... + Only send the message if time has elapsed. (The message's Time + fields are init'd to 0 when a message is queue'd or unqueue'd, so the + first time through, the delta time will appear large.) + .....................................................................*/ + curtime = Time(); + if (curtime - send_entry->LastTime > RetryDelta) { + /* + ......................... Send the message ......................... + */ + Send (send_entry->Buffer, send_entry->BufLen); + /* + ....................... Fill in Time fields ........................ + */ + send_entry->LastTime = curtime; + if (send_entry->SendCount==0) { + send_entry->FirstTime = curtime; + /*............................................................... + If this is the 1st time we're sending this packet, and it doesn't + require an ACK, mark it as ACK'd; then, the next time through, + it will just be removed from the queue. + ...............................................................*/ + packet_hdr = (CommHeaderType *)send_entry->Buffer; + if (packet_hdr->Code == PACKET_DATA_NOACK) + send_entry->IsACK = 1; + } + +#ifdef DEBUG_SEQ +packet_hdr = (CommHeaderType *)send_entry->Buffer; +if (packet_hdr->Code == PACKET_DATA_NOACK) { + printf("Sending PACKET_DATA_NOACK (%d)\n",packet_hdr->PacketID); +} else { + printf("Sending PACKET_DATA_ACK (%d)\n",packet_hdr->PacketID); +} +#endif + /* + ......................... Update SendCount ......................... + */ + send_entry->SendCount++; + /*.................................................................. + Perform error detection, based on either MaxRetries or Timeout + ..................................................................*/ + if (MaxRetries != -1 && send_entry->SendCount > MaxRetries) + return(false); + if (Timeout != -1 && send_entry->LastTime - + send_entry->FirstTime > Timeout) + return(false); + + } + } + + return(true); +} + + +/*************************************************************************** + * SequencedConnClass::Service_Receive_Queue -- services the recieve queue * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/20/1994 BR : Created. * + *=========================================================================*/ +int SequencedConnClass::Service_Receive_Queue (void) +{ + CommHeaderType ackpacket; // ACK packet to send + ReceiveQueueType *rec_entry; // ptr to receive entry header + CommHeaderType *packet_hdr; // packet header + + /*------------------------------------------------------------------------ + Get a pointer to the next received entry + ------------------------------------------------------------------------*/ + rec_entry = Queue->Next_Receive(); + if (rec_entry==NULL) + return(true); + + /*------------------------------------------------------------------------ + If this packet doesn't require an ACK, mark it as ACK'd. + ------------------------------------------------------------------------*/ + packet_hdr = (CommHeaderType *)(rec_entry->Buffer); + if (packet_hdr->Code==PACKET_DATA_NOACK) + rec_entry->IsACK = 1; + + /*------------------------------------------------------------------------ + If this packet hasn't been ACK'd, send an ACK: + - Fill in the MagicNum & the Code + - Set the PacketID to the same ID that the sending system used, so the + sending system knows which packet the ACK is for + ------------------------------------------------------------------------*/ + if (rec_entry->IsACK==0) { +#ifdef DEBUG_SEQ +printf("Sending ACK (%d)\n",packet_hdr->PacketID); +#endif + ackpacket.MagicNumber = MagicNum; + ackpacket.Code = PACKET_ACK; + ackpacket.PacketID = packet_hdr->PacketID; + + Send((char *)&ackpacket, sizeof(CommHeaderType)); + + rec_entry->IsACK = 1; + } + + /*------------------------------------------------------------------------ + If this packet has been read by the application, and has been ACK'd, and + there is another packet in the queue behind this one, it means the other + system got the ACK we sent for this packet; remove this packet from the + queue. + ------------------------------------------------------------------------*/ + if (rec_entry!=NULL && rec_entry->IsRead && rec_entry->IsACK && + Queue->Num_Receive() > 1) { +#ifdef DEBUG_SEQ +printf(">>>Unqueueing Send packet #%d<<<\n",packet_hdr->PacketID); +#endif + Queue->UnQueue_Receive(NULL,NULL); + } + + return(true); +} + +#endif \ No newline at end of file diff --git a/code/redalert/seqconn.h b/code/redalert/seqconn.h new file mode 100644 index 0000000..6280358 --- /dev/null +++ b/code/redalert/seqconn.h @@ -0,0 +1,108 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\seqconn.h_v 1.13 01 Mar 1996 17:45:24 JOE_BOSTIC $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SEQCONN.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : December 19, 1994 * + * * + * Last Update : April 9, 1995 [BR] * + * * + *-------------------------------------------------------------------------* + * * + * This class provides a "Sequenced" ACK/Retry approach to packet * + * transmission. It waits until the last packet has been ACK'd before * + * sending another packet. Thus, it guarantees order of delivery of * + * packets, but its performance will be slower than the Non-Sequenced * + * approach. * + * * + * A derived class must provide: * + * - Init: Initialization of any hardware-specific values. * + * - Send: a hardware-dependent send routine. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SEQCONN_H +#define SEQCONN_H + +#include "connect.h" +#include "comqueue.h" + + +/* +***************************** Class Declaration ***************************** +*/ +class SequencedConnClass : public ConnectionClass +{ + /* + ---------------------------- Public Interface ---------------------------- + */ + public: + /*..................................................................... + Constructor/destructor. + .....................................................................*/ + SequencedConnClass (int numsend, int numrecieve, int maxlen, + unsigned short magicnum, unsigned long retry_delta, + unsigned long max_retries, unsigned long timeout); + virtual ~SequencedConnClass (); + + /*..................................................................... + Initialization. + .....................................................................*/ + virtual void Init (void); + + /*..................................................................... + Send/Receive routines. + .....................................................................*/ + virtual int Send_Packet (void * buf, int buflen, int ack_req); + virtual int Receive_Packet (void * buf, int buflen); + virtual int Get_Packet (void * buf, int *buflen); + + /*..................................................................... + The packet queue. + .....................................................................*/ + CommQueueClass *Queue; + + /* + -------------------------- Protected Interface --------------------------- + */ + protected: + /*..................................................................... + Routines to service the Send & Receive queues. + .....................................................................*/ + virtual int Service_Send_Queue (void); + virtual int Service_Receive_Queue (void); + + /*..................................................................... + Running totals of # of packets we send & receive which require an ACK, + and those that don't. + .....................................................................*/ + unsigned long NumRecNoAck; + unsigned long NumRecAck; + unsigned long NumSendNoAck; + unsigned long NumSendAck; + +}; + +#endif +/*************************** end of seqconn.h ******************************/ diff --git a/code/redalert/session.cpp b/code/redalert/session.cpp new file mode 100644 index 0000000..909eac9 --- /dev/null +++ b/code/redalert/session.cpp @@ -0,0 +1,1875 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SESSION.CPP 3 3/10/97 6:23p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : September 10, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SessionClass::SessionClass -- Constructor * + * SessionClass::~SessionClass -- Destructor * + * SessionClass::One_Time -- one-time initializations * + * SessionClass::Init -- Initializes all values * + * SessionClass::Create_Connections -- forms connections to other players * + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * SessionClass::Save -- Saves this class to a file * + * SessionClass::Load -- Loads this class from a file * + * SessionClass::Read_MultiPlayer_Settings -- reads settings from INI * + * SessionClass::Write_MultiPlayer_Settings -- writes settings to INI * + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * SessionClass::Trap_Object -- searches for an object, for debugging * + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include // for station ID computation +#include // for station ID computation + +//#include "WolDebug.h" + +/***************************** Globals *************************************/ +//--------------------------------------------------------------------------- +// This is the array of remap colors. Each player in a network game is +// assigned one of these colors. The 'G' is for graphics drawing; the 'T' +// is for text printing (indicates a remap table for the font to use). +//--------------------------------------------------------------------------- +//int SessionClass::GColors[MAX_MPLAYER_COLORS] = { + //5, // Yellow + //127, // Red + //135, // BlueGreen + //26, // Orange + //4, // Green + //202 // Blue-Grey +//}; + +//int SessionClass::TColors[MAX_MPLAYER_COLORS] = { + //CC_GDI_COLOR, // Yellow + //CC_NOD_COLOR, // Red + //CC_BLUE_GREEN, // BlueGreen + //CC_ORANGE, // Orange + //CC_GREEN, // Green + //CC_BLUE_GREY, // Blue +//}; + +/*--------------------------------------------------------------------------- +Min & Max unit count values; index0 = bases OFF, index1 = bases ON +---------------------------------------------------------------------------*/ +int SessionClass::CountMin[2] = {1,0}; +int SessionClass::CountMax[2] = {50,12}; + +//--------------------------------------------------------------------------- +// This is a list of all the names of the multiplayer scenarios +//--------------------------------------------------------------------------- +char SessionClass::Descriptions[100][40]; + +//--------------------------------------------------------------------------- +// These values are used purely for the Mono debug display. They show the +// names of the Global Channel packet types, and the event types. +//--------------------------------------------------------------------------- +char * SessionClass::GlobalPacketNames[] = { + "Game?", + "Game!", + "Player?", + "Player!", + "Join?", + "Join!", + "Reject", + "GameOptions", + "Sign Off", + "GO!", + "Message", + "Ping", + "Load" +}; + +char * SessionClass::SerialPacketNames[] = { + "CONNECT", + "GAME_OPTIONS", + "SIGN_OFF", + "GO", + "MESSAGE", + "TIMING", + "SCORE_SCREEN", + "LOADGAME", + "LAST_COMMAND", +}; + + +char * SessionClass::DialMethodCheck[ DIAL_METHODS ] = { + "T", + "P" +}; + +char *SessionClass::CallWaitStrings[ CALL_WAIT_STRINGS_NUM ] = { + "*70,", + "70#,", + "1170,", + "CUSTOM - " +}; + +/*************************************************************************** + * SessionClass::SessionClass -- Constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::SessionClass(void) +{ + Type = GAME_NORMAL; + CommProtocol = DEFAULT_COMM_PROTOCOL; + + Options.ScenarioIndex = 0; + Options.Bases = 0; + Options.Credits = 0; + Options.Tiberium = 0; + Options.Goodies = 0; + Options.Ghosts = 0; + Options.UnitCount = 0; + + UniqueID = 0; + + Handle[0] = 0; + PrefColor = PCOLOR_FIRST; + ColorIdx = PCOLOR_FIRST; + House = HOUSE_GOOD; + ObiWan = 0; + Solo = 0; + + MaxPlayers = 8; + NumPlayers = 0; + + MaxAhead = 5; + FrameSendRate = DEFAULT_FRAME_SEND_RATE; + + LoadGame = 0; + EmergencySave = 0; + + LastMessage[0] = 0; + WWChat = 0; + + RecordFile.Set_Name("RECORD.BIN"); // always uses this name + Record= 0; // set via command line + Play = 0; // set via command line + Attract = 0; // set via command line + + IsBridge = 0; + NetStealth = 0; + NetProtect = 1; + NetOpen = 0; + GameName[0] = 0; + GProductID = 0; + + MetaSize = MAX_IPX_PACKET_SIZE; + + ModemService = true; + CurPhoneIdx = 0; // set from INI file + SerialDefaults.Port = 0x2f8; // set from INI file + SerialDefaults.IRQ = 3; // set from INI file + SerialDefaults.Baud = 9600; // set from INI file + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; // set from INI file + SerialDefaults.InitStringIndex = 0; // set from INI file + SerialDefaults.CallWaitStringIndex = 0; // set from INI file + strcpy(SerialDefaults.CallWaitString,""); + ModemType = MODEM_NULL_HOST; // set from INI file + + TrapFrame = 0x7fffffff; // frame to start trapping object values at + TrapObjType = RTTI_NONE; // type of object to trap + TrapObject.Ptr.All = NULL; // ptr to object being trapped + TrapCoord = 0; // COORDINATE of object to trap + TrapTarget = TARGET_NONE; // TARGET value of object to trap + TrapCell = NULL; // for trapping a cell + TrapCheckHeap = 0; // start checking the Heap + TrapPrintCRC = 0; // output CRC file + +#if(TEN) + TenPacket = NULL; + TenSize = 200; + TenPlayerID = -1; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // TEN + +#if(MPATH) + MPathPacket = NULL; + MPathSize = 200; + OptionsFile[0] = 0; + AllowSolo = 0; + NetResponseTime = 600; +#endif // MPATH + +} // end of SessionClass + + +/*************************************************************************** + * SessionClass::~SessionClass -- Destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +SessionClass::~SessionClass(void) +{ +} // end of ~SessionClass + + +/*************************************************************************** + * SessionClass::One_Time -- one-time initializations * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/01/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::One_Time(void) +{ + Read_MultiPlayer_Settings(); + Read_Scenario_Descriptions(); + + UniqueID = Compute_Unique_ID(); + +} // end of One_Time + + +/*************************************************************************** + * SessionClass::Init -- Initializes all values * + * * + * This function should be called for every new game played; it only sets * + * those variables that should be set for a new game. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Init(void) +{ + +} // end of Init + + +/*************************************************************************** + * SessionClass::Create_Connections -- forms connections to other players * + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_Connections(void) +{ +#if (0)//PG + int i; + + if (Session.Type != GAME_IPX && Session.Type != GAME_INTERNET) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ipx.Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + &(Players[i]->Address) ); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } +#endif + return (1); + +} // end of Create_Connections + + +#if(TEN) +/*************************************************************************** + * SessionClass::Create_TEN_Connections -- forms connections to TEN players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_TEN_Connections(void) +{ + int i; + + if (Session.Type != GAME_TEN) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + Ten->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->TenAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_TEN_Connections + +#endif // TEN + + +#if(MPATH) +/*************************************************************************** + * SessionClass::Create_MPATH_Connections -- forms connections to MPATH players* + * * + * This routine uses the contents of the Players vector, combined with * + * that of the Houses array, to create connections to each other player. * + * It is assumed that 'Players' contains all the other players to connect * + * to, and that the HouseClass's have been filled in with players' data. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1 = success, 0 = failure * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/30/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Create_MPATH_Connections(void) +{ + int i; + + if (Session.Type != GAME_MPATH) { + return (0); + } + + //------------------------------------------------------------------------ + // Loop through all entries in 'Players'. To avoid connecting to myself, + // skip the 1st entry. + //------------------------------------------------------------------------ + for (i = 1; i < Players.Count(); i++) { + //..................................................................... + // Make sure the name matches before creating the connection + //..................................................................... + if (!stricmp (Players[i]->Name, + HouseClass::As_Pointer(Players[i]->Player.ID)->IniName)) { + MPath->Create_Connection((int)Players[i]->Player.ID, Players[i]->Name, + Players[i]->MPathAddress); + Players[i]->Player.ProcessTime = -1; + } + else { + return (0); + } + } + + return (1); + +} // end of Create_MPATH_Connections + +#endif // MPATH + + +/*************************************************************************** + * SessionClass::Am_I_Master -- tells if the local system is the "master" * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1995 BRR : Created. * + *=========================================================================*/ +bool SessionClass::Am_I_Master(void) +{ + int i; + HousesType house; + HouseClass *hptr; + + //------------------------------------------------------------------------ + // Check every house; if PlayerPtr points to the first human house, we're + // the master. + //------------------------------------------------------------------------ + for (i = 0; i < Session.MaxPlayers; i++) { + house = (HousesType)((int)HOUSE_MULTI1 + i); + hptr = HouseClass::As_Pointer(house); + if (hptr->IsHuman) { + if (PlayerPtr == hptr) { + return (true); + } + else { + return (false); + } + } + } + + return (false); + +} // end of Am_I_Master + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Save(Pipe & file) const +{ +#ifdef FIXIT_MULTI_SAVE + file.Put(&CommProtocol, sizeof(CommProtocol)); + file.Put(&MaxAhead, sizeof(MaxAhead)); + file.Put(&FrameSendRate, sizeof(FrameSendRate)); + file.Put(&DesiredFrameRate, sizeof(DesiredFrameRate)); +#endif // FIXIT_MULTI_SAVE + file.Put(&PrefColor, sizeof(PrefColor)); + file.Put(&ColorIdx, sizeof(ColorIdx)); + file.Put(&House, sizeof(House)); + file.Put(&NumPlayers, sizeof(NumPlayers)); + file.Put(&Options.Bases, sizeof(Options.Bases)); + file.Put(&Options.Credits, sizeof(Options.Credits)); + file.Put(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Put(&Options.Goodies, sizeof(Options.Goodies)); + file.Put(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Put(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Put(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Put(&ObiWan, sizeof(ObiWan)); + file.Put(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Load(Straw & file) +{ +#ifdef FIXIT_MULTI_SAVE +// if(GameVersion != 0x0100616D){ + file.Get(&CommProtocol, sizeof(CommProtocol)); + file.Get(&MaxAhead, sizeof(MaxAhead)); + file.Get(&FrameSendRate, sizeof(FrameSendRate)); + file.Get(&DesiredFrameRate, sizeof(DesiredFrameRate)); +// } +#endif // FIXIT_MULTI_SAVE + file.Get(&PrefColor, sizeof(PrefColor)); + file.Get(&ColorIdx, sizeof(ColorIdx)); + file.Get(&House, sizeof(House)); + file.Get(&NumPlayers, sizeof(NumPlayers)); + file.Get(&Options.Bases, sizeof(Options.Bases)); + file.Get(&Options.Credits, sizeof(Options.Credits)); + file.Get(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Get(&Options.Goodies, sizeof(Options.Goodies)); + file.Get(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Get(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Get(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Get(&ObiWan, sizeof(ObiWan)); + file.Get(&EmergencySave, sizeof(EmergencySave)); + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Save -- Saves this class to a file * + * * + * Only certain members of this class should be saved into a save-game * + * file; this routine saves only those members. * + * * + * INPUT: * + * file file to save to * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Save(CCFileClass & file) +{ + int i; + + file.Write(&Type, sizeof(Type)); + file.Write(&CommProtocol, sizeof(CommProtocol)); + file.Write(&FrameSendRate, sizeof(FrameSendRate)); + file.Write(&PrefColor, sizeof(PrefColor)); + file.Write(&ColorIdx, sizeof(ColorIdx)); + file.Write(&House, sizeof(House)); + file.Write(&NumPlayers, sizeof(NumPlayers)); + file.Write(&Options.Bases, sizeof(Options.Bases)); + file.Write(&Options.Credits, sizeof(Options.Credits)); + file.Write(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Write(&Options.Goodies, sizeof(Options.Goodies)); + file.Write(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Write(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Write(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Write(&ObiWan, sizeof(ObiWan)); + file.Write(&EmergencySave, sizeof(EmergencySave)); + + i = Players.Count(); + file.Write(&i, sizeof(i)); + for (i = 0; i < Players.Count(); i++) { + file.Write(Players[i], sizeof(NodeNameType)); + } + + return (1); + +} // end of Save + + +/*************************************************************************** + * SessionClass::Load -- Loads this class from a file * + * * + * INPUT: * + * file file to load from * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/04/1995 BRR : Created. * + *=========================================================================*/ +int SessionClass::Load(CCFileClass & file) +{ + int count; + int i; + NodeNameType *node; + + file.Read(&Type, sizeof(Type)); + file.Read(&CommProtocol, sizeof(CommProtocol)); + file.Read(&FrameSendRate, sizeof(FrameSendRate)); + file.Read(&PrefColor, sizeof(PrefColor)); + file.Read(&ColorIdx, sizeof(ColorIdx)); + file.Read(&House, sizeof(House)); + file.Read(&NumPlayers, sizeof(NumPlayers)); + file.Read(&Options.Bases, sizeof(Options.Bases)); + file.Read(&Options.Credits, sizeof(Options.Credits)); + file.Read(&Options.Tiberium, sizeof(Options.Tiberium)); + file.Read(&Options.Goodies, sizeof(Options.Goodies)); + file.Read(&Options.Ghosts, sizeof(Options.Ghosts)); + file.Read(&Options.UnitCount, sizeof(Options.UnitCount)); + file.Read(&Options.AIPlayers, sizeof(Options.AIPlayers)); + file.Read(&ObiWan, sizeof(ObiWan)); + file.Read(&EmergencySave, sizeof(EmergencySave)); + + file.Read(&count, sizeof(count)); + for (i = 0; i < count; i++) { + node = new NodeNameType; + file.Read(node, sizeof(NodeNameType)); + Players.Add(node); + } + + return (1); + +} // end of Load + + +/*************************************************************************** + * SessionClass::Read_MultiPlayer_Settings -- reads settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Read_MultiPlayer_Settings (void) +{ +#if (0)//PG + char *tokenptr; // ptr to token + PhoneEntryClass *phone; // a phone book entry + char *entry; // a phone book entry + char buf[128]; // buffer for parsing INI entry + int i; + CELL cell; + + +// CCFileClass file (CONFIG_FILE_NAME); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete[] InitStrings[i]; + } + InitStrings.Clear(); + + // Clear the dialing entries + for (i = 0; i < PhoneBook.Count(); i++) { + delete[] PhoneBook[i]; + } + PhoneBook.Clear(); + + // Create filename and read the file. + INIClass ini; + if (ini.Load(RawFileClass(CONFIG_FILE_NAME))) { + + // Get the player's last-used Handle + ini.Get_String("MultiPlayer", "Handle", "Noname", Handle, sizeof(Handle)); + + // Get the player's last-used Color + PrefColor = (PlayerColorType)ini.Get_Int("MultiPlayer", "Color", 0); +#ifdef FIXIT_VERSION_3 + int iSide = ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); + iSide = max( 2, min( 6, iSide ) ); + House = (HousesType)iSide; +#else + House = (HousesType)ini.Get_Int("MultiPlayer", "Side", HOUSE_USSR); +#endif + CurPhoneIdx = ini.Get_Int("MultiPlayer", "PhoneIndex", -1); + TrapCheckHeap = ini.Get_Int("MultiPlayer", "CheckHeap", 0); + + // Read in default serial settings + ini.Get_String("SerialDefaults", "ModemName", "NoName", SerialDefaults.ModemName, MODEM_NAME_MAX); + if (!strcmp ( SerialDefaults.ModemName, "NoName")) { + SerialDefaults.ModemName[0] = 0; + } + SerialDefaults.Port = ini.Get_Int("SerialDefaults", "Port", 0); + SerialDefaults.IRQ = ini.Get_Int("SerialDefaults", "IRQ", -1); + SerialDefaults.Baud = ini.Get_Int("SerialDefaults", "Baud", -1); + SerialDefaults.Compression = ini.Get_Int ("SerialDefaults", "Compression", 0); + SerialDefaults.ErrorCorrection = ini.Get_Int ("SerialDefaults", "ErrorCorrection", 0); + SerialDefaults.HardwareFlowControl = ini.Get_Int ("SerialDefaults", "HardwareFlowControl", 1); + + ini.Get_String("SerialDefaults", "DialMethod", "T", buf, 2); + +#ifndef WIN32 + /* + ** Ignore any modem name in DOS. This should only be nessasary if the user + ** previously set up the modem in the windows version. + */ + if (SerialDefaults.ModemName[0] && SerialDefaults.Port == 1) { + SerialDefaults.Port = 0x3F8; + SerialDefaults.ModemName[0] = 0; + } +#endif //WIN32 + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + SerialDefaults.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + SerialDefaults.DialMethod = DIAL_TOUCH_TONE; + } + + SerialDefaults.InitStringIndex = ini.Get_Int("SerialDefaults", "InitStringIndex", 0); + + SerialDefaults.CallWaitStringIndex = ini.Get_Int("SerialDefaults", "CallWaitStringIndex", CALL_WAIT_CUSTOM); + + ini.Get_String("SerialDefaults", "CallWaitString", "", SerialDefaults.CallWaitString, CWAITSTRBUF_MAX); + + if (SerialDefaults.IRQ == 0 || SerialDefaults.Baud == 0) { + SerialDefaults.Port = 0; + SerialDefaults.IRQ = -1; + SerialDefaults.Baud = -1; + } + + int initcount = ini.Entry_Count("InitStrings"); + for (int index = 0; index < initcount; index++) { + entry = new char[ INITSTRBUF_MAX ]; + entry[0] = 0; + ini.Get_String("InitStrings", ini.Get_Entry("InitStrings", index), NULL, entry, INITSTRBUF_MAX); + strupr( entry ); + InitStrings.Add( entry ); + } + + // if no entries then have at least one + if (initcount == 0) { + entry = new char[ INITSTRBUF_MAX ]; + strcpy( entry, "ATZ" ); + InitStrings.Add( entry ); + SerialDefaults.InitStringIndex = 0; + } + + // Read the entry names in + int phonecount = ini.Entry_Count("PhoneBook"); + for (int index = 0; index < phonecount; index++) { + // Create a new phone book entry + phone = new PhoneEntryClass(); + + // Read the entire entry in + ini.Get_String("PhoneBook", ini.Get_Entry("PhoneBook", index), NULL, buf, sizeof(buf)); + + // Extract name, phone # & serial port settings + tokenptr = strtok( buf, "|" ); + if (tokenptr) { + strcpy( phone->Name, tokenptr ); + strupr( phone->Name ); + } else { + phone->Name[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy( phone->Number, tokenptr ); + strupr( phone->Number ); + } else { + phone->Number[0] = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + sscanf( tokenptr, "%x", &phone->Settings.Port ); + } else { + phone->Settings.Port = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.IRQ = atoi( tokenptr ); + } else { + phone->Settings.IRQ = -1; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.Baud = atoi( tokenptr ); + } else { + phone->Settings.Baud = -1; + } + + phone->Settings.Compression = 0; + phone->Settings.ErrorCorrection = 0; + phone->Settings.HardwareFlowControl = 1; + + /* + ** Find out if this phonebook entry has the new settings included. If not + ** then we need to skip this section. + */ + tokenptr = strtok( NULL, "|" ); + if (tokenptr){ + strcpy( buf, tokenptr ); + + // find dial method + + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + /* + ** This must be an old phonebook entry + */ + break; + } + } + + /* + ** Method wasnt found - assume its a new phonebook entry so get the extra settings + */ + // if method not found set to touch tone + + if (i == DIAL_METHODS) { + + phone->Settings.Compression = atoi( tokenptr ); + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.ErrorCorrection = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.HardwareFlowControl = atoi( tokenptr ); + } + + tokenptr = strtok( NULL, "|" ); + } + } + + + if (tokenptr) { + strcpy( buf, tokenptr ); + + // find dial method + for (i = 0; i < DIAL_METHODS; i++) { + if ( !strcmpi( buf, DialMethodCheck[ i ]) ) { + phone->Settings.DialMethod = (DialMethodType)i; + break; + } + } + + // if method not found set to touch tone + if (i == DIAL_METHODS) { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + } else { + phone->Settings.DialMethod = DIAL_TOUCH_TONE; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.InitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.InitStringIndex = 0; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + phone->Settings.CallWaitStringIndex = atoi( tokenptr ); + } else { + phone->Settings.CallWaitStringIndex = CALL_WAIT_CUSTOM; + } + + tokenptr = strtok( NULL, "|" ); + if (tokenptr) { + strcpy (phone->Settings.CallWaitString, tokenptr); + } else { + phone->Settings.CallWaitString[0] = 0; + } + + // Add it to our list + PhoneBook.Add(phone); + } + + // Read special recording playback values, to help find sync bugs + TrapFrame = ini.Get_Int("SyncBug", "Frame", 0x7fffffff); + + ini.Get_String("SyncBug", "Type", "NONE", buf, 80); + + if (!stricmp(buf,"AIRCRAFT")) + TrapObjType = RTTI_AIRCRAFT; + else if (!stricmp(buf,"ANIM")) + TrapObjType = RTTI_ANIM; + else if (!stricmp(buf,"BUILDING")) + TrapObjType = RTTI_BUILDING; + else if (!stricmp(buf,"BULLET")) + TrapObjType = RTTI_BULLET; + else if (!stricmp(buf,"INFANTRY")) + TrapObjType = RTTI_INFANTRY; + else if (!stricmp(buf,"UNIT")) + TrapObjType = RTTI_UNIT; + else { + TrapObjType = RTTI_NONE; + } + + ini.Get_String("SyncBug", "Coord", "0", buf, 80); + sscanf(buf,"%x",&TrapCoord); + + ini.Get_String("SyncBug", "Target", "0", buf, 80); + sscanf(buf,"%x",&TrapTarget); + + ini.Get_String("SyncBug", "Cell", "0", buf, 80); + cell = atoi(buf); + if (cell) { + TrapCell = &(Map[cell]); + } + + TrapPrintCRC = ini.Get_Int("SyncBug", "PrintCRC", 0x7fffffff); + } +#endif +} + + +/*************************************************************************** + * SessionClass::Write_MultiPlayer_Settings -- writes settings INI * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + *=========================================================================*/ +void SessionClass::Write_MultiPlayer_Settings (void) +{ +#ifdef NEVER + char * buffer; // INI staging buffer pointer. + CCFileClass file; + int i; + char entrytext[4]; + char buf[128]; // buffer for parsing INI entry + + //------------------------------------------------------------------------ + // Get a working pointer to the INI staging buffer. Make sure that the + // buffer starts cleared out of any data. + //------------------------------------------------------------------------ + buffer = (char *)_ShapeBuffer; + memset(buffer, '\0', _ShapeBufferSize); + + file.Set_Name(CONFIG_FILE_NAME); + if (file.Is_Available()) { + file.Open(READ); + file.Read(buffer, _ShapeBufferSize-1); + file.Close(); + } + + //------------------------------------------------------------------------ + // Save the player's last-used Handle & Color + //------------------------------------------------------------------------ + WWWritePrivateProfileInt("MultiPlayer", "PhoneIndex", CurPhoneIdx, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Color", (int)PrefColor, buffer); + WWWritePrivateProfileInt ("MultiPlayer", "Side", House, buffer); + WWWritePrivateProfileString("MultiPlayer", "Handle", Handle, buffer); + + //------------------------------------------------------------------------ + // Clear all existing Settings.SerialDefault entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("SerialDefaults", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save default serial settings in opposite order you want to see them + //------------------------------------------------------------------------ + WWWritePrivateProfileString("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex, buffer); + WWWritePrivateProfileString("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ], buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Baud", SerialDefaults.Baud, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "IRQ", SerialDefaults.IRQ, buffer); + sprintf(buf, "%x", SerialDefaults.Port); + WWWritePrivateProfileString("SerialDefaults", "Port", buf, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "Compression", SerialDefaults.Compression , buffer); + WWWritePrivateProfileInt ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection, buffer); + WWWritePrivateProfileInt ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl, buffer); + + //------------------------------------------------------------------------ + // Clear all existing InitString entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("InitStrings", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all InitString entries. In descending order so they come out in + // ascending order. + //------------------------------------------------------------------------ + for (i = (InitStrings.Count() - 1); i >= 0; i--) { + sprintf( buf, "%03d", i); + WWWritePrivateProfileString ("InitStrings", buf, InitStrings[i], buffer); + } + + //------------------------------------------------------------------------ + // Clear all existing Phone Book entries. + //------------------------------------------------------------------------ + WWWritePrivateProfileString ("PhoneBook", NULL, NULL, buffer); + + //------------------------------------------------------------------------ + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + //------------------------------------------------------------------------ + for (i = (PhoneBook.Count() - 1); i >= 0; i--) { + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + WWWritePrivateProfileString ("PhoneBook", entrytext, buf, buffer); + } + + //------------------------------------------------------------------------ + // Write the INI data out to a file. + //------------------------------------------------------------------------ + file.Open(WRITE); + file.Write(buffer,strlen(buffer)); + file.Close(); +#endif + +#if (0)//PG + INIClass ini; + RawFileClass file(CONFIG_FILE_NAME); + if (ini.Load(file)) { + + // Save the player's last-used Handle & Color + ini.Put_Int("MultiPlayer", "PhoneIndex", CurPhoneIdx); + ini.Put_Int("MultiPlayer", "Color", (int)PrefColor); + ini.Put_Int("MultiPlayer", "Side", House); + ini.Put_String("MultiPlayer", "Handle", Handle); + + // Clear all existing Settings.SerialDefault entries. + ini.Clear("SerialDefaults"); + + // Save default serial settings in opposite order you want to see them + ini.Put_String("SerialDefaults", "CallWaitString", SerialDefaults.CallWaitString); + ini.Put_Int("SerialDefaults", "CallWaitStringIndex", SerialDefaults.CallWaitStringIndex); + ini.Put_Int("SerialDefaults", "InitStringIndex", SerialDefaults.InitStringIndex); + ini.Put_String("SerialDefaults", "DialMethod", DialMethodCheck[ SerialDefaults.DialMethod ]); + ini.Put_Int("SerialDefaults", "Baud", SerialDefaults.Baud); + ini.Put_Int("SerialDefaults", "IRQ", SerialDefaults.IRQ); + ini.Put_Int("SerialDefaults", "Port", SerialDefaults.Port, 1); + ini.Put_String("SerialDefaults", "ModemName", SerialDefaults.ModemName); + ini.Put_Int ("SerialDefaults", "Compression", SerialDefaults.Compression ); + ini.Put_Int ("SerialDefaults", "ErrorCorrection", SerialDefaults.ErrorCorrection ); + ini.Put_Int ("SerialDefaults", "HardwareFlowControl", SerialDefaults.HardwareFlowControl ); + + // Clear all existing InitString entries. + ini.Clear("InitStrings"); + + // Save all InitString entries. + for (int index = 0; index < InitStrings.Count(); index++) { + char buf[10]; + sprintf( buf, "%03d", index); + ini.Put_String("InitStrings", buf, InitStrings[index]); + } + + // Clear all existing Phone Book entries. + ini.Clear("PhoneBook"); + + // Save all Phone Book entries. + // Format: Entry=Name,PhoneNum,Port,IRQ,Baud,InitString + for (int i = (PhoneBook.Count() - 1); i >= 0; i--) { + char buf[128]; + char entrytext[10]; + sprintf(buf,"%s|%s|%x|%d|%d|%d|%d|%d|%s|%d|%d|%s", + PhoneBook[i]->Name, + PhoneBook[i]->Number, + PhoneBook[i]->Settings.Port, + PhoneBook[i]->Settings.IRQ, + PhoneBook[i]->Settings.Baud, + PhoneBook[i]->Settings.Compression, + PhoneBook[i]->Settings.ErrorCorrection, + PhoneBook[i]->Settings.HardwareFlowControl, + DialMethodCheck[ PhoneBook[i]->Settings.DialMethod ], + PhoneBook[i]->Settings.InitStringIndex, + PhoneBook[i]->Settings.CallWaitStringIndex, + PhoneBook[i]->Settings.CallWaitString); + sprintf( entrytext, "%03d", i ); + ini.Put_String("PhoneBook", entrytext, buf); + } + + // Write the INI data out to a file. + ini.Save(file); + } +#endif +} + + +// Determine if a mission is from counterstrike or aftermath, or either. +// Multiplayer maps >24, with a numerical name, are Counterstrike. +// Multiplayer maps with an alphabetical name, like SCMJGEA.INI, are Aftermath. + +bool Is_Mission_Counterstrike (char *file_name) +{ + int scenario_number = 0; + + if ( isdigit ( file_name[5] )){ + sscanf (file_name, "SCM%03d", &scenario_number); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!isdigit(file_name[3]) || !isdigit(file_name[4])) { + return(false); + } +#endif + sscanf (file_name, "SCM%02d", &scenario_number); + } + return ( scenario_number > 24 ); +} + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +bool Is_Mission_Aftermath (char *file_name) +{ + // ajw added + // Must start with "scm". + char szCopy[ _MAX_PATH + 1 ]; + strcpy( szCopy, file_name ); + _strlwr( szCopy ); + if( strstr( szCopy, "scm" ) != szCopy ) + return false; + + if (isdigit(file_name[5])) { + return(false); + } + + if ( !isdigit(file_name[3]) || !isdigit(file_name[4]) ) { + return (true); + } + return (false); +} + +/* +** Certain missions are 126x126 size, and those can't be downloaded to a +** non-Aftermath player, so this function checks to see if the map in +** question is one of those. We'll know that by the file name: if it's +** K0 -> M9, it's 126x126. +*/ +bool Is_Mission_126x126 (char *file_name) // This is no longer used. ajw +{ + if (isdigit(file_name[5])) { + return(false); + } + + if ( (file_name[3] >= 'k' && file_name[3] <= 'm') || + (file_name[3] >= 'K' && file_name[3] <= 'M') ) { + return (true); + } + return (false); +} + +#endif + + + + + +/*************************************************************************** + * SessionClass::Read_Scenario_Descriptions -- reads scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 02/14/1995 BR : Created. * + * 09/10/1996 JLB : Searches using different method. * + *=========================================================================*/ +void SessionClass::Read_Scenario_Descriptions (void) +{ + + // Clear the scenario description lists + Scenarios.Clear(); + + /* + ** Fetch the main multiplayer scenario packet data. + */ + CCFileClass file("MISSIONS.PKT"); + if (file.Is_Available()) { + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in Missions.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "msns_pkt.txt" ); + file.Seek( 0, SEEK_SET ); + long lSize = file.Size(); + char* pData = new char[ lSize + 1 ]; + file.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } + + /* + ** Fetch any scenario packet lists and apply them first. + */ +#ifdef WIN32 + WIN32_FIND_DATA block; + HANDLE handle = FindFirstFile("*.PKT", &block); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + char const * name = &block.cAlternateFileName[0]; + if (*name == '\0') name = &block.cFileName[0]; +//Mono_Printf("Found file '%s'.\n", block.cAlternateFileName); +//Mono_Printf("Found file '%s'.\n", block.cFileName); +//debugprint("Found file '%s'.\n", block.cAlternateFileName); +//debugprint("Found file '%s'.\n", block.cFileName); +//debugprint( "Found alternate PKT file.\n" ); + CCFileClass file(name); + INIClass ini; + ini.Load(file); + + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else // FIXIT_VERSION_3 +#ifdef FIXIT_CSII // checked - ajw + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif +#endif // FIXIT_VERSION_3 + } + } + + if (FindNextFile(handle, &block) == 0) break; + } + + + #ifdef FIXIT_CSII // checked - ajw + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +#ifdef FIXIT_VERSION_3 + if( Is_Counterstrike_Installed() ) + { +#endif + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in cstrike.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_VERSION_3 + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#else + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } +/* // ajw Copy file for viewing. + CCFileClass fileCopy( "cs_pkt.txt" ); + file2.Seek( 0, SEEK_SET ); + long lSize = file2.Size(); + char* pData = new char[ lSize + 1 ]; + file2.Read( pData, lSize ); + fileCopy.Write( pData, lSize ); + fileCopy.Close(); +*/ } +#ifdef FIXIT_VERSION_3 + } +#endif + #endif + +#ifdef FIXIT_VERSION_3 // Aftermath scenarios are now in their own pkt file. + if( Is_Aftermath_Installed() ) + { + CCFileClass file2("AFTMATH.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); +//debugprint( "Found %i missions in aftmath.pkt\n", count ); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); + } + } + } +#endif + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + char const * file_name; + char name_buffer[128]; + char digest_buffer[32]; + + handle = FindFirstFile ( "*.MPR" , &block ); + while (handle != INVALID_HANDLE_VALUE) { + if ((block.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY)) == 0) { + file_name = &block.cAlternateFileName[0]; + if (*file_name == '\0') file_name = &block.cFileName[0]; +//debugprint( "Found MPR '%s'\n", file_name ); + CCFileClass file(file_name); + INIClass ini; + ini.Load(file); + + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + Scenarios.Add (new MultiMission (file_name, name_buffer, digest_buffer,ini.Get_Bool("Basic", "Official", false), false )); + } + + if (FindNextFile(handle, &block) == 0) break; + } + +#else //WIN32 + +#error What? You think you can still build the DOS version after all this time? + + char name_buffer[128]; + char digest_buffer[32]; + + struct find_t block; + if (_dos_findfirst("*.PKT", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); +#ifdef FIXIT_CSII + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); +#else + Scenarios.Add(new MultiMission(fname, buffer, NULL, true, + Is_Mission_Counterstrike ((char*)fname))); +#endif + } + + } while(_dos_findnext(&block) == 0); + } + + + /* + ** Scan the current directory for any loose .MPR files and build the appropriate entries + ** into the scenario list list + */ + if (_dos_findfirst("*.MPR", _A_NORMAL, &block) == 0) { + do { + CCFileClass file(block.name); + INIClass ini; + ini.Load(file); + ini.Get_String ("Basic", "Name", "No Name", name_buffer, sizeof (name_buffer) ); + ini.Get_String ("Digest", "1", "No Digest", digest_buffer, sizeof (digest_buffer) ); + bool official = ini.Get_Bool("Basic", "Official", false); + Scenarios.Add (new MultiMission (block.name, name_buffer, digest_buffer, official, false )); + } while(_dos_findnext(&block) == 0); + } + + #ifdef FIXIT_CSII + /* + ** Fetch the Counterstrike multiplayer scenario packet data. + ** Load the scenarios regardless of whether counterstrike's installed, + ** and at the point of hosting a network game, enable the counterstrike + ** maps only if they have CS installed. If they don't, then the maps + ** are available as a guest, but not as a host, which fixes a multitude + ** of problems without obviously giving the maps away to non-CS owners. + */ +// if (Is_Counterstrike_Installed()) { + CCFileClass file2("CSTRIKE.PKT"); + if (file2.Is_Available()) { + INIClass ini; + ini.Load(file2); + int count = ini.Entry_Count("Missions"); + for (int index = 0; index < count; index++) { + char const * fname = ini.Get_Entry("Missions", index); + char buffer[128]; + ini.Get_String("Missions", fname, "", buffer, sizeof(buffer)); + bool official = Is_Mission_126x126( (char *)fname); + if (!official) { + official = !Is_Mission_Aftermath((char *)fname); + } + + Scenarios.Add(new MultiMission(fname, buffer, NULL, official, + Is_Mission_Counterstrike ((char*)fname))); + } + } +// } + #endif + +#endif //WIN32 +} + + +/*************************************************************************** + * SessionClass::Free_Scenario_Descriptions -- frees scen. descriptions * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/05/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Free_Scenario_Descriptions(void) +{ + int i; + + //------------------------------------------------------------------------ + // Clear the scenario descriptions & filenames + //------------------------------------------------------------------------ + for (int index = 0; index < Scenarios.Count(); index++) { + delete Scenarios[index]; + } + Scenarios.Clear(); +// Filenum.Clear(); + + //------------------------------------------------------------------------ + // Clear the initstring entries + //------------------------------------------------------------------------ + for (i = 0; i < InitStrings.Count(); i++) { + delete InitStrings[i]; + } + InitStrings.Clear(); +#if (0)//PG + //------------------------------------------------------------------------ + // Clear the dialing entries + //------------------------------------------------------------------------ + for (i = 0; i < PhoneBook.Count(); i++) { + delete PhoneBook[i]; + } + PhoneBook.Clear(); +#endif +} /* end of Free_Scenario_Descriptions */ + + +/*************************************************************************** + * SessionClass::Trap_Object -- searches for an object, for debugging * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 06/02/1995 BRR : Created. * + *=========================================================================*/ +void SessionClass::Trap_Object(void) +{ + int i; + + //------------------------------------------------------------------------ + // Initialize + //------------------------------------------------------------------------ + TrapObject.Ptr.All = NULL; + + //------------------------------------------------------------------------ + // Search for the object based upon its type, then its coordinate or + // 'this' pointer value. + //------------------------------------------------------------------------ + switch (TrapObjType) { + case RTTI_AIRCRAFT: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Ptr(i)->Coord == TrapCoord || + Aircraft.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Ptr(i); + break; + } + } + break; + + case RTTI_ANIM: + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Ptr(i)->Coord == TrapCoord || + Anims.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Ptr(i); + break; + } + } + break; + + case RTTI_BUILDING: + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Ptr(i)->Coord == TrapCoord || + Buildings.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Ptr(i); + break; + } + } + break; + + case RTTI_BULLET: + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Ptr(i)->Coord == TrapCoord || + Bullets.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Ptr(i); + break; + } + } + break; + + case RTTI_INFANTRY: + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Ptr(i)->Coord == TrapCoord || + Infantry.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Ptr(i); + break; + } + } + break; + + case RTTI_UNIT: + for (i = 0; i < Units.Count(); i++) { + if (Units.Ptr(i)->Coord == TrapCoord || + Units.Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Ptr(i); + break; + } + } + break; + + //..................................................................... + // Last-ditch find-the-object-right-now-darnit loop + //..................................................................... + case RTTI_NONE: + for (i = 0; i < Aircraft.Count(); i++) { + if (Aircraft.Raw_Ptr(i)->Coord == TrapCoord || + Aircraft.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Aircraft = Aircraft.Raw_Ptr(i); + TrapObjType = RTTI_AIRCRAFT; + return; + } + } + for (i = 0; i < Anims.Count(); i++) { + if (Anims.Raw_Ptr(i)->Coord == TrapCoord || + Anims.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Anim = Anims.Raw_Ptr(i); + TrapObjType = RTTI_ANIM; + return; + } + } + for (i = 0; i < Buildings.Count(); i++) { + if (Buildings.Raw_Ptr(i)->Coord == TrapCoord || + Buildings.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Building = Buildings.Raw_Ptr(i); + TrapObjType = RTTI_BUILDING; + return; + } + } + for (i = 0; i < Bullets.Count(); i++) { + if (Bullets.Raw_Ptr(i)->Coord == TrapCoord || + Bullets.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Bullet = Bullets.Raw_Ptr(i); + TrapObjType = RTTI_BULLET; + return; + } + } + for (i = 0; i < Infantry.Count(); i++) { + if (Infantry.Raw_Ptr(i)->Coord == TrapCoord || + Infantry.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Infantry = Infantry.Raw_Ptr(i); + TrapObjType = RTTI_INFANTRY; + return; + } + } + for (i = 0; i < Units.Count(); i++) { + if (Units.Raw_Ptr(i)->Coord == TrapCoord || + Units.Raw_Ptr(i)->As_Target()==TrapTarget) { + TrapObject.Ptr.Unit = Units.Raw_Ptr(i); + TrapObjType = RTTI_UNIT; + return; + } + } + + default: + break; + } +} + + +/*************************************************************************** + * SessionClass::Compute_Unique_ID -- computes unique local ID number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1995 BRR : Created. * + *=========================================================================*/ +unsigned long SessionClass::Compute_Unique_ID(void) +{ + return 1;//PG +#if (0) //PG + time_t tm; + unsigned long id; + struct diskfree_t dtable; + char *path; + int i; + + //------------------------------------------------------------------------ + // Start with the seconds since Jan 1, 1970 (system local time) + //------------------------------------------------------------------------ + time(&tm); + id = (unsigned long)tm; + + //------------------------------------------------------------------------ + // Now add in the free space on the hard drive + //------------------------------------------------------------------------ + if (_dos_getdiskfree(3, &dtable) == 0) { + Add_CRC(&id, (unsigned long)dtable.avail_clusters); + Add_CRC(&id, (unsigned long)dtable.total_clusters); + Add_CRC(&id, (unsigned long)dtable.bytes_per_sector); + Add_CRC(&id, (unsigned long)dtable.sectors_per_cluster); + } + + //------------------------------------------------------------------------ + // Add in every byte in the user's path environment variable + //------------------------------------------------------------------------ + path = getenv("PATH"); + if (path) { + for (i = 0; i < strlen(path); i++) { + Add_CRC(&id, (unsigned long)path[i]); + } + } + + return (id); +#endif +} // end of Compute_Unique_ID + + + + +MultiMission::MultiMission(char const * filename, char const * description, char const * digest, bool official, bool expansion) +{ + Set_Filename(filename); + Set_Description(description); + Set_Digest(digest); + Set_Official(official); + Set_Expansion(expansion); +} + + +void MultiMission::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(ScenarioDescription, x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(ScenarioDescription, x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +void MultiMission::Set_Description(char const * description) +{ + if (description != NULL) { + strncpy(ScenarioDescription, description, ARRAY_SIZE(ScenarioDescription)); + ScenarioDescription[ARRAY_SIZE(ScenarioDescription)-1] = '\0'; + } +} + + +void MultiMission::Set_Filename(char const * filename) +{ + if (filename != NULL) { + strncpy(Filename, filename, ARRAY_SIZE(Filename)); + Filename[ARRAY_SIZE(Filename)-1] = '\0'; + } +} + +void MultiMission::Set_Digest(char const * digest) +{ + if (digest != NULL) { + strncpy(Digest, digest, ARRAY_SIZE(Digest)); + Digest[ARRAY_SIZE(Digest)-1] = '\0'; + } + else + { + strcpy( Digest, "NODIGEST" ); + } +} + +void MultiMission::Set_Official (bool official) +{ + IsOfficial = official; +} + +void MultiMission::Set_Expansion (bool expansion) +{ + IsExpansion = expansion; +} + + +/************************** end of session.cpp *****************************/ diff --git a/code/redalert/session.h b/code/redalert/session.h new file mode 100644 index 0000000..7a373c3 --- /dev/null +++ b/code/redalert/session.h @@ -0,0 +1,703 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SESSION.H 4 3/10/97 6:23p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : SESSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 11/30/95 * + * * + * Last Update : November 30, 1995 [BRR] * + * * + * The purpose of this class is to contain those variables & routines * + * specifically related to a multiplayer game. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SESSION_H +#define SESSION_H + +#include "ipxaddr.h" +#include "msglist.h" +#include "connect.h" + +//--------------------------------------------------------------------------- +// Forward declarations +//--------------------------------------------------------------------------- +class AircraftClass; +class AnimClass; +class BuildingClass; +class BulletClass; +class InfantryClass; +class UnitClass; +class PhoneEntryClass; +class CellClass; + +//--------------------------------------------------------------------------- +// Defines +//--------------------------------------------------------------------------- +//........................................................................... +// Various limiting values +//........................................................................... +#define MPLAYER_BUILD_LEVEL_MAX 10 // max build level in multiplay +#define MAX_MPLAYER_COLORS 8 // max # of colors + +//........................................................................... +// Max sizes of packets we want to send +// The IPX packet's size is IPX's max size (546), rounded down to accommodate +// the max number of events possible. +//........................................................................... +#define MAX_IPX_PACKET_SIZE (((546 - sizeof(CommHeaderType)) / \ + sizeof(EventClass) ) * sizeof(EventClass)) +#define MAX_SERIAL_PACKET_SIZE 256 + +//........................................................................... +// Max length of player names fields; attempt to use the constant for the +// HouseClass, if it's been defined; otherwise, define it myself. +//........................................................................... +#ifdef HOUSE_NAME_MAX +#define MPLAYER_NAME_MAX HOUSE_NAME_MAX +#else +#define MPLAYER_NAME_MAX 12 // max length of a player's name +#endif + +//........................................................................... +// Values to control the multiplayer score screen +//........................................................................... +#define MAX_MULTI_NAMES 8 // max # names (rows) on the score screen +#define MAX_MULTI_GAMES 4 // max # games (columns) on the score screen + +//........................................................................... +// Min value for MaxAhead, for both net & modem; only applies for +// COMM_PROTOCOL_MULTI_E_COMP. +//........................................................................... +#define MODEM_MIN_MAX_AHEAD 5 +#define NETWORK_MIN_MAX_AHEAD 2 + +//........................................................................... +// Send period (in frames) for COMM_PROTOCOL_MULTI_E_COMP and above +//........................................................................... +#define DEFAULT_FRAME_SEND_RATE 3 + +//........................................................................... +// Modem-specific constants +//........................................................................... +#define PORTBUF_MAX 64 // dialog field sizes +#define IRQBUF_MAX 3 +#define BAUDBUF_MAX 7 +#define INITSTRBUF_MAX 41 +#define CWAITSTRBUF_MAX 16 +#define CREDITSBUF_MAX 5 +#define PACKET_TIMING_TIMEOUT 40 // ticks b/w sending a timing packet +#define MODEM_NAME_MAX PORTBUF_MAX - 1 // Max length of modem name in list box + +//--------------------------------------------------------------------------- +// Enums +//--------------------------------------------------------------------------- +//........................................................................... +// Types of games; used to tell which protocol we're using +//........................................................................... +typedef enum GameEnum { + GAME_NORMAL, // not multiplayer + GAME_MODEM, // modem game + GAME_NULL_MODEM, // NULL-modem + GAME_IPX, // IPX Network game + GAME_INTERNET, // Internet H2H + GAME_SKIRMISH, // 1 plr vs. AI's + GAME_TEN, // TEN Network game + GAME_MPATH, // MPath Network game + GAME_GLYPHX_MULTIPLAYER // Multiplayer game controlled by the GLYPHX engine. ST - 5/14/2019 11:41AM +} GameType; + +//........................................................................... +// Various Modem-specific enums +//........................................................................... +typedef enum DetectPortType { + PORT_VALID = 0, + PORT_INVALID, + PORT_IRQ_INUSE +} DetectPortType; + +typedef enum DialStatusType { + DIAL_CONNECTED = 0, + DIAL_NO_CARRIER, + DIAL_BUSY, + DIAL_ERROR, + DIAL_NO_DIAL_TONE, + DIAL_CANCELED +} DialStatusType; + +typedef enum DialMethodType { + DIAL_TOUCH_TONE = 0, + DIAL_PULSE, + DIAL_METHODS +} DialMethodType; + +typedef enum CallWaitStringType { + CALL_WAIT_TONE_1 = 0, + CALL_WAIT_TONE_2, + CALL_WAIT_PULSE, + CALL_WAIT_CUSTOM, + CALL_WAIT_STRINGS_NUM +} CallWaitStringType; + +typedef enum ModemGameType { + MODEM_NULL_HOST = 0, + MODEM_NULL_JOIN, + MODEM_DIALER, + MODEM_ANSWERER +} ModemGameType; + +//........................................................................... +// Commands sent over the serial Global Channel +//........................................................................... +typedef enum SerialCommandType { + SERIAL_CONNECT = 100, // Are you there? Hello? McFly? + SERIAL_GAME_OPTIONS = 101, // Hey, dudes, here's some new game options + SERIAL_SIGN_OFF = 102, // Bogus, dudes, my boss is coming; I'm outta here! + SERIAL_GO = 103, // OK, dudes, jump into the game loop! + SERIAL_MESSAGE = 104, // Here's a message + SERIAL_TIMING = 105, // timimg packet + SERIAL_SCORE_SCREEN = 106, // player at score screen + SERIAL_LOADGAME = 107, // Start the game, loading a saved game first + SERIAL_LAST_COMMAND, // last command + SERIAL_REQ_SCENARIO = 1000, // Reqest that host sends the scenario file to the other players. + SERIAL_FILE_INFO = 1001, // Info about the file that is going to be transferred + SERIAL_FILE_CHUNK = 1002, // A chunk of scenario + SERIAL_READY_TO_GO = 1003, // Sent in response to a 'GO' command + SERIAL_NO_SCENARIO = 1004 // Scenario isnt available on remote machine so we cant play +} SerialCommandType; + +//........................................................................... +// Commands sent over the network Global Channel +//........................................................................... +typedef enum NetCommandType { + NET_QUERY_GAME, // Hey, what games are out there? + NET_ANSWER_GAME, // Yo, Here's my game's name! + NET_QUERY_PLAYER, // Hey, what players are in this game? + NET_ANSWER_PLAYER, // Yo, I'm in that game! + NET_CHAT_ANNOUNCE, // I'm at the chat screen + NET_CHAT_REQUEST, // Respond with a CHAT_ANNOUNCE, please. + NET_QUERY_JOIN, // Hey guys, can I play too? + NET_CONFIRM_JOIN, // Well, OK, if you really want to. + NET_REJECT_JOIN, // No, you can't join; sorry, dude. + NET_GAME_OPTIONS, // Hey, dudes, here's some new game options + NET_SIGN_OFF, // Bogus, dudes, my boss is coming; I'm outta here! + NET_GO, // OK, jump into the game loop! + NET_MESSAGE, // Here's a message + NET_PING, // I'm pinging you to take a time measurement + NET_LOADGAME, // start a game by loading a saved game + NET_REQ_SCENARIO =1000,// Reqest that host sends the scenario file to the other players. + NET_FILE_INFO =1001,// Info about the file that is going to be transferred + NET_FILE_CHUNK =1002,// A chunk of scenario + NET_READY_TO_GO =1003,// Sent in response to a 'GO' command + NET_NO_SCENARIO =1004 // Scenario isnt available on remote machine so we cant play +} NetCommandType; + +//--------------------------------------------------------------------------- +// Structures +//--------------------------------------------------------------------------- +//........................................................................... +// An entry on the score screen is defined by this structure +//........................................................................... +typedef struct { + char Name[MPLAYER_NAME_MAX]; + int Wins; + int Kills[MAX_MULTI_GAMES]; + PlayerColorType Color; +} MPlayerScoreType; + +//........................................................................... +// Settings for the serial port +//........................................................................... +typedef struct { + int Port; + int IRQ; + int Baud; + DialMethodType DialMethod; + int InitStringIndex; + int CallWaitStringIndex; + char CallWaitString[ CWAITSTRBUF_MAX ]; + bool Compression; + bool ErrorCorrection; + bool HardwareFlowControl; + char ModemName [ MODEM_NAME_MAX ]; +} SerialSettingsType; + +//........................................................................... +// This is a "node", used for the lists of available games & players. The +// 'Game' structure is used for games; the 'Player' structure for players. +//........................................................................... +typedef struct NodeNameTag { + char Name[MPLAYER_NAME_MAX]; // player or game name + IPXAddressClass Address; +#if(TEN) + int TenAddress; +#endif +#if(MPATH) + int MPathAddress; +#endif + union { + struct { + unsigned char IsOpen; // is the game open? + unsigned long LastTime; // last time we heard from this guy + } Game; + struct { + HousesType House; // "ActLike" House of this player + PlayerColorType Color; // Color of this player + HousesType ID; // Actual House of this player + int ProcessTime; // Length of time to process players main loop + } Player; + struct { + unsigned long LastTime; // last time we heard from this guy + unsigned char LastChance; // we're about to remove him from the list + PlayerColorType Color; // chat player's color + } Chat; + }; +} NodeNameType; + + +//........................................................................... +// Packet sent over the serial Global Channel +//........................................................................... +typedef struct { + SerialCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + unsigned char ID; // unique ID of sender of message + union { + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color or SIGNOFF ID + unsigned long MinVersion; // min version this game supports + unsigned long MaxVersion; // max version this game supports + char Scenario[DESCRIP_MAX]; // Scenario name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario : 1; // Is this scenario an official Westwood one? + int CheatCheck; // Unique ID of "rules.ini" file. + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long ResponseTime; // packet response time + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Message[MAX_MESSAGE_LENGTH]; // inter-player message + PlayerColorType Color; // player's color or SIGNOFF ID + } Message; + struct { + PlayerColorType Color; // player's color or SIGNOFF ID + } Chat; + }; +} SerialPacketType; + +//........................................................................... +// Other packet sent over the serial global channel (for file transfers) +//........................................................................... +#define MAX_SEND_FILE_PACKET_SIZE MAX_SERIAL_PACKET_SIZE - 64 +typedef struct { + SerialCommandType Command; // Enum defined above. Should be a file transfer enum. + unsigned short BlockNumber; // Index position of this file chunk in the file + unsigned short BlockLength; // Length of data in the RawData buffer + unsigned char RawData [MAX_SEND_FILE_PACKET_SIZE]; +} RemoteFileTransferType; + + +//........................................................................... +// Packet sent over the network Global Channel +//........................................................................... +typedef struct GlobalPacketType { + NetCommandType Command; // One of the enum's defined above + char Name[MPLAYER_NAME_MAX]; // Player or Game Name + union { + struct { + unsigned int IsOpen : 1; // 1 = game is open for joining + } GameInfo; + struct { + HousesType House; // player's House + PlayerColorType Color; // player's color + unsigned long NameCRC; // CRC of player's game's name + unsigned long MinVersion; // game's min supported version + unsigned long MaxVersion; // game's max supported version + int CheatCheck; // Unique ID of "rules.ini" file. + } PlayerInfo; + struct { + char Scenario[DESCRIP_MAX]; // Scenario Name + unsigned int Credits; // player's credits + unsigned int IsBases : 1; // 1 = bases are allowed + unsigned int IsTiberium : 1; // 1 = tiberium is allowed + unsigned int IsGoodies : 1; // 1 = goodies are allowed + unsigned int IsGhosties : 1; // 1 = ghosts are allowed + unsigned int OfficialScenario :1;// Is this scenario an official Westwood one? + unsigned char BuildLevel; // buildable level + unsigned char UnitCount; // max # units + unsigned char AIPlayers; // # of AI players allowed + int Seed; // random number seed + SpecialClass Special; // command-line options + unsigned int GameSpeed; // Game Speed + unsigned long Version; // version # common to all players + unsigned int FileLength; // Length of scenario file to expect from host. +#ifdef WOLAPI_INTEGRATION + char ShortFileName[13]; // Name of scenario file to expect from host +#else + char ShortFileName[12]; // Name of scenario file to expect from host +#endif + unsigned char FileDigest[32]; // Digest of scenario file to expect from host + // ajw - This is not necessarily null-terminated. + } ScenarioInfo; + struct { + char Buf[MAX_MESSAGE_LENGTH]; // inter-user message + PlayerColorType Color; // color of sender of message + unsigned long NameCRC; // CRC of sender's Game Name + } Message; + struct { + int OneWay; // one-way response time + } ResponseTime; + struct { + int Why; // why were we rejected from the game? + } Reject; + struct { + unsigned long ID; // unique ID for this chat node + PlayerColorType Color; // my color + } Chat; + }; +} GlobalPacketType; + + +//........................................................................... +// For finding sync bugs; filled in by the engine when certain conditions +// are met; the pointers allow examination of objects in the debugger. +//........................................................................... +typedef struct { + union { + AircraftClass *Aircraft; + AnimClass *Anim; + BuildingClass *Building; + BulletClass *Bullet; + InfantryClass *Infantry; + UnitClass *Unit; + void *All; + } Ptr; +} TrapObjectType; + +/* +** This is the identifier for a multiplayer mission. This can be used to +** identify the filename of the mission as well as display the mission in a +** mission selection list. +*/ +class MultiMission +{ + public: + MultiMission(char const * filename = NULL, char const * description = NULL, char const *digest = NULL, bool official = true, bool expansion = false); + + void Set_Description(char const * description); + void Set_Filename(char const * filename); + void Set_Digest(char const * digest); + void Set_Official(bool official); + void Set_Expansion(bool expansion); + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Description(void) const {return(ScenarioDescription);} + char const * Get_Filename(void) const {return(Filename);} + char const * Get_Digest(void) const {return(Digest);} + bool Get_Official(void) { return (IsOfficial); } + bool Get_Expansion(void) { return (IsExpansion); } // Implied "IsCounterstrike". No longer used. -ajw + + private: + char ScenarioDescription[DESCRIP_MAX]; + char Filename[_MAX_FNAME+_MAX_EXT]; + char Digest[32]; + bool IsOfficial; + bool IsExpansion; +}; + + +typedef struct { + int ScenarioIndex; //Used on host machine only as index into scenario list + int Bases; + int Credits; + int Tiberium; + int Goodies; + int Ghosts; + int UnitCount; + int AIPlayers; // # of AI players allowed to be built + char ScenarioDescription [DESCRIP_MAX]; //Used on client machines only +} GameOptionsType; + +//--------------------------------------------------------------------------- +// Class Definition +//--------------------------------------------------------------------------- +class SessionClass +{ + //------------------------------------------------------------------------ + // Public interface + //------------------------------------------------------------------------ + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + SessionClass(void); + ~SessionClass(void); + + //..................................................................... + // Initialization + //..................................................................... + void One_Time(void); + void Init(void); + + //..................................................................... + // Reads/writes to the INI file + //..................................................................... + void Read_MultiPlayer_Settings (void); + void Write_MultiPlayer_Settings (void); + void Read_Scenario_Descriptions (void); + void Free_Scenario_Descriptions(void); + + //..................................................................... + // Utility functions + //..................................................................... + int Create_Connections(void); + bool Am_I_Master(void); + unsigned long Compute_Unique_ID(void); + +#if(TEN) + int Create_TEN_Connections(void); +#endif // TEN + +#if(MPATH) + int Create_MPATH_Connections(void); +#endif // MPATH + + //..................................................................... + // File I/O + //..................................................................... + int Save(Pipe & file) const; + int Load(Straw & file); + int Save(CCFileClass & file); + int Load(CCFileClass & file); + + //..................................................................... + // Debugging / Sync Bugs + //..................................................................... + void Trap_Object(void); + + //--------------------------------------------------------------------- + // Public Data + //--------------------------------------------------------------------- + //..................................................................... + // The type of session being played + //..................................................................... + GameType Type; + + //..................................................................... + // The current communications protocol + //..................................................................... + CommProtocolType CommProtocol; + + //..................................................................... + // Game options + //..................................................................... + GameOptionsType Options; + + //..................................................................... + // Unique workstation ID, for detecting my own packets + //..................................................................... + unsigned long UniqueID; + + //..................................................................... + // Player's local options + //..................................................................... + char Handle[MPLAYER_NAME_MAX]; // player name + PlayerColorType PrefColor; // preferred color index + PlayerColorType ColorIdx; // actual color index + HousesType House; // GDI / NOD + int ObiWan; // 1 = player can see all + int Solo; // 1 = player can play alone + + //..................................................................... + // Max allowable # of players & actual # of (human) players + //..................................................................... + int MaxPlayers; + int NumPlayers; + + //..................................................................... + // Frame-sync'ing timing variables + // 'MaxAhead' is the number of frames ahead of this one to execute + // a given packet. It's set by the RESPONSE_TIME event. + // 'FrameSendRate' is the # frames between data packets + //..................................................................... + unsigned long MaxAhead; + unsigned long FrameSendRate; + + int DesiredFrameRate; + + int ProcessTimer; + int ProcessTicks; + int ProcessFrames; + + //..................................................................... + // This flag is set when we've loaded a multiplayer game. + //..................................................................... + int LoadGame; + + //..................................................................... + // This flag is set when the modem game saves the game due to a lost + // connection. + //..................................................................... + int EmergencySave; + + //..................................................................... + // List of scenarios & their file numbers + //..................................................................... + DynamicVectorClass Scenarios; +// DynamicVectorClass Scenarios; +// DynamicVectorClass Filenum; + + char ScenarioFileName[_MAX_FNAME+_MAX_EXT+1]; //File name of scenario to load + + char ScenarioDigest [32]; //Digest of scenario to load + unsigned int ScenarioFileLength; + bool ScenarioIsOfficial; + + char ScenarioRequests[20]; //Which players requested scenario files + int RequestCount; + IPXAddressClass HostAddress; + + //..................................................................... + // This is the multiplayer messaging system + //..................................................................... + MessageListClass Messages; + IPXAddressClass MessageAddress; + char LastMessage[MAX_MESSAGE_LENGTH]; + unsigned WWChat : 1; // 1 = go into special WW Chat mode + + //..................................................................... + // This is the multiplayer scorekeeping system + //..................................................................... + MPlayerScoreType Score[MAX_MULTI_NAMES]; + int GamesPlayed; // # games played this run + int NumScores; // # active entries in MPlayerScore + int Winner; // index of winner of last game + int CurGame; // index of current game being played + + //..................................................................... + // Static arrays + //..................................................................... + static char Descriptions[100][40]; + static int CountMin[2]; + static int CountMax[2]; + static char * GlobalPacketNames[]; + static char * SerialPacketNames[]; + + //..................................................................... + // For Recording & Playing back a file + //..................................................................... + CCFileClass RecordFile; + unsigned Record : 1; + unsigned Play : 1; + unsigned Attract : 1; + + //..................................................................... + // IPX-specific variables + //..................................................................... + int IsBridge; // 1 = we're crossing a bridge + IPXAddressClass BridgeNet; // address of bridge + bool NetStealth; // makes us invisible + bool NetProtect; // keeps others from messaging us + bool NetOpen; // 1 = game is open for joining + char GameName[MPLAYER_NAME_MAX]; // game's name + GlobalPacketType GPacket; // global packet + int GPacketlen; // global packet length + IPXAddressClass GAddress; // address of sender + unsigned short GProductID; // product ID of sender + char MetaPacket[MAX_IPX_PACKET_SIZE]; // packet building buffer + int MetaSize; // size of MetaPacket + DynamicVectorClass Games; // list of games + DynamicVectorClass Players; // list of players + DynamicVectorClass Chat; // list of chat nodes + + //..................................................................... + // Modem-specific variables + //..................................................................... + unsigned ModemService : 1; // 1 = service modem in Call_Back + int CurPhoneIdx; // phone listing index + SerialSettingsType SerialDefaults; // default serial settings + ModemGameType ModemType; // caller or answerer? + + DynamicVectorClass PhoneBook; + DynamicVectorClass InitStrings; + static char * DialMethodCheck[ DIAL_METHODS ]; + static char * CallWaitStrings[ CALL_WAIT_STRINGS_NUM ]; + + //..................................................................... + // For finding Sync Bugs + //..................................................................... + long TrapFrame; // frame # to start trapping 'TrapObject' + RTTIType TrapObjType; // type of object to trap + TrapObjectType TrapObject; // ptr to object to trap (watch) + COORDINATE TrapCoord; // coord of object, 0 = ignore + TARGET TrapTarget; // Target # of object, 0 = ignore + CellClass * TrapCell; // Ptr to cell to trap (watch) + int TrapCheckHeap; // true = check the heap as of TrapFrame + long TrapPrintCRC; // Frame # to print CRC state file + +#if(TEN) + // + // TEN-specific variables + // + char *TenPacket; + int TenSize; + int TenMessageAddress; + int TenAddress; + int TenPlayerID; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // TEN + +#if(MPATH) + // + // MPATH-specific variables + // + char *MPathPacket; + int MPathSize; + int MPathMessageAddress; + int MPathAddress; + char OptionsFile[256]; + int AllowSolo; + int NetResponseTime; +#endif // MPATH +}; + +#endif // SESSION_H + +/*************************** end of session.h ******************************/ diff --git a/code/redalert/sha.cpp b/code/redalert/sha.cpp new file mode 100644 index 0000000..aedbf5a --- /dev/null +++ b/code/redalert/sha.cpp @@ -0,0 +1,314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHA.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/03/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAEngine::Result -- Fetch the current digest. * + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data blo* + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +//#include +#include "sha.h" + + + +#if !defined(__BORLANDC__) && !defined(min) +#define min(a, b) ((a)<(b))?(a):(b) +#endif + + +/*********************************************************************************************** + * SHAEngine::Process_Partial -- Helper routine to process any partially accumulated data bloc * + * * + * This routine will see if there is a partial block already accumulated in the holding * + * buffer. If so, then the data is fetched from the source such that a full buffer is * + * accumulated and then processed. If there is insufficient data to fill the buffer, then * + * it accumulates what data it can and then returns so that this routine can be called * + * again later. * + * * + * INPUT: data -- Reference to a pointer to the data. This pointer will be modified if * + * this routine consumes any of the data in the buffer. * + * * + * length-- Reference to the length of the data available. If this routine consumes * + * any of the data, then this length value will be modified. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Partial(void const * & data, long & length) +{ + if (length == 0 || data == NULL) return; + + /* + ** If there is no partial buffer and the source is greater than + ** a source block size, then partial processing is unnecessary. + ** Bail out in this case. + */ + if (PartialCount == 0 && length >= SRC_BLOCK_SIZE) return; + + /* + ** Attach as many bytes as possible from the source data into + ** the staging buffer. + */ + int add_count = min((int)length, SRC_BLOCK_SIZE - PartialCount); + memcpy(&Partial[PartialCount], data, add_count); + data = ((char const *&)data) + add_count; + PartialCount += add_count; + length -= add_count; + + /* + ** If a full staging buffer has been accumulated, then process + ** the staging buffer and then bail. + */ + if (PartialCount == SRC_BLOCK_SIZE) { + Process_Block(&Partial[0], Acc); + Length += (long)SRC_BLOCK_SIZE; + PartialCount = 0; + } +} + + +/*********************************************************************************************** + * SHAEngine::Hash -- Process an arbitrarily long data block. * + * * + * This is the main access routine to the SHA engine. It will take the arbitrarily long * + * data block and process it. The hash value is accumulated with any previous calls to * + * this routine. * + * * + * INPUT: data -- Pointer to the data block to process. * + * * + * length -- The number of bytes to process. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Hash(void const * data, long length) +{ + IsCached = false; + + /* + ** Check for and handle any smaller-than-512bit blocks. This can + ** result in all of the source data submitted to this routine to be + ** consumed at this point. + */ + Process_Partial(data, length); + + /* + ** If there is no more source data to process, then bail. Speed reasons. + */ + if (length == 0) return; + + /* + ** First process all the whole blocks available in the source data. + */ + long blocks = (length / SRC_BLOCK_SIZE); + long const * source = (long const *)data; + for (int bcount = 0; bcount < blocks; bcount++) { + Process_Block(source, Acc); + Length += (long)SRC_BLOCK_SIZE; + source += SRC_BLOCK_SIZE/sizeof(long); + length -= (long)SRC_BLOCK_SIZE; + } + + /* + ** Process any remainder bytes. This data is stored in the source + ** accumulator buffer for future processing. + */ + data = source; + Process_Partial(data, length); +} + + +#define Reverse_LONG(a) ((a>>24)&0x000000FFL) | ((a>>8)&0x0000FF00L) | ((a<<8)&0x00FF0000L) | ((a<<24)&0xFF000000L) + + +/*********************************************************************************************** + * SHAEngine::Result -- Fetch the current digest. * + * * + * This routine will return the digest as it currently stands. * + * * + * INPUT: pointer -- Pointer to the buffer that will hold the digest -- 20 bytes. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will always be * + * 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAEngine::Result(void * result) const +{ + /* + ** If the final hash result has already been calculated for the + ** current data state, then immediately return with the precalculated + ** value. + */ + if (IsCached) { + memcpy(result, &FinalResult, sizeof(FinalResult)); + } + + long length = Length + PartialCount; + int partialcount = PartialCount; + char partial[SRC_BLOCK_SIZE]; + memcpy(partial, Partial, sizeof(Partial)); + + /* + ** Cap the end of the source data stream with a 1 bit. + */ + partial[partialcount] = (char)0x80; + + /* + ** Determine if there is insufficient room to append the + ** data length number to the hash source. If not, then + ** fill out the rest of the accumulator and flush it to + ** the hash so that there will be room for the final + ** count value. + */ + SHADigest acc = Acc; + if ((SRC_BLOCK_SIZE - partialcount) < 9) { + if (partialcount+1 < SRC_BLOCK_SIZE) { + memset(&partial[partialcount+1], '\0', SRC_BLOCK_SIZE - (partialcount+1)); + } + Process_Block(&partial[0], acc); + partialcount = 0; + } else { + partialcount++; + } + + /* + ** Put the length of the source data as a 64 bit integer in the + ** last 8 bytes of the pseudo-source data. + */ + memset(&partial[partialcount], '\0', SRC_BLOCK_SIZE - partialcount); + *(long *)(&partial[SRC_BLOCK_SIZE-4]) = Reverse_LONG((length*8)); + Process_Block(&partial[0], acc); + + memcpy((char *)&FinalResult, &acc, sizeof(acc)); + for (int index = 0; index < sizeof(FinalResult)/sizeof(long); index++) { +// for (int index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + (long &)FinalResult.Long[index] = Reverse_LONG(FinalResult.Long[index]); + } + (bool&)IsCached = true; + memcpy(result, &FinalResult, sizeof(FinalResult)); + return(sizeof(FinalResult)); +} + +/* +** This pragma to turn off the warning "Conversion may lose significant digits" is to +** work around a bug within the Borland compiler. It will give this warning when the +** _rotl() function is called but will NOT give the warning when the _lrotl() function +** is called even though they both have the same parameters and declaration attributes. +*/ +//#pragma warn -sig +template +T _rotl(T X, int n) +{ + return(T)( ( X << n ) | ( (unsigned)X >> ((sizeof(T)*8) - n) ) ); +} +//unsigned long _RTLENTRY _rotl(unsigned long X, int n) +//{ +// return(unsigned long)( (unsigned long)( (unsigned long)( (unsigned long)X ) << (int)n ) | (unsigned long)( ((unsigned long) X ) >> ( (int)((int)(sizeof(long)*(long)8) - (long)n) ) ) ); +//} +void memrev(char * buffer, size_t length); + + +/*********************************************************************************************** + * SHAEngine::Process_Block -- Process a full data block into the hash accumulator. * + * * + * This helper routine is called when a full block of data is available for processing * + * into the hash. * + * * + * INPUT: source -- Pointer to the block of data to process. * + * * + * acc -- Reference to the hash accumulator that this hash step will be * + * accumulated into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SHAEngine::Process_Block(void const * source, SHADigest & acc) const +{ + /* + ** The hash is generated by performing operations on a + ** block of generated/seeded data. + */ + long block[PROC_BLOCK_SIZE/sizeof(long)]; + + /* + ** Expand the source data into a large 80 * 32bit buffer. This is the working + ** data that will be transformed by the secure hash algorithm. + */ + long const * data = (long const *)source; + int index; + for (index = 0; index < SRC_BLOCK_SIZE/sizeof(long); index++) { + block[index] = Reverse_LONG(data[index]); + } + + for (index = SRC_BLOCK_SIZE/sizeof(long); index < PROC_BLOCK_SIZE/sizeof(long); index++) { +// block[index] = _rotl(block[(index-3)&15] ^ block[(index-8)&15] ^ block[(index-14)&15] ^ block[(index-16)&15], 1); + block[index] = _rotl(block[index-3] ^ block[index-8] ^ block[index-14] ^ block[index-16], 1); + } + + /* + ** This is the core algorithm of the Secure Hash Algorithm. It is a block + ** transformation of 512 bit source data with a 2560 bit intermediate buffer. + */ + SHADigest alt = acc; + for (index = 0; index < PROC_BLOCK_SIZE/sizeof(long); index++) { + long temp = _rotl(alt.Long[0], 5) + Do_Function(index, alt.Long[1], alt.Long[2], alt.Long[3]) + alt.Long[4] + block[index] + Get_Constant(index); + alt.Long[4] = alt.Long[3]; + alt.Long[3] = alt.Long[2]; + alt.Long[2] = _rotl(alt.Long[1], 30); + alt.Long[1] = alt.Long[0]; + alt.Long[0] = temp; + } + acc.Long[0] += alt.Long[0]; + acc.Long[1] += alt.Long[1]; + acc.Long[2] += alt.Long[2]; + acc.Long[3] += alt.Long[3]; + acc.Long[4] += alt.Long[4]; +} + diff --git a/code/redalert/sha.h b/code/redalert/sha.h new file mode 100644 index 0000000..e75dfb2 --- /dev/null +++ b/code/redalert/sha.h @@ -0,0 +1,201 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHA.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHA.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 04/26/96 * + * * + * Last Update : April 26, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHA_H +#define SHA_H + + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +#include +#include +#include +#include + + +/* +** This implements the Secure Hash Algorithm. It is a cryptographically +** secure hash with no known weaknesses. It generates a 160 bit hash +** result given an arbitrary length data source. +*/ +class SHAEngine +{ + public: + SHAEngine(void) : IsCached(false), Length(0), PartialCount(0) { + Acc.Long[0] = SA; + Acc.Long[1] = SB; + Acc.Long[2] = SC; + Acc.Long[3] = SD; + Acc.Long[4] = SE; + }; + + void Init(void) { + new ((void*)this) SHAEngine; + }; + + // Fetch result as if source data were to stop now. + int Result(void * result) const; + + void Hash(void const * data, long length); + + static int Digest_Size(void) {return(sizeof(SHADigest));} + + private: + + typedef union { + unsigned long Long[5]; + unsigned char Char[20]; + } SHADigest; + + /* + ** This holds the calculated final result. It is cached + ** here to avoid the overhead of recalculating it over + ** multiple sequential requests. + */ + bool IsCached; + SHADigest FinalResult; + + enum { + // These are the initial seeds to the block accumulators. + SA=0x67452301L, + SB=0xefcdab89L, + SC=0x98badcfeL, + SD=0x10325476L, + SE=0xc3d2e1f0L, + + // These are the constants used in the block transformation. + K1=0x5a827999L, // t=0..19 2^(1/2)/4 + K2=0x6ed9eba1L, // t=20..39 3^(1/2)/4 + K3=0x8f1bbcdcL, // t=40..59 5^(1/2)/4 + K4=0xca62c1d6L, // t=60..79 10^(1/2)/4 + + // Source data is grouped into blocks of this size. + SRC_BLOCK_SIZE=16*sizeof(long), + + // Internal processing data is grouped into blocks this size. + PROC_BLOCK_SIZE=80*sizeof(long) + }; + + long Get_Constant(int index) const { + if (index < 20) return K1; + if (index < 40) return K2; + if (index < 60) return K3; + return K4; + }; + + // Used for 0..19 + long Function1(long X, long Y, long Z) const { + return(Z ^ ( X & ( Y ^ Z ) ) ); + }; + + // Used for 20..39 + long Function2(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + // Used for 40..59 + long Function3(long X, long Y, long Z) const { + return( (X & Y) | (Z & (X | Y) ) ); + }; + + // Used for 60..79 + long Function4(long X, long Y, long Z) const { + return( X ^ Y ^ Z ); + }; + + long Do_Function(int index, long X, long Y, long Z) const { + if (index < 20) return Function1(X, Y, Z); + if (index < 40) return Function2(X, Y, Z); + if (index < 60) return Function3(X, Y, Z); + return Function4(X, Y, Z); + }; + + // Process a full source data block. + void Process_Block(void const * source, SHADigest & acc) const; + + // Processes a partially filled source accumulator buffer. + void Process_Partial(void const * & data, long & length); + + /* + ** This is the running accumulator values. These values + ** are updated by a block processing step that occurs + ** every 512 bits of source data. + */ + SHADigest Acc; + + /* + ** This is the running length of the source data + ** processed so far. This total is used to modify the + ** resulting hash value as if it were appended to the end + ** of the source data. + */ + long Length; + + /* + ** This holds any partial source block. Partial source blocks are + ** a consequence of submitting less than block sized data chunks + ** to the SHA Engine. + */ + int PartialCount; + char Partial[SRC_BLOCK_SIZE]; +}; + + +#define SHA_SOURCE1 "abc" +#define SHA_DIGEST1a "\xA9\x99\x3E\x36\x47\x06\x81\x6A\xBA\x3E\x25\x71\x78\x50\xC2\x6C\x9C\xD0\xD8\x9D" +#define SHA_DIGEST1b "\x01\x64\xB8\xA9\x14\xCD\x2A\x5E\x74\xC4\xF7\xFF\x08\x2C\x4D\x97\xF1\xED\xF8\x80" + + +#define SHA_SOURCE2 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +#define SHA_DIGEST2a "\x84\x98\x3E\x44\x1C\x3B\xD2\x6E\xBA\xAE\x4A\xA1\xF9\x51\x29\xE5\xE5\x46\x70\xF1" +#define SHA_DIGEST2b "\xD2\x51\x6E\xE1\xAC\xFA\x5B\xAF\x33\xDF\xC1\xC4\x71\xE4\x38\x44\x9E\xF1\x34\xC8" + +#define SHA_SOURCE3 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define SHA_DIGEST3a "\x34\xAA\x97\x3C\xD4\xC4\xDA\xA4\xF6\x1E\xEB\x2B\xDB\xAD\x27\x31\x65\x34\x01\x6F" +#define SHA_DIGEST3b "\x32\x32\xAF\xFA\x48\x62\x8A\x26\x65\x3B\x5A\xAA\x44\x54\x1F\xD9\x0D\x69\x06\x03" + +#endif diff --git a/code/redalert/shape.cpp b/code/redalert/shape.cpp new file mode 100644 index 0000000..e516583 --- /dev/null +++ b/code/redalert/shape.cpp @@ -0,0 +1,1588 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/** + * @file + * + * @author CCHyper + * @author OmniBlade + * + * @brief Low level functions for loading and rendering C&C sprite files. + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +/* +============================ + +This file has been heavily modified by IceColdDuke(Justin Marshall) to render with OpenGL. + +============================ +*/ + +#include "FUNCTION.H" +#include +#include + +#include "Image.h" + +#define le16toh(x) x +#define le32toh(x) x + +using std::memcpy; + +#define SHP_HAS_PAL 0x0001 +#define SHP_LCW_FRAME 0x80 +#define SHP_XOR_FAR_FRAME 0x40 +#define SHP_XOR_PREV_FRAME 0x20 +#define BIGSHP_BUFFER_MIN_FREE 128000 +#define BIGSHP_BUFFER_GROW 2000000 + +#define SHAPE_TRANSPARENT 0x40 + +struct ShapeHeaderStruct +{ + uint16_t m_FrameCount; + uint16_t m_XPos; // In memory 0xDDD5 here indicates we have a keyframe slot allocated + uint16_t m_YPos; // Used in memory to hold keyframe slot + uint16_t m_Width; + uint16_t m_Height; + uint16_t m_LargestFrameSize; + uint16_t m_Flags; +}; + +struct ShapeBufferHeader +{ + uint32_t m_DrawFlags; + uint32_t m_FrameOffset; + BOOL m_IsTheaterShape; +}; + +extern "C" BOOL UseBigShapeBuffer; +extern unsigned BigShapeBufferLength; +extern unsigned TheaterShapeBufferLength; +extern bool OriginalUseBigShapeBuffer; + +extern "C" extern char* BigShapeBufferStart; +extern char* BigShapeBufferPtr; +// int TotalBigShapes = 0; +BOOL g_ReallocShapeBufferFlag; +extern "C" extern char* TheaterShapeBufferStart; +extern char* TheaterShapeBufferPtr; +int g_TotalTheaterShapes = 0; +uint32_t* g_KeyFrameSlots[1500]; +int g_TotalSlotsUsed = 0; +// int BuildFrameLength = 0; +extern "C" extern bool UseOldShapeDraw; +extern "C" char* _ShapeBuffer = nullptr; +long _ShapeBufferSize = 0; +int g_TheaterSlotsUsed = 1000; +int g_ShapeLength; + +// The predator effect basically just takes a destination pixel and replaces it +// with the value g_PredTable[g_PredFrame] pixels away if g_PartialCount +// is greater than or equal to 256. After every pixel, it is increased by +// g_PartialPred and reset to % 256 after reaching 256 or greater. +static const int16_t g_PredTable[8] = { 1, 3, 2, 5, 2, 3, 4, 1 }; +static uint32_t g_PredFrame; +static uint32_t g_PartialCount; +static uint32_t g_PartialPred; +static uint8_t* g_PredatorLimit = nullptr; + +// Buffer Frame to Page function pointer type defs +typedef void (*BF_Function)(int, int, uint8_t*, uint8_t*, int, int, uint8_t*, uint8_t*, uint8_t*, int); +typedef void (*Single_Line_Function)(int, uint8_t*, uint8_t*, uint8_t*, uint8_t*, uint8_t*, int); + +bool renderHDTexture = false; +void Buffer_Enable_HD_Texture(bool hdTextureEnabled) { + renderHDTexture = hdTextureEnabled; +} + +#define ChannelBlend_Alpha(A,B,O) ((uint8_t)((O / 255.0f) * A + (1 - (O / 255.0f)) * B)) + +// Just copy source to dest as is. +void BF_Copy(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + memcpy(dst, src, width); + dst = dst + dst_pitch + width; + src = src + src_pitch + width; + } +} + +// Index 0 transparency +void BF_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + dst[0] = backbuffer_palette[(sbyte * 3) + 0]; + dst[1] = backbuffer_palette[(sbyte * 3) + 1]; + dst[2] = backbuffer_palette[(sbyte * 3) + 2]; + dst[3] = 255; + } + + dst += 4; + } + + src += src_pitch; + dst += dst_pitch * 4; + } +} + +// Fading table based shadow and transparency +void BF_Ghost(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst++ = sbyte; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +// Fading table based shadow and transparency with index 0 ignored +void BF_Ghost_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + dst[0] = backbuffer_palette[(sbyte * 3) + 0]; + dst[1] = backbuffer_palette[(sbyte * 3) + 1]; + dst[2] = backbuffer_palette[(sbyte * 3) + 2]; + dst[3] = 255; + } + + dst += 4; + } + + src += src_pitch; + dst += dst_pitch * 4; + } +} + +void BF_Fading(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src; + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } + } +} + +void BF_Fading_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Ghost_Fading(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Ghost_Fading_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + dst[0] = backbuffer_palette[(sbyte * 3) + 0]; + dst[1] = backbuffer_palette[(sbyte * 3) + 1]; + dst[2] = backbuffer_palette[(sbyte * 3) + 2]; + dst[3] = 255; + } + + dst += 4; + } + + src += src_pitch; + dst += dst_pitch * 4; + } +} + +void BF_Predator(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, uint8_t* ghost_lookup, + uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + g_PartialCount += g_PartialPred; + + // if ( g_PartialCount & 0xFF00 ) { + // g_PartialCount &= 0xFFFF00FF; + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + *dst = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + ++dst; + } + + src += src_pitch + width; + dst += dst_pitch; + } +} + +void BF_Predator_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + *dst = sbyte; + } + + ++dst; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Predator_Ghost(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst++ = sbyte; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Predator_Ghost_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst = sbyte; + } + + ++dst; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Predator_Fading(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } + } +} + +void BF_Predator_Fading_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Predator_Ghost_Fading(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +void BF_Predator_Ghost_Fading_Trans(int width, int height, uint8_t* dst, uint8_t* src, int dst_pitch, int src_pitch, + uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + while (height--) { + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } + + src += src_pitch; + dst += dst_pitch; + } +} + +// Jump table for BF_* functions +static const BF_Function OldShapeJumpTable[16] = { BF_Copy, + BF_Trans, + BF_Ghost, + BF_Ghost_Trans, + BF_Fading, + BF_Fading_Trans, + BF_Ghost_Fading, + BF_Ghost_Fading_Trans, + BF_Predator, + BF_Predator_Trans, + BF_Predator_Ghost, + BF_Predator_Ghost_Trans, + BF_Predator_Fading, + BF_Predator_Fading_Trans, + BF_Predator_Ghost_Fading, + BF_Predator_Ghost_Fading_Trans }; + +// Single line versions +void Single_Line_Skip( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ +} + +// This was Short_Single_Line_Copy, renamed for consistency +void Single_Line_Copy( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + memcpy(dst, src, width); +} + +void Single_Line_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Ghost( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Ghost_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Fading( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Fading_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Single_Fade( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + *dst++ = fade_tab[*src++]; + } +} + +void Single_Line_Single_Fade_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + *dst = fade_tab[sbyte]; + } + + ++dst; + } +} + +void Single_Line_Ghost_Fading( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Ghost_Fading_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Predator( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + g_PartialCount += g_PartialPred; + + // captainslog_dbgassert(g_PredFrame < 8, "Predator frame %u.\n", g_PredFrame); + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + *dst = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + ++dst; + } +} + +void Single_Line_Predator_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + // captainslog_dbgassert(g_PredFrame < 8, "Predator frame %u.\n", g_PredFrame); + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Predator_Ghost( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Predator_Ghost_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Predator_Fading( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Predator_Fading_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +void Single_Line_Predator_Ghost_Fading( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst++ = sbyte; + } +} + +void Single_Line_Predator_Ghost_Fading_Trans( + int width, uint8_t* dst, uint8_t* src, uint8_t* ghost_lookup, uint8_t* ghost_tab, uint8_t* fade_tab, int count) +{ + for (int i = width; i > 0; --i) { + uint8_t sbyte = *src++; + + if (sbyte) { + g_PartialCount += g_PartialPred; + + if (g_PartialCount >= 256) { + g_PartialCount %= 256; + + if (&dst[g_PredTable[g_PredFrame]] < g_PredatorLimit) { + sbyte = dst[g_PredTable[g_PredFrame]]; + } + + g_PredFrame = (g_PredFrame + 2) % 8; + } + + uint8_t fbyte = ghost_lookup[sbyte]; + + if (fbyte != 0xFF) { + sbyte = ghost_tab[*dst + fbyte * 256]; + } + + for (int i = 0; i < count; ++i) { + sbyte = fade_tab[sbyte]; + } + + *dst = sbyte; + } + + ++dst; + } +} + +// Jump table for Single_Line_* functions +static Single_Line_Function NewShapeJumpTable[32] = { Single_Line_Copy, + Single_Line_Trans, + Single_Line_Ghost, + Single_Line_Ghost_Trans, + Single_Line_Fading, + Single_Line_Fading_Trans, + Single_Line_Ghost_Fading, + Single_Line_Ghost_Fading_Trans, + Single_Line_Predator, + Single_Line_Predator_Trans, + Single_Line_Predator_Ghost, + Single_Line_Predator_Ghost_Trans, + Single_Line_Predator_Fading, + Single_Line_Predator_Fading_Trans, + Single_Line_Predator_Ghost_Fading, + Single_Line_Predator_Ghost_Fading_Trans, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip, + Single_Line_Skip }; + +// This one appears to deal with the buffered shape data +void* Get_Shape_Header_Data(void* shape) +{ + ShapeBufferHeader* header = static_cast(shape); + if (UseBigShapeBuffer) { + if (header->m_IsTheaterShape) { + return header->m_FrameOffset + TheaterShapeBufferStart; + } + else { + return header->m_FrameOffset + BigShapeBufferStart; + } + } + + return shape; +} + +int Get_Last_Frame_Length() +{ + return g_ShapeLength; +} + +void Reset_Theater_Shapes() +{ + // I think this loops through and deletes any slot that is > 1000 + if (g_TheaterSlotsUsed > 1000) { + for (int i = 1000; i < g_TheaterSlotsUsed; ++i) { + delete[] g_KeyFrameSlots[i]; + } + } + + TheaterShapeBufferPtr = TheaterShapeBufferStart; + g_TotalTheaterShapes = 0; + g_TheaterSlotsUsed = 1000; +} + +void Reallocate_Big_Shape_Buffer() +{ + { + // g_MemoryError = nullptr; + BigShapeBufferLength += BIGSHP_BUFFER_GROW; + BigShapeBufferPtr -= (intptr_t)BigShapeBufferStart; + BigShapeBufferStart = (char*)Resize_Alloc(BigShapeBufferStart, BigShapeBufferLength); + // captainslog_debug("Reallocating Big Shape Buffer, size is now %d.", BigShapeBufferLength); + // TODO + // g_MemoryError = Memory_Error_Handler; + if (BigShapeBufferStart) { + g_ReallocShapeBufferFlag = false; + BigShapeBufferPtr += (intptr_t)BigShapeBufferStart; + } + else { + UseBigShapeBuffer = false; + } + } +} + +void Disable_Uncompressed_Shapes() +{ + UseBigShapeBuffer = false; +} + +void Enable_Uncompressed_Shapes() +{ + UseBigShapeBuffer = OriginalUseBigShapeBuffer; +} + +void* Build_Frame2(void* shape, uint16_t frame, void* buffer) +{ + uint8_t* shape_data = static_cast(shape); + // frame = frame; + g_ShapeLength = 0; + + if (shape == nullptr || buffer == nullptr) { + return nullptr; + } + + ShapeHeaderStruct* header = static_cast(shape); + if (frame >= le16toh(header->m_FrameCount)) { + // captainslog_debug( + // "Requested frame %d is greater than total frames %d in this shape file.\n", frame, + // le16toh(header->m_FrameCount)); + + return nullptr; + } + + // If we are using a cache + if (UseBigShapeBuffer) { + if (!BigShapeBufferStart) { + // captainslog_debug("Allocating buffers for UseBigShapeBuffer."); + BigShapeBufferStart = static_cast(Alloc(BigShapeBufferLength, MEM_NORMAL)); + BigShapeBufferPtr = BigShapeBufferStart; + TheaterShapeBufferStart = static_cast(Alloc(TheaterShapeBufferLength, MEM_NORMAL)); + TheaterShapeBufferPtr = TheaterShapeBufferStart; + } + + if (BigShapeBufferLength + (uintptr_t)BigShapeBufferStart - (uintptr_t)BigShapeBufferPtr + < BIGSHP_BUFFER_MIN_FREE) { + g_ReallocShapeBufferFlag = true; + } + + // Do we have a keyframe slot allocated already? + if (header->m_XPos != 0xDDD5) { + header->m_XPos = 0xDDD5; + if (IsTheaterShape) { + header->m_YPos = g_TheaterSlotsUsed++; + } + else { + header->m_YPos = g_TotalSlotsUsed++; + } + + g_KeyFrameSlots[header->m_YPos] = new uint32_t[header->m_FrameCount]; + memset(g_KeyFrameSlots[header->m_YPos], 0, sizeof(uint32_t) * header->m_FrameCount); + } + + // Do we have anything in our keyframe slot yet? If so, return it. + uint32_t shp_buff_offset = g_KeyFrameSlots[header->m_YPos][frame]; + if (shp_buff_offset != 0) { + // captainslog_debug("Using Cached frame."); + + if (IsTheaterShape) { + return shp_buff_offset + TheaterShapeBufferStart; + } + else { + return shp_buff_offset + BigShapeBufferStart; + } + } + } + + // If we don't have a cache or failed to find a cached image for this frame, we need to decode the frame we want. + uint32_t offset_buff[7]; + int frame_size = le16toh(header->m_Height) * le16toh(header->m_Width); + memcpy(offset_buff, &shape_data[8 * frame + sizeof(ShapeHeaderStruct)], 12); + uint8_t frame_type = (le32toh(offset_buff[0]) & 0xFF000000) >> 24; + + if (frame_type & SHP_LCW_FRAME) { + // captainslog_debug("Decoding key frame."); + uint8_t* frame_data = &shape_data[le32toh(offset_buff[0]) & 0xFFFFFF]; + + // Amazingly it seems that shp files actually do support having a pal, just none do. + if (header->m_Flags & SHP_HAS_PAL) { + frame_data = &shape_data[(le32toh(offset_buff[0]) & 0xFFFFFF) + 768]; + } + + frame_size = LCW_Uncomp(frame_data, buffer, frame_size); + } + else { + // captainslog_debug("Decoding XOR frame."); + int ref_frame = 0; + // If we have an Xor chain, load first delta address into buffer + if (frame_type & SHP_XOR_PREV_FRAME) { + ref_frame = le32toh(offset_buff[1]) & 0xFFFF; + memcpy(offset_buff, &shape_data[8 * ref_frame + sizeof(ShapeHeaderStruct)], 28); + } + + // Get the base LCW data and the offset from it to the Xor data + int base_m_FrameOffset = offset_buff[1] & 0xFFFFFF; + int xor_data_offset = (le32toh(offset_buff[0]) & 0xFFFFFF) - (le32toh(offset_buff[1]) & 0xFFFFFF); + char* lcw_data = (char *)&shape_data[le32toh(offset_buff[1]) & 0xFFFFFF]; + + if (header->m_Flags & SHP_HAS_PAL) { + lcw_data = (char*)&shape_data[(le32toh(offset_buff[1]) & 0xFFFFFF) + 768]; + } + + if (LCW_Uncomp(lcw_data, buffer, frame_size) > frame_size) { + // captainslog_debug("LCW decompressed more data than expected."); + return nullptr; + } + + Apply_XOR_Delta((char*)buffer, lcw_data + xor_data_offset); + + if (frame_type & SHP_XOR_PREV_FRAME) { + // captainslog_debug("Decoding delta sequence."); + ++ref_frame; + int offset_index = 2; + + while (ref_frame <= frame) { + Apply_XOR_Delta((char *)buffer, &lcw_data[(le32toh(offset_buff[offset_index]) & 0xFFFFFF) - base_m_FrameOffset]); + ++ref_frame; + offset_index += 2; + + if (offset_index >= 6 && ref_frame <= frame) { + offset_index = 0; + memcpy(offset_buff, &shape_data[8 * ref_frame + sizeof(ShapeHeaderStruct)], 28); + } + } + } + } + + // This bit handles if we have a shape buffer to cache the decompressed frames + ShapeBufferHeader* buff_header = nullptr; + + if (UseBigShapeBuffer) { + if (IsTheaterShape) { + char* saved_tsbp = TheaterShapeBufferPtr; + // Why height? I don't get it? Anyhow, this bit is aligning the memory + // Ahh, Buffer_Frame_To_Page writes flags into the extra area + // for how each line is to be processed. + char* aligned_tsbp = TheaterShapeBufferPtr + header->m_Height + sizeof(ShapeBufferHeader); + + // Align memory pointer + uintptr_t align = (uintptr_t)aligned_tsbp; + + if ((align % sizeof(void*)) != 0) { + align += sizeof(void*) - (align % sizeof(void*)); + } + + aligned_tsbp = (char*)align; + + memcpy(aligned_tsbp, buffer, frame_size); + buff_header = reinterpret_cast(TheaterShapeBufferPtr); + buff_header->m_DrawFlags = -1; + buff_header->m_IsTheaterShape = true; + buff_header->m_FrameOffset = aligned_tsbp - TheaterShapeBufferStart; + g_KeyFrameSlots[header->m_YPos][frame] = TheaterShapeBufferPtr - TheaterShapeBufferStart; + TheaterShapeBufferPtr = aligned_tsbp + frame_size; + + // Align memory pointer + align = (uintptr_t)TheaterShapeBufferPtr; + if ((align % sizeof(void*)) != 0) { + align += sizeof(void*) - (align % sizeof(void*)); + } + TheaterShapeBufferPtr = (char*)align; + + g_ShapeLength = frame_size; + + return saved_tsbp; + } + else { + char* saved_bsbp = BigShapeBufferPtr; + char* aligned_bsbp = BigShapeBufferPtr + header->m_Height + sizeof(ShapeBufferHeader); + + // Align memory pointer + uintptr_t align = (uintptr_t)aligned_bsbp; + + if ((align % sizeof(void*)) != 0) { + align += sizeof(void*) - (align % sizeof(void*)); + } + + aligned_bsbp = (char*)align; + + memcpy(aligned_bsbp, buffer, frame_size); + buff_header = reinterpret_cast(BigShapeBufferPtr); + buff_header->m_DrawFlags = -1; + buff_header->m_IsTheaterShape = false; + buff_header->m_FrameOffset = aligned_bsbp - BigShapeBufferStart; + g_KeyFrameSlots[header->m_YPos][frame] = BigShapeBufferPtr - BigShapeBufferStart; + BigShapeBufferPtr = aligned_bsbp + frame_size; + + // Align memory pointer + align = (uintptr_t)BigShapeBufferPtr; + + if ((align % sizeof(void*)) != 0) { + align += sizeof(void*) - (align % sizeof(void*)); + } + + BigShapeBufferPtr = (char*)align; + + g_ShapeLength = frame_size; + return saved_bsbp; + } + } + + return buffer; +} + +unsigned long Build_Frame(void const* dataptr, unsigned short framenumber, void* buffptr) { + UseBigShapeBuffer = false; + return (unsigned long)Build_Frame2((void *)dataptr, framenumber, buffptr); // 32 bit to 64 bit issue +} + +unsigned short Get_Build_Frame_Count(void* shape) +{ + if (shape != nullptr) { + return static_cast(shape)->m_FrameCount; + } + + return 0; +} + +unsigned short Get_Build_Frame_X(void* shape) +{ + if (shape != nullptr) { + return static_cast(shape)->m_XPos; + } + + return 0; +} + +unsigned short Get_Build_Frame_Y(void* shape) +{ + if (shape != nullptr) { + return static_cast(shape)->m_YPos; + } + + return 0; +} + +unsigned short Get_Build_Frame_Width(void* shape) +{ + if (shape != nullptr) { + return static_cast(shape)->m_Width; + } + + return 0; +} + +unsigned short Get_Build_Frame_Height(void* shape) +{ + if (shape != nullptr) { + return static_cast(shape)->m_Height; + } + + return 0; +} + +// This would handle a shp file with a palette, RA doesn't actually have any that +// have this though. +BOOL Get_Build_Frame_Palette(void* shape, void* pal) +{ + ShapeHeaderStruct* header = static_cast(shape); + + if (shape && header->m_Flags & SHP_HAS_PAL) { + // calc offset to palette + memcpy(pal, static_cast(shape) + 8 * header->m_FrameCount + sizeof(ShapeHeaderStruct), 768); + + return true; + } + + return false; +} + +extern "C" void Set_Shape_Buffer(const void* buffer, int size) +{ + _ShapeBuffer = (char *)(buffer); + _ShapeBufferSize = size; +} + +int Set_Shape_Height(void* shape, unsigned short new_height) +{ + int oldheight = static_cast(shape)->m_Height; + static_cast(shape)->m_Height = new_height; + return oldheight; +} + +// Used by Buffer_Frame_To_Page to flag which blit function to use for each line +// Results are cached for subsequent draw calls. +static void Single_Line_Flagger( + int width, int height, void* frame, void* draw_header, int flags, void* ghost_tab, void* ghost_lookup) +{ + uint8_t* shape_src; + uint8_t* flag_dst; + uint8_t tmp_flags; + int current_byte; + uint8_t has_skipped; + uint8_t row_flags; + int skipped; + + shape_src = static_cast(frame); + ShapeBufferHeader* header = static_cast(draw_header); + header->m_DrawFlags = flags & 0x1340; + flag_dst = static_cast(draw_header) + sizeof(ShapeBufferHeader); + uint8_t* ghost = static_cast(ghost_lookup); + + for (int i = height; i > 0; --i) { + tmp_flags = 0; + skipped = 0; + for (int j = width; j > 0; --j) { + current_byte = *shape_src++; + + if (current_byte || !(flags & SHAPE_TRANSPARENT)) { + if (flags & SHAPE_PREDATOR) { + tmp_flags |= 8; + } + + if (flags & SHAPE_GHOST && ghost[current_byte] != 0xFF) { + tmp_flags |= 2; + } + + if (flags & SHAPE_FADING) { + tmp_flags |= 4; + } + } + else { + tmp_flags |= 1; + ++skipped; + } + } + + has_skipped = 0; + + if (tmp_flags & 1 && (has_skipped = 1, width == skipped)) { + row_flags = 0x10; + } + else { + row_flags = (tmp_flags & 4) | (tmp_flags & 2) | (tmp_flags & 8) | has_skipped; + } + + *flag_dst++ = row_flags; + } +} + +long Buffer_Frame_To_Page(int shapeNum, int x, int y, int width, int height, struct Image_t * shape_image, unsigned int Window, int flags, ...) +{ + BOOL use_old_drawer = false; + int fade_count = 0; + ShapeBufferHeader* draw_header = nullptr; + uint8_t* fade_table = nullptr; + uint8_t* ghost_table = nullptr; + uint8_t* ghost_lookup = nullptr; + + if (!shape_image) { + return 0; + } + + va_list ap; + va_start(ap, flags); + + int blit_style = 0; + + if (flags & SHAPE_CENTER) { + x -= width / 2; + y -= height / 2; + } + + // Sets for BF_Trans functions + if (flags & SHAPE_TRANSPARENT) { + blit_style |= 1; + } + + // Sets for BF_Ghost functions + if (flags & SHAPE_GHOST) { + blit_style |= 2; + ghost_lookup = va_arg(ap, uint8_t*); + ghost_table = ghost_lookup + 256; + } + + if (!UseBigShapeBuffer || UseOldShapeDraw) { + use_old_drawer = true; + } + + // Sets for BF_Fading functions + if (flags & SHAPE_FADING) { + fade_table = va_arg(ap, uint8_t*); + fade_count = va_arg(ap, int) & 0x3F; + blit_style |= 4; + + if (!fade_count) { + flags &= ~SHAPE_FADING; + } + + // s_Special blitters for if fade step count is only 1 + NewShapeJumpTable[4] = Single_Line_Single_Fade; + NewShapeJumpTable[5] = Single_Line_Single_Fade_Trans; + + if (fade_count != 1) { + NewShapeJumpTable[4] = Single_Line_Fading; + NewShapeJumpTable[5] = Single_Line_Fading_Trans; + } + } + + // Sets for BF_Predator functions + //if (flags & SHAPE_PREDATOR) { + // int current_frame = va_arg(ap, uint32_t); + // blit_style |= 8; + // + // g_PredFrame = ((unsigned)current_frame) % 8; + // g_PartialCount = 0; + // g_PartialPred = 256; + // + // // Calculates the end of the visible display buffer, hopefully prevent crashes from predator effect. + // // Unused by default in RA, but would be nice on the phase tank in Aftermath. + // g_PredatorLimit = (uint8_t*)(viewport.Get_Offset()) + viewport.Get_Full_Pitch() * viewport.Get_Height(); + //} + + if (flags & SHAPE_PARTIAL) { + g_PartialPred = va_arg(ap, int) & 0xFF; + } + + va_end(ap); + + int xstart = x; + int ystart = y; + int yend = y + height - 1; + int xend = x + width - 1; + int ms_img_offset = 0; + + // If we aren't drawing within the viewport, return. + //if (xstart >= viewport.Get_Width() || ystart >= viewport.Get_Height() || xend <= 0 || yend <= 0) { + // return 0; + //} + + // Do any needed clipping. + //if (xstart < 0) { + // ms_img_offset = -xstart; + // xstart = 0; + // use_old_drawer = true; + //} + // + //if (ystart < 0) { + // //frame_data += width * (-ystart); + // ystart = 0; + // use_old_drawer = true; + //} + + if (xstart + width < 0 || ystart + height < 0) + return 0; + + // if (xend >= viewport.Get_Width() - 1) { + // xend = viewport.Get_Width() - 1; + // use_old_drawer = true; + // } + // + // if (yend >= viewport.Get_Height() - 1) { + // yend = viewport.Get_Height() - 1; + // use_old_drawer = true; + // } + + int blit_width = xend - xstart + 1; + int blit_height = yend - ystart + 1; + + //int pitch = viewport.Get_Full_Pitch(); + //uint8_t* dst = (ystart * pitch * 4) + (xstart * 4) + (uint8_t*)(viewport.Get_Offset()); + //uint8_t* src = frame_data + ms_img_offset; + //if (renderHDTexture) { + // Image_t* image = (Image_t*)shape; + // src = image->buffer + (ms_img_offset * 4); + //} + + //int dst_pitch = pitch - blit_width; + //int src_pitch = width - blit_width; + + // Use "new" line drawing routines that appear to have been added during the windows port. + //if (use_old_drawer != true && !renderHDTexture) { + // // DEBUG_SAY("Drawing with Single_Line draw functions\n"); + // + // // Here we can use the individual line drawing routines + // // Means we can skip drawing some lines all together or avoid using + // // more expensive routines on some lines. + // uint8_t* line_flags = reinterpret_cast(draw_header + 1); + // + // for (int i = 0; i < blit_height; ++i) { + // NewShapeJumpTable[line_flags[i] & 0x1F](blit_width, dst, src, ghost_lookup, ghost_table, fade_table, fade_count); + // src += width; + // dst += pitch * 4; + // } + // + // return 0; + //} + xstart = xstart + WindowList[Window][WINDOWX];// + LogicPage->Get_XPos(); + ystart = ystart + WindowList[Window][WINDOWY];// + LogicPage->Get_YPos(); + //GL_SetClipRect(WindowList[Window][WINDOWX], WindowList[Window][WINDOWY], WindowList[Window][WINDOWWIDTH], WindowList[Window][WINDOWWIDTH]); + if(renderHDTexture) + GL_RenderImage(shape_image, xstart, ystart, width, height, (int)fade_table, shapeNum); + else + GL_RenderImage(shape_image, xstart, ystart, width, height, 0); + + // Here we just use the function that will blit the entire frame + // using the appropriate effects. + //if (blit_height > 0 && blit_width > 0) { + // // DEBUG_SAY("Drawing with BF draw functions\n"); + // if (!renderHDTexture) + // { + // OldShapeJumpTable[blit_style & 0xF](blit_width, blit_height, dst, src, dst_pitch, src_pitch, ghost_lookup, ghost_table, fade_table, fade_count); + // } + // else + // { + // int src_index = 0; + // for (int f = 0; f < height; f++) + // { + // for (int i = width; i > 0; --i) { + // //if (src_index >= ((f + 1) * blit_height * 4)) + // // break; + // + // if (src[src_index + 3] > 0) { + // dst[0] = ChannelBlend_Alpha(src[(src_index) + 0], dst[0], src[(src_index) + 3]); + // dst[1] = ChannelBlend_Alpha(src[(src_index) + 1], dst[1], src[(src_index) + 3]); + // dst[2] = ChannelBlend_Alpha(src[(src_index) + 2], dst[2], src[(src_index) + 3]); + // dst[3] = 255; + // } + // src_index += 4; + // dst += 4; + // } + // + // src_index += src_pitch * 4; + // dst += dst_pitch * 4; + // } + // } + //} + + return 0; +} diff --git a/code/redalert/shapebtn.cpp b/code/redalert/shapebtn.cpp new file mode 100644 index 0000000..7d1dc59 --- /dev/null +++ b/code/redalert/shapebtn.cpp @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPEBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "shapebtn.h" + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Default Constructor for a shape type button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: You must call Set_Shape() before using a button constructed with this function, * + * and you must set X & Y, and ID. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0), + ReflectButtonState(false) +{ +} + + +/*********************************************************************************************** + * ShapeButtonClass::ShapeButtonClass -- Constructor for a shape type button. * + * * + * This is the normal constructor for a shape type button. Shape buttons are ones that * + * have their imagery controlled by a shape file. The various states of the button are * + * given a visual form as one of these shapes. Button dimensions are controlled by the * + * first shape. * + * * + * INPUT: id -- The button ID. * + * * + * shape -- Pointer to the shape file that controls the button's display. * + * * + * x,y -- The pixel coordinate of the upper left corner of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The width and height of the shape is controlled by the first shape in the * + * shape file provided. This means that all the shapes in the shape file must be * + * the same size. * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ShapeButtonClass::ShapeButtonClass(unsigned id, void const * shape, int x, int y) : + ToggleClass(id, x, y, 0, 0), + ReflectButtonState(false) +{ +// Width = 0; +// Height = 0; + Set_Shape(shape); +} + + +/*********************************************************************************************** + * ShapeButtonClass::Set_Shape -- Assigns a shape to this shape button. * + * * + * This routine will assign the specified shape to this shape object. * + * * + * INPUT: data -- Pointer to the shape to assign. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void ShapeButtonClass::Set_Shape(void const * data) +{ + ShapeData = data; + if (ShapeData) { + Width = Get_Build_Frame_Width(ShapeData); + Height = Get_Build_Frame_Height(ShapeData); + } +} + + +/*********************************************************************************************** + * ShapeButtonClass::Draw_Me -- Renders the shape button's imagery. * + * * + * This function is called when the button detects that it must be redrawn. The actual * + * shape to use is controled by the button's state and the shape file provided when then * + * button was constructed. * + * * + * INPUT: forced -- Should the button be redrawn regardless of the redraw flag? * + * * + * OUTPUT: bool; Was the shape redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int ShapeButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced) && ShapeData) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the body & set text color. + */ + int shapenum = 0; + if (IsDisabled) { + shapenum = DISABLED_SHAPE; + } else { + + if (!ReflectButtonState) { + + if (IsPressed) { + shapenum = DOWN_SHAPE; + } else { + shapenum = UP_SHAPE; + } + } else { + shapenum = IsOn; + } + + } + CC_Draw_Shape(ShapeData, shapenum, X, Y, WINDOW_MAIN, SHAPE_NORMAL); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + + diff --git a/code/redalert/shapebtn.h b/code/redalert/shapebtn.h new file mode 100644 index 0000000..4265e7e --- /dev/null +++ b/code/redalert/shapebtn.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPEBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPEBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SHAPEBTN_H +#define SHAPEBTN_H + +#include "toggle.h" + +class ShapeButtonClass : public ToggleClass +{ + public: + ShapeButtonClass(void); + ShapeButtonClass(unsigned id, void const * shapes, int x, int y); + virtual int Draw_Me(int forced=false); + virtual void Set_Shape(void const * data); + void const * Get_Shape_Data(void) {return(ShapeData);}; + + enum ShapeButtonClassEnums { + UP_SHAPE, // Shape to use when button is "up". + DOWN_SHAPE, // Shape to use when button is "down". + DISABLED_SHAPE // Shape to use when button is disabled. + }; + + unsigned ReflectButtonState:1; + + protected: + + /* + ** This points to the shape data file. This file contains the appropriate shapes + ** for this button in the offsets specified above. + */ + void const * ShapeData; +}; +#endif diff --git a/code/redalert/shapipe.cpp b/code/redalert/shapipe.cpp new file mode 100644 index 0000000..99cd87d --- /dev/null +++ b/code/redalert/shapipe.cpp @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPIPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAPipe::Result -- Fetches the current SHA value. * + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shapipe.h" + + +/*********************************************************************************************** + * SHAPipe::Put -- Pass data through the pipe, but use it to build a SHA digest. * + * * + * This pipe segment will not modify the data, but it will examine the data and use it when * + * building a SHA digest. * + * * + * INPUT: source -- Pointer to the data to flow through the pipe. * + * * + * length -- The number of bytes to submit. * + * * + * OUTPUT: Returns with the actual number of bytes output at the distant final end of the * + * pipe chain. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Put(void const * source, int slen) +{ + SHA.Hash(source, slen); + return(Pipe::Put(source, slen)); +} + + +/*********************************************************************************************** + * SHAPipe::Result -- Fetches the current SHA value. * + * * + * This routine will return the SHA digest for the data that has passed through this * + * link in the pipe chain. It is a non-destructive read. * + * * + * INPUT: result -- Pointer to the buffer to hold the SHA digest. This buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes copied into the buffer. This will be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAPipe::Result(void * result) const +{ + return(SHA.Result(result)); +} + + diff --git a/code/redalert/shapipe.h b/code/redalert/shapipe.h new file mode 100644 index 0000000..9d90417 --- /dev/null +++ b/code/redalert/shapipe.h @@ -0,0 +1,63 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHAPIPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHAPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/30/96 * + * * + * Last Update : June 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHAPIPE_H +#define SHAPIPE_H + +#include "pipe.h" +#include "sha.h" + +/* +** This class serves as a pipe that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAPipe : public Pipe +{ + public: + SHAPipe(void) {} + virtual int Put(void const * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAPipe(SHAPipe & rvalue); + SHAPipe & operator = (SHAPipe const & pipe); +}; + +#endif diff --git a/code/redalert/shastraw.cpp b/code/redalert/shastraw.cpp new file mode 100644 index 0000000..634f52f --- /dev/null +++ b/code/redalert/shastraw.cpp @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHASTRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHASTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * SHAStraw::Result -- Fetches the current SHA digest. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "shastraw.h" + + +/*********************************************************************************************** + * SHAStraw::Get -- Fetch data from the straw and process the SHA with the data. * + * * + * This routine will fetch the requested data and as it passes through this straw it will * + * submit it to the SHA processor. The data that passes through is unmodified by this * + * straw segment. * + * * + * INPUT: source -- Pointer to the buffer that will hold the requested data. * + * * + * length -- The length of the data requested. * + * * + * OUTPUT: Returns with the number of bytes stored in the buffer. If this number is less * + * than the number requested, then this indicates that the data stream has been * + * exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Get(void * source, int slen) +{ + if (source == NULL || slen < 1) { + return(0); + } + + int counter = Straw::Get(source, slen); + SHA.Hash(source, counter); + return(counter); +} + + +/*********************************************************************************************** + * SHAStraw::Result -- Fetches the current SHA digest. * + * * + * Use this routine to fetch the current SHA digest from the straw. It will return the * + * digest of the data that has passed through this straw segment. * + * * + * INPUT: result -- Pointer to the buffer to hold the message digest. The buffer must be * + * 20 bytes long. * + * * + * OUTPUT: Returns with the number of bytes stored into the digest buffer. This will always * + * be 20. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int SHAStraw::Result(void * result) const +{ + return(SHA.Result(result)); +} diff --git a/code/redalert/shastraw.h b/code/redalert/shastraw.h new file mode 100644 index 0000000..54c25ce --- /dev/null +++ b/code/redalert/shastraw.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SHASTRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SHASTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SHASTRAW_H +#define SHASTRAW_H + + +#include "straw.h" +#include "sha.h" + +/* +** This class serves as a straw that generates a Secure Hash from the data stream that flows +** through it. It doesn't modify the data stream in any fashion. +*/ +class SHAStraw : public Straw +{ + public: + SHAStraw(void) {} + virtual int Get(void * source, int slen); + + // Fetch the SHA hash value (stored in result buffer -- 20 bytes long). + int Result(void * result) const; + + protected: + SHAEngine SHA; + + private: + SHAStraw(SHAStraw & rvalue); + SHAStraw & operator = (SHAStraw const & straw); +}; + + +#endif diff --git a/code/redalert/sidebar.cpp b/code/redalert/sidebar.cpp new file mode 100644 index 0000000..2a9912e --- /dev/null +++ b/code/redalert/sidebar.cpp @@ -0,0 +1,2474 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/SIDEBAR.CPP 2 3/17/97 1:05a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * SidebarClass::Activate -- Controls the sidebar activation. * + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * SidebarClass::Draw_It -- Renders the sidebar display. * + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::One_Time -- Handles the one time game initializations. * + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the s* + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * SidebarClass::Set_Current -- Sets a specified object that controls the sidebar display. * + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syst* + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * SidebarClass::StripClass::Init_IO -- Adds buttons to the button list * + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side str* + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selecte* + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select bu* + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +//#undef RESFACTOR +//#define RESFACTOR 1 + +void * SidebarClass::SidebarShape = NULL; +void * SidebarClass::SidebarMiddleShape = NULL; +void * SidebarClass::SidebarBottomShape = NULL; + + +/*************************************************************************** +** This holds the translucent table for use with the construction clock +** animation. +*/ +char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; + + +/*************************************************************************** +** This points to the main sidebar shapes. These include the upgrade and +** repair buttons. +*/ +//TheaterType SidebarClass::StripClass::LastTheater = THEATER_NONE; + +typedef enum ButtonNumberType { + BUTTON_RADAR = 100, + BUTTON_REPAIR, + BUTTON_DEMOLISH, + BUTTON_UPGRADE, + BUTTON_SELECT, + BUTTON_ZOOM +} ButtonNumberType; + +/* +** Sidebar buttons +*/ +SidebarClass::SBGadgetClass SidebarClass::Background; +ShapeButtonClass SidebarClass::Repair; +ShapeButtonClass SidebarClass::Upgrade; +ShapeButtonClass SidebarClass::Zoom; +ShapeButtonClass SidebarClass::StripClass::UpButton[COLUMNS]; +ShapeButtonClass SidebarClass::StripClass::DownButton[COLUMNS]; +SidebarClass::StripClass::SelectClass +SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; + +/* +** Shape data pointers +*/ +void * SidebarClass::StripClass::LogoShapes = NULL; +void const * SidebarClass::StripClass::ClockShapes; +void const * SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(void) : + IsSidebarActive(false), + IsToRedraw(true), + IsRepairActive(false), + IsUpgradeActive(false), + IsDemolishActive(false) +{ + +} + + +/*********************************************************************************************** + * SidebarClass::SidebarClass -- This is the no initialization constructor for the sidebar. * + * * + * Unlike the normal constructor, this one doesn't do any initialization. There is one * + * exception to this. The stip classes can't call an explicit NoInitClass constructor * + * since they are an array. Since the default constructor is called for these strips, we * + * must reset the X and Y location to what we know they should be. * + * * + * INPUT: flag to indicate that this is a no initialization constructor. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +SidebarClass::SidebarClass(NoInitClass const & x) : PowerClass(x) +{ + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; +} + + +/*********************************************************************************************** + * SidebarClass::One_Time -- Handles the one time game initializations. * + * * + * This routine is used to load the graphic data that is needed by the sidebar display. It * + * should only be called ONCE. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once when the game first starts. * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::One_Time(void) +{ + PowerClass::One_Time(); + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ + new (&Column[0]) StripClass(InitClass()); + new (&Column[1]) StripClass(InitClass()); + + int column_one_x = (COLUMN_ONE_X * RESFACTOR); + int column_one_y = (COLUMN_ONE_Y * RESFACTOR); + + int column_two_x = (COLUMN_TWO_X * RESFACTOR); + int column_two_y = (COLUMN_TWO_Y * RESFACTOR); + + Column[0].X = column_one_x; + Column[0].Y = column_one_y; + Column[1].X = column_two_x; + Column[1].Y = column_two_y; + + /* + ** This sets up the clipping window. This window is used by the shape drawing + ** code so that as the sidebar buildable buttons scroll, they get properly + ** clipped at the top and bottom edges. + */ + WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X+8)) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWY] = (SIDE_Y + 1 + TOP_HEIGHT) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = ScreenHeight; +// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; + + /* + ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM + */ + WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1*RESFACTOR; + + /* + ** Set up the coordinates for the sidebar strips. These coordinates are for + ** the upper left corner. + */ +// Column[0].X = COLUMN_ONE_X * RESFACTOR; +// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; +// Column[1].X = COLUMN_TWO_X * RESFACTOR; +// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; + Column[0].One_Time(0); + Column[1].One_Time(1); + + /* + ** Load the sidebar shape in at this time. (Hi-Res sidebar is theater dependant) + */ + if (SidebarShape == NULL) { + SidebarShape = (void*)MFCD::Retrieve("SIDEBAR.SHP"); + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Clear(void) +{ + + PowerClass::Init_Clear(); + + IsToRedraw = true; + IsRepairActive = false; + IsUpgradeActive = false; + IsDemolishActive = false; + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + Activate(false); +} + + +/*********************************************************************************************** + * SidebarClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_IO(void) +{ + PowerClass::Init_IO(); + + /* + ** Add the sidebar's buttons only if we're not in editor mode. + */ + if (!Debug_Map) { + + Repair.IsSticky = true; + Repair.ID = BUTTON_REPAIR; + Repair.X = ScreenWidth - (640 - (0x1f2/2)*RESFACTOR); + Repair.Y = (0x96/2)*RESFACTOR; + Repair.IsPressed = false; + Repair.IsToggleType = true; + Repair.ReflectButtonState = true; + Repair.Set_Shape(MFCD::Retrieve("REPAIR.SHP")); + + Upgrade.IsSticky = true; + Upgrade.ID = BUTTON_UPGRADE; + Upgrade.X = ScreenWidth - (640 - 0x21f); + Upgrade.Y = (0x96/2)*RESFACTOR; + Upgrade.IsPressed = false; + Upgrade.IsToggleType = true; + Upgrade.ReflectButtonState = true; + Upgrade.Set_Shape(MFCD::Retrieve("SELL.SHP")); + + Zoom.IsSticky = true; + Zoom.ID = BUTTON_ZOOM; + Zoom.X = ScreenWidth - (640 - ((0x24c/2)*RESFACTOR)); + Zoom.Y = (0x96/2)*RESFACTOR; + Zoom.IsPressed = false; + Zoom.Set_Shape(MFCD::Retrieve("MAP.SHP")); + + if ((IsRadarActive && Is_Zoomable()) || Session.Type != GAME_NORMAL) { + Zoom.Enable(); + } else { + Zoom.Disable(); + } + Column[0].Init_IO(0); + Column[1].Init_IO(1); + + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + if (IsSidebarActive) { + IsSidebarActive = false; + Activate(1); +// Background.Zap(); +// Add_A_Button(Background); + } + } +} + + +/*********************************************************************************************** + * SidebarClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater -- The theater that is being initialized. Sometimes this has an effect on * + * the data that is loaded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Init_Theater(TheaterType theater) +{ + Reload_Sidebar(); + + PowerClass::Init_Theater(theater); + + Column[0].Init_Theater(theater); + Column[1].Init_Theater(theater); +} + +/*********************************************************************************************** + * SidebarClass::Reload_Sidebar -- Loads appropriate sidebar shapes depending on house * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/18/1996 BWG : Created. * + *=============================================================================================*/ +void SidebarClass::Reload_Sidebar(void) +{ + static char * sidebarnames[]={ + "SIDE?NA.SHP", //NATO + "SIDE?NA.SHP", + "SIDE?US.SHP", //USSR + "SIDE?NA.SHP", + "SIDE?US.SHP", //UKRAINE + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", + "SIDE?NA.SHP", //HOUSE_GOOD + "SIDE?US.SHP" //HOUSE_BAD + }; + int houseloaded = 0; + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } + + /* Don't have write access to the static char array. ST - 5/20/2019 */ +#if (0) + char * sidename = sidebarnames[houseloaded]; + *(sidename+4) = '1'; + SidebarShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '2'; + SidebarMiddleShape = (void*)MFCD::Retrieve(sidename); + *(sidename+4) = '3'; + SidebarBottomShape = (void*)MFCD::Retrieve(sidename); +#else + char sb_name[16]; + strcpy(sb_name, sidebarnames[houseloaded]); + sb_name[4] = '1'; + SidebarShape = (void*)MFCD::Retrieve(sb_name); + sb_name[4] = '2'; + SidebarMiddleShape = (void*)MFCD::Retrieve(sb_name); + sb_name[4] = '3'; + SidebarBottomShape = (void*)MFCD::Retrieve(sb_name); + +#endif + + + + + Column[0].Reload_LogoShapes(); + Column[1].Reload_LogoShapes(); +} + +/*********************************************************************************************** + * SidebarClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) +{ + assert((unsigned)type < RTTI_COUNT); + assert(id >= 0); + + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + +/*********************************************************************************************** + * SidebarClass::Refresh_Cells -- Intercepts the refresh, looking for sidebar controls. * + * * + * This routine intercepts the Refresh_Cells call in order to see if the sidebar needs * + * to be refreshed as well. If the special code to refresh the sidebar was found, it * + * flags the sidebar to be redrawn and then removes the code from the list. * + * * + * INPUT: cell -- The cell to base the refresh list on. * + * * + * list -- Pointer to the cell offset list that elaborates all the cells that * + * need to be flagged for redraw. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Refresh_Cells(CELL cell, short const * list) +{ + if (*list == REFRESH_SIDEBAR) { + IsToRedraw = true; + Column[0].IsToRedraw = true; + Column[1].IsToRedraw = true; + Flag_To_Redraw(false); + } + PowerClass::Refresh_Cells(cell, list); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Repair -- Controls the repair button on the sidebar. * + * * + * Use this routine to turn the repair sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure is friendly and damaged. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Repair(int control) +{ + bool old = IsRepairActive; + + if (control == -1) { + control = IsRepairActive ? 0 : 1; + } + switch (control) { + case 1: + IsRepairActive = true; + break; + + default: + case 0: + IsRepairActive = false; + break; + } + if (old != IsRepairActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + + if (!IsRepairActive) { + Help_Text(TXT_NONE); + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Upgrade -- Controls the upgrade button on the sidebar. * + * * + * Use this routine to turn the upgrade sidebar button on and off. Typically, the button * + * is enabled when the currently selected structure can be upgraded and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Upgrade(int control) +{ + bool old = IsUpgradeActive; + if (control == -1) { + control = IsUpgradeActive ? 0 : 1; + } + switch (control) { + case 1: + IsUpgradeActive = true; + break; + + default: + case 0: + IsUpgradeActive = false; + break; + } + if (old != IsUpgradeActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsUpgradeActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Activate_Demolish -- Controls the demolish button on the sidebar. * + * * + * Use this routine to turn the demolish/dismantle sidebar button on and off. Typically, * + * the button is enabled when a friendly building is selected and disabled otherwise. * + * * + * INPUT: control -- The controls how the button is to be activated or deactivated; * + * 0 -- Turn button off. * + * 1 -- Turn button on. * + * -1 -- Toggle button state. * + * * + * OUTPUT: bool; Was the button previously activated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate_Demolish(int control) +{ + bool old = IsDemolishActive; + + if (control == -1) { + control = IsDemolishActive ? 0 : 1; + } + switch (control) { + case 1: + IsDemolishActive = true; + break; + + default: + case 0: + IsDemolishActive = false; + break; + } + if (old != IsDemolishActive) { + Flag_To_Redraw(false); + IsToRedraw = true; + if (!IsDemolishActive) { + Set_Default_Mouse(MOUSE_NORMAL, false); + } + } + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::Add(RTTIType type, int id, bool via_capture) +{ + assert((unsigned)type < RTTI_COUNT); + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + int column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + Activate(1); + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Scroll -- Handles scrolling the sidebar object strip. * + * * + * This routine is used to scroll the sidebar strip of objects. The strip appears whenever * + * a building is selected that can produce units. If the number of units to produce is * + * greater than what the sidebar can hold, this routine is used to scroll the other object * + * into view so they can be selected. * + * * + * INPUT: up -- Should the scroll be upwards? Upward scrolling reveals object that are * + * later in the list of objects. * + * * + * OUTPUT: bool; Did scrolling occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Scroll(bool up, int column) +{ + if (column == -1) { + bool scr = false; + scr |= Column[0].Scroll(up); + scr |= Column[1].Scroll(up); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + if (scr) { + IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); + } + + if (Column[column].Scroll(up)) { + // No need to redraw the whole sidebar juts because we scrolled a strip is there? ST - 10/15/96 7:29PM + //IsToRedraw = true; + Flag_To_Redraw(false); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Draw_It -- Renders the sidebar display. * + * * + * This routine performs the actual drawing of the sidebar display. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the sidebar imagery changed at all? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 12/31/1994 JLB : Split rendering off into the sidebar strip class. * + *=============================================================================================*/ +void SidebarClass::Draw_It(bool complete) +{ + /* Draw black background */ + GL_FillRect(TBLACK, SIDE_X * RESFACTOR, 8 * RESFACTOR, SIDE_WIDTH * RESFACTOR, ScreenHeight - (8 * RESFACTOR)); + + PowerClass::Draw_It(complete); + + BStart(BENCH_SIDEBAR); + + if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { + // IsToRedraw = false; + + if (LogicPage->Lock()) { + /* + ** Draw the outline box around the sidebar buttons. + */ + int shape = complete ? 0 : 1; + + CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + + CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + + CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, ScreenHeight - (80 * RESFACTOR) + 3, WINDOW_MAIN, SHAPE_WIN_REL); + + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + + LogicPage->Unlock(); + } + } + + /* + ** Draw the side strip elements by calling their respective draw functions. + */ + if (IsSidebarActive) { + Column[0].Draw_It(complete); + Column[1].Draw_It(complete); + + if (complete || IsToRedraw) { + Repair.Draw_Me(true); + Upgrade.Draw_Me(true); + Zoom.Draw_Me(true); + } + } + + BEnd(BENCH_SIDEBAR); +} + + +/*********************************************************************************************** + * SidebarClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarClass::AI(KeyNumType & input, int x, int y) +{ + bool redraw = true; + + // + // We need to process the sidebar differently in multiplayer. ST - 8/7/2019 10:48AM + // + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + PowerClass::AI(input, x, y); + return; + } + + /* + ** Toggle the sidebar in and out with the key. + */ +#ifndef WIN32 + if (input == KN_TAB) { + Activate(-1); + } +#else + if (!Debug_Map) { + Activate(1); // Force the sidebar always on in Win95 mode + } +#endif //WIN32 + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } + +#ifdef NEVER + if (IsSidebarActive && !Debug_Map) { + + if (input == KN_DOWN) { + int scr = 0; + scr |= Column[0].Scroll(false); + scr |= Column[1].Scroll(false); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + if (input == KN_UP) { + int scr = 0; + scr |= Column[0].Scroll(true); + scr |= Column[1].Scroll(true); + if (!scr) { + Sound_Effect(VOC_SCOLD); + } + redraw |= scr; + input = KN_NONE; + } + } +#endif + + + if (IsSidebarActive) { + + /* + ** If there are any buildings in the payer's inventory, then allow the repair + ** option. + */ + if (PlayerPtr->BScan) { + Activate_Repair(true); + } else { + Activate_Repair(false); + } + + if (input == (BUTTON_REPAIR|KN_BUTTON)) { + Repair_Mode_Control(-1); + } + + if (input == (BUTTON_ZOOM|KN_BUTTON)) { + Zoom_Mode_Control(); + } + + if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + Sell_Mode_Control(-1); + } + + if (redraw) { + //IsToRedraw = true; + Column[0].Flag_To_Redraw(); + Column[1].Flag_To_Redraw(); + + Flag_To_Redraw(false); + } + } + + if ((!IsRepairMode) && Repair.IsOn) { + Repair.Turn_Off(); + } + + if ((!IsSellMode) && Upgrade.IsOn) { + Upgrade.Turn_Off(); + } + + PowerClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * SidebarClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Recalc(void) +{ + bool redraw = false; + + // Done elsewhere for new multiplayer. ST - 8/7/2019 10:49AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + return; + } + + redraw |= Column[0].Recalc(); + redraw |= Column[1].Recalc(); + + if (redraw) { + IsToRedraw = true; + Flag_To_Redraw(false); + } +} + + +/*********************************************************************************************** + * SidebarClass::Activate -- Controls the sidebar activation. * + * * + * Use this routine to turn the sidebar on or off. This routine handles updating the * + * necessary flags. * + * * + * INPUT: control -- Tells what to do with the sidebar according to the following: * + * 0 = Turn sidebar off. * + * 1 = Turn sidebar on. * + * -1= Toggle sidebar on or off. * + * * + * OUTPUT: bool; Was the sidebar already on? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Activate(int control) +{ + // + // We don't want the original sidebar to be visible. ST - 1/31/2019 11:28AM + // + //if (control < 100) { + // return IsSidebarActive; + //} + + bool old = IsSidebarActive; + + if (Session.Play) + return (old); + + /* + ** Determine the new state of the sidebar. + */ + switch (control) { + case -1: + IsSidebarActive = IsSidebarActive == false; + break; + + case 1: + IsSidebarActive = true; + break; + + default: + case 0: + IsSidebarActive = false; + break; + } + + /* + ** Only if there is a change in the state of the sidebar will anything + ** be done to change it. + */ + if (IsSidebarActive != old) { + + /* + ** If the sidebar is activated but was on the right side of the screen, then + ** activate it on the left side of the screen. + */ + if (IsSidebarActive /*&& X*/) { + Set_View_Dimensions( 0, 8 * RESFACTOR, SeenBuff.Get_Width() - ( SIDE_WIDTH * RESFACTOR ) ); + + IsToRedraw = true; + Help_Text(TXT_NONE); + Repair.Zap(); + Add_A_Button(Repair); + Upgrade.Zap(); + Add_A_Button(Upgrade); + Zoom.Zap(); + Add_A_Button(Zoom); + Column[0].Activate(); + Column[1].Activate(); + Background.Zap(); + Add_A_Button(Background); + Map.RadarButton.Zap(); + Add_A_Button(Map.RadarButton); + Map.PowerButton.Zap(); + Add_A_Button(Map.PowerButton); + } else { + Help_Text(TXT_NONE); + Set_View_Dimensions(0, 0); + Remove_A_Button(Repair); + Remove_A_Button(Upgrade); + Remove_A_Button(Zoom); + Remove_A_Button(Background); + Column[0].Deactivate(); + Column[1].Deactivate(); + Remove_A_Button(Map.RadarButton); + Remove_A_Button(Map.PowerButton); + } + + /* + ** Since the sidebar status has changed, update the map so that the graphics + ** will be rendered correctly. + */ + Flag_To_Redraw(true); + } + + return(old); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::StripClass(InitClass const & ) : + X(0), + Y(0), + ID(0), + IsToRedraw(true), + IsBuilding(false), + IsScrollingDown(false), + IsScrolling(false), + Flasher(-1), + TopIndex(0), + Scroller(0), + Slid(0), + BuildableCount(0) +{ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::One_Time -- Performs one time actions necessary for the side stri * + * * + * Call this routine ONCE at the beginning of the game. It handles retrieving pointers to * + * the shape files it needs for rendering. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::One_Time(int ) +{ + // Sidebar height + HEIGHT = ScreenHeight; + MaxButtonsVisible = ( HEIGHT - ( ( SIDE_Y + TOP_HEIGHT + OBJECT_HEIGHT ) * RESFACTOR ) ) / ( OBJECT_HEIGHT * RESFACTOR ); + + /* + ** Sidebar is player team specific in Hires + */ + ClockShapes = MFCD::Retrieve("CLOCK.SHP"); + + for (SpecialWeaponType lp = SPC_FIRST; lp < SPC_COUNT; lp++) { + char buffer[_MAX_FNAME]; + sprintf(buffer, "%sICON", SpecialWeaponFile[lp]); + + char fullname[_MAX_FNAME+_MAX_EXT]; + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + SpecialShapes[lp] = MFCD::Retrieve(fullname); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Get_Special_Cameo -- Fetches the special event cameo shape. * + * * + * This routine will return with a pointer to the cameo data for the special objects that * + * can appear on the sidebar (e.g., nuclear bomb). * + * * + * INPUT: type -- The special type to fetch the cameo imagery for. * + * * + * OUTPUT: Returns with a pointer to the cameo imagery for the specified special object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : commented * + *=============================================================================================*/ +void const * SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) +{ + if ((unsigned)type < SPC_COUNT) { + return(SpecialShapes[type]); + } + return(0); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Clear(void) +{ + IsScrollingDown = false; + IsScrolling = false; + IsBuilding = false; + Flasher = -1; + TopIndex = 0; + Slid = 0; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_IO -- Initializes the strip's buttons * + * * + * This routine doesn't actually add any buttons to the list. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_IO(int id) +{ + ID = id; + + UpButton[ID].IsSticky = true; + UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].X = X + (UP_X_OFFSET * RESFACTOR); + UpButton[ID].Y = HEIGHT - ( ( OBJECT_HEIGHT + 7 ) * RESFACTOR ); + UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); + + DownButton[ID].IsSticky = true; + DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].X = X + (DOWN_X_OFFSET * RESFACTOR); + DownButton[ID].Y = HEIGHT - ( (OBJECT_HEIGHT + 7 ) * RESFACTOR ); + DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); + + UpButton[ID].Y += 3; + DownButton[ID].Y += 3; + + for (int index = 0; index < MaxButtonsVisible; index++) { + SelectClass & g = SelectButton[ID][index]; + g.ID = BUTTON_SELECT; + g.X = X; + g.Y = Y + ((OBJECT_HEIGHT*index) * RESFACTOR); + g.Width = OBJECT_WIDTH * RESFACTOR; + g.Height = OBJECT_HEIGHT * RESFACTOR; + g.Set_Owner(*this, index); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Init_Theater -- Performs theater-specific initialization * + * * + * INPUT: theater * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Init_Theater(TheaterType theater) +{ + + Reload_LogoShapes(); + + if ( (theater != THEATER_NONE) && (theater != ::LastTheater)) { + + static TLucentType const ClockCols[1] = { + {GREEN, BLACK, 100, 0} +// {GREEN, LTGREY, 180, 0} + }; + + /* + ** Make sure that remapping doesn't occur on the colors that cycle. + */ + PaletteClass pal = OriginalPalette; + memset(&pal[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + Build_Translucent_Table(pal, &ClockCols[0], 1, (void*)ClockTranslucentTable); + +// Mem_Copy(GamePalette, OriginalPalette, 768); +// memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + + /* + ** Create the translucent table used for the sidebar. + */ +// Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); +// GamePalette = OriginalPalette; + + Conquer_Build_Fading_Table(GamePalette, &ClockTranslucentTable[256], BLACK, 100); + } +} + +void SidebarClass::StripClass::Reload_LogoShapes(void) +{ + /* + ** Load hi-res strip art here since it is player side specific + */ + static char * stripnames[]={ + "stripna.shp", //Nato + "stripna.shp", + "stripus.shp", //USSR + "stripna.shp", + "stripus.shp", //UKRAINE + "stripna.shp", + "stripna.shp", + "stripna.shp", + "stripna.shp", //HOUSE_GOOD + "stripus.shp", //HOUSE_BAD + }; + int houseloaded = 0; + + /* + ** Sidebar art is dependent on the side of the player + */ + + if(PlayerPtr) { + houseloaded = PlayerPtr->ActLike; + } +// jmarshall + if (houseloaded >= HOUSE_BAD) + return; +// jmarshall end + + LogoShapes = (void*)MFCD::Retrieve(stripnames[houseloaded]); +} + +/*********************************************************************************************** + * SidebarClass::StripClass::Activate -- Adds the strip buttons to the input system. * + * * + * This routine will add the side strip buttons to the map's input system. This routine * + * should be called once when the sidebar activates. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine a second time without first calling Deactivate(). * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Activate(void) +{ + UpButton[ID].Zap(); + Map.Add_A_Button(UpButton[ID]); + + DownButton[ID].Zap(); + Map.Add_A_Button(DownButton[ID]); + + for (int index = 0; index < MaxButtonsVisible; index++) { + SelectButton[ID][index].Zap(); + Map.Add_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Deactivate -- Removes the side strip buttons from the input syste * + * * + * Call this routine to remove all the buttons on the side strip from the map's input * + * system. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Never call this routine unless the Activate() function was previously called. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Deactivate(void) +{ + Map.Remove_A_Button(UpButton[ID]); + Map.Remove_A_Button(DownButton[ID]); + for (int index = 0; index < MaxButtonsVisible; index++) { + Map.Remove_A_Button(SelectButton[ID][index]); + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 9/24/2019 3:17PM : Added via capture parameter for new sidebar functionality * + *=============================================================================================*/ +bool SidebarClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + IsToRedraw = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Scroll -- Causes the side strip to scroll. * + * * + * Use this routine to flag the side strip to scroll. The direction scrolled is controlled * + * by the parameter. Scrolling is merely initiated by this routine. Subsequent calls to * + * the AI function and the Draw_It function are required to properly give the appearance * + * of scrolling. * + * * + * INPUT: bool; Should the side strip scroll UP? If it is to scroll down then pass false. * + * * + * OUTPUT: bool; Was the side strip started to scroll in the desired direction? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 07/29/1995 JLB : Simplified scrolling logic. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Scroll(bool up) +{ + if (up) { + if (!TopIndex) return(false); + Scroller--; + } else { + if ( TopIndex+ MaxButtonsVisible >= BuildableCount ) return(false); + Scroller++; + } + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Flag_To_Redra -- Flags the sidebar strip to be redrawn. * + * * + * This utility routine is called when something changes on the sidebar and it must be * + * reflected the next time drawing is performed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::Flag_To_Redraw(void) +{ + IsToRedraw = true; + //Map.SidebarClass::IsToRedraw = true; + Map.Flag_To_Redraw(false); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +{ + bool redraw = false; + + /* + ** If this is scroll button for this side strip, then scroll the strip as + ** indicated. + */ + if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + UpButton[ID].IsPressed = false; + if (!Scroll(true)) { + Sound_Effect(VOC_SCOLD); + } + } + if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + DownButton[ID].IsPressed = false; + if (!Scroll(false)) { + Sound_Effect(VOC_SCOLD); + } + } + + /* + ** Reflect the scroll desired direction/value into the scroll + ** logic handler. This might result in up or down scrolling. + */ + if (!IsScrolling && Scroller) { + if (BuildableCount <= MaxButtonsVisible ) { + Scroller = 0; + } else { + + /* + ** Top of list is moving toward lower ordered entries in the object list. It looks like + ** the "window" to the object list is moving up even though the actual object images are + ** scrolling downward. + */ + if (Scroller < 0) { + if (!TopIndex) { + Scroller = 0; + } else { + Scroller++; + IsScrollingDown = false; + IsScrolling = true; + TopIndex--; + Slid = 0; + } + + } else { + if (TopIndex + MaxButtonsVisible >= BuildableCount) { + Scroller = 0; + } else { + Scroller--; + Slid = OBJECT_HEIGHT; + IsScrollingDown = true; + IsScrolling = true; + } + } + } + } + + /* + ** Scroll logic is handled here. + */ + if (IsScrolling) { + if (IsScrollingDown) { + Slid -= SCROLL_RATE; + if (Slid <= 0) { + IsScrolling = false; + Slid = 0; + TopIndex++; + } + } else { + Slid += SCROLL_RATE; + if (Slid >= OBJECT_HEIGHT) { + IsScrolling = false; + Slid = 0; + } + } + redraw = true; + } + + /* + ** Handle any flashing logic. Flashing occurs when the player selects an object + ** and provides the visual feedback of a recognized and legal selection. + */ + if (Flasher != -1) { + if (Graphic_Logic()) { + redraw = true; + if (Fetch_Stage() >= 7) { + Set_Rate(0); + Set_Stage(0); + Flasher = -1; + } + } + } + + /* + ** Handle any building clock animation logic. + */ + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + redraw = true; + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending != NULL) { + switch (pending->What_Am_I()) { + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + + case RTTI_BUILDING: + Speak(VOX_CONSTRUCTION); + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + /* + ** If any of the logic determined that this side strip needs to be redrawn, then + ** set the redraw flag for this side strip. + */ + if (redraw) { + Flag_To_Redraw(); + } + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Draw_It -- Render the sidebar display. * + * * + * Use this routine to render the sidebar display. It checks to see if it needs to be * + * redrawn and only redraw if necessary. If the "complete" parameter is true, then it * + * will force redraw the entire strip. * + * * + * INPUT: complete -- Should the redraw be forced? A force redraw will ignore the redraw * + * flag. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 08/06/1995 JLB : Handles multi factory tracking in same strip. * + *=============================================================================================*/ +void SidebarClass::StripClass::Draw_It(bool complete) +{ + if (IsToRedraw || complete) { + // IsToRedraw = false; + + if (RunningAsDLL) { + return; + } + + SidebarRedraws++; + + /* + ** Fills the background to the side strip. We shouldnt need to do this if the strip + ** has a full complement of icons. + */ + /* + ** New sidebar needs to be drawn not filled + */ + if ( BuildableCount < MaxButtonsVisible ) { + CC_Draw_Shape( LogoShapes, ID, X+(2*RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + } + + /* + ** Redraw the scroll buttons. + */ + UpButton[ID].Draw_Me(true); + DownButton[ID].Draw_Me(true); + + /* + ** Loop through all the buildable objects that are visible in the strip and render + ** them. Their Y offset may be adjusted if the strip is in the process of scrolling. + */ + for (int i = 0; i < MaxButtonsVisible + (IsScrolling ? 1 : 0); i++) { + bool production; + bool completed; + int stage; + bool darken = false; + void const * shapefile = 0; + int shapenum = 0; + void const * remapper = 0; + FactoryClass * factory = 0; + int index = i+TopIndex; + int x = X; + int y = Y + (i*OBJECT_HEIGHT * RESFACTOR); + + /* + ** If the strip is scrolling, then the offset is adjusted accordingly. + */ + if (IsScrolling) { + y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; +// y -= OBJECT_HEIGHT - Slid; + } + + /* + ** Fetch the shape number for the object type located at this current working + ** slot. This shape pointer is used to draw the underlying graphic there. + */ + if (index < BuildableCount) { + ObjectTypeClass const * obj = NULL; + SpecialWeaponType spc = SPC_NONE; + + if (Buildables[index].BuildableType != RTTI_SPECIAL) { + + obj = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (obj != NULL) { + + /* + ** Fetch the remap table that is appropriate for this object + ** type. + */ + remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const *)obj)->Remap); + + /* + ** If there is already a factory producing this kind of object, then all + ** objects of this type are displays in a disabled state. + */ + bool isbusy = (PlayerPtr->Fetch_Factory(Buildables[index].BuildableType) != NULL); + if (!isbusy && PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID)) { + isbusy = true; + } + + /* + ** Infantry don't get remapped in the sidebar (special case). + */ + if (Buildables[index].BuildableType == RTTI_INFANTRYTYPE) { + remapper = 0; + } + + shapefile = obj->Get_Cameo_Data(); + shapenum = 0; + if (Buildables[index].Factory != -1) { + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } else { + production = false; +// darken = IsBuilding; + + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ + darken = isbusy; + } + } else { + darken = PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID); + } + + } else { + + spc = SpecialWeaponType(Buildables[index].BuildableID); + shapefile = Get_Special_Cameo(spc); + shapenum = 0; + + production = true; + completed = PlayerPtr->SuperWeapon[spc].Is_Ready(); + stage = PlayerPtr->SuperWeapon[spc].Anim_Stage(); + darken = false; + } + + if (obj != NULL || spc != SPC_NONE) { + /* + ** If this item is flashing then take care of it. + ** + */ + if (Flasher == index && (Fetch_Stage() & 0x01)) { + remapper = Map.FadingLight; + } + + } else { + shapefile = LogoShapes; + if (!darken) { + shapenum = SB_BLANK; + } + } + } else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; + } + + remapper = 0; + + /* + ** Now that the shape of the object at the current working slot has been found, + ** draw it and any graphic overlays as necessary. + ** + ** Don't draw blank shapes over the new 640x400 sidebar art - ST 5/1/96 6:01PM + */ + if (shapenum != SB_BLANK || shapefile != LogoShapes) { + CC_Draw_Shape(shapefile, shapenum, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + remapper); + + /* + ** Darken this object because it cannot be produced or is otherwise + ** unavailable. + */ + if (darken) { + CC_Draw_Shape(ClockShapes, 0, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + } + } + + /* + ** Draw the overlapping clock shape if this is object is being constructed. + ** If the object is completed, then display "Ready" with no clock shape. + */ + if (production) { + if (completed) { + + /* + ** Display text showing that the object is ready to place. + */ + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]))+(LEFT_EDGE_OFFSET+15) * RESFACTOR, + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } else { + + CC_Draw_Shape(ClockShapes, stage+1, + x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), + y-WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, ClockTranslucentTable); + + /* + ** Display text showing that the construction is temporarily on hold. + */ + if (factory && !factory->Is_Building()) { + CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, + (x-(WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET+15) * RESFACTOR), + (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } + } + } + + } + + LastSlid = Slid; + } +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + bool redraw = false; + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech != NULL) { + ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; + } else { + if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { + ok = PlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); + } else { + ok = false; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + TopIndex = 0; + IsToRedraw = true; + redraw = true; + BuildableCount--; + index--; + } + } + +#ifdef NEVER + /* + ** If there are no more buildable objects to display, make the sidebar go away. + */ + if (!BuildableCount) { + Map.SidebarClass::Activate(0); + } +#endif + return(redraw); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass::SelectClass -- Default constructor. * + * * + * This is the default constructor for the button that controls the buildable cameos on * + * the sidebar strip. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The coordinates are set to zero by this routine. They must be set to the * + * correct values before this button will function. * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +SidebarClass::StripClass::SelectClass::SelectClass(void) : + ControlClass(0, 0, 0, (OBJECT_WIDTH-1) * RESFACTOR, OBJECT_HEIGHT * RESFACTOR, LEFTPRESS|RIGHTPRESS|LEFTUP), + Strip(0), + Index(0) +{ +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Assigns special values to a buildable select but * + * * + * Use this routine to set custom buildable vars for this particular select button. It * + * uses this information to properly know what buildable object to start or stop production * + * on. * + * * + * INPUT: strip -- Reference to the strip that owns this buildable button. * + * * + * index -- The index (0 .. MAX_VISIBLE-1) of this button. This is used to let * + * the owning strip know what index this button refers to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +{ + Strip = &strip; + Index = index; + X = strip.X; + Y = strip.Y + ((index * OBJECT_HEIGHT) * RESFACTOR); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::SelectClass:: -- Action function when buildable cameo is selected * + * * + * This function is called when the buildable icon (cameo) is clicked on. It handles * + * starting and stopping production as indicated. * + * * + * INPUT: flags -- The input event that triggered the call. * + * * + * key -- The keyboard value at the time of the input. * + * * + * OUTPUT: Returns with whether the input list should be scanned further. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 10/09/1996 JLB : Sonar pulse converted to regular event type. * + *=============================================================================================*/ +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +{ + if (Strip == NULL) return 0; + + int index = Strip->TopIndex + Index; + RTTIType otype = Strip->Buildables[index].BuildableType; + int oid = Strip->Buildables[index].BuildableID; + int fnumber = Strip->Buildables[index].Factory; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + ObjectTypeClass const * choice = NULL; + SpecialWeaponType spc = SPC_NONE; + + /* + ** Determine the factory number that would apply to objects of the type + ** the mouse is currently addressing. This doesn't mean that the factory number + ** fetched is actually producing the indicated object, merely that that particular + ** kind of factory is specified by the "genfactory" value. This can be used to see + ** if the factory type is currently busy or not. + */ + FactoryClass * factory = PlayerPtr->Fetch_Factory(otype); + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + + if (index < Strip->BuildableCount) { + if (otype != RTTI_SPECIAL) { + choice = Fetch_Techno_Type(otype, oid); + } else { + spc = SpecialWeaponType(oid); + } + + if (fnumber != -1) { + factory = Factories.Raw_Ptr(fnumber); + } + + } else { + Map.Help_Text(TXT_NONE); + } + + if (spc != SPC_NONE) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(SpecialWeaponHelp[spc], X, Y, scheme->Color, true); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". If we are in targeting + ** mode then we don't want to be any more. + */ + if (flags & RIGHTPRESS) { + Map.IsTargettingMode = SPC_NONE; + } + /* + ** A left mouse press signal "activate". If our weapon type is + ** available then we should activate it. + */ + if (flags & LEFTPRESS) { + + if ((unsigned)spc < SPC_COUNT) { + if (PlayerPtr->SuperWeapon[spc].Is_Ready()) { + if (spc != SPC_SONAR_PULSE) { + Map.IsTargettingMode = spc; + Unselect_All(); + Speak(VOX_SELECT_TARGET); + } else { + OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SONAR_PULSE, 0)); + } + } else { + PlayerPtr->SuperWeapon[spc].Impatient_Click(); + } + } + } + + } else { + + if (choice != NULL) { + + /* + ** Display the help text if the mouse is over the button. + */ + if (flags & LEFTUP) { + Map.Help_Text(choice->Full_Name(), X, Y, scheme->Color, true); + Map.Set_Cost(choice->Cost_Of() * PlayerPtr->CostBias); + flags &= ~LEFTUP; + } + + /* + ** A right mouse button signals "cancel". + */ + if (flags & RIGHTPRESS) { + + /* + ** If production is in progress, put it on hold. If production is already + ** on hold, then abandon it. Money will be refunded, the factory + ** manager deleted, and the object under construction is returned to + ** the free pool. + */ + if (factory != NULL) { + + /* + ** Cancels placement mode if the sidebar factory is abandoned or + ** suspended. + */ + if (Map.PendingObjectPtr && Map.PendingObjectPtr->Is_Techno()) { + Map.PendingObjectPtr = 0; + Map.PendingObject = 0; + Map.PendingHouse = HOUSE_NONE; + Map.Set_Cursor_Shape(0); + } + + if (!factory->Is_Building()) { + Speak(VOX_CANCELED); + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + } else { + Speak(VOX_SUSPENDED); + OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); + Map.Column[0].IsToRedraw = true; + Map.Column[1].IsToRedraw = true; + } + } + } + + if (flags & LEFTPRESS) { + + /* + ** If there is already a factory attached to this strip but the player didn't click + ** on the icon that has the attached factory, then say that the factory is busy and + ** ignore the click. + */ + if (fnumber == -1 && factory != NULL) { + Speak(VOX_NO_FACTORY); + ControlClass::Action(flags, key); + return(true); + } + + if (factory != NULL) { + + /* + ** If this object is currently being built, then give a scold sound and text and then + ** bail. + */ + if (factory->Is_Building()) { + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If production has completed, then attempt to have the object exit + ** the factory or go into placement mode. + */ + if (factory->Has_Completed()) { + + TechnoClass * pending = factory->Get_Object(); + if (!pending && factory->Get_Special_Item()) { + Map.IsTargettingMode = SPC_ANY; + } else { + BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + if (!builder) { + OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); + Speak(VOX_NO_FACTORY); + } else { + + /* + ** If the completed object is a building, then change the + ** game state into building placement mode. This fact is + ** not transmitted to any linked computers until the moment + ** the building is actually placed down. + */ + if (pending->What_Am_I() == RTTI_BUILDING) { + PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); + } else { + + /* + ** For objects that can leave the factory under their own + ** power, queue this event and process through normal house + ** production channels. + */ + OutList.Add(EventClass(EventClass::PLACE, otype, -1)); + } + } + } + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + /* + ** The factory must have been in a suspended state. Resume construction + ** normally. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + + } else { + + if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { + // Eva scolds the player here. + } else { + + /* + ** If this side strip is already busy with production, then ignore the + ** input and announce this fact. + */ + if (otype == RTTI_INFANTRYTYPE) { + Speak(VOX_TRAINING); + } else { + Speak(VOX_BUILDING); + } + OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); + } + } + } + } else { + flags = 0; + } + } + + ControlClass::Action(flags, key); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::SBGadgetClass::Action -- Special function that controls the mouse over the si * + * * + * This routine is called whenever the mouse is over the sidebar. It makes sure that the * + * mouse is always the normal shape while over the sidebar. * + * * + * INPUT: flags -- The event flags that resulted in this routine being called. * + * * + * key -- Reference the keyboard code that may be present. * + * * + * OUTPUT: Returns that no further keyboard processing is necessary. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/28/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +{ + Map.Help_Text(TXT_NONE); + Map.Override_Mouse_Shape(MOUSE_NORMAL, false); + return(true); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + /* + ** Flag that all the icons on this strip need to be redrawn + */ + Flag_To_Redraw(); + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there was a change to the strip, then flag the strip to be redrawn. + */ + if (abandon) { + Flag_To_Redraw(); + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + + +/*********************************************************************************************** + * SidebarClass::Zoom_Mode_Control -- Handles the zoom mode toggle operation. * + * * + * This is the function that is called when the map button is pressed. It will toggle * + * between the different modes that the radar map can assume. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +void SidebarClass::Zoom_Mode_Control(void) +{ +#ifdef WIN32 + /* + ** If radar is active, cycle as follows: + ** Zoomed => not zoomed + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => zoomed + */ + if (IsRadarActive) { + if (Is_Zoomed() || Session.Type==GAME_NORMAL) { + if (Is_Zoomed() || !Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#else + /* + ** If radar is active, cycle as follows: + ** not zoomed => player status (multiplayer only) + ** player status => radar spying readout + ** radar spying readout => not zoomed + */ + if (IsRadarActive) { + if (Session.Type==GAME_NORMAL) { + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } else { + if (!Spying_On_House() && !Is_Player_Names()) { + Player_Names(1); + } else { + Player_Names(0); + if (!Spy_Next_House()) { + Zoom_Mode(Coord_Cell(TacticalCoord)); + } + } + } + } else { + if (Session.Type!=GAME_NORMAL) { + Player_Names(Is_Player_Names()==0); + } + } +#endif +} \ No newline at end of file diff --git a/code/redalert/sidebar.h b/code/redalert/sidebar.h new file mode 100644 index 0000000..ce75364 --- /dev/null +++ b/code/redalert/sidebar.h @@ -0,0 +1,398 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SIDEBAR.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_H +#define SIDEBAR_H + +#include "function.h" +#include "power.h" +#include "factory.h" + +class InitClass {}; + +extern int ScreenWidth; +#define BUTTON_ACTIVATOR 100 // Button ID for the activator. +#define SIDE_X ((ScreenWidth / 2) - 80) // The X position of sidebar upper left corner. +#define SIDE_Y (7 + 70) // The Y position of sidebar upper left corner. +#define SIDE_WIDTH SIDEBAR_WID // Width of the entire sidebar (in pixels). +#define SIDE_HEIGHT (200 - (7 + 70) ) // Height of the entire sidebar (in pixels). +#define TOP_HEIGHT 13 // Height of top section (with repair/sell buttons). +#define COLUMN_ONE_X (int)((SIDE_X) + 8) // Sidestrip upper left coordinates... +#define COLUMN_ONE_Y (int)(SIDE_Y + (TOP_HEIGHT)) +#define COLUMN_TWO_X (int)((SIDE_X) + 8 + ((80 - 16) / 2) + 3) +#define COLUMN_TWO_Y (int)(SIDE_Y + (TOP_HEIGHT)) +#define BUTTON_ONE_WIDTH 32 // Button width. +#define BUTTON_TWO_WIDTH 20 // Button width. +#define BUTTON_THREE_WIDTH 20 // Button width. +#define BUTTON_HEIGHT 9 // Button height. +#define BUTTON_ONE_X (int)(SIDE_X + 2) // Left button X coordinate. +#define BUTTON_ONE_Y (int)(SIDE_Y + 2) // Left button Y coordinate. +#define BUTTON_TWO_X (int)(SIDE_X + 36) // Right button X coordinate. +#define BUTTON_TWO_Y (int)(SIDE_Y + 2) // Right button Y coordinate. +#define BUTTON_THREE_X (int)(SIDE_X + 58) // Right button X coordinate. +#define BUTTON_THREE_Y (int)(SIDE_Y + 2) // Right button Y coordinate. + +class SidebarClass: public PowerClass +{ + public: + /* + ** These constants are used to control the sidebar rendering. They are instantiated + ** as enumerations since C++ cannot use "const" in this context. + */ + enum SideBarClassEnums { + + +//BGA: changes to all buttons +#ifdef GERMAN + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef FRENCH + BUTTON_ONE_WIDTH=20, // Button width. + BUTTON_TWO_WIDTH=27, // Button width. + BUTTON_THREE_WIDTH=26, // Button width. + BUTTON_HEIGHT=9, // Button height. + BUTTON_ONE_X=SIDE_X+2, // Left button X coordinate. + BUTTON_ONE_Y=SIDE_Y+2, // Left button Y coordinate. + BUTTON_TWO_X=SIDE_X+24, // Right button X coordinate. + BUTTON_TWO_Y=SIDE_Y+2, // Right button Y coordinate. + BUTTON_THREE_X=SIDE_X+53, // Right button X coordinate. + BUTTON_THREE_Y=SIDE_Y+2, // Right button Y coordinate. +#endif + +#ifdef ENGLISH + +#endif + + COLUMNS=2 // Number of side strips on sidebar. + }; + + static void * SidebarShape; + static void * SidebarMiddleShape; //Only used in Win95 version + static void * SidebarBottomShape; //Only used in Win95 version + + SidebarClass(void); + SidebarClass(NoInitClass const & x); + + /* + ** Initialization + */ + virtual void One_Time(void); // One-time inits + virtual void Init_Clear(void); // Clears all to known state + virtual void Init_IO(void); // Inits button list + virtual void Init_Theater(TheaterType theater); // Theater-specific inits + void Reload_Sidebar(void); // Loads house-specific sidebar art + + virtual void AI(KeyNumType & input, int x, int y); + virtual void Draw_It(bool complete); + virtual void Refresh_Cells(CELL cell, short const *list); + + void Zoom_Mode_Control(void); + bool Abandon_Production(RTTIType type, int factory); + bool Activate(int control); + bool Add(RTTIType type, int ID, bool via_capture = false); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM ); + bool Sidebar_Click(KeyNumType & input, int x, int y); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + SelectClass(NoInitClass const & x) : ControlClass(x) {}; + + void Set_Owner(StripClass & strip, int index); + + StripClass * Strip; + int Index; + + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + public: + StripClass(void) {} + StripClass(InitClass const &); + StripClass(NoInitClass const & ) {}; + + bool Add(RTTIType type, int ID, bool via_capture); // Added via_capture for new sidebar functionality. ST - 9/24/2019 3:15PM ); + bool Abandon_Production(int factory); + bool Scroll(bool up); + bool AI(KeyNumType & input, int x, int y); + void Draw_It(bool complete); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + void Reload_LogoShapes(void); + bool Recalc(void); + void Activate(void); + void Deactivate(void); + void Flag_To_Redraw(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(SpecialWeaponType type); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + BUTTON_UP=200, + BUTTON_DOWN=210, + BUTTON_SELECT=220, + MAX_BUILDABLES=75, // Maximum number of object types in sidebar. + OBJECT_HEIGHT=24, // Pixel height of each buildable object. + OBJECT_WIDTH=32, // Pixel width of each buildable object. + STRIP_WIDTH=35, // Width of strip (not counting border lines). + MAX_VISIBLE=24, // Number of object slots visible at any one time (theoretical, actual is calculated later) + SCROLL_RATE=12, // The pixel jump while scrolling (larger is faster). + + UP_X_OFFSET=2, // Scroll up arrow coordinates. + //UP_Y_OFFSET=int(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, + DOWN_X_OFFSET=18, // Scroll down arrow coordinates. + //DOWN_Y_OFFSET=UP_Y_OFFSET, // BGint(MAX_VISIBLE)*int(OBJECT_HEIGHT)+1, + + SBUTTON_WIDTH=16, // Width of the mini-scroll button. + SBUTTON_HEIGHT=12, // Height of the mini-scroll button. + + LEFT_EDGE_OFFSET=2, // Offset from left edge for building shapes. + TEXT_X_OFFSET=18, // X offset to print "ready" text. + TEXT_Y_OFFSET=15, // Y offset to print "ready" text. + TEXT_COLOR=255 // Color to use for the "Ready" text. + }; + + int MaxButtonsVisible; + + /* + ** This is the coordinate of the upper left corner that this side strip + ** uses for rendering. + */ + int X,Y,HEIGHT; + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** Shape numbers for the shapes in the STRIP.SHP file. + */ + enum SideBarStipShapeEnums { + SB_BLANK, // The blank rectangle to use if there are no objects present. + SB_FRAME + }; + + /* + ** If this particular side strip needs to be redrawn, then this flag + ** will be true. + */ + unsigned IsToRedraw:1; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This controls the sidebar slide direction. If this is true, then the sidebar + ** will scroll downward -- revealing previous objects. + */ + unsigned IsScrollingDown:1; + + /* + ** If the sidebar is scrolling, then this flag is true. Otherwise it is false. + */ + unsigned IsScrolling:1; + + /* + ** This is the object (sidebar slot) that is flashing. Only one slot can be flashing + ** at any one instant. This is usually the result of a click on the slot and construction + ** has commenced. + */ + int Flasher; + + /* + ** As the sidebar scrolls up and down, this variable holds the index for the topmost + ** visible sidebar slot. + */ + int TopIndex; + + /* + ** This is the queued scroll direction and amount. The sidebar + ** will scroll the number of slots indicated by this value. This + ** value is set according to the scroll buttons. + */ + int Scroller; + + /* + ** The sidebar has smooth scrolling. This is the number of pixels the sidebar + ** has slide down. Thus, if this value were 5, then there would be 5 pixels of + ** the TopIndex-1 sidebar object visible. When the Slid value reaches 24, then + ** the value resets to zero and the TopIndex is decremented. For sliding in the + ** opposite direction, change the IsScrollingDown flag. + */ + int Slid; + + /* + ** The value of Slid the last time we rendered the sidebar. + */ + int LastSlid; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + /* + ** Pointer to the shape data for small versions of the logos. These are used as + ** placeholder pieces on the side bar. + */ + static void * LogoShapes; + + /* + ** This points to the animation sequence of frames used to mark the passage of time + ** as an object is undergoing construction. + */ + static void const * ClockShapes; + + /* + ** This points to the animation sequence which deals with special + ** shapes which handle non-production based icons. + */ + static void const * SpecialShapes[SPC_COUNT]; + + /* + ** This is the last theater that the special palette remap table was loaded + ** for. If the current theater differs from this recorded value, then the + ** remap tables are reloaded. + */ +// static TheaterType LastTheater; + + static ShapeButtonClass UpButton[COLUMNS]; + static ShapeButtonClass DownButton[COLUMNS]; + static SelectClass SelectButton[COLUMNS][MAX_VISIBLE]; + + /* + ** This points to the shapes that are used for the clock overlay. This displays + ** progress of construction. + */ + static char ClockTranslucentTable[(1+1)*256]; + + } Column[COLUMNS]; + + + /* + ** If the sidebar is active then this flag is true. + */ + unsigned IsSidebarActive:1; + + /* + ** This flag tells the rendering system that the sidebar needs to be redrawn. + */ + unsigned IsToRedraw:1; + + class SBGadgetClass: public GadgetClass { + public: +//#ifdef WIN32 + SBGadgetClass(void) : GadgetClass((int)((int)SIDE_X+8)*RESFACTOR, (int)SIDE_Y*RESFACTOR, (int)((int)SIDE_WIDTH-1)*RESFACTOR-1, (int)((int)SIDE_HEIGHT-1)*RESFACTOR, LEFTUP) {}; +//#else +// SBGadgetClass(void) : GadgetClass((int)SIDE_X+8, (int)SIDE_Y, (int)SIDE_WIDTH-1, (int)SIDE_HEIGHT-1, LEFTUP) {}; +//#endif + protected: + virtual int Action(unsigned flags, KeyNumType & key); + }; + + /* + ** This is the button that is used to collapse and expand the sidebar. + ** These buttons must be available to derived classes, for Save/Load. + */ + static ShapeButtonClass Repair; + static ShapeButtonClass Upgrade; + static ShapeButtonClass Zoom; + static SBGadgetClass Background; + + bool Scroll(bool up, int column); + + private: + bool Activate_Repair(int control); + bool Activate_Upgrade(int control); + bool Activate_Demolish(int control); + int Which_Column(RTTIType type); + + unsigned IsRepairActive:1; + unsigned IsUpgradeActive:1; + unsigned IsDemolishActive:1; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/sidebarglyphx.cpp b/code/redalert/sidebarglyphx.cpp new file mode 100644 index 0000000..1889862 --- /dev/null +++ b/code/redalert/sidebarglyphx.cpp @@ -0,0 +1,821 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.cpv 2.13 02 Aug 1995 17:03:22 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBARGlyphx.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 14th, 2019 * + * * + * Last Update : March 14th, 2019 * + * * + *---------------------------------------------------------------------------------------------* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "SidebarGlyphx.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + +/*********************************************************************************************** + * SidebarGlyphxClass::SidebarGlyphxClass -- Default constructor for the sidebar. * + * * + * Constructor for the sidebar handler. It basically sets up the sidebar to the empty * + * condition. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::SidebarGlyphxClass(void) +{ + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_Clear -- Sets sidebar to a known (and deactivated) state * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_Clear(HouseClass *player_ptr) +{ + SidebarPlayerPtr = player_ptr; + + //IsRepairActive = false; + //IsUpgradeActive = false; + //IsDemolishActive = false; + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + Column[0].Init_Clear(); + Column[1].Init_Clear(); + + + //Activate(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Init_IO -- Adds buttons to the button list * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Init_IO(void) +{ + /* + ** If a game was loaded & the sidebar was enabled, pop it up now + */ + //if (IsSidebarActive) { + // IsSidebarActive = false; + // Activate(1); + //} +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Which_Column -- Determines which column a given type should appear. * + * * + * Use this function to resolve what column the specified object type should be placed * + * into. * + * * + * INPUT: otype -- Pointer to the object type class of the object in question. * + * * + * OUTPUT: Returns with the column number that the object should be placed in. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/01/1995 JLB : Created. * + *=============================================================================================*/ +int SidebarGlyphxClass::Which_Column(RTTIType type) +{ + if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING) { + return(0); + } + return(1); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Factory_Link -- Links a factory to a sidebar strip. * + * * + * This routine will link the specified factory to the sidebar strip. A factory must be * + * linked to the sidebar so that as the factory production progresses, the sidebar will * + * show the production progress. * + * * + * INPUT: factory -- The factory number to attach. * + * * + * type -- The object type number. * + * * + * id -- The object sub-type number. * + * * + * OUTPUT: Was the factory successfully attached to the sidebar strip? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/19/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Factory_Link(int factory, RTTIType type, int id) +{ + return(Column[Which_Column(type)].Factory_Link(factory, type, id)); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::Add -- Adds a game object to the sidebar list. * + * * + * This routine is used to add a game object to the sidebar. Call this routine when a * + * factory type building is created. It handles the case of adding an item that has already * + * been added -- it just ignores it. * + * * + * INPUT: object -- Pointer to the object that is being added. * + * * + * OUTPUT: bool; Was the object added to the sidebar? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/17/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Add(RTTIType type, int id, bool via_capture) +{ + int column; + + /* + ** Add the sidebar only if we're not in editor mode. + */ + if (!Debug_Map) { + column = Which_Column(type); + + if (Column[column].Add(type, id, via_capture)) { + //Activate(1); + return(true); + } + return(false); + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::AI -- Handles player clicking on sidebar area. * + * * + * This routine handles the processing necessary when the player clicks on the sidebar. * + * Typically, this is selection of the item to build. * + * * + * INPUT: input -- Reference to the keyboard input value. * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: bool; Was the click handled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/28/94 JLB : Created. * + * 11/11/1994 JLB : Processes input directly. * + * 12/26/1994 JLB : Uses factory manager class for construction handling. * + * 12/31/1994 JLB : Simplified to use the sidebar strip class handlers. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 06/27/1995 JLB : key toggles sidebar. * + *=============================================================================================*/ +void SidebarGlyphxClass::AI(KeyNumType & input, int x, int y) +{ + if (!Debug_Map) { + Column[0].AI(input, x, y); + Column[1].AI(input, x, y); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Recalc -- Examines the sidebar data and updates it as necessary. * + * * + * Occasionally a factory gets destroyed. This routine must be called in such a case * + * because it might be possible that sidebar object need to be removed. This routine will * + * examine all existing objects in the sidebar class and if no possible factory can * + * produce it, then it will be removed. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is exhaustive and thus time consuming. Only call it when really * + * necessary. Such as when a factory is destroyed rather than when a non-factory * + * is destroyed. * + * * + * HISTORY: * + * 11/30/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Recalc(void) +{ + Column[0].Recalc(); + Column[1].Recalc(); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::StripClass -- Default constructor for the side strip class. * + * * + * This constructor is used to reset the side strip to default empty state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +SidebarGlyphxClass::StripClass::StripClass(void) +{ + IsBuilding = false; + BuildableCount = 0; + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; + } + ParentSidebar = NULL; +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Init_Clear -- Sets sidebar to a known (and deactivated) state* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/24/1994 JLB : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::StripClass::Init_Clear(void) +{ + IsBuilding = false; + BuildableCount = 0; + + /* + ** Since we're resetting the strips, clear out all the buildables & factory pointers. + */ + for (int index = 0; index < MAX_BUILDABLES; index++) { + Buildables[index].BuildableID = 0; + Buildables[index].BuildableType = RTTI_NONE; + Buildables[index].Factory = -1; + Buildables[index].BuildableViaCapture = false; + } +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Add -- Add an object to the side strip. * + * * + * Use this routine to add a buildable object to the side strip. * + * * + * INPUT: object -- Pointer to the object type that can be built and is to be added to * + * the side strip. * + * * + * OUTPUT: bool; Was the object successfully added to the side strip? Failure could be the * + * result of running out of room in the side strip array or the object might * + * already be in the list. * + * * + * WARNINGS: none. * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Add(RTTIType type, int id, bool via_capture) +{ + if (BuildableCount <= MAX_BUILDABLES) { + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + return(false); + } + } + if (!ScenarioInit && type != RTTI_SPECIAL) { + Speak(VOX_NEW_CONSTRUCT); + } + Buildables[BuildableCount].BuildableType = type; + Buildables[BuildableCount].BuildableID = id; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = via_capture; + BuildableCount++; + return(true); + } + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::AI -- Input and AI processing for the side strip. * + * * + * The side strip AI processing is performed by this function. This function not only * + * checks for player input, but also handles any graphic logic updating necessary as a * + * result of flashing or construction animation. * + * * + * INPUT: input -- The player input code. * + * * + * x,y -- Mouse coordinate to use. * + * * + * OUTPUT: bool; Did the AI detect that it will need a rendering change? If this routine * + * returns true, then the Draw_It function should be called at the * + * earliest opportunity. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/31/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::AI(KeyNumType & input, int , int ) +{ + /* + ** This is needed as it's where units get queued for structure exit. ST -3/14/2019 12:03PM + */ + + + if (IsBuilding) { + for (int index = 0; index < BuildableCount; index++) { + int factoryid = Buildables[index].Factory; + + if (factoryid != -1) { + FactoryClass * factory = Factories.Raw_Ptr(factoryid); + + if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { + + if (factory->Has_Completed()) { + + /* + ** Construction has been completed. Announce this fact to the player and + ** try to get the object to automatically leave the factory. Buildings are + ** the main exception to the ability to leave the factory under their own + ** power. + */ + TechnoClass * pending = factory->Get_Object(); + if (pending) { + switch (pending->What_Am_I()) { + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + + case RTTI_BUILDING: + if (!factory->Is_Blocked()) { + Speak(VOX_CONSTRUCTION); + } + break; + + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; + } + } + } + } + } + } + } + + return(false); +} + + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Recalc -- Revalidates the current sidebar list of objects. * + * * + * This routine will revalidate all the buildable objects in the sidebar. This routine * + * comes in handy when a factory has been destroyed, and the sidebar needs to reflect any * + * change that this requires. It checks every object to see if there is a factory available * + * that could produce it. If none can be found, then the object is removed from the * + * sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: bool; The sidebar has changed as a result of this call? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 06/26/1995 JLB : Doesn't collapse sidebar when buildables removed. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Recalc(void) +{ + int ok; + + if (Debug_Map || !BuildableCount) { + return(false); + } + + /* + ** Sweep through all objects listed in the sidebar. If any of those object can + ** not be created -- even in theory -- then they must be removed form the sidebar and + ** any current production must be abandoned. + */ + for (int index = 0; index < BuildableCount; index++) { + TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + if (tech) { + ok = tech->Who_Can_Build_Me(true, false, ParentSidebar->SidebarPlayerPtr->Class->House) != NULL; + } else { + + if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { + ok = ParentSidebar->SidebarPlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); + } else { + ok = false; + } + } + + if (!ok) { + + /* + ** Removes this entry from the list. + */ + if (BuildableCount > 1 && index < BuildableCount-1) { + memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + } + BuildableCount--; + index--; + + Buildables[BuildableCount].BuildableID = 0; + Buildables[BuildableCount].BuildableType = RTTI_NONE; + Buildables[BuildableCount].Factory = -1; + Buildables[BuildableCount].BuildableViaCapture = false; + } + } + + return(false); +} + + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Factory_Link -- Links a factory to a sidebar button. * + * * + * This routine will link the specified factory to this sidebar strip. The exact button to * + * link to is determined from the object type and id specified. A linked button is one that * + * will show appropriate construction animation (clock shape) that matches the state of * + * the factory. * + * * + * INPUT: factory -- The factory number to link to the sidebar. * + * * + * type -- The object type that this factory refers to. * + * * + * id -- The object sub-type that this factory refers to. * + * * + * OUTPUT: Was the factory successfully attached? Failure would indicate that there is no * + * object of the specified type and sub-type in the sidebar list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Factory_Link(int factory, RTTIType type, int id) +{ + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].BuildableType == type && Buildables[index].BuildableID == id) { + Buildables[index].Factory = factory; + IsBuilding = true; + + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Abandon_Production -- Stops production of the object specified. * + * * + * This routine is used to abandon production of the object specified. The factory will * + * be completely disabled by this call. * + * * + * INPUT: type -- The object type that is to be abandoned. The sub-type is not needed * + * since it is presumed there can be only one type in production at any * + * one time. * + * * + * factory -- The factory number that is doing the production. * + * * + * OUTPUT: Was the factory successfully abandoned? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Abandon_Production(RTTIType type, int factory) +{ + return(Column[Which_Column(type)].Abandon_Production(factory)); +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::StripClass::Abandon_Produ -- Abandons production associated with sidebar. * + * * + * Production of the object associated with this sidebar is abandoned when this routine is * + * called. * + * * + * INPUT: factory -- The factory index that is to be suspended. * + * * + * OUTPUT: Was the production abandonment successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/18/1995 JLB : Created. * + * 08/06/1995 JLB : More intelligent abandon logic for multiple factories. * + *=============================================================================================*/ +bool SidebarGlyphxClass::StripClass::Abandon_Production(int factory) +{ + bool noprod = true; + bool abandon = false; + for (int index = 0; index < BuildableCount; index++) { + if (Buildables[index].Factory == factory) { + Factories.Raw_Ptr(factory)->Abandon(); + Buildables[index].Factory = -1; + abandon = true; + } else { + if (Buildables[index].Factory != -1) { + noprod = false; + } + } + } + + /* + ** If there is no production whatsoever on this strip, then flag it so. + */ + if (noprod) { + IsBuilding = false; + } + return(abandon); +} + +/*********************************************************************************************** + * SidebarGlyphxClass::Code_Pointers -- Converts classes pointers to savable representation * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Code_Pointers(void) +{ + if (SidebarPlayerPtr) { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)SidebarPlayerPtr->Class->House; + } else { + ((HouseClass *&)SidebarPlayerPtr) = (HouseClass *)HOUSE_NONE; + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Decode_Pointers -- Converts classes savable representation to run-time * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/25/2019 5:36PM ST : Created. * + *=============================================================================================*/ +void SidebarGlyphxClass::Decode_Pointers(void) +{ + if (*((HousesType*)&SidebarPlayerPtr) == HOUSE_NONE) { + SidebarPlayerPtr = NULL; + } else { + ((HouseClass *&)SidebarPlayerPtr) = HouseClass::As_Pointer(*((HousesType*)&SidebarPlayerPtr)); + } +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Load -- Loads from a save game file. * + * * + * INPUT: file -- The file to read the data from. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Load(Straw &file) +{ + ::new (this) SidebarGlyphxClass(); + + //bool ok = Read_Object(this, sizeof(*this), file, false); + if (file.Get(this, sizeof(*this)) != sizeof(*this)) { + return false; + } + + Column[0].Set_Parent_Sidebar(this); + Column[1].Set_Parent_Sidebar(this); + + return true; +} + + +/*********************************************************************************************** + * SidebarGlyphxClass::Save -- Write to a save game file. * + * * + * INPUT: file -- The file to write the data to. * + * * + * OUTPUT: true = success, false = failure * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 9/26/2019 10:57AM ST : Created. * + *=============================================================================================*/ +bool SidebarGlyphxClass::Save(Pipe &file) +{ + //return(Write_Object(this, sizeof(*this), file)); + + file.Put(this, sizeof(*this)); + + return true; +} + + +extern SidebarGlyphxClass *Get_Current_Context_Sidebar(HouseClass *player_ptr); + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_Clear(player_ptr); + } +} + +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Init_IO(); + } +} + +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Abandon_Production(type, factory); + } + + return false; +} + +bool Sidebar_Glyphx_Add(RTTIType type, int id, HouseClass *player_ptr, bool via_capture) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Add(type, id, via_capture); + } + + return false; +} + +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->Recalc(); + } +} + +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + sidebar->AI(input, 0, 0); + } +} + +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr) +{ + SidebarGlyphxClass *sidebar = Get_Current_Context_Sidebar(player_ptr); + if (sidebar) { + return sidebar->Factory_Link(factory, type, id); + } + + return false; +} + +bool Sidebar_Glyphx_Save(Pipe &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Save(file); + } + return false; +} + +bool Sidebar_Glyphx_Load(Straw &file, SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + return sidebar->Load(file); + } + return false; +} + +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Code_Pointers(); + } +} + +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar) +{ + if (sidebar) { + sidebar->Decode_Pointers(); + } +} \ No newline at end of file diff --git a/code/redalert/sidebarglyphx.h b/code/redalert/sidebarglyphx.h new file mode 100644 index 0000000..2e085a1 --- /dev/null +++ b/code/redalert/sidebarglyphx.h @@ -0,0 +1,209 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\sidebar.h_v 2.18 16 Oct 1995 16:45:24 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SIDEBAR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 20, 1994 * + * * + * Last Update : October 20, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SIDEBAR_GLYPHX_H +#define SIDEBAR_GLYPHX_H + +#pragma once + +#include "function.h" +#include "power.h" +#include "factory.h" + + +/* +** ST - 3/14/2019 10:49AM +** +** We are going to need one sidebar per player for multiplayer with GlyphX. We can't have different maps / cell arrays per +** player though, so SidebarClass being in the middle of the map/display class hierarchy is a problem. +** +** All the class static data will have to be made non-static so we can have multiple instances. +** +** So, this is a stub sidebar class with the functionality we need just to support the exporting of production data to the +** GlyphX client. +** +** +*/ + + + + +class SidebarGlyphxClass +{ + public: + + enum SideBarClassEnums { + COLUMNS=2 // Number of side strips on sidebar. + }; + + SidebarGlyphxClass(void); + + /* + ** Initialization + */ + void Init_Clear(HouseClass *player_ptr); // Clears all to known state + void Init_IO(void); // Inits button list + + void AI(KeyNumType & input, int x, int y); + + bool Abandon_Production(RTTIType type, int factory); + bool Add(RTTIType type, int ID, bool via_capture = false); + void Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + + /* + ** File I/O. + */ + void Code_Pointers(void); + void Decode_Pointers(void); + bool Load(Straw &file); + bool Save(Pipe &pipe); + + /* + ** Each side strip is managed by this class. It handles all strip specific + ** actions. + */ + class StripClass : public StageClass + { + class SelectClass : public ControlClass + { + public: + SelectClass(void); + + void Set_Owner(StripClass & strip, int index); + StripClass * Strip; + int Index; + + protected: + int Action(unsigned flags, KeyNumType & key); + }; + + public: + + StripClass(void); + bool Add(RTTIType type, int ID, bool via_capture); + bool Abandon_Production(int factory); + bool AI(KeyNumType & input, int x, int y); + void One_Time(int id); + void Init_Clear(void); + void Init_IO(int id); + void Init_Theater(TheaterType theater); + bool Recalc(void); + bool Factory_Link(int factory, RTTIType type, int id); + void const * Get_Special_Cameo(int type); + + void Set_Parent_Sidebar(SidebarGlyphxClass *parent) {ParentSidebar = parent;} + + /* + ** File I/O. + */ + bool Load(FileClass & file); + bool Save(FileClass & file); + //void Code_Pointers(void); + //void Decode_Pointers(void); + + + /* + ** Working numbers used when rendering and processing the side strip. + */ + enum SideBarGeneralEnums { + MAX_BUILDABLES = 75 // Maximum number of object types in sidebar. + }; + + SidebarGlyphxClass *ParentSidebar; + + + /* + ** This is a unique identifier for the sidebar strip. Using this identifier, + ** it is possible to differentiate the button messages that arrive from the + ** common input button list. It >MUST< be equal to the strip's index into + ** the Column[] array, because the strip uses it to access the stripclass + ** buttons. + */ + int ID; + + /* + ** If construction is in progress (no other objects in this strip can + ** be started), then this flag will be true. It will be cleared when + ** the strip is free to start production again. + */ + unsigned IsBuilding:1; + + /* + ** This is the count of the number of sidebar slots that are active. + */ + int BuildableCount; + + /* + ** This is the array of buildable object types. This array is sorted in the order + ** that it is to be displayed. This array keeps track of which objects are building + ** and ready to be placed. The very nature of this method precludes simultaneous + ** construction of the same object type. + */ + typedef struct BuildType { + int BuildableID; + RTTIType BuildableType; + int Factory; // Production manager. + bool BuildableViaCapture; // Added for new sidebar functionality. ST - 9/24/2019 3:10PM + } BuildType; + BuildType Buildables[MAX_BUILDABLES]; + + } Column[COLUMNS]; + + + private: + int Which_Column(RTTIType type); + + HouseClass *SidebarPlayerPtr; +}; + + + +void Sidebar_Glyphx_Init_Clear(HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_Init_IO(HouseClass *player_ptr = NULL); // Inits button list +bool Sidebar_Glyphx_Abandon_Production(RTTIType type, int factory, HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Add(RTTIType type, int ID, HouseClass *player_ptr = NULL, bool via_capture = false); +void Sidebar_Glyphx_Recalc(HouseClass *player_ptr = NULL); +bool Sidebar_Glyphx_Factory_Link(int factory, RTTIType type, int id, HouseClass *player_ptr = NULL); +void Sidebar_Glyphx_AI(HouseClass *player_ptr, KeyNumType & input); +bool Sidebar_Glyphx_Save(Pipe &file, SidebarGlyphxClass *sidebar); +bool Sidebar_Glyphx_Load(Straw &file, SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Code_Pointers(SidebarGlyphxClass *sidebar); +void Sidebar_Glyphx_Decode_Pointers(SidebarGlyphxClass *sidebar); + + + + + +#endif //SIDEBAR_GLYPHX_H \ No newline at end of file diff --git a/code/redalert/slider.cpp b/code/redalert/slider.cpp new file mode 100644 index 0000000..fa96ad3 --- /dev/null +++ b/code/redalert/slider.cpp @@ -0,0 +1,413 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SLIDER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SliderClass::Action -- Handles input processing for the slider. * + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * SliderClass::Step -- Steps the slider one value up or down. * + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * SliderClass::~SliderClass -- Destructor for slider object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "slider.h" + + +/*********************************************************************************************** + * SliderClass::SliderClass -- Normal constructor for a slider (with thumb) gadget. * + * * + * This is the normal constructor for the slider gadget. * + * * + * INPUT: id -- The ID number to assign to this gadget. * + * x,y -- The pixel coordinate of the upper left corner for this gadget. * + * w,h -- The width and height of the slider gadget. The slider automatically * + * adapts for horizontal or vertical operation depending on which of these * + * dimensions is greater. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list) + : GaugeClass(id, x, y, w, h) +{ + BelongToList = belong_to_list ? true : false; + + PlusGadget = 0; + MinusGadget = 0; + if (!BelongToList) { + PlusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-PLUS.SHP"), X+Width+2, Y); + MinusGadget = new ShapeButtonClass(id, MFCD::Retrieve("BTN-MINS.SHP"), X-6, Y); + + if (PlusGadget) { + PlusGadget->Make_Peer(*this); + PlusGadget->Add(*this); + PlusGadget->Flag_To_Redraw(); + } + if (MinusGadget) { + MinusGadget->Make_Peer(*this); + MinusGadget->Add(*this); + MinusGadget->Flag_To_Redraw(); + } + } + Set_Thumb_Size(1); + Recalc_Thumb(); + + /* + ** Gauges have at least 2 colors, but sliders should only have one. + */ + IsColorized = 0; +} + + +/*********************************************************************************************** + * SliderClass::~SliderClass -- Destructor for slider object. * + * * + * This cleans up the slider object in preparation for deletion. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +SliderClass::~SliderClass(void) +{ + if (PlusGadget) { + delete PlusGadget; + PlusGadget = 0; + } + if (MinusGadget) { + delete MinusGadget; + MinusGadget = 0; + } +} + + +/*********************************************************************************************** + * SliderClass::Set_Maximum -- Sets the maximum value for this slider. * + * * + * This sets the maximum value that the slider can be set at. The maximum value controls * + * the size of the thumb and the resolution of the thumb's movement. * + * * + * INPUT: value -- The value to set for the slider's maximum. * + * OUTPUT: bool; Was the maximum value changed? A false indicates a set to the value it * + * is currently set to already. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Maximum(int value) +{ + if (GaugeClass::Set_Maximum(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Set_Thumb_Size -- Sets the size of the thumb in "slider units". * + * * + * This routine will set the size of the thumb as it relates to the maximum value the * + * slider can achieve. This serves to display a proportionally sized thumb as well as * + * control how the slider "bumps" up or down. * + * * + * INPUT: value -- The new value of the thumb. It should never be larger than the slider * + * maximum. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Set_Thumb_Size(int value) +{ + Thumb = min(value, MaxValue); + Thumb = max(Thumb, 1); + Flag_To_Redraw(); + Recalc_Thumb(); +} + + +/*********************************************************************************************** + * SliderClass::Set_Value -- Sets the current thumb position for the slider. * + * * + * This routine will set the thumb position for the slider. * + * * + * INPUT: value -- The position to set the slider. This position is relative to the maximum * + * value for the slider. * + * * + * OUTPUT: bool; Was the slider thumb position changed at all? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Set_Value(int value) +{ + value = min(value, MaxValue-Thumb); + + if (GaugeClass::Set_Value(value)) { + Recalc_Thumb(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SliderClass::Recalc_Thumb -- Recalculates the thumb pixel size and starting offset. * + * * + * This takes the current thumb logical size and starting value and calculates the pixel * + * size and starting offset accordingly. This function should be called whenever one of * + * these elements has changed. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Recalc_Thumb(void) +{ + int length = IsHorizontal ? Width : Height; + int size = length * fixed(Thumb, MaxValue); +// int size = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, Thumb)); + ThumbSize = max(size, 4); + int start = length * fixed(CurValue, MaxValue); +// int start = Fixed_To_Cardinal(length, Cardinal_To_Fixed(MaxValue, CurValue)); + ThumbStart = min(start, length-ThumbSize); +} + + +/*********************************************************************************************** + * SliderClass::Action -- Handles input processing for the slider. * + * * + * This routine is called when a qualifying input event has occurred. This routine will * + * process that event and make any adjustments to the slider as necessary. * + * * + * INPUT: flags -- Flag bits that tell the input event that caused this function to * + * be called. * + * key -- Reference to the key that caused the input event. * + * OUTPUT: bool; Was the event consumed and further processing of the gadget list should be * + * aborted? * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** Handle the mouse click in a special way. If the click was not on the thumb, then + ** jump the thumb position one "step" in the appropriate direction. Otherwise, let normal + ** processing take place -- the slider then "sticks" and the thumb moves according to + ** mouse position. + */ + if (flags & LEFTPRESS) { + int mouse; // Mouse pixel position. + int edge; // Edge of slider. + + if (IsHorizontal) { + mouse = Get_Mouse_X(); + edge = X; + } else { + mouse = Get_Mouse_Y(); + edge = Y; + } + edge += 1; + + /* + ** Clicking outside the thumb: invoke parent's Action to process flags etc, + ** but turn off the event & return true so processing stops at this button. + */ + if (mouse < edge+ThumbStart) { + Bump(true); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + if (mouse > edge+ThumbStart+ThumbSize) { + Bump(false); + GaugeClass::Action(0, key); + key = KN_NONE; + return(true); + } else { + GaugeClass::Action(flags, key); + key = KN_NONE; + return(true); + } + } + } + + /* + ** CHANGE GAUGECLASS::ACTION -- REMOVE (LEFTRELEASE) FROM IF STMT + */ + return(GaugeClass::Action(flags, key)); +} + + +/*********************************************************************************************** + * SliderClass::Bump -- Bumps the slider one "thumb size" up or down. * + * * + * This support function will bump the slider one "step" or the size of the thumb up or * + * down as specified. It is typically called when the slider is clicked outside of the * + * thumb region but still inside of the slider. * + * * + * INPUT: up -- Should the bump be to increase the current position? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Bump(int up) +{ + if (up) { + return(Set_Value(CurValue - Thumb)); + } + return(Set_Value(CurValue + Thumb)); +} + + +/*********************************************************************************************** + * SliderClass::Step -- Steps the slider one value up or down. * + * * + * This routine will move the slider thumb one step in the direction specified. * + * * + * INPUT: up -- Should the step be up (i.e., forward)? * + * OUTPUT: bool; Was the slider changed at all? A false indicates that the slider is already * + * at one end or the other. * + * WARNINGS: none * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Step(int up) +{ + if (up) { + return(Set_Value(CurValue - 1)); + } + return(Set_Value(CurValue + 1)); +} + + +/*********************************************************************************************** + * SliderClass::Draw_Thumb -- Draws the "thumb" for this slider. * + * * + * This will draw the thumb graphic for this slider. Sometimes the thumb requires special * + * drawing, thus the need for this function separate from the normal Draw_Me function. * + * * + * INPUT: none * + * OUTPUT: none * + * WARNINGS: The mouse is guaranteed to be hidden when this routine is called. * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Draw_Thumb(void) +{ + if (IsHorizontal) { + Draw_Box(X+ThumbStart, Y, ThumbSize, Height, BOXSTYLE_RAISED, true); + } else { + Draw_Box(X, Y+ThumbStart, Width, ThumbSize, BOXSTYLE_RAISED, true); + } +} + + +/*********************************************************************************************** + * SliderClass::Draw_Me -- Draws the body of the gauge. * + * * + * This routine will draw the body of the gauge if necessary. * + * * + * INPUT: forced -- Should the gauge be redrawn regardless of the current redraw flag? * + * OUTPUT: bool; Was the gauge redrawn? * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int SliderClass::Draw_Me(int forced) +{ + if (BelongToList) { + if (ControlClass::Draw_Me(forced)) { + + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width, Y+Height); + } + + /* + ** Draw the body & set text color. + */ + Draw_Box (X, Y, Width, Height, BOXSTYLE_DOWN, true); + Draw_Thumb(); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + } + + /* + ** If it does not belong to a listbox... + */ + return(GaugeClass::Draw_Me(forced)); +} + + +/*********************************************************************************************** + * SliderClass::Peer_To_Peer -- A peer gadget was touched -- make adjustments. * + * * + * This routine is called when one of the peer gadgets (the scroll arrows or the slider) * + * was touched in some fashion. This routine will sort out whom and why and then make * + * any necessary adjustments to the list box. * + * * + * INPUT: flags -- The event flags that affected the peer gadget. * + * key -- The key value at the time of the event. * + * whom -- Which gadget is being touched. * + * OUTPUT: none * + * WARNINGS: none * + * HISTORY: 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void SliderClass::Peer_To_Peer(unsigned flags, KeyNumType & , ControlClass & whom) +{ + if (flags & LEFTRELEASE) { + if (&whom == PlusGadget) { + Step(false); + } + if (&whom == MinusGadget) { + Step(true); + } + } +} + + diff --git a/code/redalert/slider.h b/code/redalert/slider.h new file mode 100644 index 0000000..e0f125a --- /dev/null +++ b/code/redalert/slider.h @@ -0,0 +1,107 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SLIDER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SLIDER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SLIDER_H +#define SLIDER_H + +#include "gauge.h" +#include "shapebtn.h" + + +/*************************************************************************** + * SliderClass -- Like a Windows ListBox structure * + * * + * INPUT: int id-- id of gadget * + * int x -- x position of gadget * + * int y -- y position of gadget * + * int w -- width of gadget * + * int h -- height of gadget * + * int belong_to_list -- does this slider go with a listclass? * + * * + * OUTPUT: none. * + * WARNINGS: * + * HISTORY: 01/03/1995 MML : Created. * + *=========================================================================*/ +class SliderClass : public GaugeClass +{ + public: + SliderClass(unsigned id, int x, int y, int w, int h, int belong_to_list=false); + virtual ~SliderClass(void); + + virtual void Set_Thumb_Size(int value); + virtual int Set_Maximum(int value); + virtual int Set_Value(int); + virtual int Bump(int up); + virtual int Step(int up); + virtual int Draw_Me(int forced); + virtual void Peer_To_Peer(unsigned flags, KeyNumType & key, ControlClass & whom); + + virtual int Thumb_Pixels(void) { return (ThumbSize);} + + protected: + + /* + ** If the slider bar has been created, these point to the respective gadgets + ** that it is composed of. + */ + ShapeButtonClass * PlusGadget; + ShapeButtonClass * MinusGadget; + + /* + ** If I belong to a listbox, I have to draw myself differently... + **/ + unsigned BelongToList:1; + + /* + ** This is the logical size of the thumb. This value is used when drawing + ** the thumb imagery. It is also the amount that is bumped when the + ** Bump() function is called. (This value is in application units.) + */ + int Thumb; + + /* + ** This is the current thumb pixel size and starting offset from beginning + ** of slider region. (These values are in pixels.) + */ + int ThumbSize; + int ThumbStart; // x or y position for the thumb + + virtual int Action(unsigned flags, KeyNumType &key); + virtual void Draw_Thumb(void); + + private: + void Recalc_Thumb(void); +}; + +#endif diff --git a/code/redalert/smudge.cpp b/code/redalert/smudge.cpp new file mode 100644 index 0000000..ef2f8eb --- /dev/null +++ b/code/redalert/smudge.cpp @@ -0,0 +1,361 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SMUDGE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * SmudgeClass::operator new -- Creator of smudge objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "smudge.h" + + + +HousesType SmudgeClass::ToOwn = HOUSE_NONE; + + +/*********************************************************************************************** + * SmudgeClass::operator new -- Creator of smudge objects. * + * * + * This routine will allocate a smudge object from the smudge tracking pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a newly allocated smudge object. If one couldn't be * + * found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void * SmudgeClass::operator new(size_t ) +{ + void * ptr = Smudges.Allocate(); + if (ptr != NULL) { + ((SmudgeClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::operator delete -- Deletes the smudge from the tracking system. * + * * + * This routine is used to remove the smudge from the tracking system. * + * * + * INPUT: ptr -- Pointer to the smudge to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((SmudgeClass *)ptr)->IsActive = false; + } + Smudges.Free((SmudgeClass *)ptr); +} + + +/*********************************************************************************************** + * SmudgeClass::SmudgeClass -- Constructor for smudge objects. * + * * + * This is the typical constructor for smudge objects. If the position to place the * + * smudge is not given, then the smudge will be initialized in a limbo state. If the * + * smudge is placed on the map, then this operation causes the smudge object itself to be * + * deleted and special map values updated to reflect the presence of a smudge. * + * * + * INPUT: type -- The type of smudge to construct. * + * * + * pos -- The position to place the smudge. If -1, then the smudge is initialized * + * into a limbo state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +SmudgeClass::SmudgeClass(SmudgeType type, COORDINATE pos, HousesType house) : + ObjectClass(RTTI_SMUDGE, Smudges.ID(this)), + Class(SmudgeTypes.Ptr((int)type)) +{ + if (pos != -1) { + ToOwn = house; + if (!Unlimbo(pos)) { + delete this; + } else { + ToOwn = HOUSE_NONE; + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Init -- Initialize the smudge tracking system. * + * * + * This routine is used during the scenario clearing process to initialize the smudge * + * object tracking system to a null state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Init(void) +{ + Smudges.Free_All(); +} + + +/*********************************************************************************************** + * SmudgeClass::Mark -- Marks a smudge down on the map. * + * * + * This routine will place the smudge on the map. If the map cell allows. * + * * + * INPUT: mark -- The type of marking to perform. Only MARK_DOWN is supported. * + * * + * OUTPUT: bool; Was the smudge marked successfully? Failure occurs if the smudge isn't * + * marked DOWN. * + * * + * WARNINGS: The smudge object is DELETED by this routine. * + * * + * HISTORY: * + * 09/22/1994 JLB : Created. * + * 12/23/1994 JLB : Checks low level legality before proceeding. * + *=============================================================================================*/ +bool SmudgeClass::Mark(MarkType mark) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + if (mark == MARK_DOWN) { + CELL origin = Coord_Cell(Coord); + + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CELL newcell = origin + w + (h*MAP_CELL_W); + if (Map.In_Radar(newcell)) { + CellClass * cell = &Map[newcell]; + + if (Class->IsBib) { + cell->Smudge = Class->Type; + cell->SmudgeData = w + (h*Class->Width); + cell->Owner = ToOwn; + } else { + if (cell->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + if (Class->IsCrater && cell->Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(cell->Smudge).IsCrater) { + cell->SmudgeData++; + cell->SmudgeData = (int)min((int)cell->SmudgeData, (int)4); + } + + if (cell->Smudge == SMUDGE_NONE) { + + /* + ** Special selection of a crater that starts as close to the + ** specified coordinate as possible. + */ + if (Class->IsCrater) { + cell->Smudge = (SmudgeType)(SMUDGE_CRATER1 + CellClass::Spot_Index(Coord)); + } else { + cell->Smudge = Class->Type; + } + cell->SmudgeData = 0; + } + } + } + + /* + ** Flag everything that might be overlapping this cell to redraw itself. + */ + cell->Redraw_Objects(); + } + } + } + + /* + ** Whether it was successful in placing, or not, delete the smudge object. It isn't + ** needed once the map has been updated with the proper smudge data. Fake this object + ** as if it were never placed down! + */ + Map.Overlap_Up(Coord_Cell(Coord), this); + IsDown = false; + IsInLimbo = true; + delete this; + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * SmudgeClass::Disown -- Disowns (removes) a building bib piece. * + * * + * This routine is used when a building is removed from the game. If there was any bib * + * attached, this routine will be called to disown the cells and remove the bib artwork. * + * * + * INPUT: cell -- The origin cell for this bib removal. * + * * + * OUTPUT: none * + * * + * WARNINGS: This is actually working on a temporary bib object. It is created for the sole * + * purpose of calling this routine. It will be deleted immediately afterward. * + * * + * HISTORY: * + * 07/04/1995 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Disown(CELL cell) +{ + assert(Smudges.ID(this) == ID); + assert(IsActive); + + if (Class->IsBib) { + for (int w = 0; w < Class->Width; w++) { + for (int h = 0; h < Class->Height; h++) { + CellClass & cellptr = Map[(CELL)(cell + w + (h*MAP_CELL_W))]; + + if (cellptr.Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr.Overlay).IsWall) { + cellptr.Smudge = SMUDGE_NONE; + cellptr.SmudgeData = 0; + if (!cellptr.IsFlagged) { + cellptr.Owner = HOUSE_NONE; + } + cellptr.Redraw_Objects(); + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Read_INI -- Reads smudge data from an INI file. * + * * + * This routine is used by the scenario loader to read the smudge data in an INI file and * + * create the appropriate smudge objects on the map. * + * * + * INPUT: buffer -- Pointer to the INI file staging buffer. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/01/1994 JLB : Created. * + * 07/24/1995 JLB : Sets the smudge data value as well. * + *=============================================================================================*/ +void SmudgeClass::Read_INI(CCINIClass & ini) +{ + char buf[128]; // Working string staging buffer. + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + SmudgeType smudge; // Smudge type. + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + smudge = SmudgeTypeClass::From_Name(strtok(buf, ",")); + if (smudge != SMUDGE_NONE) { + char * ptr = strtok(NULL, ","); + if (ptr != NULL) { + int data = 0; + CELL cell = atoi(ptr); + ptr = strtok(NULL, ","); + if (ptr != NULL) data = atoi(ptr); + new SmudgeClass(smudge, Cell_Coord(cell)); + if (Map[cell].Smudge == smudge && data != 0) { + Map[cell].SmudgeData = data; + } + } + } + } +} + + +/*********************************************************************************************** + * SmudgeClass::Write_INI -- Store all the smudge data to the INI database. * + * * + * This routine will output all the smudge data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void SmudgeClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing template data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Find all templates and write them to the file. + */ + for (CELL index = 0; index < MAP_CELL_TOTAL; index++) { + CellClass * ptr; + + ptr = &Map[index]; + if (ptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass const * stype = &SmudgeTypeClass::As_Reference(ptr->Smudge); + if (!stype->IsBib) { + char uname[10]; + char buf[127]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%d,%d", stype->IniName, index, ptr->SmudgeData); + ini.Put_String(INI_Name(), uname, buf); + } + } + } +} \ No newline at end of file diff --git a/code/redalert/smudge.h b/code/redalert/smudge.h new file mode 100644 index 0000000..d75b4b8 --- /dev/null +++ b/code/redalert/smudge.h @@ -0,0 +1,96 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SMUDGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SMUDGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 9, 1994 * + * * + * Last Update : August 9, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SMUDGE_H +#define SMUDGE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This is the transitory form for smudges. They exist as independent objects +** only in the transition stage from creation to placement upon the map. Once +** they are placed on the map, they merely become 'smudges' in the cell data. This +** object is then destroyed. +*/ +class SmudgeClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + SmudgeClass(SmudgeType type, COORDINATE pos=0xFFFFFFFFUL, HousesType house = HOUSE_NONE); + SmudgeClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + operator SmudgeType(void) const {return Class->Type;}; + virtual ~SmudgeClass(void) {if (GameActive) SmudgeClass::Limbo();Class=0;}; + + static void Init(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "SMUDGE";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + virtual bool Mark(MarkType); + virtual void Draw_It(int , int , WindowNumberType ) const {}; + + void Disown(CELL cell); + + private: + + static HousesType ToOwn; + + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/sounddlg.cpp b/code/redalert/sounddlg.cpp new file mode 100644 index 0000000..c46a67f --- /dev/null +++ b/code/redalert/sounddlg.cpp @@ -0,0 +1,455 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SOUNDDLG.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SOUNDDLG.CPP * + * * + * Programmer : Maria del Mar McCready-Legg, Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : September 22, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "sounddlg.h" + +class MusicListClass : public ListClass +{ + public: + MusicListClass(int id, int x, int y, int w, int h) : + ListClass(id, x, y, w, h, TPF_6PT_GRAD|TPF_NOSHADOW, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")) + {}; + virtual ~MusicListClass(void) {}; + + protected: + virtual void Draw_Entry(int index, int x, int y, int width, int selected); +}; + + +/*********************************************************************************************** + * SoundControlsClass::Process -- Handles all the options graphic interface. * + * * + * This routine is the main control for the visual representation of the options * + * screen. It handles the visual overlay and the player input. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 12/31/1994 MML : Created. * + *=============================================================================================*/ +void SoundControlsClass::Process(void) +{ + + /* + ** Adjust dialog controls for resolution + */ + int option_width= OPTION_WIDTH * RESFACTOR; + int option_height= OPTION_HEIGHT * RESFACTOR; + + int option_x= OPTION_X * RESFACTOR; + int option_y= OPTION_Y * RESFACTOR; + + int listbox_x= LISTBOX_X * RESFACTOR; + int listbox_y= LISTBOX_Y * RESFACTOR; + int listbox_w= LISTBOX_W * RESFACTOR; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +#ifdef WIN32 + int listbox_h= (LISTBOX_H * RESFACTOR)+2; +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif +#else + int listbox_h= LISTBOX_H * RESFACTOR; +#endif + + int button_width= BUTTON_WIDTH * RESFACTOR; + int button_x= BUTTON_X * RESFACTOR; + int button_y= BUTTON_Y * RESFACTOR; + + int stop_x= STOP_X * RESFACTOR; + int stop_y= STOP_Y * RESFACTOR; + + int play_x= PLAY_X * RESFACTOR; + int play_y= PLAY_Y * RESFACTOR; + + int onoff_width= ONOFF_WIDTH * RESFACTOR; + int shuffle_x= SHUFFLE_X * RESFACTOR; + int shuffle_y= SHUFFLE_Y * RESFACTOR; + int repeat_x= REPEAT_X * RESFACTOR; + int repeat_y= REPEAT_Y * RESFACTOR; + + int mslider_x= MSLIDER_X * RESFACTOR; + int mslider_y= MSLIDER_Y * RESFACTOR; + int mslider_w= MSLIDER_W * RESFACTOR; + int mslider_height= MSLIDER_HEIGHT * RESFACTOR; + + int fxslider_x= FXSLIDER_X * RESFACTOR; + int fxslider_y= FXSLIDER_Y * RESFACTOR; + int fxslider_w= FXSLIDER_W * RESFACTOR; + int fxslider_height= FXSLIDER_HEIGHT * RESFACTOR; + + int button_stop= BUTTON_STOP; + int button_play= BUTTON_PLAY; + int button_shuffle= BUTTON_SHUFFLE; + int button_repeat= BUTTON_REPEAT; + int button_options= BUTTON_OPTIONS; + int slider_music= SLIDER_MUSIC; + int slider_sound= SLIDER_SOUND; + int button_listbox= BUTTON_LISTBOX; + + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); +// ThemeType theme; + + /* + ** List box that holds the score text strings. + */ + MusicListClass listbox(0, option_x+listbox_x, option_y+listbox_y, listbox_w, listbox_h); + + /* + ** Return to options menu button. + */ + TextButtonClass returnto(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, option_x+button_x, option_y+button_y, button_width); +// TextButtonClass returnto(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, + + /* + ** Stop playing button. + */ + ShapeButtonClass stopbtn(BUTTON_STOP, MFCD::Retrieve("BTN-ST.SHP"), option_x+stop_x, option_y+stop_y); + + /* + ** Start playing button. + */ + ShapeButtonClass playbtn(BUTTON_PLAY, MFCD::Retrieve("BTN-PL.SHP"), option_x+play_x, option_y+play_y); + + /* + ** Shuffle control. + */ + TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, onoff_width); +// TextButtonClass shufflebtn(BUTTON_SHUFFLE, TXT_OFF, TPF_BUTTON, option_x+shuffle_x, option_y+shuffle_y, ONOFF_WIDTH); + + /* + ** Repeat control. + */ + TextButtonClass repeatbtn(BUTTON_REPEAT, TXT_OFF, TPF_BUTTON, option_x+repeat_x, option_y+repeat_y, onoff_width); + + /* + ** Music volume slider. + */ + SliderClass music(SLIDER_MUSIC, option_x+mslider_x, option_y+mslider_y, mslider_w, mslider_height, true); + + /* + ** Sound volume slider. + */ + SliderClass sound(SLIDER_SOUND, option_x+fxslider_x, option_y+fxslider_y, fxslider_w, fxslider_height, true); + + /* + ** Causes left mouse clicks inside the dialog area, but not on any + ** particular button, to be ignored. + */ + GadgetClass area(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + + /* + ** Causes right clicks anywhere or left clicks outside of the dialog + ** box area to be the same a clicking the return to game options button. + */ + ControlClass ctrl(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), GadgetClass::RIGHTPRESS|GadgetClass::LEFTPRESS); + + /* + ** The repeat and shuffle buttons are of the toggle type. They toggle + ** between saying "on" and "off". + */ + shufflebtn.IsToggleType = true; + if (Options.IsScoreShuffle) { + shufflebtn.Turn_On(); + } else { + shufflebtn.Turn_Off(); + } + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + + repeatbtn.IsToggleType = true; + if (Options.IsScoreRepeat) { + repeatbtn.Turn_On(); + } else { + repeatbtn.Turn_Off(); + } + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + + /* + ** Set the initial values of the sliders. + */ + music.Set_Maximum(255); + music.Set_Thumb_Size(16); + music.Set_Value(Options.ScoreVolume * 256); + sound.Set_Maximum(255); + sound.Set_Thumb_Size(16); + sound.Set_Value(Options.Volume * 256); + + /* + ** Set up the window. Window x-coords are in bytes not pixels. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. + */ + GadgetClass * optionsbtn = &returnto; + listbox.Add_Tail(*optionsbtn); + stopbtn.Add_Tail(*optionsbtn); + playbtn.Add_Tail(*optionsbtn); + shufflebtn.Add_Tail(*optionsbtn); + repeatbtn.Add_Tail(*optionsbtn); + music.Add_Tail(*optionsbtn); + sound.Add_Tail(*optionsbtn); + area.Add_Tail(*optionsbtn); + ctrl.Add_Tail(*optionsbtn); + + /* + ** Add all the themes to the list box. The list box entries are constructed + ** and then stored into allocated EMS memory blocks. + */ + for (ThemeType index = THEME_FIRST; index < Theme.Max_Themes(); index++) { + if (Theme.Is_Allowed(index)) { + char buffer[100]; + int length = Theme.Track_Length(index); + char const * fullname = Theme.Full_Name(index); + + void * ptr = new char [sizeof(buffer)]; + if (ptr) { + sprintf((char *)ptr, "%cTrack %d\t%d:%02d\t%s", index, listbox.Count()+1, length / 60, length % 60, fullname); + listbox.Add_Item((char const *)ptr); + } + + if (Theme.What_Is_Playing() == index) { + listbox.Set_Selected_Index(listbox.Count()-1); + } + } + } + static int _tabs[] = { + 55 * RESFACTOR, 72 * RESFACTOR, 90 * RESFACTOR + }; + listbox.Set_Tabs(_tabs); + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + + Hide_Mouse(); + + /* + ** Draw the background. + */ + Dialog_Box(option_x, option_y, option_width, option_height); + + Draw_Caption(TXT_SOUND_CONTROLS, option_x, option_y, option_width); + + /* + ** Draw the Music, Speech & Sound titles. + */ + Fancy_Text_Print(TXT_MUSIC_VOLUME, option_x+mslider_x-(5 * RESFACTOR), option_y+mslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + Fancy_Text_Print(TXT_SOUND_VOLUME, option_x+fxslider_x-(5 * RESFACTOR), option_y+fxslider_y-(2 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + +#if defined(GERMAN) || defined(FRENCH) + Fancy_Text_Print(TXT_SHUFFLE, option_x+4+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#else + Fancy_Text_Print(TXT_SHUFFLE, option_x+shuffle_x-(5 * RESFACTOR), option_y+shuffle_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); +#endif + Fancy_Text_Print(TXT_REPEAT, option_x+repeat_x-(5 * RESFACTOR), option_y+repeat_y+(1 * RESFACTOR), scheme, TBLACK, TPF_TEXT|TPF_RIGHT); + + optionsbtn->Draw_All(); + Show_Mouse(); + display = false; + } + + /* + ** Get user input. + */ + KeyNumType input = optionsbtn->Input(); + + /* + ** Process Input. + */ + switch (input) { + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + process = false; + break; + + /* + ** Control music volume. + */ + case SLIDER_MUSIC|KN_BUTTON: + Options.Set_Score_Volume(fixed(music.Get_Value(), 256), true); +#ifdef FIXIT_VERSION_3 + if( Session.Type != GAME_NORMAL ) + Options.MultiScoreVolume = Options.ScoreVolume; +#endif + break; + + /* + ** Control sound volume. + */ + case SLIDER_SOUND|KN_BUTTON: + Options.Set_Sound_Volume(fixed(sound.Get_Value(), 256), true); + break; + + case BUTTON_LISTBOX|KN_BUTTON: + break; + + /* + ** Stop all themes from playing. + */ + case BUTTON_STOP|KN_BUTTON: + Theme.Stop(); + Theme.Queue_Song(THEME_QUIET); +// Theme.Queue_Song(THEME_NONE); + break; + + /* + ** Start the currently selected theme to play. + */ + case KN_SPACE: + case BUTTON_PLAY|KN_BUTTON: + Theme.Queue_Song( (ThemeType)*((unsigned char *)listbox.Current_Item()) ); + break; + + /* + ** Toggle the shuffle button. + */ + case BUTTON_SHUFFLE|KN_BUTTON: + shufflebtn.Set_Text(shufflebtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Shuffle(shufflebtn.IsOn); + break; + + /* + ** Toggle the repeat button. + */ + case BUTTON_REPEAT|KN_BUTTON: + repeatbtn.Set_Text(repeatbtn.IsOn ? TXT_ON : TXT_OFF); + Options.Set_Repeat(repeatbtn.IsOn); + break; + } + } + + /* + ** If the score volume was turned all the way down, then actually + ** stop the scores from being played. + */ + if (Options.ScoreVolume == 0) { + Theme.Stop(); + } + + /* + ** Free the items from the list box. + */ + while (listbox.Count()) { + char const * ptr = listbox.Get_Item(0); + listbox.Remove_Item(ptr); + delete [] (void*)ptr; + } +} + + +/*********************************************************************************************** + * MusicListClass::Draw_Entry -- Draw the score line in a list box. * + * * + * This routine will display the score line in a list box. It overrides the list box * + * handler for line drawing. * + * * + * INPUT: index -- The index within the list box that is being drawn. * + * * + * x,y -- The pixel coordinates of the upper left position of the line. * + * * + * width -- The width of the line that drawing is allowed to use. * + * * + * selected-- Is the current line selected? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +void MusicListClass::Draw_Entry(int index, int x, int y, int width, int selected) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + if (TextFlags & TPF_6PT_GRAD) { + TextPrintType flags = TextFlags; + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect (x, y, x + width - 1, y + LineHeight - 1, + GadgetClass::Get_Color_Scheme()->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, scheme, TBLACK, flags, width, Tabs); + + } else { + Conquer_Clip_Text_Print((char *)List[index] + 1, x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, TextFlags, width, Tabs); + } +} diff --git a/code/redalert/sounddlg.h b/code/redalert/sounddlg.h new file mode 100644 index 0000000..f7c800b --- /dev/null +++ b/code/redalert/sounddlg.h @@ -0,0 +1,112 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SOUNDDLG.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : OPTIONS.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 8, 1994 * + * * + * Last Update : June 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUNDDLG_H +#define SOUNDDLG_H + +#include "gadget.h" + +class SoundControlsClass +{ + enum SoundControlsClassEnums { +#ifdef FRENCH + OPTION_WIDTH=308, +#else + OPTION_WIDTH=292, +#endif + OPTION_HEIGHT=146, + + OPTION_X=((320 - OPTION_WIDTH) / 2), + OPTION_Y=(200 - OPTION_HEIGHT) / 2, + + LISTBOX_X=17, + LISTBOX_Y=54, + LISTBOX_W=OPTION_WIDTH-(LISTBOX_X*2), + LISTBOX_H=72, + + BUTTON_WIDTH=70, + BUTTON_X=OPTION_WIDTH-(BUTTON_WIDTH+17), // Options button x pos + BUTTON_Y=128, // Options button y pos + + STOP_X=17, // Stop button X. + STOP_Y=128, // Stop button Y. + + PLAY_X=35, + PLAY_Y=128, + + ONOFF_WIDTH=25, +#ifdef GERMAN + SHUFFLE_X=79,//BGA:91, +#else +#ifdef FRENCH + SHUFFLE_X=99, +#else + SHUFFLE_X=97, +#endif +#endif + SHUFFLE_Y=128, + +#ifdef FRENCH + REPEAT_X=169, +#else + REPEAT_X=164, +#endif + REPEAT_Y=128, + + MSLIDER_X=147, + MSLIDER_Y=28, + MSLIDER_W=108, + MSLIDER_HEIGHT=5, + + FXSLIDER_X=147, + FXSLIDER_Y=40, + FXSLIDER_W=108, + FXSLIDER_HEIGHT=5, + + BUTTON_STOP = 605, + BUTTON_PLAY, + BUTTON_SHUFFLE, + BUTTON_REPEAT, + BUTTON_OPTIONS, + SLIDER_MUSIC, + SLIDER_SOUND, + BUTTON_LISTBOX, + }; + + public: + SoundControlsClass(void) {} + void Process(void); +}; + +#endif diff --git a/code/redalert/special.cpp b/code/redalert/special.cpp new file mode 100644 index 0000000..7fb9ad4 --- /dev/null +++ b/code/redalert/special.cpp @@ -0,0 +1,602 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPECIAL.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/27/95 * + * * + * Last Update : August 20, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * Fetch_Password -- Prompts for a password entry from client. * + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * Special_Dialog -- Handles the special options dialog. * + * SpecialClass::Init -- Initialize the special class of options. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +#ifdef WIN32 +#define OPTION_WIDTH 236*2 +#define OPTION_HEIGHT 162*2 +#define OPTION_X ((640 - OPTION_WIDTH) / 2) +#define OPTION_Y (400 - OPTION_HEIGHT) / 2 +#else +#define OPTION_WIDTH 236 +#define OPTION_HEIGHT 162 +#define OPTION_X ((320 - OPTION_WIDTH) / 2) +#define OPTION_Y (200 - OPTION_HEIGHT) / 2 +#endif + + +/*********************************************************************************************** + * SpecialClass::Init -- Initialize the special class of options. * + * * + * This initialization function is required (as opposed to using a constructor) because * + * the SpecialClass is declared as part of a union. A union cannot have a member with a * + * constructor. Other than this anomoly, the function serves the same purpose as a * + * normal constructor. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/20/1996 JLB : Created. * + *=============================================================================================*/ +void SpecialClass::Init(void) +{ + IsShadowGrow = false; + IsSpeedBuild = false; + IsFromInstall = false; + IsCaptureTheFlag = false; + IsInert = false; + IsThreePoint = false; + IsTGrowth = true; + IsTSpread = true; + UseMCVDeploy = false; + IsMCVDeploy = false; + IsEarlyWin = false; +} + + +/*********************************************************************************************** + * Special_Dialog -- Handles the special options dialog. * + * * + * This dialog is used when setting the special game options. It does not appear in the * + * final version of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void Special_Dialog(bool simple) +{ + simple; +#if (0)//PG + SpecialClass oldspecial = Special; + GadgetClass * buttons = NULL; + static struct { + int Description; + int Setting; + CheckBoxClass * Button; + } _options[] = { + {TXT_THREE_POINT, 0, 0}, + {TXT_SPEED_BUILD, 0, 0}, + }; + + TextButtonClass ok(200, TXT_OK, TPF_BUTTON, OPTION_X+15*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + TextButtonClass cancel(201, TXT_CANCEL, TPF_BUTTON, OPTION_X+OPTION_WIDTH-60*RESFACTOR, OPTION_Y+OPTION_HEIGHT-15*RESFACTOR); + buttons = &ok; + cancel.Add(*buttons); + + for (int index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + _options[index].Button = new CheckBoxClass(100+index, OPTION_X+17*RESFACTOR, OPTION_Y+20*RESFACTOR+(index*10*RESFACTOR)); + if (_options[index].Button) { + _options[index].Button->Add(*buttons); + + bool value = false; + switch (_options[index].Description) { + case TXT_THREE_POINT: + value = Special.IsThreePoint; + break; + + case TXT_SPEED_BUILD: + value = Special.IsSpeedBuild; + break; + } + + _options[index].Setting = value; + if (value) { + _options[index].Button->Turn_On(); + } else { + _options[index].Button->Turn_Off(); + } + } + } + + Map.Override_Mouse_Shape(MOUSE_NORMAL); + Set_Logic_Page(SeenBuff); + bool recalc = true; + bool display = true; + bool process = true; + while (process) { + + if (Session.Type == GAME_NORMAL) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + if (display) { + display = false; + + Hide_Mouse(); + Dialog_Box(OPTION_X, OPTION_Y, OPTION_WIDTH, OPTION_HEIGHT); + Draw_Caption(TXT_SPECIAL_OPTIONS, OPTION_X, OPTION_Y, OPTION_WIDTH); + + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + Fancy_Text_Print(_options[index].Description, _options[index].Button->X+10*RESFACTOR, _options[index].Button->Y, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + } + buttons->Draw_All(); + Show_Mouse(); + } + + KeyNumType input = buttons->Input(); + switch (input) { + case KN_ESC: + case 200|KN_BUTTON: + process = false; + for (index = 0; index < sizeof(_options)/sizeof(_options[0]); index++) { + bool setting = _options[index].Setting; + switch (_options[index].Description) { + case TXT_THREE_POINT: + oldspecial.IsThreePoint = setting; + break; + + case TXT_SPEED_BUILD: + oldspecial.IsSpeedBuild = setting; + break; + } + } + if (!simple) { + OutList.Add(EventClass(oldspecial)); + } else { + Special = oldspecial; + } + break; + + case 201|KN_BUTTON: + process = false; + break; + + case KN_NONE: + break; + + default: + index = (input & ~KN_BUTTON) - 100; + if ((unsigned)index < sizeof(_options)/sizeof(_options[0])) { + _options[index].Setting = _options[index].Button->IsOn; + } + break; + } + } + + if (!simple) { + Map.Revert_Mouse_Shape(); + HidPage.Clear(); + Map.Flag_To_Redraw(true); + Map.Render(); + } +#endif +} + + +/* +** Derived from the edit class, this class allows entry of passwords style text +** as an edit box. This style is characterized by "*" being displayed for every +** real character entered. +*/ +class PWEditClass : public EditClass +{ + public: + PWEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w=-1, int h=-1) : + EditClass(id, text, max_len, flags, x, y, w, h, ALPHANUMERIC) {}; + + protected: + virtual void Draw_Text(char const * text); +}; + + +/*********************************************************************************************** + * PWEditClass::Draw_Text -- Draws password style obscured text. * + * * + * This routine is used by the password style edit box in order to display the entered * + * text. The text will be displayed as asterisks instead of the actual characters the * + * edit box may contain. This is necessary to obscure the password entry from glancing * + * eyes. * + * * + * INPUT: text -- Pointer to the text that is to be rendered. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +void PWEditClass::Draw_Text(char const * text) +{ + char buffer[80]; + + memset(buffer, '\0', sizeof(buffer)); + memset(buffer, '*', strlen(text)); + + if (FontPtr == GradFont6Ptr) { + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && (int)strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(buffer), Y+1, Color, TBLACK, TextFlags | flags); + } + } else { + Conquer_Clip_Text_Print(buffer, X+1, Y+1, Has_Focus() ? + &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY], + TBLACK, TextFlags, Width-2); + + if (Has_Focus() && (int)strlen(buffer) < MaxLength) { + Conquer_Clip_Text_Print("_", X+1+String_Pixel_Width(buffer), Y+1, + &ColorRemaps[PCOLOR_DIALOG_BLUE], TBLACK, TextFlags); + } + } +} + + +/*********************************************************************************************** + * Fetch_Password -- Prompts for a password entry from client. * + * * + * This routine will prompt for and return a password entry from the player. * + * * + * INPUT: caption -- The caption to use for the top of the prompt dialog. * + * * + * message -- The body of the message to display in the dialog. * + * * + * btext -- The button text to use to finish the dialog box entry. * + * * + * OUTPUT: Returns with a pointer to the password text entered. This pointer is valid * + * only until the next time that this routine is called. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/27/1995 JLB : Created. * + *=============================================================================================*/ +#define BUFFSIZE (511) +char const * Fetch_Password(int caption, int message, int btext) +{ + char buffer[BUFFSIZE]; + bool process; // loop while true + KeyNumType input; // user input + bool pressed; + int curbutton; + TextButtonClass ok; + + if (btext == TXT_NONE) btext = TXT_OK; + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Examine the optional button parameters. Fetch the width and starting + ** characters for each. + */ + int bwidth, bheight; // button width and height + + /* + ** Build the button list. + */ + bheight = FontHeight + FontYSpacing + 2*RESFACTOR; + bwidth = max((String_Pixel_Width(Text_String(btext)) + 8*RESFACTOR), 30*RESFACTOR); + + /* + ** Determine the dimensions of the text to be used for the dialog box. + ** These dimensions will control how the dialog box looks. + */ + buffer[BUFFSIZE-1] = 0; + strncpy(buffer, Text_String(message), BUFFSIZE-1); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, 255, width, height); + + width = max(width, 50*RESFACTOR); + width += 40*RESFACTOR; + height += (60+25)*RESFACTOR; + + int x = (SeenBuff.Get_Width() - width) / 2; + int y = (SeenBuff.Get_Height() - height) / 2; + + /* + ** Create the "ok" and password edit buttons. + */ + TextButtonClass button1(1, btext, TPF_BUTTON, + x + ((width - bwidth) >> 1), y + height - (bheight + 5*RESFACTOR), bwidth); + + static char pbuffer[45]; + memset(pbuffer, '\0', sizeof(pbuffer)); + int editx = x+26*RESFACTOR; + int editwidth = (SeenBuff.Get_Width()/2 - editx) * 2; + PWEditClass button2(2, &pbuffer[0], sizeof(pbuffer), TPF_6PT_GRAD|TPF_NOSHADOW, editx, (y+height)-35*RESFACTOR, editwidth, 10*RESFACTOR); + + TextButtonClass * buttonlist = 0; + curbutton = 0; + + /* + ** Add and initialize the buttons to the button list. + */ + buttonlist = &button1; + button2.Add(*buttonlist); + + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Set_Logic_Page(SeenBuff); + Dialog_Box(x, y, width, height); + Draw_Caption(caption, x, y, width); + + /* + ** Draw the body of the message box. + */ + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 25*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + + /* + ** Main Processing Loop. + */ + process = true; + pressed = false; + bool first = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + process = false; + break; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + input = buttonlist->Input(); + if (first) { + button2.Set_Focus(); + button2.Flag_To_Redraw(); + first = false; + } + switch (input) { + case (1|BUTTON_FLAG): + process = false; + break; + + case (KN_ESC): + case (2|BUTTON_FLAG): + process = false; + break; + + case (KN_RETURN): + process = false; + break; + + default: + break; + } + } + + return(pbuffer); +} + + +/*********************************************************************************************** + * Fetch_Difficulty -- Fetches the difficulty setting desired. * + * * + * This will display a dialog box that requests the player to specify a difficulty * + * setting. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a difficulty setting of 0 for easiest and 4 for hardest. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +int Fetch_Difficulty(bool amath) +#else +int Fetch_Difficulty(void) +#endif +{ + int const w = 250 * RESFACTOR; + int const h = 80 * RESFACTOR; + int const x = ( SeenBuff.Get_Width() - w ) / 2; + int const y = ( SeenBuff.Get_Height() - h ) / 2; + int const bwidth = 30 * RESFACTOR; + + /* + ** Fill the description buffer with the description text. Break + ** the text into appropriate spacing. + */ + char buffer[512]; + strncpy(buffer, Text_String(TXT_DIFFICULTY), sizeof(buffer)-1); + buffer[sizeof(buffer)-1] = '\0'; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// If it's an aftermath mission, trim the sentence to get rid of the campaign stuff. + if (amath) { + int index=0; + while (buffer[index] && buffer[index]!='.') index++; + if (buffer[index]=='.') { + buffer[index+1]=0; + } + } +#endif + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + int width; + int height; + Format_Window_String(buffer, w-60*RESFACTOR, width, height); + + /* + ** Create the OK button. + */ + TextButtonClass okbutton(1, TXT_OK, TPF_BUTTON, (x+w) - (bwidth+20*RESFACTOR) , (y+h) - (18*RESFACTOR), bwidth); + GadgetClass * buttonlist = &okbutton; + + /* + ** Create the slider button. + */ + SliderClass slider(2, x+20*RESFACTOR, y+h - 29*RESFACTOR, w - 40*RESFACTOR, 8*RESFACTOR, true); + if (Rule.IsFineDifficulty) { + slider.Set_Maximum(5); + slider.Set_Value(2); + } else { + slider.Set_Maximum(3); + slider.Set_Value(1); + } + slider.Add(*buttonlist); + + /* + ** Main Processing Loop. + */ + Set_Logic_Page(SeenBuff); + //bool redraw = true; + bool process = true; + while (process) { + + { +// jmarshall + // redraw = false; + Load_Title_Screen("TITLE.PCX", NULL, NULL); +// jmarshall end + /* + ** Draw the background of the dialog. + */ + Hide_Mouse(); + Dialog_Box(x, y, w, h); + + /* + ** Draw the body of the message. + */ +// Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW); + Fancy_Text_Print(buffer, x + 20*RESFACTOR, y + 15*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_NOSHADOW); + + /* + ** Display the descripton of the slider range. + */ + Fancy_Text_Print(TXT_HARD, slider.X+slider.Width, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_RIGHT|TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_EASY, slider.X, slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD|TPF_DROPSHADOW); + Fancy_Text_Print(TXT_NORMAL, slider.X + (slider.Width/2), slider.Y-9*RESFACTOR, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_DROPSHADOW); + + /* + ** Redraw the buttons. + */ + if (buttonlist) { + buttonlist->Draw_All(); + } + Show_Mouse(); + } + + /* + ** Invoke game callback. + */ + Call_Back(); + + // Device_Present(); + +#ifdef WIN32 + /* + ** Handle possible surface loss due to a focus switch + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + //redraw = true; + continue; + } +#endif //WIN32 + + /* + ** Fetch and process input. + */ + KeyNumType input = buttonlist->Input(); + + switch (input) { + case KN_RETURN: + case (1|BUTTON_FLAG): + process = false; + break; + + default: + break; + } + } + + return(slider.Get_Value() * (Rule.IsFineDifficulty ? 1 : 2)); +} diff --git a/code/redalert/special.h b/code/redalert/special.h new file mode 100644 index 0000000..f080f76 --- /dev/null +++ b/code/redalert/special.h @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPECIAL.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SPECIAL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/27/95 * + * * + * Last Update : February 27, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SPECIAL_H +#define SPECIAL_H + +class SpecialClass +{ + public: + + /* + ** This initializes all members just like a constructor. A constructor + ** cannot be used for this class because it is part of a union. + */ + void Init(void); + + /* + ** If the shroud should regenerated, then this flag will be true. + */ + unsigned IsShadowGrow:1; + + /* + ** Controls the speedy build option -- used for testing. + */ + unsigned IsSpeedBuild:1; + + /* + ** If from install, then play the special installation movie and + ** skip asking them what type of game they want to play. + */ + unsigned IsFromInstall:1; + + /* + ** If capture the flag mode is on, this flag will be true. With this + ** flag enabled, then the flag is initially placed at the start of + ** the scenario. + */ + unsigned IsCaptureTheFlag:1; + + /* + ** This flags controls whether weapons are inert. An inert weapon doesn't do any + ** damage. Effectively, if this is true, then the units never die. + */ + unsigned IsInert:1; + + /* + ** If wheeled vehicles should do a 3-point turn when rotating in place, then + ** this flag is true. + */ + unsigned IsThreePoint:1; + + /* + ** If Tiberium is allowed to spread and grow, then these flags will be true. + ** These are duplicated from the rules.ini file and also controlled by the + ** multiplayer dialog box. + */ + unsigned IsTGrowth:1; + unsigned IsTSpread:1; + + /* + ** Flag that we were spawned from WChat. + */ + unsigned IsFromWChat:1; + + /* + ** If this flag is true, then the construction yard can undeploy back into an MCV. + ** Used to override the rules setting. + */ + unsigned UseMCVDeploy:1; + unsigned IsMCVDeploy:1; + + /* + ** New anti-griefing early win mode. ST - 1/31/2020 3:42PM + */ + unsigned IsEarlyWin:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[128]; +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/sprite.cpp b/code/redalert/sprite.cpp new file mode 100644 index 0000000..045a102 --- /dev/null +++ b/code/redalert/sprite.cpp @@ -0,0 +1,326 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SPRITE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/********************************************************************** + + Sprite.cpp + + Dec 28,1995 + + GraphicBufferClass member functions for blitting, scaling, + and rotating bitmaps + +**********************************************************************/ + +#ifndef WIN32 +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + + +//#include "function.h" +#define FILE_H +#define RAWFILE_H +#define WWMEM_H +#define WWFILE_Hx +#include +#include +//#include "gbuffer.h" +//#include "math.h" + + +long _SineTab[256] = { +0 ,6 ,12 , 18 , 25 , 31 , 37 , 43 , 49 , 56 , 62 , 68 , 74 , 80 , 86 , +92 , 97 , 103 , 109 , 115 , 120 , 126 , 131 , 136 , 142 , 147 , 152 , +157 , 162 , 167 , 171 , 176 , 180 , 185 , 189 , 193 , 197 , 201 , 205 , +209 , 212 , 216 , 219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , +243 , 244 , 246 , 248 , 249 , 251 , 252 , 253 , 254 , 254 , 255 , 255 , +255 , 255 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , +246 , 245 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , +216 , 213 , 209 , 205 , 201 , 198 , 194 , 189 , 185 , 181 , 176 , 172 , +167 , 162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , +104 , 98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 44 , 37 , 31 , 25 , +19 , 12 , 6 , 0 , -5 , -12 , -18 , -24 , -30 , -37 , -43 , -49 , -55 , +-61 , -67 , -73 , -79 , -85 , -91 , -97 , -103 , -109 , -114 , -120 , +-125 , -131 , -136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , +-176 , -180 , -185 , -189 , -193 , -197 , -201 , -205 , -208 , -212 , +-215 , -219 , -222 , -225 , -228 , -231 , -233 , -236 , -238 , -240 , +-242 , -244 , -246 , -248 , -249 , -250 , -252 , -253 , -254 , -254 , +-255 , -255 , -255 , -255 , -255 , -255 , -255 , -254 , -254 , -253 , +-252 , -251 , -249 , -248 , -246 , -245 , -243 , -241 , -239 , -236 , +-234 , -231 , -228 , -226 , -223 , -219 , -216 , -213 , -209 , -206 , +-202 , -198 , -194 , -190 , -185 , -181 , -177 , -172 , -167 , -162 , +-158 , -153 , -148 , -142 , -137 , -132 , -126 , -121 , -115 , -110 , +-104 , -98 , -92 , -86 , -81 , -75 , -69 , -62 , -56 , -50 , -44 , +-38 , -32 , -25 , -19 , -13 , -7 , +}; + +long _CosineTab[256] = { +256 , 255 , 255 , 255 , 254 , 254 , 253 , 252 , 251 , 249 , 248 , 246 , +244 , 243 , 241 , 238 , 236 , 234 , 231 , 228 , 225 , 222 , 219 , 216 , +212 , 209 , 205 , 201 , 197 , 193 , 189 , 185 , 181 , 176 , 171 , 167 , +162 , 157 , 152 , 147 , 142 , 137 , 131 , 126 , 120 , 115 , 109 , 103 , +98 , 92 , 86 , 80 , 74 , 68 , 62 , 56 , 50 , 43 , 37 , 31 , 25 , 19 , +12 , 6 , 0 , -6 , -12 , -18 , -24 , -31 , -37 , -43 , -49 , -55 , -61 , +-68 , -74 , -80 , -86 , -91 , -97 , -103 , -109 , -114 , -120 , -125 , -131 , +-136 , -141 , -147 , -152 , -157 , -162 , -166 , -171 , -176 , -180 , +-185 , -189 , -193 , -197 , -201 , -205 , -209 , -212 , -216 , -219 , -222 , +-225 , -228 , -231 , -233 , -236 , -238 , -240 , -242 , -244 , -246 , +-248 , -249 , -251 , -252 , -253 , -254 , -254 , -255 , -255 , -255 , +-255 , -255 , -255 , -255 , -254 , -254 , -253 , -252 , -251 , -249 , -248 , +-246 , -245 , -243 , -241 , -239 , -236 , -234 , -231 , -228 , -225 , +-222 , -219 , -216 , -213 , -209 , -205 , -202 , -198 , -194 , -190 , -185 , +-181 , -176 , -172 , -167 , -162 , -157 , -152 , -147 , -142 , -137 , -132 , +-126 , -121 , -115 , -109 , -104 , -98 , -92 , -86 , -80 , -74 , -68 , +-62 , -56 , -50 , -44 , -38 , -31 , -25 , -19 , -13 , -6 , 0 , 5 , 11 , +18 , 24 , 30 , 36 , 43 , 49 , 55 , 61 , 67 , 73 , 79 , 85 , 91 , 97 , 103 , +108 , 114 , 120 , 125 , 131 , 136 , 141 , 146 , 151 , 156 , 161 , 166 , +171 , 176 , 180 , 184 , 189 , 193 , 197 , 201 , 205 , 208 , 212 , 215 , +219 , 222 , 225 , 228 , 231 , 233 , 236 , 238 , 240 , 242 , 244 , 246 , +248 , 249 , 250 , 252 , 253 , 253 , 254 , 255 , 255 , 255 , +}; + + + +/*************************************************************** +* +* Scale_Rotate +* +* FUNCTION: +* +* Using Bi-Linear Interpolation, draws a scaled and rotated +* bitmap onto the buffer. No clipping is performed so beware. +* +* INPUTS +* +* bmp - bitmap to draw +* pt - desired position of the center +* scale - 24.8 fixed point scale factor +* angle - 8bit angle (0=0deg, 255=360deg) +* +***************************************************************/ + +void GraphicBufferClass::Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle) +{ + unsigned int scrpos; + unsigned int temp; + + int i,j; // counter vars + int pxerror =0; // these three vars will be used in an + int pyerror =0; // integer difference alg to keep track + int pixpos =0; // of what pixel to draw. + unsigned char pixel; + + TPoint2D p0; // "upper left" corner of the rectangle + TPoint2D p1; // "upper right" corner of the rectangle + TPoint2D p2; // "lower left" corner of the rectangle + + /*------------------------------------------------- + Compute three corner points of the rectangle + -------------------------------------------------*/ + { + angle &= 0x0FF; + long c = _CosineTab[angle]; + long s = _SineTab[angle]; + long W = (scale*bmp.Width)>>1; + long L = (scale*bmp.Height)>>1; + + p0.x = (pt.x + ((( (L*c) >> 8)-((W*s) >> 8)) >> 8)); + p0.y = (pt.y + (((-(L*s) >> 8)-((W*c) >> 8)) >> 8)); + p1.x = (pt.x + ((( (L*c) >> 8)+((W*s) >> 8)) >> 8)); + p1.y = (pt.y + (((-(L*s) >> 8)+((W*c) >> 8)) >> 8)); + p2.x = (pt.x + (((-(L*c) >> 8)-((W*s) >> 8)) >> 8)); + p2.y = (pt.y + ((( (L*s) >> 8)-((W*c) >> 8)) >> 8)); + } + + /*----------------------------------- + Initialize Breshnam constants + -----------------------------------*/ + + // This breshnam line goes across the FRONT of the rectangle + // In the bitmap, this will step from left to right + + int f_deltax =(p1.x-p0.x); + int f_deltay =(p1.y-p0.y); + int f_error =0; + int f_xstep =1; + int f_ystep =Width; + + // This breshnam line goes down the SIDE of the rectangle + // In the bitmap, this line will step from top to bottom + + int s_deltax =(p2.x-p0.x); + int s_deltay =(p2.y-p0.y); + int s_error =0; + int s_xstep =1; + int s_ystep =Width; + + /*-------------------------------- + fixup deltas and step values + --------------------------------*/ + + if (f_deltay<0) { + f_deltay=-f_deltay; + f_ystep=-Width; + }; + + if (f_deltax<0) { + f_deltax=-f_deltax; + f_xstep=-1; + }; + + if (s_deltay<0) { + s_deltay=-s_deltay; + s_ystep=-Width; + }; + + if (s_deltax<0) { + s_deltax=-s_deltax; + s_xstep=-1; + }; + + scrpos=p0.x+Width*p0.y; //address of initial screen pos. + temp=scrpos; + + /*--------------------------------------------------------------------- + Now all of the differences, errors, and steps are set up so we can + begin drawing the bitmap... + + There are two cases here, + 1 - the "Front" line has a slope of < 1.0 (45 degrees) + 2 - the "Front" line has a slope of >= 1.0 + + For case 1, we step along the X direction, for case 2, step in y + ---------------------------------------------------------------------*/ + + if (f_deltax>f_deltay) { //CASE 1, step front in X, side in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltax) + { + pixpos++; + pxerror-=f_deltax; + }; + scrpos+=f_xstep; //step to next screen pos + f_error+=f_deltay; + if (f_error>f_deltax) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltax; + scrpos+=f_ystep; + }; + }; + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + + while (pyerror>s_deltay) + { + pixpos+=bmp.Width; + pyerror-=s_deltay; + }; + + f_error=0; + scrpos=temp; + scrpos+=s_ystep; + s_error+=s_deltax; + + if (s_error>s_deltay) + { + s_error-=s_deltay; + scrpos+=s_xstep; + }; + } + + } else { // CASE 2, Step front line in X, side line in Y + + // outer loop steps from top to bottom of the rectangle + for (j=0;jf_deltay) + { + pixpos++; + pxerror-=f_deltay; + }; + + scrpos+=f_ystep; //step to next screen pos + f_error+=f_deltax; + if (f_error>f_deltay) + { + if (pixel) ((unsigned char *)Get_Buffer())[scrpos]=pixel; + f_error-=f_deltay; + scrpos+=f_xstep; + }; + }; + + pxerror=0; + pixpos-=bmp.Width-1; + pyerror+=bmp.Height; + while (pyerror>s_deltax) + { + pixpos+=bmp.Width; + pyerror-=s_deltax; + }; + + scrpos=temp; + scrpos+=s_xstep; + s_error+=s_deltay; + f_error=0; + if (s_error>s_deltax) + { + s_error-=s_deltax; + scrpos+=s_ystep; + }; + } + } +} \ No newline at end of file diff --git a/code/redalert/stage.h b/code/redalert/stage.h new file mode 100644 index 0000000..1103c39 --- /dev/null +++ b/code/redalert/stage.h @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STAGE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STAGE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 17, 1994 * + * * + * Last Update : June 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STAGE_H +#define STAGE_H + +class StageClass { + + /* + ** This handles the animation stage of the object. This includes smoke, walking, + ** flapping, and rocket flames. + */ + unsigned Stage; + + /* + ** This is the countdown timer for stage animation. When this counts down + ** to zero, then the stage increments by one and the time cycle starts + ** over again. + */ + CDTimerClass Timer; + + /* + ** This is the value to assign the StageTimer whenever it needs to be reset. Thus, + ** this value is the control of how fast the stage value increments. + */ + int Rate; + + public: + StageClass(void) : Stage(0), Timer(0), Rate(0) {}; + StageClass(NoInitClass const & x) : Timer(x) {}; + + int Fetch_Stage(void) const {return(Stage);}; + int Fetch_Rate(void) const {return(Rate);}; + void Set_Stage(int stage) {Stage = stage;}; + void Set_Rate(int rate) {Timer = rate; Rate = rate;}; + void AI(void) {}; + bool About_To_Change(void) const {return(Timer == 0 && Rate != 0);} + bool Graphic_Logic(void) { + if (About_To_Change()) { + Stage++; + Timer = Rate; + return(true); + } + return(false); + }; +}; + + +#endif diff --git a/code/redalert/startup.cpp b/code/redalert/startup.cpp new file mode 100644 index 0000000..921d451 --- /dev/null +++ b/code/redalert/startup.cpp @@ -0,0 +1,1011 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/STARTUP.CPP 6 3/15/97 7:18p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STARTUP.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : October 3, 1994 * + * * + * Last Update : September 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Prog_End -- Cleans up library systems in prep for game exit. * + * main -- Initial startup routine (preps library systems). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + // jmarshall - imgui +#include "imgui.h" +#include "examples/imgui_impl_sdl.h" +#include "examples/imgui_impl_opengl3.h" +extern SDL_Window* game_window; +// jmarshall end + + +#include "function.h" +#include +#include + +#ifdef WIN32 +#include "ccdde.h" +#include "ipx95.h" +#endif //WIN32 + +#ifdef MCIMPEG // Denzil 6/15/98 +#include "mcimovie.h" +#endif + +#ifdef WOLAPI_INTEGRATION +//#include "WolDebug.h" +#endif + +extern char RedAlertINI[_MAX_PATH]; + +bool Read_Private_Config_Struct(FileClass & file, NewConfigType * config); +void Print_Error_End_Exit(char * string); +void Print_Error_Exit(char * string); + +#ifdef WIN32 +//WinTimerClass * WinTimer; +extern void Create_Main_Window ( HANDLE instance , int command_show , int width , int height); +extern bool RA95AlreadyRunning; +HINSTANCE ProgramInstance; +void Check_Use_Compressed_Shapes (void); +void Read_Setup_Options(RawFileClass *config_file); +bool VideoBackBufferAllowed = true; +#else +BOOL Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL Remove_Timer_System(VOID); +#endif //WIN32 + +const char* Game_Registry_Key(); + +#if (ENGLISH) +#define WINDOW_NAME "Red Alert" +#endif + +#if (FRENCH) +#define WINDOW_NAME "Alerte Rouge" +#endif + +#if (GERMAN) +#define WINDOW_NAME "Alarmstufe Rot" +#endif + +// Added. ST - 5/14/2019 +bool ProgEndCalled = false; + +extern "C"{ + extern char *BigShapeBufferStart; + extern char *TheaterShapeBufferStart; + extern BOOL UseBigShapeBuffer; + extern bool IsTheaterShape; +} + +extern void Free_Heaps(void); +extern void DLL_Shutdown(void); + +BOOL WINAPI DllMain(HINSTANCE instance, unsigned int fdwReason, void *lpvReserved) +{ + lpvReserved; + + switch (fdwReason) { + + case DLL_PROCESS_ATTACH: + ProgramInstance = instance; + break; + + case DLL_PROCESS_DETACH: + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + + /* + ** Red Alert doesn't clean up memory. Do some of that here. + */ + MFCD::Free_All(); + + if (BigShapeBufferStart) { + Free(BigShapeBufferStart); + BigShapeBufferStart = NULL; + } + + if (TheaterShapeBufferStart) { + Free(TheaterShapeBufferStart); + TheaterShapeBufferStart = NULL; + } + + if (_ShapeBuffer) { + delete [] _ShapeBuffer; + _ShapeBuffer = NULL; + } + + DLL_Shutdown(); + Free_Heaps(); + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + return true; +} + + + +/*********************************************************************************************** + * main -- Initial startup routine (preps library systems). * + * * + * This is the routine that is first called when the program starts up. It basically * + * handles the command line parsing and setting up library systems. * + * * + * INPUT: argc -- Number of command line arguments. * + * * + * argv -- Pointer to array of command line argument strings. * + * * + * OUTPUT: Returns with execution failure code (if any). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + *=============================================================================================*/ +void Reallocate_Big_Shape_Buffer(void); +int PASCAL WinMain ( HINSTANCE instance , HINSTANCE , char * command_line , int command_show ) +{ +#ifdef WIN32 + + RunningAsDLL = false; + //int command_show = SW_HIDE; + //HINSTANCE instance = ProgramInstance; + //char command_line[1024]; + //strcpy(command_line, command_line_in); + + Reallocate_Big_Shape_Buffer(); + + #ifndef MPEGMOVIE // Denzil 6/10/98 + //PG DDSCAPS surface_capabilities; + #endif + + /* + ** First thing to do is check if there is another instance of Red Alert already running + ** and if so, switch to the existing instance and terminate ourselves. + */ + SpawnedFromWChat = false; + + Console_Init(); + Console_Printf("Red Alert Build %s %s\n", __DATE__, __TIME__); + +#if (0)//PG + if (RA95AlreadyRunning) { //Set in the DDEServer constructor + //MessageBox (NULL, "Error - attempt to restart Red Alert 95 when already running.", "Red Alert", MB_ICONEXCLAMATION|MB_OK); + + HWND ccwindow; + ccwindow = FindWindow (WINDOW_NAME, WINDOW_NAME); + if (ccwindow) { + SetForegroundWindow ( ccwindow ); + ShowWindow ( ccwindow, SW_RESTORE ); + } + + return (EXIT_SUCCESS); + } +#endif + +#endif +//printf("in program.\n");getch(); +//printf("ram free = %ld\n",Ram_Free(MEM_NORMAL));getch(); +#ifdef WIN32 + if (Ram_Free(MEM_NORMAL) < 7000000) { +#else + + void *temp_mem = malloc (13*1024*1024); + if (temp_mem) { + free (temp_mem); + }else{ + + //if (Ram_Free(MEM_NORMAL) < 13000000) { +// if (Ram_Free(MEM_NORMAL) < 3500000) { +#endif + printf (TEXT_NO_RAM); + +#ifndef WIN32 + printf (TEXT_USE_START_MENU); + getch(); +#endif + +#if (0) + + /* + ** Take a stab at finding out how much memory there is available. + */ + + for ( int mem = 13*1024*1024 ; mem >0 ; mem -=1024 ) { + temp_mem = malloc (mem); + if (temp_mem){ + free (temp_mem); + printf ("Memory available: %d", mem); + break; + } + } + + getch(); +#endif //(0) + return(EXIT_FAILURE); + } + +#ifdef WIN32 + + if (strstr(command_line, "f:\\projects\\c&c0") != NULL || + strstr(command_line, "F:\\PROJECTS\\C&C0") != NULL) { + MessageBox(0, "Playing off of the network is not allowed.", "Red Alert", MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } + + int argc; //Command line argument count + unsigned command_scan; + char command_char; + char * argv[20]; //Pointers to command line arguments + char path_to_exe[132]; + + ProgramInstance = instance; + + /* + ** Get the full path to the .EXE + */ + GetModuleFileName (instance, &path_to_exe[0], 132); + + /* + ** First argument is supposed to be a pointer to the .EXE that is running + ** + */ + argc=1; //Set argument count to 1 + argv[0]=&path_to_exe[0]; //Set 1st command line argument to point to full path + + /* + ** Get pointers to command line arguments just like if we were in DOS + ** + ** The command line we get is cr/zero? terminated. + ** + */ + + command_scan=0; + + do { + /* + ** Scan for non-space character on command line + */ + do { + command_char = *( command_line+command_scan++ ); + } while ( command_char==' ' ); + + if ( command_char!=0 && command_char != 13 ) { + argv[argc++]=command_line+command_scan-1; + + /* + ** Scan for space character on command line + */ + bool in_quotes = false; + do { + command_char = *( command_line+command_scan++ ); + if (command_char == '"') { + in_quotes = !in_quotes; + } + } while ( (in_quotes || command_char!=' ') && command_char != 0 && command_char!=13 ); + *( command_line+command_scan-1 ) = 0; + } + + } while ( command_char != 0 && command_char != 13 && argc<20 ); + +#endif //WIN32 + + /* + ** Remember the current working directory and drive. + */ +#if (0)//PG + unsigned olddrive; + char oldpath[MAX_PATH]; + getcwd(oldpath, sizeof(oldpath)); + _dos_getdrive(&olddrive); + + /* + ** Change directory to the where the executable is located. Handle the + ** case where there is no path attached to argv[0]. + */ + char drive[_MAX_DRIVE]; + char path[_MAX_PATH]; + unsigned drivecount; + _splitpath(argv[0], drive, path, NULL, NULL); + if (!drive[0]) { + drive[0] = ('A' + olddrive)-1; + } + if (!path[0]) { + strcpy(path, "."); + } + _dos_setdrive(toupper((drive[0])-'A')+1, &drivecount); + if (path[strlen(path)-1] == '\\') { + path[strlen(path)-1] = '\0'; + } + chdir(path); +#endif + +#ifdef WOLAPI_INTEGRATION + // Look for special wolapi install program, used after the patch to version 3, to install "Shared Internet Components". + WIN32_FIND_DATA wfd; + HANDLE hWOLSetupFile = FindFirstFile( "wolsetup.exe", &wfd ); + bool bWOLSetupFile = ( hWOLSetupFile != INVALID_HANDLE_VALUE ); +// if( bWOLSetupFile ) +// debugprint( "Found wolsetup.exe\n" ); + FindClose( hWOLSetupFile ); + // Look for special registry entry that tells us when the setup exe has done its thing. + HKEY hKey; + RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ); + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WolapiInstallComplete", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) == ERROR_SUCCESS ) + { +// debugprint( "Found WolapiInstallComplete in registry\n" ); + // Setup has finished. Delete the setup exe and remove reg key. + if( bWOLSetupFile ) + { + if( DeleteFile( "wolsetup.exe" ) ) + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + else + RegDeleteValue( hKey, "WolapiInstallComplete" ); + } + RegCloseKey( hKey ); + + // I've been having problems getting the patch to delete "conquer.eng", which is present in the game + // directory for 1.08, but which must NOT be present for this version (Aftermath mix files provide the + // string overrides that the 1.08 separate conquer.eng did before Aftermath). + // Delete conquer.eng if it's found. + if( FindFirstFile( "conquer.eng", &wfd ) != INVALID_HANDLE_VALUE ) + DeleteFile( "conquer.eng" ); + +#endif + + // Load all of the XML files. + Tileset_LoadRules(); + Buildings_LoadRules(); + Units_LoadRules(); + + if (Parse_Command_Line(argc, argv)) { + +#if(TEN) + // + // Only allow the TEN version of the game to run if the TEN + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_TEN) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for TEN\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for TEN\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // TEN + +#if(MPATH) + // + // Only allow the MPATH version of the game to run if the MPATH + // or AllowSoloPlayOptions arguments are specified. + // + if (Session.AllowSolo==0 && Session.Type != GAME_MPATH) { +#ifdef WIN32 + MessageBox(NULL, "Red Alert for MPATH\n (c) 1996 Westwood Studios", + "Red Alert", MB_OK); + exit(0); +#else + printf("\n"); + printf(" Red Alert for MPATH\n"); + printf(" (c) 1996 Westwood Studios\n"); + printf("\n"); + exit(0); +#endif // WIN32 + } +#endif // MPATH + +#ifdef WIN32 + + WindowsTimer = new WinTimerClass(60, FALSE); +#if (0)//PG + int time_test = WindowsTimer->Get_System_Tick_Count(); + Sleep (1000); + if (WindowsTimer->Get_System_Tick_Count() == time_test){ + MessageBox(0, TEXT_ERROR_TIMER, TEXT_SHORT_TITLE, MB_OK|MB_ICONSTOP); + return(EXIT_FAILURE); + } +#endif +#else //WIN32 + Init_Timer_System(60, true); +#endif //WIN32 + + //////////////////////////////////////// + // The editor needs to load the Red Alert ini file from a different location than the real game. - 7/18/2019 JAS + char* red_alert_file_path = nullptr; + if (RunningFromEditor) + { + red_alert_file_path = RedAlertINI; + } + else + { + red_alert_file_path = CONFIG_FILE_NAME; + } + + RawFileClass cfile(red_alert_file_path); + //RawFileClass cfile(CONFIG_FILE_NAME); + // end of change - 7/18/2019 JAS + //////////////////////////////////////// + +#ifndef WIN32 + Install_Keyboard_Interrupt(Get_RM_Keyboard_Address(), Get_RM_Keyboard_Size()); +#endif //WIN32 + +#ifdef NEVER + Keyboard->IsLibrary = true; + if (Debug_Flag) { + Keyboard_Attributes_On(DEBUGINT); + } +#endif + + Keyboard = new KeyboardClass(); + +#ifdef WIN32 + /* + ** If there is loads of memory then use uncompressed shapes + */ + Check_Use_Compressed_Shapes(); +#endif + + /* + ** If there is not enough disk space free, don't allow the product to run. + */ + if (Disk_Space_Available() < INIT_FREE_DISK_SPACE) { + +#ifdef WIN32 +#if (0)//PG + char disk_space_message [512]; + sprintf(disk_space_message, TEXT_CRITICALLY_LOW); //PG , (INIT_FREE_DISK_SPACE) / (1024 * 1024)); + int reply = MessageBox(NULL, disk_space_message, TEXT_SHORT_TITLE, MB_ICONQUESTION|MB_YESNO); + if (reply == IDNO) { + if ( WindowsTimer ) + delete WindowsTimer; + return (EXIT_FAILURE); + } +#endif +#else + printf(TEXT_INSUFFICIENT); + printf(TEXT_MUST_HAVE, INIT_FREE_DISK_SPACE / (1024 * 1024)); + printf("\n"); + if (Keyboard) Keyboard->Get(); +// Keyboard::IsLibrary = false; + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); + return(EXIT_FAILURE); +#endif + } + + if (cfile.Is_Available()) { + + Read_Private_Config_Struct(cfile, &NewConfig); + +#ifdef WIN32 + + /* + ** Set the options as requested by the ccsetup program + */ + Read_Setup_Options( &cfile ); + + Create_Main_Window( instance , command_show , ScreenWidth , ScreenHeight ); + SoundOn = Audio_Init ( MainWindow , 16 , false , 11025*2 , 0 ); +#else //WIN32 + if (!Debug_Quiet) { + Audio_Init(NewConfig.DigitCard, + NewConfig.Port, + NewConfig.IRQ, + NewConfig.DMA, + PLAYBACK_RATE_NORMAL, +// (NewConfig.Speed) ? PLAYBACK_RATE_SLOW : PLAYBACK_RATE_NORMAL, + NewConfig.BitsPerSample, +// 4, + (Get_CPU() < 5) ? 3 : 5, +// (NewConfig.Speed) ? 3 : 5, + NewConfig.Reverse); + SoundOn = true; + } else { + Audio_Init(0, -1, -1, -1, PLAYBACK_RATE_NORMAL, 8, 5, false); + } +#endif //WIN32 + + +#ifdef WIN32 + BOOL video_success = FALSE; + + if (Set_Video_Mode (MainWindow, ScreenWidth, ScreenHeight, 8)) { + video_success = TRUE; + } + + if (!video_success) { + MessageBox(MainWindow, TEXT_VIDEO_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK); + if (WindowsTimer) delete WindowsTimer; + + return (EXIT_FAILURE); + } + + VisiblePage.Init( ScreenWidth, ScreenHeight, NULL, 0 ,(GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM) ); + HiddenPage.Init( ScreenWidth, ScreenHeight, NULL, 0 ,(GBC_Enum)(0) ); + + SeenBuff.Attach( &VisiblePage, 0, 0, 3072, 3072 ); + HidPage.Attach( &HiddenPage, 0, 0, 3072, 3072 ); + + Options.Adjust_Variables_For_Resolution(); + + /* + ** Install the memory error handler + */ + Memory_Error = &Memory_Error_Handler; + + WindowList[0][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[0][WINDOWHEIGHT] = SeenBuff.Get_Height(); + WindowList[WINDOW_EDITOR][WINDOWWIDTH] = SeenBuff.Get_Width(); + WindowList[WINDOW_EDITOR][WINDOWHEIGHT]= SeenBuff.Get_Height(); + + //////////////////////////////////////// + // The editor needs to not start the mouse up. - 7/22/2019 JAS + if (!RunningFromEditor) + { + WWMouse = new WWMouseClass(&SeenBuff, 48, 48); + MouseInstalled = TRUE; + } + + //PG CDFileClass::Set_CD_Drive (CDList.Get_First_CD_Drive()); + +#else //WIN32 + + Options.Adjust_Variables_For_Resolution(); + if (!Special.IsFromInstall) { + BlackPalette.Set(); +// Set_Palette(Palette); + Set_Video_Mode(MCGA_MODE); + } + MouseInstalled = Install_Mouse(32, 24, 320, 200); +#endif //WIN32 + + /* + ** See if we should run the intro + */ + INIClass ini; + ini.Load(cfile); + + /* + ** Check for forced intro movie run disabling. If the conquer + ** configuration file says "no", then don't run the intro. + ** Don't do this for TEN & MPath. + */ + if (!Special.IsFromInstall && Session.Type != GAME_TEN && + Session.Type != GAME_MPATH) { + Special.IsFromInstall = ini.Get_Bool("Intro", "PlayIntro", true); + } + SlowPalette = ini.Get_Bool("Options", "SlowPalette", false); + + /* + ** Regardless of whether we should run it or not, here we're + ** gonna change it to say "no" in the future. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + ini.Put_Bool("Intro", "PlayIntro", false); + ini.Save(cfile); + } + +#ifndef WOLAPI_INTEGRATION +#ifdef WIN32 + /* + ** If WChat has been trying to send us a game start packet then receive it and + ** flag that we were spawned from WCHat so we dont play the into movie. + */ + if (DDEServer.Get_MPlayer_Game_Info()){ + Check_From_WChat(NULL); + }else{ + //Check_From_WChat("C&CSPAWN.INI"); + //if (Special.IsFromWChat){ + // DDEServer.Disable(); + //} + } +#endif //WIN32 +#endif + /* + ** If the intro is being run for the first time, then don't + ** allow breaking out of it with the key. + */ + if (Special.IsFromInstall) { + BreakoutAllowed = true; +// BreakoutAllowed = false; + } + + Memory_Error_Exit = Print_Error_End_Exit; + + Main_Game(argc, argv); + + #ifdef MPEGMOVIE // Denzil 6/15/98 + if (MpgSettings != NULL) + delete MpgSettings; + + #ifdef MCIMPEG + if (MciMovie != NULL) + delete MciMovie; + #endif + #endif + + VisiblePage.Clear(); + HiddenPage.Clear(); + + Memory_Error_Exit = Print_Error_Exit; + + /* + ** Flag that this is a clean shutdown (not killed with Ctrl-Alt-Del) + */ + ReadyToQuit = 1; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + + UserInput.Process_Input(); + } while (ReadyToQuit == 1); + + return (EXIT_SUCCESS); + } else { + if (!RunningFromEditor) + { + puts(TEXT_SETUP_FIRST); + Keyboard->Get(); + } + } + +#ifdef WIN32 + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + +#else //WIN32 + Remove_Keyboard_Interrupt(); + Remove_Timer_System(); +#endif //WIN32 + } + /* + ** Restore the current drive and directory. + */ +#ifndef WIN32 + _dos_setdrive(olddrive, &drivecount); + chdir(oldpath); +#endif //WIN32 + return(EXIT_SUCCESS); +} + + +/* Initialize DirectDraw and surfaces */ +bool InitDDraw(void) +{ + + VisiblePage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + HiddenPage.Init(ScreenWidth, ScreenHeight, NULL, 0, (GBC_Enum)GBC_VIDEOMEM); + + SeenBuff.Attach(&VisiblePage, 0, 0, 3072, 3072); + HidPage.Attach(&HiddenPage, 0, 0, 3072, 3072); + + return true; +} + + +/*********************************************************************************************** + * Prog_End -- Cleans up library systems in prep for game exit. * + * * + * This routine should be called before the game terminates. It handles cleaning up * + * library systems so that a graceful return to the host operating system is achieved. * + * * + * INPUT: why - text description of exit reason * + * fatal - indicates a fatal error * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1995 JLB : Created. * + * 08/07/2019 ST : Added why and fatal params * + *=============================================================================================*/ +#ifdef WIN32 +void __cdecl Prog_End(const char *why, bool fatal) +{ + GlyphX_Debug_Print("Prog_End()"); + + if (why) { + GlyphX_Debug_Print(why); + } + if (fatal) { + *((int*)0) = 0; + } + + Sound_End(); + if (WWMouse) { + delete WWMouse; + WWMouse = NULL; + } + if (WindowsTimer) { + delete WindowsTimer; + WindowsTimer = NULL; + } + + ProgEndCalled = true; +} +#else //WIN32 + +void Prog_End(void) +{ + if (Session.Type == GAME_MODEM || Session.Type == GAME_NULL_MODEM) { + NullModem.Change_IRQ_Priority(0); + } + + Set_Video_Mode(RESET_MODE); + Remove_Keyboard_Interrupt(); + Remove_Mouse(); + Sound_End(); + Remove_Timer_System(); +} +#endif //WIN32 + + +void Print_Error_End_Exit(char * string) +{ + Prog_End(string, true); + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + +void Print_Error_Exit(char * string) +{ + GlyphX_Debug_Print(string); + + printf( "%s\n", string ); + if (!RunningAsDLL) { + exit(1); + } +} + + + + + + + +/*********************************************************************************************** + * Emergency_Exit -- Function to call when we want to exit unexpectedly. * + * Use this function instead of exit(n) so everything is properly cleaned up.* + * * + * * + * INPUT: Code to return to the OS * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/13/97 1:32AM ST : Created * + *=============================================================================================*/ +void Emergency_Exit ( int code ) +{ + if (RunningAsDLL) { //PG + return; + } + +#ifdef WIN32 + + /* + ** Clear out the video buffers so we dont glitch when we lose focus + */ + VisiblePage.Clear(); + HiddenPage.Clear(); + BlackPalette.Set(); + Memory_Error_Exit = Print_Error_Exit; + + /* + ** Flag that this is an emergency shut down - not a clean shutdown but + ** not killed with Ctrl-Alt-Del either. + */ + ReadyToQuit = 3; + + /* + ** Post a message to our message handler to tell it to clean up. + */ + PostMessage(MainWindow, WM_DESTROY, 0, 0); + + /* + ** Wait until the message handler has dealt with the message + */ + do + { + Keyboard->Check(); + + g_globalKeyNumType = KN_NONE; + g_globalKeyFlags = 0; + UserInput.Process_Input(); + } while (ReadyToQuit == 3); + + +#else //WIN32 + /* + ** Do the DOS end + */ + Prog_End(); + +#endif //WIN32 + + exit ( code ); +} + + + + + + + + + + + +#ifdef WIN32 + +/*********************************************************************************************** + * Read_Setup_Options -- Read stuff in from the INI file that we need to know sooner * + * * + * * + * * + * INPUT: Ptr to config file class * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 4:09PM ST : Created * + * 09/30/1996 JLB : Uses INI class. * + *=============================================================================================*/ +void Read_Setup_Options( RawFileClass *config_file ) +{ + if (config_file->Is_Available()) { + + INIClass ini; + + ini.Load(*config_file); + + /* + ** Read in the boolean options + */ + VideoBackBufferAllowed = ini.Get_Bool("Options", "VideoBackBuffer", true); + AllowHardwareBlitFills = ini.Get_Bool("Options", "HardwareFills", true); + + ScreenWidth = 1280; + ScreenHeight = 720; + + /* + ** See if an alternative socket number has been specified + */ + int socket = ini.Get_Int("Options", "Socket", 0); + if (socket >0 ) { + socket += 0x4000; + if (socket >= 0x4000 && socket < 0x8000) { + Ipx.Set_Socket (socket); + } + } + + /* + ** See if a destination network has been specified + */ + char netbuf [512]; + memset(netbuf, 0, sizeof(netbuf)); + char * netptr = netbuf; + bool found = ini.Get_String("Options", "DestNet", NULL, netbuf, sizeof(netbuf)); + + if (found && netptr != NULL && strlen(netbuf)) { + NetNumType net; + NetNodeType node; + + /* + ** Scan the string, pulling off each address piece + */ + int i = 0; + char * p = strtok(netbuf,"."); + int x; + while (p != NULL) { + sscanf(p,"%x",&x); // convert from hex string to int + if (i < 4) { + net[i] = (char)x; // fill NetNum + } else { + node[i-4] = (char)x; // fill NetNode + } + i++; + p = strtok(NULL,"."); + } + + /* + ** If all the address components were successfully read, fill in the + ** BridgeNet with a broadcast address to the network across the bridge. + */ + if (i >= 4) { + Session.IsBridge = 1; + memset(node, 0xff, 6); + Session.BridgeNet = IPXAddressClass(net, node); + } + } + } +} + +void Get_OS_Version (void) +{ + //PG + //WindowsNT = ((GetVersion() & 0x80000000) == 0) ? true : false; + +#if (0) + OSVERSIONINFO osversion; + if ( GetVersionEx (&osversion) ){ + WindowsNT = (osversion.dwPlatformId == VER_PLATFORM_WIN32_NT) ? true : false; + OutputDebugString ("RA95 - Got OS version\n"); + }else{ + OutputDebugString ("RA95 - Failed to get OS version\n"); + char debugstr [128]; + sprintf (debugstr, "RA95 - Error code is %d\n", GetLastError()); + OutputDebugString (debugstr); + + } +#endif //(0) + +} + + +#endif //WIN32 \ No newline at end of file diff --git a/code/redalert/statbtn.cpp b/code/redalert/statbtn.cpp new file mode 100644 index 0000000..94ce2fd --- /dev/null +++ b/code/redalert/statbtn.cpp @@ -0,0 +1,270 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STATBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "statbtn.h" + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(unsigned , char const * text, TextPrintType style, int x, int y, int w, int h) : + GadgetClass(x, y, w, h, FlagEnum(0)), + String(NULL), + PrintFlags(style) +{ + /* + ** Make a duplicate of the string to display. + */ + Set_Text(text, false); + + if (w == -1 || h == -1) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String); + } + if (h == -1) { + Height = FontHeight; + } + } +} + + +/*********************************************************************************************** + * StaticButtonClass::StaticButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +StaticButtonClass::StaticButtonClass(void) : + GadgetClass(0, 0, 0, 0, FlagEnum(0)), + String(NULL), + PrintFlags(TPF_8POINT) +{ +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int StaticButtonClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * StaticButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Set_Text(char const * text, bool resize) +{ + if (String != NULL) { + delete [] String; + String = NULL; + } + + if (text != NULL) { + String = new char[strlen(text)+1]; + if (String != NULL) { + strcpy(String, text); + } + } + + Flag_To_Redraw(); + if (resize && String != NULL) { + Draw_Background(); + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String); + Height = FontHeight + FontYSpacing; + Background = Buffer(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Background(void) +{ + /* + ** If the background hasn't been recorded from the buffer, then + ** allocate and record the background image now. + */ + if (Background.Get_Buffer() == NULL && Width > 0 && Height > 0) { + new(&Background) Buffer(Width*Height); + if (Background.Get_Buffer() != NULL) { + LogicPage->To_Buffer(X, Y, Width, Height, Background, Background.Get_Size()); + } + } + + /* + ** If there is a background image present, then restore it to the buffer now. + */ + if (Background.Get_Buffer() != NULL && LogicPage->Lock()) { + Buffer_To_Page(X, Y, Width, Height, Background, *LogicPage); + LogicPage->Unlock(); + } +} + + +/*********************************************************************************************** + * StaticButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void StaticButtonClass::Draw_Text(char const * text) +{ + /* + ** Display the text. + */ + if (String != NULL) { + int x = X; + + if (PrintFlags & TPF_CENTER) { + x += Width/2; + } + if (PrintFlags & TPF_RIGHT) { + x += Width-1; + } + + Fancy_Text_Print(text, x, Y, GadgetClass::Get_Color_Scheme(), TBLACK, PrintFlags); + } +} diff --git a/code/redalert/statbtn.h b/code/redalert/statbtn.h new file mode 100644 index 0000000..d793d90 --- /dev/null +++ b/code/redalert/statbtn.h @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STATBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STATBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STATBTN_H +#define STATBTN_H + +#include "gadget.h" +#include "buff.h" + +class StaticButtonClass : public GadgetClass +{ + public: + StaticButtonClass(void); + StaticButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const * text, bool resize = false); + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + /* + ** If a background is to be preserved for this button, then this will point to + ** a buffer that holds a pristine background image. + */ + Buffer Background; + + /* + ** This points to a copy of the string that is used for the button's text. + */ + char * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; +}; + +#endif + diff --git a/code/redalert/stats.cpp b/code/redalert/stats.cpp new file mode 100644 index 0000000..536e7a4 --- /dev/null +++ b/code/redalert/stats.cpp @@ -0,0 +1,911 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 05/29/1996 * + * * + * Last Update : May 29th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Internet game statistics to collect and upload to the server * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" +#include "packet.h" +#include "ccdde.h" + +#define FIELD_PACKET_TYPE "TYPE" +#define FIELD_GAME_ID "IDNO" +#define FIELD_START_CREDITS "CRED" +#define FIELD_BASES "BASE" +#define FIELD_TIBERIUM "TIBR" +#define FIELD_CRATES "CRAT" +#define FIELD_AI_PLAYERS "AIPL" +#define FIELD_CAPTURE_THE_FLAG "FLAG" +#define FIELD_START_UNIT_COUNT "UNIT" +#define FIELD_TECH_LEVEL "TECH" +#define FIELD_SCENARIO "SCEN" +#define FIELD_COMPLETION "CMPL" +#define FIELD_START_TIME "TIME" +#define FIELD_GAME_DURATION "DURA" +#define FIELD_FRAME_RATE "AFPS" +#define FIELD_SPEED_SETTING "SPED" +#define FIELD_GAME_VERSION "VERS" +#define FIELD_GAME_BUILD_DATE "DATE" +#define FIELD_COVERT_PRESENT "COVT" +#define FIELD_CPU_TYPE "PROC" +#define FIELD_MEMORY "MEMO" +#define FIELD_VIDEO_MEMORY "VIDM" +#define FIELD_SHADOW_REGROWS "SHAD" + +#ifdef WOLAPI_INTEGRATION +#define FIELD_HOSTORNOT "SDFX" +#define FIELD_TOURNAMENT "TRNY" +#define FIELD_NUM_INITIAL_PLAYERS "NUMP" +#define FIELD_NUM_REMAINING_PLAYERS "REMN" +#define FIELD_DISCONNECT_PINGS "PING" +#define FIELD_COMPUTERTOOKOVER "QUIT" +//#define FIELD_HARDWARE_GUID "GUID" +#define FIELD_PLAYER1_IP "ADR1" +#define FIELD_PLAYER2_IP "ADR2" +#endif + +// ajw The following were never used (thank god). +#define FIELD_PLAYER1_HANDLE "NAM1" +#define FIELD_PLAYER2_HANDLE "NAM2" +#define FIELD_PLAYER1_TEAM "SID1" +#define FIELD_PLAYER2_TEAM "SID2" +#define FIELD_PLAYER1_COLOR "COL1" +#define FIELD_PLAYER2_COLOR "COL2" +#define FIELD_PLAYER1_CREDITS "CRD1" +#define FIELD_PLAYER2_CREDITS "CRD2" + +#define FIELD_PLAYER1_UNITS_LEFT "UNL1" +#define FIELD_PLAYER2_UNITS_LEFT "UNL2" +#define FIELD_PLAYER1_INFANTRY_LEFT "INL1" +#define FIELD_PLAYER2_INFANTRY_LEFT "INL2" +#define FIELD_PLAYER1_PLANES_LEFT "PLL1" +#define FIELD_PLAYER2_PLANES_LEFT "PLL2" +#define FIELD_PLAYER1_BUILDINGS_LEFT "BLL1" +#define FIELD_PLAYER2_BUILDINGS_LEFT "BLL2" +#define FIELD_PLAYER1_VESSELS_LEFT "VSL1" +#define FIELD_PLAYER2_VESSELS_LEFT "VSL2" + +#define FIELD_PLAYER1_UNITS_BOUGHT "UNB1" +#define FIELD_PLAYER2_UNITS_BOUGHT "UNB2" +#define FIELD_PLAYER1_INFANTRY_BOUGHT "INB1" +#define FIELD_PLAYER2_INFANTRY_BOUGHT "INB2" +#define FIELD_PLAYER1_PLANES_BOUGHT "PLB1" +#define FIELD_PLAYER2_PLANES_BOUGHT "PLB2" +#define FIELD_PLAYER1_BUILDINGS_BOUGHT "BLB1" +#define FIELD_PLAYER2_BUILDINGS_BOUGHT "BLB2" +#define FIELD_PLAYER1_VESSELS_BOUGHT "VSB1" +#define FIELD_PLAYER2_VESSELS_BOUGHT "VSB2" + +#define FIELD_PLAYER1_UNITS_KILLED "UNK1" +#define FIELD_PLAYER2_UNITS_KILLED "UNK2" +#define FIELD_PLAYER1_INFANTRY_KILLED "INK1" +#define FIELD_PLAYER2_INFANTRY_KILLED "INK2" +#define FIELD_PLAYER1_PLANES_KILLED "PLK1" +#define FIELD_PLAYER2_PLANES_KILLED "PLK2" +#define FIELD_PLAYER1_BUILDINGS_KILLED "BLK1" +#define FIELD_PLAYER2_BUILDINGS_KILLED "BLK2" +#define FIELD_PLAYER1_VESSELS_KILLED "VSK1" +#define FIELD_PLAYER2_VESSELS_KILLED "VSK2" + +#define FIELD_PLAYER1_BUILDINGS_CAPTURED "BLC1" +#define FIELD_PLAYER2_BUILDINGS_CAPTURED "BLC2" + +#define FIELD_PLAYER1_CRATES_FOUND "CRA1" +#define FIELD_PLAYER2_CRATES_FOUND "CRA2" +#define FIELD_PLAYER1_HARVESTED "HRV1" +#define FIELD_PLAYER2_HARVESTED "HRV2" + + +#define PACKET_TYPE_HOST_GAME_INFO (unsigned char) 50 +#define PACKET_TYPE_GUEST_GAME_INFO (unsigned char) 51 + +// Note: These enums match those in the game results server code. +enum { + COMPLETION_CONNECTION_LOST, + COMPLETION_PLAYER_1_WON, + COMPLETION_PLAYER_1_WON_BY_RESIGNATION, + COMPLETION_PLAYER_1_WON_BY_DISCONNECTION, + COMPLETION_PLAYER_2_WON, + COMPLETION_PLAYER_2_WON_BY_RESIGNATION, + COMPLETION_PLAYER_2_WON_BY_DISCONNECTION, +#ifdef FIXIT_VERSION_3 // Stalemate games. + COMPLETION_WASH = 64, +#endif +}; + + +extern unsigned long PlanetWestwoodGameID; +extern HINSTANCE ProgramInstance; +extern unsigned long PlanetWestwoodStartTime; + +extern "C" char CPUType; + + +bool GameTimerInUse = false; +TimerClass GameTimer; +long GameEndTime; +void *PacketLater = NULL; + +//#include "WolDebug.h" + +#ifdef WOLAPI_INTEGRATION +#include "WolapiOb.h" +extern WolapiObject* pWolapi; + +extern bool bReconnectDialogCancelled; +#endif + +/*********************************************************************************************** + * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/29/96 12:38PM ST : Created * + *=============================================================================================*/ + +void Send_Statistics_Packet(void) +{ +#if (0)//PG +// debugprint( "Stats: Send_Statistics_Packet() called.\n" ); +#ifndef INTERNET_OFF // Denzil 5/4/98 + +#ifdef WOLAPI_INTEGRATION + if( !pWolapi ) // Should no longer ever happen. + return; +#endif + + PacketClass stats; + HouseClass *player; + static int packet_size; + int index; + bool packet_later = false; // Should the packet be sent later + void *packet; + + static char field_player_handle[5] = { "NAM?" }; + static char field_player_team[5] = { "SID?" }; + static char field_player_color[5] = { "COL?" }; + static char field_player_credits[5] = { "CRD?" }; + static char field_player_units_left[5] = { "UNL?" }; + static char field_player_infantry_left[5] = { "INL?" }; + static char field_player_planes_left[5] = { "PLL?" }; + static char field_player_buildings_left[5] = { "BLL?" }; + static char field_player_vessels_left[5] = { "VSL?" }; + static char field_player_units_bought[5] = { "UNB?" }; + static char field_player_infantry_bought[5] = { "INB?" }; + static char field_player_planes_bought[5] = { "PLB?" }; + static char field_player_buildings_bought[5] = { "BLB?" }; + static char field_player_vessels_bought[5] = { "VSB?" }; + static char field_player_units_killed[5] = { "UNK?" }; + static char field_player_infantry_killed[5] = { "INK?" }; + static char field_player_planes_killed[5] = { "PLK?" }; + static char field_player_buildings_killed[5] = { "BLK?" }; + static char field_player_vessels_killed[5] = { "VSK?" }; + static char field_player_buildings_captured[5] = { "BLC?" }; + static char field_player_crates_found[5] = { "CRA?" }; + static char field_player_harvested[5] = { "HRV?" }; + + static char *houses[] = { + "SPA", + "GRE", + "USS", + "ENG", + "ITA", + "GER", + "FRA", + "TKY", + "GUD", + "BAD", + "CIV", + "JP ", + "M01", + "M02", + "M03", + "M04", + "M05", + "M06", + "M07", + "M08" + }; + + if (!PacketLater){ + + /* + ** Field to identify this as C&C 95 internet game statistics packet + */ +#ifdef WOLAPI_INTEGRATION + if( pWolapi->bGameServer ) + { + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)0 ); // Reversed meaning of this for Neal. + }else{ + stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)1 ); + } +#else + if (Server){ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO); + }else{ + stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO); + } +#endif + + /* + ** Game ID. A unique game identifier assigned by WChat. + */ + stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID); + +#ifdef WOLAPI_INTEGRATION + + // Number of players initially in game. + stats.Add_Field( FIELD_NUM_INITIAL_PLAYERS, (unsigned long)pWolapi->GameInfoCurrent.iPlayerCount ); +//debugprint( "Stats: number of initial players is %i\n", pWolapi->GameInfoCurrent.iPlayerCount ); + + // Number of players remaining in game. Not sure of what use this will be statistically... + stats.Add_Field( FIELD_NUM_REMAINING_PLAYERS, (unsigned long)Session.Players.Count() ); +//debugprint( "Stats: number of remaining players is %i\n", Session.Players.Count() ); + + // Whether or not this was a tournament game. + stats.Add_Field( FIELD_TOURNAMENT, (unsigned char)( pWolapi->GameInfoCurrent.bTournament ? 1 : 0 ) ); + +// ajw This is now in WOLAPI... +// // A unique value that identifies the machine that the game was played on. +// HW_PROFILE_INFO hwinfo; +// ::GetCurrentHwProfile( &hwinfo ); +// stats.Add_Field( FIELD_HARDWARE_GUID, hwinfo.szHwProfileGuid ); +#endif + + /* + ** Start credits. + */ + stats.Add_Field(FIELD_START_CREDITS, (unsigned long)Session.Options.Credits); + + /* + ** Bases (On/Off) + */ + stats.Add_Field(FIELD_BASES, Session.Options.Bases ? "ON" : "OFF"); + + /* + ** Tiberium (On/Off) + */ + stats.Add_Field(FIELD_TIBERIUM, Session.Options.Tiberium ? "ON" : "OFF"); + + /* + ** Crates (On/Off) + */ + stats.Add_Field(FIELD_CRATES, Session.Options.Goodies ? "ON" : "OFF"); + + /* + ** AI Players (On/Off) + */ + stats.Add_Field(FIELD_AI_PLAYERS, (unsigned long) Session.Options.AIPlayers); + + /* + ** Shadow regrowth enabled + */ + stats.Add_Field(FIELD_SHADOW_REGROWS, Special.IsShadowGrow ? "ON" : "OFF" ); + + /* + ** Capture the flag mode (On/Off) + */ + stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF"); + + /* + ** Start unit count + */ + stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)Session.Options.UnitCount); + + /* + ** Tech level. + */ + stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel); + + /* + ** Scenario + */ +#if (1) + + stats.Add_Field(FIELD_SCENARIO, Session.Options.ScenarioDescription); + +#else //(1) + char fname[128]; + char namebuffer[40]; + char *abuffer = (char *)_ShapeBuffer; + memset(abuffer, '\0', _ShapeBufferSize); + sprintf(fname,"%s.INI",Scen.ScenarioName); + CCFileClass fileo; + fileo.Set_Name (fname); + fileo.Read(abuffer, _ShapeBufferSize-1); + fileo.Close(); + WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer); + stats.Add_Field(FIELD_SCENARIO, namebuffer); + //stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]); +#endif //(1) + +#ifdef WOLAPI_INTEGRATION + // Completion status is set for Tournament games only - ajw. + if( pWolapi->GameInfoCurrent.bTournament ) + { +#endif + + /* + ** Game completion status. + ** + ** Connection lost. + ** Player 1 won + ** Player 2 won + ** Player 1 won by resignation + ** Player 2 won by resignation + ** Player 1 aborted + ** Player 2 aborted + ** + ** Game was a draw + */ + HouseClass *player1 = NULL; + HouseClass *player2 = NULL; + for (int h=0 ; hIsHuman){ + if (player1){ + player2 = ptr; + break; + }else{ + player1 = ptr; + } + } + } + + int completion = -1; + + if (player1 && player2){ // Can this ever fail? ajw +#ifdef FIXIT_VERSION_3 + // Send IP addresses of both players. + NetNumType net; + NetNodeType node; + char szIPAddress[ 30 ]; + Session.Players[ 0 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + if( strcmp( szIPAddress, "255.255.255.255" ) == 0 ) + { + + // Ok. It's not set. Let's try to get it ourselves... + char szHostName[ 512 ]; + int iRes = gethostname( szHostName, 512 ); + if( iRes != SOCKET_ERROR ) // else forget about trying + { +// debugprint( "gethostname got me %s\n", szHostName ); + struct hostent* pHostent = gethostbyname( szHostName ); + if( pHostent ) // else forget about trying + { + int i = 0; + int* piAddress = (int*)pHostent->h_addr_list[ i ]; + while( piAddress ) + { + // There is a non-null value for this h_addr_list entry. + char szAsciiIP[ 30 ]; + strcpy( szAsciiIP, inet_ntoa( *( (struct in_addr*)piAddress ) ) ); + // We have an address in the right form. + // Now, is it an address in a private network? If so we should ignore it. + unsigned char q1 = ( (char*)piAddress )[ 0 ]; // First digit. + unsigned char q2 = ( (char*)piAddress )[ 1 ]; // Second digit. +// debugprint( "ip: %s\n", szAsciiIP ); + if( q1 == 10 || ( q1 == 172 && ( q2 >= 16 && q2 <= 31 ) ) || ( q1 == 192 && q2 == 168 ) ) + { + // This is a private network address - ignore it and go on to next. + } + else + { + strcpy( szIPAddress, szAsciiIP ); + break; + } + piAddress = (int*)pHostent->h_addr_list[ ++i ]; + } + } +// else +// debugprint( "gethostbyname failed. Error %i\n", WSAGetLastError() ); + } +// else +// debugprint( "gethostname failed with %i, error %i\n", iRes, WSAGetLastError() ); + } + stats.Add_Field( FIELD_PLAYER1_IP, (char*)szIPAddress ); + Session.Players[ 1 ]->Address.Get_Address( net, node ); + sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] ); + stats.Add_Field( FIELD_PLAYER2_IP, (char*)szIPAddress ); +#endif +#ifdef FIXIT_VERSION_3 // Stalemate games. + if( Scen.bLocalProposesDraw && Scen.bOtherProposesDraw ) + { + completion = COMPLETION_WASH; + } + else + { +#endif + if (ConnectionLost){ +#ifdef WOLAPI_INTEGRATION + if( bReconnectDialogCancelled ) + { + if( Session.Players[ 0 ]->Player.ID == HOUSE_MULTI1 ) + // I am player1. + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + else + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + else + { + completion = COMPLETION_CONNECTION_LOST; + if( pWolapi->bDisconnectPingingCompleted ) + { + char szPingResult[ 8 ]; // Format is "x/y a/b", e.g., "3/5 4/5" + pWolapi->DisconnectPingResultsString( szPingResult ); + stats.Add_Field( FIELD_DISCONNECT_PINGS, (char*)szPingResult ); + } +// else +// debugprint( "Stats: bDisconnectPingingCompleted is false! Should be finished!!!!!!!!!!!!!!!\n" ); + } +#else + completion = COMPLETION_CONNECTION_LOST; +#endif + }else{ + + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + + + if (player2->IsDefeated){ + /* + ** Player 1 won. Find out how. + */ + completion = COMPLETION_PLAYER_1_WON; + if (player2->IsResigner){ + completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION; + }else{ + if (player2->IsGiverUpper){ + completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION; + } + } + + + }else{ + + if (player1->IsDefeated){ + /* + ** Player 2 won. Find out how. + */ + completion = COMPLETION_PLAYER_2_WON; + if (player1->IsResigner){ + completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION; + }else{ + if (player1->IsGiverUpper){ + completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION; + } + } + } + } + } +#ifdef FIXIT_VERSION_3 // Stalemate games. + } +#endif + } + + stats.Add_Field (FIELD_COMPLETION, (char) completion); +//debugprint( "Stats: Tournament game completion value: %i\n", completion ); + +#ifdef WOLAPI_INTEGRATION + } +#endif + + + /* + ** Game start time (GMT or Pacific?) + ** + ** Passed from WChat + */ + stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime); + + /* + ** Game duration (seconds). + */ + stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60); + + /* + ** Avg. frame rate. + */ +#ifdef FIXIT_IP_CRASH + long divisor = GameEndTime / 60; + if (divisor != 0) { + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); + } else { + stats.Add_Field (FIELD_FRAME_RATE, 0l); + } +#else + stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) ); +#endif + + + /* + ** CPU type + */ + stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType); + + /* + ** Memory + */ + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys); + + /* + ** Video memory + */ + DDCAPS video_capabilities; + long video_memory; + + if (DirectDrawObject){ + video_capabilities.dwSize = sizeof (video_capabilities); + if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){ + video_memory = video_capabilities.dwVidMemTotal; + video_memory += 1024*1024 -1; + video_memory &= 0xfff00000; + stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory); + } + } + + /* + ** Game speed setting. + */ + stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed); + + /* + ** Red Alert version/build date + */ + char version[128]; + sprintf (version, "V%s", VerNum.Version_Name() ); + stats.Add_Field (FIELD_GAME_VERSION, (char*)version); + + char path_to_exe[280]; + FILETIME write_time; //File time is 64 bits + + GetModuleFileName (ProgramInstance, path_to_exe, 280); + RawFileClass file; + file.Set_Name(path_to_exe); + file.Open(); + HANDLE handle = file.Get_File_Handle(); + + if (handle != INVALID_HANDLE_VALUE){ + if (GetFileTime (handle, NULL, NULL, &write_time)){ + write_time.dwLowDateTime = htonl (write_time.dwLowDateTime); + write_time.dwHighDateTime = htonl (write_time.dwHighDateTime); + stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time)); + } + } + + /* + ** Covert installed? (Yes/No) + */ + //stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present()); + + /* + ** Build the player specific statistics + ** + */ +#ifdef WOLAPI_INTEGRATION + for (int house = 0 ; house < 8 ; house++){ +#else + for (int house = 0 ; house < 2 ; house++){ +#endif + player = HouseClass::As_Pointer((HousesType) (house + HOUSE_MULTI1)); + +#ifdef WOLAPI_INTEGRATION + if( !player ) + continue; +#endif + + /* + ** Player handle. + */ + field_player_handle[3] = '1' + (char)house; +#ifdef WOLAPI_INTEGRATION + stats.Add_Field (field_player_handle, (char*) player->InitialName); +//debugprint( "Stats: Player %i name %s\n", house, (char*) player->InitialName ); +//debugprint( "Stats: Player %i ending name %s\n", house, (char*) player->IniName ); +#else + stats.Add_Field (field_player_handle, (char*) player->IniName); +#endif + +#ifdef WOLAPI_INTEGRATION + // Whether or not this player was taken over by the computer, due to his quitting the game. + if( strcmp( player->IniName, player->InitialName ) ) + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)1 ); + else + stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)0 ); +#endif + /* + ** Player team. (NOD or GDI) + */ + field_player_team[3] = '1' + (char)house; + stats.Add_Field (field_player_team, houses[player->ActLike]); + + /* + ** Player color + */ + field_player_color[3] = '1' + (char)house; + stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1)); + + /* + ** Player end credits. + */ + field_player_credits[3] = '1' + (char)house; + stats.Add_Field (field_player_credits, player->Credits + player->Tiberium); + + /* + ** Number of each unit/building type built + */ + field_player_infantry_bought[3] = '1' + (char)house; + field_player_units_bought[3] = '1' + (char)house; + field_player_planes_bought[3] = '1' + (char)house; + field_player_buildings_bought[3] = '1' + (char)house; + field_player_vessels_bought[3] = '1' + (char)house; + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_bought, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + player->InfantryTotals->To_PC_Format(); + player->UnitTotals->To_PC_Format(); + player->AircraftTotals->To_PC_Format(); + player->BuildingTotals->To_PC_Format(); + + /* + ** Clear out the counts and use the space to count up the current number of units/buildings + */ + player->InfantryTotals->Clear_Unit_Total(); + player->AircraftTotals->Clear_Unit_Total(); + player->UnitTotals->Clear_Unit_Total(); + player->BuildingTotals->Clear_Unit_Total(); + player->VesselTotals->Clear_Unit_Total(); + + /* + ** Number of units remaining to player + */ + for (index = 0; index < Units.Count(); index++) { + UnitClass const * unit = Units.Ptr(index); + if (player == unit->House){ + player->UnitTotals->Increment_Unit_Total (unit->Class->Type); + } + } + + for (index = 0; index < Infantry.Count(); index++) { + InfantryClass const * infantry = Infantry.Ptr(index); + if (player == infantry->House && !infantry->Class->IsCivilian){ + player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type); + } + } + + for (index = 0; index < Aircraft.Count(); index++) { + AircraftClass const * aircraft = Aircraft.Ptr(index); + if (player == aircraft->House){ // && aircraft->Class->Type != AIRCRAFT_CARGO){ + player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type); + } + } + + for (index = 0; index < Buildings.Count(); index++) { + BuildingClass const * building = Buildings.Ptr(index); + if (player == building->House){ + player->BuildingTotals->Increment_Unit_Total (building->Class->Type); + } + } + + for (index = 0; index < Vessels.Count(); index++) { + VesselClass const * vessel = Vessels.Ptr(index); + if (player == vessel->House){ + player->VesselTotals->Increment_Unit_Total (vessel->Class->Type); + } + } + + + player->InfantryTotals->To_Network_Format(); + player->UnitTotals->To_Network_Format(); + player->AircraftTotals->To_Network_Format(); + player->BuildingTotals->To_Network_Format(); + player->VesselTotals->To_Network_Format(); + + field_player_infantry_left[3] = '1' + (char)house; + field_player_units_left[3] = '1' + (char)house; + field_player_planes_left[3] = '1' + (char)house; + field_player_buildings_left[3] = '1' + (char)house; +#ifdef FIXIT_IP_STATS + field_player_vessels_left[3] = '1' + (char)house; +#endif + stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4); + stats.Add_Field (field_player_vessels_left, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4); + + + /* + ** Number of enemy units/buildings of each type destroyed. + */ + + player->DestroyedInfantry->To_Network_Format(); + player->DestroyedUnits->To_Network_Format(); + player->DestroyedAircraft->To_Network_Format(); + player->DestroyedBuildings->To_Network_Format(); + player->DestroyedVessels->To_Network_Format(); + + field_player_infantry_killed[3] = '1' + (char)house; + field_player_units_killed[3] = '1' + (char)house; + field_player_planes_killed[3] = '1' + (char)house; + field_player_buildings_killed[3] = '1' + (char)house; + field_player_vessels_killed[3] = '1' + (char)house; + + stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4); + stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4); + stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4); + stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#ifdef FIXIT_VERSION_3 + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedVessels->Get_Unit_Count()*4); +#else + stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4); +#endif + + + /* + ** Number and type of enemy buildings captured + */ + field_player_buildings_captured[3] = '1' + (char)house; + player->CapturedBuildings->To_Network_Format(); + stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4); + + /* + ** Number of crates discovered and their contents + */ + field_player_crates_found[3] = '1' + (char)house; + player->TotalCrates->To_Network_Format(); + stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4); + + /* + ** Amount of tiberium turned into credits + */ + field_player_harvested[3] = '1' + (char)house; + stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits); + + } + + /* + ** Create the comms packet to be sent + */ + packet = stats.Create_Comms_Packet(packet_size); + +#ifndef WOLAPI_INTEGRATION // ajw - 'PacketLater' is no longer ever used. + /* + ** If a player disconnected then dont send the packet at this time - save it for later + */ + if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION + || completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){ + PacketLater = packet; + return; + } +#endif + + }else{ //else for if (!PacketLater) + + /* + ** Send the packet we calculated earlier when the disconnect occurred + */ + packet = PacketLater; + PacketLater = NULL; + } + + /* + ** Send it..... + */ + const char* szGameResServer; + int iPort; + if( pWolapi->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + szGameResServer = pWolapi->szGameResServerHost2; + iPort = pWolapi->iGameResServerPort2; + } + else + { + szGameResServer = pWolapi->szGameResServerHost1; + iPort = pWolapi->iGameResServerPort1; + } + + if( *szGameResServer ) + { + if( pWolapi->pNetUtil->RequestGameresSend( szGameResServer, iPort, (unsigned char*)packet, packet_size ) != S_OK ) + //debugprint( "RequestGameresSend( %s, %i ) failed!!!\n", szGameResServer, iPort ); + ; + } + + /* + ** Save it to disk as well so I can see it + */ +// RawFileClass anotherfile ("packet.net"); +// anotherfile.Write(packet, packet_size); +//debugprint( "Wrote out packet.net\n" ); + + /* + ** Tidy up + */ + delete [] packet; + + GameStatisticsPacketSent = true; +#endif // INTERNET_OFF +#endif +} + + + +void Register_Game_Start_Time(void) +{ + + GameTimer.Set (0, true); + GameTimerInUse = true; +} + +extern void Register_Game_End_Time(void) +{ + GameEndTime = GameTimer.Time(); + GameTimerInUse = false; +} + + +#endif //WIN32 diff --git a/code/redalert/straw.cpp b/code/redalert/straw.cpp new file mode 100644 index 0000000..9dd6783 --- /dev/null +++ b/code/redalert/straw.cpp @@ -0,0 +1,138 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STRAW.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Straw::Get_From -- Connect one straw segment to another. * + * Straw::Get -- Fetch some data from the straw chain. * + * Straw::~Straw -- Destructor for a straw segment. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "straw.h" +#include +#include + + +/*********************************************************************************************** + * Straw::~Straw -- Destructor for a straw segment. * + * * + * This destructor will remove this segment from the straw chain. If there were any * + * connected segments to either side, they will be joined so that the straw chain will * + * still remain linked. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +Straw::~Straw(void) +{ + if (ChainTo != NULL) { + ChainTo->ChainFrom = ChainFrom; + } + if (ChainFrom != NULL) { + ChainFrom->Get_From(ChainTo); + } + + ChainFrom = NULL; + ChainTo = NULL; +} + + +/*********************************************************************************************** + * Straw::Get_From -- Connect one straw segment to another. * + * * + * Use this routine to connect straw segments together. The straw segment specified as the * + * parameter will be linked to the chain such that when data is requested, it will be * + * fetched from the specified link before being processed by this link. * + * * + * INPUT: straw -- Pointer to the straw segment that data will be fetched from. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void Straw::Get_From(Straw * straw) +{ + if (ChainTo != straw) { + if (straw != NULL && straw->ChainFrom != NULL) { + straw->ChainFrom->Get_From(NULL); + straw->ChainFrom = NULL; + } + + if (ChainTo != NULL) { + ChainTo->ChainFrom = NULL; + } + + ChainTo = straw; + if (ChainTo != NULL) { + ChainTo->ChainFrom = this; + } + } +} + + +/*********************************************************************************************** + * Straw::Get -- Fetch some data from the straw chain. * + * * + * Use this routine to fetch some data from the straw. It is presumed that this routine * + * will be overridden to provide some useful functionality. At this level, the straw chain * + * merely passes the request on to the next straw in the chain. * + * * + * INPUT: source -- Pointer to the buffer to hold the requested data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If the number returned * + * is less than the number requested, then this indicates that the straw data has * + * been exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int Straw::Get(void * source, int slen) +{ + if (ChainTo != NULL) { + return(ChainTo->Get(source, slen)); + } + return(0); +} + + diff --git a/code/redalert/straw.h b/code/redalert/straw.h new file mode 100644 index 0000000..9e5b068 --- /dev/null +++ b/code/redalert/straw.h @@ -0,0 +1,87 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/STRAW.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : STRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef STRAW_H +#define STRAW_H + +#include + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +/* +** This is a demand driven data carrier. It will retrieve the byte request by passing +** the request down the chain (possibly processing on the way) in order to fulfill the +** data request. Without being derived, this class merely passes the data through. Derived +** versions are presumed to modify the data in some useful way or monitor the data +** flow. +*/ +class Straw +{ + public: + Straw(void) : ChainTo(0), ChainFrom(0) {} + virtual ~Straw(void); + + virtual void Get_From(Straw * pipe); + void Get_From(Straw & pipe) {Get_From(&pipe);} + virtual int Get(void * buffer, int slen); + + /* + ** Pointer to the next pipe segment in the chain. + */ + Straw * ChainTo; + Straw * ChainFrom; + + private: + + /* + ** Disable the copy constructor and assignment operator. + */ + Straw(Straw & rvalue); + Straw & operator = (Straw const & pipe); +}; + + +#endif diff --git a/code/redalert/stub.cpp b/code/redalert/stub.cpp new file mode 100644 index 0000000..9768e85 --- /dev/null +++ b/code/redalert/stub.cpp @@ -0,0 +1,22 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#include +#include + +void main(void) +{ + printf("Run C&C.COM.\n"); +} diff --git a/code/redalert/style.h b/code/redalert/style.h new file mode 100644 index 0000000..dd770d0 --- /dev/null +++ b/code/redalert/style.h @@ -0,0 +1,81 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + STYLE GUIDE FOR COMMAND & CONQUER : TIBERIAN SUN + +In addition to the standard Westwood programmer's style guide, the following +guidelines apply. + +> Use every feature the compiler has that will help catch bugs and encourage +solid portable coding practices. i.e., turn on all warnings and treat all warnings +as errors. Use helper programs such as Lint, BoundsChecker, or CodeGuard whenever +possible. + +> Keep related items together. Examples: Declare variables right before they are +used. Keep functions that work on the same subsystem, within the same module. + +> Use consistent commenting and spacing style. Examples: see existing Red Alert +code. Creative freedom does not extend to formatting. + +> Use descriptive variable names. Examples: Use "index" rather than "i". Use +"unit_index" rather than "index". + +> Use a consistent variable and function naming convention. Examples; boolean +variables should begin with "Is" (e.g., "IsActive", "IsFiring"). Functions that +exist solely to return a similar boolean query should begin with "Is_". + +> If you have a variable that only serves a boolean function, then declare it +as "bool" rather than as "int" and assign it "true" or "false" rather than "1" +or "0". + +> Choose function and variable names that are precise, descriptive, and concise (in that +order). + +> Hide data where possible: Examples: If a global is only used in one module, make +it static to that module. If a helper function is only used in one module, make it +static as well. Hide class members in the private section if they aren't needed +public. Most variables aren't needed as public. + +> Keep function bodies short. It is often times, more easy to understand and maintain +if a very long function is broken up into helper functions with descriptive names. +A clue if a function is too long is if you can page down 5 times and still be somewhere +in the middle of the function. If you can page down 10 times and still be in the +function, you've got a serious function size problem. + +> Compare pointers to NULL and integer types to zero (NULL) instead of using the +"if (x)" or "if (!x)" format. This short format should only be used for boolean +values and expressions. It is just as efficient and it makes it more clear to the +reader that the variable is a pointer and not a simple boolean value. + +> Use "const" when declaring any member functions that do not modify data. Corollary -- +make sure any function that appears like it will not modify data will, in fact, NOT +modify any data. Example; A function called Is_Able_To_Move() shouldn't modify the +object nor should it actually cause something to start moving! The use of "const" is a +variation on style guide rule #1 (use the compiler to help ensure proper code). + +> Shun using assembly language except in extreme cases. Such would be if a +function can only be performed by using assembly (requires special opcodes), or +if performance would be PROVEABLY dramatically improved. Don't code in assembly for +performance reasons if there isn't benchmarking code in place to analyze the results. +As a corollary, design a better algorithm as the first recourse. + +> Keep class interfaces small and simple. + +> Organize algorithms to use as few special case "if" statements as possible. Data +tables or data files are preferred. This follows the generally good guideline of offloading +as much processing as possible to compile-time rather than run-time. + +*/ diff --git a/code/redalert/super.cpp b/code/redalert/super.cpp new file mode 100644 index 0000000..f3ac697 --- /dev/null +++ b/code/redalert/super.cpp @@ -0,0 +1,388 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SUPER.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : October 11, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * SuperClass::AI -- Process the super weapon AI. * + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * SuperClass::Discharged -- Handles discharged action for special super weapon. * + * SuperClass::Enable -- Enable this super special weapon. * + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * SuperClass::Remove -- Removes super weapon availability. * + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * SuperClass::SuperClass -- Constructor for special super weapon objects. * + * * + * This is the constructor for the super weapons. * + * * + * INPUT: recharge -- The recharge delay time (in game frames). * + * * + * charging -- Voice to announce that the weapon is charging. * + * * + * ready -- Voice to announce that the weapon is fully charged. * + * * + * impatient -- Voice to announce current charging state. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +SuperClass::SuperClass(int recharge, bool powered, VoxType charging, VoxType ready, VoxType impatient, VoxType suspend) : + IsPowered(powered), + IsPresent(false), + IsOneTime(false), + IsReady(false), + Control(0), + OldStage(-1), + VoxRecharge(ready), + VoxCharging(charging), + VoxImpatient(impatient), + VoxSuspend(suspend), + RechargeTime(recharge) +{ +} + + +/*********************************************************************************************** + * SuperClass::Suspend -- Suspend the charging of the super weapon. * + * * + * This will temporarily put on hold the charging of the special weapon. This might be the * + * result of insufficient power. * + * * + * INPUT: on -- Should the weapon charging be suspended? Else, it will unsuspend. * + * * + * OUTPUT: Was the weapon suspend state changed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Suspend(bool on) +{ + if (IsPresent && !IsReady && !IsOneTime && on == Control.Is_Active()) { + if (!on) { + Control.Start(); + } else { + Control.Stop(); + } +// if (on != IsSuspended) { +// if (on) { +// SuspendTime = Control; +// } else { +// Control = SuspendTime; +// } +// IsSuspended = on; + return(true); +// } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Enable -- Enable this super special weapon. * + * * + * This routine is called when the special weapon needs to be activated. This is used for * + * both the normal super weapons and the special one-time super weapons (from crates). * + * * + * INPUT: onetime -- Is this a special one time super weapon? * + * * + * player -- Is this weapon for the player? If true, then there might be a voice * + * announcement of this weapon's availability. * + * * + * quiet -- Request that the weapon start in suspended state (quiet mode). * + * * + * OUTPUT: Was the special super weapon enabled? Failure might indicate that the weapon was * + * already available. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Enable(bool onetime, bool player, bool quiet) +{ + if (!IsPresent) { + IsPresent = true; + IsOneTime = onetime; + bool retval = Recharge(player && !quiet); + if (quiet) Suspend(true); + return(retval); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Remove -- Removes super weapon availability. * + * * + * Call this routine when the super weapon should be removed because of some outside * + * force. For one time special super weapons, they can never be removed except as the * + * result of discharging them. * + * * + * INPUT: none * + * * + * OUTPUT: Was the special weapon removed and disabled? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Remove(void) +{ + if (IsPresent && !IsOneTime) { + IsReady = false; + IsPresent = false; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Recharge -- Starts the special super weapon recharging. * + * * + * This routine is called when the special weapon is allowed to recharge. Suspension will * + * be disabled and the animation process will begin. * + * * + * INPUT: player -- Is this for a player owned super weapon? If so, then a voice * + * announcement might be in order. * + * * + * OUTPUT: Was the super weapon begun charging up? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Recharge(bool player) +{ + if (IsPresent && !IsReady) { +// IsSuspended = false; + OldStage = -1; + Control.Start(); + Control = RechargeTime; + +#ifdef CHEAT_KEYS + if (Special.IsSpeedBuild) { + Control = 1; + } +#endif + + if (player) { + Speak(VoxCharging); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Superclass::Discharged -- Handles discharged action for special super weapon. * + * * + * This routine should be called when the special super weapon has been discharged. The * + * weapon will either begin charging anew or will be removed entirely -- depends on the * + * one time flag for the weapon. * + * * + * INPUT: player -- Is this special weapon for the player? If so, then there might be a * + * voice announcement. * + * * + * OUTPUT: Should the sidebar be reprocessed because the special weapon has been eliminated? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::Discharged(bool player) +{ + if (Control.Is_Active() && IsPresent && IsReady) { + IsReady = false; + if (IsOneTime) { + IsOneTime = false; + return(Remove()); + } else { + Recharge(player); + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::AI -- Process the super weapon AI. * + * * + * This routine will process the charge up AI for this super weapon object. If the weapon * + * has advanced far enough to change any sidebar graphic that might represent it, then * + * "true" will be returned. Use this return value to intelligently update the sidebar. * + * * + * INPUT: player -- Is this for the player? If it is and the weapon is now fully charged, * + * then this fully charged state will be announced to the player. * + * * + * OUTPUT: Was the weapon's state changed such that a sidebar graphic update will be * + * necessary? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +bool SuperClass::AI(bool player) +{ + if (IsPresent && !IsReady) { + if (!Control.Is_Active()) { + if (OldStage != -1) { + OldStage = -1; + return(true); + } + } else { + if (Control == 0) { + IsReady = true; + if (player) { + Speak(VoxRecharge); + } + return(true); + } else { + if (Anim_Stage() != OldStage) { + OldStage = Anim_Stage(); + return(true); + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * SuperClass::Anim_Stage -- Fetches the animation stage for this super weapon. * + * * + * This will return the current animation stage for this super weapon. The value will be * + * between zero (uncharged) to ANIMATION_STAGES (fully charged). Use this value to render * + * the appropriate graphic on the sidebar. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current animation stage for this special super weapon powerup. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + * 10/11/1996 JLB : Doesn't show complete until really complete. * + *=============================================================================================*/ +int SuperClass::Anim_Stage(void) const +{ + if (IsPresent) { + if (IsReady) { + return(ANIMATION_STAGES); + } +// int time = Control; +// if (IsSuspended) { +// time = SuspendTime; +// } + + int stage = ANIMATION_STAGES * fixed(RechargeTime-Control.Value(), RechargeTime); + stage = min(stage, ANIMATION_STAGES-1); + return(stage); + } + return(0); +} + + +/*********************************************************************************************** + * SuperClass::Impatient_Click -- Called when player clicks on unfinished super weapon. * + * * + * This routine is called when the player clicks on the super weapon icon on the sidebar * + * when the super weapon is not ready yet. This results in a voice message feedback to the * + * player. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/28/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Impatient_Click(void) const +{ + if (!Control.Is_Active()) { + Speak(VoxSuspend); + } else { + Speak(VoxImpatient); + } +} + + +/*********************************************************************************************** + * SuperClass::Forced_Charge -- Force the super weapon to full charge state. * + * * + * This routine will force the special weapon to full charge state. Call it when the weapon * + * needs to be instantly charged. The airstrike (when it first becomes available) is a * + * good example. * + * * + * INPUT: player -- Is this for the player? If true, then the full charge state will be * + * announced. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void SuperClass::Forced_Charge(bool player) +{ + if (IsPresent) { + IsReady = true; + Control.Start(); + Control = 0; +// IsSuspended = false; + if (player) { + Speak(VoxRecharge); + } + } +} diff --git a/code/redalert/super.h b/code/redalert/super.h new file mode 100644 index 0000000..b9838bc --- /dev/null +++ b/code/redalert/super.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SUPER.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SUPER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/28/95 * + * * + * Last Update : July 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SUPER_H +#define SUPER_H + +#include "ftimer.h" + +class SuperClass { + public: + SuperClass(NoInitClass const & x) : Control(x) {}; + SuperClass(void) : Control(NoInitClass()) {}; + SuperClass(int recharge, bool powered, VoxType charging=VOX_NONE, VoxType ready=VOX_NONE, VoxType impatient=VOX_NONE, VoxType suspend=VOX_NONE); + + bool Suspend(bool on); + bool Enable(bool onetime = false, bool player=false, bool quiet=false); + void Forced_Charge(bool player=false); + bool AI(bool player=false); + bool Remove(void); + void Impatient_Click(void) const; + int Anim_Stage(void) const; + bool Discharged(bool player); + bool Is_Ready(void) const {return(IsReady);} + bool Is_Present(void) const {return(IsPresent);} + bool Is_One_Time(void) const {return(IsOneTime && IsPresent);} + bool Is_Powered(void) const {return(IsPowered);} + + //Needed access to recharge times for tooltips - 2019/08/14 Jason Scott + int Get_Recharge_Time() const { return(RechargeTime); }; + + private: + bool Recharge(bool player=false); + + unsigned IsPowered:1; + unsigned IsPresent:1; + unsigned IsOneTime:1; + unsigned IsReady:1; + + CDTimerClass Control; + int OldStage; + + VoxType VoxRecharge; + VoxType VoxCharging; + VoxType VoxImpatient; + VoxType VoxSuspend; + int RechargeTime; + + //Needed to make ANIMATION_STAGES public so the animation frame numbers could be turned into progress + //percentages - 2019/08/07 Jason Scott +public: + enum { + ANIMATION_STAGES=54 + }; +}; + + + +#endif diff --git a/code/redalert/surface.cpp b/code/redalert/surface.cpp new file mode 100644 index 0000000..aeaa859 --- /dev/null +++ b/code/redalert/surface.cpp @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SURFACE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SURFACE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#if (0)//PG +#include "FUNCTION.H" +#include "surface.h" + + +Surface::Surface(int w, int h, Buffer const * buffer, int pitch) : + Width(w), + Height(h), + Pitch(pitch) +{ + /* + ** If a buffer was specified, then this means that the surface will use the buffer memory + ** and thus not allocate and manage its own memory. + */ + if (buffer != NULL) { + SurfaceData = *buffer; + + /* + ** Reduce the dimensions if the buffer is not big enough. This size bounding is only + ** possible if the buffer size is known. Otherwise, presume that it is big enough. + */ + if (buffer->Get_Size() > 0 && Get_Size() > buffer->Get_Size()) { + + Height = buffer->Get_Size() / (Width+Pitch); + if (Height == 0) { + Height = 1; + Width = buffer->Get_Size(); + } + } + } else { + /* + ** A new buffer without existing memory specified, will allocate and manage its + ** own memory for the surface. + */ + new(&SurfaceData) Buffer(Logical_Size()); + } +} + + +Surface::Surface(Surface const & surface, int x, int y, int w, int h) : + Width(w), + Height(h), + Pitch(surface.Bytes_Per_Line() % w) +{ + new(&SurfaceData) Buffer((char*)surface.Get_Buffer() + y*surface.Bytes_Per_Line() + x); +} + + +void Surface::Copy_To(Buffer & buffer, int x, int y, int w, int h) const +{ + assert(buffer.Is_Valid()); + + /* + ** Determine the width of the region to copy from this surface. + */ + int width = w; + if (width == -1) { + width = Width; + } + + /* + ** Determine the height of the region to copy from this surface. + */ + int height = h; + if (height == -1) { + height = Height; + } + + Copy_To(Rect(x, y, width, height), buffer); +} + + +void Surface::Copy_To(Rect const & fromrect, Buffer & tobuffer) const +{ + assert(fromrect.Is_Valid()); + assert(tobuffer.Is_Valid()); + + /* + ** Determine the copy-from rectangle. The size is bounded to the source + ** size of the surface, regardless of what was specified as the source + ** rectangle. + */ + Rect rect = fromrect.Intersect(Rect(0, 0, Width, Height)); + + /* + ** Determine the number of bytes to copy. If this number would be + ** larger than the size of the destination buffer (presuming the size + ** of the destination buffer is known), then limit the copy size + ** to the buffer size. + */ + int tocopy = rect.Size(); + if (tobuffer.Get_Size() > 0 && tobuffer.Get_Size() > tocopy) { + tocopy = tobuffer.Get_Size(); + } + + /* + ** Determine the source starting byte pointer. + */ + char * source = (char*)Get_Buffer() + rect.Y*Bytes_Per_Line() + rect.X; + + /* + ** Determine the destination buffer pointer. This will always be the + ** start of the destination buffer object specified. + */ + char * dest = tobuffer; + + /* + ** Determine the working pitch value to use. For full width surface + ** copies on surfaces that have no inherent pitch, then a very fast + ** copy-in-one-fast-step operation can be performed. + */ + int pitch = Bytes_Per_Line() % rect.Width; + if (pitch == 0) { + memmove(dest, source, tocopy); + } else { + + /* + ** Copy the source to the destination in line segments. + */ + for (int y = 0; y < rect.Height; y++) { + memmove(dest, source, width); + dest += rect.Width; + source += rect.Width + pitch; + } + } +} +#endif \ No newline at end of file diff --git a/code/redalert/surface.h b/code/redalert/surface.h new file mode 100644 index 0000000..3899704 --- /dev/null +++ b/code/redalert/surface.h @@ -0,0 +1,113 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/SURFACE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : SURFACE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 09/08/96 * + * * + * Last Update : September 8, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef SURFACE_H +#define SURFACE_H + +#include "buff.h" +#include "rect.h" + +/* +** This class is used to represent an arbitrary rectangle in a graphic memory space. Typically, +** this represents what could be called a graphic buffer or even a portion of a graphic buffer. +** It can also represent a piece of artwork (such as a shape image) to be used to construct a +** display image. This wide range of use is possible, because this class is a very minimal +** representation of the graphic data. It holds only the information is absolutely needs and +** has the minimum member functions possible. +*/ +class Surface +{ + public: + Surface(void) : Width(0), Height(0), Pitch(0) {} + Surface(int w, int h, Buffer const * buffer=NULL, int pitch=0); + Surface(Surface const & surface, int x, int y, int w, int h); + virtual ~Surface(void) {} + + /* + ** Basic manipulation routines for copying entire surfaces or + ** a sub-region. + */ + void Copy_To(Rect const & fromrect, Surface & tosurface, Rect const & torect) const; + void Copy_To(Rect const & fromrect, Buffer & tobuffer) const; + + /* + ** Convenience routines that make presumptions on what is desired so that a + ** minimum of parameters can be specified. These routine ultimately map to the + ** basic copy routines. + */ + void Copy_To(Buffer & buffer, int x=0, int y=0, int w=-1, int h=-1) const; + void Copy_From(Buffer const & buffer, int x=0, int y=0, int w=-1, int h=-1); + + /* + ** Basic query functions. Support routines that will manipulate the underlying + ** image data will require access to this information. + */ + void * Get_Buffer(void) const {return(SurfaceData.Get_Buffer());} + int Get_Size(void) const {return(Bytes_Per_Line() * Height);} + int Get_Width(void) const {return(Width);} + int Get_Height(void) const {return(Height);} + int Get_Pitch(void) const {return(Pitch);} + + protected: + int Bytes_Per_Line(void) const {return(Width+Pitch);} + + /* + ** This is the pointer to the surface memory. Sometimes this could be allocated memory + ** managed by this class but more likely, it is memory that is merely referred to by + ** this class. + */ + Buffer SurfaceData; + + /* + ** This is the width (columns) of the surface (in pixels). + */ + int Width; + + /* + ** This ithe height (rows) of the surface (in pixels). + */ + int Height; + + /* + ** This is the modulus of the surface underlying memory 'display surface' width + ** divided by this surface's width. For a surface that is full width, this value + ** will be 0. This value will be non-zero if this surface is referring to a + ** sub-region within another surface. + */ + int Pitch; +}; + + +#endif diff --git a/code/redalert/tab.cpp b/code/redalert/tab.cpp new file mode 100644 index 0000000..8527a0e --- /dev/null +++ b/code/redalert/tab.cpp @@ -0,0 +1,295 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TAB.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : September 20, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TabClass::AI -- Handles player I/O with the tab buttons. * + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * TabClass::TabClass -- Default construct for the tab button class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +void const * TabClass::TabShape = NULL; + + +/*********************************************************************************************** + * TabClass::TabClass -- Default construct for the tab button class. * + * * + * The default constructor merely sets the tab buttons to default non-selected state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +TabClass::TabClass(void) : + FlasherTimer(0), + IsToRedraw(false), + MoneyFlashTimer(0) +{ +} + + +/*********************************************************************************************** + * TabClass::Draw_It -- Displays the tab buttons as necessary. * + * * + * This routine is called whenever the display is being redrawn (in some fashion). The * + * parameter can be used to force the tab buttons to redraw completely. The default action * + * is to only redraw if the tab buttons have been explicitly flagged to be redraw. The * + * result of this is the elimination of unnecessary redraws. * + * * + * INPUT: complete -- bool; Force redraw of the entire tab button graphics? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 05/19/1995 JLB : New EVA style. * + *=============================================================================================*/ +#define EVA_WIDTH 80 +#define TAB_HEIGHT 8 +void TabClass::Draw_It(bool complete) +{ + SidebarClass::Draw_It(complete); + + if (Debug_Map) { + return; + } + + /* + ** Redraw the top bar imagery if flagged to do so or if the entire display needs + ** to be redrawn. + */ + if ((complete || IsToRedraw) && LogicPage->Lock()) { + + int width = SeenBuff.Get_Width(); + int rightx = width - 1; + int tab_height = TAB_HEIGHT * RESFACTOR; + + LogicPage->Fill_Rect(0, 0, rightx, tab_height - 1, BLACK); + + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 0, 0, 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH / 2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); + + Draw_Credits_Tab(); + + LogicPage->Draw_Line(0, tab_height-(1* RESFACTOR), rightx, tab_height-(1 * RESFACTOR), BLACK); + + + if (!IsSidebarActive) + { + CC_Draw_Shape(TabShape, 0, width-(EVA_WIDTH * RESFACTOR), 0, WINDOW_MAIN, SHAPE_NORMAL); + Fancy_Text_Print(TXT_TAB_SIDEBAR, width-((EVA_WIDTH/2) * RESFACTOR), 0, &ColorRemaps[PCOLOR_GREY], TBLACK, TPF_METAL12|TPF_NOSHADOW|TPF_CENTER|TPF_BRIGHT_COLOR); + } + + LogicPage->Unlock(); + } + + Credits.Graphic_Logic(complete || IsToRedraw); + + IsToRedraw = false; +} + + +void TabClass::Draw_Credits_Tab(void) +{ + /* + ** Use the new sidebar art for 640x400 + */ + int width = SeenBuff.Get_Width(); + CC_Draw_Shape(TabShape, Map.MoneyFlashTimer > 1 ? 8 : 6, width - (EVA_WIDTH * RESFACTOR), 0, WINDOW_MAIN, SHAPE_NORMAL); + + if (Scen.MissionTimer.Is_Active()) { + bool light = ((int)Scen.MissionTimer < TICKS_PER_MINUTE * Rule.TimerWarning) || Map.FlasherTimer > 0; + CC_Draw_Shape(TabShape, light ? 4 : 2, width - ( ( EVA_WIDTH * 2 ) * RESFACTOR) , 0, WINDOW_MAIN, SHAPE_NORMAL); + } +} + + +void TabClass::Hilite_Tab(int tab) +{ + int xpos = 0; + int text = TXT_TAB_BUTTON_CONTROLS; + int textx = (EVA_WIDTH/2) * RESFACTOR; + + if (tab) { + xpos = (320-EVA_WIDTH) * RESFACTOR; + text = TXT_TAB_SIDEBAR; + textx = (320-(EVA_WIDTH/2)) * RESFACTOR; + } + + /* + ** Use the new sidebar art for 640x400 + */ + CC_Draw_Shape(TabShape, 1, xpos, 0, WINDOW_MAIN, SHAPE_NORMAL); + MetalScheme.Color = 128+6; + Fancy_Text_Print(TXT_TAB_BUTTON_CONTROLS, (EVA_WIDTH/2) * RESFACTOR, 0, &MetalScheme, TBLACK, TPF_METAL12 | TPF_CENTER | TPF_USE_GRAD_PAL); + MetalScheme.Color = 128; +} + + +/*********************************************************************************************** + * TabClass::AI -- Handles player I/O with the tab buttons. * + * * + * This routine is called every game tick and passed whatever key the player has supplied. * + * If the input selects a tab button, then the graphic gets updated accordingly. * + * * + * INPUT: input -- The player's input character (might be mouse click). * + * * + * x,y -- Mouse coordinates at time of input. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + * 12/31/1994 JLB : Uses mouse coordinate parameters. * + * 05/31/1995 JLB : Fixed to handle mouse shape properly. * + * 08/25/1995 JLB : Handles new scrolling option. * + *=============================================================================================*/ +void TabClass::AI(KeyNumType &input, int x, int y) +{ + if (y >= 0 && y < (TAB_HEIGHT * RESFACTOR) && x < (SeenBuff.Get_Width() - 1) && x > 0) { + + bool ok = false; + int width = SeenBuff.Get_Width(); + + /* + ** If the mouse is at the top of the screen, then the tab bars only work + ** in certain areas. If the special scroll modification is not active, then + ** the tabs never work when the mouse is at the top of the screen. + */ + if (y > 0) { + ok = true; + } + + if (ok) { + if (input == KN_LMOUSE) { + int sel = -1; + if (x < EVA_WIDTH * RESFACTOR) sel = 0; +#ifndef WIN32 // No Sidebar tab in hires - sidebar is always active. + if (x > (320-80) * RESFACTOR) sel = 1; +#endif //WIN32 + if (sel >= 0) { + Set_Active(sel); + input = KN_NONE; + } + } + + Override_Mouse_Shape(MOUSE_NORMAL, false); + } + } + + if (MoneyFlashTimer == 1) { + IsToRedraw = true; + Flag_To_Redraw(false); + } + + Credits.AI(); + + SidebarClass::AI(input, x, y); +} + + +/*********************************************************************************************** + * TabClass::Set_Active -- Activates a "filefolder tab" button. * + * * + * This function is used to activate one of the file folder tab buttons that appear at the * + * top edge of the screen. * + * * + * INPUT: select -- The button to activate. 0 = left button, 1=next button, etc. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/15/1994 JLB : Created. * + *=============================================================================================*/ +void TabClass::Set_Active(int select) +{ + switch (select) { + case 0: + Queue_Options(); + break; + + case 1: + Map.SidebarClass::Activate(-1); + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * TabClass::One_Time -- Performs one time initialization of tab handler class. * + * * + * This routine will perform any one time initializations of the tab handler class. This * + * typically includes the loading of the shapes that appear on it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +void TabClass::One_Time(void) +{ + SidebarClass::One_Time(); + RawFileClass file("tabs.shp"); + TabShape = MFCD::Retrieve("TABS.SHP"); +} + + +void TabClass::Flash_Money(void) +{ + IsToRedraw = true; + Flag_To_Redraw(false); + MoneyFlashTimer = 7; +} diff --git a/code/redalert/tab.h b/code/redalert/tab.h new file mode 100644 index 0000000..053c9c0 --- /dev/null +++ b/code/redalert/tab.h @@ -0,0 +1,76 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TAB.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TAB.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/15/94 * + * * + * Last Update : December 15, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TAB_H +#define TAB_H + +#include "sidebar.h" +#include "credits.h" + +class TabClass: public SidebarClass +{ + public: + TabClass(void); + TabClass(NoInitClass const & x) : SidebarClass(x), Credits(x), FlasherTimer(x), MoneyFlashTimer(x) {}; + + virtual void AI(KeyNumType &input, int x, int y); + virtual void Draw_It(bool complete=false); + static void Draw_Credits_Tab(void); + static void Hilite_Tab(int tab); + void Flash_Money(void); + + virtual void One_Time(void); // One-time inits + void Redraw_Tab(void) {IsToRedraw = true;Flag_To_Redraw(false);}; + + CreditClass Credits; + + CDTimerClass FlasherTimer; + + protected: + + /* + ** If the tab graphic is to be redrawn, then this flag is true. + */ + unsigned IsToRedraw:1; + + private: + void Set_Active(int select); + + CDTimerClass MoneyFlashTimer; + + static void const * TabShape; +}; + + +#endif diff --git a/code/redalert/taction.cpp b/code/redalert/taction.cpp new file mode 100644 index 0000000..98f5468 --- /dev/null +++ b/code/redalert/taction.cpp @@ -0,0 +1,902 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TACTION.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ACTION.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Action_From_Name -- retrieves ActionType for given name * + * Action_Needs -- Figures out what data an action object needs. * + * Name_From_Action -- retrieves name for ActionType * + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * TActionClass::Detach -- Removes any attachment from associated action. * + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * TActionClass::operator -- Performs the action that this object does. * + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + +/* +** New sidebar for GlyphX multiplayer. ST - 8/7/2019 10:10AM +*/ +#include "SidebarGlyphx.h" + +/* +** These are the text names for the various actions. If the action name ends with "..." then +** this means that additional data is probably required. +*/ +static const char * ActionText[TACTION_COUNT] = { + "-No Action-", + "Winner is...", + "Loser is...", + "Production Begins", + "Create Team...", + "Destroy All Teams", + "All to Hunt...", + "Reinforcement (team)...", + "Drop Zone Flare (waypoint)...", + "Fire Sale...", + "Play Movie...", + "Text Trigger (ID num)...", + "Destroy Trigger...", + "Autocreate Begins...", + "~don't use~", + "Allow Win", + "Reveal all map", + "Reveal around waypoint...", + "Reveal zone of waypoint...", + "Play sound effect...", + "Play music theme...", + "Play speech...", + "Force Trigger...", + "Timer Start", + "Timer Stop", + "Timer Extend (1/10th min)...", + "Timer Shorten (1/10th min)...", + "Timer Set (1/10th min)...", + "Global Set...", + "Global Clear...", + "Auto Base Building...", + "Grow shroud one 'step'", + "Destroy attached building", + "Add 1-time special weapon...", + "Add repeating special weapon...", + "Preferred target...", + "Launch Nukes" +}; + + +ActionChoiceClass ActionChoices[TACTION_COUNT] = { + {TACTION_NONE}, + {TACTION_WIN}, + {TACTION_LOSE}, + {TACTION_BEGIN_PRODUCTION}, + {TACTION_CREATE_TEAM}, + {TACTION_DESTROY_TEAM}, + {TACTION_ALL_HUNT}, + {TACTION_REINFORCEMENTS}, + {TACTION_DZ}, + {TACTION_FIRE_SALE}, + {TACTION_PLAY_MOVIE}, + {TACTION_TEXT_TRIGGER}, + {TACTION_DESTROY_TRIGGER}, + {TACTION_AUTOCREATE}, + {TACTION_WINLOSE}, + {TACTION_ALLOWWIN}, + {TACTION_REVEAL_ALL}, + {TACTION_REVEAL_SOME}, + {TACTION_REVEAL_ZONE}, + {TACTION_PLAY_SOUND}, + {TACTION_PLAY_MUSIC}, + {TACTION_PLAY_SPEECH}, + {TACTION_FORCE_TRIGGER}, + {TACTION_START_TIMER}, + {TACTION_STOP_TIMER}, + {TACTION_ADD_TIMER}, + {TACTION_SUB_TIMER}, + {TACTION_SET_TIMER}, + {TACTION_SET_GLOBAL}, + {TACTION_CLEAR_GLOBAL}, + {TACTION_BASE_BUILDING}, + {TACTION_CREEP_SHADOW}, + {TACTION_DESTROY_OBJECT}, + {TACTION_1_SPECIAL}, + {TACTION_FULL_SPECIAL}, + {TACTION_PREFERRED_TARGET}, + {TACTION_LAUNCH_NUKES} +}; + + +/*********************************************************************************************** + * ActionChoiceClass::Draw_It -- Display the action choice as part of a list box. * + * * + * This is a support routine only used to display this object when it is part of a list * + * box. * + * * + * INPUT: x,y -- The coordinate (upper left) to display the description. * + * * + * width,height -- Dimensions of the area to display the description. * + * * + * selected -- Is this item highlighted? * + * * + * flags -- The text print flags to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void ActionChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TActionClass::Detach -- Removes any attachment from associated action. * + * * + * This routine will remove any action reference to the team type specified. This routine * + * is called when the team type is being destroyed. All references to that team type must * + * also be severed. This routine does that with respect to trigger actions. * + * * + * INPUT: target-- The target object or type to remove from this taction object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Detach(TARGET target) +{ + if (Is_Target_TeamType(target) && Team == As_TeamType(target)) { + Team = NULL; + } + if (Is_Target_TriggerType(target) && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*********************************************************************************************** + * TActionClass::Build_INI_Entry -- Builds an INI entry for this trigger action. * + * * + * This routine will build the text (INI entry) format for the data of this trigger * + * action object. Typical use of this is when the INI file is being written. * + * * + * INPUT: ptr -- Pointer to the location to build the INI text to. The buffer is presumed * + * to be big enough. * + * * + * OUTPUT: none * + * * + * WARNINGS: The buffer passed to this routine must be big enough to hold the largest * + * text that will be created into it. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d,%d", Action, TeamTypes.Logical_ID(Team), TriggerTypes.Logical_ID(Trigger), Data.Value); +} + + +/*********************************************************************************************** + * TActionClass::Read_INI -- Converts INI text into appropriate action data. * + * * + * This routine will convert INI data into the right values within this trigger action * + * object. Typical use of this routine is when the INI file is being read. It is the * + * counterpart to the Build_INI_Entry function. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Read_INI(void) +{ + switch (NewINIFormat) { + default: + Action = TActionType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + case 0: + Action = TActionType(atoi(strtok(NULL, ","))); + + char const * ptr = strtok(NULL, ","); + Team = TeamTypeClass::From_Name(ptr); + assert(Action_Needs(Action) != NEED_TEAM || Team.Is_Valid()); + + /* + ** Since triggers refer to other triggers, only record a copy of the trigger text + ** name. This will be fixed up later. + */ + Trigger.Set_Raw((long)strdup(strtok(NULL, ","))); + + Data.Value = atoi(strtok(NULL, ",")); + break; + } +} + + +/*********************************************************************************************** + * TActionClass::Code_Pointers -- Convert embedded pointers into a transportable format. * + * * + * This routine is called prior to saving the game. It will convert any pointers into a * + * format that is safe for persistent storage. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine makes the object unfit for use. The Decode_Pointers() routine * + * must be called prior to using this object. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Code_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::Decode_Pointers -- Converts coded pointers into usable format. * + * * + * This routine is called after a game has been loaded. The encoded pointers will be * + * converted back into usable format by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will NOT work unless the pointer are, in fact, coded. There is * + * no prevention check to protect against calling this routine twice. * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +void TActionClass::Decode_Pointers(void) +{ +} + + +/*********************************************************************************************** + * TActionClass::operator -- Performs the action that this object does. * + * * + * This routine is called when the action associated with this action object must be * + * performed. Typically, this occurs when a trigger has "sprung" and now it must take * + * effect. The action object is what carries out this effect. * + * * + * INPUT: house -- The owner of this action. This information is necessary since some * + * actions depend on who the trigger was owned by. * + * * + * object-- Pointer to the object that the springing trigger was attached to. If * + * this parameter is null, then the trigger wasn't attached to any object. * + * * + * id -- Trigger ID (only if forced) otherwise -1. * + * * + * cell -- The cell this trigger is attached to (if any). * + * * + * OUTPUT: bool; Was this action able to perform what it needed to do? Failure could be * + * because a reinforcement couldn't be generated, for example. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + * 04/10/1996 JLB : Added the ID parameter. * + *=============================================================================================*/ +bool TActionClass::operator() (HousesType house, ObjectClass * object, int id, CELL cell) +{ + /* + ** Otherwise, take an appropriate action. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + TriggerClass * trig = NULL; + if (id != -1) { + trig = Triggers.Raw_Ptr(id); + } + bool success = true; +// TeamTypeClass * ttype = Team; + + /* + ** Ensure that the specified object is not actually dead. A dead object could + ** be passed to this routine in the case of a multiple event trigger that + ** had the first event kill the object. + */ + if (object && !object->IsActive) { + object = 0; + } + + switch (Action) { + /* + ** Display a text message overlayed onto the tactical map. + */ + case TACTION_TEXT_TRIGGER: + Session.Messages.Add_Message(NULL, Data.Value, (char *)TutorialText[Data.Value], PCOLOR_GREEN, TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_FULLSHADOW, Rule.MessageDelay * TICKS_PER_MINUTE); + break; + + /* + ** Launch nuclear missiles (duds) from all mslo's + */ + case TACTION_LAUNCH_NUKES: + { + for(int index = 0; index < Buildings.Count(); index++) { + BuildingClass *bldg = Buildings.Ptr(index); + if (*bldg == STRUCT_MSLO) { + bldg->Assign_Mission(MISSION_MISSILE); + } + } + break; + } + + /* + ** Set the preferred target for the house. + */ + case TACTION_PREFERRED_TARGET: + if (hptr) { + hptr->PreferredTarget = Data.Quarry; + } + break; + + /* + ** Initiate (or disable) the computer AI. When active, the computer will + ** build bases and units. + */ + case TACTION_BASE_BUILDING: + if (Data.Bool) { + hptr->IsBaseBuilding = true; + } else { + hptr->IsBaseBuilding = false; + } + break; + + /* + ** Cause the shadow to creep back one step. + */ + case TACTION_CREEP_SHADOW: + Map.Encroach_Shadow(PlayerPtr); + break; + + /* + ** Set a scenario global. + */ + case TACTION_SET_GLOBAL: + Scen.Set_Global_To(Data.Value, true); + break; + + /* + ** Clear a scenario global. + */ + case TACTION_CLEAR_GLOBAL: + Scen.Set_Global_To(Data.Value, false); + break; + + /* + ** Reveal the map around the area specified. + */ + case TACTION_REVEAL_SOME: + if (!PlayerPtr->IsVisionary) { + Map.Sight_From(Scen.Waypoint[Data.Value], Rule.GapShroudRadius, PlayerPtr, false); + } + break; + + /* + ** Reveal all cells of the zone that the specified waypoint is located + ** in. This can be used to reveal whole islands or bodies of water + */ + case TACTION_REVEAL_ZONE: + if (!PlayerPtr->IsVisionary) { + int zone = Map[Scen.Waypoint[Data.Value]].Zones[MZONE_CRUSHER]; + + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + if (Map[cell].Zones[MZONE_CRUSHER] == zone) { + Map.Map_Cell(cell, PlayerPtr); + } + } + + } + break; + + /* + ** Reveal the entire map. + */ + case TACTION_REVEAL_ALL: + if (!PlayerPtr->IsVisionary) { + PlayerPtr->IsVisionary = true; + for (CELL cell = 0; cell < MAP_CELL_TOTAL; cell++) { + Map.Map_Cell(cell, PlayerPtr); + } + } + break; + + /* + ** Star the mission timer. + */ + case TACTION_START_TIMER: + if (!Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + } + break; + + /* + ** Stop the mission timer. This will really just + ** suspend the timer. + */ + case TACTION_STOP_TIMER: + if (Scen.MissionTimer.Is_Active()) { + Scen.MissionTimer.Stop(); + Map.Redraw_Tab(); + } + break; + + /* + ** Add time to the mission timer. + */ + case TACTION_ADD_TIMER: + Scen.MissionTimer = Scen.MissionTimer + (Data.Value * (TICKS_PER_MINUTE/10)); + Map.Redraw_Tab(); + break; + + /* + ** Remove time from the mission timer. + */ + case TACTION_SUB_TIMER: + if ((int)Scen.MissionTimer <= Data.Value * (TICKS_PER_MINUTE/10)) { + Scen.MissionTimer = 0; + } else { + Scen.MissionTimer = Scen.MissionTimer - (Data.Value * (TICKS_PER_MINUTE/10)); + } + Map.Redraw_Tab(); + break; + + /* + ** Set the mission timer to the value specified. + */ + case TACTION_SET_TIMER: + Scen.MissionTimer = Data.Value * (TICKS_PER_MINUTE/10); + Scen.MissionTimer.Start(); + Map.Redraw_Tab(); + break; + + /* + ** Play a movie immediately. The game is temporarily + ** suspended while the movie plays. + */ + case TACTION_PLAY_MOVIE: + Hide_Mouse(); + SeenPage.Clear(); + Play_Movie(VQType(char(Data.Movie)), THEME_NONE, true, true); + GamePalette.Set(); + Map.Flag_To_Redraw(true); + Show_Mouse(); + break; + + /* + ** Play a sound effect. + */ + case TACTION_PLAY_SOUND: + Sound_Effect(Data.Sound); + break; + + /* + ** Play a musical theme. + */ + case TACTION_PLAY_MUSIC: + Theme.Queue_Song(Data.Theme); + break; + + /* + ** Play the speech data specified. + */ + case TACTION_PLAY_SPEECH: + Speak(Data.Speech); + break; + + /* + ** Give the special weapon to the house. + */ + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + hptr->SuperWeapon[Data.Special].Enable(Action==TACTION_1_SPECIAL, false); +// hptr->SuperWeapon[Data.Special].Forced_Charge(PlayerPtr == hptr); + + // Add to Glyphx multiplayer sidebar. ST - 8/7/2019 10:13AM + if (Session.Type == GAME_GLYPHX_MULTIPLAYER) { + if (hptr->IsHuman) { + Sidebar_Glyphx_Add(RTTI_SPECIAL, Data.Special, hptr); + } + } + else { + if (PlayerPtr == hptr) { + Map.Add(RTTI_SPECIAL, Data.Special); + Map.Column[1].Flag_To_Redraw(); + } + } + break; + + /* + ** Destroying a trigger means that all triggers of that type will be destroyed. + */ + case TACTION_DESTROY_TRIGGER: + if (Trigger.Is_Valid()) { + for (int index = 0; index < Triggers.Count(); index++) { + if (Triggers.Ptr(index)->Class == Trigger) { + Detach_This_From_All(Triggers.Ptr(index)->As_Target()); + delete Triggers.Ptr(index); + index--; + } + } + } + break; + + /* + ** A forced trigger will force an existing trigger of that type or + ** will create a trigger of that type and then force it to be sprung. + */ + case TACTION_FORCE_TRIGGER: + if (Trigger.Is_Valid()) { + Find_Or_Make(Trigger)->Spring(TEVENT_ANY, 0, 0, true); + } + break; + + /* + ** Place a smoke marker at the waypoint specified. + */ + case TACTION_DZ: + new AnimClass(ANIM_LZ_SMOKE, Cell_Coord(Scen.Waypoint[Data.Value])); + break; + + /* + ** Flag the house specified as the winner. Really the house value + ** is only used to determine if it is the player or the computer. + */ + case TACTION_WIN: + if (Data.House == PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** Flag the house specified as the loser. The house parameter is only + ** used to determine if it refers to the player or the computer. + */ + case TACTION_LOSE: + if (Data.House != PlayerPtr->Class->House) { + PlayerPtr->Flag_To_Win(); + } else { + PlayerPtr->Flag_To_Lose(); + } + break; + + /* + ** This will enable production to begin for the house specified. + */ + case TACTION_BEGIN_PRODUCTION: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->Begin_Production(); + } + break; + + /* + ** Cause all buildings to be sold and all units to go into + ** hunt mode. + */ + case TACTION_FIRE_SALE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->State = STATE_ENDGAME; + } + break; + + /* + ** Begin the team autocreate logic for the house specified. + */ + case TACTION_AUTOCREATE: + if (Data.House != HOUSE_NONE) { + HouseClass * specified_house = HouseClass::As_Pointer(Data.House); + specified_house->IsAlerted = true; + } + break; + + /* + ** Manually create the team specified. + */ + case TACTION_CREATE_TEAM: + ScenarioInit++; + Team->Create_One_Of(); + ScenarioInit--; + break; + + /* + ** Destroy all teams of the type specified. + */ + case TACTION_DESTROY_TEAM: + Team->Destroy_All_Of(); + break; + + /* + ** Create a reinforcement of the team specified. + */ + case TACTION_REINFORCEMENTS: + success = Do_Reinforcements(&*Team); + break; + + /* + ** Force all units of the house specified to go into + ** hunt mode. + */ + case TACTION_ALL_HUNT: + HouseClass::As_Pointer(Data.House)->Do_All_To_Hunt(); + break; + + /* + ** This will destroy all objects that this trigger is + ** attached to. + */ + case TACTION_DESTROY_OBJECT: + if (object) { + int damage = object->Strength; + object->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } else { + success = false; + } + + /* + ** If the trigger is attached to a bridge, then the bridge + ** gets destroyed regardless of whether the trigger was a + ** forced or natural spring event. + */ + if (cell != 0) { + Map.Destroy_Bridge_At(cell); + } + + /* + ** Loop through and destroy all objects that have this trigger + ** attached to them. + */ + if (trig) { + for (int u_index = 0; u_index < Units.Count(); u_index++) { + UnitClass * unit = Units.Ptr(u_index); + + if (unit && unit->Trigger == trig) { + unit->Trigger = NULL; + int damage = unit->Strength; + unit->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int i_index = 0; i_index < Infantry.Count(); i_index++) { + InfantryClass * infantry = Infantry.Ptr(i_index); + + if (infantry && infantry->Trigger == trig) { + infantry->Trigger = NULL; + int damage = infantry->Strength; + infantry->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int a_index = 0; a_index < Aircraft.Count(); a_index++) { + AircraftClass * aircraft = Aircraft.Ptr(a_index); + + if (aircraft && aircraft->Trigger == trig) { + aircraft->Trigger = NULL; + int damage = aircraft->Strength; + aircraft->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + + for (int b_index = 0; b_index < Buildings.Count(); b_index++) { + BuildingClass * building = Buildings.Ptr(b_index); + + if (building && building->Trigger == trig) { + building->Trigger = NULL; + int damage = building->Strength; + building->Take_Damage(damage, 0, WARHEAD_AP, 0, true); + } + } + } + break; + + /* + ** Do no action at all. + */ + case TACTION_NONE: + default: + break; + } + return(success); +} + + +/*********************************************************************************************** + * Action_From_Name -- retrieves ActionType for given name * + * * + * INPUT: * + * name name to get ActionType for * + * * + * OUTPUT: * + * ActionType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TActionType Action_From_Name (char const * name) +{ + if (name == NULL) { + return(TACTION_NONE); + } + + for (TActionType i = TACTION_NONE; i < TACTION_COUNT; i++) { + if (!stricmp(name, ActionText[i])) { + return(i); + } + } + + return(TACTION_NONE); +} + + +/*********************************************************************************************** + * Name_From_Action -- retrieves name for ActionType * + * * + * INPUT: * + * action ActionType to get name for * + * * + * OUTPUT: * + * name of ActionType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Action(TActionType action) +{ + return(ActionText[action]); +} + + +/*********************************************************************************************** + * Action_Needs -- Figures out what data an action object needs. * + * * + * Use this routine to determine what extra data is needed for the specified action. This * + * data will be prompted for in the scenario editor. * + * * + * INPUT: action -- The action that is to be queried. * + * * + * OUTPUT: Returns with the data type (enumeration) needed for this action type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/22/1996 JLB : Created. * + *=============================================================================================*/ +NeedType Action_Needs(TActionType action) +{ + switch (action) { + case TACTION_1_SPECIAL: + case TACTION_FULL_SPECIAL: + return(NEED_SPECIAL); + + case TACTION_FIRE_SALE: + case TACTION_WIN: + case TACTION_LOSE: + case TACTION_ALL_HUNT: + case TACTION_BEGIN_PRODUCTION: + case TACTION_AUTOCREATE: + return(NEED_HOUSE); + + case TACTION_BASE_BUILDING: + return(NEED_BOOL); + + case TACTION_CREATE_TEAM: + case TACTION_DESTROY_TEAM: + case TACTION_REINFORCEMENTS: + return(NEED_TEAM); + + case TACTION_FORCE_TRIGGER: + case TACTION_DESTROY_TRIGGER: + return(NEED_TRIGGER); + + case TACTION_DZ: + return(NEED_WAYPOINT); + + case TACTION_REVEAL_SOME: + case TACTION_REVEAL_ZONE: + return(NEED_WAYPOINT); + + case TACTION_PLAY_MUSIC: + return(NEED_THEME); + + case TACTION_PLAY_MOVIE: + return(NEED_MOVIE); + + case TACTION_PLAY_SOUND: + return(NEED_SOUND); + + case TACTION_PLAY_SPEECH: + return(NEED_SPEECH); + + case TACTION_TEXT_TRIGGER: + case TACTION_ADD_TIMER: + case TACTION_SUB_TIMER: + case TACTION_SET_TIMER: + case TACTION_SET_GLOBAL: + case TACTION_CLEAR_GLOBAL: + return(NEED_NUMBER); + + case TACTION_PREFERRED_TARGET: + return(NEED_QUARRY); + + default: + break; + } + return(NEED_NONE); +} + + diff --git a/code/redalert/taction.h b/code/redalert/taction.h new file mode 100644 index 0000000..0cdaf5f --- /dev/null +++ b/code/redalert/taction.h @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TACTION.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : ACTION.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef ACTION_H +#define ACTION_H + +typedef enum TActionType : unsigned char { + TACTION_NONE, + + TACTION_WIN, // player wins! + TACTION_LOSE, // player loses. + TACTION_BEGIN_PRODUCTION, // computer begins factory production. + TACTION_CREATE_TEAM, // computer creates a certain type of team + TACTION_DESTROY_TEAM, + TACTION_ALL_HUNT, // all enemy units go into hunt mode (teams destroyed). + TACTION_REINFORCEMENTS, // player gets reinforcements + // (house that gets them is determined by + // the Reinforcement instance) + TACTION_DZ, // Deploy drop zone smoke. + TACTION_FIRE_SALE, // Sell all buildings and go on rampage. + TACTION_PLAY_MOVIE, // Play movie (temporarily suspend game). + TACTION_TEXT_TRIGGER, // Triggers a text message display. + TACTION_DESTROY_TRIGGER, // Destroy specified trigger. + TACTION_AUTOCREATE, // Computer to autocreate teams. + TACTION_WINLOSE, // Win if captured, lose if destroyed. + TACTION_ALLOWWIN, // Allows winning if triggered. + + TACTION_REVEAL_ALL, // Reveal the entire map. + TACTION_REVEAL_SOME, // Reveal map around cell #. + TACTION_REVEAL_ZONE, // Reveal all of specified zone. + TACTION_PLAY_SOUND, // Play sound effect. + TACTION_PLAY_MUSIC, // Play musical score. + TACTION_PLAY_SPEECH, // Play EVA speech. + TACTION_FORCE_TRIGGER, // Force trigger to activate. + TACTION_START_TIMER, // Start mission timer. + TACTION_STOP_TIMER, // Stop mission timer. + TACTION_ADD_TIMER, // Increase mission timer time. + TACTION_SUB_TIMER, // Decrease mission timer time. + TACTION_SET_TIMER, // Set and start the mission timer. + TACTION_SET_GLOBAL, // Set global variable. + TACTION_CLEAR_GLOBAL, // Clear global variable. + TACTION_BASE_BUILDING, // Automated base building. + TACTION_CREEP_SHADOW, // Shadow grows back one 'step'. + + TACTION_DESTROY_OBJECT, // Destroys the building this trigger is attached to. + TACTION_1_SPECIAL, // Add a one-time special weapon ability to house. + TACTION_FULL_SPECIAL, // Add a repeating special weapon ability to house. + + TACTION_PREFERRED_TARGET, // Designates preferred target for house. + TACTION_LAUNCH_NUKES, // Launch fake nuclear missiles from all silos + + TACTION_COUNT, + TACTION_FIRST=0 +} TActionType; + +TActionType Action_From_Name(char const * name); +char const * Name_From_Action(TActionType action); +NeedType Action_Needs(TActionType action); + +class TriggerTypeClass; +class TeamTypeClass; + +/* +** This elaborates the information necessary to carry out +** a trigger's action. +*/ +struct TActionClass { + TActionType Action; // Action to perform. + + CCPtr Team; // Team type pointer for this action (if needed). + + CCPtr Trigger; // Trigger type pointer for this action (if needed). + + union { + ThemeType Theme; // Musical theme. + VocType Sound; // Sound effect. + VoxType Speech; // Speech identifier. + HousesType House; // House to be affected. + SpecialWeaponType Special; // Special weapon ability. + QuarryType Quarry; // Preferred target for attack. + VQType Movie; // The movie to play. + bool Bool; // Boolean value. + long Value; + } Data; + + TActionClass(void) : Action(TACTION_NONE) { + Data.Theme = THEME_NONE; + Data.Value = -1; + }; + TActionClass(NoInitClass const & x) : Team(x), Trigger(x) {}; + + void Detach(TARGET target); + void Code_Pointers(void); + void Decode_Pointers(void); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; + + bool operator() (HousesType house, ObjectClass * object, int id, CELL cell); +}; + + +class ActionChoiceClass { + public: + ActionChoiceClass(TActionType event=TACTION_NONE) : Action(event) {} + + operator TActionType (void) const {return(Action);} + bool operator == (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action);} + bool operator != (ActionChoiceClass const & rvalue) const {return(Action != rvalue.Action);} + bool operator > (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (ActionChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (ActionChoiceClass const & rvalue) const {return(Action == rvalue.Action || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Action(Action));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TActionType Action; +}; + + +extern ActionChoiceClass ActionChoices[TACTION_COUNT]; + +#endif diff --git a/code/redalert/target.cpp b/code/redalert/target.cpp new file mode 100644 index 0000000..4dc1adc --- /dev/null +++ b/code/redalert/target.cpp @@ -0,0 +1,941 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TARGET.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : July 16, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * As_Animation -- Converts target value into animation pointer. * + * As_Building -- Converts a target value into a building object pointer. * + * As_Bullet -- Converts the target into a bullet pointer. * + * As_Cell -- Converts a target value into a cell number. * + * As_Coord -- Converts a target value into a coordinate value. * + * As_Infantry -- If the target is infantry, return a pointer to it. * + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * As_Object -- Converts a target value into an object pointer. * + * As_Target -- Converts a cell into a target value. * + * As_Target -- Converts a coordinate into a target value. * + * As_Team -- Converts a target number into a team pointer. * + * As_TeamType -- Converts a target into a team type pointer. * + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * As_Trigger -- Converts specified target into a trigger pointer. * + * As_TriggerType -- Convert the specified target into a trigger type. * + * As_Unit -- Converts a target value into a unit pointer. * + * As_Vessel -- Converts a target number into a vessel pointer. * + * TClass::TClass -- Constructor for target from object pointer. * + * TargetClass::As_Object -- Converts a target into an object pointer. * + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * Target_Legal -- Determines if the specified target is legal. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "target.h" + + +TargetClass::TargetClass(TARGET target) +{ + Target.Target = target; +} + + +TargetClass::TargetClass(AbstractClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(AbstractTypeClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = ptr->RTTI; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +TargetClass::TargetClass(CellClass const * ptr) { + if (ptr != NULL) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = ptr->ID; + } else { + Target.Sub.Exponent = RTTI_NONE; + } +} + +CellClass * xTargetClass::As_Cell(void) const +{ + if (Target.Sub.Exponent == RTTI_CELL) { + return(&Map[(CELL)Target.Sub.Mantissa]); + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Trigger -- Converts specified target into a trigger pointer. * + * * + * This routine will convert the specified target number into a trigger pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the trigger pointer that the specified target number represents. If * + * it doesn't represent a legal trigger object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass * As_Trigger(TARGET target, bool check_active) +{ + TriggerClass* trigger = Is_Target_Trigger(target) ? Triggers.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && trigger != NULL && !trigger->IsActive) { + trigger = NULL; + } + return(trigger); +} + + +/*********************************************************************************************** + * As_Team -- Converts a target number into a team pointer. * + * * + * This routine will convert the specified target number into a team pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with the team object that the specified target number represents. If it * + * doesn't represent a legal team then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * As_Team(TARGET target, bool check_active) +{ + TeamClass* team = Is_Target_Team(target) ? Teams.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && team != NULL && !team->IsActive) { + team = NULL; + } + return(team); +} + + +/*********************************************************************************************** + * As_TeamType -- Converts a target into a team type pointer. * + * * + * This routine will convert the specified target number into a team type pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the team type represented by the target number. If the * + * target number doesn't represent a legal team type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * As_TeamType(TARGET target) +{ + return(Is_Target_TeamType(target) ? TeamTypes.Raw_Ptr(Target_Value(target)) : NULL); +} + + +/*********************************************************************************************** + * As_Animation -- Converts target value into animation pointer. * + * * + * This routine will convert the specified target number into an animation pointer. * + * * + * INPUT: target -- The target number to convert into an animation pointer. * + * * + * OUTPUT: Returns with a pointer to the legal animation that this target represents. If it * + * doesn't represent a legal animation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +AnimClass * As_Animation(TARGET target, bool check_active) +{ + AnimClass* anim = Is_Target_Animation(target) ? Anims.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && anim != NULL && !anim->IsActive) { + anim = NULL; + } + return(anim); +} + + +/*********************************************************************************************** + * As_Bullet -- Converts the target into a bullet pointer. * + * * + * This routine will convert the specified target number into a bullet pointer. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the bullet it specifies. If the target doesn't refer to * + * a legal bullet, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +BulletClass * As_Bullet(TARGET target, bool check_active) +{ + BulletClass* bullet = Is_Target_Bullet(target) ? Bullets.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && bullet != NULL && !bullet->IsActive) { + bullet = NULL; + } + return(bullet); +} + + +/*********************************************************************************************** + * As_Aircraft -- Converts the target value into an aircraft pointer. * + * * + * This routine will convert the specified target value into an aircraft object pointer. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with a pointer to the aircraft that this target value represents. If the * + * specified target value doesn't represent an aircraft, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +AircraftClass * As_Aircraft(TARGET target, bool check_active) +{ + AircraftClass* aircraft = Is_Target_Aircraft(target) ? Aircraft.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && aircraft != NULL && !aircraft->IsActive) { + aircraft = NULL; + } + return(aircraft); +} + + +/*********************************************************************************************** + * As_Techno -- Converts a target value into a TechnoClass pointer. * + * * + * This routine will take the target value specified and convert it into a TechnoClass * + * pointer if the target represents an object that has a TechnoClass. * + * * + * INPUT: target -- The target value to convert into a TechnoClass pointer. * + * * + * OUTPUT: Returns with a pointer to the associated object's TechnoClass. If the target * + * cannot be converted into a TechnoClass pointer, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass * As_Techno(TARGET target, bool check_active) +{ + ObjectClass * obj = As_Object(target, check_active); + + if (obj && obj->Is_Techno()) { + return(TechnoClass *)obj; + } + return(NULL); +} + + +/*********************************************************************************************** + * As_Object -- Converts a target value into an object pointer. * + * * + * This routine is used to convert the target value specified into an object pointer. If * + * the target doesn't represent an object or the target value is illegal, then NULL is * + * returned. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the object it represent, or NULL if not an object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * As_Object(TARGET target, bool check_active) +{ + int val = Target_Value(target); + ObjectClass * object = NULL; + switch (Target_Kind(target)) { + case RTTI_INFANTRY: + object = Infantry.Raw_Ptr(val); + break; + + case RTTI_UNIT: + object = Units.Raw_Ptr(val); + break; + + case RTTI_VESSEL: + object = Vessels.Raw_Ptr(val); + break; + + case RTTI_BUILDING: + object = Buildings.Raw_Ptr(val); + break; + + case RTTI_AIRCRAFT: + object = Aircraft.Raw_Ptr(val); + break; + + case RTTI_TERRAIN: + object = Terrains.Raw_Ptr(val); + break; + + case RTTI_BULLET: + object = Bullets.Raw_Ptr(val); + break; + + case RTTI_ANIM: + object = Anims.Raw_Ptr(val); + break; + + default: + break; + } + + /* + ** Special check to ensure that a target value that references an + ** invalid object will not be converted back into an object pointer. + ** This condition is rare, but could occur in a network game if the + ** object it refers to is destroyed between the time an event message + ** is sent and when it is received. + */ + if (check_active && object != NULL && !object->IsActive) { + object = NULL; + } + + return(object); +} + + +/*********************************************************************************************** + * As_Unit -- Converts a target value into a unit pointer. * + * * + * This routine is used to convert the target value specified into a pointer to a unit * + * object. * + * * + * INPUT: target -- The target value to convert into a unit pointer. * + * * + * OUTPUT: Returns with a pointer to the unit the target value represents or NULL if not * + * a unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass * As_Unit(TARGET target, bool check_active) +{ + UnitClass* unit = Is_Target_Unit(target) ? Units.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && unit != NULL && !unit->IsActive) { + unit = NULL; + } + return(unit); +} + + +/*********************************************************************************************** + * As_Vessel -- Converts a target number into a vessel pointer. * + * * + * Use this routine to conver the specified target number into a pointer to a vessel object * + * that it represents. * + * * + * INPUT: target -- The target number to convert to a vessel pointer. * + * * + * OUTPUT: Returns with a pointer to the vessel object that this target value represents. If * + * the target number does not represent a vessel, then null is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass * As_Vessel(TARGET target, bool check_active) +{ + VesselClass* vessel = Is_Target_Vessel(target) ? Vessels.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && vessel != NULL && !vessel->IsActive) { + vessel = NULL; + } + return(vessel); +} + + +/*********************************************************************************************** + * As_Infantry -- If the target is infantry, return a pointer to it. * + * * + * This routine will translate the specified target value into an infantry pointer if the * + * target actually represents an infantry object. * + * * + * INPUT: target -- The target to convert to a pointer. * + * * + * OUTPUT: Returns a pointer to the infantry object that this target value represents. If * + * the target doesn't represent an infantry object, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +InfantryClass * As_Infantry(TARGET target, bool check_active) +{ + InfantryClass* infantry = Is_Target_Infantry(target) ? Infantry.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && infantry != NULL && !infantry->IsActive) { + infantry = NULL; + } + return(infantry); +} + + +/*********************************************************************************************** + * As_Building -- Converts a target value into a building object pointer. * + * * + * This routine is used to convert the target value specified into a building pointer. * + * * + * INPUT: target -- The target value to convert from. * + * * + * OUTPUT: Returns with a pointer to the building object that the target value represents. * + * If it doesn't represent a building, then return NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +BuildingClass * As_Building(TARGET target, bool check_active) +{ + BuildingClass* building = Is_Target_Building(target) ? Buildings.Raw_Ptr(Target_Value(target)) : NULL; + if (check_active && building != NULL && !building->IsActive) { + building = NULL; + } + return(building); +} + + +/*********************************************************************************************** + * Target_Legal -- Determines if the specified target is legal. * + * * + * This routine is used to check for the legality of the target value specified. It is * + * necessary to call this routine if there is doubt about the the legality of the target. * + * It is possible for the unit that a target value represents to be eliminated and thus * + * rendering the target value invalid. * + * * + * INPUT: target -- The target value to check. * + * * + * OUTPUT: bool; Is the target value legal? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +bool Target_Legal(TARGET target) +{ + if (target == TARGET_NONE) return(false); + + ObjectClass * obj = As_Object(target, false); + if (obj) { + return(obj->IsActive); + } + return(true); +} + + +/*********************************************************************************************** + * As_Cell -- Converts a target value into a cell number. * + * * + * This routine is used to convert the target value specified, into a cell value. This is * + * necessary for find path and other procedures that need a cell value. * + * * + * INPUT: target -- The target value to convert to a cell value. * + * * + * OUTPUT: Returns with the target value expressed as a cell location. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + *=============================================================================================*/ +CELL As_Cell(TARGET target) +{ + return(Coord_Cell(As_Coord(target))); +} + + +/*********************************************************************************************** + * As_Coord -- Converts a target value into a coordinate value. * + * * + * This routine is used to convert the target value specified into a coordinate value. It * + * is necessary for those procedures that require a coordinate value. * + * * + * INPUT: target -- The target value to convert. * + * * + * OUTPUT: Returns with the target expressed as a COORDINATE value. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1994 JLB : Created. * + * 11/16/1994 JLB : Simplified. * + *=============================================================================================*/ +COORDINATE As_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + int v = Target_Value(target); + + int x = ((v & 0x0FFF) << 4) + 0x0008; + int y = (((v>>12) & 0x0FFF) << 4) + 0x0008; + return(XY_Coord(x, y)); + +// return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + assert(obj->IsActive); + return(obj->Target_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * As_Movement_Coord -- Fetches coordinate if trying to move to this target. * + * * + * This routine will convert the specified target into a coordinate location. This location * + * is used when moving to the target specified. For cells, this is the center of the cell. * + * For special buildings that allow docking, it is the center location of the docking * + * bay. * + * * + * INPUT: target -- The target to convert into a coordinate value. * + * * + * OUTPUT: Returns with the docking coordinate of the target value specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE As_Movement_Coord(TARGET target) +{ + if (Target_Legal(target)) { + /* + ** Cell target values are handled as a special case. The value of the target number is + ** actually the cell index number. + */ + if (Is_Target_Cell(target)) { + return(Cell_Coord((CELL)Target_Value(target))); + } + + /* + ** Normal targets correspond to game objects. Fetch the object pointer and then ask it + ** for the center coordinate. Return the center coordinate as the target's coordinate. + */ + ObjectClass * obj = As_Object(target); + if (obj) { + return(obj->Docking_Coord()); + } + } + + /* + ** An unrecognized target value results in a null coordinate value. + */ + return(0x00000000L); +} + + +/*********************************************************************************************** + * TargetClass::As_Object -- Converts a target into an object pointer. * + * * + * If the target represents an object of some type, then this routine will return a * + * pointer to the object. Otherwise it will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the object that this target represents or NULL if it * + * doesn't represent a target. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +AbstractClass * xTargetClass::As_Abstract(bool check_active) const +{ + AbstractClass* abst = NULL; + switch ((RTTIType)*this) { + case RTTI_TEAM: + abst = Teams.Raw_Ptr(Value()); + break; + + case RTTI_BULLET: + abst = Bullets.Raw_Ptr(Value()); + break; + + case RTTI_OVERLAY: + abst = Overlays.Raw_Ptr(Value()); + break; + + case RTTI_SMUDGE: + abst = Smudges.Raw_Ptr(Value()); + break; + + case RTTI_UNIT: + abst = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + abst = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + abst = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + abst = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + abst = Aircraft.Raw_Ptr(Value()); + break; + + case RTTI_TERRAIN: + abst = Terrains.Raw_Ptr(Value()); + break; + + case RTTI_ANIM: + abst = Anims.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && abst != NULL && !abst->IsActive) { + abst = NULL; + } + return(abst); +} + + +AbstractTypeClass * xTargetClass::As_TypeClass(void) const +{ + switch ((RTTIType)*this) { + case RTTI_TEAMTYPE: + return(TeamTypes.Raw_Ptr(Value())); + + case RTTI_TRIGGERTYPE: + return(TriggerTypes.Raw_Ptr(Value())); + + case RTTI_BULLETTYPE: + return((BulletTypeClass *)&BulletTypeClass::As_Reference(BulletType(Value()))); + + case RTTI_OVERLAY: + return((OverlayTypeClass *)&OverlayTypeClass::As_Reference(OverlayType(Value()))); + + case RTTI_SMUDGE: + return((SmudgeTypeClass *)&SmudgeTypeClass::As_Reference(SmudgeType(Value()))); + + case RTTI_UNIT: + return((UnitTypeClass *)&UnitTypeClass::As_Reference(UnitType(Value()))); + + case RTTI_VESSEL: + return((VesselTypeClass *)&VesselTypeClass::As_Reference(VesselType(Value()))); + + case RTTI_BUILDING: + return((BuildingTypeClass *)&BuildingTypeClass::As_Reference(StructType(Value()))); + + case RTTI_INFANTRY: + return((InfantryTypeClass *)&InfantryTypeClass::As_Reference(InfantryType(Value()))); + + case RTTI_AIRCRAFT: + return((AircraftTypeClass *)&AircraftTypeClass::As_Reference(AircraftType(Value()))); + + case RTTI_TERRAIN: + return((TerrainTypeClass *)&TerrainTypeClass::As_Reference(TerrainType(Value()))); + + case RTTI_ANIM: + return((AnimTypeClass *)&AnimTypeClass::As_Reference(AnimType(Value()))); + + default: + break; + } + return(0); +} + + +/*********************************************************************************************** + * TargetClass::As_Techno -- Converts a target into a techno object pointer. * + * * + * This routine is used to convert the target object into a pointer to a techno class * + * object. If the target doesn't specify a techno class object, then NULL is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the techno class object that this target represents or * + * else it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TechnoClass * xTargetClass::As_Techno(bool check_active) const +{ + TechnoClass* techno = NULL; + switch ((RTTIType)*this) { + case RTTI_UNIT: + techno = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + techno = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + techno = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + techno = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + techno = Aircraft.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && techno != NULL && !techno->IsActive) { + techno = NULL; + } + return(techno); +} + + +ObjectClass * xTargetClass::As_Object(bool check_active) const +{ + ObjectClass* object = NULL; + switch ((RTTIType)*this) { + case RTTI_TERRAIN: + object = Terrains.Raw_Ptr(Value()); + break; + + case RTTI_SMUDGE: + object = Smudges.Raw_Ptr(Value()); + break; + + case RTTI_OVERLAY: + object = Overlays.Raw_Ptr(Value()); + break; + + case RTTI_BULLET: + object = Bullets.Raw_Ptr(Value()); + break; + + case RTTI_ANIM: + object = Anims.Raw_Ptr(Value()); + break; + + case RTTI_UNIT: + object = Units.Raw_Ptr(Value()); + break; + + case RTTI_VESSEL: + object = Vessels.Raw_Ptr(Value()); + break; + + case RTTI_BUILDING: + object = Buildings.Raw_Ptr(Value()); + break; + + case RTTI_INFANTRY: + object = Infantry.Raw_Ptr(Value()); + break; + + case RTTI_AIRCRAFT: + object = Aircraft.Raw_Ptr(Value()); + break; + + default: + break; + } + if (check_active && object != NULL && !object->IsActive) { + object = NULL; + } + return(object); +} + + + + +/*********************************************************************************************** + * As_Target -- Converts a cell into a target value. * + * * + * This routine will convert a cell into a target value. * + * * + * INPUT: cell -- The cell number that will be coerced into a target value. * + * * + * OUTPUT: Returns with the target value that this cell represents. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(CELL cell) +{ + int x = Cell_X(cell); + int y = Cell_Y(cell); + + x <<= 4; + y <<= 4; + + x += 0x0008; + y += 0x0008; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_Target -- Converts a coordinate into a target value. * + * * + * This routine is used to convert the specified coordinate into a target value. * + * * + * INPUT: coord -- The coordinate that is to be converted into a target value. * + * * + * OUTPUT: Returns with the target value that represents the coordinate. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/05/1996 JLB : Created. * + *=============================================================================================*/ +TARGET As_Target(COORDINATE coord) +{ + int x = Coord_X(coord); + int y = Coord_Y(coord); + + x >>= 4; + y >>= 4; + + return(Build_Target(RTTI_CELL, ((y << 12) | x) )); +} + + +/*********************************************************************************************** + * As_TechnoType -- Convert the target number into a techno type class pointer. * + * * + * This routine will conver the specified target number into a pointer to the techno * + * type class that it represents. * + * * + * INPUT: target -- The target number to convert. * + * * + * OUTPUT: Returns with a pointer to the TechnoTypeClass object that the target number * + * represents. If it doesn't represent that kind of object, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TechnoTypeClass const * As_TechnoType(TARGET target) +{ + int val = Target_Value(target); + switch (Target_Kind(target)) { + case RTTI_INFANTRYTYPE: + return(&InfantryTypeClass::As_Reference(InfantryType(val))); + + case RTTI_UNITTYPE: + return(&UnitTypeClass::As_Reference(UnitType(val))); + + case RTTI_VESSELTYPE: + return(&VesselTypeClass::As_Reference(VesselType(val))); + + case RTTI_AIRCRAFTTYPE: + return(&AircraftTypeClass::As_Reference(AircraftType(val))); + + case RTTI_BUILDINGTYPE: + return(&BuildingTypeClass::As_Reference(StructType(val))); + + } + return(NULL); +} + + +/*********************************************************************************************** + * As_TriggerType -- Convert the specified target into a trigger type. * + * * + * This routine will conver the target number into a pointer to the trigger type it * + * represents. * + * * + * INPUT: target -- The target value to convert into a trigger type pointer. * + * * + * OUTPUT: Returns with a pointer to the trigger type object that the specified target value * + * represents. If it doesn't represent a trigger type, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/16/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * As_TriggerType(TARGET target) +{ + if (Target_Kind(target) == RTTI_TRIGGERTYPE) { + return(TriggerTypes.Raw_Ptr(Target_Value(target))); + } + return(NULL); +} \ No newline at end of file diff --git a/code/redalert/target.h b/code/redalert/target.h new file mode 100644 index 0000000..cb9970f --- /dev/null +++ b/code/redalert/target.h @@ -0,0 +1,173 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TARGET.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TARGET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TARGET_H +#define TARGET_H + + +inline RTTIType Target_Kind(TARGET a) +{ + return(RTTIType(((TARGET_COMPOSITE &)a).Sub.Exponent)); +} + +inline unsigned Target_Value(TARGET a) +{ + return(((TARGET_COMPOSITE &)a).Sub.Mantissa); +} + +inline bool Is_Target_Team(TARGET a) {return (Target_Kind(a) == RTTI_TEAM);} +inline bool Is_Target_TeamType(TARGET a) {return (Target_Kind(a) == RTTI_TEAMTYPE);} +inline bool Is_Target_Trigger(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGER);} +inline bool Is_Target_TriggerType(TARGET a) {return (Target_Kind(a) == RTTI_TRIGGERTYPE);} +inline bool Is_Target_Infantry(TARGET a) {return (Target_Kind(a) == RTTI_INFANTRY);} +inline bool Is_Target_Bullet(TARGET a) {return (Target_Kind(a) == RTTI_BULLET);} +inline bool Is_Target_Terrain(TARGET a) {return (Target_Kind(a) == RTTI_TERRAIN);} +inline bool Is_Target_Cell(TARGET a) {return (Target_Kind(a) == RTTI_CELL);} +inline bool Is_Target_Unit(TARGET a) {return (Target_Kind(a) == RTTI_UNIT);} +inline bool Is_Target_Vessel(TARGET a) {return (Target_Kind(a) == RTTI_VESSEL);} +inline bool Is_Target_Building(TARGET a) {return (Target_Kind(a) == RTTI_BUILDING);} +inline bool Is_Target_Template(TARGET a) {return (Target_Kind(a) == RTTI_TEMPLATE);} +inline bool Is_Target_Aircraft(TARGET a) {return (Target_Kind(a) == RTTI_AIRCRAFT);} +inline bool Is_Target_Animation(TARGET a) {return (Target_Kind(a) == RTTI_ANIM);} +inline bool Is_Target_Object(TARGET a) +{ + return (Target_Kind(a) == RTTI_TERRAIN || + Target_Kind(a) == RTTI_UNIT || + Target_Kind(a) == RTTI_VESSEL || + Target_Kind(a) == RTTI_INFANTRY || + Target_Kind(a) == RTTI_BUILDING || + Target_Kind(a) == RTTI_AIRCRAFT); +} + + +TARGET As_Target(CELL cell); +TARGET As_Target(COORDINATE coord); +//inline TARGET As_Target(CELL cell) {return (TARGET)(((unsigned)RTTI_CELL << TARGET_MANTISSA) | cell);} + +class UnitClass; +class BuildingClass; +class TechnoClass; +class TerrainClass; +class ObjectClass; +class InfantryClass; +class BulletClass; +class TriggerClass; +class TeamClass; +class TeamTypeClass; +class AnimClass; +class AircraftClass; +class VesselClass; +class CellClass; +class TriggerTypeClass; + +/* +** Must not have a constructor since Watcom cannot handle a class that has a constructor if +** that class object is in a union. Don't use this class for normal purposes. Use the TargetClass +** instead. The xTargetClass is only used in one module for a special reason -- keep it that way. +*/ +class xTargetClass +{ + protected: + + TARGET_COMPOSITE Target; + + public: + + // conversion operator to RTTIType + operator RTTIType (void) const {return(RTTIType(Target.Sub.Exponent));} + + // comparison operator + int operator == (xTargetClass & tgt) {return (tgt.Target.Target==Target.Target ? 1 : 0);} + + // conversion operator to regular TARGET type + TARGET As_TARGET(void) const {return(Target.Target);} + + unsigned Value(void) const {return(Target.Sub.Mantissa);}; + + void Invalidate(void) {Target.Sub.Exponent = RTTI_NONE;Target.Sub.Mantissa = -1;} + bool Is_Valid(void) const {return (Target.Sub.Exponent != RTTI_NONE);} + + TARGET As_Target(void) const {return(Target.Target);} + AbstractTypeClass * As_TypeClass(void) const; + AbstractClass * As_Abstract(bool check_active = true) const; + TechnoClass * As_Techno(bool check_active = true) const; + ObjectClass * As_Object(bool check_active = true) const; + CellClass * As_Cell(void) const; + + /* + ** Helper routines to combine testing for, and fetching a pointer to, the + ** type of object indicated. + */ + TriggerTypeClass * As_TriggerType(void) const {if (*this == RTTI_TRIGGERTYPE) return((TriggerTypeClass *)As_TypeClass());return(0);} + TeamTypeClass * As_TeamType(void) const {if (*this == RTTI_TEAMTYPE) return((TeamTypeClass *)As_TypeClass());return(0);} + TerrainClass * As_Terrain(bool check_active = true) const {if (*this == RTTI_TERRAIN) return((TerrainClass *)As_Abstract(check_active));return(0);} + BulletClass * As_Bullet(bool check_active = true) const {if (*this == RTTI_BULLET) return((BulletClass *)As_Abstract(check_active));return(0);} + AnimClass * As_Anim(bool check_active = true) const {if (*this == RTTI_ANIM) return((AnimClass *)As_Abstract(check_active));return(0);} + TeamClass * As_Team(bool check_active = true) const {if (*this == RTTI_TEAM) return((TeamClass *)As_Abstract(check_active));return(0);} + InfantryClass * As_Infantry(bool check_active = true) const {if (*this == RTTI_INFANTRY) return((InfantryClass *)As_Techno(check_active));return(0);} + UnitClass * As_Unit(bool check_active = true) const {if (*this == RTTI_UNIT) return((UnitClass *)As_Techno(check_active));return(0);} + BuildingClass * As_Building(bool check_active = true) const {if (*this == RTTI_BUILDING) return((BuildingClass *)As_Techno(check_active));return(0);} + AircraftClass * As_Aircraft(bool check_active = true) const {if (*this == RTTI_AIRCRAFT) return((AircraftClass *)As_Techno(check_active));return(0);} + VesselClass * As_Vessel(bool check_active = true) const {if (*this == RTTI_VESSEL) return((VesselClass *)As_Techno(check_active));return(0);} +}; + +/* +** This class only serves as a wrapper to the xTargetClass. This class must not define any members except +** for the constructors. This is because the xTargetClass is used in a union and this target object is +** used as its initializer. If this class had any extra members they would not be properly copied and +** communicated to the other machines in a network/modem game. Combining this class with xTargetClass would +** be more efficient, but Watcom doesn't allow class objects that have a constructor to be part of a union [even +** if the class object has a default constructor!]. +*/ +class TargetClass : public xTargetClass +{ + public: + + TargetClass(void) {Invalidate();} + TargetClass(NoInitClass const &) {} + TargetClass(RTTIType rtti, int id) { + Target.Sub.Exponent = rtti; + Target.Sub.Mantissa = id; + } + TargetClass(CELL cell) { + Target.Sub.Exponent = RTTI_CELL; + Target.Sub.Mantissa = cell; + } + TargetClass(TARGET target); + TargetClass(AbstractClass const * ptr); + TargetClass(AbstractTypeClass const * ptr); + TargetClass(CellClass const * ptr); +}; + +#endif diff --git a/code/redalert/tcpip.cpp b/code/redalert/tcpip.cpp new file mode 100644 index 0000000..b6515ff --- /dev/null +++ b/code/redalert/tcpip.cpp @@ -0,0 +1,906 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : TCPIP.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : March 11th, 1996 * + * * + * Last Update : March 20th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * Overview: * + * * + * Member functions of the TcpipManagerClass which provides the Winsock * + * interface for C&C * + * * + * * + *-------------------------------------------------------------------------* + * Functions: * + * * + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * TMC::Close -- restores any currently in use Winsock resources * + * TMC::Init -- Initialised Winsock for use. * + * TMC::Start_Server -- Initialise connection and start listening. * + * TMC::Read -- read any pending input from the stream socket * + * TMC::Write -- Send data via the Winsock streaming socket * + * TMC::Add_Client -- A client has requested to connect. * + * TMC::Message_Handler -- Message handler for Winsock. * + * TMC::Set_Host_Address -- Set the address of the host * + * TMC::Start_Client -- Start trying to connect to a game host * + * TMC::Close_Socket -- Close an opened Winsock socket. * + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifdef WIN32 + +#include "function.h" +#include "tcpip.h" + + +/* +** Nasty globals +*/ +#ifndef WOLAPI_INTEGRATION +bool Server; //Is this player acting as client or server +#endif +TcpipManagerClass Winsock; //The object for interfacing with Winsock + + + +/*********************************************************************************************** + * TMC::TcpipManagerClass -- constructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +TcpipManagerClass::TcpipManagerClass(void) +{ + WinsockInitialised = FALSE; + Connected = FALSE; + UseUDP = TRUE; + SocketReceiveBuffer = 4096; + SocketSendBuffer = 4096; +} + + +/*********************************************************************************************** + * TMC::~TcpipManagerClass -- destructor for the TcpipManagerClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +TcpipManagerClass::~TcpipManagerClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * TMC::Close -- restores any currently in use Winsock resources * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + if (Async){ + WSACancelAsyncRequest(Async); + } + + /* + ** Close any open sockets + */ + if (ConnectSocket != INVALID_SOCKET){ + Close_Socket(ConnectSocket); + ConnectSocket = INVALID_SOCKET; + } + + if (ListenSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + ListenSocket = INVALID_SOCKET; + } + + if (UDPSocket != INVALID_SOCKET){ + Close_Socket(ListenSocket); + UDPSocket = INVALID_SOCKET; + } + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = FALSE; + Connected = FALSE; +} + + + +/*********************************************************************************************** + * TMC::Init -- Initialised Winsock for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (TRUE); + + /* + ** Initialise sockets to null + */ + ListenSocket = INVALID_SOCKET; + ConnectSocket =INVALID_SOCKET; + UDPSocket = INVALID_SOCKET; + + /* + ** Start WinSock, and fill in our WinSockData + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, &WinsockInfo); + if (rc != 0) { + return (FALSE); + } + + /* + ** Check the Winsock version number + */ + if ((WinsockInfo.wVersion & 0x00ff) != (version & 0x00ff) || + (WinsockInfo.wVersion >> 8) != (version >> 8)) { + return (FALSE); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = TRUE; + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Start_Server -- initialise out connection as the server. Start listening for clients. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:56PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Server(void) +{ + int i; + //struct sockaddr_in addr; + + Start_Client(); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; iCheck(); + + /* + ** Copy any outstanding incoming data to the buffer provided + */ + if (ReceiveBuffers[RXBufferTail].InUse){ + memcpy (buffer, ReceiveBuffers[RXBufferTail].Buffer, + MIN(ReceiveBuffers[RXBufferTail].DataLength, buffer_len)); + ReceiveBuffers[RXBufferTail].InUse = false; + + bytes_copied = MIN(ReceiveBuffers[RXBufferTail++].DataLength, buffer_len); + + RXBufferTail &= WS_NUM_RX_BUFFERS-1; + } + + return (bytes_copied); +} + + + +/*********************************************************************************************** + * TMC::Write -- Send data via the Winsock UDP socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Write(void *buffer, int buffer_len) +{ + char *source_buf = (char*) buffer; + + /* + ** Copy the data to one of the classes internal buffers + */ + if (!TransmitBuffers[TXBufferHead].InUse){ + memcpy (TransmitBuffers[TXBufferHead].Buffer, + buffer, + MIN (buffer_len, WS_INTERNET_BUFFER_LEN)); + TransmitBuffers[TXBufferHead].InUse = true; + TransmitBuffers[TXBufferHead++].DataLength = MIN(buffer_len, WS_INTERNET_BUFFER_LEN); + TXBufferHead &= WS_NUM_TX_BUFFERS-1; + } + + /* + ** Send a message to ourselves to start off the event + */ + if (UseUDP){ + SendMessage(MainWindow, WM_UDPASYNCEVENT, 0, (LONG)FD_WRITE); + }else{ + SendMessage(MainWindow, WM_ASYNCEVENT, 0, (LONG)FD_WRITE); + } + /* + ** Make sure the message loop gets called because all the Winsock notifications + ** are done via messages. + */ + Keyboard->Check(); +} + + + + + + +/*********************************************************************************************** + * TMC::Add_Client -- a client has requested to connect. Make the connection * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if client was successfully connected * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:02PM ST : Created * + *=============================================================================================*/ + +BOOL TcpipManagerClass::Add_Client(void) +{ + struct sockaddr_in addr; + int addrsize; + bool delay = TRUE; + + /* + ** Accept the connection. If there is an error then dont do anything else + */ + addrsize = sizeof(addr); + ConnectSocket = accept (ListenSocket, (LPSOCKADDR)&addr, &addrsize); + if (ConnectSocket == INVALID_SOCKET) { + //Show_Error("accept", WSAGetLastError()); + return(FALSE); + } + + /* + ** Set options for this socket + */ + setsockopt (ConnectSocket, IPPROTO_TCP, TCP_NODELAY, (char*)&delay, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (ConnectSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Save the clients address + */ + memcpy(&ClientIPAddress, &addr.sin_addr.s_addr,4); + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr,4); + + /* + ** Initiate an asynchronous host lookup by address. Our window will receive notification + ** when this is complete or when it times out. + */ + Async = WSAAsyncGetHostByAddr (MainWindow, WM_HOSTBYADDRESS, + (char const *)&addr.sin_addr, 4, PF_INET, &HostBuff[0], + MAXGETHOSTSTRUCT); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, + FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (ConnectSocket); + return(FALSE); + } + + /* + ** Create our UDP socket + */ + UDPSocket = socket(AF_INET, SOCK_DGRAM, 0); + if (UDPSocket == INVALID_SOCKET) { + return (FALSE); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (bind(UDPSocket, (LPSOCKADDR)&addr, sizeof(addr)) == + SOCKET_ERROR) { + Close_Socket(UDPSocket); + ConnectStatus = NOT_CONNECTING; + return(FALSE); + } + + /* + ** Set options for the UDP socket + */ + setsockopt (UDPSocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketReceiveBuffer, 4); + setsockopt (UDPSocket, SOL_SOCKET, SO_SNDBUF, (char*)&SocketSendBuffer, 4); + + /* + ** Enable asynchronous events on this socket + */ + if (WSAAsyncSelect (UDPSocket, MainWindow, WM_UDPASYNCEVENT, + FD_READ | FD_WRITE) == SOCKET_ERROR){ + WSACancelAsyncRequest(Async); + Close_Socket (UDPSocket); + Close_Socket (ConnectSocket); + return(FALSE); + } + + return (TRUE); + +} + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Message_Handler(HWND, UINT message, UINT , LONG lParam) +{ + struct hostent *hentry; + struct sockaddr_in addr; + int event; + int rc; + int addr_len; + + switch (message){ + + /* + ** Handle the GetHostByAddress result + */ + case WM_HOSTBYADDRESS: + + if (IsServer){ + /* + ** We are the server + */ + ConnectStatus = CONNECTING; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (&ClientName[0], hentry->h_name); + } + Async = 0; + return; + + }else{ + /* + ** We are the client + */ + ConnectStatus = CONTACTING_SERVER; + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + strcpy (Server.Name, hentry->h_name); + } + else { + Server.Name[0] = 0; + } + Async = 0; + return; + } + + + /* + ** Retrieve host by name: Start connecting now that we have the + ** address. + */ + case WM_HOSTBYNAME: + if (WSAGETASYNCERROR(lParam)==0) { + hentry = (struct hostent *)&HostBuff[0]; + memcpy (&(Server.Addr.s_addr), hentry->h_addr, 4); + memcpy(&UDPIPAddress, hentry->h_addr, 4); + strcpy (Server.DotAddr, inet_ntoa(Server.Addr)); + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + Server.Name[0] = 0; + strcpy (Server.DotAddr, "????"); + ConnectStatus = SERVER_ADDRESS_LOOKUP_FAILED; + } + Async = 0; + return; + + + /* + ** Connection is ready - accept the client + */ + case WM_ACCEPT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + return; + } + if (Add_Client()) { + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + } + else { + ConnectStatus = UNABLE_TO_ACCEPT_CLIENT; + } + return; + + + + /* + ** Handle UDP packet events + */ + case WM_UDPASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + + case FD_READ: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + addr_len = sizeof(addr); + rc = recvfrom(UDPSocket, ReceiveBuffer, WS_RECEIVE_BUFFER_LEN, 0, + (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error(UDPSocket); + return; + } + memcpy(&UDPIPAddress, &addr.sin_addr.s_addr, 4); + Copy_To_In_Buffer(rc); + return; + + + case FD_WRITE: + if (UseUDP){ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error(UDPSocket); + return; + } + + addr.sin_family = AF_INET; + addr.sin_port = htons(PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, &UDPIPAddress, 4); + + /* + ** Send as many bytes as there are in the buffer; if there's + ** an error, just bail out. If we get a WOULDBLOCK error, + ** WinSock will send us another message when the socket is + ** available for another write. + */ + while (TransmitBuffers[TXBufferTail].InUse){ + rc = sendto(UDPSocket, + TransmitBuffers[TXBufferTail].Buffer, + TransmitBuffers[TXBufferTail].DataLength, + 0, + (LPSOCKADDR)&addr, + sizeof (addr)); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error(UDPSocket); + } + break; + } + TransmitBuffers[TXBufferTail++].InUse = false; + TXBufferTail &= WS_NUM_TX_BUFFERS-1; + } + return; + } + } + + + + /* + ** Handle the asynchronous event callbacks + */ + case WM_ASYNCEVENT: + event = WSAGETSELECTEVENT(lParam); + switch (event) { + /* + ** FD_CLOSE: the client has gone away. Remove the client from our system. + */ + case FD_CLOSE: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0 && rc != WSAECONNRESET) { + ConnectStatus = CONNECTION_LOST; + return; + } + if (Async != 0) { + WSACancelAsyncRequest(Async); + } + WSAAsyncSelect (ConnectSocket, MainWindow, WM_ASYNCEVENT, 0); + Close_Socket (ConnectSocket); + ConnectSocket = INVALID_SOCKET; + //Connected = FALSE; + ConnectStatus = CONNECTION_LOST; + break; + + /* + ** FD_CONNECT: A connection was made, or an error occurred. + */ + case FD_CONNECT: + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + ConnectStatus = UNABLE_TO_CONNECT; + return; + } + + ConnectStatus = CONNECTED_OK; + Connected = TRUE; + return; + + } + } +} + + + +/*********************************************************************************************** + * TMC::Copy_To_In_Buffer -- copy data from our winsock buffer to our internal buffer * + * * + * * + * * + * INPUT: bytes to copy * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:17PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Copy_To_In_Buffer(int bytes) +{ + if (!ReceiveBuffers[RXBufferHead].InUse){ + memcpy (ReceiveBuffers[RXBufferHead].Buffer, ReceiveBuffer, MIN(bytes, WS_INTERNET_BUFFER_LEN)); + ReceiveBuffers[RXBufferHead].InUse = true; + ReceiveBuffers[RXBufferHead++].DataLength = MIN(bytes, WS_INTERNET_BUFFER_LEN); + RXBufferHead &= WS_NUM_RX_BUFFERS-1; + } +} + + + +/*********************************************************************************************** + * TMC::Set_Host_Address -- Set the address of the host game we want to connect to * + * * + * * + * * + * INPUT: ptr to address string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ +void TcpipManagerClass::Set_Host_Address(char *address) +{ + strcpy(HostAddress, address); +} + + + +/*********************************************************************************************** + * TMC::Start_Client -- Start trying to connect to a game host * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:19PM ST : Created * + *=============================================================================================*/ + +void TcpipManagerClass::Start_Client(void) +{ + struct sockaddr_in addr; + bool delay = true; + int i; + + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + /* + ** Set up the incoming and outgoing data buffers head and tail pointers + */ + TXBufferHead = 0; + TXBufferTail = 0; + RXBufferHead = 0; + RXBufferTail = 0; + + for (i=0 ; irenderwidth, hdImage->renderheight); + ((void const*&)terrain.HDImageData) = hdImage; + } + else + { + char tmp[512]; + sprintf(tmp, "icon_%s", fullname); + ((void const*&)terrain.HDImageData) = Load_Stamp(fullname, terrain.ImageData); + } + + IsTheaterShape = true; //Let Build_Frame know that this is a theater specific shape + if (terrain.RadarIcon != NULL) delete[] (char *)terrain.RadarIcon; + ((void const *&)terrain.RadarIcon) = Get_Radar_Icon(terrain.Get_Image_Data(), 0, 1, 3); + IsTheaterShape = false; + + } + } + } +} + + +/*********************************************************************************************** + * TerrainTypeClass::From_Name -- Convert name to terrain type. * + * * + * This routine is used to convert a text name into the matching * + * terrain type number. This is used during scenario initialization. * + * * + * INPUT: name -- The name to convert. * + * * + * OUTPUT: Returns the TerrainType that matches the name specified. If * + * no match was found, then TERRAIN_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +TerrainType TerrainTypeClass::From_Name(char const * name) +{ + TerrainType index; + + if (name != NULL) { + for (index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (stricmp(name, As_Reference(index).IniName) == 0) { + return(index); + } + } + } + return(TERRAIN_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TerrainTypeClass::Display -- Display a generic terrain object. * + * * + * This routine is used to display a generic terrain object. Typical * + * use is during scenario editing. * + * * + * INPUT: x,y -- Pixel coordinates to display object at (centered). * + * * + * window-- The window to display the object within. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Display(int x, int y, WindowNumberType window, HousesType) const +{ + IsTheaterShape = true; + CC_Draw_Shape(Get_Image_Data(), 0, x, y, window, SHAPE_NORMAL|SHAPE_CENTER|SHAPE_WIN_REL); + IsTheaterShape = false; +} + + +/*********************************************************************************************** + * TerrainTypeClass::Prep_For_Add -- Prepares to add terrain object. * + * * + * Submits all of the valid terrain objects to the scenario editor for possible selection * + * and subsequent placement on the map. All terrain objects, that have a valid shape * + * file available, are added. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainTypeClass::Prep_For_Add(void) +{ + for (TerrainType index = TERRAIN_FIRST; index < TERRAIN_COUNT; index++) { + if (As_Reference(index).Get_Image_Data()) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif + + +/*********************************************************************************************** + * TerrainTypeClass::Create_And_Place -- Creates and places terrain object on map. * + * * + * This support routine is used by the scenario editor to add a terrain object to the map. * + * * + * INPUT: cell -- The cell to place the terrain object in. * + * * + * OUTPUT: bool; Was the placement successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainTypeClass::Create_And_Place(CELL cell, HousesType ) const +{ + if (new TerrainClass(Type, cell)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Create_On_Of -- Creates a terrain object from type. * + * * + * This is used to create a terrain object by using the terrain type as a guide. This * + * routine is typically used by the scenario editor in order to place a terrain object * + * onto the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the created terrain object or NULL if one couldn't be * + * created. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * TerrainTypeClass::Create_One_Of(HouseClass *) const +{ + return(new TerrainClass(Type, -1)); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Occupy_List -- Returns with the occupy list for the terrain object type. * + * * + * This routine will return with the occupy list for the terrain object type. If there is * + * no occupy list for this terrain object type, then a special zero length occupy list * + * pointer is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object's occupy list. A zero length list is * + * returned in the case of no occupy list. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +short const * TerrainTypeClass::Occupy_List(bool ) const +{ + if (Occupy != NULL) return(Occupy); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +/*********************************************************************************************** + * TerrainTypeClass::Overlap_List -- Fetches the overlap list for the terrain type. * + * * + * This routine will return with the overlap list for the terrain object type. If there * + * is no overlap list for the terrain object, then a null length list is returned in order * + * to ensure that a non-null pointer is returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list for this object type. A special zero * + * length list pointer is returned if there is no overlap list associated with the * + * object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/20/1995 JLB : Created. * + *=============================================================================================*/ +short const * TerrainTypeClass::Overlap_List(void) const +{ + if (Overlap != NULL) return(Overlap); + + static short const _simple[1] = { + REFRESH_EOL + }; + return(&_simple[0]); +} + + +/*********************************************************************************************** + * TerrainTypeClass::As_Reference -- Fetches a reference to the terrain type object specified. * + * * + * Use this routine to convert the terrain type number into a reference to a terrain * + * type class object. * + * * + * INPUT: type -- The terrain type number to convert. * + * * + * OUTPUT: Returns with a reference to the terrain type class object that is referred to by * + * the terrain type number. * + * * + * WARNINGS: Be sure that the terrain type number is valid. Using an invalid value causes * + * undefined behavior. * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +TerrainTypeClass & TerrainTypeClass::As_Reference(TerrainType type) +{ + return(*TerrainTypes.Ptr(type)); +} + + +COORDINATE TerrainTypeClass::Coord_Fixup(COORDINATE coord) const +{ + return Coord_Whole(coord); +} diff --git a/code/redalert/team.cpp b/code/redalert/team.cpp new file mode 100644 index 0000000..f9b26a3 --- /dev/null +++ b/code/redalert/team.cpp @@ -0,0 +1,3073 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAM.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : August 27, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamClass::AI -- Process team logic. * + * TeamClass::Add -- Adds specified object to team. * + * TeamClass::Assign_Mission_Target -- Sets teams mission target and clears old target * + * TeamClass::Calc_Center -- Determines average location of team members. * + * TeamClass::Can_Add -- Determines if the specified object can be added to team. * + * TeamClass::Control -- Updates control on a member unit. * + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * TeamClass::Debug_Dump -- Displays debug information about the team. * + * TeamClass::Detach -- Removes specified target from team tracking. * + * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * + * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * + * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * TeamClass::Remove -- Removes the specified object from the team. * + * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * + * TeamClass::TMission_Attack -- Perform the team attack mission command. * + * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * + * TeamClass::TMission_Formation -- Process team formation change command. * + * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of tim* + * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * + * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * + * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * + * TeamClass::TMission_Spy -- Perform the team spy mission. * + * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * + * TeamClass::TeamClass -- Constructor for the team object type. * + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * TeamClass::operator delete -- Deallocates a team object. * + * TeamClass::operator new -- Allocates a team object. * + * TeamClass::~TeamClass -- Team object destructor. * + * _Is_It_Breathing -- Checks to see if unit is an active team member. * + * _Is_It_Playing -- Determines if unit is active and an initiated team member. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "mission.h" + + +/*********************************************************************************************** + * _Is_It_Breathing -- Checks to see if unit is an active team member. * + * * + * A unit could be a team member, but not be active. Such a case would occur when a * + * reinforcement team is inside a transport. It could also occur if a unit is in the * + * process of dying. Call this routine to ensure that the specified unit is a will and * + * able participant in the team. * + * * + * INPUT: object -- Pointer to the unit/infantry/aircraft that is to be checked. * + * * + * OUTPUT: bool; Is the specified unit active and able to be given commands by the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +static inline bool _Is_It_Breathing(FootClass const * object) +{ + /* + ** If the object is not present or appears to be dead, then it + ** certainly isn't an active member of the team. + */ + if (object == NULL || !object->IsActive || object->Strength == 0) return(false); + + /* + ** If the object is in limbo, then it isn't an active team member either. However, if the + ** scenario init flag is on, then it is probably a reinforcement issue or scenario + ** creation situation. In such a case, the members are considered active because they need to + ** be given special orders and treatment. + */ + if (!ScenarioInit && object->IsInLimbo) return(false); + + /* + ** Nothing eliminated this object from being considered an active member of the team (i.e., + ** "breathing"), then return that it is ok. + */ + return(true); +} + + +/*********************************************************************************************** + * _Is_It_Playing -- Determines if unit is active and an initiated team member. * + * * + * Use this routine to determine if the specified unit is an active participant of the * + * team. When a unit is first recruited to the team, it must travel to the team's location * + * before it can become an active player. Call this routine to determine if the specified * + * unit can be considered an active player. * + * * + * INPUT: object -- Pointer to the object that is to be checked to see if it is an * + * active player. * + * * + * OUTPUT: bool; Is the specified unit an active, living, initiated member of the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +static inline bool _Is_It_Playing(FootClass const * object) +{ + /* + ** If the object is not active, then it certainly can be a participating member of the + ** team. + */ + if (!_Is_It_Breathing(object)) return(false); + + /* + ** Only members that have been "Initiated" are considered "playing" participants of the + ** team. This results in the team members that are racing to regroup with the team (i.e., + ** not initiated), will continue to catch up to the team even while the initiated team members + ** carry out their team specific orders. + */ + if (!object->IsInitiated && object->What_Am_I() != RTTI_AIRCRAFT) return(false); + + /* + ** If it reaches this point, then nothing appears to disqualify the specified object from + ** being considered an active playing member of the team. In this case, return that + ** information. + */ + return(true); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TeamClass::Debug_Dump -- Displays debug information about the team. * + * * + * This routine will display information about the team. This is useful for debugging * + * purposes. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the debugging information will * + * be displayed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Debug_Dump(MonoClass * mono) const +{ + mono->Set_Cursor(1, 20);mono->Printf("%8.8s", Class->IniName); + mono->Set_Cursor(10, 20);mono->Printf("%3d", Total); + mono->Set_Cursor(17, 20);mono->Printf("%3d", Quantity[Class->ID]); + if (CurrentMission != -1) { + mono->Set_Cursor(1, 22); + mono->Printf("%-29s", Class->MissionList[CurrentMission].Description(CurrentMission)); + } + mono->Set_Cursor(40, 20);mono->Printf("%-10s", FormationName[Formation]); + mono->Set_Cursor(22, 20);mono->Printf("%08X", Zone); + mono->Set_Cursor(31, 20);mono->Printf("%08X", Target); + + mono->Fill_Attrib(53, 20, 12, 1, IsUnderStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 21, 12, 1, IsFullStrength ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(53, 22, 12, 1, IsHasBeen ? MonoClass::INVERSE : MonoClass::NORMAL); + + mono->Fill_Attrib(66, 20, 12, 1, IsMoving ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 21, 12, 1, IsForcedActive ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 22, 12, 1, IsReforming ? MonoClass::INVERSE : MonoClass::NORMAL); +} +#endif + + +/*********************************************************************************************** + * TeamClass::Init -- Initializes the team objects for scenario preparation. * + * * + * This routine clears out the team object array in preparation for starting a new * + * scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Init(void) +{ + Teams.Free_All(); +} + + +/*********************************************************************************************** + * TeamClass::operator new -- Allocates a team object. * + * * + * This routine will allocate a team object from the team object pool. * + * * + * INPUT: size -- The size of the requested allocation. * + * * + * OUTPUT: Returns with a pointer to the freshly allocated team object. If an allocation * + * could not be made, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void * TeamClass::operator new(size_t) +{ + void * ptr = Teams.Allocate(); + if (ptr != NULL) { + ((TeamClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TeamClass::operator delete -- Deallocates a team object. * + * * + * This routine will return a team object to the team object pool. * + * * + * INPUT: ptr -- Pointer to the team object to deallocate. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((TeamClass *)ptr)->IsActive = false; + } + Teams.Free((TeamClass *)ptr); +} + + +/*********************************************************************************************** + * TeamClass::~TeamClass -- Team object destructor. * + * * + * This routine is called when a team object is destroyed. It handles updating the total * + * number count for this team object type. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + * 07/04/1996 JLB : Keeps trigger if trigger still attached to objects. * + *=============================================================================================*/ +TeamClass::~TeamClass(void) +{ + if (GameActive && Class.Is_Valid()) { + while (Member != NULL) { + Remove(Member); + } + Class->Number--; + + /* + ** When the team dies, any trigger associated with it, dies as well. This will only occur + ** if there are no other objects linked to this trigger. Only player reinforcement + ** members that broke off of the team earlier will have this occur. + */ + if (Trigger.Is_Valid()) { + if (Trigger->AttachCount == 0) { + delete (TriggerClass *)Trigger; + } + Trigger = NULL; + } + } +} + + +/*********************************************************************************************** + * TeamClass::TeamClass -- Constructor for the team object type. * + * * + * This routine is called when the team object is created. * + * * + * INPUT: type -- Pointer to the team type to make this team object from. * + * * + * owner -- The owner of this team. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass::TeamClass(TeamTypeClass const * type, HouseClass * owner) : + AbstractClass(RTTI_TEAM, Teams.ID(this)), + Class((TeamTypeClass *)type), + House(owner), + IsForcedActive(false), + IsHasBeen(false), + IsFullStrength(false), + IsUnderStrength(true), + IsReforming(false), + IsLagging(false), + IsAltered(true), + JustAltered(false), + IsMoving(false), + IsNextMission(true), + IsLeaveMap(false), + Suspended(false), + Trigger(NULL), + Zone(TARGET_NONE), + ClosestMember(TARGET_NONE), + MissionTarget(TARGET_NONE), + Target(TARGET_NONE), + Total(0), + Risk(0), + Formation(FORMATION_NONE), + SuspendTimer(0), + CurrentMission(-1), + TimeOut(0), + Member(0) +{ + assert(Class); + assert(Class->IsActive); + assert(Class->ClassCount > 0); + + if (owner == NULL) { + House = HouseClass::As_Pointer(Class->House); + } + + memset(Quantity, 0, sizeof(Quantity)); + if (Class->Origin != -1) { + Zone = ::As_Target(Scen.Waypoint[Class->Origin]); + } + Class->Number++; + + /* + ** If there is a trigger tightly associated with this team, then + ** create an instance of that trigger and attach it to the team. + */ + if (Class->Trigger.Is_Valid()) { + Trigger = new TriggerClass(Class->Trigger); + } +} + + +/*************************************************************************** + * TeamClass::Assign_Mission_Target -- Sets mission target and clears old * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Assign_Mission_Target(TARGET new_target) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** First go through and find anyone who is currently targeting + ** the old mission target and clear their Tarcom. + */ + FootClass * unit = Member; + if (MissionTarget != TARGET_NONE) { + while (unit != NULL) { + bool tar = (unit->TarCom == MissionTarget); + bool nav = (unit->NavCom == MissionTarget); + if (tar || nav) { + + /* + ** If the unit was doing something related to the team mission + ** then we kick him into guard mode so that he is easy to change + ** missions for. + */ + unit->Assign_Mission(MISSION_GUARD); + + /* + ** If the unit's tarcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (nav) { + unit->Assign_Destination(TARGET_NONE); + } + + /* + ** If the unit's navcom is set to the old mission target, then + ** clear it, so that it will be reset by whatever happens next. + */ + if (tar) { + unit->Assign_Target(TARGET_NONE); + } + } + unit = unit->Member; + } + } + + /* + ** If there is not currently an override on the current mission target + ** then assign both MissionTarget and Target to the new target. If + ** there is an override, allow the team to keep fighting the override but + ** make sure they pick up on the new mission when they are ready. + */ + if (Target == MissionTarget || !Target_Legal(Target)) { + MissionTarget = Target = new_target; + } else { + MissionTarget = new_target; + } +} + + +/*********************************************************************************************** + * TeamClass::AI -- Process team logic. * + * * + * General purpose team logic is handled by this routine. It should be called once per * + * active team per game tick. This routine handles recruitment and assigning orders to * + * member units. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/06/1995 JLB : Choreographed gesture. * + *=============================================================================================*/ +void TeamClass::AI(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + int desired = 0; + int old_under = IsUnderStrength; + int old_full = IsFullStrength; + + /* + ** If the team has been suspended then we need to check if it's time for + ** us to reactivate the team. If not, no team logic will be processed + ** for this team. + */ + if (Suspended) { + if (SuspendTimer != 0) { + return; + } + Suspended = false; + } + + /* + ** If this team senses that its composition has been altered, then it should + ** recalculate the under strength and full strength flags. + */ + if (IsAltered) { + + /* + ** Figure out the total number of objects that this team type requires. + */ + for (int index = 0; index < Class->ClassCount; index++) { + desired += Class->Members[index].Quantity; + } + assert(desired != 0); + + if (Total) { + IsFullStrength = (Total == desired); + if (IsFullStrength) { + IsHasBeen = true; + } + + /* + ** Reinforceable teams will revert (or snap out of) the under strength + ** mode when the members transition the magic 1/3 strength threshold. + */ + if (Class->IsReinforcable) { + if (desired > 2) { + IsUnderStrength = (Total <= desired / 3); + } else { + IsUnderStrength = (Total < desired); + } + } else { + + /* + ** Teams that are not flagged as reinforceable are never considered under + ** strength if the team has already started its main mission. This + ** ensures that once the team has started, it won't dally to pick up + ** new members. + */ + IsUnderStrength = !IsHasBeen; + } + + IsAltered = JustAltered = false; + } else { + IsUnderStrength = true; + IsFullStrength = false; + Zone = TARGET_NONE; + + /* + ** A team that exists on the player's side is automatically destroyed + ** when there are no team members left. This team was created as a + ** result of reinforcement logic and no longer needs to exist when there + ** are no more team members. + */ + if (IsHasBeen || Session.Type != GAME_NORMAL) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + } + + /* + ** If the team has gone from under strength to no longer under + ** strength than the team needs to reform. + */ + if (old_under != IsUnderStrength) { + IsReforming = true; + } + } + + /* + ** If the team is under strength, then flag it to regroup. + */ + if (IsMoving && IsUnderStrength) { + IsMoving = false; + CurrentMission = -1; + if (Total) { + Calc_Center(Zone, ClosestMember); + + /* + ** When a team is badly damaged and needs to regroup it should + ** pick a friendly building to go and regroup at. Its first preference + ** should be somewhere near repair factory. If it cannot find a repair + ** factory then it should pick another structure that is friendly to + ** its side. + */ + CELL dest = As_Cell(Zone); + int max = 0x7FFFFFFF; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * b = Buildings.Ptr(index); + + if (b != NULL && !b->IsInLimbo && b->House == House && b->Class->PrimaryWeapon == NULL) { + CELL cell = Coord_Cell(b->Center_Coord()); + int dist = ::Distance(b->Center_Coord(), As_Coord(Zone)) * (Map.Cell_Threat(cell, House->Class->House) + 1); + + if (*b == STRUCT_REPAIR) { + dist /= 2; + } + if (dist < max) { + cell = Fetch_A_Leader()->Safety_Point(As_Cell(Zone), cell, 2, 4); +// cell = Member->Safety_Point(As_Cell(Zone), cell, 2, 4); + if (cell != -1) { + max = dist; + dest = cell; + } + } + } + } + + // Should calculate a regroup location. + Target = ::As_Target(dest); + Coordinate_Move(); + return; + } else { + Zone = TARGET_NONE; + } + } + + /* + ** Flag this team into action when it gets to full strength. Human owned teams are only + ** used for reinforcement purposes -- always consider them at full strength. + */ + if (!IsMoving && (IsFullStrength || IsForcedActive)) { + IsMoving = true; + IsHasBeen = true; + IsUnderStrength = false; + + /* + ** Infantry can do a gesture when they start their mission. Pick + ** a gesture at random. + */ + FootClass * techno = Member; + DoType doaction = Percent_Chance(50) ? DO_GESTURE1 : DO_GESTURE2; + while (techno) { + if (_Is_It_Breathing(techno) && techno->What_Am_I() == RTTI_INFANTRY) { + ((InfantryClass *)techno)->Do_Action(doaction); + } + + if (IsReforming || IsForcedActive) { + techno->IsInitiated = true; + } + + techno = techno->Member; + } + CurrentMission = -1; + IsNextMission = true; +// IsForcedActive = false; + } + + /* + ** If the team is moving or if there is no center position for + ** the team, then the center position must be recalculated. + */ + if (IsReforming || IsMoving || Zone == TARGET_NONE || ClosestMember == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Try to recruit members if there is room to do so for this team. + ** Only try to recruit members for a non player controlled team. + */ + if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((!House->IsHuman || !IsHasBeen) && Session.Type == GAME_NORMAL)) { +// if ((!IsMoving || (!IsFullStrength && Class->IsReinforcable)) && ((/*!House->IsHuman ||*/ !IsHasBeen) && Session.Type == GAME_NORMAL)) { + for (int index = 0; index < Class->ClassCount; index++) { + if (Quantity[index] < Class->Members[index].Quantity) { + Recruit(index); + } + } + } + + /* + ** If there are no members of the team and the team has reached + ** full strength at one time, then delete the team. + */ + if (Member == NULL && IsHasBeen) { + + /* + ** If this team had no members (i.e., the team object wasn't terminated by some + ** outside means), then pass through the logic triggers to see if one that + ** depends on this team leaving the map should be sprung. + */ + if (IsLeaveMap) { + for (int index = 0; index < LogicTriggers.Count(); index++) { + TriggerClass * trig = LogicTriggers[index]; + if (trig->Spring(TEVENT_LEAVES_MAP)) { + index--; + if (LogicTriggers.Count() == 0) break; + } + } + } + delete this; + return; + } + + /* + ** If the mission should be advanced to the next entry, then do so at + ** this time. Various events may cause the mission to advance, but it + ** all boils down to the following change-mission code. + */ + if (IsMoving && !IsReforming && IsNextMission) { + IsNextMission = false; + CurrentMission++; + if (CurrentMission < Class->MissionCount) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + TimeOut = mission->Data.Value * (TICKS_PER_MINUTE/10); + Target = TARGET_NONE; + switch (mission->Mission) { + + case TMISSION_MOVECELL: + Assign_Mission_Target(::As_Target((CELL)(mission->Data.Value))); + break; + + case TMISSION_MOVE: + if ((unsigned)mission->Data.Value < WAYPT_COUNT && Member != NULL) { + FootClass * leader = Fetch_A_Leader(); + CELL movecell = Scen.Waypoint[mission->Data.Value]; + if (!Is_Leaving_Map()) { + if (leader->Can_Enter_Cell(movecell) != MOVE_OK) { + movecell = Map.Nearby_Location(movecell, leader->Techno_Type_Class()->Speed); + } + } + Assign_Mission_Target(::As_Target(movecell)); + Target = ::As_Target(movecell); + } + break; + + case TMISSION_ATT_WAYPT: + case TMISSION_PATROL: + case TMISSION_SPY: + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + break; + + case TMISSION_ATTACKTARCOM: + Assign_Mission_Target(mission->Data.Value); + break; + + case TMISSION_UNLOAD: + default: + Assign_Mission_Target(TARGET_NONE); + break; + } + } else { + delete this; + return; + } + } + + /* + ** Perform mission of the team. This depends on the mission list. + */ + if (Member != NULL && IsMoving && !IsReforming && !IsUnderStrength) { + + /* + ** If the current Target has been dealt with but the mission target + ** has not, then the current target needs to be reset to the mission + ** target. + */ + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** If the current mission is one that times out, then check for + ** this case. If it has timed out then advance to the next + ** mission in the list or disband the team. + */ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; +// FootClass * member = Member; + + switch (mission->Mission) { + case TMISSION_PATROL: + TMission_Patrol(); + break; + + case TMISSION_FORMATION: + TMission_Formation(); + break; + + case TMISSION_ATTACKTARCOM: + case TMISSION_ATTACK: + TMission_Attack(); + break; + + case TMISSION_LOAD: + TMission_Load(); + break; + + case TMISSION_DEPLOY: + TMission_Deploy(); + break; + + case TMISSION_UNLOAD: + TMission_Unload(); + break; + + case TMISSION_MOVE: + case TMISSION_MOVECELL: + Coordinate_Move(); + break; + + /* + ** All members of this team become invulnerable as if by magic. + */ + case TMISSION_INVULNERABLE: + TMission_Invulnerable(); + break; + + case TMISSION_GUARD: + Coordinate_Regroup(); + break; + + case TMISSION_DO: + Coordinate_Do(); + break; + + case TMISSION_SET_GLOBAL: + TMission_Set_Global(); + break; + + case TMISSION_ATT_WAYPT: + if (!Target_Legal(MissionTarget)) { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } else { + Coordinate_Attack(); + } + break; + + case TMISSION_SPY: + TMission_Spy(); + break; + + case TMISSION_HOUND_DOG: + TMission_Follow(); + break; + + case TMISSION_LOOP: + TMission_Loop(); + break; + } + + /* + ** Check for mission time out condition. If the mission does in fact time out, then + ** flag it so that the team mission list will advance. + */ + switch (mission->Mission) { +// case TMISSION_UNLOAD: + case TMISSION_GUARD: + if (TimeOut == 0) { + IsNextMission = true; + } + break; + } + + } else { + + if (IsMoving) { + IsReforming = !Coordinate_Regroup(); + } else { + Coordinate_Move(); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Add -- Adds specified object to team. * + * * + * Use this routine to add the specified object to the team. The object is checked to make * + * sure that it can be assigned to the team. If it can't, then the object will be left * + * alone and false will be returned. * + * * + * INPUT: obj -- Pointer to the object that is to be assigned to this team. * + * * + * OUTPUT: bool; Was the unit added to the team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation flag setup. * + * 08/06/1995 JLB : Allows member stealing from lesser priority teams. * + *=============================================================================================*/ +bool TeamClass::Add(FootClass * obj) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + if (!obj) return(false); + + int typeindex; + if (!Can_Add(obj, typeindex)) return(false); + + /* + ** All is ok to add the object to the team, but if the object is already part of + ** another team, then it must be removed from that team first. + */ + if (obj->Team.Is_Valid()) { + obj->Team->Remove(obj); + } + + /* + ** Actually add the object to the team. + */ + Quantity[typeindex]++; + obj->IsInitiated = (Member == NULL); + obj->Member = Member; + Member = obj; + obj->Team = this; + + /* + ** If a common trigger is designated for this team type, then attach the + ** trigger to this team member. + */ + if (Trigger.Is_Valid()) { + obj->Attach_Trigger(Trigger); + } + + Total++; + Risk += obj->Risk(); + if (Zone == TARGET_NONE) { + Calc_Center(Zone, ClosestMember); + } + + /* + ** Return with success, since the object was added to the team. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Can_Add -- Determines if the specified object can be added to team. * + * * + * This routine will examine the team and determine if the specified object can be * + * properly added to this team. This is a security check to filter out those objects that * + * should not be added because of conflicting priorities or other restrictions. * + * * + * INPUT: obj -- Pointer to the candidate object that is being checked for legal * + * adding to this team. * + * * + * typeindex-- The class index number (according to the team type's class array) that * + * the candidate object is classified as. The routine processes much * + * faster if you can provide this information, but if you don't, the * + * routine will figure it out. * + * * + * OUTPUT: bool; Can the specified object be added to this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Can_Add(FootClass * obj, int & typeindex) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Trying to add the team member to itself is an error condition. + */ + if (obj->Team == this) { + return(false); + } + + /* + ** The object must be active, a member of this house. A special dispensation is given to + ** units that are in radio contact. It is presumed that they are very busy and should + ** not be disturbed. + */ + if (!_Is_It_Breathing(obj) || obj->In_Radio_Contact() || obj->House != House) { + return(false); + } + + /* + ** If the object is doing some mission that precludes it from joining + ** a team then don't add it. + */ + if (obj->Mission != MISSION_NONE && !MissionClass::Is_Recruitable_Mission(obj->Mission)) { + return(false); + } + + /* + ** If this object is part of another team, then check to make sure that it + ** is permitted to leave the other team in order to join this one. If not, + ** then no further processing is allowed -- bail. + */ + if (obj->Team.Is_Valid() && (obj->Team->Class->RecruitPriority >= Class->RecruitPriority)) { + return(false); + } + + /* + ** Aircraft that have no ammo for their weapons cannot be recruited into a team. + */ + if (obj->What_Am_I() == RTTI_AIRCRAFT && obj->Techno_Type_Class()->PrimaryWeapon != NULL && !obj->Ammo) { + return(false); + } + + /* + ** Search for the exact member index that the candidate object matches. + ** If no match could be found, then adding the object to the team cannot + ** occur. + */ + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + if (typeindex == Class->ClassCount) { + return(false); + } + + /* + ** If the team is already full of this type, then adding the object is not allowed. + ** Return with a failure flag in this case. + */ + if (Quantity[typeindex] >= Class->Members[typeindex].Quantity) { + return(false); + } + + return(true); +} + + + +/*********************************************************************************************** + * TeamClass::Remove -- Removes the specified object from the team. * + * * + * Use this routine to remove an object from a team. Objects removed from the team are * + * then available to be recruited by other teams, or even by the same team at a later time. * + * * + * INPUT: obj -- Pointer to the object that is to be removed from this team. * + * * + * typeindex-- Optional index of where this object type is specified in the type * + * type class. This parameter can be omitted. It only serves to make * + * the removal process faster. * + * * + * OUTPUT: bool; Was the object removed from this team? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 01/02/1995 JLB : Initiation tracking and team captain selection. * + *=============================================================================================*/ +bool TeamClass::Remove(FootClass * obj, int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Make sure that the object is in fact a member of this team. If not, then it can't + ** be removed. Return success because the end result is the same. + */ + if (this != obj->Team) { + return(true); + } + + /* + ** Detach the common trigger for this team type. Only current and active members of the + ** team have that trigger attached. The exception is for player team members that + ** get removed from a reinforcement team. + */ + if (obj->Trigger == Trigger && !obj->House->IsPlayerControl) { + obj->Attach_Trigger(NULL); + } + + /* + ** If the proper team index was not provided, then find it in the type type class. The + ** team type class will not be set if the appropriate type could not be found + ** for this object. This indicates that the object was illegally added. Continue to + ** process however, since removing this object from the team is a good idea. + */ + if (typeindex == -1) { + for (typeindex = 0; typeindex < Class->ClassCount; typeindex++) { + if (Class->Members[typeindex].Class == &obj->Class_Of()) { + break; + } + } + } + + /* + ** Decrement the counter for the team class. There is now one less of this object type. + */ + if (typeindex < Class->ClassCount) { + Quantity[typeindex]--; + } + + /* + ** Actually remove the object from the team. Scan through the team members + ** looking for the one that matches the one specified. If it is found, it + ** is unlinked from the member chain. During this scan, a check is made to + ** ensure that at least one remaining member is still initiated. If not, then + ** a new team captain must be chosen. + */ + bool initiated = false; + FootClass * prev = 0; + FootClass * curr = Member; + bool found = false; + while (curr != NULL && (!found || !initiated)) { + if (curr == obj) { + if (prev != NULL) { + prev->Member = curr->Member; + } else { + Member = curr->Member; + } + FootClass * temp = curr->Member; + curr->Member = 0; + curr->Team = 0; + curr->SuspendedMission = MISSION_NONE; + curr->SuspendedNavCom = TARGET_NONE; + curr->SuspendedTarCom = TARGET_NONE; + curr = temp; + Total--; + found = true; + Risk -= obj->Risk(); + continue; + } + + /* + ** If this (remaining) member is initiated, then keep a record of this. + */ + initiated |= curr->IsInitiated; + + prev = curr; + curr = curr->Member; + } + + /* + ** A unit that breaks off of a team will enter idle mode. + */ + obj->Enter_Idle_Mode(); + + /* + ** If, after removing the team member, there are no initiated members left + ** in the team, then just make the first remaining member of the team the + ** team captain. Mark the center location of the team as invalid so that + ** it will be centered around the captain. + */ + if (!initiated && Member != NULL) { + Member->IsInitiated = true; + Zone = TARGET_NONE; + } + + /* + ** Must record that the team composition has changed. At the next opportunity, + ** the team members will be counted and appropriate AI adjustments made. + */ + IsAltered = JustAltered = true; + return(true); +} + + +/*********************************************************************************************** + * TeamClass::Recruit -- Attempts to recruit members to the team for the given index ID. * + * * + * This routine will take the given index ID and scan for available objects of that type * + * to recruit to the team. Recruiting will continue until that object type has either * + * been exhausted or if the team's requirement for that type has been filled. * + * * + * INPUT: typeindex -- The index for the object type to recruit. The index is used to * + * look into the type type's array of object types that make up this * + * team. * + * * + * OUTPUT: Returns with the number of objects added to this team. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + * 04/10/1995 JLB : Scans for units too. * + *=============================================================================================*/ +int TeamClass::Recruit(int typeindex) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + COORDINATE center = As_Coord(Zone); + + if (Class->Origin != -1) { + center = Cell_Coord(Scen.Waypoint[Class->Origin]); + } + + int added = 0; // Total number added to team. + + /* + ** Quick check to see if recruiting is really allowed for this index or not. + */ + if (Class->Members[typeindex].Quantity > Quantity[typeindex]) { + switch (Class->Members[typeindex].Class->What_Am_I()) { + + /* + ** For infantry objects, sweep through the infantry in the game looking for + ** ones owned by the house that owns the team. When found, try to add. + */ + case RTTI_INFANTRYTYPE: + case RTTI_INFANTRY: + { + InfantryClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Infantry.Count(); index++) { + InfantryClass * infantry = Infantry.Ptr(index); + int d = infantry->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(infantry, typeindex)) { + best = infantry; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_AIRCRAFTTYPE: + case RTTI_AIRCRAFT: + { + AircraftClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Aircraft.Count(); index++) { + AircraftClass * aircraft = Aircraft.Ptr(index); + int d = aircraft->Distance(center); + + if ((d < bestdist || bestdist == -1) && Can_Add(aircraft, typeindex)) { + best = aircraft; + bestdist = d; + } + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + } + } + break; + + case RTTI_UNITTYPE: + case RTTI_UNIT: + { + UnitClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + int d = unit->Distance(center); + + if (unit->House == House && unit->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(unit, typeindex)) { + best = unit; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + + case RTTI_VESSELTYPE: + case RTTI_VESSEL: + { + VesselClass * best = 0; + int bestdist = -1; + + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + int d = vessel->Distance(center); + + if (vessel->House == House && vessel->Class == Class->Members[typeindex].Class) { + + if ((d < bestdist || bestdist == -1) && Can_Add(vessel, typeindex)) { + best = vessel; + bestdist = d; + } + + } + + if (best) { + best->Assign_Target(TARGET_NONE); + Add(best); + added++; + + /* + ** If a transport is added to the team, the occupants + ** are added by default. + */ + FootClass * f = best->Attached_Object(); + while (f) { + Add(f); + f = (FootClass *)(ObjectClass *)f->Next; + } + } + } + } + break; + } + } + return(added); +} + + +/*********************************************************************************************** + * TeamClass::Detach -- Removes specified target from team tracking. * + * * + * When a target object is about to be removed from the game (e.g., it was killed), then * + * any team that is looking at that target must abort from that target. * + * * + * INPUT: target -- The target object that is going to be removed from the game. * + * * + * all -- Is the target going away for good as opposed to just cloaking/hiding? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Detach(TARGET target, bool ) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** If the target to detach matches the target of this team, then remove + ** the target from this team's Tar/Nav com and let the chips fall + ** where they may. + */ + if (Target == target) { + Target = TARGET_NONE; + } + if (MissionTarget == target) { + MissionTarget = TARGET_NONE; + } + if (Trigger.Is_Valid() && Trigger->As_Target() == target) { + Trigger = 0; + } +} + + +/*********************************************************************************************** + * TeamClass::Calc_Center -- Determines average location of team members. * + * * + * Use this routine to calculate the "center" location of the team. This is the average * + * position of all members of the team. Using this center value it is possible to tell * + * if a team member is too far away and where to head to in order to group up. * + * * + * INPUT: center -- Average center target location of the team. Only initiated members * + * will be considered. * + * * + * close_member--Location (as target) of the unit that is closest to the team's * + * target. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Calc_Center(TARGET & center, TARGET & close_member) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + /* + ** Presume there is no center. This will be confirmed in the following scanning + ** operation. + */ + close_member = TARGET_NONE; + center = TARGET_NONE; + + FootClass const * team_member = Member; // Working team member pointer. + + /* + ** If there are no members of the team, then there can be no center point of the team. + */ + if (team_member == NULL) return; + + /* + ** If the team is supposed to follow a nearby friendly unit, then the + ** team's "center" will actually be that unit. Otherwise, calculated the + ** average center location for the team. + */ + if (Class->MissionList[CurrentMission].Mission == TMISSION_HOUND_DOG) { + + /* + ** First pick a member of the team. The closest friendly object to that member + ** will be picked. + */ + if (!team_member) return; + + FootClass const * closest = NULL; // Current closest friendly object. + int distance = -1; // Record of last closest distance calc. + + /* + ** Scan through all vehicles. + */ + for (int unit_index = 0; unit_index < Units.Count(); unit_index++) { + FootClass const * trial_unit = Units.Ptr(unit_index); + + if (_Is_It_Breathing(trial_unit) && trial_unit->House->Is_Ally(House) && trial_unit->Team != this) { + int trial_distance = team_member->Distance(trial_unit); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_unit; + } + } + } + + /* + ** Scan through all infantry. + */ + for (int infantry_index = 0; infantry_index < Infantry.Count(); infantry_index++) { + FootClass const * trial_infantry = Infantry.Ptr(infantry_index); + + if (_Is_It_Breathing(trial_infantry) && trial_infantry->House->Is_Ally(House) && trial_infantry->Team != this) { + int trial_distance = team_member->Distance(trial_infantry); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_infantry; + } + } + } + + /* + ** Scan through all vessels. + */ + for (int vessel_index = 0; vessel_index < Vessels.Count(); vessel_index++) { + FootClass const * trial_vessel = Vessels.Ptr(vessel_index); + + if (_Is_It_Breathing(trial_vessel) && trial_vessel->House->Is_Ally(House) && trial_vessel->Team != this) { + int trial_distance = team_member->Distance(trial_vessel); + + if (distance == -1 || trial_distance < distance) { + distance = trial_distance; + closest = trial_vessel; + } + } + } + + /* + ** Set the center location as actually the friendly object that is closest. If there + ** is no friendly object, then don't set any center location at all. + */ + if (closest) { + center = closest->As_Target(); + close_member = Member->As_Target(); + } + + } else { + + long x = 0; // Accumulated X coordinate. + long y = 0; // Accumulated Y coordinate. + int dist = 0; // Closest recorded distance to team target. + int quantity = 0; // Number of team members counted. + FootClass const * closest = 0; // Closest member to target. + + /* + ** Scan through all team members and accumulate the X and Y component of their + ** location. Only team members that are active will be considered. Also keep + ** track of the team member that is closest to the team's target. + */ + while (team_member != NULL) { + if (_Is_It_Playing(team_member)) { + + /* + ** Accumulate X and Y components of qualified team members. + */ + x += Coord_X(team_member->Coord); + y += Coord_Y(team_member->Coord); + quantity++; + + /* + ** Keep a record of the team member that is nearest to the team's + ** target. + */ + int try_dist = team_member->Distance(Target); + if (!dist || try_dist < dist) { + dist = try_dist; + closest = team_member; + } + } + + team_member = team_member->Member; + } + + /* + ** If there were any qualifying members, then the team's center point can be + ** determined. + */ + if (quantity) { + x /= quantity; + y /= quantity; + COORDINATE coord = XY_Coord((int)x, (int)y); + center = ::As_Target(coord); + + + /* + ** If the center location is impassable, then just pick the location of + ** one of the team members. + */ + if (!closest->Can_Enter_Cell(As_Cell(center))) { +// if (Class->Origin != -1) { +// center = ::As_Target(Scen.Waypoint[Class->Origin]); +// } else { + center = ::As_Target(Coord_Cell(closest->Center_Coord())); +// } + } + } + + /* + ** Record the position of the closest member to the team's target and + ** that will be used as the regroup point. + */ + if (closest != NULL) { + close_member = ::As_Target(Coord_Cell(closest->Center_Coord())); + } + } +} + + +/*********************************************************************************************** + * TeamClass::Took_Damage -- Informs the team when the team member takes damage. * + * * + * This routine is used when a team member takes damage. Usually the team will react in * + * some fashion to the attack. This reaction can range from running away to assigning this * + * new target as the team's target. * + * * + * INPUT: obj -- The team member that was damaged. * + * * + * result -- The severity of the damage taken. * + * * + * source -- The perpetrator of the damage. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/29/1994 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Took_Damage(FootClass * , ResultType result, TechnoClass * source) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if ((result != RESULT_NONE) && (!Class->IsSuicide)) { + if (!IsMoving) { + + // TCTCTC + // Should run to a better hiding place or disband into a group of hunting units. + + } else { + /* + ** Respond to the attack, but not if we're an aircraft or a LST. + */ + if (source && !Is_A_Member(source) && Member && Member->What_Am_I() != RTTI_AIRCRAFT && (Member->What_Am_I() != RTTI_VESSEL || *(VesselClass *)((FootClass *)Member) != VESSEL_TRANSPORT)) { + if (Target != source->As_Target()) { + + /* + ** Don't change target if the team's target is one that can fire as well. There is + ** no point in endlessly shuffling between targets that have firepower. + */ + if (Target_Legal(Target)) { + TechnoClass * techno = As_Techno(Target); + + if (techno && ((TechnoTypeClass const &)techno->Class_Of()).PrimaryWeapon != NULL) { + if (techno->In_Range(As_Coord(Zone), 0)) { + return; + } + } + } + + /* + ** Don't change target to aggressor if the aggressor cannot normally be attacked. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT || (source->What_Am_I() == RTTI_VESSEL && (Member->What_Am_I() == RTTI_UNIT || Member->What_Am_I() == RTTI_INFANTRY))) { + return; + } + + Target = source->As_Target(); + } + } + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Attack -- Handles coordinating a team attack. * + * * + * This function is called when the team knows what it should attack. This routine will * + * give the necessary orders to the members of the team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Attack(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + /* + ** Check if they're attacking a cell. If the contents of the cell are + ** a bridge or a building/unit/techno, then it's a valid target. Otherwise, + ** the target is invalid. This only applies to non-aircraft teams. An aircraft team + ** can "attack" an empty cell and this is perfectly ok (paratrooper drop and parabombs + ** are prime examples). + */ + if (Is_Target_Cell(Target) && Member != NULL && Fetch_A_Leader()->What_Am_I() != RTTI_AIRCRAFT) { + CellClass *cellptr = &Map[As_Cell(Target)]; + TemplateType tt = cellptr->TType; + if (cellptr->Cell_Object()) { + Target = cellptr->Cell_Object()->As_Target(); + } else { + if (tt != TEMPLATE_BRIDGE1 && tt != TEMPLATE_BRIDGE2 && + tt != TEMPLATE_BRIDGE1H && tt != TEMPLATE_BRIDGE2H && + tt != TEMPLATE_BRIDGE_1A && tt != TEMPLATE_BRIDGE_1B && + tt != TEMPLATE_BRIDGE_2A && tt != TEMPLATE_BRIDGE_2B && + tt != TEMPLATE_BRIDGE_3A && tt != TEMPLATE_BRIDGE_3B ) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + FootClass *unit = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if(unit->What_Am_I() != RTTI_UNIT || + *(UnitClass *)unit != UNIT_CHRONOTANK || + mission->Mission != TMISSION_SPY) +#endif + Target = 0; // invalidize the target so it'll go to next mission. + } + } + } + + if (!Target_Legal(Target)) { + IsNextMission = true; + + } else { + + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + FootClass * unit = Member; + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)unit == INFANTRY_SPY) { + unit->Assign_Mission(MISSION_CAPTURE); + unit->Assign_Target(Target); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (mission->Mission == TMISSION_SPY && unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_CHRONOTANK) { + UnitClass *tank = (UnitClass *)unit; + tank->Teleport_To(::As_Cell(Target)); + tank->MoebiusCountDown = ChronoTankDuration * TICKS_PER_MINUTE; + Scen.Do_BW_Fade(); + Sound_Effect(VOC_CHRONOTANK1, unit->Coord); + tank->Assign_Target(TARGET_NONE); + tank->Assign_Mission(MISSION_GUARD); + } else { +#endif + if (unit->Mission != MISSION_ATTACK && unit->Mission != MISSION_ENTER && unit->Mission != MISSION_CAPTURE) { + unit->Transmit_Message(RADIO_OVER_OUT); + unit->Assign_Mission(MISSION_ATTACK); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + if (unit->TarCom != Target) { + unit->Assign_Target(Target); + } + } + + unit = unit->Member; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Regroup -- Handles team idling (regrouping). * + * * + * This routine is called when the team must delay at its current location. Team members * + * are grouped together by this function. It is called when the team needs to sit and * + * wait. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Has the team completely regrouped? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Regroup(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool retval = true; + + /* + ** Regroup default logic. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->Distance(Zone) > Rule.StrayDistance && (unit->Mission != MISSION_GUARD_AREA || !Target_Legal(unit->TarCom))) { + if (!Target_Legal(unit->NavCom)) { +// TCTCTC +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, Zone) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + + retval = false; + if (!unit->IsFormationMove) { + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + } else { + retval = true; // formations are always considered regrouped. + } + } + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (unit->Mission != MISSION_GUARD_AREA) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } + return(retval); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Do -- Handles the team performing specified mission. * + * * + * This will assign the specified mission to the team. If there are team members that are * + * too far away from the center of the team, then they will be told to move to the team's * + * location. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This only works if the special mission the team members are to perform does not * + * require extra parameters. The ATTACK and MOVE missions are particularly bad. * + * * + * HISTORY: * + * 05/11/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Do(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + MissionType do_mission = Class->MissionList[CurrentMission].Data.Mission; + + /* + ** For each unit either head it back to the team center or give it the main + ** team mission order as appropriate. + */ + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Distance(Zone) > Rule.StrayDistance * 2) { + + /* + ** Only if the unit isn't already heading to regroup with the team, will it + ** be given orders to do so. + */ + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(Zone); + unit->Assign_Mission(MISSION_MOVE); + CELL dest = unit->Adjust_Dest(As_Cell(Zone)); + unit->Assign_Destination(::As_Target(dest)); + + } else { + + /* + ** The team is regrouping, so just sit here and wait. + */ + if (!Target_Legal(unit->TarCom) && !Target_Legal(unit->NavCom) && unit->Mission != do_mission) { + unit->ArchiveTarget = TARGET_NONE; + unit->Assign_Mission(do_mission); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(TARGET_NONE); + } + } + + } + + unit = unit->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Move -- Handles team movement coordination. * + * * + * This routine is called when the team must move to a new location. Movement and grouping * + * commands associated with this task are initiated here. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Coordinate_Move(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + bool found = false; + + if (!Target_Legal(Target)) { + Target = MissionTarget; + } + + if (Target_Legal(Target)) { + + if (!Lagging_Units()) { + + while (unit != NULL) { + + /* + ** Tell the unit, if necessary, that it should regroup + ** with the main team location. If the unit is regrouping, then + ** the team should continue NOT qualify as fully reaching the desired + ** location. + */ + if (Coordinate_Conscript(unit)) { + finished = false; + } + + if (unit->Mission == MISSION_UNLOAD || unit->MissionQueue == MISSION_UNLOAD) { + finished = false; + } + + if (_Is_It_Playing(unit) && unit->Mission != MISSION_UNLOAD && unit->MissionQueue != MISSION_UNLOAD) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + if (unit->What_Am_I() == RTTI_INFANTRY && ((InfantryClass const *)unit)->Class->IsDog) { + if (Target_Legal(unit->TarCom)) stray = unit->Techno_Type_Class()->ThreatRange; + if (Target_Legal(unit->TarCom) && unit->Distance(unit->TarCom) > stray) { + unit->Assign_Target(TARGET_NONE); + } + } + found = true; + + int dist = unit->Distance(Target); + if (unit->IsFormationMove) { + if (::As_Target(Coord_Cell(unit->Coord)) != unit->NavCom) { + dist = Rule.StrayDistance + 1; // formation moves must be exact. + } + } + + if (dist > stray || + (unit->What_Am_I() == RTTI_AIRCRAFT && +// (unit->In_Which_Layer() == LAYER_TOP && + ((AircraftClass *)unit)->Height > 0 && + Coord_Cell(unit->Center_Coord()) != As_Cell(Target) && + !((AircraftClass *)unit)->Class->IsFixedWing && + Class->MissionList[CurrentMission+1].Mission != TMISSION_MOVE)) { + + bool wasform = false; + + + if (unit->Mission != MISSION_MOVE) { + unit->Assign_Mission(MISSION_MOVE); + } + + if (unit->NavCom != Target) { + + /* + ** Check if this destination should be adjusted for + ** a formation move + */ + if (Is_Target_Cell(Target) && unit->IsFormationMove) { + CELL newcell = unit->Adjust_Dest(As_Cell(Target)); + if (Coord_Cell(unit->Coord) != newcell) { + unit->Assign_Destination(::As_Target(newcell)); + } else { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + wasform = true; + } + } else { + unit->Assign_Destination(Target); + } + } + + if (!wasform) { + finished = false; + } + + } else { + if (unit->Mission == MISSION_MOVE && (!Target_Legal(unit->NavCom) || Distance(unit->NavCom) < CELL_LEPTON_W)) { + unit->Assign_Destination(TARGET_NONE); + unit->Enter_Idle_Mode(); + } + } + + /* + ** If any member still has a valid NavCom then consider this + ** movement mission to still be in progress. This will ensure + ** that the members come to a complete stop before the next + ** mission commences. Without this, the team will prematurely + ** start on the next mission even when all members aren't yet + ** in their proper spot. + */ + if (Target_Legal(unit->NavCom)) { + finished = false; + } + } + + unit = unit->Member; + } + } else { + finished = false; + } + } + + /* + ** If there are no initiated members to this team, then it certainly + ** could not have managed to move to the target destination. + */ + if (!found) { + finished = false; + } + + /* + ** If all the team members are close enough to the desired destination, then + ** move to the next mission. + */ + if (finished && IsMoving) { + IsNextMission = true; + } +} + + +/*********************************************************************************************** + * TeamClass::Lagging_Units -- Finds and orders any lagging units to catch up. * + * * + * This routine will examine the team and find any lagging units. The units are then * + * ordered to catch up to the team member that is closest to the team's destination. This * + * routine will not do anything unless lagging members are suspected. This fact is * + * indicated by setting the IsLagging flag. The flag is set by some outside agent. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure to set IsLagging for the team if a lagging member is suspected. * + * * + * HISTORY: * + * 08/01/1995 PWG : Created. * + * 04/11/1996 JLB : Modified. * + *=============================================================================================*/ +bool TeamClass::Lagging_Units(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool lag = false; + + //BG: HACK - if it's in a formation move, then disable the check for + // VG added NULL check laggers, 'cause they're all moving simultaneously. + if (unit != NULL && unit->IsFormationMove) IsLagging = false; + + /* + ** If the IsLagging bit is not set, then obviously there are no lagging + ** units. + */ + if (!IsLagging) return(false); + + /* + ** Scan through all of the units, searching for units who are having + ** trouble keeping up with the pack. + */ + while (unit != NULL) { + + if (_Is_It_Playing(unit)) { + int stray = Rule.StrayDistance; + if (unit->What_Am_I() == RTTI_AIRCRAFT) { + stray *= 3; + } + + /* + ** If we find a unit who has fallen too far away from the center of + ** the pack, then we need to order that unit to catch up with the + ** first unit. + */ + if (unit->Distance(ClosestMember) > stray) { +// TCTCTC + if (!Target_Legal(unit->NavCom)) { +// if (!Target_Legal(unit->NavCom) || ::Distance(unit->NavCom, ClosestMember) > Rule.StrayDistance) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Destination(ClosestMember); + } + lag = true; + + } else { + /* + ** We need to order all of the other units to hold their + ** position until all lagging units catch up. + */ + if (unit->Mission != MISSION_GUARD) { + unit->Assign_Mission(MISSION_GUARD); + unit->Assign_Destination(TARGET_NONE); + } + } + } + unit = unit->Member; + } + + /* + ** Once we have handled the loop we know whether there are any lagging + ** units or not. + */ + IsLagging = lag; + return(lag); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Unload -- Tells the team to unload passengers now. * + * * + * This routine tells all transport vehicles to unload passengers now. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/14/1995 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Unload(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + /* + ** Only assign the mission if the unit is carrying a passenger, OR + ** if the unit is a minelayer, with mines in it, and the cell it's + ** on doesn't have a building (read: mine) in it already. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* Also, allow unload if it's a MAD Tank. */ + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MAD )) { +#else + if (unit->Is_Something_Attached() || (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo) ) { +#endif + if (unit->Is_Something_Attached()) { + /* + ** Passenger-carrying vehicles will always return false until + ** they've unloaded all passengers. + */ + finished = false; + } + + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (Map[unit->Center_Coord()].Cell_Building() == NULL && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + + } else { + + /* + ** A loaner transport should vacate the map when all transported objects + ** have been offloaded. + */ + if (unit->IsALoaner) { + Remove(unit); + unit->Assign_Mission(MISSION_RETREAT); + unit->Commence(); + } + } + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Load -- Tells the team to load onto the transport now. * + * * + * This routine tells all non-transport units in the team to climb onto the transport in the* + * team. Note the transport must be a member of this team. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/28/1996 BWG : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Load(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + FootClass * trans = 0; + + /* + ** First locate the transport in the team, if there is one. There should + ** only be one transport in the team. + */ + while(unit != NULL && trans == NULL) { + if (unit->Techno_Type_Class()->Max_Passengers() > 0) { + trans = unit; + break; + } + unit = unit->Member; + } + + /* + ** In the case of no transport available, then consider the mission complete + ** since it can never complete otherwise. + */ + if (trans == NULL) { + IsNextMission = true; + return(1); + } + + /* + ** If the transport is already in radio contact, then this means that + ** it is in the process of loading. During this time, don't bother to assign + ** the enter mission to the other team members. + */ + if (trans->In_Radio_Contact()) { + return(1); + } + + /* + ** Find a member to assign the entry logic for. + */ + bool finished = true; + unit = Member; // re-point at the first member of the team again. + while (unit != NULL && Total > 1) { + Coordinate_Conscript(unit); + + /* + ** Only assign the mission if the unit is not the transport. + */ + if (_Is_It_Playing(unit) && unit != trans) { + if (unit->Mission != MISSION_ENTER) { + unit->Assign_Mission(MISSION_ENTER); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Destination(trans->As_Target()); + finished = false; + break; + } + finished = false; + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Coordinate_Conscript -- Gives orders to new recruit. * + * * + * This routine will give the movement orders to the conscript so that it will group * + * with the other members of the team. * + * * + * INPUT: unit -- Pointer to the conscript unit. * + * * + * OUTPUT: bool; Is the unit still scurrying to reach the team's current location? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/06/1995 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Coordinate_Conscript(FootClass * unit) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (_Is_It_Breathing(unit) && !unit->IsInitiated) { + if (unit->Distance(Zone) > Rule.StrayDistance) { + if (!Target_Legal(unit->NavCom)) { + unit->Assign_Mission(MISSION_MOVE); + unit->Assign_Target(TARGET_NONE); + unit->IsFormationMove = false; + unit->Assign_Destination(Zone); + } + return(true); + + } else { + + /* + ** This unit has gotten close enough to the team center so that it is + ** now considered initiated. An initiated unit is considered when calculating + ** the center of the team. + */ + unit->IsInitiated = true; + } + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Is_A_Member -- Tests if a unit is a member of a team * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/16/1995 PWG : Created. * + *=========================================================================*/ +bool TeamClass::Is_A_Member(void const * who) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + while (unit != NULL) { + if (unit == who) { + return(true); + } + unit = unit->Member; + } + return(false); +} + + +/*************************************************************************** + * TeamClass::Suspend_Teams -- Suspends activity for low priority teams * + * * + * INPUT: int priority - determines what is considered low priority. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/19/1995 PWG : Created. * + *=========================================================================*/ +void TeamClass::Suspend_Teams(int priority, HouseClass const * house) +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + /* + ** If a team is below the "survival priority level", then it gets + ** destroyed. The team members are then free to be reassigned. + */ + if (team != NULL && team->House == house && team->Class->RecruitPriority < priority) { + FootClass * unit = team->Member; + while (team->Member) { + team->Remove(team->Member); + } + team->IsAltered = team->JustAltered = true; + team->SuspendTimer = Rule.SuspendDelay * TICKS_PER_MINUTE; + team->Suspended = true; + } + } +} + + +/*********************************************************************************************** + * TeamClass::Is_Leaving_Map -- Checks if team is in process of leaving the map * + * * + * This routine is used to see if the team is leaving the map. A team that is leaving the * + * map gives implicit permission for its members to leave the map. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this team trying to leave the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/30/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Is_Leaving_Map(void) const +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + if (IsMoving && CurrentMission >= 0) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + if (mission->Mission == TMISSION_MOVE && !Map.In_Radar(Scen.Waypoint[mission->Data.Value])) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * TeamClass::Has_Entered_Map -- Determines if the entire team has entered the map. * + * * + * This will examine all team members and only if all of them have entered the map, will * + * it return true. This routine is used to recognize the case of a team that has been * + * generated off map and one that has already entered game play. This knowledge can lead * + * to more intelligent behavior regarding team and member disposition. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Have all members of this team entered the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamClass::Has_Entered_Map(void) const +{ + bool ok = true; + FootClass * foot = Member; + while (foot != NULL) { + if (!foot->IsLocked) { + ok = false; + break; + } + foot = (FootClass *)(ObjectClass *)(foot->Next); + } + return(ok); +} + + +/*********************************************************************************************** + * TeamClass::Scan_Limit -- Force all members of the team to have limited scan range. * + * * + * This routine is used when one of the team members cannot get within range of the team's * + * target. In such a case, the team must be assigned a new target and all members of that * + * team must recognize that a restricted target scan is required. This is done by clearing * + * out the team's target so that it will be forced to search for a new one. Also, since the * + * members are flagged for short scanning, whichever team member is picked to scan for a * + * target will scan for one that is within range. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The team will reassign its target as a result of this routine. * + * * + * HISTORY: * + * 07/26/1996 JLB : Created. * + *=============================================================================================*/ +void TeamClass::Scan_Limit(void) +{ + Assign_Mission_Target(TARGET_NONE); + FootClass * foot = Member; + while (foot != NULL) { + foot->Assign_Target(TARGET_NONE); + foot->IsScanLimited = true; + foot = foot->Member; + } +} + + +/*********************************************************************************************** + * TeamClass::TMission_Formation -- Process team formation change command. * + * * + * This routine will change the team's formation to that specified in the team command * + * parameter. It is presumed that the team will have further movement orders so that the * + * formation can serve some purpose. Merely changing the formation doesn't alter the * + * member's location. The team must be given a movement order before team member * + * repositioning will occur. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to delay before further team actions should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Formation(void) +{ + FootClass * member = Member; + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Formation = mission->Data.Formation; + int group = ID + 10; + int xdir = 0; + int ydir = 0; + bool evenodd = 1; + + /* + ** Assign appropriate formation offsets for each of the members + ** of this team. + */ + switch (Formation) { + case FORMATION_NONE: + while (member != NULL) { + member->Group = 0xFF; + member->XFormOffset = 0x80000000; + member->YFormOffset = 0x80000000; + member->IsFormationMove = false; + member = member->Member; + } + break; + case FORMATION_TIGHT: + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + } + break; + case FORMATION_LOOSE: + break; + case FORMATION_WEDGE_N: + ydir = -(Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir += 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_E: + xdir = (Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_S: + ydir = (Total / 2); + xdir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + xdir = -xdir; + evenodd ^= 1; + if (!evenodd) { + xdir -= 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_WEDGE_W: + xdir = -(Total / 2); + ydir = 0; + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = ydir; + member->IsFormationMove = true; + ydir = -ydir; + evenodd ^= 1; + if (!evenodd) { + xdir += 2; + ydir -= 2; + } + member = member->Member; + } + break; + case FORMATION_LINE_NS: + ydir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = 0; + member->YFormOffset = ydir; + member->IsFormationMove = true; + member = member->Member; + ydir += 2; + } + break; + case FORMATION_LINE_EW: + xdir = -(Total/2); + while (member != NULL) { + member->Group = group; + member->XFormOffset = xdir; + member->YFormOffset = 0; + member->IsFormationMove = true; + member = member->Member; + xdir += 2; + } + break; + } + + /* + ** Now calculate the group's movement type and speed + */ + if (Formation != FORMATION_NONE) { + TeamSpeed[group] = SPEED_WHEEL; + TeamMaxSpeed[group] = MPH_LIGHT_SPEED; + member = Member; + while (member != NULL) { + RTTIType mytype = member->What_Am_I(); + SpeedType memspeed; + MPHType memmax; + bool speedcheck = false; + + if (mytype == RTTI_INFANTRY) { + memspeed = SPEED_FOOT; + memmax = ((InfantryClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + if (mytype == RTTI_UNIT) { + memspeed = ((UnitClass *)member)->Class->Speed; + memmax = ((UnitClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (mytype == RTTI_VESSEL) { + memspeed = ((VesselClass *)member)->Class->Speed; + memmax = ((VesselClass *)member)->Class->MaxSpeed; + speedcheck = true; + } + + if (speedcheck) { + if (memmax < TeamMaxSpeed[group]) { + TeamMaxSpeed[group] = memmax; + TeamSpeed[group] = memspeed; + } + } + member = member->Member; + } + + /* + ** Now that it's all calculated, assign the movement type and + ** speed to every member of the team. + */ + member = Member; + while (member != NULL) { + member->FormationSpeed = TeamSpeed[group]; + member->FormationMaxSpeed = TeamMaxSpeed[group]; + if (member->What_Am_I() == RTTI_INFANTRY) { + member->FormationSpeed = SPEED_FOOT; + member->FormationMaxSpeed = MPH_SLOW_ISH; + } + member = member->Member; + } + } + + // Advance past the formation-setting command. + IsNextMission = true; + + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Attack -- Perform the team attack mission command. * + * * + * This will tell the team to attack the quarry specified in the team command. If the team * + * already has a target, this it is presumed that this target take precidence and it won't * + * be changed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Attack(void) +{ + if (!Target_Legal(MissionTarget) && Member != NULL) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + + /* + ** Pick a team leader that has a weapon. Only in the case of no + ** team members having any weapons, will a member without a weapon + ** be chosen. + */ + FootClass const * candidate = Fetch_A_Leader(); + + /* + ** Have the team leader pick what the next team target will be. + */ + switch (mission->Data.Quarry) { + case QUARRY_ANYTHING: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_BUILDINGS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BUILDINGS)); + break; + + case QUARRY_HARVESTERS: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_TIBERIUM)); + break; + + case QUARRY_INFANTRY: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_INFANTRY)); + break; + + case QUARRY_VEHICLES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_VEHICLES)); + break; + + case QUARRY_FACTORIES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FACTORIES)); + break; + + case QUARRY_DEFENSE: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_BASE_DEFENSE)); + break; + + case QUARRY_THREAT: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_NORMAL)); + break; + + case QUARRY_POWER: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_POWER)); + break; + + case QUARRY_FAKES: + Assign_Mission_Target(candidate->Greatest_Threat(THREAT_FAKES)); + break; + + default: + break; + } + if (!Target_Legal(MissionTarget)) IsNextMission = true; + } + Coordinate_Attack(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Spy -- Perform the team spy mission. * + * * + * This will give the team a spy mission to the location specified. It is presumed that * + * the location of the team mission actually resides under the building to be spied. If * + * no building exists at the location, then the spy operation is presumed to be a mere * + * move operation. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Spy(void) +{ + if (Is_Target_Cell(MissionTarget)) + { + CELL cell = ::As_Cell(MissionTarget); + CellClass * cellptr = &Map[cell]; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + ObjectClass * bldg = cellptr->Cell_Building(); +#else + ObjectClass * bldg = cellptr->Cell_Object(); +#endif + if (bldg != NULL) + { + Assign_Mission_Target(bldg->As_Target()); + Coordinate_Attack(); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + else + { + FootClass *member = Member; + if(member->What_Am_I() == RTTI_UNIT && *(UnitClass *)member == UNIT_CHRONOTANK) + { + bool finished = true; + while (member) + { + if ( !((UnitClass *)member)->MoebiusCountDown) finished = false; + member = member->Member; + } + + if (!finished) + { + Coordinate_Attack(); + } + else + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + } + } +#endif + } + else + { + if (!Target_Legal(MissionTarget)) + { + Assign_Mission_Target(TARGET_NONE); + IsNextMission = true; + } + else + { + Coordinate_Attack(); + } + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Follow -- Perform the "follow friendlies" team command. * + * * + * This will cause the team members to search out and follow the nearest friendly object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Follow(void) +{ + Calc_Center(Zone, ClosestMember); + Target = Zone; + Coordinate_Move(); + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Loop -- Causes the team mission processor to jump to new location. * + * * + * This is equivalent to a jump or goto command. It will alter the team command processing * + * such that it will continue processing at the command number specified. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should be performed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Loop(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + CurrentMission = mission->Data.Value-1; + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Invulnerable -- Makes the entire team invulnerable for a period of time * + * * + * This is a team mission that simulates the Iron Curtain device. It will make all team * + * members invlunerable for a temporary period of time. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Invulnerable(void) +{ + FootClass * foot = Member; + while (foot != NULL) { + foot->IronCurtainCountDown = Rule.IronCurtainDuration * TICKS_PER_MINUTE; + foot->Mark(MARK_CHANGE); + foot = foot->Member; + } + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMission_Set_Global -- Performs a set global flag operation. * + * * + * This routine is used by the team to set a global variable but otherwise perform no * + * visible effect on the team. By using this routine, sophisticated trigger dependencies * + * can be implemented. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next team logic operation should occur. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/06/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Set_Global(void) +{ + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + Scen.Set_Global_To(mission->Data.Value, true); + IsNextMission = true; + return(1); +} + + +/*********************************************************************************************** + * TeamClass::TMision_Patrol -- Handles patrolling from one location to another. * + * * + * A patrolling team will move to the designated waypoint, but along the way it will * + * periodically scan for nearby enemies. If an enemy is found, the patrol mission turns * + * into an attack mission until the target is destroyed -- after which it resumes its * + * patrol duties. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before the next call to this routine is needed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TeamClass::TMission_Patrol(void) +{ + /* + ** Reassign the movement destination if the target has been prematurely + ** cleared (probably because the object has been destroyed). + */ + if (!Target_Legal(Target)) { + TeamMissionClass const * mission = &Class->MissionList[CurrentMission]; + if ((unsigned)mission->Data.Value < WAYPT_COUNT) { + Assign_Mission_Target(::As_Target(Scen.Waypoint[mission->Data.Value])); + } + } + + /* + ** Every so often, scan for a nearby enemy. + */ + if (Frame % (Rule.PatrolTime * TICKS_PER_MINUTE) == 0) { + FootClass * leader = Fetch_A_Leader(); + if (leader != NULL) { + TARGET target = leader->Greatest_Threat(THREAT_NORMAL|THREAT_RANGE); + + if (Target_Legal(target)) { + Assign_Mission_Target(target); + } else { + Assign_Mission_Target(TARGET_NONE); + } + } + } + + /* + ** If the mission target looks like it should be attacked, then do so, otherwise + ** treat it as a movement destination. + */ + if (Is_Target_Object(Target)) { + Coordinate_Attack(); + } else { + Coordinate_Move(); + } + return(1); +} + + +int TeamClass::TMission_Deploy(void) +{ + assert(IsActive); + assert(Teams.ID(this) == ID); + + FootClass * unit = Member; + bool finished = true; + + while (unit != NULL) { + + Coordinate_Conscript(unit); + + if (_Is_It_Playing(unit)) { + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MCV) { + if (unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + if (unit->What_Am_I() == RTTI_UNIT && *(UnitClass *)unit == UNIT_MINELAYER && unit->Ammo != 0) { + /* + ** The check for a building is located here because the mine layer may have + ** already unloaded the mine but is still in the process of retracting + ** the mine layer. During this time, it should not be considered to have + ** finished its unload mission. + */ + if (!Map[unit->Center_Coord()].Cell_Building() && unit->Mission != MISSION_UNLOAD) { + unit->Assign_Destination(TARGET_NONE); + unit->Assign_Target(TARGET_NONE); + unit->Assign_Mission(MISSION_UNLOAD); + finished = false; + } + } + + } + + unit = unit->Member; + } + + if (finished) { + IsNextMission = true; + } + return(1); +} + + +/*********************************************************************************************** + * TeamClass::Fetch_A_Leader -- Looks for a suitable leader member of the team. * + * * + * This will scan through the team members looking for one that is suitable as a leader * + * type. A team can sometimes contain limboed or unarmed members. These members are not * + * suitable for leadership roles. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suitable leader type unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/27/1996 JLB : Created. * + *=============================================================================================*/ +FootClass * TeamClass::Fetch_A_Leader(void) const +{ + FootClass * leader = Member; + + /* + ** Scan through the team members trying to find one that is an active member and + ** is equipped with a weapon. + */ + while (leader != NULL) { + if (_Is_It_Playing(leader) && leader->Is_Weapon_Equipped()) break; + leader = leader->Member; + } + + /* + ** If no suitable leader was found, then just return with the first conveniently + ** accessable team member. This presumes that some member is better than no member + ** at all. + */ + if (leader == NULL) { + leader = Member; + } + + return(leader); +} \ No newline at end of file diff --git a/code/redalert/team.h b/code/redalert/team.h new file mode 100644 index 0000000..fce8402 --- /dev/null +++ b/code/redalert/team.h @@ -0,0 +1,272 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAM.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 12/11/94 * + * * + * Last Update : December 11, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAM_H +#define TEAM_H + + +#include "wwfile.h" +#include "teamtype.h" +#include "abstract.h" + +/* +** Units are only allowed to stray a certain distance away from their +** team. When they exceed this distance, some sort of fixup must be +** done. +*/ +#define STRAY_DISTANCE 2 + +class TeamClass : public AbstractClass +{ + public: + /* + ** This specifies the type of team this is. + */ + CCPtr Class; + + /* + ** This specifies the owner of this team. + */ + CCPtr House; + + /* + ** This flag forces the team into active state regardless of whether it + ** is understrength or not. + */ + unsigned IsForcedActive:1; + + /* + ** This flag is set to true when the team initiates into active mode. The + ** flag is never cleared. By examining this flag, it is possible to determine + ** if the team has ever launched into active mode. + */ + unsigned IsHasBeen:1; + + /* + ** If the team is full strength, then this flag is true. A full strength + ** team will not try to recruit members. + */ + unsigned IsFullStrength:1; + + /* + ** A team that is below half strength has this flag true. It means that the + ** the team should hide back at the owner's base and try to recruit + ** members. + */ + unsigned IsUnderStrength:1; + + /* + ** If a team is not understrength but is not yet full strength, then + ** the team is regrouping. If this flag is set and the team becomes + ** full strength, the all members of the team will become initiated + ** and this flag will be reset. + */ + unsigned IsReforming:1; + + /* + ** This bit should be set if a team is determined to have lagging + ** units in its formation. + */ + unsigned IsLagging:1; + + /* + ** If a team member was removed or added, then this flag will be set to true. The + ** team system uses this flag to tell whether it should recalculate the team + ** under strength or full strength flags. This process does not need to occur + ** EVERY time a unit added or deleted from a team, just every so often if the + ** team has been changed. + */ + unsigned IsAltered:1; + unsigned JustAltered:1; + + /* + ** If the team is working on it's primary mission (it is past the build up stage) + ** then this flag will be true. The transition between "moving" and "stationary" + ** stages usually requires some action on the team's part. + */ + unsigned IsMoving:1; + + /* + ** When the team determines that the next mission should be advanced to, it will + ** set this flag to true. Mission advance will either change the behavior of the + ** team or cause it to disband. + */ + unsigned IsNextMission:1; + + /* + ** If at least one member of this team successfully left the map, then this + ** flag will be set. At the time the team is terminated, if this flag is true, then + ** if there are any triggers that depend upon this team leaving, they will be + ** sprung. + */ + unsigned IsLeaveMap:1; + + /* + ** Records whether the team is suspended from production. + */ + unsigned Suspended:1; + + /* + ** A team will have a center point. This is the point used to determine if + ** any member of the team is "too far" from the team and must return. This + ** center point is usually calculated as the average position of all the + ** team members. + */ + TARGET Zone; + + /* + ** This is the target value of the team member that is closest to the + ** destination of the team. The implied location serves as the + ** regroup point for the unit as it is moving. + */ + TARGET ClosestMember; + + /* + ** This is the target of the team. Typically, it is a unit or structure, but + ** for the case of teams with a movement mission, it might represent a + ** destination cell. + */ + TARGET MissionTarget; + TARGET Target; + + /* + ** This is the total number of members in this team. + */ + int Total; + + /* + ** This is the teams combined risk value + */ + int Risk; + + /* + ** If this team is assigned a formation, then the formation type + ** will be stored here. If the formation type is FORMATION_NONE, then + ** the team is a loose grouping -- just like C&C. + */ + FormationType Formation; + + /* + ** This is the amount of time the team is suspended for. + */ + CDTimerClass SuspendTimer; + + /* + ** If there is a trigger that should be attached to every member of this team, then + ** it is pointed to by this. + */ + CCPtr Trigger; + + //------------------------------------------------------------ + TeamClass(TeamTypeClass const * team=0, HouseClass * owner=0); + TeamClass(NoInitClass const & x) : AbstractClass(x), Class(x), House(x), SuspendTimer(x), Trigger(x), TimeOut(x), Member(x) {}; + virtual ~TeamClass(void); + static void operator delete(void *ptr); + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void Init(void); + static void Suspend_Teams(int priority, HouseClass const * house); + void Debug_Dump(MonoClass * mono) const; + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + bool Is_Empty(void) const {return(Member == (void*)NULL);} + bool Has_Entered_Map(void) const; + void Force_Active(void) {IsForcedActive = true;IsUnderStrength=false;}; + bool Remove(FootClass *, int typeindex=-1); + void Detach(TARGET target, bool all); + void AI(void); + void Took_Damage(FootClass * obj, ResultType result, TechnoClass * source); + bool Add(FootClass *); + bool Can_Add(FootClass * obj, int & typeindex) const; + void Assign_Mission_Target(TARGET new_target); + bool Is_Leaving_Map(void) const; + void Scan_Limit(void); + + private: + + /* + ** The current mission index into the mission list is recorded here. + */ + int CurrentMission; + + /* + ** Some missions will time out. This is the timer that keeps track of the + ** time to transition between missions. + */ + CDTimerClass TimeOut; + + int TMission_Formation(void); + int TMission_Attack(void); + int TMission_Spy(void); + int TMission_Follow(void); + int TMission_Loop(void); + int TMission_Load(void); + int TMission_Unload(void); + int TMission_Invulnerable(void); + int TMission_Set_Global(void); + int TMission_Patrol(void); + int TMission_Deploy(void); + bool Coordinate_Regroup(void); + void Coordinate_Attack(void); + void Coordinate_Move(void); + bool Coordinate_Conscript(FootClass * unit); + void Coordinate_Do(void); + void Calc_Center(TARGET ¢er, TARGET &obj_center) const; + int Recruit(int typeindex); + bool Is_A_Member(void const * who) const; + bool Lagging_Units(void); + FootClass * Fetch_A_Leader(void) const; + + /* + ** Points to the first member in the list of members for this team. + */ + SmartPtr Member; + + unsigned char Quantity[TeamTypeClass::MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/teamtype.cpp b/code/redalert/teamtype.cpp new file mode 100644 index 0000000..43c02f9 --- /dev/null +++ b/code/redalert/teamtype.cpp @@ -0,0 +1,1879 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAMTYPE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/28/96 * + * * + * Last Update : July 30, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TeamMissionClass::Description -- Compose a text description of team mi * + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * TeamMission_Needs -- Determines what extra data is needed by team miss * + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * TeamTypeClass::Description -- Builds a description of the team. * + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * TeamTypeClass::Edit -- Edit the team type. * + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * TeamTypeClass::Init -- pre-scenario initialization * + * TeamTypeClass::Member_Description -- Builds a member description string * + * TeamTypeClass::Mission_From_Name -- returns mission for given name * + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * TeamTypeClass::Read_INI -- reads INI data * + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * TeamTypeClass::TeamTypeClass -- class constructor * + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * TeamTypeClass::operator delete -- 'delete' operator * + * TeamTypeClass::operator new -- 'new' operator * + * TeamTypeClass::~TeamTypeClass -- class destructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +TeamMissionClass TeamMissions[TMISSION_COUNT] = { + {TMISSION_ATTACK}, + {TMISSION_ATT_WAYPT}, + {TMISSION_FORMATION}, + {TMISSION_MOVE}, + {TMISSION_MOVECELL}, + {TMISSION_GUARD}, + {TMISSION_LOOP}, + {TMISSION_ATTACKTARCOM}, + {TMISSION_UNLOAD}, + {TMISSION_DEPLOY}, + {TMISSION_HOUND_DOG}, + {TMISSION_DO}, + {TMISSION_SET_GLOBAL}, + {TMISSION_LOAD}, + {TMISSION_SPY}, + {TMISSION_PATROL}, +}; + + + +int atoh(char * str); + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Draw_It -- Display the team type in a list box. * + * * + * This is a helper routine that is used when this team type is stored into a list box * + * and must be displayed. It will display the team type as a single line of text. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {35, 60, 80, 100}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/* +********************************** Globals ********************************** +*/ +char const * TeamTypeClass::TMissions[TMISSION_COUNT] = { + "Attack...", + "Attack Waypoint...", + "Change Formation to...", + "Move to waypoint...", + "Move to Cell...", + "Guard area (1/10th min)...", + "Jump to line #...", + "Attack Tarcom", + "Unload", + "Deploy", + "Follow friendlies", + "Do this...", + "Set global...", + "Invulnerable", + "Load onto Transport", + "Spy on bldg @ waypt...", + "Patrol to waypoint..." +}; + + +/*************************************************************************** + * TeamTypeClass::TeamTypeClass -- class constructor * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 11/22/1995 JLB : Uses initializer constructor method. * + *=========================================================================*/ +TeamTypeClass::TeamTypeClass(void) : + AbstractTypeClass(RTTI_TEAMTYPE, TeamTypes.ID(this), TXT_NONE, ""), + IsRoundAbout(false), + IsSuicide(false), + IsAutocreate(false), + IsPrebuilt(true), + IsReinforcable(true), + IsTransient(false), + RecruitPriority(7), + InitNum(0), + MaxAllowed(0), + Fear(0), + House(HOUSE_NONE), + Trigger(0), + Origin(-1), + Number(0), + MissionCount(0), + ClassCount(0) +{ + for (int i = 0; i < MAX_TEAM_CLASSCOUNT; i++) { + Members[i].Class = NULL; + Members[i].Quantity = 0; + } +} + + +/*************************************************************************** + * TeamTypeClass::Init -- pre-scenario initialization * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::Init(void) +{ + TeamTypes.Free_All(); +} + + +/*************************************************************************** + * TeamTypeClass::As_Pointer -- gets ptr for team type with given name * + * * + * INPUT: * + * name name of teamtype * + * * + * OUTPUT: * + * ptr to TeamType with that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + *=========================================================================*/ +TeamTypeClass * TeamTypeClass::As_Pointer(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (!stricmp(name, TeamTypes.Ptr(index)->IniName)) { + return(TeamTypes.Ptr(index)); + } + } + } + return(NULL); +} + + +/*************************************************************************** + * TeamTypeClass::Mission_From_Name -- returns team mission for given name * + * * + * INPUT: * + * name name to compare * + * * + * OUTPUT: * + * mission for that name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +TeamMissionType TeamTypeClass::Mission_From_Name(char const * name) +{ + if (name) { + for (TeamMissionType order = TMISSION_FIRST; order < TMISSION_COUNT; order++) { + if (stricmp(TMissions[order], name) == 0) { + return(order); + } + } + } + + return(TMISSION_NONE); +} + + +/*************************************************************************** + * TeamTypeClass::Name_From_Mission -- returns name for given mission * + * * + * INPUT: * + * order mission to get name for * + * * + * OUTPUT: * + * name of mission * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/13/1994 BR : Created. * + *=========================================================================*/ +char const * TeamTypeClass::Name_From_Mission(TeamMissionType order) +{ + assert((unsigned)order < TMISSION_COUNT); + + return(TMissions[order]); +} + + +/*************************************************************************** + * TeamTypeClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new TeamType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void * TeamTypeClass::operator new(size_t ) +{ + void * ptr = TeamTypes.Allocate(); + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*************************************************************************** + * TeamTypeClass::operator delete -- 'delete' operator * + * * + * INPUT: * + * ptr pointer to delete * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=========================================================================*/ +void TeamTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TeamTypeClass *)ptr)->IsActive = false; + } + TeamTypes.Free((TeamTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TeamTypeClass::Create_One_Of -- Creates a team of this type. * + * * + * Use this routine to create a team object from this team type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly created team object. If one could not be * + * created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +TeamClass * TeamTypeClass::Create_One_Of(void) const +{ + if (ScenarioInit || Number < MaxAllowed) { +// if (ScenarioInit || TeamClass::Number[ID] < MaxAllowed) { + return(new TeamClass(this, HouseClass::As_Pointer(House))); + } + return(NULL); +} + + +/*********************************************************************************************** + * TeamTypeClass::Destroy_All_Of -- Destroy all teams of this type. * + * * + * This routine will destroy all teams of this type. Typical use of this is from a trigger * + * event. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Destroy_All_Of(void) const +{ + for (int index = 0; index < Teams.Count(); index++) { + TeamClass * team = Teams.Ptr(index); + + if (team->Class == this) { + delete team; + index--; + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Suggested_New_Team -- Suggests a new team to create. * + * * + * This routine will scan through the team types available and create teams of the * + * type that can best utilize the existing unit mix. * + * * + * INPUT: house -- Pointer to the house that this team is to be created for. * + * * + * atypes -- A bit mask of the aircraft types available for this house. * + * * + * utypes -- A bit mask of the unit types available for this house. * + * * + * itypes -- A bit mask of the infantry types available for this house. * + * * + * vtypes -- A bit mask of the vessel types available for this house. * + * * + * alerted -- Is this house alerted? If true, then the Autocreate teams will be * + * considered in the selection process. * + * * + * OUTPUT: Returns with a pointer to the team type that should be created. If no team should * + * be created, then it returns NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/13/1995 JLB : Created. * + * 07/21/1995 JLB : Will autocreate team even if no members in field. * + *=============================================================================================*/ +TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long , long , long , long , bool alerted) +//TeamTypeClass const * TeamTypeClass::Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted) +{ +// TeamTypeClass const * best = NULL; +// int bestvalue = 0; + + TeamTypeClass const * choices[20]; + int choicecount = 0; + + for (int index = 0; index < TeamTypes.Count(); index++) { + TeamTypeClass const * ttype = TeamTypes.Ptr(index); + + assert(ttype != NULL); + + int maxnum = ttype->MaxAllowed; + if ((alerted && !ttype->IsAutocreate) || (!alerted && ttype->IsAutocreate)) { + maxnum = 0; + } + + if (choicecount >= ARRAY_SIZE(choices)) break; + + if (ttype != NULL && ttype->House == house->Class->House && ttype->Number < maxnum) { + + choices[choicecount++] = ttype; + +#ifdef OBSOLETE + /* + ** Determine what kind of units this team requires. + */ + long uneeded = 0; + long ineeded = 0; + long vneeded = 0; + long aneeded = 0; + for (int ctype = 0; ctype < ttype->ClassCount; ctype++) { + switch (ttype->Members[ctype].Class->What_Am_I()) { + case RTTI_INFANTRYTYPE: + ineeded |= (1 << ((InfantryTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_UNITTYPE: + uneeded |= (1 << ((UnitTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_VESSELTYPE: + vneeded |= (1 << ((VesselTypeClass *)ttype->Members[ctype].Class)->Type); + break; + + case RTTI_AIRCRAFTTYPE: + aneeded |= (1 << ((AircraftTypeClass *)ttype->Members[ctype].Class)->Type); + break; + } + } + + /* + ** If this team can use the types required, then consider it a possible + ** team type to create. + */ + int value = 0; + if ((aneeded & atypes) != 0 || (ineeded & itypes) != 0 || (uneeded & utypes) != 0 || (vneeded & vtypes) != 0) { + value = ttype->RecruitPriority; + } else { + value = ttype->RecruitPriority/2; + } + + if (best == NULL || bestvalue < value) { + bestvalue = value; + best = ttype; + } +#endif + } + } + + if (choicecount > 0) { + return(choices[Random_Pick(0, choicecount-1)]); + } + return(NULL); + +// return(best); +} + + +/*********************************************************************************************** + * TeamTypeClass::From_Name -- Converts a name into a team type pointer. * + * * + * This routine is used to convert an ASCII name of a team type into the corresponding * + * team type pointer. * + * * + * INPUT: name -- Pointer to the ASCII name of the team type. * + * * + * OUTPUT: Returns with a pointer to the team type that this ASCII name represents. If there * + * is no match, the NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +TeamTypeClass * TeamTypeClass::From_Name(char const * name) +{ + if (name) { + for (int index = 0; index < TeamTypes.Count(); index++) { + if (stricmp(name, TeamTypes.Ptr(index)->IniName) == 0) { + return(TeamTypes.Ptr(index)); + } + } + } + return(0); +} + + +/*********************************************************************************************** + * TeamMission_Needs -- Determines what extra data is needed by team mission. * + * * + * This routine will return the required extra data that the specified team mission will * + * need. * + * * + * INPUT: tmtype -- The team mission type to check. * + * * + * OUTPUT: Returns with the data type needed for this team mission. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/26/1996 JLB : Created. * + *=============================================================================================*/ +NeedType TeamMission_Needs(TeamMissionType tmtype) +{ + switch (tmtype) { + /* + ** Requires a formation type. + */ + case TMISSION_FORMATION: + return(NEED_FORMATION); + + /* + ** Team mission requires a target quarry value. + */ + case TMISSION_ATTACK: + return(NEED_QUARRY); + + /* + ** Team mission requires a data value. + */ + case TMISSION_MOVECELL: + return(NEED_HEX_NUMBER); + + case TMISSION_SET_GLOBAL: + case TMISSION_GUARD: + case TMISSION_LOOP: + return(NEED_NUMBER); + + /* + ** Team mission requires a waypoint. + */ + case TMISSION_PATROL: + case TMISSION_MOVE: + case TMISSION_ATT_WAYPT: + case TMISSION_SPY: + return(NEED_WAYPOINT); + + /* + ** Team mission requires a general mission type. + */ + case TMISSION_DO: + return(NEED_MISSION); + + default: + break; + } + return(NEED_NONE); +} + + +#ifdef SCENARIO_EDITOR +/*********************************************************************************************** + * TeamMissionClass::Draw_It -- Draws a team mission list box entry. * + * * + * This routine will display a team mission list box entry. It converts the index number * + * into the appropriate text string and then displays the text at the coordinates * + * specified. * + * * + * INPUT: index -- The index number to use when rendering the team mission. * + * * + * x,y -- The X,Y coordinate to use when drawing the team mission entry. * + * * + * width, height -- The dimensions of the area that the description can be * + * rendered into. * + * * + * selected -- Is this entry currently selected? If so, then it should be rendered * + * differently. * + * * + * flags -- Text print control flags used for when the text is printed. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +void TeamMissionClass::Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, + scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(index), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(index), x, y, + (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Edit -- Edit the team type. * + * * + * This routine handles the editing dialog box for the team type. * + * * + * INPUT: none * + * * + * OUTPUT: Was the team type edited? A FALSE indicates that the dialog was canceled. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +bool TeamTypeClass::Edit(void) +{ + /* + ** Dialog & button dimensions + */ + enum { + D_DIALOG_W = 400, + D_DIALOG_H = 250, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + D_NAME_X=D_DIALOG_X+35, + D_NAME_Y=D_DIALOG_Y+27, + ED_WIDTH=40, + + D_CHECK_X=D_DIALOG_X+35, // Start of check box attribute list. + D_CHECK_Y=D_NAME_Y+25, + + CB_SPACING_Y=9, // Vertical spacing between check box lines. + CB_SPACING_X=8, // Horizontal spacing for check box description text. + D_SPACING_X=9, // Horizontal spacing between data entry fields. + + D_CANCEL_W = 50, + D_CANCEL_H = 9, + D_CANCEL_X = D_DIALOG_X + D_DIALOG_W - (D_CANCEL_W+35), + D_CANCEL_Y = D_DIALOG_Y + D_DIALOG_H - (D_CANCEL_H+20), + + D_OK_W = 50, + D_OK_H = 9, + D_OK_X = D_DIALOG_X + D_DIALOG_W - (D_OK_W+18)*2, + D_OK_Y = D_CANCEL_Y + }; + + /* + ** Button enumerations: + */ + enum { + BUTTON_NAME=100, + BUTTON_RECRUIT, + BUTTON_MAXNUM, + BUTTON_INITNUM, + BUTTON_HOUSE, + BUTTON_ROUNDABOUT, + BUTTON_LEARNING, + BUTTON_SUICIDE, + BUTTON_AUTO, + BUTTON_PREBUILT, + BUTTON_REINFORCE, + BUTTON_MISSION1, + BUTTON_MISSION2, + BUTTON_ADD, + BUTTON_INSERT, + BUTTON_DELETE, + BUTTON_REPLACE, + BUTTON_ARG, + BUTTON_FORMATION, + BUTTON_MEMBERS, + BUTTON_MISSION, + BUTTON_TRIGGER, + BUTTON_ORIGIN, + BUTTON_OK, + BUTTON_CANCEL, + BUTTON_QUARRY, + }; + + /* + ** Dialog variables: + */ + ControlClass * commands = 0; + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Team name edit field. + */ + char name_buf[10]; + EditClass name_edt(BUTTON_NAME, name_buf, sizeof(name_buf), + TPF_EFNT | TPF_NOSHADOW, + D_NAME_X, D_NAME_Y, ED_WIDTH, 9, EditClass::ALPHANUMERIC); + strcpy(name_buf, IniName); + commands = &name_edt; + + /* + ** House ownership of this team. + */ + char housetext[25] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X+name_edt.Width+D_SPACING_X, name_edt.Y, 55, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + housebtn.Add(*commands); + + /* + ** Recruit priority for this team. + */ + char recr_buf[4]; + EditClass recr_edt(BUTTON_RECRUIT, recr_buf, sizeof(recr_buf), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X+housebtn.Width+5+D_SPACING_X, housebtn.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(recr_buf, "%d", RecruitPriority); + recr_edt.Add(*commands); + + /* + ** Maximum allowed for this team type. + */ + char maxnum_buf[4]; + EditClass maxnum_edt(BUTTON_MAXNUM, maxnum_buf, sizeof(maxnum_buf), + TPF_EFNT | TPF_NOSHADOW, + recr_edt.X+recr_edt.Width+D_SPACING_X, recr_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(maxnum_buf, "%d", MaxAllowed); + maxnum_edt.Add(*commands); + + /* + ** Initial number for this team type. + */ + char initnum_buf[4]; + EditClass initnum_edt(BUTTON_INITNUM, initnum_buf, sizeof(initnum_buf), + TPF_EFNT | TPF_NOSHADOW, + maxnum_edt.X+maxnum_edt.Width+D_SPACING_X, maxnum_edt.Y, ED_WIDTH, 9, EditClass::NUMERIC); + sprintf(initnum_buf, "%d", InitNum); + initnum_edt.Add(*commands); + + /* + ** Waypoint preference to create/reinforce this team. + */ + char origin[4]; + EditClass originbtn(BUTTON_ORIGIN, origin, sizeof(origin), + TPF_EFNT | TPF_NOSHADOW, + initnum_edt.X+initnum_edt.Width+D_SPACING_X, initnum_edt.Y, 20, 9, EditClass::ALPHA); + *originbtn.Get_Text() = '\0'; + if (Origin != -1) { + if (Origin < 26) { + sprintf(originbtn.Get_Text(), "%c", Origin + 'A'); + } else { + sprintf(originbtn.Get_Text(), "%c%c", (Origin/26) + 'A'-1, Origin % 26 + 'A'); + } + } + originbtn.Add(*commands); + + /* + ** Members of this team control button. + */ + TextButtonClass membersbtn(BUTTON_MEMBERS, "Members", TPF_EBUTTON, name_edt.X, name_edt.Y+12, 50); + membersbtn.Add(*commands); + + /* + ** Trigger to assign to each member of this team (when object joins team). + */ + char trigtext[25] = ""; + DropListClass triggerbtn(BUTTON_TRIGGER, trigtext, sizeof(trigtext), + TPF_EFNT | TPF_NOSHADOW, + D_DIALOG_X+D_DIALOG_W-95, membersbtn.Y, 60, 8*5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + triggerbtn.Add_Item(""); + for (int index = 0; index < TriggerTypes.Count(); index++) { + triggerbtn.Add_Item(TriggerTypes.Ptr(index)->IniName); + } + if (Trigger.Is_Valid()) { + triggerbtn.Set_Selected_Index(Trigger->Name()); + } else { + triggerbtn.Set_Selected_Index(0); + } + triggerbtn.Add(*commands); + + /* + ** Roundabout travel logic attribute for this team. + */ + CheckBoxClass roundbtn(BUTTON_ROUNDABOUT, D_CHECK_X, D_CHECK_Y); + if (IsRoundAbout) { + roundbtn.Turn_On(); + } else { + roundbtn.Turn_Off(); + } + roundbtn.Add(*commands); + + /* + ** Suicide travel to target attribute. + */ + CheckBoxClass suicidebtn(BUTTON_SUICIDE, D_CHECK_X, roundbtn.Y+CB_SPACING_Y); + if (IsSuicide) { + suicidebtn.Turn_On(); + } else { + suicidebtn.Turn_Off(); + } + suicidebtn.Add(*commands); + + /* + ** Autocreate attribute for this team. + */ + CheckBoxClass autocreatebtn(BUTTON_AUTO, D_CHECK_X, suicidebtn.Y+CB_SPACING_Y); + if (IsAutocreate) { + autocreatebtn.Turn_On(); + } else { + autocreatebtn.Turn_Off(); + } + autocreatebtn.Add(*commands); + + /* + ** Prebuild team members attribute for this team. + */ + CheckBoxClass prebuildbtn(BUTTON_PREBUILT, D_CHECK_X, autocreatebtn.Y+CB_SPACING_Y); + if (IsPrebuilt) { + prebuildbtn.Turn_On(); + } else { + prebuildbtn.Turn_Off(); + } + prebuildbtn.Add(*commands); + + /* + ** Reinforce this team in progress attribute. + */ + CheckBoxClass reinforcebtn(BUTTON_REINFORCE, D_CHECK_X, prebuildbtn.Y+CB_SPACING_Y); + if (IsReinforcable) { + reinforcebtn.Turn_On(); + } else { + reinforcebtn.Turn_Off(); + } + reinforcebtn.Add(*commands); + + /* + ** Create drop box of possible team missions. This is used when building/editing the + ** team mission list. + */ + char droptext[45]; + DropListClass missionlist1(BUTTON_MISSION1, droptext, sizeof(droptext), + TPF_EFNT | TPF_NOSHADOW, + reinforcebtn.X, reinforcebtn.Y+15, 170, 8*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TeamMissionType tm = TMISSION_FIRST; tm < TMISSION_COUNT; tm++) { + missionlist1.Add_Item(TeamTypeClass::Name_From_Mission(tm)); + } + missionlist1.Set_Selected_Index(0); + missionlist1.Add_Tail(*commands); + + /* + ** Optional mission argument entry field. + */ + char arg_buf[6] = {0}; + EditClass arg_edt(BUTTON_ARG, arg_buf, sizeof(arg_buf), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 60, -1, EditClass::ALPHANUMERIC); +// arg_edt.Add(*commands); + + char qtext[55]; + DropListClass qlist(BUTTON_QUARRY, qtext, sizeof(qtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist.Add_Item(QuarryName[q]); + } + qlist.Set_Selected_Index(0); + qlist.Add_Tail(*commands); + + char ftext[55]; + DropListClass flist(BUTTON_FORMATION, ftext, sizeof(ftext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (FormationType f = FORMATION_FIRST; f < FORMATION_COUNT; f++) { + flist.Add_Item(FormationName[f]); + } + flist.Set_Selected_Index(0); + flist.Add_Tail(*commands); + + char mtext[55]; + DropListClass mlist(BUTTON_MISSION, mtext, sizeof(mtext), + TPF_EFNT | TPF_NOSHADOW, + missionlist1.X + missionlist1.Width + 15, missionlist1.Y, 100, 5*8, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (MissionType m = MISSION_FIRST; m < MISSION_COUNT; m++) { + mlist.Add_Item(MissionClass::Mission_Name(m)); + } + mlist.Set_Selected_Index(0); + mlist.Add_Tail(*commands); + + TListClass missionlist2(BUTTON_MISSION2, missionlist1.X+60, missionlist1.Y+22, 240, 8*7, + TPF_EFNT | TPF_NOSHADOW, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (index = 0; index < MissionCount; index++) { + missionlist2.Add_Item(new TeamMissionClass(MissionList[index])); +// missionlist2.Add_Item(&TeamMissions[MissionList[index].Mission]); + } + static int tabs[] = {13, 40}; // list box tab stops + missionlist2.Set_Tabs(tabs); + missionlist2.Add_Tail(*commands); + + /* + ** Mission editing command buttons. + */ + TextButtonClass addbtn(BUTTON_ADD, "Append", TPF_EBUTTON, D_NAME_X, missionlist1.Y+missionlist1.Height+1, 50); + addbtn.Add(*commands); + + TextButtonClass insertbtn(BUTTON_INSERT, "Insert", TPF_EBUTTON, addbtn.X, addbtn.Y+10, 50); + insertbtn.Add(*commands); + + TextButtonClass delbtn(BUTTON_DELETE, "Delete", TPF_EBUTTON, insertbtn.X, insertbtn.Y+10, 50); + delbtn.Add(*commands); + + TextButtonClass repbtn(BUTTON_REPLACE, "Replace", TPF_EBUTTON, delbtn.X, delbtn.Y+10, 50); + repbtn.Add(*commands); + + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_OK_X, D_OK_Y, D_OK_W, D_OK_H); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_CANCEL_X, D_CANCEL_Y, D_CANCEL_W, D_CANCEL_H); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the list + */ + okbtn.Add(*commands); + cancelbtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + int lastindex = -1; + int lastcount = -1; + int lastbutton = -1; + bool cancel = false; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display) { + + /* + ** Add the optional data entry field as necessary. + */ + arg_edt.Remove(); + qlist.Remove(); + flist.Remove(); + mlist.Remove(); + switch (TeamMission_Needs(TeamMissionType(missionlist1.Current_Index()))) { + default: + break; + + case NEED_MISSION: + mlist.Add(*commands); + break; + + case NEED_FORMATION: + flist.Add(*commands); + break; + + case NEED_WAYPOINT: + case NEED_NUMBER: + case NEED_HEX_NUMBER: + arg_edt.Add(*commands); + break; + + case NEED_QUARRY: + qlist.Add(*commands); + break; + } + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + + Draw_Caption(TXT_TEAM_EDIT, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Pri:", recr_edt.X, recr_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Max:", maxnum_edt.X, maxnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Num:", initnum_edt.X, initnum_edt.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Loc:", originbtn.X, originbtn.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("# Team Mission", missionlist2.X, missionlist2.Y-7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Trigger:", triggerbtn.X-4, triggerbtn.Y+1, scheme, TBLACK, TPF_RIGHT | TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print(Member_Description(), membersbtn.X + membersbtn.Width + 3, membersbtn.Y+1, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + Fancy_Text_Print("Use safest, possibly longer, route to target?", roundbtn.X+CB_SPACING_X, roundbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Charge toward target ignoring distractions?", suicidebtn.X+CB_SPACING_X, suicidebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Only 'Autocreate A.I.' uses this team type?", autocreatebtn.X+CB_SPACING_X, autocreatebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Prebuild team members before team is created?", prebuildbtn.X+CB_SPACING_X, prebuildbtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Automatically reinforce team whenever possible?", reinforcebtn.X+CB_SPACING_X, reinforcebtn.Y, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + /* + ** Redraw the buttons + */ + commands->Draw_All(); + Show_Mouse(); + + display = false; + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + + /* + ** Select a Mission on the right-hand mission list; update the Argument + ** field to reflect the current value. This only serves as an aide to editing + ** the team mission list. + */ + case BUTTON_MISSION2 | KN_BUTTON: + if (missionlist2.Count() && lastcount == missionlist2.Count() && lastbutton == BUTTON_MISSION2 && lastindex == missionlist2.Current_Index()) { + missionlist1.Set_Selected_Index(missionlist2.Current_Item()->Mission); + + switch (TeamMission_Needs(missionlist2.Current_Item()->Mission)) { + case NEED_MISSION: + mlist.Set_Selected_Index(missionlist2.Current_Item()->Data.Mission); + break; + + case NEED_FORMATION: + flist.Set_Selected_Index(missionlist2.Current_Item()->Data.Formation); + break; + + case NEED_NUMBER: + sprintf(arg_edt.Get_Text(), "%d", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(arg_edt.Get_Text(), "%x", missionlist2.Current_Item()->Data.Value); + break; + + case NEED_QUARRY: + strcpy(qlist.Get_Text(), QuarryName[missionlist2.Current_Item()->Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (missionlist2.Current_Item()->Data.Value < 26) { + sprintf(arg_edt.Get_Text(), "%c", missionlist2.Current_Item()->Data.Value + 'A'); + } else { + sprintf(arg_edt.Get_Text(), "%c%c", (missionlist2.Current_Item()->Data.Value)/26 + 'A'-1, (missionlist2.Current_Item()->Data.Value % 26) + 'A'); + } + break; + } + } + lastindex = missionlist2.Current_Index(); + lastcount = missionlist2.Count(); + display = true; + break; + + /* + ** Add current mission data to current position of team mission list. Any + ** subsequent missions get moved downward. + */ + case BUTTON_INSERT | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Insert_Item(tm); + } + display = true; + break; + + /* + ** Add mission data to the end of the mission list. + */ + case BUTTON_ADD | KN_BUTTON: + if (missionlist2.Count() < MAX_TEAM_MISSIONS) { + TeamMissionClass * tm = new TeamMissionClass; + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A';; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A';; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2.Add_Item(tm); + display = true; + } + break; + + /* + ** Replace the currently selected mission with the work mission data. + */ + case BUTTON_REPLACE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = (TeamMissionClass *)missionlist2.Current_Item(); + tm->Mission = TeamMissionType(missionlist1.Current_Index()); + tm->Data.Value = 0; + switch (TeamMission_Needs(tm->Mission)) { + case NEED_MISSION: + tm->Data.Mission = MissionType(mlist.Current_Index()); + break; + + case NEED_FORMATION: + tm->Data.Formation = FormationType(flist.Current_Index()); + break; + + case NEED_QUARRY: + tm->Data.Quarry = QuarryType(qlist.Current_Index()); + break; + + case NEED_NUMBER: + tm->Data.Value = atoi(arg_edt.Get_Text()); + break; + + case NEED_HEX_NUMBER: + tm->Data.Value = atoh(arg_edt.Get_Text()); + break; + + case NEED_WAYPOINT: + tm->Data.Value = toupper(*arg_edt.Get_Text()) - 'A'; + if ( *((arg_edt.Get_Text())+1)) { + tm->Data.Value = (tm->Data.Value+1)*26; + tm->Data.Value += toupper(*((arg_edt.Get_Text())+1)) - 'A'; + } + if ((unsigned)tm->Data.Value >= WAYPT_HOME) { + tm->Data.Value = 0; + } + break; + + default: + tm->Data.Value = 0; + break; + } + missionlist2[missionlist2.Current_Index()] = tm; + } + display = true; + break; + + /* + ** Delete the currently selected mission. + */ + case BUTTON_DELETE | KN_BUTTON: + if (missionlist2.Count()) { + TeamMissionClass * tm = (TeamMissionClass*)missionlist2.Current_Item(); + missionlist2.Remove_Index(missionlist2.Current_Index()); + delete tm; + } + display = true; + break; + + /* + ** Invoke the members dialog + */ + case BUTTON_MEMBERS | KN_BUTTON: + + /* + ** Take editor focus away + */ + membersbtn.Turn_Off(); + + /* + ** Invoke the dialog + */ + Map.Team_Members(HousesType(housebtn.Current_Index())); + + /* + ** Redraw + */ + display = true; + break; + + /* + ** When the OK button is selected, lift the values from the dialog box + ** and place them into the team type object. + */ + case BUTTON_OK | KN_BUTTON: + strtrim(name_edt.Get_Text()); + if (strlen(name_edt.Get_Text()) != 0) { + strcpy(IniName, name_edt.Get_Text()); + } else { + strcpy(IniName, "----"); + } + + IsRoundAbout = roundbtn.IsOn; + IsSuicide = suicidebtn.IsOn; + IsAutocreate = autocreatebtn.IsOn; + IsPrebuilt = prebuildbtn.IsOn; + IsReinforcable = reinforcebtn.IsOn; + + RecruitPriority = atoi(recr_edt.Get_Text()); + InitNum = atoi(initnum_edt.Get_Text()); + MaxAllowed = atoi(maxnum_edt.Get_Text()); + House = HousesType(housebtn.Current_Index()); + Trigger = NULL; + if (triggerbtn.Current_Index() > 0) { + Trigger = TriggerTypes.Ptr(triggerbtn.Current_Index()-1); + } + + MissionCount = missionlist2.Count(); + for (index = 0; index < MissionCount; index++) { + MissionList[index].Data.Value = 0; // Clears extra bits. + MissionList[index] = *missionlist2[index]; + } + +#ifdef TOFIX +// the mission class objects pointed to in the list should be deleted? +#endif + + if (strlen(originbtn.Get_Text())) { + if (strlen(originbtn.Get_Text()) == 1) { + Origin = toupper(*originbtn.Get_Text()) - 'A'; + } else { + Origin = (toupper(*originbtn.Get_Text())+1 - 'A' ) * 26; + Origin += toupper(*(originbtn.Get_Text()+1)) - 'A'; + } + } else { + Origin = -1; + } + cancel = false; + process = false; + break; + + /* + ** Cancel: return + */ + case BUTTON_CANCEL | KN_BUTTON: + cancel = true; + process = false; + break; + + /* + ** Unrecognized events are ignored. If any button related event is + ** detected, then collapse any drop down list boxes. This keeps the + ** dialog box clean. + */ + default: + if (input & KN_BUTTON) { + housebtn.Collapse(); + missionlist1.Collapse(); + triggerbtn.Collapse(); + display = true; + } + break; + } + + /* + ** Record the last dialog control touched so that a double click + ** can be detected. + */ + if (input & KN_BUTTON) { + lastbutton = (input & ~KN_BUTTON); + } + } + + return(!cancel); +} + + +int atoh(char * str) +{ + int retval = 0; + while (*str) { + retval *= 16; + if (*str >= '0' && *str <= '9') { + retval += *str-'0'; + } else { + if (*str >= 'a' && *str <= 'f') { + retval += 10 + (*str - 'a'); + } else { + if (*str >= 'A' && *str <= 'F') { + retval += 10 + (*str - 'A'); + } + } + } + str++; + } + return(retval); +} + +#endif + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TeamTypeClass::Member_Description -- Builds a member description string. * + * * + * This routine will build a team member description string. The string will be composed * + * of the team member type and quantity. As many team member types will be listed that * + * can fit within a reasonable size. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the text string that contains a description of the team * + * type members. * + * * + * WARNINGS: The return string may be truncated if necessary to fit within reasonable size * + * limits. * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Member_Description(void) const +{ + static char buffer[128]; + + buffer[0] = '\0'; + + /* + ** Fill in class & count for all classes + */ + for (int index = 0; index < ClassCount; index++) { + char txt[10]; + + strcat(buffer, Members[index].Class->IniName); + strcat(buffer, ":"); + + sprintf(txt, "%d", Members[index].Quantity); + strcat(buffer, txt); + + if (index < ClassCount-1) { + strcat(buffer, ","); + } + } + + if (strlen(buffer) > 25) { + strcpy(&buffer[25-3], "..."); + } + + return(buffer); +} + + +/*********************************************************************************************** + * TeamTypeClass::Description -- Builds a description of the team. * + * * + * This routine will build a brief description of the team type. This description is used * + * in the team type list. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the composed text string that represents the team type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamTypeClass::Description(void) const +{ + static char _buffer[128]; + char extra = ' '; + char loc[3]; + + loc[0] = loc[1] = loc[2] = 0; + if (IsAutocreate) extra = '*'; + if (Origin > -1) { +// if (Origin != -1) { + if (Origin < 26) { + loc[0] = 'A' + Origin; + } else { + loc[0] = Origin / 26 + 'A'-1; + loc[1] = Origin % 26 + 'A'; + } + } + + sprintf(_buffer, "%s\t%s\t%c%s\t%d\t%s", IniName, HouseTypeClass::As_Reference(House).Suffix, extra, loc, MissionCount, Member_Description()); + return(_buffer); +} + + +/*********************************************************************************************** + * TeamMissionClass::Description -- Compose a text description of team mission. * + * * + * This routine will create a text representation of a team mission. This description will * + * be used in the list of team missions for display purposes. * + * * + * INPUT: index -- The index to assign to this team. The index is used since some team * + * missions refer to the another team mission by index number. * + * * + * OUTPUT: Returns with a pointer to the team mission text. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/05/1996 JLB : Created. * + *=============================================================================================*/ +char const * TeamMissionClass::Description(int index) const +{ + static char buffer[64]; + + sprintf(buffer, "%d\t%s", index, TeamTypeClass::Name_From_Mission(Mission)); + + switch (TeamMission_Needs(Mission)) { + case NEED_MISSION: + strcat(buffer, MissionClass::Mission_Name(Data.Mission)); + break; + + case NEED_FORMATION: + strcat(buffer, FormationName[Data.Quarry]); + break; + + case NEED_NUMBER: + sprintf(&buffer[strlen(buffer)], "%d", Data.Value); + break; + + case NEED_HEX_NUMBER: + sprintf(&buffer[strlen(buffer)], "%x", Data.Value); + break; + + case NEED_QUARRY: + strcat(buffer, QuarryName[Data.Quarry]); + break; + + case NEED_WAYPOINT: + if (Data.Value < 26) { + sprintf(&buffer[strlen(buffer)], "%c", Data.Value + 'A'); + } else { + sprintf(&buffer[strlen(buffer)], "%c%c", (Data.Value/26) + 'A'-1, (Data.Value % 26) + 'A'); + } + break; + + } + + return(buffer); +} +#endif + + +/*********************************************************************************************** + * TeamTypeClass::Detach -- Detach the specified target from this team type. * + * * + * This routine is called when some object is about to be removed from the game system and * + * all references to it must be severed. This will check to see if the specified object * + * is a trigger that this team refers to. If so, then the reference will be cleared. * + * * + * INPUT: target -- The target object to remove references to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Detach(TARGET target, bool) +{ + if (Is_Target_TriggerType(target) && Trigger.Is_Valid() && Trigger == As_TriggerType(target)) { + Trigger = NULL; + } +} + + +/*************************************************************************** + * TeamTypeClass::Read_INI -- reads INI data * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 12/07/1994 BR : Created. * + * 02/01/1995 BR : No del team if no classes (editor needs empty teams!) * + *=========================================================================*/ +void TeamTypeClass::Read_INI(CCINIClass & ini) +{ + TeamTypeClass * team; // Working team pointer. + char buf[500]; // INI entry buffer + + int len = ini.Entry_Count(INI_Name()); + + /* + ** Loop for all team entries, create and fill in. + */ + for (int index = 0; index < len; index++) { + team = new TeamTypeClass(); + if (team != NULL) { + char const * entry = ini.Get_Entry(INI_Name(), index); + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + team->Fill_In((char *)entry, buf); + } + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given teamtype with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * TeamName = Housename,Roundabout,Learning,Suicide,Spy,Mercenary, * + * RecruitPriority,MaxAllowed,InitNum,Fear, * + * ClassCount,Class:Num,Class:Num,..., * + * MissionCount,Mission:Arg,Mission:Arg,Mission:Arg,... * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + * 11/29/1995 JLB : Revamped to use new team class. * + *=============================================================================================*/ +void TeamTypeClass::Fill_In(char * name, char * entry) +{ + assert(TeamTypes.ID(this) == ID); + + /* + ** Set its name + */ + Set_Name(name); + + House = HousesType(atoi(strtok(entry, ","))); + + int code; + switch (NewINIFormat) { + default: + code = atoi(strtok(NULL, ",")); + IsRoundAbout = ((code & 0x0001) != 0); + IsSuicide = ((code & 0x0002) != 0); + IsAutocreate = ((code & 0x0004) != 0); + IsPrebuilt = ((code & 0x0008) != 0); + IsReinforcable = ((code & 0x0010) != 0); + break; + + case 0: + case 1: + IsRoundAbout = atoi(strtok(NULL, ",")); + IsSuicide = atoi(strtok(NULL, ",")); + IsAutocreate = atoi(strtok(NULL, ",")); + IsPrebuilt = atoi(strtok(NULL, ",")); + IsReinforcable = atoi(strtok(NULL, ",")); + break; + } + + RecruitPriority = atoi(strtok(NULL, ",")); + InitNum = atoi(strtok(NULL, ",")); + MaxAllowed = atoi(strtok(NULL, ",")); + Origin = atoi(strtok(NULL, ",")); + + switch (NewINIFormat) { + default: + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + break; + + case 0: + case 1: + // Throw this token away -- it isn't used. + strtok(NULL, ","); + break; + } + + /* + ** Fetch the team member types and quantity values. + */ + ClassCount = atoi(strtok(NULL, ",")); + for (int index = 0; index < ClassCount; index++) { + char * p1 = strtok(NULL, ",:"); + char * p2 = strtok(NULL, ",:"); + TechnoTypeClass const * otype = NULL; + + /* + ** See if this is an infantry name + */ + InfantryType i_id = InfantryTypeClass::From_Name(p1); + if (i_id != INFANTRY_NONE) { + otype = &InfantryTypeClass::As_Reference(i_id); + } else { + + /* + ** See if this is a unit name + */ + UnitType u_id = UnitTypeClass::From_Name(p1); + if (u_id != UNIT_NONE) { + otype = &UnitTypeClass::As_Reference(u_id); + } else { + + /* + ** See if this is an aircraft name + */ + AircraftType a_id = AircraftTypeClass::From_Name(p1); + if (a_id != AIRCRAFT_NONE) { + otype = &AircraftTypeClass::As_Reference(a_id); + } else { + + /* + ** See if this is a vessel name. + */ + VesselType v_id = VesselTypeClass::From_Name(p1); + if (v_id != VESSEL_NONE) { + otype = &VesselTypeClass::As_Reference(v_id); + } + } + } + } + + /* + ** If the name was resolved, add this class + */ + if (otype) { + if (index < MAX_TEAM_CLASSCOUNT) { + Members[index].Class = otype; + Members[index].Quantity = atoi(p2); + } + } else { + ClassCount--; + if (index == 0) break; + index--; + } + } + ClassCount = min(MAX_TEAM_CLASSCOUNT, ClassCount); + + /* + ** Fetch the missions assigned to this team type. + */ + MissionCount = atoi(strtok(NULL, ",")); + for (int index = 0; index < MissionCount; index++) { + MissionList[index].Mission = TeamMissionType(atoi(strtok(NULL, ",:"))); + MissionList[index].Data.Value = atoi(strtok(NULL, ",:")); + } + + if (NewINIFormat < 2) { + /* + ** Fetch the trigger ID. + */ + Trigger.Set_Raw(atoi(strtok(NULL, ","))); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Write_INI -- Write out the team types to the INI database. * + * * + * This routine will take all team types and write them out to the INI database specified. * + * * + * INPUT: ini -- Reference to the INI database that will hold al the teams. * + * * + * OUTPUT: none * + * * + * WARNINGS: All preexisting team data in the database will be erased by this routine. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("TeamTypes"); + ini.Clear(INI_Name()); + + /* + ** Now write all the team data out + */ + for (int index = 0; index < TeamTypes.Count(); index++) { +// for (int index = TeamTypes.Count()-1; index >= 0; index--) { + TeamTypeClass * team = TeamTypes.Ptr(index); + char buf[256]; + + buf[0] = 0; + team->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), team->IniName, buf); + } +} + + +/*********************************************************************************************** + * TeamTypeClass::Build_INI_Entry -- Builds the INI entry for this team type. * + * * + * This routine is used to build the text string that will go into the INI database for * + * a team of this type. This text string will be parsed back into a team object when the * + * scenario INI is read in. * + * * + * INPUT: buf -- Pointer to a buffer that will hold the team text entry. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure that the buffer can hold the string build. A size of 80 or so is * + * usually sufficient. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TeamTypeClass::Build_INI_Entry(char * buf) +{ + int code = 0; + code |= IsRoundAbout ? 0x0001 : 0; + code |= IsSuicide ? 0x0002 : 0; + code |= IsAutocreate ? 0x0004 : 0; + code |= IsPrebuilt ? 0x0008 : 0; + code |= IsReinforcable ? 0x0010 : 0; + + /* + ** Output the general data for this team type. + */ + sprintf(buf, "%d,%d,%d,%d,%d,%d,%d", + House, + code, + RecruitPriority, + InitNum, + MaxAllowed, + Origin, + TriggerTypes.Logical_ID(Trigger) + ); + buf += strlen(buf); + + /* + ** For every class in the team, record the class's name & desired count + */ + sprintf (buf, ",%d", ClassCount); + buf += strlen(buf); + for (int i = 0; i < ClassCount; i++) { + sprintf (buf, ",%s:%d", Members[i].Class->IniName, Members[i].Quantity); + buf += strlen(buf); + } + + /* + ** Record the # of missions, and each mission name & argument value. + */ + sprintf(buf, ",%d", MissionCount); + buf += strlen(buf); + for (int i = 0; i < MissionCount; i++) { + sprintf (buf, ",%d:%d", MissionList[i].Mission, MissionList[i].Data.Value); + buf += strlen(buf); + } +} + + diff --git a/code/redalert/teamtype.h b/code/redalert/teamtype.h new file mode 100644 index 0000000..131ca15 --- /dev/null +++ b/code/redalert/teamtype.h @@ -0,0 +1,282 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEAMTYPE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEAMTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/02/96 * + * * + * Last Update : July 2, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEAMTYPE_H +#define TEAMTYPE_H + +/* +** TeamMissionType: the various missions that a team can have. +*/ +typedef enum TeamMissionType : char { + TMISSION_NONE=-1, + TMISSION_ATTACK, // Attack specified quarry type. + TMISSION_ATT_WAYPT, // Attack specified waypoint + TMISSION_FORMATION, // Change formation of team. + TMISSION_MOVE, // moves to waypoint specified. + TMISSION_MOVECELL, // moves to cell # specified. + TMISSION_GUARD, // works like an infantry's guard mission + TMISSION_LOOP, // loop back to start of mission list + TMISSION_ATTACKTARCOM, // attack tarcom + TMISSION_UNLOAD, // Unload at current location. + TMISSION_DEPLOY, // Deploy mobile building type. + TMISSION_HOUND_DOG, // Follow nearest friendly unit. + TMISSION_DO, // Do guard, sticky, area guard (mission sticks on this). + TMISSION_SET_GLOBAL, // Set global variable. + TMISSION_INVULNERABLE, // Magical invulnerability. + TMISSION_LOAD, // Load onto transport member of team. + TMISSION_SPY, // Spy enter the building at specified waypoint + TMISSION_PATROL, // Move but look for enemies as well. + + TMISSION_COUNT, + TMISSION_FIRST=0 +} TeamMissionType; + + +/* +** Forward declarations. +*/ +class TechnoTypeClass; + + +/* +** This structure contains one team mission value & its argument. +*/ +class TeamMissionClass +{ + public: +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(int index) const; + operator const char * () const {return(Description(0));}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TeamMissionType Mission; // Mission type. + union { + FormationType Formation; // Formation to use. + QuarryType Quarry; // Combat quarry type. + MissionType Mission; // General mission orders. + int Value; // Usually a waypoint number. + } Data; +}; + + +/* +** This class specifies the quantity and type of members desired for the +** team. +*/ +class TeamMemberClass +{ + public: + int Quantity; // Number of objects desired for this type. + TechnoTypeClass const * Class; // The type of object desired. +}; + + +/* +** TeamTypeClass declaration +*/ +class TeamTypeClass : public AbstractTypeClass +{ + public: + enum TeamTypeClassEnums { + MAX_TEAM_CLASSCOUNT=5, + MAX_TEAM_MISSIONS=20 + }; + + /* + ** Constructor/Destructor + */ + TeamTypeClass(void); + TeamTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Trigger(x) {}; + virtual ~TeamTypeClass(void) {}; + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all team types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + void Build_INI_Entry(char * buffer); + static void Read_INI(CCINIClass & ini); + void Fill_In(char *name, char *entry); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "TeamTypes";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** As_Pointer gets a pointer to the trigger object give its name + */ + static TeamTypeClass *As_Pointer(char const * name); + + /* + ** Processing routines + */ + TeamClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + void Detach(TARGET target, bool all=true); + + /* + ** Utility routines + */ + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + static char const * Name_From_Mission(TeamMissionType order); + static TeamMissionType Mission_From_Name(char const *name); + static TeamTypeClass const * Suggested_New_Team(HouseClass * house, long atypes, long utypes, long itypes, long vtypes, bool alerted); + static TeamTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Member_Description(void) const; + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + + /* + ** If this teamtype object is active, then this flag will be true. + ** TeamType objects that are not active are either not yet created or have + ** been deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** If RoundAbout, the team avoids high-threat areas + */ + unsigned IsRoundAbout:1; + + /* + ** If Suicide, the team won't stop until it achieves its mission or it's + ** dead + */ + unsigned IsSuicide:1; + + /* + ** Is this team type allowed to be created automatically by the computer + ** when the appropriate trigger indicates? + */ + unsigned IsAutocreate:1; + + /* + ** This flag tells the computer that it should build members to fill + ** a team of this type regardless of whether there actually is a team + ** of this type active. + */ + unsigned IsPrebuilt:1; + + /* + ** If this team should allow recruitment of new members, then this flag + ** will be true. A false value results in a team that fights until it + ** is dead. This is similar to IsSuicide, but they will defend themselves. + */ + unsigned IsReinforcable:1; + + /* + ** A transient team type was created exclusively to bring on reinforcements + ** as a result of some special event. As soon as there are no teams + ** existing of this type, then this team type should be deleted. + */ + unsigned IsTransient:1; + + /* + ** Priority given the team for recruiting purposes; higher priority means + ** it can steal members from other teams (scale: 0 - 15) + */ + int RecruitPriority; + + /* + ** Initial # of this type of team + */ + unsigned char InitNum; + + /* + ** Max # of this type of team allowed at one time + */ + unsigned char MaxAllowed; + + /* + ** Fear level of this team + */ + unsigned char Fear; + + /* + ** House the team belongs to + */ + HousesType House; + + /* + ** Trigger to assign to each object as it joins this team. + */ + CCPtr Trigger; + + /* + ** This is the waypoint origin to use when creating this team or + ** when bringing the team on as a reinforcement. + */ + WAYPOINT Origin; + + /* + ** This records the number of teams of this type that are currently + ** active. + */ + int Number; + + /* + ** Number and list of missions that this team will follow. + */ + int MissionCount; + TeamMissionClass MissionList[MAX_TEAM_MISSIONS]; + + /* + ** Number and type of members desired for this team. + */ + int ClassCount; + TeamMemberClass Members[MAX_TEAM_CLASSCOUNT]; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + static char const * TMissions[TMISSION_COUNT]; +}; + + +#endif \ No newline at end of file diff --git a/code/redalert/techno.cpp b/code/redalert/techno.cpp new file mode 100644 index 0000000..29d6634 --- /dev/null +++ b/code/redalert/techno.cpp @@ -0,0 +1,6911 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TECHNO.CPP 5 3/17/97 1:28a Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : BASE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 8, 1994 * + * * + * Last Update : November 1, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TechnoClass::AI -- Handles AI processing for techno object. * + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order* + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * TechnoClass::Captured -- Handles capturing this object. * + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon.* + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate.* + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * TechnoClass::Mark -- Handles marking of techno objects. * + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * TechnoClass::Owner -- Who is the owner of this object? * + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects.* + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * TechnoClass::Renovate -- Heal a building to maximum * + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * TechnoClass::Set_Mission -- Forced mission set (used by editor). * + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * TechnoClass::TechnoClass -- Constructor for techno type objects. * + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * TechnoClass::Value -- Fetches the target value for this object. * + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** Cloaking control values. +*/ +#define MAX_UNCLOAK_STAGE 38 + +/*************************************************************************** +** These are the pointers to the special shape data that the units may need. +*/ +void const * TechnoTypeClass::WakeShapes = 0; +void const * TechnoTypeClass::TurretShapes = 0; +void const * TechnoTypeClass::SamShapes = 0; +void const * TechnoTypeClass::MGunShapes = 0; + +//Xlat Tables for French and German +// For name overriding +//#define NEWNAMES 22 +#ifdef GERMAN +const char* NewName[] = { + "Scout Ant", "Kundschafter-Ameise", + "Warrior Ant", "Krieger-Ameise", + "Fire Ant", "Feuer-Ameise", + "Queen Ant", "Ameisenk”nigin", + "ATS", "Angriffs-U-Boot", + "Tesla Tank", "Telsapanzer", + "Volkov", "Modell Volkov", + "Chitzkoi", "Roboterhund", + "Stavros", "Stavros", + "F-A Longbow", "Jagdhubschrauber Longbow", + "Civilian Specialist", "Wissenschaftler", + "Alloy Facility", "Legierungswerk", + NULL, + }; + +#endif +#ifdef FRENCH +const char* NewName[] = { + "Scout Ant", "Fourmi de Reconnaissance", + "Warrior Ant", "Fourmi GuerriŠre", + "Fire Ant", "Fourmi Lance-Flammes", + "Queen Ant", "Reine des Fourmis", + "ATS", "SMTA", + "Tesla Tank", "Tank Tesla", + "Volkov", "Volkov", + "Chitzkoi", "Cyber-Chien", + "Stavros", "Stavros", + "F-A Longbow", "HAA (H‚licoptŠre d'Assaut Avanc‚)", + "Civilian Specialist", "Sp‚cialiste Civil", + "Alloy Facility", "Usine M‚tallurgique", + NULL, + }; + +#endif + + +/*************************************************************************** +** Which shape to use depending on which facing is controlled by these arrays. +*/ +int const TechnoClass::BodyShape[32] = {0,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}; + + +/*********************************************************************************************** + * TechnoClass::Is_Players_Army -- Determines if this object is part of the player's army. * + * * + * The player's army is considered to be all those mobile units that can be selected * + * and controlled by the player (they may or may not have weapons in the traditional * + * sense). * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object part of the player's selectable controllable army? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/23/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Players_Army(void) const +{ + /* + ** An object that is dead (or about to be) is not considered part of + ** the player's army. + */ + if (Strength <= 0) { + return(false); + } + + /* + ** If the object is not yet on the map or is otherwise indisposed, then + ** don't consider it. + */ + if (IsInLimbo || !IsLocked) { + return(false); + } + + /* + ** Buildings, although sometimes serving a combat purpose, are not part + ** of the player's army. + */ + if (What_Am_I() == RTTI_BUILDING) { + return(false); + } + + /* + ** If not discoverd by the player, then don't consider it part of the + ** player's army (yet). + */ + if (!Is_Discovered_By_Player()) { + return(false); + } + + /* + ** If not selectable, then not really part of the player's active army. + */ + if (!Techno_Type_Class()->IsSelectable) { + return(false); + } + + /* + ** If not under player control, then it isn't part of the player's army. + */ + if (!House->IsPlayerControl) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Teleport_Here -- Checks cell to see if a valid teleport destination. * + * * + * Use this routine to examine the cell specified and if the cell could be a valid * + * destination for a teleport, then return true. * + * * + * INPUT: cell -- The cell to examine as a possible legal teleport destination. * + * * + * OUTPUT: bool; Is the specified cell a legal teleport destination? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Teleport_Here(CELL cell) const +{ + /* + ** Infantry are never allowed to teleport. + */ + if (What_Am_I() == RTTI_INFANTRY) { + return(false); + } + + /* + ** The destination cell must be on the playfield. + */ + if (!Map.In_Radar(cell)) { + return(false); + } + + /* + ** Teleporting directly onto the flag spot is not allowed. This is due to the + ** requirement that entering that location must proceed under normal channels. + */ + if (Map[cell].Overlay == OVERLAY_FLAG_SPOT) { + return(false); + } + + /* + ** Determine if the object could enter the cell by normal means. If the + ** cell is impassable, then it can't be teleported there. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (!Map[cell].Is_Clear_To_Move(ttype->Speed, true, true, -1, ttype->Speed == SPEED_FLOAT ? MZONE_WATER : MZONE_NORMAL)) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::What_Weapon_Should_I_Use -- Determines what is the best weapon to use. * + * * + * This routine will compare the weapons this object is equipped with verses the * + * candidate target object. The best weapon to use against the target will be returned. * + * Special emphasis is given to weapons that can fire on the target without requiring * + * this object to move within range. * + * * + * INPUT: target -- The candidate target to determine which weapon is best against. * + * * + * OUTPUT: Returns with an identifier the specifies what weapon to use against the target. * + * The return value will be "0" for the primary weapon and "1" for the secondary. * + * * + * WARNINGS: This routine is called very frequently. It should be as efficient as possible. * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::What_Weapon_Should_I_Use(TARGET target) const +{ + if (!Target_Legal(target)) return(0); + + /* + ** Fetch the armor of the candidate target object. Presume that if the target + ** is not an object, then its armor is equivalent to wood. Who knows why? + */ + ArmorType armor = ARMOR_WOOD; + if (Is_Target_Object(target)) { + armor = As_Object(target)->Class_Of().Armor; + } + + TechnoTypeClass const * ttype = Techno_Type_Class(); + + /* + ** Get the value of the primary weapon verses the candidate target. Increase the + ** value of the weapon if it happens to be in range. + */ + int w1 = 0; + WeaponTypeClass const * wptr = ttype->PrimaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w1 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 0)) w1 *= 2; + FireErrorType ok = Can_Fire(target, 0); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w1 = 0; + + /* + ** Calculate a similar value for the secondary weapon. + */ + int w2 = 0; + wptr = ttype->SecondaryWeapon; + if (wptr != NULL && wptr->WarheadPtr != NULL) w2 = wptr->WarheadPtr->Modifier[armor] * 1000; + if (In_Range(target, 1)) w2 *= 2; + ok = Can_Fire(target, 1); + if (ok == FIRE_CANT || ok == FIRE_ILLEGAL) w2 = 0; + + /* + ** Return with the weapon identifier that should be used to fire upon the + ** candidate target. + */ + if (w2 > w1) return(1); + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::How_Many_Survivors -- Determine the number of survivors to escape. * + * * + * This routine will determine the number of survivors that should run from this object * + * when it is destroyed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of survivors that should run from this object * + * when the object gets destroyed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/04/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::How_Many_Survivors(void) const +{ + if (Techno_Type_Class()->IsCrew) { + return(1); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Combat_Damage -- Fetch the amount of damage infliced by the specified weapon. * + * * + * This routine will examine the specified weapon of this object and determine how much * + * damage it could inflict -- best case. * + * * + * INPUT: which -- Which weapon to consider. If -1 is specified, then the average of both * + * weapon types (if present) is returned. * + * * + * OUTPUT: Returns with the combat damage that could be inflicted by the specified weapon. * + * * + * WARNINGS: This routine could return a negative number if a medic is scanned. * + * * + * HISTORY: * + * 09/16/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Combat_Damage(int which) const +{ + TechnoTypeClass const * ttype = Techno_Type_Class(); + + int divisor = 0; + int value = 0; + + if (which == 0 || which == -1) { + if (ttype->PrimaryWeapon != NULL) { + value += ttype->PrimaryWeapon->Attack; + divisor += 1; + } + } + + if (which == 1 || which == -1) { + if (ttype->SecondaryWeapon != NULL) { + value += ttype->SecondaryWeapon->Attack; + divisor += 1; + } + } + + if (divisor > 1) { + return(value / divisor); + } + return(value); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Recloak -- Can this object recloak? * + * * + * Determine is this object can recloak now and returns that info. Usually the answer is * + * yes, but it can be overridden be derived classes. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object recloak now? (presumes it has the ability to cloak) * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Recloak(void) const +{ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Coord -- Determine the coordinate where bullets appear. * + * * + * This routine will determine the coordinate to use when this object fires. The coordinate * + * is the location where bullets appear (or fire effects appear) when the object fires * + * its weapon. * + * * + * INPUT: which -- Which weapon is the coordinate to be calculated for? 0 means primary * + * weapon, 1 means secondary weapon. * + * * + * OUTPUT: Returns with the coordinate that any bullets fired from the specified weapon * + * should appear. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TechnoClass::Fire_Coord(int which) const +{ + assert(IsActive); + + DirType dir = Turret_Facing(); + TechnoTypeClass const * tclass = Techno_Type_Class(); + + int dist = 0; + int lateral = 0; + if (which == 0) { + dist = tclass->PrimaryOffset; + lateral = tclass->PrimaryLateral; + } else { + dist = tclass->SecondaryOffset; + lateral = tclass->SecondaryLateral; + } + + COORDINATE coord = Coord_Move(Center_Coord(), DIR_N, tclass->VerticalOffset + Height); + coord = Coord_Move(coord, DIR_E, tclass->HorizontalOffset); + if (IsSecondShot) { + coord = Coord_Move(coord, (DirType)(dir+DIR_E), lateral); + } else { + coord = Coord_Move(coord, (DirType)(dir+DIR_W), lateral); + } + coord = Coord_Move(coord, dir, dist); + + return(coord); +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TechnoClass::Debug_Dump -- Displays the base class data to the monochrome screen. * + * * + * This routine is used to dump the status of the object class to the monochrome screen. * + * This display can be used to track down or prevent bugs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Debug_Dump(MonoClass * mono) const +{ + assert(IsActive); + mono->Set_Cursor(10, 7);mono->Printf("%2d", Fetch_Rate()); + mono->Set_Cursor(14, 7);mono->Printf("%2d", Fetch_Stage()); + mono->Set_Cursor(49, 1);mono->Printf("%3d", Ammo); + mono->Set_Cursor(71, 1);mono->Printf("$%4d", PurchasePrice); + mono->Set_Cursor(54, 1);mono->Printf("%3d", (long)Arm); + if (Is_Something_Attached()) { + mono->Set_Cursor(1, 5);mono->Printf("%08X", Attached_Object()); + } + if (Target_Legal(TarCom)) { + mono->Set_Cursor(29, 3);mono->Printf("%08X", TarCom); + } + if (Target_Legal(SuspendedTarCom)) { + mono->Set_Cursor(38, 3);mono->Printf("%08X", SuspendedTarCom); + } + if (Target_Legal(ArchiveTarget)) { + mono->Set_Cursor(69, 5);mono->Printf("%08X", ArchiveTarget); + } + mono->Set_Cursor(47, 3);mono->Printf("%02X:%02X", PrimaryFacing.Current(), PrimaryFacing.Desired()); + mono->Set_Cursor(64, 1);mono->Printf("%d(%d)", Cloak, CloakingDevice); + + mono->Fill_Attrib(14, 15, 12, 1, IsUseless ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 16, 12, 1, IsTickedOff ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(14, 17, 12, 1, IsCloakable ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 13, 12, 1, IsLeader ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 14, 12, 1, IsALoaner ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 15, 12, 1, IsLocked ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 16, 12, 1, IsInRecoilState ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(27, 17, 12, 1, IsTethered ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 13, 12, 1, IsOwnedByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 14, 12, 1, IsDiscoveredByPlayer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 15, 12, 1, IsDiscoveredByComputer ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(40, 16, 12, 1, IsALemon ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Set_Cursor(47, 17);mono->Printf("%3d", (long)IronCurtainCountDown); + mono->Fill_Attrib(40, 17, 12, 1, IronCurtainCountDown > 0 ? MonoClass::INVERSE : MonoClass::NORMAL); + + RadioClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TechnoClass::TechnoClass -- Default constructor for techno objects. * + * * + * This default constructor for techno objects is used to reset all internal variables to * + * their default state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +TechnoClass::TechnoClass(RTTIType rtti, int id, HousesType house) : + RadioClass(rtti, id), + IsUseless(false), + IsTickedOff(false), + IsCloakable(false), + IsLeader(false), + IsALoaner(false), + IsLocked(false), + IsInRecoilState(false), + IsTethered(false), + IsOwnedByPlayer(false), + IsDiscoveredByPlayer(false), + IsDiscoveredByComputer(false), + IsALemon(false), + IsSecondShot(true), + ArmorBias(1), + FirepowerBias(1), + IdleTimer(0), + IronCurtainCountDown(0), + SpiedBy(0), + ArchiveTarget(TARGET_NONE), + House(HouseClass::As_Pointer(house)), + Cloak(UNCLOAKED), + TarCom(TARGET_NONE), + SuspendedTarCom(TARGET_NONE), + PrimaryFacing(DIR_N), + Arm(0), + Ammo(-1), + ElectricZapDelay(-1), + ElectricZapTarget(0), + ElectricZapWhich(0), + PurchasePrice(0) +{ + //IsOwnedByPlayer = (PlayerPtr == House); + // Added for multiplayer changes. ST - 4/24/2019 10:40AM + IsDiscoveredByPlayerMask = 0; + if (Session.Type == GAME_NORMAL) { + IsOwnedByPlayer = (PlayerPtr == House); + } else { + IsOwnedByPlayer = House->IsHuman; + } +} + + +/*********************************************************************************************** + * TechnoClass::Time_To_Build -- Determines the time it would take to build this. * + * * + * Use this routine to determine the amount of time it would take to build an object of * + * this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time it should take (unmodified by outside factors) to build * + * this object. The time is expressed in game frames. * + * * + * WARNINGS: The time will usually be modified by power status and handicap rating for the * + * owning house. The value returned is merely the raw unmodified time to build. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + * 09/27/1996 JLB : Takes into account the power availability. * + *=============================================================================================*/ +//#define UNIT_BUILD_BIAS fixed(5,4) +//#define UNIT_BUILD_BIAS fixed(6,4) +#define UNIT_BUILD_BIAS fixed(1,1) +//#define UNIT_BUILD_BIAS fixed(5,1) + +extern int UnitBuildPenalty; + +int TechnoClass::Time_To_Build(void) const +{ + int val = Class_Of().Time_To_Build(); + +#ifdef FIXIT_VERSION_3 + if (Session.Type == GAME_NORMAL ) { +#else + if (Session.Type == GAME_NORMAL || + PlayingAgainstVersion == VERSION_RED_ALERT_104 || + PlayingAgainstVersion == VERSION_RED_ALERT_107){ +#endif + val *= House->BuildSpeedBias; + }else{ + if (What_Am_I() == RTTI_BUILDING || What_Am_I() == RTTI_INFANTRY) { + val *= House->BuildSpeedBias; + } else { + val *= House->BuildSpeedBias * fixed (UnitBuildPenalty, 100); //UNIT_BUILD_BIAS; + } + } + + + /* + ** Adjust the time to build based on the power output of the owning house. + */ + fixed power = House->Power_Fraction(); + if (power > 1) power = 1; + if (power < 1 && power > fixed::_3_4) power = fixed::_3_4; + if (power < fixed::_1_2) power = fixed::_1_2; + power.Inverse(); + val *= power; + + int divisor = House->Factory_Count(What_Am_I()); + if (divisor != 0) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack: allow the multiple-factory bonus, but only up to two factories if +// this is an AM<->AM game. + if(NewUnitsEnabled) { + val /= min(divisor,2); + } else { + val /= divisor; + } +#else + val /= divisor; +#endif + } + return(val); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Visible_On_Radar -- Is this object visible on player's radar screen? * + * * + * Use this routine to determine if this object should be visible on the player's radar * + * screen. This routine doesn't take into consideration mapped terrain or whether the * + * radar is active. It merely checks to see that if all else is functioning correctly, * + * is this unit invisible or visible on the radar map. Typically, cloaked vehicles will * + * not be visible. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object nominally visible on the player's radar screen? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/27/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Visible_On_Radar(void) const +{ + /* + ** Hack: MRJ is invisible to radar, unless it's allied with the player. + */ + if (What_Am_I() == RTTI_UNIT) { + if(*((UnitClass *)this) == UNIT_MRJ) { + if(!House->Is_Ally(PlayerPtr)) { + return(false); + } + } + } + if (!Techno_Type_Class()->IsInvisible && (Cloak != CLOAKED || House->Is_Ally(PlayerPtr))) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Revealed -- Handles revealing an object to the house specified. * + * * + * When a unit moves out from under the shroud or when it gets delivered into already * + * explored terrain, then it must be "revealed". Objects that are revealed may be * + * announced to the player. The discovered bit updated accordingly. * + * * + * INPUT: house -- The house that this object is revealed to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + * 12/27/1994 JLB : Discovered trigger event processing. * + *=============================================================================================*/ +bool TechnoClass::Revealed(HouseClass * house) +{ + assert(IsActive); + + /* Replaced by client/server version from TD. ST - 8/7/2019 11:09AM + if (house == PlayerPtr && IsDiscoveredByPlayer) return(false); + if (house != PlayerPtr) { + if (IsDiscoveredByComputer) return(false); + IsDiscoveredByComputer = true; + } + */ + if (house == NULL) { + return false; + } + + if (Is_Discovered_By_Player(house)) { + return false; + } + + if (house->IsHuman == false) { + if (IsDiscoveredByComputer) { + return false; + } + IsDiscoveredByComputer = true; + } + + if (RadioClass::Revealed(house)) { + + /* + ** An enemy object that is discovered will go into hunt mode if + ** its current mission is to ambush. + */ + if (!house->IsHuman && Mission == MISSION_AMBUSH) { + Assign_Mission(MISSION_HUNT); + } + + if (Session.Type == GAME_NORMAL) { + if (house == PlayerPtr) { + IsDiscoveredByPlayer = true; + + if (!IsOwnedByPlayer) { + + /* + ** If there is a trigger event associated with this object, then process + ** it for discovery purposes. + */ + if (!ScenarioInit && Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_DISCOVERED, this); + } + + /* + ** Alert the enemy house to presence of the friendly side. + */ + House->IsDiscovered = true; + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } else { + IsDiscoveredByComputer = true; + } + + /* + ** A newly revealed object will always perform a look operation. + */ + Look(); + } + } else { + IsDiscoveredByComputer = true; + } + + } else { + + if (house->IsHuman) { + Set_Discovered_By_Player(house); + } + + Look(); + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Hidden -- Returns the object back into the hidden state. * + * * + * This routine is called for every object that returns to the darkness shroud or when * + * it gets destroyed. This also occurs when an object enters another (such as infantry * + * entering a transport helicopter). * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Hidden(void) +{ + assert(IsActive); + + //if (!IsDiscoveredByPlayer) return; + if (!Is_Discovered_By_Player()) { // ST - 8/7/2019 11:17AM + return; + } + if (!House->IsHuman) { + Clear_Discovered_By_Players(); + } +} + + +/*********************************************************************************************** + * TechnoClass::Mark -- Handles marking of techno objects. * + * * + * On the Techno-level, marking handles transmission of the redraw command to any object * + * that it is 'connected' with. This only occurs during loading and unloading. * + * * + * INPUT: mark -- The marking method. This routine just passes this on to base classes. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Mark(MarkType mark) +{ + assert(IsActive); + + if (RadioClass::Mark(mark)) { + + /* + ** When redrawing an object, if there is another object tethered to this one, + ** redraw it as well. + */ + if (IsTethered) { + Transmit_Message(RADIO_REDRAW); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Receive_Message -- Handles inbound message as appropriate. * + * * + * This routine is used to handle inbound radio messages. It only handles those messages * + * that deal with techno objects. Typically, these include loading and unloading. * + * * + * INPUT: from -- Pointer to the originator of the radio message. * + * * + * message -- The inbound radio message. * + * * + * param -- Reference to optional parameter that might be used to transfer * + * more information than is possible with the simple radio message * + * type. * + * * + * OUTPUT: Returns with the radio response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 06/17/1995 JLB : Handles tether contact messages. * + *=============================================================================================*/ +RadioMessageType TechnoClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(IsActive); + + switch (message) { + + /* + ** Just received instructions to attack the specified target. + */ + case RADIO_ATTACK_THIS: + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + Assign_Target(param); + Assign_Mission(MISSION_ATTACK); + return(RADIO_ROGER); + } + break; + + /* + ** Establish a tethered connection to the object in radio contact. + */ + case RADIO_TETHER: + if (!IsTethered) { + IsTethered = true; + Transmit_Message(RADIO_TETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** Break the tethered connection to the object in radio contact. + */ + case RADIO_UNTETHER: + if (IsTethered) { + IsTethered = false; + Transmit_Message(RADIO_UNTETHER, from); + return(RADIO_ROGER); + } + break; + + /* + ** A tethered unit has reached it's destination. All is + ** clear, so radio contact can be broken. + */ + case RADIO_UNLOADED: + Transmit_Message(RADIO_UNTETHER, from); + return(Transmit_Message(RADIO_OVER_OUT, from)); + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + Transmit_Message(RADIO_UNTETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Request to be informed when unloaded. This message is transmitted + ** by the transport unit to the transported unit as it is about to be + ** unloaded. It is saying, "All is clear. Drive off now." + */ + case RADIO_UNLOAD: + case RADIO_BACKUP_NOW: + case RADIO_HOLD_STILL: + Transmit_Message(RADIO_TETHER, from); + RadioClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + /* + ** Handle reloading one ammo point for this unit. + */ + case RADIO_RELOAD: + if (Ammo == Techno_Type_Class()->MaxAmmo) return(RADIO_NEGATIVE); +/*BG*/ Mark(MARK_CHANGE_REDRAW); + Ammo++; + return(RADIO_ROGER); + + /* + ** Handle repair of this unit. + */ + case RADIO_REPAIR: + /* + ** If it's a mine layer, re-arm him if he's empty. This always takes precedence + ** over repair, since this operation is free. + */ + if (What_Am_I() == RTTI_UNIT && *((UnitClass *)this) == UNIT_MINELAYER && ((UnitClass *)this)->Ammo < ((UnitClass *)this)->Class->MaxAmmo) { + ((UnitClass *)this)->Ammo = ((UnitClass *)this)->Class->MaxAmmo; + return(RADIO_NEGATIVE); + } + + /* + ** Determine if this unit can be repaired becaause it is under strength. If so, then + ** proceed with the repair process. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + int cost = Techno_Type_Class()->Repair_Cost(); + cost = max(cost, 1); + int step = Techno_Type_Class()->Repair_Step(); + step = max(step, 1); + + /* + ** If there is sufficient money to repair the unit one step, then do so. + ** Otherwise return with a "can't complete" radio response. + */ + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + + /* + ** Return with either an all ok or mission accomplished radio message. This + ** lets the repairing object know if it should abort the repair control process + ** or continue it. + */ + if (Health_Ratio() < Rule.ConditionGreen) { + return(RADIO_ROGER); + } else { + Strength = Techno_Type_Class()->MaxStrength; + return(RADIO_ALL_DONE); + } + } else { + return(RADIO_CANT); + } + } + return(RADIO_NEGATIVE); + + default: + break; + } + return(RadioClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * TechnoClass::Per_Cell_Process -- Handles once-per-cell operations for techno type objects. * + * * + * This routine handles marking a game object as not a loaner. It is set only if the unit * + * is not player owned and is on the regular map. This is necessary so that enemy objects * + * can exist off-map but as soon as they move onto the map, are flagged so that can never * + * leave it again. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Handles scanner units. * + * 12/27/1994 JLB : Checks for an processes any trigger in cell. * + *=============================================================================================*/ +void TechnoClass::Per_Cell_Process(PCPType why) +{ + assert(IsActive); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Center_Coord()); + + /* + ** When enemy units enter the proper map area from off map, they are + ** flagged so that they won't travel back off the map again. + */ + if (!IsLocked && Map.In_Radar(cell)) { + IsLocked = true; + } + + /* + ** If this object somehow moves into mapped terrain, but is not yet + ** discovered, then flag it to be discovered. + */ + // Change for client/server multiplayer ST - 8/7/2019 11:19AM + //if (!IsDiscoveredByPlayer && Map[cell].IsVisible) { + //Revealed(PlayerPtr); + if (!Is_Discovered_By_Player() && Map[cell].Is_Visible(House)) { + Revealed(House); + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Draw_It -- Draws the health bar (if necessary). * + * * + * This routine will draw the common elements for techno type objects. This element is * + * the health bar. The main game object has already been rendered by the time this * + * routine is called. * + * * + * INPUT: x,y -- The coordinate of the center of the unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/17/1994 JLB : Created. * + * 10/26/94 JLB : Knows about radar scanned cells. * + * 12/13/1994 JLB : Clips health bar against map edge. * + * 01/23/1995 JLB : Dynamic selected object rectangle. * + *=============================================================================================*/ +void TechnoClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Tells the door logic that it has been drawn. + */ + ((TechnoClass *)this)->Clear_Redraw_Flag(); + + /* + ** Draw electric zap + */ + if ((ElectricZapDelay >= 0) && ElectricZapTarget) { + Electric_Zap(ElectricZapTarget, ElectricZapWhich, window); + } + + int width, height; + Class_Of().Dimensions(width, height); + + const bool show_health_bar = (Strength > 0) && (Is_Selected_By_Player() || + ((Rule.HealthBarDisplayMode == RulesClass::HB_DAMAGED) && (Strength < Techno_Type_Class()->MaxStrength)) || + (Rule.HealthBarDisplayMode == RulesClass::HB_ALWAYS)); + + /* + ** Draw the selected object graphic. + */ + int lx = width / 2; + int ly = height / 2; + int dx = width / 5; + int dy = height / 5; + int fudge = show_health_bar ? 4 : 0; + if (What_Am_I() == RTTI_VESSEL) { + lx = width / 2; + } + + //if (IsSelected) { + if (Is_Selected_By_Player() || show_health_bar) { + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT]); + + + /* + ** The infantry select box should be a bit higher than normal. + */ + if (What_Am_I() == RTTI_INFANTRY) { + y -= 6; + } + + if (What_Am_I() == RTTI_BUILDING && ((BuildingTypeClass const &)Class_Of()).Type == STRUCT_BARRACKS) { + y -= 5; + } + + /* + ** Fetch the dimensions of the object. These dimensions will be used to draw + ** the selection box and the health bar. + */ + + if (show_health_bar) { + fixed ratio = Health_Ratio(); + int pwidth; // Pixel width of bar interior. + int color; // The color to give the interior of the bargraph. + + int xx = x-width/2; + int yy = y-(height/2); + + /* + ** Draw the outline of the bargraph. + */ + draw_window.Remap(xx+1, yy+1, width-1, 3-1, Map.FadingShade); + draw_window.Draw_Rect(xx, yy, xx+width-1, yy+3, BLACK); + + /* + ** Determine the width of the interior strength + ** graph. + */ + pwidth = (width-2) * ratio; + + pwidth = Bound(pwidth, 1, width-2); + + color = LTGREEN; + if (ratio <= Rule.ConditionYellow) { + color = YELLOW; + } + if (ratio <= Rule.ConditionRed) { + color = RED; + } + draw_window.Fill_Rect(xx+1, yy+1, xx+pwidth, yy+(3-1), color); + } + + /* + ** Draw the selected object graphic. + */ + if (Is_Selected_By_Player()) { + // Upper left corner. + draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx + dx, fudge + y - ly, WHITE); + draw_window.Draw_Line(x - lx, fudge + y - ly, x - lx, fudge + y - ly + dy, WHITE); + + // Upper right corner. + draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx - dx, fudge + y - ly, WHITE); + draw_window.Draw_Line(x + lx, fudge + y - ly, x + lx, fudge + y - ly + dy, WHITE); + + // Lower right corner. + draw_window.Draw_Line(x + lx, y + ly, x + lx - dx, y + ly, WHITE); + draw_window.Draw_Line(x + lx, y + ly, x + lx, y + ly - dy, WHITE); + + // Lower left corner. + draw_window.Draw_Line(x - lx, y + ly, x - lx + dx, y + ly, WHITE); + draw_window.Draw_Line(x - lx, y + ly, x - lx, y + ly - dy, WHITE); + } + } + + // MBL 04.21.2020 + bool selected = Is_Selected_By_Player() || Rule.ResourceBarDisplayMode == RulesClass::RB_ALWAYS; + // if ((window == WINDOW_VIRTUAL) || (Is_Selected_By_Player() && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) + if ((window == WINDOW_VIRTUAL) || (selected && (House->Is_Ally(PlayerPtr) || (Spied_By() & (1 << (PlayerPtr->Class->House)))))) + { + Draw_Pips((x-lx)+5, y+ly-3, window); + } +} + + +/*********************************************************************************************** + * TechnoClass::Unlimbo -- Performs unlimbo process for all techno type objects. * + * * + * This routine handles the common operation between techno objects when they are * + * unlimboed. This includes revealing the map. * + * * + * INPUT: coord -- The coordinate to unlimbo object at. * + * * + * dir (optional) -- initial facing direction for this object * + * * + * OUTPUT: bool; Was the unlimbo successful? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(IsActive); + + if (RadioClass::Unlimbo(coord, dir)) { + PrimaryFacing = dir; + Enter_Idle_Mode(true); + Commence(); + + IsLocked = Map.In_Radar(Coord_Cell(coord)); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine is used to compare the distance to the specified target with the range * + * of the weapon. If the target is outside of weapon range, then false is returned. * + * * + * INPUT: target -- The target to check if it is within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the specified target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(TARGET target, int which, bool reciprocal_check) const +{ + assert(IsActive); + + if (IsLocked && Target_Legal(target)) { + int range = Weapon_Range(which); + BuildingClass const * building = As_Building(target); + if (building != NULL) { + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check && !building) { + ObjectClass *target_object = As_Object(target); + if (target_object) { + RTTIType my_type = What_Am_I(); + if (target_object->What_Am_I() == my_type) { + if (range == target_object->Weapon_Range(which)) { + TechnoClass *tech = As_Techno(target); + if (tech->In_Range(As_Target(), which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if specified target is within weapon range. * + * * + * This routine will determine if the pointer to the target object passed into this * + * routine is within weapon range. * + * * + * INPUT: target -- Pointer to the target object to check if within weapon range. * + * * + * which -- Which weapon to use in determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the target within weapon range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(ObjectClass const * target, int which, bool reciprocal_check) const +{ + assert(IsActive); + + if (IsLocked && target) { + int range = Weapon_Range(which); + if (target->What_Am_I() == RTTI_BUILDING) { + BuildingClass const * building = (BuildingClass const *)target; + range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4)); + } + + if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) { + return(true); + } + + /* + ** There's a longstanding bug where enemy units aligned north/south may have different perceptions of whether they + ** are in range of each other due to the turrets offset on the unit. This check will say that if either considers the + ** the other to be in range, then they both do. ST - 3/18/2020 10:44AM + */ + if (reciprocal_check) { + RTTIType my_type = What_Am_I(); + if (my_type != RTTI_BUILDING) { + if (target->What_Am_I() == my_type) { + if (range == target->Weapon_Range(which)) { + if (((TechnoClass*)target)->In_Range(this, which, false)) { + return true; + } + } + } + } + } + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::In_Range -- Determines if the specified coordinate is within range. * + * * + * Use this routine to determine if the specified coordinate is within weapon range. * + * * + * INPUT: coord -- The coordinate to examine against the object to determine range. * + * * + * which -- The weapon to consider when determining range. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Is the weapon within range? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/16/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::In_Range(COORDINATE coord, int which) const +{ + assert(IsActive); + + return(IsLocked && ::Distance(Fire_Coord(which), coord) <= Weapon_Range(which)); +} + + +/*********************************************************************************************** + * TechnoClass::Area_Modify -- Determine the area scan modifier for the cell. * + * * + * This routine scans around the cell specified and if there are any friendly buildings * + * nearby, the multiplier return value will be reduced. If there are no friendly buildings * + * nearby, then the return value will be 1. It checks to see if the primary weapon is * + * supposed to perform this scan and if so, the scan will be performed. Otherwise the * + * default value is quickly returned. * + * * + * INPUT: cell -- The cell where the potential target lies. An area around this cell will * + * be scanned for friendly buildings. * + * * + * OUTPUT: Returns with the multiplier to be multiplied by the potential target score value. * + * For less opportune targets, the multiplier fraction will be less than one. For * + * all other cases, it will return the default value of 1. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/23/1996 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Area_Modify(CELL cell) const +{ +// assert(Techno_Type_Class()->PrimaryWeapon != NULL); + if (Techno_Type_Class()->PrimaryWeapon == NULL || !Techno_Type_Class()->PrimaryWeapon->IsSupressed) return(1); + + int crange = Lepton_To_Cell(Rule.SupressRadius); + fixed odds = 1; + + for (int radius = 1; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + BuildingClass const * building = Map[newcell].Cell_Building(); + if (building != NULL && House->Is_Ally(building)) { + odds /= 2; + } + } + } + } + return(odds); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Object -- Determines score value of specified object. * + * * + * This routine is used to determine the score value (value as a potential target) of the * + * object specified. This routine will check the specified object for all the various * + * legality checks that threat scanning requires. This is the main workhorse routine for * + * target searching. * + * * + * INPUT: method -- The threat method requested. This is a combined bitflag value that * + * not only specifies the kind of targets to consider, but how far away * + * they are allowed to be. * + * * + * mask -- This is an RTTI mask to use for quickly eliminating object types. * + * The mask is created outside of this routine because this routine is * + * usually called from within a loop and this value is constant in that * + * context. * + * * + * range -- The range at which potential target objects are rejected. * + * 0 = must be within weapon range. * + * >0 = must be within this lepton distance. * + * <0 = range doesn't matter. * + * * + * object -- Pointer to the object itself. * + * * + * value -- Reference to the value variable that this routine will fill in. The * + * higher the value the more likely this object will be selected as best. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Did the target pass all legality checks? If this value is returned true, then the * + * value parameter will be filled in correctly. * + * * + * WARNINGS: This routine is time consuming. Don't call unless necessary. * + * * + * HISTORY: * + * 06/30/1995 JLB : Created. * + * 07/14/1995 JLB : Forces SAM site to not fire on landed aircraft. * + * 09/22/1995 JLB : Zone checking enabled. * + * 10/05/1995 JLB : Gives greater weight to designated enemy house targets. * + * 02/16/1996 JLB : Added additional threat checks. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone) const +{ + assert(IsActive); + assert(object != NULL); + + BStart(BENCH_EVAL_OBJECT); + + /* + ** An object in limbo can never be a valid target. + */ + if (object == NULL || object->IsInLimbo) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is cloaked, then it isn't a legal target. + */ + if (object->Cloak == CLOAKED) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is in a "harmless" state, then don't bother to consider it + ** a threat. + */ + if (MissionControl[object->Mission].IsNoThreat) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the object is not within the desired zone, then ignore it, but only if + ** zone checking is desired. + */ + COORDINATE objectcoord = object->Center_Coord(); + if (zone != -1 && Map[objectcoord].Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Friendly units are never considered a good target. Bail if this + ** object is a friend. Unless we're a medic, of course. But then, + ** only consider it a target if it's injured. + */ + if (House->Is_Ally(object)) { + if (Combat_Damage() < 0) { + if (object->Health_Ratio() == Rule.ConditionGreen) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is further away than allowed, bail. + */ + int dist = Distance(object); + if (range > 0 && dist > range) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + if (range == 0) { + int primary = What_Weapon_Should_I_Use(object->As_Target()); + if (!In_Range(object, primary)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If the object is not visible, then bail. Human controlled units + ** are always considered to be visible. + */ + if (!object->IsOwnedByPlayer && !object->IsDiscoveredByPlayer && Session.Type == GAME_NORMAL && object->What_Am_I() != RTTI_AIRCRAFT) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Quickly eliminate all unit types that are not allowed according to the mask + ** value. + */ + RTTIType otype = object->What_Am_I(); + if (!((1 << otype) & mask)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Mask failure. + } + + /* + ** Determine if the target is theoretically allowed to be a target. If + ** not, then bail. + */ + TechnoTypeClass const * tclass = object->Techno_Type_Class(); + if (!tclass->IsLegalTarget) { + BEnd(BENCH_EVAL_OBJECT); + return(false); // Legality failure. + } + + /* + ** Never consider a spy to be a valid target, unless you're a dog + */ + if (otype == RTTI_INFANTRY && ((InfantryTypeClass const *)tclass)->Type == INFANTRY_SPY) { + if (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsDog) { + // continue executing... + } else { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** Special case so that SAM site doesn't fire on aircraft that are landed. + */ + if (otype == RTTI_AIRCRAFT && What_Am_I() == RTTI_BUILDING && *((BuildingClass *)this) == STRUCT_SAM) { + if (((AircraftClass *)object)->Height == 0) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If only allowed to attack civilians, then eliminate all other types. + */ + if ((method & THREAT_CIVILIANS) && object->Owner() != HOUSE_NEUTRAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the scan is limited to capturable buildings only, then bail if the examined + ** object isn't a capturable building. + */ + if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If we're a sub and the subject is a structure, bail if the structure + ** is other than a sub pen or shipyard. + */ + if (otype == RTTI_BUILDING && What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_SS) { + StructType ostruc = *(BuildingClass *)object; + if (ostruc != STRUCT_SUB_PEN && ostruc != STRUCT_SHIP_YARD) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** SPECIAL CASE: Friendly units won't automatically fire on buildings + ** if the building is not aggressive. That is, unless it is part of a team. A team + ** is allowed to pick any target it so chooses. + */ + if ((!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && + (House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && + otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#ifdef OBSOLETE + if ((!Is_Foot() || ((FootClass *)this)->Team.Is_Valid()) && House->IsHuman && otype == RTTI_BUILDING && tclass->PrimaryWeapon == NULL) { +#endif + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** Player-controlled demo trucks never automatically target. + */ + if ((House->IsHuman || (House->IsPlayerControl && Session.Type == GAME_NORMAL)) && What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_DEMOTRUCK) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + + /* + ** If the search is restricted to Tiberium processing objects, then + ** perform the special qualification check now. + */ + if (method & THREAT_TIBERIUM) { + switch (otype) { + case RTTI_UNIT: + if (!((UnitTypeClass const *)tclass)->IsToHarvest) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + case RTTI_BUILDING: + if (!((BuildingTypeClass const *)tclass)->Capacity && Session.Type != GAME_NORMAL) { + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + break; + + default: + BEnd(BENCH_EVAL_OBJECT); + return(false); + } + } + + /* + ** If this target value is better than the previously recorded best + ** target value then record this target for possible return as the + ** best. + */ + int rawval = object->Value(); + value = rawval + object->Crew.Kills; + + /* + ** If the candidate object is owned by the designated enemy of this house, then + ** give it a higher value. This will tend to gravitate attacks toward the main + ** antagonist of this house. + */ + if (House->Enemy != HOUSE_NONE && House->Enemy == object->House->Class->House) { + value += 500; + value *= 3; + } + + /* + ** If the object is outside of the protective umbrella of the enemy base, then give it + ** a target boost value. + */ + if (object->House->Which_Zone(object) == ZONE_NONE) { + value *= 2; + } + + /* + ** If fake buildings are considered to be a greater target option, then boost + ** the fake building's value. + */ + if ((method & THREAT_FAKES) && otype == RTTI_BUILDING) { + //switch (!((BuildingTypeClass const *)tclass)->Type) { + //The '!' in front of the expression means we are switching on a bool instead of the type. Going to remove it so it compiles, but it might change behavior depending on what Watcom original made of this. + //Might be better to remove this switch altogether + //ST - 5/9/2019 + switch (((BuildingTypeClass const *)tclass)->Type) { + case STRUCT_FAKECONST: + case STRUCT_FAKEWEAP: + case STRUCT_FAKE_YARD: + case STRUCT_FAKE_PEN: + case STRUCT_FAKE_RADAR: + break; + + /* + ** Ignore all non-fake buildings. + */ + default: + value = 0; + break; + } + } + + /* + ** If power plants are to be considered a greater threat, then increase + ** their value here. Buildings that produce no power are not considered + ** a threat. + */ + if ((method & THREAT_POWER) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->Power > 0) { + value += ((BuildingTypeClass const *)tclass)->Power * 1000; + } else { + value = 0; + } + } + + /* + ** If factories are to be considered a greater threat, then don't + ** consider any non-factory building. + */ + if ((method & THREAT_FACTORIES) && otype == RTTI_BUILDING) { + if (((BuildingTypeClass const *)tclass)->ToBuild == RTTI_NONE) { + value = 0; + } + } + + /* + ** If base defensive structures are to be considered a greater threat, then + ** don't consider an unarmed building to be a threat. + */ + if ((method & THREAT_BASE_DEFENSE) /*&& otype == RTTI_BUILDING*/) { + if (tclass->PrimaryWeapon == NULL) { + value = 0; + } + } + + /* + ** Possibly cause a reduction of the target's value if it is nearby friendly + ** structures and the primary weapon of this object is flagged for + ** friendly fire supression special check logic. + */ + fixed areamod = Area_Modify(Coord_Cell(object->Center_Coord())); + if (areamod != 1) { + value = areamod * value; + } + + /* + ** Adjust the target value upward if it is in the 'nervous zone' of the + ** owning base. This will tend to protect the base more thoroughly than + ** an unmodified scan would. + */ + if (House->Which_Zone(object) != ZONE_NONE) { + value *= Rule.NervousBias; + } + + /* + ** Lessen threat as a factor of distance. + */ + if (value) { +// if (rawval) { + + value = (value * 32000) / ((dist/ICON_LEPTON_W)+1); +// value = (value * 32000) / (((dist/ICON_LEPTON_W)*(dist/ICON_LEPTON_W))+1); + +// if (value < MAP_CELL_W*2) value = dist/ICON_LEPTON_W; + value = max(value, 1); + BEnd(BENCH_EVAL_OBJECT); + return(true); + } + value = 0; + BEnd(BENCH_EVAL_OBJECT); + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Cell -- Determine the value and object of specified cell. * + * * + * This routine will examine the specified cell and return with the potential target * + * object it contains and the value of it. Use this routine when searching for threats. * + * * + * INPUT: method -- The scan method to use for target searching. * + * * + * mask -- Prebuilt mask of object RTTI types acceptable for scanning. * + * * + * range -- Scan range limit to use for elimination purposes. This ensures that * + * objects in the "corner" of a square scan get properly discarded. * + * * + * object -- Pointer to object pointer to be filled in with the object at this * + * cell as a valid target. * + * * + * value -- Reference to the value of the object in this cell. It will be set * + * according to the object's value. * + * * + * zone -- The zone restriction if any. A -1 means no zone check required. * + * * + * OUTPUT: Was a valid potential target found in this cell? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/19/1995 JLB : Created. * + * 09/22/1995 JLB : Zone checking enabled. * + *=============================================================================================*/ +bool TechnoClass::Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const * * object, int & value, int zone) const +{ + assert(IsActive); + + BStart(BENCH_EVAL_CELL); + + *object = NULL; + value = 0; + + /* + ** If the cell is not on the legal map, then always ignore it. + */ + if ((unsigned)cell > MAP_CELL_TOTAL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + if (!Map.In_Radar(cell)) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + /* + ** Fetch the techno object from the cell. If there is no + ** techno object there, then bail. + */ + CellClass * cellptr = &Map[cell]; + + /* + ** Don't consider for evaluation a cell that is not within the same zone. Only + ** perform this check if zone checking is required. + */ + if (zone != -1 && cellptr->Zones[Techno_Type_Class()->MZone] != zone) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + + TechnoClass const * tentative = (TechnoClass const *)cellptr->Cell_Occupier(); + while (tentative != NULL) { + if (tentative != this) { + if (tentative->Is_Techno()) { + if (Combat_Damage() < 0) { + if (tentative->Health_Ratio() < Rule.ConditionGreen && House->Is_Ally(tentative)) break; + } else { + if (!House->Is_Ally(tentative)) break; + } + } + } + tentative = (TechnoClass const *)(ObjectClass *)tentative->Next; + } + + if (tentative == NULL) { + BEnd(BENCH_EVAL_CELL); + return(false); + } + *object = tentative; + + bool result = Evaluate_Object(method, mask, range, tentative, value); + + BEnd(BENCH_EVAL_CELL); + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Evaluate_Just_Cell -- Evaluate a cell as a target by itself. * + * * + * This will examine the cell (as if it contained no sentient objects) and determine a * + * target value to assign to it. Typically, this is only useful for wall destroyable * + * weapons when dealing with enemy walls. * + * * + * INPUT: cell -- The cell to examine and evaluate. * + * * + * OUTPUT: Returns with the target value to assign to this cell. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/10/1996 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Evaluate_Just_Cell(CELL cell) const +{ + BStart(BENCH_EVAL_WALL); + + /* + ** Ships don't scan for walls. + */ + if (What_Am_I() == RTTI_VESSEL) { + return(0); + } + + /* + ** First, only computer objects are allowed to automatically scan for walls. + */ + if (House->IsHuman) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Even then, if the difficulty indicates that it shouldn't search for wall + ** targets, then don't allow it to do so. + */ + if (!Rule.Diff[House->Difficulty].IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Determine if, in fact, a wall is located at this cell location. + */ + CellClass const * cellptr = &Map[cell]; + if (cellptr->Overlay == OVERLAY_NONE || !OverlayTypeClass::As_Reference(cellptr->Overlay).IsWall) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** As a convenience to the target scanning logic, don't consider any wall to be + ** a target if it isn't in range of the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (!In_Range(Cell_Coord(cell), primary)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** See if the object has a weapon that can damage walls. + */ + TechnoTypeClass const * ttype = (TechnoTypeClass const *)Techno_Type_Class(); + if (ttype->PrimaryWeapon == NULL || ttype->PrimaryWeapon->WarheadPtr == NULL) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the weapon cannot deal with ground based targets, then don't consider + ** this a valid cell target. + */ + if (ttype->PrimaryWeapon->Bullet != NULL && !ttype->PrimaryWeapon->Bullet->IsAntiGround) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If the primary weapon cannot destroy a wall, then don't give the cell any + ** value as a target. + */ + if (!ttype->PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** If this is a friendly wall, then don't attack it. + */ + if (House->Is_Ally(cellptr->Owner)) { + BEnd(BENCH_EVAL_WALL); + return(0); + } + + /* + ** Since a wall was found, then return a value adjusted according to the range the wall + ** is from the object. The greater the range, the lesser the value returned. + */ + BEnd(BENCH_EVAL_WALL); + return(Weapon_Range(0) - Distance(Cell_Coord(cell))); +} + + +/*********************************************************************************************** + * TechnoClass::Greatest_Threat -- Determines best target given search criteria. * + * * + * This routine will scan game objects looking for the best target. It is used by the * + * general target searching processes. The type of target scan to perform is controlled * + * by the method control parameter. * + * * + * INPUT: method -- The method control parameter is used to control the type of target * + * scan performed. It consists of a series of bit flags (see ThreatType) * + * that are combined to form the target scan desired. * + * * + * OUTPUT: Returns the target value of a suitable target. If no target was found then the * + * value TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/14/1994 JLB : Created. * + * 06/20/1995 JLB : Greatly optimized scan method. * + * 09/22/1995 JLB : Takes into account the zone (if necessary). * + * 05/30/1996 JLB : Tighter elimination mask checking. * + *=============================================================================================*/ +TARGET TechnoClass::Greatest_Threat(ThreatType method) const +{ + assert(IsActive); + + BStart(BENCH_GREATEST_THREAT); + + ObjectClass const * bestobject = NULL; + int bestval = -1; + int zone = -1; + + TargetScan++; + + /* + ** Determine the zone that the target must be in. For aircraft and gunboats, they + ** ignore zones since they either can fly over any zone or are designed to fire into + ** other zones. If scanning for targets that are within range, then zone checking need + ** not be performed -- range checking is much more thorough and effective. + */ + if (!(method & THREAT_RANGE) && + What_Am_I() != RTTI_VESSEL && + What_Am_I() != RTTI_BUILDING && + What_Am_I() != RTTI_AIRCRAFT) { + + zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + } + + /* + ** Hack for dogs, 'cause they can only consider infantrymen to be a + ** threat. Medics also. + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (((InfantryClass *)this)->Class->IsDog || Combat_Damage() < 0) { + method = THREAT_INFANTRY | (method & (THREAT_RANGE | THREAT_AREA)); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*(InfantryClass *)this == INFANTRY_MECHANIC) { + method = (THREAT_VEHICLES | THREAT_AIR) | (method & (THREAT_RANGE | THREAT_AREA)); + } +#endif + } + } + + /* + ** Build a quick elimination mask. If the RTTI of the object doesn't + ** qualify with this mask, then we KNOW that it shouldn't be considered. + */ + int mask = 0; + if (method & THREAT_CIVILIANS) mask |= ((1 << RTTI_BUILDING) | (1 << RTTI_INFANTRY) | (1 << RTTI_UNIT)); + if (method & THREAT_AIR) mask |= (1 << RTTI_AIRCRAFT); + if (method & THREAT_CAPTURE) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_BUILDINGS|THREAT_FACTORIES|THREAT_POWER|THREAT_FAKES|THREAT_BASE_DEFENSE|THREAT_TIBERIUM)) mask |= (1 << RTTI_BUILDING); + if (method & (THREAT_CIVILIANS|THREAT_INFANTRY|THREAT_BASE_DEFENSE)) mask |= (1 << RTTI_INFANTRY); + if (method & THREAT_VEHICLES) mask |= (1 << RTTI_UNIT); + if (method & THREAT_BASE_DEFENSE) mask |= (1 << RTTI_BUILDING); + if (method & THREAT_BOATS) mask |= (1 << RTTI_VESSEL); + + /* + ** Limit area target scans use a method where the actual map cells are + ** examined for occupants. The occupant is then examined in turn. The + ** best target within the area is returned as a target. + */ + if (method & (THREAT_AREA|THREAT_RANGE)) { + int range = Threat_Range((method & THREAT_RANGE) ? 0 : 1); + + int crange = range / ICON_LEPTON_W; + if (range == 0) { + crange = max(Weapon_Range(0), Weapon_Range(1)) / ICON_LEPTON_W; + crange++; + } + CELL cell = Coord_Cell(Fire_Coord(0)); + + /* + ** BG: Miserable hack to get the stupid doctor to actually do area + ** guarding. + */ + if (Combat_Damage() < 0) { + /*if (method & THREAT_AREA)*/ crange++; + } + + /* + ** If aircraft are a legal target, then scan through all of them at this time. + ** Scanning by cell is not possible for aircraft since they are not recorded + ** at the cell level. + */ + if (method & THREAT_AIR) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (object->In_Which_Layer() != LAYER_GROUND && Evaluate_Object(method, mask, range, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Radiate outward from the object's location, looking for the best + ** target. + */ + CELL bestcell = -1; + int bestcellvalue = 0; + TechnoClass const * object; + int value; +// int rad = 1; + + // BG: Medics need to be able to look in their own cell too. +// if (Combat_Damage() < 0 || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass*)this)->Class->IsDog)) { +// rad = 0; +// } + + for (int radius = 0; radius < crange; radius++) { + + /* + ** Scan the top and bottom rows of the "box". + */ + for (int x = -radius; x <= radius; x++) { + CELL newcell; + + if ((Cell_X(cell) + x) < Map.MapCellX) continue; + if ((Cell_X(cell) + x) >= (Map.MapCellX+Map.MapCellWidth)) continue; + + if ((Cell_Y(cell) - radius) >= Map.MapCellY) { + newcell = XY_Cell(Cell_X(cell) + x, Cell_Y(cell)-radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_Y(cell) + radius) < (Map.MapCellY+Map.MapCellHeight)) { + newcell = XY_Cell(Cell_X(cell)+x, Cell_Y(cell)+radius); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Scan the left and right columns of the "box". + */ + for (int y = -(radius-1); y < radius; y++) { + CELL newcell; + + if ((Cell_Y(cell) + y) < Map.MapCellY) continue; + if ((Cell_Y(cell) + y) >= (Map.MapCellY+Map.MapCellHeight)) continue; + + if ((Cell_X(cell) - radius) >= Map.MapCellX) { + newcell = XY_Cell(Cell_X(cell)-radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + + if ((Cell_X(cell) + radius) < (Map.MapCellX+Map.MapCellWidth)) { + newcell = XY_Cell(Cell_X(cell)+radius, Cell_Y(cell)+y); + if (Evaluate_Cell(method, mask, newcell, range, &object, value, zone)) { + if (bestval < value) { + bestobject = object; + } + } + if (bestobject == NULL) { + value = Evaluate_Just_Cell(newcell); + if (bestcellvalue < value) { + bestcellvalue = value; + bestcell = newcell; + } + } + } + } + + /* + ** Bail early if a target has already been found and the range is at + ** one of the breaking points (i.e., normal range or range * 2). + */ + if (bestobject != NULL) { + if (radius == crange/4) { + return(bestobject->As_Target()); + } + if (radius == crange/2) { + return(bestobject->As_Target()); + } + } + if (bestcell != -1) { + return(::As_Target(bestcell)); + } + } + + } else { + /* + ** A full map scan was requested. First scan through aircraft. The top map layer + ** is NOT scanned since that layer will probably contain more bullets and animations + ** than aircraft. + */ + if (mask & (1L << RTTI_AIRCRAFT)) { + for (int index = 0; index < Aircraft.Count(); index++) { + TechnoClass * object = Aircraft.Ptr(index); + + int value = 0; + if (Evaluate_Object(method, mask, -1, object, value)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + /* + ** When scanning the ground, always consider landed aircraft as a valid + ** potential target. This is only true if vehicles are considered a + ** valid target. A landed aircraft is considered a vehicle. + */ + if (method & THREAT_VEHICLES) { + mask |= (1 << RTTI_AIRCRAFT); + } + + /* + ** Now scan through the entire ground layer. This is painful, but what other + ** choice is there? + */ + for (int index = 0; index < Map.Layer[LAYER_GROUND].Count(); index++) { + ObjectClass const * object = Map.Layer[LAYER_GROUND][index]; + + int value = 0; + if (object->Is_Techno() && Evaluate_Object(method, mask, -1, (TechnoClass const *)object, value, zone)) { + if (value > bestval) { + bestobject = object; + bestval = value; + } + } + } + } + + BEnd(BENCH_GREATEST_THREAT); + + /* + ** If a good target object was found, then return with the target value + ** of it. + */ + if (bestobject != NULL) { + return(bestobject->As_Target()); + } + return(TARGET_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Owner -- Who is the owner of this object? * + * * + * Use this routine to examine this object and return who the owner is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the house number of the owner of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +HousesType TechnoClass::Owner(void) const +{ + assert(IsActive); + + return(House->Class->House); +} + + +/*********************************************************************************************** + * TechnoClass::Clicked_As_Target -- Sets the flash count for this techno object. * + * * + * Use this routine to set the flash count for the object. This flash count is the number * + * of times the object will "flash". Typically it is called as a result of the player * + * clicking on this object in order to make it the target of a move or attack. * + * * + * INPUT: count -- The number of times the object should flash. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Clicked_As_Target(HousesType house, int count) // 2019/09/20 JAS - Added record of who clicked on the object +{ + assert(IsActive); + + FlashCount = count; + + // 2019/09/20 JAS - Flashing info needs to exist per player + if (house < HOUSE_COUNT) + { + FlashCountPerPlayer[house] = count; + } + else + { + //receiving HOUSE_COUNT means do it for every player + for (int i = 0; i < HOUSE_COUNT; ++i) + { + FlashCountPerPlayer[i] = count; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::AI -- Handles AI processing for techno object. * + * * + * This routine handles AI processing for techno objects. Typically, this merely dispatches * + * to the appropriate AI routines for the base classes. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Make sure that this routine is only called ONCE per game tick. * + * * + * HISTORY: * + * 12/09/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::AI(void) +{ + assert(IsActive); + + /* + ** Handle recoil recovery here. + */ + if (IsInRecoilState) { + IsInRecoilState = false; + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If this building is being spied on by the player, need to redraw if selected + ** since the money amount is rendering. + */ + if (Is_Selected_By_Player()) { + if (What_Am_I() == RTTI_BUILDING) { + int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); + if (spiedby) { + if (((BuildingClass *)this)->Class->Capacity) { + Mark(MARK_CHANGE_REDRAW); + } + } + } + } + + CargoClass::AI(); + RadioClass::AI(); + + if (!IsActive || (Height > 0 && What_Am_I() != RTTI_AIRCRAFT)) return; + + DoorClass::AI(); + + /* + ** If this is a vehicle that heals itself (e.g., Mammoth Tank), then it will perform + ** the heal logic here. + */ + if (Techno_Type_Class()->IsSelfHealing && (Frame % (Rule.RepairRate * TICKS_PER_MINUTE)) == 0 && Health_Ratio() <= Rule.ConditionYellow) { + Strength++; + Mark(MARK_CHANGE); + } + + /* + ** Cloaking device processing. + */ + Cloaking_AI(); + + /* + ** If for some strange reason, the computer is firing upon itself, then + ** tell it not to. + */ + if (!House->IsHuman && As_Techno(TarCom) && As_Techno(TarCom)->House->Is_Ally(this)) { +//#ifdef FIXIT_CSII // checked - ajw 9/28/98 (commented out) +//if(What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)this==INFANTRY_GENERAL && Session.Type==GAME_NORMAL && House->Class->House==HOUSE_UKRAINE) { +//} else +//#endif + Assign_Target(TARGET_NONE); + } + + /* + ** Perform a maintenance check to see that if somehow this object is trying to fire + ** upon an object it can never hit (because it can't reach it), then abort the tarcom + */ + if (What_Am_I() != RTTI_AIRCRAFT && Target_Legal(TarCom) && (!Is_Foot() || !((FootClass *)this)->Team.Is_Valid()) && (!Is_Foot() || !Is_In_Same_Zone(As_Cell(TarCom)))) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + + /* + ** Update the animation timer system. If the animation stage + ** changes, then flag the object to be redrawn as well as determine + ** if the current animation process needs to change. + */ + if (What_Am_I() != RTTI_BUILDING) { + if (StageClass::About_To_Change()) { + Mark(MARK_CHANGE_REDRAW); + } + if (StageClass::Graphic_Logic() || Time_To_Redraw()) { + Mark(MARK_CHANGE_REDRAW); + } + } + + /* + ** If the object is flashing and a change of flash state has occurred, then mark the + ** object to be redrawn. + */ + if (FlasherClass::Process()) { + Mark(MARK_CHANGE); + } + + /* + ** Handle electric zap delay logic. + */ + if (ElectricZapDelay >= 0) { + Map.Flag_To_Redraw(true); + if (--ElectricZapDelay < 0) { + ElectricZapTarget = 0; + ElectricZapWhich = 0; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Cloaking_AI -- Perform the AI maintenance for a cloaking device. * + * * + * This routine handles the cloaking device logic for this object. It will handle the * + * transition effects as the object cloaks or decloaks. It will also try to start an * + * object to cloak if possible. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Cloaking_AI(void) +{ + /* + ** Handle decision to re-cloak here. Process the cloaking/decloaking operation. + */ + if (IsCloakable) { + + /* + ** If this object is uncloaked, but it can be cloaked and it thinks that it + ** is a good time do so, then begin cloaking. + */ + if (Cloak == UNCLOAKED) { +#ifdef PREDATOR + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + //if (IsOwnedByPlayer) Mark(MARK_CHANGE); + if (Is_Owned_By_Player()) Mark(MARK_CHANGE); +#endif + CloakingDevice.Graphic_Logic(); + if (Is_Ready_To_Cloak()) { + if (Health_Ratio() > Rule.ConditionRed) { + Do_Cloak(); + } else { + if (Percent_Chance(4)) { + Do_Cloak(); + } + } + } + } else { + + CloakingDevice.Graphic_Logic(); + switch (Cloak) { + + /* + ** Handle the uncloaking process. Always mark to redraw + ** the object and when cloaking is complete, stabilize into + ** the normal uncloaked state. + */ + case UNCLOAKING: + Mark(MARK_CHANGE); + if (Visual_Character(true) == VISUAL_NORMAL) { + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); // re-start the stage counter + Cloak = UNCLOAKED; + CloakDelay = Rule.CloakDelay * TICKS_PER_MINUTE; + Mark(MARK_CHANGE); + } + break; + + /* + ** Handle the cloaking process. Always mark to redraw the object + ** and when the cloaking process is complete, stabilize into the + ** normal cloaked state. + */ + case CLOAKING: + Mark(MARK_CHANGE); + if(!CloakingDevice.Fetch_Rate()) { + CloakingDevice.Set_Rate(1); + } + switch (Visual_Character(true)) { + + /* + ** If badly damaged, then it can never fully cloak. + */ + case VISUAL_DARKEN: + if (Health_Ratio() <= Rule.ConditionRed && Percent_Chance(25)) { + Cloak = UNCLOAKING; + } + break; + + case VISUAL_HIDDEN: + Cloak = CLOAKED; + CloakingDevice.Set_Rate(0); + CloakingDevice.Set_Stage(0); + Mark(MARK_CHANGE); + + Map[Center_Coord()].Redraw_Objects(true); + Map.RadarClass::Flag_To_Redraw(true); + + /* + ** Special check to ensure that if the unit is carrying a captured + ** flag, it will never fully cloak. + */ + if (What_Am_I() == RTTI_UNIT && ((UnitClass *)this)->Flagged != HOUSE_NONE) { + Do_Shimmer(); + } else { + Detach_All(false); + } + + /* + ** A computer controlled unit will try to scatter if possible so + ** that it will be much harder to locate. + */ + if (What_Am_I() == RTTI_UNIT && !House->IsHuman) { + Scatter(0, true); + } + break; + } + break; + + /* + ** A cloaked object will always be redrawn if it is owned by the + ** player. This ensures that the shimmering effect will animate. + */ + case CLOAKED: +#ifdef PREDATOR + //if (IsOwnedByPlayer) { + // Changed for multiplayer so we can visually see the different players in the original renderer. ST - 3/13/2019 5:40PM + if (Is_Owned_By_Player()) { + Mark(MARK_CHANGE); + } +#endif + break; + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Cloak -- Determines if this object is ready to begin cloaking. * + * * + * This routine will examine this object and determine if it can and is ready and able * + * to begin cloaking. It will also check to make sure it appears to be a good time to cloak * + * as well. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this unit ready and able to start cloaking? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Cloak(void) const +{ + /* + ** If it is already cloaked or in the process of cloaking, then it can't start cloaking. + */ + if (Cloak == CLOAKED || (Cloak == CLOAKING && CloakingDevice.Fetch_Rate())) { + return(false); + } + + /* + ** If the object cannot recloak, then it certainly is not allowed to start. + */ + if (!IsCloakable || !Is_Allowed_To_Recloak()) { + return(false); + } + + /* + ** If the object is currently rearming, then don't begin to recloak. + */ + if (Arm != 0) { + return(false); + } + + /* + ** If it seems like this object is about to fire on a target, then don't begin + ** cloaking either. + */ + if (Target_Legal(TarCom) && In_Range(TarCom)) { + return(false); + } + + /* + ** Recloaking can only begin if the cloaking device is not already operating. + */ + if (CloakingDevice.Fetch_Stage() != 0) { + return(false); + } + + /* + ** If the arbitrary cloak delay value is still counting down, then don't + ** allow recloaking just yet. + */ + if (CloakDelay != 0) { + return(false); + } + + /* + ** All tests passed, so this object is allowed to begin cloaking. + */ + return(true); +} + + + +/*********************************************************************************************** + * TechnoClass::Select -- Selects object and checks to see if can be selected. * + * * + * This function checks to see if this techno object can be selected. If it can, then it * + * is selected. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/11/1994 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Select(bool allow_mixed) +{ + assert(IsActive); + + //if (!IsDiscoveredByPlayer && !House->IsPlayerControl && !Debug_Unshroud) { // ST - 8/7/2019 11:24AM + if (!Is_Discovered_By_Player() && !House->IsPlayerControl && !Debug_Unshroud) { + return(false); + } + + if (RadioClass::Select(allow_mixed)) { + + /* + ** Speak a confirmation of selection. + */ + if (House->IsPlayerControl && AllowVoice) { + Response_Select(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Fire -- Determines if this techno object can fire. * + * * + * This performs a simple check to make sure that this techno object can fire. At this * + * level, the only thing checked for is the rearming delay. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the fire legality control code. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const +{ + assert(IsActive); + + /* + ** Don't allow firing if the target is illegal. + */ + if (!Target_Legal(target)) { + return(FIRE_ILLEGAL); + } + + ObjectClass * object = As_Object(target); + + /* + ** If the object is completely cloaked, then you can't fire on it. + */ + if (object != NULL && object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) { + return(FIRE_CANT); + } + + /* + ** A falling object is too busy falling to fire. + */ + if (IsFalling) { + return(FIRE_CANT); + } + + /* + ** If there is no weapon, then firing is not allowed. + */ + WeaponTypeClass const * weapon = ((which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon); + if (weapon == NULL) { + return(FIRE_CANT); + } + + /* + ** Can only fire anti-aircraft weapons against aircraft unless the aircraft is + ** sitting on the ground. + */ + if (object != NULL && object->What_Am_I() == RTTI_AIRCRAFT && + !weapon->Bullet->IsAntiAircraft && + ((AircraftClass *)object)->Height > 0) { + + return(FIRE_CANT); + } + + /* + ** If the object is on the ground, then don't allow firing if it can't fire upon ground objects. + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || (*((VesselClass*)object) != VESSEL_SS && *((VesselClass*)object) != VESSEL_MISSILESUB )) && +#else + if (object != NULL && object->Height == 0 && (object->What_Am_I() != RTTI_VESSEL || *((VesselClass*)object) != VESSEL_SS) && +#endif + !weapon->Bullet->IsAntiGround) { + + return(FIRE_CANT); + } + if (Is_Target_Cell(target) && !weapon->Bullet->IsAntiGround) { + return(FIRE_CANT); + } + + /* + ** Don't allow firing if still rearming. + */ + if (Arm != 0) return(FIRE_REARM); + + /* + ** The target must be within range in order to allow firing. + */ + if (!In_Range(target, which)) { + return(FIRE_RANGE); + } + + /* + ** If there is no ammo left, then it can't fire. + */ + if (!Ammo) { + return(FIRE_AMMO); + } + + /* + ** If cloaked, then firing is disabled. + */ + if (Cloak != UNCLOAKED) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Special hack for John Archer's Hunt-The-Wumpus multiplayer mission... if +// the object firing is a cloaked civilian, don't require uncloaking before +// allowing firing. + if (What_Am_I()==RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCivilian ) { + return(FIRE_OK); + } +#endif + return(FIRE_CLOAKED); + } + + return(FIRE_OK); +} + + +/*********************************************************************************************** + * TechnoClass::Stun -- Prepares the object for removal from the game. * + * * + * This routine handles cleaning up this techno object from the game system so that when * + * it is subsequently removed, it doesn't leave any loose ends. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Stun(void) +{ + assert(IsActive); + + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + Transmit_Message(RADIO_OVER_OUT); + Detach_All(); + //Unselect(); + //When an object is stunned it needs to be deselected from all players, not just the current PlayerPtr. + // - 8/18/2019 JAS + Unselect_All_Players(); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Target -- Assigns the targeting computer with specified target. * + * * + * Use this routine to set the targeting computer for this object. It checks to make sure * + * that targeting of itself is prohibited. * + * * + * INPUT: target -- The target for this object to attack. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/23/1994 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Target(TARGET target) +{ + assert(IsActive); + + if (target == TarCom) return; + + if (!Target_Legal(target)) { + target = TARGET_NONE; + } else { + + /* + ** Prevent targeting of self. + */ + if (target == As_Target()) { + target = ::As_Target(Coord_Cell(Coord)); + } else { + + /* + ** Make sure that the target is not already dead. + */ + ObjectClass * object = As_Object(target); + if (object != NULL && (object->IsActive == false || object->Strength == 0)) { + target = TARGET_NONE; + } + } + } + + /* + ** Set the unit's targeting computer. + */ + TarCom = target; +} + + +/*********************************************************************************************** + * TechnoClass::Rearm_Delay -- Calculates the delay before firing can occur. * + * * + * This function calculates the delay between shots. It determines this from the standard * + * rate of fire (ROF) of the base class and modifies it according to game speed and * + * whether this is the first or second shot. All single shot attackers consider their * + * shots to be "second" since the second shot is the one handled normally. The first shot * + * usually gets assigned a much shorter delay time before the next shot can fire. * + * * + * INPUT: second -- bool; Is this the second of a two shot salvo? * + * * + * OUTPUT: Returns with the number of game frames to delay before the next shot may fire. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Rearm_Delay(bool second, int which) const +{ + assert(IsActive); + + if (What_Am_I() == RTTI_BUILDING && Ammo > 1) { + return(1); + } + + WeaponTypeClass const * weapon = (which == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; + if (second && weapon != NULL) { + return(weapon->ROF * House->ROFBias); + } + return(3); +} + + +/*********************************************************************************************** + * TechnoClass::Electric_Zap -- Fires electric zap at the target specified. * + * * + * This routine is used to fire an electric zap at the target specified. * + * * + * INPUT: target -- The target to fire the zap at. * + * * + * which -- Which weapon is this zap associated with (0=primary, 1=secondary). * + * * + * window -- The clipping window to use when rendering. * + * * + * source_coord -- The coordinate that the zap is to originate from. This is an * + * override value and if not specifide, the normal fire coordinate * + * is used. * + * * + * remap -- Pointer to the zap animation remap override table. If not specified * + * then the zap remains the normal blue white color. * + * * + * OUTPUT: bool; Does this object need to redraw? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/30/1996 BWG : Created. * + * 09/30/1996 JLB : Uses standard facing conversion and distance routines. * + *=============================================================================================*/ +bool TechnoClass::Electric_Zap(COORDINATE target_coord, int which, WindowNumberType window, COORDINATE source_coord, unsigned char * remap) const +{ + //int x,y,x1,y1; + //PG init variables + int x = 0; + int y = 0; + int x1 = 0; + int y1 = 0; + COORDINATE source; + + if (source_coord != 0) { + source = source_coord; + } else { + source = Fire_Coord(which); + } + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharging = false; + } + bool gonnadraw = false; + + if (SpecialDialog == SDLG_NONE) { + Map.Coord_To_Pixel(source, x, y); + Map.Coord_To_Pixel(target_coord, x1, y1); + x += Map.TacPixelX; + x1 += Map.TacPixelX; + y += Map.TacPixelY; + y1 += Map.TacPixelY; + gonnadraw = true; + } + + static int _shape[]={ 2, 3, 1, 0, 2, 3, 1, 0}; + static int _xadd[8][8]={ + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + { 0, 8, 8, 8, 0, 0, 0, 0}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8}, + {-8, 0, 0, 0,-8,-8,-8,-8} + }; + static int _yadd[8][8]={ + {-8,-8,-8, 0, 0, 0,-8,-8}, + {-8,-8,-8, 0, 0, 0,-8,-8}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + { 0, 0, 0, 8, 8, 8, 0, 0}, + {-8,-8,-8, 0, 0, 0,-8,-8} + }; + + int savex = x, savey = y; + if (gonnadraw) { + for (int shots = 0; shots < 3; shots++) { + x = savex; + y = savey; + int lastfacing = 0; + while (::Distance(x, y, x1, y1) > 8) { + + /* + ** Determine true (0..7) facing from current position to + ** destination (actually the source coordinate of the zap). + */ + int facing = Dir_Facing(Desired_Facing8(x, y, x1, y1)); + + /* + ** If there's quite a bit of distance to go, + ** we may vary the desired facing to give the + ** bolt some randomness. + */ + if (::Distance(x, y, x1, y1) > 40) { + switch (Sim_Random_Pick(1, 3 + ((shots==0) ? 3 : 0))) { + case 1: + facing++; + break; + + case 2: + facing--; + break; + + default: + break; + } + facing &= 7; + } + + /* + ** Now that we have the direction of the bolt, + ** draw it and move the x & y coords in the right + ** direction for the next piece. + */ + // Electric zap coordinates are always tactical, so don't use the partial window if passed - SKY + x += _xadd[facing][lastfacing]; + y += _yadd[facing][lastfacing]; + if (remap != NULL) { + CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap); + } else { + CC_Draw_Shape(this, "LITNING", LightningShapes, _shape[facing]+(shots ? 4 : 0), x, y, (window != WINDOW_PARTIAL) ? window : WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_WIN_REL); + } + lastfacing = facing; + } + } + } + + return (gonnadraw); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_At -- Fires projectile at target specified. * + * * + * This is the main projectile firing code. Buildings, units, and infantry route fire * + * requests through this function. * + * * + * INPUT: target -- The target that the projectile is to be fired at. * + * * + * which -- Which weapon to fire. * + * * + * OUTPUT: Returns with a pointer to the projectile object that was fired. If no projectile * + * could be created or there was some other illegality detected, the return value * + * will be NULL. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/26/1994 JLB : Created. * + * 07/03/1995 JLB : Moving platforms fire inaccurate projectiles. * + * 02/22/1996 JLB : Handles camera "weapon" case. * + *=============================================================================================*/ +BulletClass * TechnoClass::Fire_At(TARGET target, int which) +{ + assert(IsActive); + + BulletClass * bullet; // Projectile. + DirType dir; // The facing to impart upon the projectile. + COORDINATE target_coord; // Coordinate of the target. + COORDINATE fire_coord; // Coordinate of firing position. + TechnoTypeClass const & tclass = *Techno_Type_Class(); + ObjectClass * object; + WeaponTypeClass const * weapon = (which == 0) ? tclass.PrimaryWeapon : tclass.SecondaryWeapon; + + /* + ** If this object doesn't have a weapon, then it is obvious that firing + ** cannot ever succeed. Return with failure flag. + */ + if (weapon == NULL) return(NULL); + + BulletTypeClass const & btype = *weapon->Bullet; + + /* + ** Perform a quick legality check to see if firing can occur. + */ + if (Debug_Map || !Target_Legal(target)) { + return(NULL); + } + + /* + ** Fetch the target coordinate for the target specified. + */ + object = As_Object(target); + if (object != NULL) { + target_coord = object->Target_Coord(); + } else { + target_coord = As_Coord(target); + } + + /* + ** Get the location where the projectile should appear. + */ + fire_coord = Fire_Coord(which); + + /* + ** If the projectile is a homing type (such as a missile), then it will + ** launch in the direction the turret is facing, NOT necessarily the same + ** direction as the target. + */ + if (btype.ROT != 0 || btype.IsDropping) { + dir = Fire_Direction(); + if (btype.IsDropping) { + fire_coord = Center_Coord(); + } + } else { + dir = ::Direction(fire_coord, target_coord); + } + + /* + ** Create the projectile. Then process any special operations that + ** need to be performed according to the style of projectile + ** created. + */ + int firepower = weapon->Attack; + if (firepower > 0) { + firepower = weapon->Attack * FirepowerBias * House->FirepowerBias; + } + + + /* + ** Give the bullet a boost of speed if the weapon indicates that this is required. Only + ** need to perform this check if the target is an aircraft. + */ + int firespeed = weapon->MaxSpeed; + if (weapon->IsTurboBoosted && Is_Target_Aircraft(target)) { + firespeed *= Rule.TurboBoost; + } + + bullet = new BulletClass(weapon->Bullet->Type, target, this, firepower, WarheadType(weapon->WarheadPtr->ID), firespeed); + + if (bullet != NULL) { + + /* + ** If this is firing from a moving platform, then the projectile is inaccurate. + */ + if (Is_Foot() && ((FootClass const *)this)->IsDriving) { + bullet->IsInaccurate = true; + } + + if (bullet->Unlimbo(fire_coord, dir)) { + } else { + delete bullet; + } + if (tclass.IsTurretEquipped) { + IsInRecoilState = true; + Mark(MARK_CHANGE_REDRAW); + } + + Arm = Rearm_Delay(IsSecondShot, which); + if (tclass.Is_Two_Shooter()) { + IsSecondShot = (IsSecondShot == false); + } + + /* + ** Perform any animation effect for this weapon. + */ + AnimType a = weapon->Anim; + switch (a) { + case ANIM_GUN_N: + a = AnimType(a + Dir_Facing(Fire_Direction())); + break; + + case ANIM_SAM_N: + a = AnimType(ANIM_SAM_N + Dir_Facing(PrimaryFacing.Current())); + break; + } + + /* + ** Play any sound effect tied to this weapon type. + */ + Sound_Effect(weapon->Sound, Fire_Coord(which)); + + /* + ** If there is a special firing animation, then create and attach it + ** now. + */ + if (a != ANIM_NONE) { + AnimClass * anim = new AnimClass(a, Fire_Coord(which)); + if (anim != NULL) { + anim->Attach_To(this); + } + } + + /* + ** Electric zap animation. + */ + if (weapon->IsElectric) { + ElectricZapDelay = 3; + ElectricZapTarget = target_coord; + ElectricZapWhich = which; +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() != RTTI_INFANTRY) { + Set_Stage(0); + Set_Rate(0); + } +#else + Set_Stage(0); + Set_Rate(0); +#endif + if (Ammo <= 1 && What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->IsCharged = false; + } + + TechnoClass * tech = As_Techno(target); + if (tech != NULL) { + tech->Clicked_As_Target(PlayerPtr->Class->House, 4); // 2019/09/20 JAS - Added record of who clicked on the object + } + } + + /* + ** Reduce ammunition for this object. + */ + if (Ammo > 0) { + Ammo--; + } + + /* + ** Firing will in all likelihood, require the unit to be redrawn. Flag it to be + ** redrawn here. + */ + Mark(MARK_CHANGE); + + /* + ** If a projectile was fired from a unit that is hidden in the darkness, + ** reveal that unit and a little area around it. + ** For multiplayer games, only reveal the unit if the target is the + ** local player. + */ +#if (0) + if ((!IsOwnedByPlayer && !IsDiscoveredByPlayer) || (!Map[Center_Coord()].IsMapped && (What_Am_I()!=RTTI_AIRCRAFT || !IsOwnedByPlayer)) ) { + if (Session.Type == GAME_NORMAL) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } else { + ObjectClass * obj = As_Object(target); + if (obj != NULL) { + HousesType tgt_owner = obj->Owner(); + + if (PlayerPtr->Class->House == tgt_owner) { + Map.Sight_From(Coord_Cell(Center_Coord()), 2, PlayerPtr, false); + } + } + } + } + +#else + + /* + ** For client/server multiplayer, we need to reveal for any human player that is the target. ST - 3/13/2019 5:43PM + */ + ObjectClass *obj = As_Object(target); + if (obj) { + HousesType tgt_owner = obj->Owner(); + + HouseClass *player = HouseClass::As_Pointer(tgt_owner); + if ((player != NULL) && player->IsHuman) { + if ((!Is_Owned_By_Player(player) && !Is_Discovered_By_Player(player)) || (!Map[Coord_Cell(Center_Coord())].Is_Mapped(House) && (What_Am_I()!=RTTI_AIRCRAFT || !Is_Owned_By_Player(player))) ) { + Map.Sight_From(Coord_Cell(Center_Coord()), 1, player, false); + } + } + } + +#endif + } + + return(bullet); +} + + +/*********************************************************************************************** + * TechnoClass::Player_Assign_Mission -- Assigns a mission as result of player input. * + * * + * This routine is called when the mission for an object needs to change as a result of * + * player input. The basic operation would be to queue the event and let the action * + * occur at the frame dictated by the queuing system. However, if a voice response is * + * indicated, then perform it at this time. This will give a greater illusion of * + * immediate response. * + * * + * INPUT: mission -- The mission order to assign to this object. * + * * + * target -- The target of this object. This will be used for combat and attack. * + * * + * destination -- The movement destination for this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination) +{ + assert(IsActive); + + if (AllowVoice) { + if (mission == MISSION_ATTACK) { + Response_Attack(); + } else { + Response_Move(); + } + } + + if (FormMove) { + SpeedType speed = FormSpeed; + MPHType maxspeed = FormMaxSpeed; + if (Is_Foot()) { + const FootClass* foot = (const FootClass*)this; + if (foot->Group < MAX_TEAMS) { + speed = TeamSpeed[foot->Group]; + maxspeed = TeamMaxSpeed[foot->Group]; + } + } + Queue_Mission(TargetClass(this), mission, target, destination, speed, maxspeed); + } else { + + /* + ** Cooerce the movement mission into a queued movement mission if the ALT key was + ** held down. + */ + // + // MBL 04.14.2020 Original code KO, since is still active and can still hit + // + // if (mission == MISSION_MOVE && (Keyboard->Down(Options.KeyQueueMove1) || Keyboard->Down(Options.KeyQueueMove2))) { + // mission = MISSION_QMOVE; + // } + + // + // MBL 04.14.2020 - Apply the same logic as above, using what is assigned as hotkeys + // + if (PlayerPtr && House) { + if (PlayerPtr == House) { + if (mission == MISSION_MOVE && PlayerPtr->IsQueuedMovementToggle) { + mission = MISSION_QMOVE; + } + } + } + + Queue_Mission(TargetClass(this), mission, target, destination); + } +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines what action to perform if object is selected. * + * * + * This routine will examine the object specified and return with the action that will * + * be performed if the mouse button were clicked over the object. * + * * + * INPUT: object -- The object that the mouse button might be clicked on. * + * * + * OUTPUT: Returns with the action that will be performed if the object was clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 03/21/1995 JLB : Special target control for trees. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(ObjectClass const * object) const +{ + assert(IsActive); + + if (object != NULL) { + + /* + ** Return the ACTION_SELF flag if clicking on itself. However, if this + ** object cannot do anything special with itself, then just return with + ** the no action flag. + */ + if (object == this && CurrentObject.Count() == 1 && House->IsPlayerControl) { + return(ACTION_SELF); + } + + bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); + bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() /*KO && Can_Player_Fire()*/) { +// if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (altdown) { + if (House->IsPlayerControl && Can_Player_Move()) { + return(ACTION_MOVE); + } + } + + /* + ** Override so that toggled select state can be performed while the key + ** is held down. + */ + bool is_a_loaner = object->Is_Techno() && ((TechnoClass*)object)->IsALoaner; + if (shiftdown) { + if (!is_a_loaner) { + return(ACTION_TOGGLE_SELECT); + } + } + + /* + ** If the weapon is blatantly disallowed from firing on the object specified, then + ** don't allow the attach check logic to proceed. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (Is_Weapon_Equipped() && + ttype->PrimaryWeapon->Bullet != NULL && + ttype->PrimaryWeapon->Bullet->IsSubSurface && + Map[object->Target_Coord()].Land_Type() != LAND_WATER) { + + // Do nothing. + + } else { + + /* + ** If firing is possible and legal, then return this action potential. + */ + if (House->IsPlayerControl && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Rule.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) { + + if (Is_Weapon_Equipped() || + (What_Am_I() == RTTI_INFANTRY && + (((InfantryTypeClass const *)ttype)->IsBomber || + ((InfantryTypeClass const *)ttype)->IsCapture) + )) { + // Check for anti-air capability + int primary = What_Weapon_Should_I_Use(object->As_Target()); + WeaponTypeClass const * weapon = (primary == 0) ? Techno_Type_Class()->PrimaryWeapon : Techno_Type_Class()->SecondaryWeapon; + if ((object->What_Am_I() != RTTI_AIRCRAFT) || + ((weapon != NULL) && weapon->Bullet->IsAntiAircraft) || + (object->Is_Techno() && (((TechnoClass *)object)->Height == 0))) { + if (Can_Player_Move() || In_Range(object, primary)) { + if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable)) { + return(ACTION_ATTACK); + } else { + if (!Can_Player_Move()) { + return(ACTION_NONE); + } else { + return(ACTION_ATTACK); + } + } + } + } + } + } + } + + /* + ** Possibly try to select the specified object, if that is warranted. + */ + if (!Is_Weapon_Equipped() || !House->IsPlayerControl || object->Owner() == Owner()) { + if ((!is_a_loaner || !Is_Owned_By_Player()) && object->Class_Of().IsSelectable && (!object->Is_Selected_By_Player() || CurrentObject.Count())) { + return(ACTION_SELECT); + } + return(ACTION_NONE); + } + } + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::What_Action -- Determines action to perform if cell is clicked on. * + * * + * Use this routine to determine what action will be performed if the specified cell * + * is clicked on. Usually this action is either a ACTION_MOVE or ACTION_NOMOVE. The action * + * nomove is used to perform special case checking for nearby cells if in fact the mouse * + * is clicked over the cell. * + * * + * INPUT: cell -- The cell to check for being clicked over. * + * * + * OUTPUT: Returns with the action that will occur if the cell is clicked on. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + * 07/10/1995 JLB : Force fire for buildings is explicitly disabled. * + *=============================================================================================*/ +ActionType TechnoClass::What_Action(CELL cell) const +{ + assert(IsActive); + + CellClass const * cellptr = &Map[cell]; + OverlayTypeClass const * optr = NULL; + + bool altdown = (Keyboard->Down(Options.KeyForceMove1) || Keyboard->Down(Options.KeyForceMove2)); + bool ctrldown = (Keyboard->Down(Options.KeyForceAttack1) || Keyboard->Down(Options.KeyForceAttack2)); + bool shiftdown = (Keyboard->Down(Options.KeySelect1) || Keyboard->Down(Options.KeySelect2)); + + /* + ** Disable recognizing the key forced fire option when dealing with buildings. + */ + if (What_Am_I() == RTTI_BUILDING) ctrldown = false; + + /* + ** Disable recognizing the key forced fire option when dealing with submarines. + */ + if(What_Am_I() == RTTI_VESSEL) { + WeaponTypeClass const * weapon = ((VesselClass *)this)->Class->PrimaryWeapon; + if (weapon && weapon->Bullet->IsSubSurface) ctrldown = false; + } + + + if (cellptr->Overlay != OVERLAY_NONE) { + optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + } + + /* + ** Special guard area mission is possible if both the control and the + ** alt keys are held down. + */ + if (House->IsPlayerControl && ctrldown && altdown && Can_Player_Move() && Can_Player_Fire()) { + return(ACTION_GUARD_AREA); + } + + if (House->IsPlayerControl && Techno_Type_Class()->PrimaryWeapon != NULL && (ctrldown || (optr && optr->IsLegalTarget))) { + WarheadTypeClass const * whead = Techno_Type_Class()->PrimaryWeapon->WarheadPtr; + +// To be fixed for firing on ore by accounting for ore and ignoring the overlay in that case. + + if (optr == NULL || (optr->IsWall && (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)))) { + int primary = What_Weapon_Should_I_Use(::As_Target(cell)); + if (Can_Player_Move() || In_Range(::As_Target(cell), primary)) { + return(ACTION_ATTACK); + } + } + } + + if (House->IsPlayerControl && Can_Player_Move()) { + + /* + ** Special override to force a move regardless of what is occupying the location. + */ + if (shiftdown) { + return(ACTION_MOVE); + } + + /* + ** If the object can enter the cell specified, then allow + ** movement to it. + */ + if (Can_Enter_Cell(cell) <= MOVE_CLOAK) { + return(ACTION_MOVE); + } + return(ACTION_NOMOVE); + } + + return(ACTION_NONE); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Move -- Determines if the object can move be moved by player. * + * * + * Use this routine to determine whether a movement order can be given to this object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given a movement order by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Move(void) const +{ + assert(IsActive); + + return(House->IsPlayerControl); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Player_Fire -- Determines if the player can give this object a fire order. * + * * + * Call this routine to determine if this object can be given a fire order by the player. * + * Such objects will affect the mouse cursor accordingly -- usually causes the targeting * + * cursor to appear. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this object be given firing orders by the player? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Player_Fire(void) const +{ + assert(IsActive); + + if (House->IsPlayerControl && Is_Techno() && Techno_Type_Class()->PrimaryWeapon != NULL) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Weapon_Equipped -- Determines if this object has a combat weapon. * + * * + * Use this routine to determine if this object is equipped with a combat weapon. Such * + * determination is used by the AI system to gauge the threat potential of the object. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object equipped with a combat weapon? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Weapon_Equipped(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->PrimaryWeapon != NULL); +} + + +/*********************************************************************************************** + * TechnoClass::Can_Repair -- Determines if the object can and should be repaired. * + * * + * Use this routine to determine if the specified object is a candidate for repair. In * + * order to qualify, the object must be allowed to be repaired (in theory) and it must * + * be below full strength. If these conditions are met, then it can be repaired. * + * * + * INPUT: none * + * * + * OUTPUT: bool; May this unit be repaired? A return value of false may mean that the object * + * is not allowed to be repaired, or it might be full strength already. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Can_Repair(void) const +{ + assert(IsActive); + + /* + ** Temporary hack to disable repair cursor over non-buildings. + */ + if (What_Am_I() != RTTI_BUILDING) { + return(false); + } + return(Techno_Type_Class()->IsRepairable && Strength != Class_Of().MaxStrength); +} + + +/*********************************************************************************************** + * TechnoClass::Weapon_Range -- Determines the maximum range for the weapon. * + * * + * Use this routine to determine the maximum range for the weapon indicated. * + * * + * INPUT: which -- Which weapon to use when determining the range. 0=primary, 1=secondary. * + * * + * OUTPUT: Returns with the range of the weapon (in leptons). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Weapon_Range(int which) const +{ + assert(IsActive); + assert((unsigned)which < 2); + + WeaponTypeClass const * weapon = NULL; + TechnoTypeClass const & ttype = *Techno_Type_Class(); + + switch (which) { + case 0: + weapon = ttype.PrimaryWeapon; + break; + + case 1: + weapon = ttype.SecondaryWeapon; + break; + } + if (weapon != NULL) { + return(weapon->Range); + } + return(0); +} + + +/*************************************************************************** + * TechnoClass::Override_Mission -- temporarily overrides a units mission * + * * + * * + * * + * INPUT: MissionType mission - the mission we want to override * + * TARGET tarcom - the new target we want to override * + * TARGET navcom - the new navigation point to override* + * * + * OUTPUT: none * + * * + * WARNINGS: If a mission is already overridden, the current mission is * + * just re-assigned. * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +void TechnoClass::Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom) +{ + assert(IsActive); + + SuspendedTarCom = TarCom; + RadioClass::Override_Mission(mission, tarcom, navcom); + Assign_Target(tarcom); +} + + +/*************************************************************************** + * TechnoClass::Restore_Mission -- Restores an overridden mission * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/28/1995 PWG : Created. * + *=========================================================================*/ +bool TechnoClass::Restore_Mission(void) +{ + assert(IsActive); + + if (RadioClass::Restore_Mission()) { + Assign_Target(SuspendedTarCom); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Renovate -- Heal a building to maximum * + * * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/15/1996 BWG : Created. * + *=============================================================================================*/ +void TechnoClass::Renovate(void) +{ + assert(IsActive); + + Mark(MARK_CHANGE); + Strength = Techno_Type_Class()->MaxStrength; + if (What_Am_I() == RTTI_BUILDING) { + ((BuildingClass *)this)->Repair(0); + } +} + + +/*********************************************************************************************** + * TechnoClass::Captured -- Handles capturing this object. * + * * + * This routine is called when this object gets captured by the house specified. It handles * + * removing this object from any targeting computers and then changes the ownership of * + * the object to the new house. * + * * + * INPUT: newowner -- Pointer to the house that is now the new owner. * + * * + * OUTPUT: Was the object captured? Failure would mean that it is already under control of * + * the house specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 09/29/1995 JLB : Keeps track of quantity records. * + *=============================================================================================*/ +bool TechnoClass::Captured(HouseClass * newowner) +{ + assert(IsActive); + + if (newowner != House) { + + /* + ** Capture attempt springs any "entered" trigger. The entered trigger + ** occurs first since there may be a special trigger attached to this + ** object that flags a capture as a win and a destroy as a loss. This + ** order is necessary because the object is recorded as a kill as well. + */ + if (Trigger.Is_Valid()) { + Trigger->Spring(TEVENT_PLAYER_ENTERED, this); + } + + /* + ** Record this as a kill. + */ + Record_The_Kill(NULL); + + /* + ** Special kill record logic for capture process. + */ + House->Tracking_Remove(this); + newowner->Tracking_Add(this); + switch (What_Am_I()) { + case RTTI_BUILDING: + newowner->BuildingsKilled[Owner()]++; + break; + + case RTTI_AIRCRAFT: + case RTTI_INFANTRY: + case RTTI_UNIT: + case RTTI_VESSEL: + newowner->UnitsKilled[Owner()]++; + break; + + default: + break; + } + House->WhoLastHurtMe = newowner->Class->House; + + /* + ** Remove from targeting computers. + */ + Detach_All(false); + + /* + ** Change ownership now. + */ + House = newowner; + IsOwnedByPlayer = (House == PlayerPtr); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TechnoClass::Take_Damage -- Records damage assessed to this object. * + * * + * This routine is called when this object has taken damage. It handles recording whether * + * this object has been destroyed. If it has, then mark the appropriate kill records as * + * necessary. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/20/1995 JLB : Created. * + *=============================================================================================*/ +ResultType TechnoClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(IsActive); + + ResultType result = RESULT_NONE; + + /* + ** If not a forced damage condition, adjust damage according to house override armor + ** value. + */ + if (!forced && damage > 0) { + damage = damage * ArmorBias * House->ArmorBias; + } + + if (IronCurtainCountDown == 0) { + result = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + } + + switch (result) { + case RESULT_DESTROYED: + Transmit_Message(RADIO_OVER_OUT); + Stun(); + + /* + ** If this object explodes with violent damage, then perform the explosion + ** now and use the warhead type and full strength as the explosion values. + */ + if (Techno_Type_Class()->IsExploding) { + + /* + ** The warhead to use is based on the weapon this object is equipped with. + */ + WarheadType wh = WARHEAD_HE; + if (Techno_Type_Class()->PrimaryWeapon != NULL) { + wh = WarheadType(Techno_Type_Class()->PrimaryWeapon->WarheadPtr->ID); + } + + int damage = Techno_Type_Class()->MaxStrength; + new AnimClass(Combat_Anim(damage, wh, Map[Center_Coord()].Land_Type()), Center_Coord()); + int radius = damage * Rule.ExplosionSpread; +// int radius = damage/2; + Wide_Area_Damage(Center_Coord(), radius, damage, source, wh); + } + + if (this == (TechnoClass *)::As_Object(House->UnitToTeleport)) { + House->UnitToTeleport = 0; + if (!Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + if (Map.IsTargettingMode == SPC_CHRONO2) { + KeyNumType input = KN_RMOUSE; + Map.AI(input, 0, 0); + } + } + + /* + ** May trigger an achievement. ST - 11/14/2019 1:56PM + */ + if (Session.Type == GAME_NORMAL && !House->IsHuman && source && source->House && source->House->IsHuman) { + TechnoTypeClass const *object_type = Techno_Type_Class(); + if (object_type) { + + RTTIType what = What_Am_I(); + if (what == RTTI_AIRCRAFT || what == RTTI_INFANTRY || what == RTTI_UNIT || what == RTTI_VESSEL) { + On_Achievement_Event(source->House, "UNIT_DESTROYED", object_type->IniName); + } + } + } + break; + + /* + ** If some damage was received and this object is cloaked, shimmer + ** the cloak a bit. + */ + default: + if (source != NULL && !House->Is_Ally(source)) { + IsTickedOff = true; + } + Do_Shimmer(); + break; + + case RESULT_NONE: + break; + } + return(result); +} + + +/*********************************************************************************************** + * TechnoClass::Record_The_Kill -- Records the death of this object. * + * * + * This routine is used to record the death of this object. It will handle updating the * + * owner house with the kill record as well as springing any trigger events associated with * + * this object's death. * + * * + * INPUT: source -- Pointer to the source of this object's death (if there is a source). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 08/23/1995 JLB : Building loss is only counted if it received damage. * + *=============================================================================================*/ +void TechnoClass::Record_The_Kill(TechnoClass * source) +{ + assert(IsActive); + + int total_recorded = 0; + + int points = Techno_Type_Class()->Points; + + /* + ** Handle any trigger event associated with this object. + */ + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_ATTACKED, this); + + if (Trigger.Is_Valid() && source) Trigger->Spring(TEVENT_DISCOVERED, this); + + if (Trigger.Is_Valid()) Trigger->Spring(TEVENT_DESTROYED, this); + + if (source != NULL) { + Crew.Made_A_Kill(); + + House->WhoLastHurtMe = source->Owner(); + + /* + ** Add up the score for killing this unit + */ + source->House->PointTotal += points; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Hack check: if they were trying to teleport this unit when it died, take +// the map mode out of teleportation mode. + if(IsOwnedByPlayer && Map.IsTargettingMode == SPC_CHRONO2 && House->UnitToTeleport == As_Target()) { + Map.IsTargettingMode = SPC_NONE; + } +#endif + switch (What_Am_I()) { + case RTTI_BUILDING: + { + StructType bldg = *(BuildingClass *)this; + if (bldg != STRUCT_BARREL && bldg != STRUCT_BARREL3 && + bldg != STRUCT_APMINE && bldg != STRUCT_AVMINE) { + if (((BuildingClass *)this)->WhoLastHurtMe != HOUSE_NONE) { + House->BuildingsLost++; + } + + if (source != NULL) { + if (Session.Type == GAME_INTERNET) { + source->House->DestroyedBuildings->Increment_Unit_Total( ((BuildingClass*)this)->Class->Type ); + } + source->House->BuildingsKilled[Owner()]++; + } + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + } + } + break; + + case RTTI_AIRCRAFT: + if (source != NULL && Session.Type == GAME_INTERNET) { + source->House->DestroyedAircraft->Increment_Unit_Total( ((AircraftClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_INFANTRY: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedInfantry->Increment_Unit_Total( ((InfantryClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_UNIT: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((UnitClass*)this)->Class->Type ); + total_recorded++; + } + //Fall through..... + case RTTI_VESSEL: + if (source != NULL && !total_recorded && Session.Type == GAME_INTERNET) { + source->House->DestroyedUnits->Increment_Unit_Total( ((VesselClass*)this)->Class->Type ); + } + + House->UnitsLost++; + if (source != NULL) source->House->UnitsKilled[Owner()]++; + + /* + ** If the map is displaying the multiplayer player names & their + ** # of kills, tell it to redraw. + */ + if (Map.Is_Player_Names()) { + Map.Player_Names(true); + } + break; + + default: + break; + } + + /* + ** Since we lost an object, we lose the associated points as well. + */ + House->PointTotal -= points; +} + + +/*********************************************************************************************** + * TechnoClass::Nearby_Location -- Radiates outward looking for clear cell nearby. * + * * + * This routine is used to find a nearby location from center of this object. It can lean * + * toward finding a location closest to an optional object. * + * * + * INPUT: object -- Optional object that the finding algorithm will try to find a close * + * spot to. * + * * + * OUTPUT: Returns with the cell that is closest to this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/1995 JLB : Created. * + * 09/28/1995 JLB : Uses map scan function. * + *=============================================================================================*/ +CELL TechnoClass::Nearby_Location(TechnoClass const * techno) const +{ + assert(IsActive); + + SpeedType speed = Techno_Type_Class()->Speed; + if (speed == SPEED_WINGED) { + speed = SPEED_TRACK; + } + + CELL cell = 0; + if (techno != NULL) { + cell = Coord_Cell(techno->Center_Coord()); + } else { + cell = Coord_Cell(Center_Coord()); + } + + return(Map.Nearby_Location(cell, speed, Map[cell].Zones[Techno_Type_Class()->MZone], Techno_Type_Class()->MZone)); +} + + +/*********************************************************************************************** + * TechnoClass::Do_Uncloak -- Cause the stealth tank to uncloak. * + * * + * This routine will start the stealth tank to uncloak. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Uncloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == CLOAKED || Cloak == CLOAKING)) { + if (Cloak == CLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + Cloak = UNCLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Cloak -- Start the object into cloaking stage. * + * * + * This routine will start the object into its cloaking state. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Cloak(void) +{ + assert(IsActive); + + if (IsCloakable && (Cloak == UNCLOAKED || Cloak == UNCLOAKING)) { + Detach_All(false); + + if (Cloak == UNCLOAKED) { + Map.RadarClass::Flag_To_Redraw(true); + } + + Cloak = CLOAKING; + CloakingDevice.Set_Stage(0); + CloakingDevice.Set_Rate(1); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(What_Am_I() == RTTI_VESSEL) { + Sound_Effect(VOC_SUBSHOW, Coord); + } else { + Sound_Effect(VOC_IRON1, Coord); + } +#else + Sound_Effect(VOC_SUBSHOW, Coord); +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Do_Shimmer -- Causes this object to shimmer if it is cloaked. * + * * + * This routine is called when this object should shimmer. If the object is cloaked, then * + * a shimmering effect (partial decloak) occurs. For objects that are not cloaked, no * + * effect occurs. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Do_Shimmer(void) +{ + assert(IsActive); +#if(0) + if (IsCloakable && Cloak == CLOAKED) { + Cloak = CLOAKING; + CloakingDevice.Set_Stage(MAX_UNCLOAK_STAGE/2); + CloakingDevice.Set_Rate(1); + } +#else + Do_Uncloak(); +#endif +} + + +/*********************************************************************************************** + * TechnoClass::Visual_Character -- Determine the visual character of the object. * + * * + * This routine will determine how this object should be drawn. Typically, this is the * + * unmodified visible state, but cloaked objects have a different character. * + * * + * INPUT: raw -- Should the check be based on the unmodified cloak condition of the * + * object? If false, then an object owned by the player will never become * + * completely invisible. * + * * + * OUTPUT: Returns with the visual character to use when displaying this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/07/1995 JLB : Created. * + * 05/27/1996 JLB : Knows about invisible objects. * + *=============================================================================================*/ +VisualType TechnoClass::Visual_Character(bool raw) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsInvisible) { + if ((Session.Type != GAME_NORMAL) || Is_Owned_By_Player()) return(VISUAL_NORMAL); + if (!Debug_Map) return(VISUAL_HIDDEN); + } + + /* + ** When uncloaked or in map editor mode, always draw the object normally. + */ + if (Cloak == UNCLOAKED || Debug_Map) return(VISUAL_NORMAL); + + /* + ** A cloaked unit will not be visible at all unless it is owned + ** by the player. + */ + if (Cloak == CLOAKED) { + // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + return(VISUAL_HIDDEN); + } + + int stage = CloakingDevice.Fetch_Stage(); + if (Cloak == UNCLOAKING) stage = MAX_UNCLOAK_STAGE - stage; + if (stage <= 0) { + return(VISUAL_NORMAL); + } + + stage = fixed(stage, MAX_UNCLOAK_STAGE) * 256; + + if (stage < 0x0040) return(VISUAL_INDISTINCT); + if (stage < 0x0080) return(VISUAL_DARKEN); + if (stage < 0x00C0) return(VISUAL_SHADOWY); + //if (!raw && IsOwnedByPlayer) return(VISUAL_SHADOWY); + if (!raw && Is_Owned_By_Player()) return(VISUAL_SHADOWY); // Changed for multiplayer. Not needed except to test in the old renderer. ST - 3/13/2019 5:56PM + if (stage < 0x00FF) return(VISUAL_RIPPLE); + return(VISUAL_HIDDEN); +} + +void TechnoClass::Techno_Draw_Object_HD(struct Image_t* image, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const { + //void const* remap = Remap_Table(); + void const* shadow = Map.UnitShadow; + + PlayerColorType remapColor = PCOLOR_GOLD; + if (Techno_Type_Class()->IsRemappable) { + remapColor = House->RemapColor; + } + + y -= Lepton_To_Pixel(Height); + + CC_Draw_Shape(image, shapenum, x, y, window, SHAPE_CENTER | SHAPE_WIN_REL | SHAPE_FADING | SHAPE_GHOST, (const void *)remapColor, shadow, rotation); +} + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object -- General purpose draw object routine. * + * * + * This routine is used to draw the object. It will handle any remapping or cloaking * + * effects required. This logic is isolated here since all techno object share the same * + * render logic when it comes to remapping and cloaking. * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * rotation -- The rotation of the object. * + * * + * scale -- The scaling factor to use (24.8 fixed point). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + * 01/11/1996 JLB : Added rotation and scaling. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale) const +{ + assert(IsActive); + + if (rotation != DIR_N || scale != 0x0100) { + Disable_Uncompressed_Shapes(); + } + + if (shapefile != NULL) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + void const * shadow = Map.UnitShadow; + +#ifdef PARTIAL + /* + ** Create a minimum shape rectangle if one hasn't already been + ** calculated and the shape file matches the one that the + ** class thinks it should be using. This check is necessary because + ** the dimension rectangle pointer is referenced from the type class + ** object on the presumption that the shapefile pointer passed to this + ** routine matches. If it doesn't match, then the wrong rectangle information + ** will be stored into the type class object. + */ + TechnoTypeClass * ttype = Techno_Type_Class(); + if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { + if (ttype->DimensionData == NULL) { + ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; + } + if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { + ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); + } + } +#endif + if (Height > 0) { + shadow = Map.UnitShadowAir; + } + + y -= Lepton_To_Pixel(Height); + + /* + ** If they're viewing a spy, and the spy belongs to some other house, + ** make it look like an infantryman from our house + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (!IsOwnedByPlayer) { + if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); + } + if (((InfantryClass *)this)->Class->IsRemapOverride) { + remap = ((InfantryClass *)this)->Class->OverrideRemap; + } + } + + /* + ** Check for the special visual effect for the iron curtain + */ + if (IronCurtainCountDown > 0) { +// remap = RemapEmber; + remap = DisplayClass::FadingRed; + } + +#ifdef PREDATOR + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); + } +#else + switch (visual) { + case VISUAL_NORMAL: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + break; + + case VISUAL_INDISTINCT: + case VISUAL_DARKEN: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); + break; + + case VISUAL_SHADOWY: + case VISUAL_RIPPLE: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + break; + + case VISUAL_HIDDEN: + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if (window == WINDOW_VIRTUAL) { + CC_Draw_Shape(this, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + } + break; + } +#endif + } + + Enable_Uncompressed_Shapes(); +} + + + +/*********************************************************************************************** + * TechnoClass::Techno_Draw_Object_Virtual -- Draw object with virtual window support * + * * + * INPUT: shapefile -- Pointer to the shape file that the shape will be drawn from. * + * * + * shapenum -- The shape number of the object in the file to use. * + * * + * x,y -- Center pixel coordinate to use for rendering this object. * + * * + * window -- The clipping window to use when rendering. * + * * + * rotation -- The rotation of the object. * + * * + * scale -- The scaling factor to use (24.8 fixed point). * + * * + * shape_name -- The name of the shapefile * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 8/1/2019 5:32PM - ST : Created. * + *=============================================================================================*/ +void TechnoClass::Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation, int scale, const char *shape_name) const +{ + assert(IsActive); + + if (shape_name == NULL || *shape_name == 0) { + /* + ** If there's no override shape name, then call the regular draw + */ + Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); + return; + } + + if (shapefile != NULL) { + VisualType visual = Visual_Character(); + void const * remap = Remap_Table(); + void const * shadow = Map.UnitShadow; + +#ifdef PARTIAL + /* + ** Create a minimum shape rectangle if one hasn't already been + ** calculated and the shape file matches the one that the + ** class thinks it should be using. This check is necessary because + ** the dimension rectangle pointer is referenced from the type class + ** object on the presumption that the shapefile pointer passed to this + ** routine matches. If it doesn't match, then the wrong rectangle information + ** will be stored into the type class object. + */ + TechnoTypeClass * ttype = Techno_Type_Class(); + if (shapefile == ttype->Get_Image_Data() && shapenum < Get_Build_Frame_Count(shapefile)-1) { + if (ttype->DimensionData == NULL) { + ttype->DimensionData = new Rect [Get_Build_Frame_Count(shapefile)]; + } + if (ttype->DimensionData != NULL && !ttype->DimensionData[shapenum].Is_Valid()) { + ttype->DimensionData[shapenum] = Shape_Dimensions(shapefile, shapenum); + } + } +#endif + + if (Height > 0) { + shadow = Map.UnitShadowAir; + } + + y -= Lepton_To_Pixel(Height); + + /* + ** If they're viewing a spy, and the spy belongs to some other house, + ** make it look like an infantryman from our house + */ + if (What_Am_I() == RTTI_INFANTRY) { + if (!IsOwnedByPlayer) { + if (*(InfantryClass *)this == INFANTRY_SPY) remap = PlayerPtr->Remap_Table(); + } + if (((InfantryClass *)this)->Class->IsRemapOverride) { + remap = ((InfantryClass *)this)->Class->OverrideRemap; + } + } + + /* + ** Check for the special visual effect for the iron curtain + */ + if (IronCurtainCountDown > 0) { +// remap = RemapEmber; + remap = DisplayClass::FadingRed; + } + +#ifdef PREDATOR + if (visual != VISUAL_HIDDEN && visual != VISUAL_RIPPLE) { + if (visual == VISUAL_SHADOWY) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_PREDATOR, NULL, Map.FadingShade, rotation, scale); + } else { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + } + if (visual == VISUAL_DARKEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING, remap, Map.FadingShade, rotation, scale); + } + } + if (visual != VISUAL_NORMAL && visual != VISUAL_HIDDEN) { + CC_Draw_Shape(shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_CENTER|SHAPE_WIN_REL, NULL, NULL, rotation, scale); + } +#else + switch (visual) { + case VISUAL_NORMAL: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_FADING|SHAPE_GHOST, remap, shadow, rotation, scale); + break; + + case VISUAL_INDISTINCT: + case VISUAL_DARKEN: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, remap, Map.FadingShade, rotation, scale); + break; + + case VISUAL_SHADOWY: + case VISUAL_RIPPLE: + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + break; + + case VISUAL_HIDDEN: + // Server still needs to "render" hidden objects to the virtual window, so objects get created properly - SKY + if (window == WINDOW_VIRTUAL) { + CC_Draw_Shape(this, shape_name, shapefile, shapenum, x, y, window, SHAPE_PREDATOR|SHAPE_FADING|SHAPE_CENTER|SHAPE_WIN_REL, NULL, Map.FadingShade, rotation, scale); + } + break; + } +#endif + } +} + + +/*********************************************************************************************** + * TechnoClass::Remap_Table -- Fetches the appropriate remap table to use. * + * * + * This routine is used to fetch the appropriate remap table to use for this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the remap table to use for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/08/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoClass::Remap_Table(void) const +{ + assert(IsActive); + + if (Techno_Type_Class()->IsRemappable) { + return(House->Remap_Table(IsBlushing, Techno_Type_Class()->Remap)); + } + return(ColorRemaps[PCOLOR_GOLD].RemapTable); +} + + +/*********************************************************************************************** + * TechnoClass::Detach -- Handles removal of target from tracking system. * + * * + * This routine is called when the specified object is about to be removed from the game * + * system. The target object is removed from any tracking computers that this object may * + * have. * + * * + * INPUT: target -- The target object (as a target value) that is being removed from the * + * game. * + * * + * all -- Is the target about to die? A false value might indicate that the * + * object is merely cloaking. In such a case, radio contact will not * + * be affected. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Detach(TARGET target, bool all) +{ + assert(IsActive); + RadioClass::Detach(target, all); + + if (SuspendedMission != MISSION_NONE && SuspendedTarCom == target) { + SuspendedMission = MISSION_NONE; + SuspendedTarCom = TARGET_NONE; + } + + /* + ** If the targeting computer is assigned to the target, then the targeting + ** computer must be cleared. + */ + if (TarCom == target) { + Assign_Target(TARGET_NONE); + Restore_Mission(); + } + + /* + ** If it is in radio contact with another object, then that radio contact + ** must be broken. + */ + if (all && In_Radio_Contact() && Contact_With_Whom()->As_Target() == target) { + Transmit_Message(RADIO_OVER_OUT); + } +} + + +/*********************************************************************************************** + * TechnoClass::Kill_Cargo -- Destroys any cargo attached to this object. * + * * + * This routine handles the destruction of any cargo this object may contain. Typical of * + * this would be when a transport helicopter gets destroyed. * + * * + * INPUT: source -- The source of the destruction of the cargo. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Kill_Cargo(TechnoClass * source) +{ + assert(IsActive); + + while (Is_Something_Attached()) { + FootClass * foot = Detach_Object(); + if (foot != NULL) { + foot->Record_The_Kill(source); + delete foot; + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Crew_Type -- Fetches the kind of crew this object contains. * + * * + * This routine is called when generating survivors to this object. This routine returns * + * the type of survivor to generate. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the infantry type of a survivor. * + * * + * WARNINGS: This routine is designed to be called repeatedly. Once for each survivor to * + * generate. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType TechnoClass::Crew_Type(void) const +{ + assert(IsActive); + + /* + ** If this object contains no crew, then there can be no + ** crew inside, duh... return this news. + */ + if (!Techno_Type_Class()->IsCrew) { + return(INFANTRY_NONE); + } + + /* + ** The normal infantry survivor is the standard issue + ** minigunner. Certain buildings, especially neutral ones, tend to have + ** civilians exit them instead. + */ + InfantryType infantry = INFANTRY_E1; + if (House->ActLike == HOUSE_NEUTRAL) { + infantry = Random_Pick(INFANTRY_C1, INFANTRY_C9); + } else { + if (Techno_Type_Class()->PrimaryWeapon == NULL && Percent_Chance(15)) { + if (Percent_Chance(50)) { + infantry = INFANTRY_C1; + } else { + infantry = INFANTRY_C7; + } + } + } + return(infantry); +} + + +/*********************************************************************************************** + * TechnoClass::Value -- Fetches the target value for this object. * + * * + * This routine is used to fetch the target value for this object. The greater the value * + * returned, the better this object is as a target. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + * 08/16/1995 JLB : Adjusted for early mission lame-out. * + *=============================================================================================*/ +int TechnoClass::Value(void) const +{ + assert(IsActive); + + int value = 0; + + /* + ** In early missions, contents of transports are not figured + ** into the total value. + */ + if (Rule.Diff[House->Difficulty].IsContentScan || House->IQ >= Rule.IQContentScan) { + if (Is_Something_Attached()) { + FootClass * object = Attached_Object(); + + while (object != NULL) { + value += object->Value(); + object = (FootClass *)(ObjectClass *)object->Next; + } + } + } + +#ifdef TOFIX + /* + ** Increase the value of power producing object when there is power critical + ** defensive structures. + */ + if (What_Am_I() == RTTI_BUILDING && ((BuildingClass *)this)->Class->Power) { + if (House->BScan & (STRUCTF_ATOWER|STRUCTF_OBELISK)) { + value += Techno_Type_Class()->Reward; + } + } +#endif + + return Risk() + Techno_Type_Class()->Reward + value; +} + + +/*********************************************************************************************** + * TechnoClass::Threat_Range -- Returns the range to scan based on threat control. * + * * + * This routine will return the range to scan based on the control value specified. The * + * value returned by this routine is typically used when scanning for enemies. * + * * + * INPUT: control -- The range control parameter. * + * 0 = Use weapon range (zero is returned in this special case). * + * -1 = Scan without range restrictions (-1 is returned in this case). * + * 1 = Scan up to twice weapon range. * + * * + * OUTPUT: Returns with a range (or special value) that can be used in the threat scan * + * process. If zero is returned, then always check threat against In_Range(). If * + * -1 is returned, then no range limitation restriction exists. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Threat_Range(int control) const +{ + assert(IsActive); + + /* + ** Threat range means nothing if scanning the whole map. In such a case, just + ** return with the same control flag specified. + */ + if (control == -1) return(-1); + + /* + ** If simple guard range is requested, then return "0" since + ** this is a special control value that is calculated as the object's + ** weapon range. + */ + if (control == 0) { + /* + ** For normal guard mode or for area guard mode, use the override + ** threat range value as specified by the object's type class. + */ + if (Techno_Type_Class()->ThreatRange != 0) { + return(Techno_Type_Class()->ThreatRange); + } + return(0); + } + + /* + ** Area guard range is specified, so figure twice the weapon range of the + ** longest range weapon this object is equipped with. + */ + int range = Techno_Type_Class()->ThreatRange; + if (range == 0) { + range = max(Weapon_Range(0), Weapon_Range(1)); + } + + range *= 2; + range = Bound(range, 0x0000, 0x0A00); + + return(range); +} + + +/*********************************************************************************************** + * TechnoClass::Is_In_Same_Zone -- Determine if specified cell is in same zone as object. * + * * + * This will examine the specified cell to determine if it is in the same zone as this * + * object's location. * + * * + * INPUT: cell -- The cell that is to be checked against this object's current location. * + * * + * OUTPUT: bool; Is the specified cell in the same zone as this object is? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/06/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_In_Same_Zone(CELL cell) const +{ + MZoneType zone = Techno_Type_Class()->MZone; + return(Map[cell].Zones[zone] == Map[Center_Coord()].Zones[zone]); +} + + +/*********************************************************************************************** + * TechnoClass::Base_Is_Attacked -- Handle panic response to base being attacked. * + * * + * This routine is called when the base is being attacked. It will pull units off of the * + * field and send them back to defend the base. This routine will make taking an enemy * + * base much more difficult. * + * * + * INPUT: enemy -- Pointer to the enemy object that did the damage on the base. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can drastically affect the game play. The computer will probably * + * call off its attacks as a result. * + * * + * HISTORY: * + * 06/25/1995 JLB : Commented. * + * 10/15/1996 JLB : Alternates between guard area and attack. * + * 11/01/1996 JLB : Allow recruit of guard area units in multiplay. * + *=============================================================================================*/ +void TechnoClass::Base_Is_Attacked(TechnoClass const * enemy) +{ + assert(IsActive); + + FootClass * defender[6]; + memset(defender, '\0', sizeof(defender)); + + int value[ARRAY_SIZE(defender)]; + memset(value, '\0', sizeof(value)); + + int count = 0; + int weakest = 0; + int desired = enemy->Risk() * House->Control.TechLevel; + int risktotal = 0; + int zone = Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]; + + /* + ** Humans have to deal with their own base is attacked problems. + */ + if (enemy == NULL || House->Is_Ally(enemy) || House->IsHuman) { + return; + } + + /* + ** Don't overreact if this building can defend itself. + */ + if (Session.Type == GAME_NORMAL && Techno_Type_Class()->PrimaryWeapon != NULL) return; + + /* + ** If the enemy is not an infantry or a unit there is not much we can + ** do about it. + */ + if (enemy->What_Am_I() != RTTI_INFANTRY && enemy->What_Am_I() != RTTI_UNIT) { + return; + } + + /* + ** If we are a certain type of building, such as a barrel or land mine, + ** ignore the attack. + */ + if (Techno_Type_Class()->IsInsignificant) { + return; + } + + /* + ** If the threat has already been dealt with then we don't need to do + ** any work. Check for that here. + */ + if (enemy->Is_Foot() && ((FootClass *)enemy)->BaseAttackTimer != 0) { + return; + } + + /* + ** We will need units to defend our base. We need to suspend teams until + ** the situation has been dealt with. + */ + TeamClass::Suspend_Teams(Rule.SuspendPriority, House); + + /* + ** Loop through the infantry looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Infantry.Count() && desired > 0; index++) { + InfantryClass * infantry = Infantry.Ptr(index); + if (infantry != NULL && infantry->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!infantry->Is_Weapon_Equipped() || + (!MissionControl[infantry->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; +// (Mission != MISSION_GUARD_AREA || Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (infantry->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[infantry->Center_Coord()].Zones[infantry->Class->MZone] != Map[Center_Coord()].Zones[infantry->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = infantry->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (ArchiveTarget == As_Target()) { + threat *= 100; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = infantry; + value[count] = threat; + count++; + continue; + } + + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) infantry; + continue; + } + if (value[lp] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + /* + ** Loop through the units looking for those who are capable of going + ** on a rescue mission. + */ + for (int index = 0; index < Units.Count() && desired > 0; index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && unit->Owner() == Owner()) { + + /* + ** Never recruit sticky guard units to defend a base. + */ + if (!unit->Is_Weapon_Equipped() || + (!MissionControl[unit->Mission].IsRecruitable && Session.Type == GAME_NORMAL)) continue; + + /* + ** Don't allow a response if it doesn't have a weapon that will affect the + ** enemy object. + */ + if (unit->Class->PrimaryWeapon->WarheadPtr->Modifier[enemy->Techno_Type_Class()->Armor] == 0) { + continue; + } + + /* + ** Don't try to help if the building is on another planet. + */ + if (Map[unit->Center_Coord()].Zones[unit->Class->MZone] != Map[Center_Coord()].Zones[unit->Class->MZone]) continue; + + /* + ** Find the amount of threat that this unit can apply to the + ** enemy. + */ + int threat = unit->Rescue_Mission(enemy->As_Target()); + + /* + ** If it can't apply any threat then do just skip it and do not + ** add it to the list. + */ + if (!threat) { + continue; + } + + /* + ** Greatly increase the threat value if this unit is already assigned to protect + ** the target. + */ + if (threat > 0 && ArchiveTarget == As_Target()) { + threat *= 10; + } + + /* + ** If the value returned is negative then this unit is already + ** assigned to fighting the enemy, so subtract its value from + ** the enemy's desired value. + */ + if (threat < 0) { + desired += threat; + continue; + } + + if (count < ARRAY_SIZE(defender)) { + defender[count] = unit; + value[count] = threat; + count++; + continue; + } + if (threat > weakest) { + int newweakest = threat; + + for (int lp = 0; lp < count; lp ++) { + if (value[lp] == weakest) { + value[lp] = threat; + defender[lp] = (FootClass *) unit; + continue; + } + if (value[lp] < newweakest) { +// if (value[count] < newweakest) { + newweakest = value[lp]; + } + } + weakest = newweakest; + } + } + } + + if (desired > 0) { + + /* + ** Sort the defenders by value, this doesn't take very long and will + ** help the closest defenders to respond. + */ + for (int lp = 0; lp < count - 1; lp ++) { + for (int lp2 = lp + 1; lp2 < count; lp2++) { + if (value[lp] < value[lp2]) { + + value[lp] ^= value[lp2]; + value[lp2] ^= value[lp]; + value[lp] ^= value[lp2]; + + FootClass *temp; + temp = defender[lp]; + defender[lp] = defender[lp2]; + defender[lp2] = temp; + } + } + } + + for (int lp = 0; lp < count; lp ++) { + if (Percent_Chance(50)) { + defender[lp]->Assign_Mission(MISSION_RESCUE); + } else { + defender[lp]->Assign_Mission(MISSION_GUARD_AREA); + defender[lp]->ArchiveTarget = As_Target(); + } + defender[lp]->Assign_Target(enemy->As_Target()); + risktotal += defender[lp]->Risk(); + if (risktotal > desired) { + break; + } + } + } + + if (risktotal > desired && enemy->Is_Foot()) { + ((FootClass *)enemy)->BaseAttackTimer = TICKS_PER_MINUTE * Rule.BaseDefenseDelay; + } +} + + +/*********************************************************************************************** + * TechnoClass::Is_Allowed_To_Retaliate -- Checks object to see if it can retaliate. * + * * + * This routine is called when this object has suffered some damage and it needs to know * + * if it should fight back. The object that caused the damage is specifed as a parameter. * + * * + * INPUT: source -- The points to the object that was the source of the damage applied * + * to this object. * + * * + * OUTPUT: bool; Should retaliation occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Allowed_To_Retaliate(TechnoClass const * source) const +{ + /* + ** If there is no source of the damage, then retaliation cannot occur. + */ + if (source == NULL) return(false); + + /* + ** If the mission precludes retaliation, then don't retaliate. + */ + if (!MissionControl[Mission].IsRetaliate) return(false); + + /* + ** Fixed wing aircraft are not responsive enough to retaliate to damage recieved. + */ + if (What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)this)->Class->IsFixedWing) { + return(false); + } + + /* + ** If the source of the damage is an ally, then retaliation shouldn't + ** occur either. + */ + if (House->Is_Ally(source)) return(false); + + /* + ** Only objects that have a damaging weapon are allowed to retaliate. + */ + if (Combat_Damage() <= 0 || !Is_Weapon_Equipped()) return(false); + + /* + ** If this is not equipped with a weapon that can attack the molester, then + ** don't allow retaliation. + */ + TechnoTypeClass const * ttype = Techno_Type_Class(); + if (ttype->PrimaryWeapon->WarheadPtr != NULL && + ttype->PrimaryWeapon->WarheadPtr->Modifier[source->Techno_Type_Class()->Armor] == 0) { + return(false); + } + + /* + ** One can never retaliate against a dog because of their peculiar nature of attacking. + ** Dogs must be attacked using normal target processing. + */ + if (source->What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)source)->Class->IsDog) return(false); + + /* + ** Don't allow retaliation if it isn't equipped with a weapon that can deal with the threat. + */ + if (source->What_Am_I() == RTTI_AIRCRAFT && !ttype->PrimaryWeapon->Bullet->IsAntiAircraft) return(false); + + /* + ** Tanya is not allowed to retaliate against buildings in the normal sense while in guard mode. That + ** is, unless it is owned by the computer. Normally, Tanya can't do anything substantial to a building + ** except to blow it up. + */ + if ((House->IsHuman || (Session.Type == GAME_NORMAL && House->IsPlayerControl)) + && source->What_Am_I() == RTTI_BUILDING && What_Am_I() == RTTI_INFANTRY && ((InfantryTypeClass const *)ttype)->IsBomber) { + return(false); + } + + /* + ** If a human house is not allowed to retaliate automatically, then don't + */ + if (House->IsHuman && !Rule.IsSmartDefense && (What_Am_I() != RTTI_INFANTRY || *((InfantryClass*)this) != INFANTRY_TANYA || source->What_Am_I() != RTTI_INFANTRY)) return(false); + + /* + ** If this object is part of a team that prevents retaliation then don't allow retaliation. + */ + if (Is_Foot() && ((FootClass *)this)->Team.Is_Valid() && ((FootClass *)this)->Team->Class->IsSuicide) { + return(false); + } + + /* + ** Compare potential threat of the current target and the potential new target. Don't retaliate + ** if it is currently attacking the greater threat. + */ + if (!House->IsHuman && Percent_Chance(50)) { + fixed source_val = 0; + int primary = What_Weapon_Should_I_Use(source->As_Target()); + WeaponTypeClass const * weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(source, primary)) { + source_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + + fixed current_val = 0; + TechnoClass const * tech = As_Techno(TarCom); + if (tech != NULL) { + primary = What_Weapon_Should_I_Use(tech->As_Target()); + weapon = (primary == 0) ? source->Techno_Type_Class()->PrimaryWeapon : source->Techno_Type_Class()->SecondaryWeapon; + if (weapon != NULL && weapon->WarheadPtr != NULL && In_Range(tech, primary)) { + current_val = weapon->WarheadPtr->Modifier[Techno_Type_Class()->Armor]; + } + } + if (source_val <= current_val) return(false); + } + + /* + ** If it is already busy attacking another target, then don't retaliate. + */ +// if (In_Range(TarCom)) return(false); + + /* + ** All checks passed, so return that retaliation is allowed. + */ + return(true); +} + + +/*********************************************************************************************** + * TechnoClass::Get_Ownable -- Fetches the ownable bits for this object. * + * * + * This routine will return the ownable bits for this object. The ownable bits represent * + * the houses that are allowed to own this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the ownable bits for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Get_Ownable(void) const +{ + assert(IsActive); + + return ((TechnoTypeClass const &)Class_Of()).Get_Ownable(); +// return ((TechnoTypeClass const &)Class_Of()).Ownable; +} + + +/*********************************************************************************************** + * TechnoClass::Risk -- Fetches the risk associated with this object. * + * * + * This routine is called when the risk value for this object needs to be determined. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the risk value for this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Risk(void) const +{ + assert(IsActive); + + return(Techno_Type_Class()->Risk); +} + + +/*********************************************************************************************** + * TechnoClass::Tiberium_Load -- Fetches the current tiberium load percentage. * + * * + * This routine will return the current Tiberium load (expressed as a fixed point fraction) * + * that this object currently contains. Typical implementor of this function would be * + * the harvester. Any object that can return a non-zero value should derive from this * + * function in order to return the appropriate value. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current Tiberium load expressed as a fixed point number. * + * 0x0000 = empty * + * 0x0080 = half full * + * 0x0100 = full * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +fixed TechnoClass::Tiberium_Load(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Desired_Load_Dir -- Fetches loading parameters for this object. * + * * + * This routine is called when an object desires to load up on this object. The object * + * desiring to load is specified. The cell that the loading object should move to is * + * determined. The direction that this object should face is also calculated. This routine * + * will be overridden by those objects that can actually load up passengers. * + * * + * INPUT: object -- The object that is desiring to load up. * + * * + * moveto -- Reference to the cell that the loading object should move to before * + * the final load process occurs (this value will be filled in). * + * * + * OUTPUT: Returns with the direction that the transport object should face. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Desired_Load_Dir(ObjectClass * , CELL & moveto) const +{ + assert(IsActive); + + moveto = 0; + return(DIR_N); +} + + +/*********************************************************************************************** + * TechnoClass::Pip_Count -- Fetches the number of pips to display on this object. * + * * + * This routine will return the number of pips to display on this object when the object * + * is selected. The default condition is to return no pips at all. This routine is * + * derived for those objects that can have pips. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to display on this object when selected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Pip_Count(void) const +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Fire_Direction -- Fetches the direction projectile fire will take. * + * * + * This routine will fetch the direction that a fired projectile will take. This is * + * usually the facing of the object's weapon. This routine will be derived for the objects * + * that have their weapon barrel facing a different direction than the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the direction a fired projectile will take. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +DirType TechnoClass::Fire_Direction(void) const +{ + assert(IsActive); + + return(Turret_Facing()); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Select -- Handles the voice response when selected. * + * * + * This routine is called when a voice response to a select action is desired. This routine * + * should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Select(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Move -- Handles the voice response to a movement request. * + * * + * This routine is called when a voice response to a movement order is desired. This * + * routine should be overridden for any object that actually has a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Move(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Response_Attack -- Handles the voice response when given attack order. * + * * + * This routine is called when a voice response to an attack order is desired. This routine * + * should be overridden for any object that actually have a voice response. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This can generate an audio response. * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Response_Attack(void) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Target_Something_Nearby -- Handles finding and assigning a nearby target. * + * * + * This routine will search for a nearby target and assign it to this object's TarCom. * + * The method to use when scanning for a target is controlled by the parameter passed. * + * * + * INPUT: threat -- The threat control parameter used to control the range searched. The * + * only values recognized are THREAT_RANGE and THREAT_AREA. * + * * + * OUTPUT: Was a suitable target acquired and assigned to the TarCom? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Target_Something_Nearby(ThreatType threat) +{ + assert(IsActive); + threat = threat & (THREAT_RANGE|THREAT_AREA); + + /* + ** Determine that if there is an existing target it is still legal + ** and within range. + */ + if (Target_Legal(TarCom)) { + if ((threat & THREAT_RANGE)) { + int primary = What_Weapon_Should_I_Use(TarCom); + if (!In_Range(TarCom, primary)) { + Assign_Target(TARGET_NONE); + } + } + } + + /* + ** If there is no target, then try to find one and assign it as + ** the target for this unit. + */ + if (!Target_Legal(TarCom)) { + Assign_Target(Greatest_Threat(threat)); + } + + /* + ** Return with answer to question: Does this unit now have a target? + */ + return(Target_Legal(TarCom)); +} + + +/*********************************************************************************************** + * TechnoClass::Exit_Object -- Causes specified object to leave this object. * + * * + * This routine is called when there is an attached object that should detach and leave * + * this object. Typical of this would be the refinery and APC. * + * * + * INPUT: object -- The object that is trying to leave this object. * + * * + * OUTPUT: Was the object successfully launched from this object? Failure might indicate that * + * there is insufficient room to detach the specified object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Exit_Object(TechnoClass *) +{ + assert(IsActive); + + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Is_Ready_To_Random_Animate -- Determines if the object should random animate. * + * * + * This will examine this object to determine if it is time and ready to perform some * + * kind of random animation. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is it time to perform an random animation? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoClass::Is_Ready_To_Random_Animate(void) const +{ + assert(IsActive); + return(IdleTimer == 0); +} + + +/*********************************************************************************************** + * TechnoClass::Assign_Destination -- Assigns movement destination to the object. * + * * + * This routine is called when the object needs to have a new movement destination * + * assigned. This routine must be overridden since at this level, movement is not allowed. * + * * + * INPUT: destination -- The destination to assign to this object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Assign_Destination(TARGET ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Enter_Idle_Mode -- Object enters its default idle condition. * + * * + * This routine is called when the object should intelligently revert to an idle state. * + * Typically this routine is called after some mission has completed. This routine must * + * be overridden by the various object types. It is located at this level merely to provide * + * a virtual function entry point. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/24/1995 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Enter_Idle_Mode(bool ) +{ + assert(IsActive); +} + + +/*********************************************************************************************** + * TechnoClass::Draw_Pips -- Draws the transport pips and other techno graphics. * + * * + * This routine is used to render the small transportation pip (occupant feedback graphic) * + * used for transporter object. It will also display if the techno object is "primary" * + * if necessary. * + * * + * INPUT: x,y -- The pixel coordinate for the center of the first pip. Subsequent pips * + * are drawn rightward. * + * * + * window-- The window that pip clipping is relative to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/08/1995 JLB : Created. * + * 10/06/1995 JLB : Displays the team group number. * + * 09/10/1996 JLB : Medic hack for red pip. * + *=============================================================================================*/ +void TechnoClass::Draw_Pips(int x, int y, WindowNumberType window) const +{ + assert(IsActive); + + /* + ** Transporter type objects have a different graphic representation for the pips. The + ** pip color represents the type of occupant. + */ + bool carrying_passengers = (Techno_Type_Class()->Max_Passengers() > 0) && + ((What_Am_I() != RTTI_AIRCRAFT) || (*(AircraftClass*)this != AIRCRAFT_BADGER) || (Mission != MISSION_HUNT)); + if (carrying_passengers) { + ObjectClass const * object = Attached_Object(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + PipEnum pip = PIP_EMPTY; + + if (object != NULL) { + pip = PIP_FULL; + if (object->What_Am_I() == RTTI_INFANTRY) { + pip = ((InfantryClass *)object)->Class->Pip; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (What_Am_I() == RTTI_VESSEL && *(VesselClass *)this == VESSEL_CARRIER) { + if (object->What_Am_I() == RTTI_AIRCRAFT) { + AircraftClass *heli = (AircraftClass *)object; + if (heli->Ammo != heli->Techno_Type_Class()->MaxAmmo) { + pip = PIP_ENGINEER; + if (!heli->Ammo) { + pip = PIP_COMMANDO; + } + } + } + } +#endif + object = object->Next; + } + CC_Draw_Pip(this, Class_Of().PipShapes, pip, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + } else { + + /* + ** Display number of how many attached objects there are. This is also used + ** to display the fullness rating for a harvester. + */ + int pips = Pip_Count(); + + /* + ** Check if it's a harvester, to show the right type of pips for the + ** various minerals it could have harvested. + */ + if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_HARVESTER) { + UnitClass * harv = (UnitClass *)this; + + int iron = harv->Gems; + int nickel = harv->Gold; + int graypips = pips * fixed(iron, Rule.BailCount); + int greenpips = pips * fixed(nickel, Rule.BailCount); + + while (greenpips + graypips < pips) { + int ironnickelmax = max(iron, nickel); + if (iron > nickel) { + graypips++; + } else { + greenpips++; + } + } + + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + int shape = PIP_EMPTY; + if (index < pips) { + if (greenpips) { + shape = PIP_FULL; + greenpips--; + } else { + shape = PIP_COMMANDO; + graypips--; + } + } + CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + /* + ** Check if it's a Chrono tank, to show the recharge gauge. + */ + else if (What_Am_I() == RTTI_UNIT && *(UnitClass *)this == UNIT_CHRONOTANK) { + for (int index = 0; index < 5; index++) { + int shape = PIP_EMPTY; + if (index < pips) { + switch(index) { + case 0: + case 1: + shape = PIP_COMMANDO; + break; + + case 2: + case 3: + shape = PIP_ENGINEER; + break; + + case 4: + shape = PIP_FULL; + default: + break; + } + } + CC_Draw_Pip(this, Class_Of().PipShapes, shape, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } +#endif + } else { + bool building = false; + int pip = PIP_FULL; // green + if(!IsOwnedByPlayer && What_Am_I() == RTTI_BUILDING) { + if(*(BuildingClass *)this==STRUCT_POWER || *(BuildingClass *)this==STRUCT_ADVANCED_POWER) { + building = true; + if (House->Power_Fraction() < 1) { + pip = PIP_ENGINEER; // gold + if (House->Drain > (House->Power * 2) ) { + pip = PIP_COMMANDO; + } + } + } + } + + // Ally/spied power display is handled separately in the virtual window + if (!building || (window != WINDOW_VIRTUAL)) { + for (int index = 0; index < (building ? 5 : Class_Of().Max_Pips()); index++) { + if (building) { + CC_Draw_Pip(this, Class_Of().PipShapes, pip, x, y-index*3, window, SHAPE_CENTER|SHAPE_WIN_REL); + } else { + CC_Draw_Pip(this, Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + +//BG for (int index = 0; index < Class_Of().Max_Pips(); index++) { +//BG CC_Draw_Shape(Class_Of().PipShapes, (index < pips) ? PIP_FULL : PIP_EMPTY, x+index*3, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +//BG } + } + } + } + + /* + ** Special hack to display a red pip on the medic. + */ + if (What_Am_I() == RTTI_INFANTRY && Combat_Damage() < 0) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_MEDIC, x+8, y, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + + /* + ** Display whether this unit is a leader unit or not. + */ + if (IsLeader && (window != WINDOW_VIRTUAL)) { + PipEnum prishape = PIP_PRIMARY; + + if(What_Am_I() == RTTI_BUILDING) { + if(*((BuildingClass *)this) == STRUCT_KENNEL) { + prishape = PIP_PRI; + } + } + CC_Draw_Shape(Class_Of().PipShapes, prishape, x-2, y-3, window, /*SHAPE_CENTER|*/SHAPE_WIN_REL); + } + + /* + ** Display what group this unit belongs to. This corresponds to the team + ** number assigned with the key. + */ + if (Is_Foot() && ((FootClass *)this)->Group != 0xFF && ((FootClass *)this)->Group < 10) { + int yval = -1; + int group = ((FootClass *)this)->Group; + + if (Class_Of().Max_Pips()) yval -= 4; + if (group == 10) group = 0; + + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS+group, x+2, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + + /* + ** If this unit is part of a formation, draw an 'F' after the group + ** number. + */ + if ( ((FootClass *)this)->XFormOffset != 0x80000000UL) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_LETTERF, x+8, y+yval, window, SHAPE_CENTER|SHAPE_WIN_REL); + } + } + + /* + ** If this building is being spied on by the player, draw the money or + ** factory-producing item or whatever. + */ + if (What_Am_I() == RTTI_BUILDING) { + int spiedby = Spied_By() & (1<<(PlayerPtr->Class->House)); + + /* + ** Print word "Decoy" above buildings that are spied upon or fake + */ + if (((BuildingClass *)this)->Class->IsFake) { + if (spiedby || IsOwnedByPlayer) { + CC_Draw_Shape(Class_Of().PipShapes, PIP_DECOY, x, y-16, window, SHAPE_WIN_REL); + } + } + /* + ** See if we should print the credits for a spied refinery + */ + if (spiedby) { + // If it's a refinery/silo, print the enemy's money + if (((BuildingClass *)this)->Class->Capacity) { + long money = House->Available_Money(); + + /* + ** Determine how many digits will be printed. + */ + int digits; + int factor = 10; + for (digits = 1; digits < 9; digits++) { + if (money < factor) break; + factor *= 10; + } + + int startx = x + 6 * digits - 3;// + 6 * 8; + while (money) { + int xdigit = money % 10; + money /= 10; + CC_Draw_Shape(Class_Of().PipShapes, PIP_NUMBERS + xdigit, startx, y-6, window, SHAPE_CENTER|SHAPE_WIN_REL); + startx -= 6; + } + } + } + } +} + + +/*********************************************************************************************** + * TechnoClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + * 08/13/1995 JLB : Recognizes the "IsLeader" method of building preference. * + *=============================================================================================*/ +BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + assert(IsActive); + + BuildingClass * best = 0; + + /* + ** First check to see if there are ANY buildings of the specified + ** type in this house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->Get_Quantity(b) != 0) { + int bestval = -1; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (building != NULL && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + !building->IsInLimbo && + *building == b && + (What_Am_I() == RTTI_AIRCRAFT || Map[building->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) && + ((TechnoClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + if (bestval == -1 || Distance(building) < bestval || building->IsLeader) { + best = building; + bestval = Distance(building); + } + } + } + } + return(best); +} + + +/*********************************************************************************************** + * TechnoClass::Find_Exit_Cell -- Finds an appropriate exit cell for this object. * + * * + * This routine is called when an object would like to exit from this (presumed) transport. * + * A suitable cell should be returned by this routine. The specified object will probably * + * be unloaded at that cell. * + * * + * INPUT: techno -- Pointer to the object that would like to unload. This is used to * + * determine suitability for placement. * + * * + * OUTPUT: Returns with the cell that is recommended for object exit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1995 JLB : Created. * + *=============================================================================================*/ +CELL TechnoClass::Find_Exit_Cell(TechnoClass const * ) const +{ + assert(IsActive); + + return(Coord_Cell(Docking_Coord())); +} + + +/*********************************************************************************************** + * TechnoClass::Refund_Amount -- Returns with the money to refund if this object is sold. * + * * + * This routine is used by the selling back mechanism in order to credit the owning house * + * with some refund credits. The value returned is the credits to refund to the owner. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credits to refund to the owner. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Refund_Amount(void) const +{ + assert(IsActive); + + int cost = Techno_Type_Class()->Raw_Cost() * House->CostBias; + +#ifdef TOFIX + /* + ** If the object is carrying Tiberium directly (i.e., the harvester), then + ** account for the credits of the load. + */ +// cost += Fixed_To_Cardinal(UnitTypeClass::FULL_LOAD_CREDITS, Tiberium_Load())/2; +#endif + + if (House->IsHuman) { + cost = cost * Rule.RefundPercent; +// cost /= 2; + } + return(cost); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Air -- Determines the anti-aircraft strength of the object. * + * * + * This routine will calculate and return the anti-aircraft strength of this object. * + * Typical users of this strength value is the base defense expert system AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-aircraft defense value of this object. The value returned * + * is an abstract number to be used for relative comparisons only. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Air(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + + if (bullet->IsAntiAircraft) { + int value = ((weapon->Attack * warhead->Modifier[ARMOR_ALUMINUM]) * weapon->Range) / weapon->ROF; + + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + return(value/50); + } + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Armor -- Determines the anti-armor strength of the object. * + * * + * This routine is used to examine and calculate the anti-armor strength of this object. * + * Typical user user of this would be the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the relative anti-armor combat value for this object. The value * + * is abstract and is only to be used in relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Armor(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_STEEL]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Anti_Infantry -- Calculates the anti-infantry strength of this object. * + * * + * This routine is used to determine the anti-infantry strength of this object. The * + * typical user of this routine is the expert system base defense AI. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the anti-infantry strength of this object. The value returned is * + * abstract and should only be used for relative comparisons. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoClass::Anti_Infantry(void) const +{ + assert(IsActive); + + if (Is_Weapon_Equipped()) { + if (!Techno_Type_Class()->PrimaryWeapon->Bullet->IsAntiGround) return(0); + + WeaponTypeClass const * weapon = Techno_Type_Class()->PrimaryWeapon; + BulletTypeClass const * bullet = weapon->Bullet; + WarheadTypeClass const * warhead = weapon->WarheadPtr; + int mrange = min(weapon->Range, 0x0400); + + int value = ((weapon->Attack * warhead->Modifier[ARMOR_NONE]) * mrange * warhead->SpreadFactor) / weapon->ROF; + if (Techno_Type_Class()->Is_Two_Shooter()) { + value *= 2; + } + if (bullet->IsInaccurate) { + value /= 2; + } + return(value/50); + } + return(0); +} + + +/*********************************************************************************************** + * TechnoClass::Look -- Performs a look around (map reveal) action. * + * * + * This routine will reveal the map around this object. * + * * + * INPUT: incremental -- This parameter can enable a more efficient map reveal logic. * + * If it is absolutely known that the object has only moved one * + * cell from its previous location that it performed a Look() at, * + * then set this parameter to TRUE. It will only perform the look * + * check on the perimeter cells. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine is slow, try to call it only when necessary. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void TechnoClass::Look(bool incremental) +{ + assert(IsActive); + assert(!IsInLimbo); + + int sight_range = Techno_Type_Class()->SightRange; + + if (sight_range) { + + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + + +#if (0) // Leaving this here for posterity, in case we need it for revealing allies. ST - 10/17/2019 10:51AM + /* + ** Changed this function to reveal for the appropriate players in GlyphX multiplayer. ST - 8/7/2019 11:34AM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + + Map.Sight_From(Coord_Cell(Coord), sight_range, House, incremental); + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HousesType house_type = Session.Players[i]->Player.ID; + HouseClass *house = HouseClass::As_Pointer(house_type); + + if (Is_Owned_By_Player(house) || Is_Discovered_By_Player(house)) { + Map.Sight_From(Coord_Cell(Center_Coord()), sight_range, house, incremental); + } + } + } +#endif + } +} + + +//********************************************************************************************** +// MODULE SEPARATION -- TechnoTypeClass member functions follow. +//********************************************************************************************** + + +/*********************************************************************************************** + * TechnoTypeClass::TechnoTypeClass -- Constructor for techno type objects. * + * * + * This is the normal constructor for techno type objects. It is called in the process of * + * constructing all the object type (constant) data for the various techno type objects. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1995 JLB : Created. * + * 05/11/1996 JLB : Moderated risk calc so range doesn't dominate. * + *=============================================================================================*/ +TechnoTypeClass::TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed, + int horizontaloffset) : + ObjectTypeClass( rtti, + id, + true, + is_stealthy, + is_selectable, + is_legal_target, + is_insignificant, + is_immune, + is_footprint, + name, + ininame), + Remap(remap), + IsDoubleOwned(false), + IsInvisible(false), + IsLeader(false), + IsScanner(false), + IsNominal(is_nominal), + IsTheater(is_theater), + IsTurretEquipped(is_turret_equipped), + IsCrew(false), + IsRepairable(true), + IsRemappable(is_remappable), + IsCloakable(false), + IsSelfHealing(false), + IsExploding(false), + MZone(MZONE_NORMAL), + ThreatRange(0), + MaxPassengers(0), + SightRange(0), + Cost(0), + Level(-1), + Prerequisite(STRUCTF_NONE), + Risk(0),Reward(0), + MaxSpeed(MPH_IMMOBILE), + Speed(speed), + MaxAmmo(-1), + Ownable(0), + CameoData(NULL), + Rotation(rotation), + ROT(0), + PrimaryWeapon(NULL), + SecondaryWeapon(NULL), + HorizontalOffset(horizontaloffset), + VerticalOffset(verticaloffset), + PrimaryOffset(primaryoffset), + PrimaryLateral(primarylateral), + SecondaryOffset(secondaryoffset), + SecondaryLateral(secondarylateral), + Points(0) +{ +} + + +/*********************************************************************************************** + * TechnoTypeClass::Raw_Cost -- Fetches the raw (base) cost of the object. * + * * + * This routine is used to find the underlying cost for this object. The underlying cost * + * does not include any free items that normally come with the object when purchased * + * directly. Example: The raw cost of a refinery is the normal cost minus the cost of a * + * harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit cost of the base object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Raw_Cost(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Ownable -- Fetches the ownable bits for this object type. * + * * + * This routine will return the ownable bits for this object type. The ownable bits are * + * a bitflag composite of the houses that can own (build) this object type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the ownable bits for this object type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Get_Ownable(void) const +{ + if (IsDoubleOwned && Session.Type != GAME_NORMAL) { + return(Ownable | HOUSEF_SOVIET | HOUSEF_ALLIES); + } + return(Ownable); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Time_To_Build -- Fetches the time to build this object. * + * * + * This routine will return the time it takes to construct this object. Usually the time * + * to produce is directly related to cost. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the time to produce this object type. The time is expressed in the * + * form of game ticks. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Time_To_Build(void) const +{ + return(Cost * Rule.BuildSpeedBias * fixed(TICKS_PER_MINUTE, 1000)); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Cost_Of -- Fetches the cost of this object type. * + * * + * This routine will return the cost to produce an object of this type. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to produce one object of this type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Cost_Of(void) const +{ + return(Cost); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Get_Cameo_Data -- Fetches the cameo image for this object type. * + * * + * This routine will fetch the cameo (sidebar small image) shape of this object type. * + * If there is no cameo data available (typical for non-produceable units), then NULL will * + * be returned. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the cameo data for this object type if present. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +void const * TechnoTypeClass::Get_Cameo_Data(void) const +{ + return(CameoData); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Cost -- Fetches the cost to repair one step. * + * * + * This routine will return the cost to repair one step. At the TechnoTypeClass level, * + * this merely serves as a placeholder function. The derived classes will provide a * + * functional version of this routine. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the cost to repair one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Cost(void) const +{ + // MBL 04.28.2020 "Fix" for RA Barracks structures coming back with a repair cost of 0, and repairing for free + // + // if (Is_Foot()) { + // return((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); + // } + // return((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); + // + int repair_cost = 0; + if (Is_Foot()) { + repair_cost = ((Raw_Cost()/(MaxStrength/Rule.URepairStep)) * Rule.URepairPercent); + } + else + { + repair_cost = ((Raw_Cost()/(MaxStrength/Rule.RepairStep)) * Rule.RepairPercent); + } + repair_cost = MAX(repair_cost, 1); + return repair_cost; +} + + +/*********************************************************************************************** + * TechnoTypeClass::Repair_Step -- Fetches the health to repair one step. * + * * + * This routine merely serves as placeholder virtual function. The various type classes * + * will override this routine to return the number of health points to repair in one * + * "step". * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of health points to repair in one step. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +int TechnoTypeClass::Repair_Step(void) const +{ + if (Is_Foot()) { + return(Rule.URepairStep); + } + return(Rule.RepairStep); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Is_Two_Shooter -- Determines if this object is a double shooter. * + * * + * Some objects fire two shots in quick succession. If this is true for this object, then * + * a 'true' value will be returned from this routine. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is this object a two shooter? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Is_Two_Shooter(void) const +{ + if (PrimaryWeapon != NULL && (PrimaryWeapon == SecondaryWeapon || PrimaryWeapon->Burst > 1)) { + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * _Scale_To_256 -- Scales a 1..100 number into a 1..255 number. * + * * + * This is a helper routine that will take a decimal percentage number and convert it * + * into a game based fixed point number. * + * * + * INPUT: val -- Decimal percent number to convert. * + * * + * OUTPUT: Returns with the decimal percent number converted to a game fixed point number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/17/1996 JLB : Created. * + *=============================================================================================*/ +static inline int _Scale_To_256(int val) +{ + val = min(val, 100); + val = max(val, 0); + val = ((val * 256) / 100); + val = min(val, 255); + return(val); +} + + +/*********************************************************************************************** + * TechnoTypeClass::Read_INI -- Reads the techno type data from the INI database. * + * * + * Use this routine to fill in the data for this techno type class object from the * + * database specified. Typical use of this is for the rules parsing. * + * * + * INPUT: ini -- Reference to the INI database that the information will be lifted from. * + * * + * OUTPUT: bool; Was the database used to extract information? A failure (false) response * + * would mean that the database didn't contain a section that applies to this * + * techno class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool TechnoTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { +#ifdef FIXIT_NAME_OVERRIDE + char buffer[256]; + int id = ((RTTI+1) * 100) + ID; + + ini.Get_String(Name(), "Name", "", buffer, sizeof(buffer)); + if (strlen(buffer) > 0) { + +#if defined (GERMAN)||(FRENCH) + + for(int xx=0; NewName[xx] != NULL; xx++){ + if(!strcmp(NewName[xx], buffer)){ + memcpy(buffer, "", sizeof(buffer)); + memcpy(buffer, NewName[xx+1], sizeof(buffer)); + break; + } + } +#endif + + /* + ** Insert the new name text into the buffer list. + */ + for (int index = 0; index < ARRAY_SIZE(NameOverride); index++) { + if (NameIDOverride[index] == 0) { + NameOverride[index] = strdup(buffer); + NameIDOverride[index] = id; +// FullName = -(index+1); + break; + } + } + } +#endif + + IsDoubleOwned = ini.Get_Bool(Name(), "DoubleOwned", IsDoubleOwned); + ThreatRange = ini.Get_Lepton(Name(), "GuardRange", ThreatRange); + IsExploding = ini.Get_Bool(Name(), "Explodes", IsExploding); + PrimaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Primary", PrimaryWeapon != NULL ? (WeaponType)(PrimaryWeapon->ID) : WEAPON_NONE)); + SecondaryWeapon = WeaponTypeClass::As_Pointer(ini.Get_WeaponType(Name(), "Secondary", SecondaryWeapon != NULL ? (WeaponType)(SecondaryWeapon->ID) : WEAPON_NONE)); + IsCloakable = ini.Get_Bool(Name(), "Cloakable", IsCloakable); + IsCrushable = ini.Get_Bool(Name(), "Crushable", IsCrushable); + IsScanner = ini.Get_Bool(Name(), "Sensors", IsScanner); + Armor = ini.Get_ArmorType(Name(), "Armor", Armor); + Prerequisite = ini.Get_Buildings(Name(), "Prerequisite", Prerequisite); + MaxStrength = ini.Get_Int(Name(), "Strength", MaxStrength); + SightRange = ini.Get_Int(Name(), "Sight", SightRange); + Level = ini.Get_Int(Name(), "TechLevel", Level); + MaxSpeed = MPHType(_Scale_To_256(ini.Get_Int(Name(), "Speed", fixed(MaxSpeed, 256) * 100))); + Cost = 50; // ini.Get_Int(Name(), "Cost", Cost); + MaxAmmo = ini.Get_Int(Name(), "Ammo", MaxAmmo); + Risk = Reward = Points = ini.Get_Int(Name(), "Points", Points); + Ownable = ini.Get_Owners(Name(), "Owner", Ownable); + IsCrew = ini.Get_Bool(Name(), "Crewed", IsCrew); + IsRepairable = ini.Get_Bool(Name(), "Repairable", IsRepairable); + IsInvisible = ini.Get_Bool(Name(), "Invisible", IsInvisible); + IsSelfHealing = ini.Get_Bool(Name(), "SelfHealing", IsSelfHealing); + ROT = ini.Get_Int(Name(), "ROT", ROT); + MaxPassengers = ini.Get_Int(Name(), "Passengers", MaxPassengers); +//Mono_Printf("before image=: %s\n",GraphicName); + ini.Get_String(Name(), "Image", GraphicName, GraphicName, sizeof(GraphicName)); +//Mono_Printf("after image=: %s\n",GraphicName);if(Random_Pick(0,4)) Keyboard->Get(); + + IsLeader = false; + if (PrimaryWeapon != NULL && PrimaryWeapon->Attack > 0) { + IsLeader = true; + } + + /* + ** Check to see what zone this object should recognize. + */ + if (PrimaryWeapon != NULL && PrimaryWeapon->WarheadPtr != NULL && PrimaryWeapon->WarheadPtr->IsWallDestroyer) { + MZone = MZONE_DESTROYER; + } + if (Speed == SPEED_FLOAT) { + MZone = MZONE_WATER; + } + + return(true); + } + return(false); +} + + +int TechnoTypeClass::Legal_Placement(CELL pos) const +{ + if (pos == -1) return(0); + + /* + ** Normal buildings must check to see that every foundation square is free of + ** obstacles. If this check passes for all foundation squares, only then does the + ** routine return that it is legal to place. + */ + short const * offset = Occupy_List(true); + bool build = (What_Am_I() == RTTI_BUILDINGTYPE); + + while (offset != NULL && *offset != REFRESH_EOL) { + CELL cell = pos + *offset++; + if (!Map.In_Radar(cell)) return(false); + if (build) { + if (!Map[cell].Is_Clear_To_Build(Speed)) { + return(0); + } + } else { + if (!Map[cell].Is_Clear_To_Move(Speed, false, false)) { + return(0); + } + } + } + return(1); +} + + + + + + + +/* +** Additions to TechnoClass to track discovery per-player. ST - 3/6/2019 11:18AM +** +** +** +** +** +*/ + + +/*********************************************************************************************** + * TechnoClass::Is_Discovered_By_Player -- Has this object been disovered by the given player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: True if discovered by that player * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:20AM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Discovered_By_Player(HouseClass *player) const +{ + if (player && player->Class) { + int shift = (int) player->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } else { + int shift = (int) PlayerPtr->Class->House; + return (IsDiscoveredByPlayerMask & (1 << shift)) ? true : false; + } + return false; +} + + +/*********************************************************************************************** + * TechnoClass::Set_Discovered_By_Player -- Mark that this object been disovered by the player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/6/2019 11:23AM - ST * + *=============================================================================================*/ +void TechnoClass::Set_Discovered_By_Player(HouseClass *player) +{ + int shift = 0; + if (player && player->Class) { + shift = (int) player->Class->House; + } else { + shift = (int)PlayerPtr->Class->House; + } + IsDiscoveredByPlayerMask |= (1 << shift); + + if (Session.Type == GAME_NORMAL && player == PlayerPtr) { + IsDiscoveredByPlayer = true; + } +} + + + +/*********************************************************************************************** + * TechnoClass::Clear_Discovered_By_Players -- Clear player discovery flags * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 4/27/2020 - SKY * + *=============================================================================================*/ +void TechnoClass::Clear_Discovered_By_Players() +{ + IsDiscoveredByPlayerMask = 0; + IsDiscoveredByPlayer = false; +} + + + +/*********************************************************************************************** + * TechnoClass::Is_Owned_By_Player -- Is this object owned by the active human player * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 3/13/2019 5:00PM - ST * + *=============================================================================================*/ +bool TechnoClass::Is_Owned_By_Player(HouseClass *player) const +{ + if (player == NULL) { + return (PlayerPtr == House) ? true : false; + } + return (player == House) ? true : false; +} + + + +/*********************************************************************************************** + * TechnoClass::Spied_By -- Spied by flags for this object * + * * + * INPUT: Player pointer * + * * + * OUTPUT: * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/31/2019 - SKY * + *=============================================================================================*/ +unsigned TechnoClass::Spied_By() const +{ + return SpiedBy; +} \ No newline at end of file diff --git a/code/redalert/techno.h b/code/redalert/techno.h new file mode 100644 index 0000000..ee1e558 --- /dev/null +++ b/code/redalert/techno.h @@ -0,0 +1,421 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TECHNO.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TECHNO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TECHNO_H +#define TECHNO_H + +#include "radio.h" +#include "stage.h" +#include "cargo.h" +#include "flasher.h" +#include "house.h" +#include "target.h" +#include "bullet.h" +#include "door.h" +#include "crew.h" + +/**************************************************************************** +** This is the common data between building and units. +*/ +class TechnoClass : public RadioClass, + public FlasherClass, + public StageClass, + public CargoClass, + public DoorClass +{ + public: + CrewClass Crew; + + /* + ** If this techno object has detected that it has outlived its + ** purpose, then this flag will be true. Such object will either + ** be sold or sacrificed at the first opportunity. + */ + unsigned IsUseless:1; + + /* + ** This flag will be true if the object has been damaged with malice. + ** Damage received due to friendly fire or wear and tear does not count. + ** The computer is not allowed to sell a building unless it has been + ** damaged with malice. + */ + unsigned IsTickedOff:1; + + /* + ** If this object has inherited the ability to cloak, then this bit will + ** be set to true. + */ + unsigned IsCloakable:1; + + /* + ** If this object is designated as special then this flag will be true. For + ** buildings, this means that it is the primary factory. For units, it means + ** that the unit is the team leader. + */ + unsigned IsLeader:1; + + /* + ** Certain units are flagged as "loaners". These units are typically transports that + ** are created solely for the purpose of delivering reinforcements. Such "loaner" + ** units are not owned by the player and thus cannot be directly controlled. These + ** units will leave the game as soon as they have fulfilled their purpose. + */ + unsigned IsALoaner:1; + + /* + ** Once a unit enters the map, then this flag is set. This flag is used to make + ** sure that a unit doesn't leave the map once it enters the map. + */ + unsigned IsLocked:1; + + /* + ** Buildings and units with turrets usually have a recoil animation when they + ** fire. If this flag is true, then the next rendering of the object will be + ** in the "recoil state". The flag will then be cleared pending the next + ** firing event. + */ + unsigned IsInRecoilState:1; + + /* + ** If this unit is "loosely attached" to another unit it is given special + ** processing. A unit is in such a condition when it is in the process of + ** unloading from a transport type object. During the unloading process + ** the transport object must stay still until the unit is free and clear. + ** At that time it radios the transport object and the "tether" is broken - + ** freeing both the unit and the transport object. + */ + unsigned IsTethered:1; + + /* + ** Is this object owned by the player? If not, then it is owned by the computer + ** or remote opponent. This flag facilitates the many logic differences when dealing + ** with player's or computer's units or buildings. + */ + unsigned IsOwnedByPlayer:1; + + /* + ** The more sophisticated game objects must keep track of whether they are discovered + ** or not. This is because the state of discovery can often control how the object + ** behaves. In addition, this fact is used in radar and user I/O processing. + */ + unsigned IsDiscoveredByPlayer:1; + + /* + ** This is used to control the computer recognizing this object. + */ + unsigned IsDiscoveredByComputer:1; + + /* + ** Some game objects can be of the "lemon" variety. This means that they take damage + ** even when everything is ok. This adds a little variety to the game. + */ + unsigned IsALemon:1; + + /* + ** This flag is used to control second shot processing for those units or buildings + ** that fire two shots in quick succession. When this flag is true, it indicates that + ** the second shot is ready to fire. After this shot is fired, regular rearm timing + ** is used rather than the short rearm time. + */ + unsigned IsSecondShot:1; + + /* + ** This is the firepower and armor modifiers for this techno object. Normally, + ** these values are fixed at 0x0100, but they can be modified by certain + ** crate powerups. + */ + fixed ArmorBias; + fixed FirepowerBias; + + /* + ** Idle animations (if any are supported by the object type) are regulated by + ** this timer. When the timer expires an idle animation occurs. Then the + ** timer is reinitialized to some random (bounded) setting. + */ + CDTimerClass IdleTimer; + + /* + ** This timer keeps track of how long the unit is under the influence + ** of the iron curtain. + */ + CDTimerClass IronCurtainCountDown; + + + /* + ** This is a list of bits of which houses are spying on this building, + ** if in fact this is a building. + */ + unsigned SpiedBy; + + /* + ** For units in area guard mode, this is the recorded home position. The guarding + ** unit will try to stay near this location in the course of it's maneuvers. This is + ** also used to record a pending transport for those passengers that are waiting for + ** the transport to become available. It is also used by harvesters so that they know + ** where to head back to after unloading. + */ + TARGET ArchiveTarget; + + /* + ** This is the house that the unit belongs to. + */ + CCPtr House; + + /* + ** This records the current cloak state for this vehicle. + */ + CloakType Cloak; + StageClass CloakingDevice; + CDTimerClass CloakDelay; + + /* (Targeting Computer) + ** This is the target value for the item that this vehicle should ATTACK. If this + ** is a vehicle with a turret, then it may differ from its movement destination. + */ + TARGET TarCom; + TARGET SuspendedTarCom; + + /* + ** This is the visible facing for the unit or building. + */ + FacingClass PrimaryFacing; + + /* + ** This is the arming countdown. It represents the time necessary + ** to reload the weapon. + */ + CDTimerClass Arm; + + /* + ** The number of shot this object can fire before running out of ammo. If this + ** value is zero, then firing is not allowed. If -1, then there is no ammunition + ** limit. + */ + int Ammo; + + /* + ** Used by the tesla to handle electric zap + */ + int ElectricZapDelay; + COORDINATE ElectricZapTarget; + int ElectricZapWhich; + + /* + ** This is the amount of money spent to produce this object. This value really + ** only comes into play for the case of buildings that have special "free" + ** objects available when purchased at the more expensive rate. + */ + int PurchasePrice; + + /* + ** Per-player view of whether a techno object is discovered. One bit for each house type. ST - 3/6/2019 11:15AM + */ + unsigned int IsDiscoveredByPlayerMask; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[16]; + + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + TechnoClass(RTTIType rtti, int id, HousesType house=HOUSE_NONE); +#ifdef FIXIT_MULTI_SAVE + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IdleTimer(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#else + TechnoClass(NoInitClass const & x) : RadioClass(x), FlasherClass(x), StageClass(x), CargoClass(x), DoorClass(x), IronCurtainCountDown(x), House(x), Crew(x), CloakDelay(x), PrimaryFacing(x), Arm(x) {}; +#endif + virtual ~TechnoClass(void) {House=0;}; + + /* + ** Query functions. + */ + bool Is_Allowed_To_Retaliate(TechnoClass const * source) const; + bool Can_Teleport_Here(CELL cell) const; + bool Is_In_Same_Zone(CELL cell) const; + virtual bool Is_Players_Army(void) const; + int Combat_Damage(int which=-1) const; + bool Is_Ready_To_Cloak(void) const; + virtual int How_Many_Survivors(void) const; + virtual DirType Turret_Facing(void) const {return(PrimaryFacing.Current());} + CELL Nearby_Location(TechnoClass const * from=NULL) const; + TechnoTypeClass * Techno_Type_Class(void) const {return((TechnoTypeClass *)&Class_Of());}; + bool Is_Visible_On_Radar(void) const; + int Anti_Air(void) const; + int Anti_Armor(void) const; + int Anti_Infantry(void) const; + int Time_To_Build(void) const; + int What_Weapon_Should_I_Use(TARGET target) const; + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * target) const; + virtual BuildingClass * Find_Docking_Bay(StructType b, bool friendly) const; + virtual CELL Find_Exit_Cell(TechnoClass const * techno) const; + virtual COORDINATE Fire_Coord(int which) const; + virtual DirType Desired_Load_Dir(ObjectClass * , CELL & moveto) const; + virtual DirType Fire_Direction(void) const; + virtual HousesType Owner(void) const; + virtual InfantryType Crew_Type(void) const; + virtual bool Can_Player_Fire(void) const; + virtual bool Can_Player_Move(void) const; + virtual bool Is_Allowed_To_Recloak(void) const; + virtual bool Can_Repair(void) const; + virtual bool Is_Weapon_Equipped(void) const; + virtual fixed Tiberium_Load(void) const; + virtual int Pip_Count(void) const; + virtual int Rearm_Delay(bool second=true, int which=0) const; + virtual int Refund_Amount(void) const; + virtual int Risk(void) const; + virtual int Threat_Range(int control) const; + virtual int Value(void) const; + virtual int Get_Ownable(void) const; + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType house, int count = 7); // 2019/09/20 JAS - Added record of who clicked on the object + virtual bool Select(bool allow_mixed = false); + virtual void Response_Select(void); + virtual void Response_Move(void); + virtual void Response_Attack(void); + virtual void Player_Assign_Mission(MissionType order, TARGET target=TARGET_NONE, TARGET destination=TARGET_NONE); + + /* + ** Combat related. + */ + fixed Area_Modify(CELL cell) const; + virtual int Made_A_Kill(void) {return(Crew.Made_A_Kill());} + void Base_Is_Attacked(TechnoClass const *enemy); + void Kill_Cargo(TechnoClass * source); + virtual void Record_The_Kill(TechnoClass * source); + virtual bool Target_Something_Nearby(ThreatType threat=THREAT_NORMAL); + virtual void Stun(void); + virtual bool In_Range(COORDINATE coord, int which=0) const; + virtual bool In_Range(TARGET target, int which=0, bool reciprocal_check = true) const; + virtual bool In_Range(ObjectClass const * target, int which=0, bool reciprocal_check = true) const; + virtual void Death_Announcement(TechnoClass const * source=0) const = 0; + virtual FireErrorType Can_Fire(TARGET target, int which=0) const; + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual void Assign_Target(TARGET target); + virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); + virtual bool Restore_Mission(void); + virtual BulletClass * Fire_At(TARGET target, int which=0); + virtual int Weapon_Range(int which) const; + virtual bool Captured(HouseClass * newowner); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value, int zone=0) const; + bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value, int zone=-1) const; + int Evaluate_Just_Cell(CELL cell) const; + virtual bool Electric_Zap (COORDINATE target_coord, int which, WindowNumberType window, COORDINATE source_coord=0L, unsigned char * remap=NULL) const; + + /* + ** AI. + */ + virtual void Renovate(void); + virtual void AI(void); + virtual bool Revealed(HouseClass * house); + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + void Cloaking_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void const * Remap_Table(void) const; + VisualType Visual_Character(bool raw = false) const; + void Techno_Draw_Object(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation=DIR_N, int scale=0x0100) const; +// jmarshall + void Techno_Draw_Object_HD(struct Image_t* image, int shapenum, int x, int y, WindowNumberType window, DirType rotation = DIR_N, int scale = 0x0100) const; +// jmarshall end + + // Added. ST - 8/1/2019 5:37PM + void Techno_Draw_Object_Virtual(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, DirType rotation=DIR_N, int scale=0x0100, const char *shape_name = NULL) const; + + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual void Draw_Pips(int x, int y, WindowNumberType window) const; + virtual void Hidden(void); + virtual bool Mark(MarkType mark=MARK_CHANGE); + virtual int Exit_Object(TechnoClass *); + virtual void Do_Uncloak(void); + virtual void Do_Cloak(void); + virtual void Do_Shimmer(void); + + /* + ** Movement and animation. + */ + virtual bool Is_Ready_To_Random_Animate(void) const; + virtual bool Random_Animate(void) {return(false);} + virtual void Assign_Destination(TARGET target); + virtual void Per_Cell_Process(PCPType why); + virtual void Enter_Idle_Mode(bool initial=false); + virtual void Look(bool incremental=false); + + /* + ** Map entry and exit logic. + */ + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + virtual void Detach(TARGET target, bool all); + + /* + ** New functions for per-player discovery for multiplayer. ST - 3/6/2019 11:17AM + */ + bool Is_Discovered_By_Player(HouseClass *player = NULL) const; + void Set_Discovered_By_Player(HouseClass *player = NULL); + void Clear_Discovered_By_Players(); + bool Is_Owned_By_Player(HouseClass *player = NULL) const; + + virtual unsigned Spied_By() const; + + /* + ** Facing translation tables that fix the flaw with 3D studio when + ** it renders 45 degree angles. + */ + static int const BodyShape[32]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/template.cpp b/code/redalert/template.cpp new file mode 100644 index 0000000..83777ae --- /dev/null +++ b/code/redalert/template.cpp @@ -0,0 +1,238 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEMPLATE.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : January 23, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TemplateClass::Init -- Resets the template object system. * + * TemplateClass::Mark -- Lifts or drops a template object. * + * TemplateClass::Select -- Select the template object. * + * TemplateClass::TemplateClass -- Template object constructor. * + * TemplateClass::Unlimbo -- Places a template object into the game/map system. * + * TemplateClass::delete -- Returns a template object to the pool. * + * TemplateClass::new -- Allocates a template object from pool * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "template.h" + + +/*********************************************************************************************** + * TemplateClass::Init -- Resets the template object system. * + * * + * This routine resets the template object system. It is called * + * prior to loading a new scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::Init(void) +{ + Templates.Free_All(); +} + + +/*********************************************************************************************** + * TemplateClass::Mark -- Lifts or drops a template object. * + * * + * This routine handles placing or removing a template object. This * + * entails marking the map as appropriate and redisplaying affected * + * cells. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the template successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + * 12/23/1994 JLB : Examines low level legality before processing. * + *=============================================================================================*/ +bool TemplateClass::Mark(MarkType mark) +{ + assert(Templates.ID(this) == ID); + assert(IsActive); + + static bool noup = false; + void const * iset = Get_Image_Data(); + if (iset && ObjectClass::Mark(mark)) { + + void * map = Get_Icon_Set_Map(iset); + + for (int y = 0; y < Class->Height; y++) { + for (int x = 0; x < Class->Width; x++) { + CELL cell = Coord_Cell(Coord) + y*MAP_CELL_W + x; + if (Map.In_Radar(cell)) { + CellClass * cellptr = &Map[cell]; + int number = y*Class->Width + x; + + /* + ** Determine if this logical icon actually maps to a real icon. If no real + ** icon is associated with this logical position, then don't do any action + ** since none is required. + */ + char * mapptr = (char*)map; + bool real = (mapptr[number] != -1); + + if (real) { + /* + ** Lift the terrain object from the map. + */ + if (mark == MARK_UP && !noup) { + if (cellptr->TType == Class->Type && cellptr->TIcon == number) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } + } + + /* + ** Place the terrain object down. + */ + if (mark == MARK_DOWN) { + if (*this == TEMPLATE_CLEAR1) { + cellptr->TType = TEMPLATE_NONE; + cellptr->TIcon = 0; + } else { + cellptr->TType = Class->Type; + cellptr->TIcon = number; + } + + /* + ** Make sure that no overlays or smudges exist after + ** placing the template down. + */ + cellptr->Smudge = SMUDGE_NONE; + cellptr->SmudgeData = 0; + cellptr->Overlay = OVERLAY_NONE; + cellptr->OverlayData = 0; + } + + cellptr->Redraw_Objects(); + cellptr->Recalc_Attributes(); + } + } + } + } + + /* + ** When marking this template down onto the map, the map template numbers are update + ** but the template is removed from existence. Make sure that the deletion of the + ** template object doesn't also lift the template numbers up from the map. + */ + if (mark == MARK_DOWN) { + noup = true; + delete this; + noup = false; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TemplateClass::new -- Allocates a template object from pool * + * * + * This routine is used to allocate a template object from the * + * template object pool. * + * * + * INPUT: size -- The size of a template object (not used). * + * * + * OUTPUT: Returns with a pointer to an available template object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void * TemplateClass::operator new(size_t ) +{ + void * ptr = Templates.Allocate(); + if (ptr) { + ((TemplateClass *)ptr)->IsActive = true; + } + return(ptr); +} + + +/*********************************************************************************************** + * TemplateClass::delete -- Returns a template object to the pool. * + * * + * This routine will return a template object to the template object * + * pool. A template so returned is available for allocation again. * + * * + * INPUT: ptr -- Pointer to the object to be returned. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +void TemplateClass::operator delete(void * ptr) +{ + if (ptr) { + ((TemplateClass *)ptr)->IsActive = false; + } + Templates.Free((TemplateClass *)ptr); +} + + +/*********************************************************************************************** + * TemplateClass::TemplateClass -- Template object constructor. * + * * + * This is the constructor for a template object. * + * * + * INPUT: type -- The template object this is to become. * + * * + * pos -- The position on the map to place the object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +TemplateClass::TemplateClass(TemplateType type, CELL pos) : + ObjectClass(RTTI_TEMPLATE, Templates.ID(this)), + Class(TemplateTypes.Ptr((int)type)) +{ + if (pos != -1) { + Unlimbo(Cell_Coord(pos)); + } +} diff --git a/code/redalert/template.h b/code/redalert/template.h new file mode 100644 index 0000000..710e6a5 --- /dev/null +++ b/code/redalert/template.h @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEMPLATE.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEMPLATE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 17, 1994 * + * * + * Last Update : May 17, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include "object.h" +#include "type.h" + + +/****************************************************************************** +** This class controls the template object. Template objects function congruously +** to carpet on a floor. They have no depth, but merely control the icon to be rendered +** as the cell's bottom most layer. +*/ +class TemplateClass : public ObjectClass +{ + public: + /* + ** This is a pointer to the template object's class. + */ + CCPtr Class; + + /*------------------------------------------------------------------- + ** Constructors and destructors. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TemplateClass(TemplateType type, CELL pos=-1); + TemplateClass(NoInitClass const & x) : ObjectClass(x), Class(x) {}; + virtual ~TemplateClass(void) {if (GameActive) TemplateClass::Limbo();Class=0;}; + operator TemplateType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + int Icon_Number(CELL cell); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int , int , WindowNumberType ) const {}; + virtual bool Mark(MarkType mark); + + /* + ** File I/O. + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/tenmgr.cpp b/code/redalert/tenmgr.cpp new file mode 100644 index 0000000..b30787e --- /dev/null +++ b/code/redalert/tenmgr.cpp @@ -0,0 +1,1118 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : July 22, 1996 [BRR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TenConnManClass::TenConnManClass -- Class constructor * + * TenConnManClass::~TenConnManClass -- Class destructor * + * TenConnManClass::Init -- Inits TEN * + * TenConnManClass::Service -- Service routine * + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * TenConnManClass::Get_Global_Message -- Gets next global message * + * TenConnManClass::Num_Connections -- Reports # connections * + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * TenConnManClass::Connection_Index -- Gets a connection's index * + * TenConnManClass::Create_Connection -- Creates a new connection * + * TenConnManClass::Delete_Connection -- Deletes a connection * + * TenConnManClass::Connection_Name -- Reports a connection's name * + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * TenConnManClass::Flush_All -- Flushes all packets * + * TenConnManClass::Reset_Response_Time -- Does nothing * + * TenConnManClass::Response_Time -- Reports response time * + * TenConnManClass::Set_Timing -- Does nothing * + * TenConnManClass::Configure_Debug -- Does nothing * + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * terminateApp -- Callback: app terminates on error * + * debugMessage -- outputs debug message * + * doAlert -- Outputs debug message * + * doPregameHook -- Callback: game is starting * + * doIncomingPacket -- Callback: packet has arrived * + * doPlayerJoins -- Callback: player joins * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef WIN32 +#define WINDOWS +#endif + +#if(TEN) +#include "ten.h" + +//************************************************************************** +// Constants +// +const unsigned char kGlobalChannelFlag = 0x80; +const int kMaxPlayers = 8; + +//************************************************************************** +// Functions +// +static void terminateApp(void); +static void debugMessage(int msgLevel, char *msg); +static void doAlert(int, int, char *); +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *); +void doIncomingPacket(int addr, void *buf, size_t size); +void doPlayerEntered(int pid, int isYou, char *, char *, char *, long , char *); + + +//************************************************************************** +// Globals +// +static int IgnoreIncoming = 0; + +/*************************************************************************** + * TenConnManClass::TenConnManClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::TenConnManClass(void) +{ + int i; + + IsHost = 0; + + GlobalQueue = new CommBufferClass(1, 50, sizeof(GlobalPacketType), 4); + PrivateQueue = new CommBufferClass(1, 50, Session.TenSize, 4); + + NumConnections = 0; + for (i = 0; i < MAX_PLAYERS; i++) { + Connections[i] = 0; + ID[i] = 0; + Names[i][0] = 0; + } + +} // end of TenConnManClass + + +/*************************************************************************** + * TenConnManClass::~TenConnManClass -- Class destructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +TenConnManClass::~TenConnManClass() +{ + tenArExitArena(); + + delete GlobalQueue; + delete PrivateQueue; + +} // end of ~TenConnManClass + + +/*************************************************************************** + * TenConnManClass::Init -- Inits TEN * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Init(void) +{ + // + // set the debugging functions + // + setExitRoutine(terminateApp); + setDebugMsgRoutine(debugMessage); + + // + // callback function addresses + // + tenArSetAlertMessageRoutine(doAlert); + + tenArSetPregameHookRoutine(doPregameHook); + + tenArSetIncomingPacketRoutine(doIncomingPacket); + + tenArSetPlayerEnteredRoutine(doPlayerEntered); + + verifyNoErr(tenArInitArena("redalt")); + + return (1); + +} // end of Init + + +/*************************************************************************** + * TenConnManClass::Service -- Service routine * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 1. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Service(void) +{ + tenArIdleArena(); + + return (1); + +} // Service + + +/*************************************************************************** + * TenConnManClass::Send_Private_Message -- Sends a "private" message * + * * + * If Connection ID is -1, the packet is multicast; otherwise, it's sent * + * to only the specified connection. * + * * + * Private & Global messages are sent via the same mechanism. The only * + * way to tell the difference between them is that the Global Channel * + * packets have the 'kGlobalChannelFlag' bit set in the 1st byte (the * + * "Type" field for EventClass's, and the NetCommand field for Global * + * packets). * + * * + * INPUT: * + * buf packet to send * + * buflen size of packet * + * reliable 1 = must be delivered reliably * + * conn_id connection ID to send to, -1 = all * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Private_Message(void *buf, int buflen, + int reliable, int conn_id) +{ + int doBroadcast = conn_id == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = Connection_Address(conn_id); + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + return (1); + +} // end of Send_Private_Message + + +/*************************************************************************** + * TenConnManClass::Get_Private_Message -- Gets the next private message * + * * + * Retrieves the next-available "private" message, if there is one. * + * * + * INPUT: * + * buf packet retrieved * + * buflen length of packet * + * conn_id ptr to store sender's connection ID * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Private_Message(void *buf, int *buflen, + int *conn_id) +{ + int addr; + int addrlen; + int i; + + if (PrivateQueue->Num_Receive() > 0) { + PrivateQueue->UnQueue_Receive(buf, buflen, 0, &addr, &addrlen); + (void)verify(addrlen == 4); + (*conn_id) = CONNECTION_NONE; + for (i = 0; i < NumConnections; i++) { + if (addr == Connections[i]) { + (*conn_id) = ID[i]; + return (1); + } + } + } + + return (0); + +} // end of Get_Private_Message + + +/*************************************************************************** + * TenConnManClass::Send_Global_Message -- Sends a "global" message * + * * + * INPUT: * + * buf packet to send * + * buflen length of packet * + * reliable 1 = send reliably * + * address address to send to; -1 = broadcast it (for TEN, this * + * means send to all connected players). * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Send_Global_Message(void *buf, int buflen, + int reliable, int address) +{ + int doBroadcast = address == -1; + unsigned char *ucbuf = (unsigned char *)buf; + + // + // Ensure the global channel flag isn't set on this outgoing packet + // + (void)verify(!(ucbuf[0] & kGlobalChannelFlag)); + + // + // Set the global channel flag for this packet + // + ucbuf[0] |= kGlobalChannelFlag; + + if (doBroadcast) + { + if (reliable) + { + verifyNoErr(tenArSendToOtherPlayers(buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToOtherPlayers(buf, buflen)); + } + } + else + { + int pid = address; + + (void)verify(pid >= 0 && pid < kMaxPlayers); + if (reliable) + { + verifyNoErr(tenArSendToPlayer(pid, buf, buflen)); + } + else + { + verifyNoErr(tenArUnreliableSendToPlayer(pid, buf, buflen)); + } + } + + // + // The caller may re-use this buffer, so clear the global channel flag. + // + ucbuf[0] &= ~kGlobalChannelFlag; + + return (1); + +} // end of Send_Global_Message + + +/*************************************************************************** + * TenConnManClass::Get_Global_Message -- Gets next global message * + * * + * INPUT: * + * buf buffer to store packet in * + * buflen ptr filled in with packet length * + * address ptr filled in with address (Player ID) of sender * + * * + * OUTPUT: * + * 1 = OK, 0 = error * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Get_Global_Message(void *buf, int *buflen, + int *address) +{ + int addrlen; + + if (GlobalQueue->Num_Receive() > 0) { + GlobalQueue->UnQueue_Receive(buf, buflen, 0, address, &addrlen); + (void)verify(addrlen == 4); + return (1); + } + + return (0); + +} // end of Get_Global_Message + + +/*************************************************************************** + * TenConnManClass::Num_Connections -- Reports # connections * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # connections * + * * + * WARNINGS: * + * * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Num_Connections(void) +{ + return (NumConnections); + +} // end of Num_Connections + + +/*************************************************************************** + * TenConnManClass::Connection_ID -- Reports a connection's ID * + * * + * INPUT: * + * index index of connection to report * + * * + * OUTPUT: * + * connection ID for this connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_ID(int index) +{ + if (index >= 0 && index < NumConnections) { + return(ID[index]); + } else { + return(CONNECTION_NONE); + } + +} // end of Connection_ID + + +/*************************************************************************** + * TenConnManClass::Connection_Index -- Gets a connection's index * + * * + * INPUT: * + * id Connection ID to find index for * + * * + * OUTPUT: * + * index for that connection * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Index(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i] == id) { + return (i); + } + } + + return(CONNECTION_NONE); + +} // end of Connection_Index + + +/*************************************************************************** + * TenConnManClass::Create_Connection -- Creates a new connection * + * * + * INPUT: * + * id ID of connection * + * name name of connection * + * address TEN address (player ID) to give this connection * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Create_Connection(int id, char *name, int address) +{ + Connections[NumConnections] = address; + ID[NumConnections] = id; + strcpy(Names[NumConnections], name); + NumConnections++; + + return (1); + +} // end of Create_Connection + + +/*************************************************************************** + * TenConnManClass::Delete_Connection -- Deletes a connection * + * * + * INPUT: * + * id ID for connection to delete * + * * + * OUTPUT: * + * 1 = OK, 0 = error + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Delete_Connection(int id) +{ + int i; + int idx = Connection_Index(id); + if (idx == CONNECTION_NONE) { + return 0; + } + + for (i = idx; i < NumConnections - 1; i++) { + Connections[i] = Connections[i+1]; + ID[i] = ID[i + 1]; + strcpy (Names[i], Names[i + 1]); + } + + NumConnections--; + + return (1); + +} // end of Delete_Connection + + +/*************************************************************************** + * TenConnManClass::Connection_Name -- Reports a connection's name * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +char * TenConnManClass::Connection_Name(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Names[i]); + } + } + + return(NULL); + +} // end of Connection_Name + + +/*************************************************************************** + * TenConnManClass::Connection_Address -- Gets a connection's "address" * + * * + * INPUT: * + * id ID of connection to report * + * * + * OUTPUT: * + * connection "address" (TEN player ID) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Connection_Address(int id) +{ + int i; + + for (i = 0; i < NumConnections; i++) { + if (ID[i]==id) { + return(Connections[i]); + } + } + + return(NULL); + +} // end of Connection_Address + + +/*************************************************************************** + * TenConnManClass::Global_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Send(void) +{ + return(0); + +} // end of Global_Num_Send + +/*************************************************************************** + * TenConnManClass::Global_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Global_Num_Receive(void) +{ + return (GlobalQueue->Num_Receive()); + +} // end of Global_Num_Receive + +/*************************************************************************** + * TenConnManClass::Private_Num_Send -- Reports # outgoing packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * 0. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Send(int /*id*/) +{ + return(0); + +} // end of Private_Num_Send + +/*************************************************************************** + * TenConnManClass::Private_Num_Receive -- Reports # incoming packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * # packets waiting to be read * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +int TenConnManClass::Private_Num_Receive(int /*id*/) +{ + return (PrivateQueue->Num_Receive()); + +} // end of Private_Num_Receive + + +/*************************************************************************** + * TenConnManClass::Flush_All -- Flushes all packets * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Flush_All(void) +{ + int i; + int maxqueuesize; + int rc; + + // + // Set the max # of packets that Ten will send me during any given + // call to tenArIdleArena() to slightly smaller than my max queue + // size. + // + maxqueuesize = 45; + rc = tenArSetOption(kTenArOptReadQueueSize, &maxqueuesize, + sizeof(maxqueuesize)); + + verifyNoErr(rc); + + + // + // Set the flag to tell the doIncomingPacket routine to ignore packets. + // (doIncomingPacket() is called by tenArIdleArena().) + // + IgnoreIncoming = 1; + + while (i++ < 1000) { + tenArIdleArena(); + if (GlobalQueue->Num_Receive() == 0 && + PrivateQueue->Num_Receive() == 0) { + break; + } + GlobalQueue->Init(); + PrivateQueue->Init(); + } + + IgnoreIncoming = 0; + +} // end of Flush_All + + +/*************************************************************************** + * TenConnManClass::Reset_Response_Time -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Reset_Response_Time(void) +{ + // + // (This function intentionally left blank.) + // + +} // end of Reset_Response_Time + + +/*************************************************************************** + * TenConnManClass::Response_Time -- Reports response time * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * worst-case connection response time (round-trip) * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +unsigned long TenConnManClass::Response_Time(void) +{ + return((Session.NetResponseTime * 60) / 1000); // 300 milliseconds one way + +} // end of Response_Time + + +/*************************************************************************** + * TenConnManClass::Set_Timing -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Set_Timing(unsigned long /*retrydelta*/, + unsigned long /*maxretries*/, unsigned long /*timeout*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Set_Timing + + +/*************************************************************************** + * TenConnManClass::Configure_Debug -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Configure_Debug(int /*index*/, int /*type_offset*/, + int /*type_size*/, char **/*names*/, int /*namestart*/, int /*namecount*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Configure_Debug + +/*************************************************************************** + * TenConnManClass::Mono_Debug_Print -- Does nothing * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void TenConnManClass::Mono_Debug_Print(int /*index*/, int /*refresh*/) +{ + // + // (This function intentionally left blank.) + // + +} // end of Mono_Debug_Print + +/*************************************************************************** + * terminateApp -- Callback: app terminates on error * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void terminateApp(void) +{ + Prog_End(); + dprintf("Exiting due to a fatal error.\n"); + exit(0); + +} // end of terminateApp + + +/*************************************************************************** + * debugMessage -- outputs debug message * + * * + * INPUT: * + * msgLevel not used * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void debugMessage(int /*msgLevel*/, char *msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tendebug.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of debugMessage + + +/*************************************************************************** + * doAlert -- Outputs debug message * + * * + * INPUT: * + * msg message to print * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doAlert(int, int, char * msg) +{ + static int recurse = 0; + + if (recurse) { + return; + } + + recurse = 1; + + if (MonoClass::Is_Enabled()) { + Mono_Printf("%s\n",msg); + } else { + //printf("%s\n",msg); + FILE *fp; + fp = fopen("tenalert.log","at"); + if (fp) { + fprintf(fp,"%s\n",msg); + fclose(fp); + } + } + + if (GameActive) { + WWMessageBox().Process(msg); + } + + recurse = 0; + +} // end of doAlert + + +/*************************************************************************** + * doPregameHook -- Callback: game is starting * + * * + * INPUT: * + * joinType if "create", we're the game host * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +static void doPregameHook(char * joinType, char *, char *, + char *, char *, char *) +{ + char typeToken[16]; + + sscanf(joinType, "%s", typeToken); + if (!strcmp(typeToken, "create")) { + Ten->IsHost = 1; + } + + if (Ten->IsHost) { + verifyNoErr(tenArReturnGameOptions("")); + } + + verifyNoErr(tenArReturnPlayerOptions("")); + +} // end of doPregameHook + + +/*************************************************************************** + * doIncomingPacket -- Callback: packet has arrived * + * * + * INPUT: * + * addr player ID of sender * + * buf buffer containing incoming packet * + * size size of incoming packet * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doIncomingPacket(int addr, void *buf, size_t size) +{ + int rc; + unsigned char *byte; + + // + // Check to see if this packet belongs to the Global Channel or not + // + byte = (unsigned char *)buf; + + // + // If the global channel flag is set in this packet, queue it onto the + // Global Channel queue. Ignore any errors if "IgnoreIncoming" is set, + // which means we're in the process of flushing the queues. + // + if (byte[0] & kGlobalChannelFlag) { + byte[0] &= (~kGlobalChannelFlag); + rc = Ten->GlobalQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } else { + rc = Ten->PrivateQueue->Queue_Receive(buf, size, &addr, 4); + if (!IgnoreIncoming) { + (void)verify(rc==1); + } + } + +} // end of doIncomingPacket + + +/*************************************************************************** + * doPlayerJoins -- Callback: player joins * + * * + * INPUT: * + * pid player ID of the player joining * + * isYou true = this is you * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 07/22/1996 BRR : Created. * + *=========================================================================*/ +void doPlayerEntered(int pid, int isYou, char * /*options*/, + char */*termOptions*/, char */*address*/, long /*uniqueId*/, + char */*joinType*/) +{ + if (isYou) { + Session.TenPlayerID = pid; + } + +} // end of doPlayerJoins + + +#endif //TEN +/************************** end of tenmgr.cpp ******************************/ diff --git a/code/redalert/tenmgr.h b/code/redalert/tenmgr.h new file mode 100644 index 0000000..f5a9c84 --- /dev/null +++ b/code/redalert/tenmgr.h @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TENMGR.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 06/26/96 * + * * + * Last Update : June 26, 1996 [BRR] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +class TenConnManClass : public ConnManClass +{ + public: + // + // Constructor / Destructor + // + TenConnManClass(void); + virtual ~TenConnManClass(); + + // + // Initialization + // + int Init(void); + + // + // Service routine + // + virtual int Service(void); + + // + // Sending & receiving data + // + virtual int Send_Private_Message (void *buf, int buflen, + int ack_req = 1, int conn_id = CONNECTION_NONE); + virtual int Get_Private_Message (void *buf, int *buflen, + int *conn_id); + + int Send_Global_Message(void *buf, int buflen, + int ack_req = 0, int address = 0); + int Get_Global_Message(void *buf, int *buflen, + int *address = 0); + + // + // Connection management + // + virtual int Num_Connections(void); + virtual int Connection_ID(int index); + virtual int Connection_Index(int id); + + int Create_Connection(int id, char *name, int address); + int Delete_Connection(int id); + char *Connection_Name(int id); + int Connection_Address(int id); + + // + // Queue utility routines + // + virtual int Global_Num_Send(void); + virtual int Global_Num_Receive(void); + virtual int Private_Num_Send(int id = CONNECTION_NONE); + virtual int Private_Num_Receive(int id = CONNECTION_NONE); + + void Flush_All(void); + + // + // Timing management + // + virtual void Reset_Response_Time(void); + virtual unsigned long Response_Time(void); + virtual void Set_Timing (unsigned long retrydelta, + unsigned long maxretries, unsigned long timeout); + + // + // Debugging + // + virtual void Configure_Debug(int index, int type_offset, int type_size, + char **names, int namestart, int namecount); + virtual void Mono_Debug_Print(int index, int refresh); + + CommBufferClass *GlobalQueue; + CommBufferClass *PrivateQueue; + + // + // This flag will be set if I'm the game host. + // + int IsHost; + + private: + int Connections[MAX_PLAYERS]; + int ID[MAX_PLAYERS]; + char Names[MAX_PLAYERS][128]; + int NumConnections; + +}; + +/***************************** end of tenmgr.h *****************************/ diff --git a/code/redalert/terrain.cpp b/code/redalert/terrain.cpp new file mode 100644 index 0000000..f166f96 --- /dev/null +++ b/code/redalert/terrain.cpp @@ -0,0 +1,789 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TERRAIN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : October 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TerrainClass::AI -- Process the terrain object AI. * + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * TerrainClass::Heath_Ratio -- Determines the health ratio for the terrain object. * + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * TerrainClass::Mark -- Marks the terrain object on the map. * + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * TerrainClass::TerrainClass -- This is the constructor for a terrain object. * + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * TerrainClass::delete -- Deletes a terrain object. * + * TerrainClass::new -- Creates a new terrain object. * + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "terrain.h" + + +/*********************************************************************************************** + * TerrainClass::~TerrainClass -- Default destructor for terrain class objects. * + * * + * This is the default destructor for terrain objects. It will remove the object from the * + * map and tracking systems, but only if the game is running. Otherwise, it does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +TerrainClass::~TerrainClass(void) +{ + if (GameActive && Class) { + TerrainClass::Limbo(); + } +} + + +/*********************************************************************************************** + * TerrainClass::Take_Damage -- Damages the terrain object as specified. * + * * + * This routine is called when damage is to be inflicted upon the terrain object. It is * + * through this routine that terrain objects are attacked and thereby destroyed. Not all * + * terrain objects can be damaged by this routine however. * + * * + * INPUT: damage -- The damage points to inflict (raw). * + * * + * warhead -- The warhead type the indicates the kind of damage. This is used to * + * determine if the terrain object is damaged and if so, by how much. * + * * + * OUTPUT: bool; Was the terrain object destroyed by this damage? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 12/11/1994 JLB : Shortens attached burning animations. * + *=============================================================================================*/ +ResultType TerrainClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Small arms can never destroy a terrain element. + */ + if ((!IsOnFire || warhead == WARHEAD_FIRE) && warhead != WARHEAD_SA && !Class->IsImmune) { + + res = ObjectClass::Take_Damage(damage, distance, warhead, source, forced); + + if (damage && warhead == WARHEAD_FIRE) { + Catch_Fire(); + } + + /* + ** If the terrain object is destroyed by this damage, then only remove it if it + ** currently isn't on fire and isn't in the process of crumbling. + */ + if (res == RESULT_DESTROYED) { + + /* + ** Remove this terrain object from the targeting computers of all other + ** game objects. No use beating a dead horse. + */ + Detach_All(); + + if (IsOnFire) { + + /* + ** Attached flame animation should be shortened as much as possible so that + ** crumbling can begin soon. + */ + Shorten_Attached_Anims(this); + } else { + Start_To_Crumble(); + } + } + } + return(res); +} + + +/*********************************************************************************************** + * TerrainClass::new -- Creates a new terrain object. * + * * + * This routine creates a new terrain object by grabbing a free slot * + * out of the terrain object pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the terrain object allocated. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void * TerrainClass::operator new(size_t) +{ + void * ptr = Terrains.Allocate(); + if (ptr) { + ((TerrainClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * TerrainClass::delete -- Deletes a terrain object. * + * * + * This routine deletes a terrain object by returning it to the * + * terrain object pool. * + * * + * INPUT: ptr -- Pointer to the terrain object to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/14/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::operator delete(void * ptr) +{ + if (ptr) { + ((TerrainClass *)ptr)->IsActive = false; + } + Terrains.Free((TerrainClass *)ptr); +} + + +/*********************************************************************************************** + * TerrainClass::TerrainClass -- This is the constructor for a terrain object * + * * + * This constructor for a terrain object will initialize the terrain * + * object with it's proper type and insert it into the access * + * tracking system. * + * * + * INPUT: type -- The terrain object type. * + * * + * cell -- The location of the terrain object. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + *=============================================================================================*/ +TerrainClass::TerrainClass(TerrainType type, CELL cell) : + ObjectClass(RTTI_TERRAIN, Terrains.ID(this)), + Class(TerrainTypes.Ptr((int)type)), + IsOnFire(false), + IsCrumbling(false) +{ + Strength = Class->MaxStrength; + if (cell != -1) { + if (!Unlimbo(Cell_Coord(cell))) { + delete this; + } + } + Set_Rate(0); // turn off animation +} + + +/*********************************************************************************************** + * TerrainClass::Mark -- Marks the terrain object on the map. * + * * + * This routine will mark or remove the terrain object from the map * + * tracking system. This is typically called when the terrain object * + * is first created, when it is destroyed, and whenever it needs to be * + * redrawn. * + * * + * INPUT: mark -- The marking operation to perform. * + * * + * OUTPUT: bool; Was the terrain object successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/02/1994 JLB : Created. * + * 12/23/1994 JLB : Performs low level legality check before proceeding. * + *=============================================================================================*/ +bool TerrainClass::Mark(MarkType mark) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (ObjectClass::Mark(mark)) { + CELL cell = Coord_Cell(Coord); + + switch (mark) { + case MARK_UP: + Map.Pick_Up(cell, this); + break; + + case MARK_DOWN: + Map.Place_Down(cell, this); + break; + + default: + Map.Refresh_Cells(cell, Overlap_List(true)); + Map.Refresh_Cells(cell, Occupy_List()); + break; + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Draw_It -- Renders the terrain object at the location specified. * + * * + * This routine is used to render the terrain object at the location specified and * + * clipped to the window specified. This is the gruntwork drawing routine for the * + * terrain objects as they are displayed on the map. * + * * + * INPUT: x,y -- The coordinate to draw the terrain object at (centered). * + * * + * window -- The clipping window to draw to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + * 11/09/1994 JLB : Changed selected terrain highlight method. * + *=============================================================================================*/ +void TerrainClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + void const * shapedata; + + shapedata = Get_Image_Data(); + if (shapedata) { + int shapenum = 0; + + /* + ** Determine the animation stage to render the terrain object. If it is crumbling, then + ** it will display the crumbling animation. + */ + if (IsCrumbling) { + shapenum = Fetch_Stage()+IsCrumbling; + } else { + if (Strength < 2) { + shapenum++; + } + } + + ShapeFlags_Type flags = SHAPE_NORMAL; + if (Is_Selected_By_Player() && Debug_Map) flags = flags | SHAPE_FADING; + + /* + **Terrain is always theater specific so flag it as such for Build_Frame + */ + IsTheaterShape = true; + // Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019 + CC_Draw_Shape(this, shapedata, shapenum, x, y, window, flags|SHAPE_WIN_REL|SHAPE_GHOST, Map.FadingLight, Map.UnitShadow); + IsTheaterShape = false; + } +} + + +/*********************************************************************************************** + * TerrainClass::Init -- Initialize the terrain object tracking system. * + * * + * This routine will clear out the terrain object system so that no terrain objects will * + * exists. It is called prior to loading or starting a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Init(void) +{ + Terrains.Free_All(); +} + + +/*********************************************************************************************** + * TerrainClass::Can_Enter_Cell -- Determines if the terrain object can exist in the cell. * + * * + * This routine will examine the cell specified and determine if the the terrain object * + * can legally exist there. * + * * + * INPUT: cell -- The cell to examine. * + * * + * OUTPUT: If the terrain object can be placed in the cell specified, then a value less than * + * 256 will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/24/1994 JLB : Created. * + * 01/01/1995 JLB : Actually works now. * + *=============================================================================================*/ +MoveType TerrainClass::Can_Enter_Cell(CELL cell, FacingType) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + short const * offset; // Pointer to cell offset list. + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + offset = Occupy_List(); + while (*offset != REFRESH_EOL) { + if (Class->IsWaterBased) { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build(SPEED_FLOAT)) { + return(MOVE_NO); + } + } else { + if (!Map[(CELL)(cell + *offset++)].Is_Clear_To_Build()) { + return(MOVE_NO); + } + } + } + return(MOVE_OK); +} + + +/*********************************************************************************************** + * TerrainClass::Catch_Fire -- Catches the terrain object on fire. * + * * + * This routine is called if the terrain object is supposed to catch on fire. The routine * + * performs checking to make sure that only flammable terrain objects that aren't already * + * on fire get caught on fire. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object caught on fire by this routine? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 12/11/1994 JLB : Don't catch fire if already on fire or crumbling. * + *=============================================================================================*/ +bool TerrainClass::Catch_Fire(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling && !IsOnFire && Class->Armor == ARMOR_WOOD) { + AnimClass * anim = new AnimClass(ANIM_BURN_BIG, Coord_Add(Sort_Y(), 0xFFB00000L)); + if (anim) { + anim->Attach_To(this); + } + anim = new AnimClass(ANIM_BURN_MED, Coord_Add(Sort_Y(), 0xFF200000L), 15); + if (anim) { + anim->Attach_To(this); + } + IsOnFire = true; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Fire_Out -- Handles when fire has gone out. * + * * + * When the fire has gone out on a burning terrain object, this routine is called. The * + * animation has already been terminated prior to calling this routine. All this routine * + * needs to perform is any necessary local flag updating. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Fire_Out(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (IsOnFire) { + IsOnFire = false; + if (!IsCrumbling && !Strength) { + Detach_All(); + Mark(); + Start_To_Crumble(); + new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, Class->CenterBase)); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::AI -- Process the terrain object AI. * + * * + * This is used to handle any AI processing necessary for terrain objects. This might * + * include animation effects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + * 09/28/1994 JLB : Crumbling animation. * + * 08/12/1996 JLB : Reset map zone when terrain object destroyed. * + * 10/04/1996 JLB : Growth speed regulated by rules. * + *=============================================================================================*/ +void TerrainClass::AI(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::AI(); + + if ((*this == TERRAIN_MINE) && (Frame % (Rule.GrowthRate * TICKS_PER_MINUTE)) == 0) { + Map[::As_Cell(As_Target())].Spread_Tiberium(true); + } + if (StageClass::Graphic_Logic()) { + Mark(); + + /* + ** If the terrain object is in the process of crumbling, then when at the + ** last stage of the crumbling animation, delete the terrain object. + */ + if (IsCrumbling && Fetch_Stage() == Get_Build_Frame_Count(Class->Get_Image_Data())-1) { + delete this; + + Map.Zone_Reset(MZONEF_NORMAL|MZONEF_CRUSHER|MZONEF_DESTROYER); + } + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * TerrainClass::Debug_Dump -- Displays the status of the terrain object. * + * * + * This debugging support routine is used to display the status of the terrain object to * + * the debug screen. * + * * + * INPUT: mono -- The mono screen to display the status to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/27/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Debug_Dump(MonoClass * mono) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + ObjectClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * TerrainClass::Unlimbo -- Unlimbo terrain object onto the map. * + * * + * This routine is used to unlimbo the terrain object onto a location on the map. Normal * + * unlimbo procedures are sufficient except that the coordinate location of a terrain * + * object is based on the upper left corner of a cell rather than the center. Mask the * + * coordinate value so that it snaps to the upper left corner and then proceed with a * + * normal unlimbo process. * + * * + * INPUT: coord -- The coordinate to mark as the terrain's location. * + * * + * dir -- unused * + * * + * OUTPUT: bool; Was the terrain object successful in the unlimbo process? Failure could be * + * the result of illegal positioning. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/02/1994 JLB : Created. * + * 11/16/1994 JLB : Checks for theater legality. * + *=============================================================================================*/ +bool TerrainClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (Class->Theater & (1 << Scen.Theater)) { + return(ObjectClass::Unlimbo(coord, dir)); + } + return(false); +} + + +/*********************************************************************************************** + * TerrainClass::Start_To_Crumble -- Initiates crumbling of terrain (tree) object. * + * * + * This routine is used to start the crumbling process for terrain object. This only * + * applies to trees. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Start_To_Crumble(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsCrumbling) { + IsCrumbling = true; + Set_Rate(2); + Set_Stage(0); + } +} + + +/*********************************************************************************************** + * TerrainClass::Limbo -- Handles terrain specific limbo action. * + * * + * This routine (called as a part of the limbo process) will remove the terrain occupation * + * flag in the cell it occupies. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the terrain object unlimboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool TerrainClass::Limbo(void) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + if (!IsInLimbo) { + CELL cell = Coord_Cell(Coord); + Map[cell].Flag.Occupy.Monolith = false; + } + return(ObjectClass::Limbo()); +} + + +/*********************************************************************************************** + * TerrainClass::Center_Coord -- Fetches the center point coordinate for terrain object. * + * * + * Use this routine to fetch the center point terrain * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Center_Coord(void) const +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, Class->CenterBase)); +} + + +/*********************************************************************************************** + * TerrainClass::Radar_Icon -- Fetches pointer to radar icon to use. * + * * + * This routine will return with a pointer to the radar icon to use for the cell number * + * specified. * + * * + * INPUT: cell -- The cell number to use when determine what icon pointer to return. * + * * + * OUTPUT: Returns with a pointer to the 9 pixel values that make up the icon of this * + * terrain object located at the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +unsigned char * TerrainClass::Radar_Icon(CELL cell) +{ + assert(Terrains.ID(this) == ID); + assert(IsActive); + + unsigned char * icon = (unsigned char *)Class->Get_Radar_Data(); // get a pointer to radar icons + int width = *icon++; // extract the width from data + int height = *icon++; // extract the width from data + + /* + ** Icon number that we need can be found by converting the cell and base + ** cell to and x and y offset from the upper left of the cell, and then + ** multiplying it by the width of the terrain in icons, which we + ** conveniently stored out as the first byte of every icon we made. + */ + int basecell = Coord_Cell(Coord); // find the base cell of terrain + int ydiff = Cell_Y(cell) - Cell_Y(basecell); + int xdiff = Cell_X(cell) - Cell_X(basecell); + if (xdiff < width && ydiff < height) { + int iconnum = (ydiff * width) + xdiff; + return(icon + (iconnum * 9)); + } + return(NULL); +} + + +/*********************************************************************************************** + * TerrainClass::Target_Coord -- Returns with the target coordinate. * + * * + * This routine will return with the coordinate to use if this terrain object were to be * + * fired upon and the coordinate where the bullets should hit is needed. Usually, this * + * location is the base of the object (e.g., the trunk of a tree). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the coordinate to use when firing at this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/07/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE TerrainClass::Target_Coord(void) const +{ + return(Coord_Add(XY_Coord(0, -Height), Sort_Y())); +} + + +/*********************************************************************************************** + * TerrainClass::Read_INI -- Reads terrain objects from INI file. * + * * + * This routine reads a scenario control INI file and creates all * + * terrain objects specified therein. Objects so created are placed * + * upon the map. * + * * + * INI entry format: * + * cellnum = TypeName, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Read_INI(CCINIClass & ini) +{ + TerrainClass * tptr; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + TerrainType terrain = ini.Get_TerrainType(INI_Name(), entry, TERRAIN_NONE); + CELL cell = atoi(entry); + + if (terrain != TERRAIN_NONE) { + tptr = new TerrainClass(terrain, cell); + } + } +} + + +/*********************************************************************************************** + * TerrainClass::Write_INI -- Write all terrain objects to the INI database specified. * + * * + * This routine will clear out any old terrain data from the INI database and then * + * fill it in with all the data from the terrain objects that currently exists. * + * * + * INPUT: ini -- Reference to the INI database to store the terrain objects in. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void TerrainClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing terrain data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the terrain data out. + */ + for (int index = 0; index < Terrains.Count(); index++) { + TerrainClass * terrain; + + terrain = Terrains.Ptr(index); + if (terrain != NULL && !terrain->IsInLimbo && terrain->IsActive) { + char uname[10]; + sprintf(uname, "%d", Coord_Cell(terrain->Coord)); + ini.Put_TerrainType(INI_Name(), uname, *terrain); + } + } +} \ No newline at end of file diff --git a/code/redalert/terrain.h b/code/redalert/terrain.h new file mode 100644 index 0000000..7744b18 --- /dev/null +++ b/code/redalert/terrain.h @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TERRAIN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TERRAIN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 29, 1994 * + * * + * Last Update : April 29, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TERRAIN_H +#define TERRAIN_H + +#include "object.h" +#include "type.h" + + +/**************************************************************************** +** Each type of terrain has certain pieces of static information associated +** with it. This class elaborates this data. +*/ +class TerrainClass : public ObjectClass, public StageClass +{ + public: + /* + ** This points to the constant terrain data (for this type) that gives this + ** terrain object its character. + */ + CCPtr Class; + + /* + ** Constructor for terrain object class. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + TerrainClass(TerrainType id, CELL cell); + TerrainClass(NoInitClass const & x) : ObjectClass(x), Class(x), StageClass(x) {}; + virtual ~TerrainClass(void); + operator TerrainType(void) const {return Class->Type;}; + + static void Init(void); + + /* + ** Terrain specific support functions. + */ + void Start_To_Crumble(void); + + /* + ** Query functions. + */ + virtual ObjectTypeClass const & Class_Of(void) const {return *Class;}; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Center_Coord(void) const; + virtual COORDINATE Render_Coord(void) const {return Coord;}; + virtual COORDINATE Sort_Y(void) const {return Coord_Add(Coord, Class->CenterBase);}; + virtual COORDINATE Target_Coord(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Unlimbo(COORDINATE coord, DirType dir=DIR_N); + virtual bool Limbo(void); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing = FACING_NONE) const; + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual bool Mark(MarkType mark=MARK_CHANGE); + unsigned char *Radar_Icon(CELL cell); + + /* + ** User I/O. + */ + virtual void Clicked_As_Target(HousesType house, int) {}; // 2019/09/20 JAS - Added record of who clicked on the object + + /* + ** Combat related. + */ + virtual void Fire_Out(void); + virtual bool Catch_Fire(void); + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced=false); + + /* + ** AI. + */ + virtual void AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "TERRAIN";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + private: + + /* + ** If this terrain object is on fire, then this flag will be true. + */ + unsigned IsOnFire:1; + + /* + ** Is this a terrain object that undergoes crumbling animation and it is + ** in fact crumbling at this time? + */ + unsigned IsCrumbling:1; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[8]; +}; + +#endif \ No newline at end of file diff --git a/code/redalert/tevent.cpp b/code/redalert/tevent.cpp new file mode 100644 index 0000000..449afbc --- /dev/null +++ b/code/redalert/tevent.cpp @@ -0,0 +1,766 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEVENT.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEVENT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : July 29, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Attaches_To -- Determines what event can be attached to. * + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * Event_From_Name -- retrieves EventType for given name * + * Event_Needs -- Returns with what this event type needs for data. * + * Name_From_Event -- retrieves name for EventType * + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + +/* +** This is the text name for all of the trigger events. These are used by the scenario editor +*/ +const char * EventText[TEVENT_COUNT] = { + "-No Event-", + "Entered by...", + "Spied by...", + "Thieved by...", + "Discovered by player", + "House Discovered...", + "Attacked by anybody", + "Destroyed by anybody", + "Any Event", + "Destroyed, Units, All...", + "Destroyed, Buildings, All...", + "Destroyed, All...", + "Credits exceed (x100)...", + "Elapsed Time (1/10th min)...", + "Mission Timer Expired", + "Destroyed, Buildings, #...", + "Destroyed, Units, #...", + "No Factories left", + "Civilians Evacuated", + "Build Building Type...", + "Build Unit Type...", + "Build Infantry Type...", + "Build Aircraft Type...", + "Leaves map (team)...", + "Zone Entry by...", + "Crosses Horizontal Line...", + "Crosses Vertical Line...", + "Global is set...", + "Global is clear...", + "Destroyed, Fakes, All...", + "Low Power...", + "All bridges destroyed", + "Building exists..." +}; + + +/* +** This is an ordinal list of trigger events. This list +** is used when generating the trigger dialog box. +*/ +EventChoiceClass EventChoices[TEVENT_COUNT] = { + {TEVENT_NONE}, + {TEVENT_PLAYER_ENTERED}, + {TEVENT_SPIED}, + {TEVENT_THIEVED}, + {TEVENT_DISCOVERED}, + {TEVENT_HOUSE_DISCOVERED}, + {TEVENT_ATTACKED}, + {TEVENT_DESTROYED}, + {TEVENT_ANY}, + {TEVENT_UNITS_DESTROYED}, + {TEVENT_BUILDINGS_DESTROYED}, + {TEVENT_ALL_DESTROYED}, + {TEVENT_CREDITS}, + {TEVENT_TIME}, + {TEVENT_MISSION_TIMER_EXPIRED}, + {TEVENT_NBUILDINGS_DESTROYED}, + {TEVENT_NUNITS_DESTROYED}, + {TEVENT_NOFACTORIES}, + {TEVENT_EVAC_CIVILIAN}, + {TEVENT_BUILD}, + {TEVENT_BUILD_UNIT}, + {TEVENT_BUILD_INFANTRY}, + {TEVENT_BUILD_AIRCRAFT}, + {TEVENT_LEAVES_MAP}, + {TEVENT_ENTERS_ZONE}, + {TEVENT_CROSS_HORIZONTAL}, + {TEVENT_CROSS_VERTICAL}, + {TEVENT_GLOBAL_SET}, + {TEVENT_GLOBAL_CLEAR}, + {TEVENT_FAKES_DESTROYED}, + {TEVENT_LOW_POWER}, + {TEVENT_ALL_BRIDGES_DESTROYED}, + {TEVENT_BUILDING_EXISTS}, +}; + + +/*********************************************************************************************** + * EventChoiceClass::Draw_It -- Displays the event choice class as a text string. * + * * + * This utility routine is used by the list box display code to print the text line that * + * describes the event choice action. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void EventChoiceClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} + + +/*********************************************************************************************** + * TEventClass::Reset -- Reset the trigger for a subsequent "spring". * + * * + * This will reset the trigger event so that it may be "sprung" again. Typically, this * + * occurs for persistent triggers. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Reset(TDEventClass & td) const +{ + td.IsTripped = false; + if (Event == TEVENT_TIME) { + td.Timer = Data.Value * (TICKS_PER_MINUTE/10); + } +} + + +/*********************************************************************************************** + * TEventClass::operator () -- Action operator to see if event is satisfied. * + * * + * This routine is called to see if the event has been satisfied. Typically, when the * + * necessary trigger events have been satisfied, then the trigger is sprung. For some * + * events, the act of calling this routine is tacit proof enough that the event has * + * occurred. For most other events, the success condition must be explicitly checked. * + * * + * INPUT: event -- The event that has occurred according to the context from which this * + * routine was called. In the case of no specific event having occurred, * + * then TEVENT_ANY will be passed in. * + * * + * house -- The house that this event is tied to. * + * * + * object-- The object that this event might apply to. For object triggering * + * events, this will point to the perpetrator object. * + * * + * forced-- If this event is forced by some outside action, this flag will be true. * + * Forcing only occurs as an explicit action from another trigger. * + * * + * OUTPUT: Was this event satisfied? A satisfied event will probably spring the trigger * + * it is attached to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +bool TEventClass::operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced) +{ + /* + ** If this trigger event has been forced, then no further checks are required. + ** Force the trigger to be tripped. + */ + if (forced) { + td.IsTripped = true; + } + + if (td.IsTripped) return(true); + + /* + ** Triggers based on the game's global environment such as time or + ** global flags are triggered only when the appropriate condition + ** is true. + */ + switch (Event) { + case TEVENT_GLOBAL_SET: + if (!Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_GLOBAL_CLEAR: + if (Scen.GlobalFlags[Data.Value]) return(false); + return(true); + + case TEVENT_MISSION_TIMER_EXPIRED: +// if (MissionSuspend == -1 || MissionTimer != 0) return(false); + if (!Scen.MissionTimer.Is_Active() || Scen.MissionTimer != 0) return(false); + return(true); + + case TEVENT_TIME: + if (td.Timer != 0) return(false); + return(true); + } + + /* + ** Don't trigger this event if the parameters mean nothing. Typical of + ** this would be for events related to time or other outside influences. + */ + if (Event == TEVENT_NONE) { + return(false); + } + + /* + ** If this is not the event for this trigger, just return. This is only + ** necessary to check for those trigger events that are presumed to be + ** true just by the fact that this routine is called with the appropriate + ** event identifier. + */ + if (Event == TEVENT_ATTACKED || + Event == TEVENT_DESTROYED || + Event == TEVENT_DISCOVERED || + Event == TEVENT_SPIED || + Event == TEVENT_NONE || + Event == TEVENT_CROSS_HORIZONTAL || + Event == TEVENT_CROSS_VERTICAL || + Event == TEVENT_ENTERS_ZONE || + Event == TEVENT_PLAYER_ENTERED) { + + if (event != Event && event != TEVENT_ANY) { + return(false); + } + } + + /* + ** The cell entry trigger event is only tripped when an object of the + ** matching ownership has entered the cell in question. All other + ** conditions will not trigger the event. + */ + if (Event == TEVENT_PLAYER_ENTERED || Event == TEVENT_CROSS_HORIZONTAL || Event == TEVENT_CROSS_VERTICAL || Event == TEVENT_ENTERS_ZONE) { + if (!object || object->Owner() != Data.House) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** Check for all bridges destroyed condition. + */ + if (Event == TEVENT_ALL_BRIDGES_DESTROYED) { + if (Scen.BridgeCount) return(false); + td.IsTripped = true; + return(true); + } + + /* + ** The following trigger events are not considered to have sprung + ** merely by fact that this routine has been called. These trigger + ** events must be verified manually by examining the house that + ** they are assigned to. + */ + HouseClass * hptr = HouseClass::As_Pointer(house); + int index; + if (hptr != NULL) { + switch (Event) { + /* + ** Check to see if a team of the appropriate type has left the map. + */ + case TEVENT_LEAVES_MAP: + for (index = 0; index < Teams.Count(); index++) { + TeamClass * ptr = Teams.Ptr(index); + if (ptr->Class == Team && ptr->Is_Empty() && ptr->IsLeaveMap) { +// if (ptr->Class == Team && ptr->House == hptr && ptr->Is_Empty() && ptr->IsLeaveMap) { + td.IsTripped = true; + break; + } + } + if (index == Teams.Count()) return(false); + break; + + /* + ** Credits must be equal or greater to the value specified. + */ + case TEVENT_CREDITS: + if (hptr->Available_Money() < Data.Value) return(false); + break; + + /* + ** Ensure that there are no more factories left. + */ + case TEVENT_NOFACTORIES: + if (hptr->BScan & (STRUCTF_AIRSTRIP|STRUCTF_TENT|STRUCTF_WEAP|STRUCTF_BARRACKS|STRUCTF_CONST)) return(false); + break; + + /* + ** Ensure that there are no fake structures left. + */ + case TEVENT_FAKES_DESTROYED: + //PG_TO_FIX + //warning C4293: '<<': shift count negative or too big, undefined behavior + //Indeed, STRUCTF_FAKEWEAP and STRUCTF_FAKECONST don't fit in the BScan bit range. ST - 5/9/2019 + //if (hptr->BScan & (STRUCTF_FAKECONST|STRUCTF_FAKEWEAP)) return(false); + break; + + /* + ** A civilian must have been evacuated. + */ + case TEVENT_EVAC_CIVILIAN: + if (!hptr->IsCivEvacuated) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILDING_EXISTS: + if ((hptr->ActiveBScan & (1 << Data.Structure)) == 0) return(false); +// if (hptr->Get_Quantity(Data.Structure) == 0) return(false); + break; + + /* + ** Verify that the structure has been built. + */ + case TEVENT_BUILD: + if (hptr->JustBuiltStructure != Data.Structure) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the unit has been built. + */ + case TEVENT_BUILD_UNIT: + if (hptr->JustBuiltUnit != Data.Unit) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the infantry has been built. + */ + case TEVENT_BUILD_INFANTRY: + if (hptr->JustBuiltInfantry != Data.Infantry) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the aircraft has been built. + */ + case TEVENT_BUILD_AIRCRAFT: + if (hptr->JustBuiltAircraft != Data.Aircraft) return(false); + td.IsTripped = true; + break; + + /* + ** Verify that the specified number of buildings have been destroyed. + */ + case TEVENT_NBUILDINGS_DESTROYED: + if ((int)hptr->BuildingsLost < Data.Value) return(false); + break; + + /* + ** Verify that the specified number of units have been destroyed. + */ + case TEVENT_NUNITS_DESTROYED: + if ((int)hptr->UnitsLost < Data.Value) return(false); + break; + + default: + break; + } + } + + hptr = HouseClass::As_Pointer(Data.House); + if (hptr) { + switch (Event) { + case TEVENT_LOW_POWER: + if (hptr->Power_Fraction() >= 1) return(false); + break; + +// case TEVENT_SPIED: +// if (!hptr->IsSpied) return(false); +// break; + + case TEVENT_THIEVED: + if (!hptr->IsThieved) return(false); + break; + + /* + ** Verify that the house has been discovered. + */ + case TEVENT_HOUSE_DISCOVERED: + if (!hptr->IsDiscovered) return(false); + break; + + /* + ** Verify that all buildings have been destroyed. + */ + case TEVENT_BUILDINGS_DESTROYED: + if (hptr->ActiveBScan) return(false); + break; + + /* + ** Verify that all units have been destroyed -- with some + ** exceptions. + */ + case TEVENT_UNITS_DESTROYED: + if (hptr->ActiveUScan | hptr->ActiveIScan) return(false); + break; + + /* + ** Verify that all buildings and units have been destroyed. + */ + case TEVENT_ALL_DESTROYED: + if (hptr->ActiveBScan | hptr->ActiveUScan | hptr->ActiveIScan | hptr->ActiveVScan) return(false); + break; + + default: + break; + } + } + + return(true); +} + + +/*********************************************************************************************** + * TEventClass::Build_INI_Entry -- Builds the ini text for this event. * + * * + * This routine will build the ini text for this trigger event. The ini text is appended * + * to the string buffer specified. This routine is used to build the complete trigger * + * ini text for writing out to the INI scenario file. * + * * + * INPUT: ptr -- Pointer to the string buffer that will hold the event INI data. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Build_INI_Entry(char * ptr) const +{ + sprintf(ptr, "%d,%d,%d", Event, TeamTypes.Logical_ID(Team), Data.Value); +} + + +/*********************************************************************************************** + * TEventClass::Read_INI -- Parses the INI text for this event's data. * + * * + * This routine is used to parse the INI data line to fetch the event's data from it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +void TEventClass::Read_INI(void) +{ + char const * token; + switch (NewINIFormat) { + default: + Event = TEventType(atoi(strtok(NULL, ","))); + Team.Set_Raw(atoi(strtok(NULL, ","))); + Data.Value = atoi(strtok(NULL, ",")); + break; + + case 1: + token = strtok(NULL, ","); + Event = TEVENT_NONE; + if (token) Event = TEventType(atoi(token)); + + token = strtok(NULL, ","); + Team = NULL; + Data.Value = -1; + if (token) { + if (Event_Needs(Event) == NEED_TEAM) { + Team = TeamTypes.Raw_Ptr(atoi(token)); + } else { + Data.Value = atoi(token); + } + } + break; + + case 0: + Event = TEventType(atoi(strtok(NULL, ","))); + + strtok(NULL, ","); + strtok(NULL, ","); + + Team = TeamTypeClass::From_Name(strtok(NULL, ",")); + Data.Value = atoi(strtok(NULL, ",")); + strtok(NULL, ","); + break; + } +} + + +/*********************************************************************************************** + * Event_Needs -- Returns with what this event type needs for data. * + * * + * This routine will examine the specified event type and return a code that indicates * + * the type of data that must be supplied to this event. Some events require no data and * + * these will return NEED_NONE. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the type of additional data that is needed by this event. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +NeedType Event_Needs(TEventType event) +{ + switch (event) { + case TEVENT_THIEVED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_LOW_POWER: + return(NEED_HOUSE); + + case TEVENT_NUNITS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_CREDITS: + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + return(NEED_NUMBER); + + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + return(NEED_STRUCTURE); + + case TEVENT_BUILD_UNIT: + return(NEED_UNIT); + + case TEVENT_BUILD_INFANTRY: + return(NEED_INFANTRY); + + case TEVENT_BUILD_AIRCRAFT: + return(NEED_AIRCRAFT); + + case TEVENT_LEAVES_MAP: + return(NEED_TEAM); + + default: + break; + } + return(NEED_NONE); +} + + +/*********************************************************************************************** + * Event_From_Name -- retrieves EventType for given name * + * * + * INPUT: * + * name name to get event for * + * * + * OUTPUT: * + * EventType for given name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +TEventType Event_From_Name (char const * name) +{ + if (name) { + for (TEventType i = TEVENT_NONE; i < TEVENT_COUNT; i++) { + if (!stricmp(name, EventText[i])) { + return(i); + } + } + } + + return(TEVENT_NONE); +} + + +/*********************************************************************************************** + * Name_From_Event -- retrieves name for EventType * + * * + * INPUT: * + * event EventType to get name for * + * * + * OUTPUT: * + * name for EventType * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +char const * Name_From_Event(TEventType event) +{ + return(EventText[event]); +} + + +/*********************************************************************************************** + * Attaches_To -- Determines what event can be attached to. * + * * + * This routine is used to determine what this event type can be attached to in the game. * + * Some events are specifically tied to cells or game object. This routine will indicate * + * this requirement. * + * * + * INPUT: event -- The event type to examine. * + * * + * OUTPUT: Returns with the attachable characteristics for this event type. These * + * characteristics are represented by a composite bit field. Some events can be * + * attached to multiple objects. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/28/1995 JLB : Created. * + *=============================================================================================*/ +AttachType Attaches_To(TEventType event) +{ + AttachType attach = ATTACH_NONE; + + switch (event) { + case TEVENT_CROSS_HORIZONTAL: + case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_PLAYER_ENTERED: + case TEVENT_ANY: + case TEVENT_DISCOVERED: + case TEVENT_NONE: + attach = attach | ATTACH_CELL; + break; + + default: + break; + } + + switch (event) { + case TEVENT_SPIED: + case TEVENT_PLAYER_ENTERED: + case TEVENT_DISCOVERED: + case TEVENT_DESTROYED: + case TEVENT_ATTACKED: + case TEVENT_ANY: + case TEVENT_NONE: + attach = attach | ATTACH_OBJECT; + break; + + default: + break; + } + + switch (event) { +// case TEVENT_CROSS_HORIZONTAL: +// case TEVENT_CROSS_VERTICAL: + case TEVENT_ENTERS_ZONE: + case TEVENT_ANY: + attach = attach | ATTACH_MAP; + break; + + default: + break; + } + + switch (event) { + case TEVENT_LOW_POWER: + case TEVENT_EVAC_CIVILIAN: + case TEVENT_BUILDING_EXISTS: + case TEVENT_BUILD: + case TEVENT_BUILD_UNIT: + case TEVENT_BUILD_INFANTRY: + case TEVENT_BUILD_AIRCRAFT: + case TEVENT_NOFACTORIES: + case TEVENT_BUILDINGS_DESTROYED: + case TEVENT_NBUILDINGS_DESTROYED: + case TEVENT_UNITS_DESTROYED: + case TEVENT_NUNITS_DESTROYED: + case TEVENT_ALL_DESTROYED: + case TEVENT_HOUSE_DISCOVERED: + case TEVENT_CREDITS: +// case TEVENT_ATTACKED: + case TEVENT_THIEVED: + case TEVENT_ANY: + case TEVENT_FAKES_DESTROYED: + attach = attach | ATTACH_HOUSE; + break; + + default: + break; + } + + switch (event) { + case TEVENT_TIME: + case TEVENT_GLOBAL_SET: + case TEVENT_GLOBAL_CLEAR: + case TEVENT_MISSION_TIMER_EXPIRED: + case TEVENT_ANY: + case TEVENT_ALL_BRIDGES_DESTROYED: + case TEVENT_LEAVES_MAP: + attach = attach | ATTACH_GENERAL; + break; + + default: + break; + } + + return(attach); +} diff --git a/code/redalert/tevent.h b/code/redalert/tevent.h new file mode 100644 index 0000000..7d55088 --- /dev/null +++ b/code/redalert/tevent.h @@ -0,0 +1,180 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEVENT.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEVENT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/28/95 * + * * + * Last Update : November 28, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma once + +/* +** These are the trigger events that are checked for and if qualified, they will signal +** a successful trigger event. This might result in the trigger action being performed. +*/ +typedef enum TEventType : unsigned char { + TEVENT_NONE, + TEVENT_PLAYER_ENTERED, // player enters this square + TEVENT_SPIED, // Spied by. + TEVENT_THIEVED, // Thieved by (raided or stolen vehicle). + TEVENT_DISCOVERED, // player discovers this object + TEVENT_HOUSE_DISCOVERED, // House has been discovered. + TEVENT_ATTACKED, // player attacks this object + TEVENT_DESTROYED, // player destroys this object + TEVENT_ANY, // Any object event will cause the trigger. + TEVENT_UNITS_DESTROYED, // all house's units destroyed + TEVENT_BUILDINGS_DESTROYED, // all house's buildings destroyed + TEVENT_ALL_DESTROYED, // all house's units & buildings destroyed + TEVENT_CREDITS, // house reaches this many credits + TEVENT_TIME, // Scenario elapsed time from start. + TEVENT_MISSION_TIMER_EXPIRED, // Pre expired mission timer. + TEVENT_NBUILDINGS_DESTROYED, // Number of buildings destroyed. + TEVENT_NUNITS_DESTROYED, // Number of units destroyed. + TEVENT_NOFACTORIES, // No factories left. + TEVENT_EVAC_CIVILIAN, // Civilian has been evacuated. + TEVENT_BUILD, // Specified building has been built. + TEVENT_BUILD_UNIT, // Specified unit has been built. + TEVENT_BUILD_INFANTRY, // Specified infantry has been built. + TEVENT_BUILD_AIRCRAFT, // Specified aircraft has been built. + TEVENT_LEAVES_MAP, // Specified team member leaves map. + TEVENT_ENTERS_ZONE, // Enters same zone as waypoint 'x'. + TEVENT_CROSS_HORIZONTAL, // Crosses horizontal trigger line. + TEVENT_CROSS_VERTICAL, // Crosses vertical trigger line. + TEVENT_GLOBAL_SET, // If specified global has been set. + TEVENT_GLOBAL_CLEAR, // If specified global has been cleared. + TEVENT_FAKES_DESTROYED, // If all fake structures are gone. + TEVENT_LOW_POWER, // When power drops below 100%. + TEVENT_ALL_BRIDGES_DESTROYED, // All bridges destroyed. + TEVENT_BUILDING_EXISTS, // Check for building existing. + + TEVENT_COUNT, + TEVENT_FIRST=0 +} TEventType; + +TEventType Event_From_Name(char const * name); +NeedType Event_Needs(TEventType event); +char const * Name_From_Event(TEventType event); + +/* +** This holds the changable data that is associated with an event as +** it relates to a trigger. +*/ +struct TDEventClass { + /* + ** If this event has been triggered by something that is temporal, then + ** this flag will be set to true so that subsequent trigger examination + ** will return a successful event trigger flag. Typical use of this is + ** for when objects of a specific type are built. + */ + unsigned IsTripped:1; + + /* + ** Timer based events require a special timer control handler. + */ + CDTimerClass Timer; + + TDEventClass(void) : IsTripped(false), Timer(0) {}; + TDEventClass(NoInitClass const & x) : Timer(x) {}; +}; + + +/* +** This elaborates the information necessary to trigger +** an event. +*/ +class TeamTypeClass; +struct TEventClass { + + /* + ** This is the event that will controls how this event gets triggered. + */ + TEventType Event; + + /* + ** If this event needs to reference a team type, then this is the pointer + ** to the team type object. This must be separated from the following + ** union because Watcom compiler won't allow a class that has a + ** constructor to be declared in a union. + */ + CCPtr Team; + + union { + StructType Structure; // Used for structure type checking. + UnitType Unit; // Used for unit type checking. + InfantryType Infantry; // Used for infantry type checking. + AircraftType Aircraft; // Used for aircraft type checking. + HousesType House; // Used for house specific events. + long Value; // Used for other events that need data. + } Data; + + TEventClass(void) : Event(TEVENT_NONE) {Data.Value = 0;}; + TEventClass(TEventType event) : Event(event) {Data.Value = 0;}; + TEventClass(NoInitClass const & x) : Team(x) {}; + + void Code_Pointers(void); + void Decode_Pointers(void); + void Reset(TDEventClass & td) const; + bool operator () (TDEventClass & td, TEventType event, HousesType house, ObjectClass const * object, bool forced); + void Read_INI(void); + void Build_INI_Entry(char * buffer) const; +}; + + +typedef enum AttachType : unsigned char { + ATTACH_NONE=0x00, // Trigger doesn't attach to anything (orphan trigger types). + ATTACH_CELL=0x01, // Trigger can only attach to a cell. + ATTACH_OBJECT=0x02, // Trigger can attach only to object (usually building or vehicle). + ATTACH_MAP=0x04, // Trigger applies to the general map (usually zone or parallel triggers). + ATTACH_HOUSE=0x08, // Trigger applies only to a house. + ATTACH_GENERAL=0x10, // General purpose trigger attached to game state. + ATTACH_TEAM=0x20 // Trigger applies to team object. +} AttachType; + +AttachType Attaches_To(TEventType event); + + + +class EventChoiceClass { + public: + EventChoiceClass(TEventType event=TEVENT_NONE) : Event(event) {} + + operator TEventType (void) const {return(Event);} + bool operator == (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event);} + bool operator != (EventChoiceClass const & rvalue) const {return(Event != rvalue.Event);} + bool operator > (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) > 0);} + bool operator < (EventChoiceClass const & rvalue) const {return(stricmp(Description(), rvalue.Description()) < 0);} + bool operator <= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) < 0);} + bool operator >= (EventChoiceClass const & rvalue) const {return(Event == rvalue.Event || stricmp(Description(), rvalue.Description()) > 0);} + char const * Description(void) const {return(Name_From_Event(Event));} + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; + + TEventType Event; +}; + +extern EventChoiceClass EventChoices[TEVENT_COUNT]; diff --git a/code/redalert/textbtn.cpp b/code/redalert/textbtn.cpp new file mode 100644 index 0000000..7111cd4 --- /dev/null +++ b/code/redalert/textbtn.cpp @@ -0,0 +1,353 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEXTBTN.CPP 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 19, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "textbtn.h" + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- Pointer to the text string to display on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: Call Set_Text & Set_Style, & init X,Y,Width,Height,ID before using this button. * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass(id, x, y, w, h), + String(text) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + + if (w == -1 || h == -1) { + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Default constructor for a text button. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass(void) : + ToggleClass(0, 0, 0, 0, 0) +{ + IsBlackBorder = false; + String = NULL; + PrintFlags = TPF_8POINT; +} + + +/*********************************************************************************************** + * TextButtonClass::TextButtonClass -- Normal constructor for a text button. * + * * + * This is the constructor for text buttons if the text is provided as a string pointer. * + * * + * INPUT: id -- The ID number to assign to this button. * + * * + * text -- The text number to use for displaying on top of the button. * + * * + * x,y -- Pixel coordinate of button's upper left corner. * + * * + * w,h -- Dimensions of the button. If these are not filled in (or with -1), then * + * the dimensions are adapted to fit the text assigned to the button. * + * * + * * + * style -- The print style for the text in the button. These are the TPF_ flags * + * used by Fancy_Text_Print(). * + * * + * border-- If the button is to be surrounded by a black border, then this flag * + * should be set to true. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +TextButtonClass::TextButtonClass (unsigned id, int text, TextPrintType style, int x, int y, int w, int h, int blackborder) : + ToggleClass (id, x, y, w, h), + String(0) +{ + PrintFlags = style; + IsBlackBorder = blackborder; + Set_Text(text); + + if (w == -1 || h == -1) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + if (w == -1) { + Width = String_Pixel_Width(String)+8; + } + if (h == -1) { + Height = FontHeight + FontYSpacing + 2; + } + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Me -- Draws the text buttons as indicated. * + * * + * This routine will draw the text button. * + * * + * INPUT: forced -- If the button is to be redrawn regardless of the state of the redraw * + * flag, then this parameter will be true. * + * * + * OUTPUT: bool; Was the button redrawn? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/03/1995 MML : Created. * + * 01/16/1995 JLB : Modified * + *=============================================================================================*/ +int TextButtonClass::Draw_Me(int forced) +{ + if (ControlClass::Draw_Me(forced)) { + /* + ** Hide the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Hide_Mouse(X, Y, X+Width-1, Y+Height-1); + } + + /* + ** Draw the background and overlaying text. These are virtual function + ** calls so that they may be overridden. + */ + Draw_Background(); + Draw_Text(String); + + /* + ** Display the mouse. + */ + if (LogicPage == &SeenBuff) { + Conditional_Show_Mouse(); + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Assigns a new text string to this button. * + * * + * Use this routine to assign a new text string to this button. By using this function it * + * is possible to dynamically change the button's text. An example of this would be an * + * on/off button that every time it is clicked, the text toggles between "on" and "off". * + * * + * INPUT: text -- Pointer to the text string to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text is NOT copied to this button. You must make sure that the text * + * remains valid throughout the lifetime of this text button. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(char const * text, bool resize) +{ + String = text; + Flag_To_Redraw(); + if (resize && String) { + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE, 0, 0, TBLACK, TBLACK, PrintFlags); + Width = String_Pixel_Width(String)+8; + Height = FontHeight + FontYSpacing + 2; + } +} + + +/*********************************************************************************************** + * TextButtonClass::Set_Text -- Sets the text for this text button. * + * * + * This will set the text for this button. The text is provided as a text number. This * + * number is automatically converted to the appropriate text string before being stored * + * in the button's structure. * + * * + * INPUT: text -- The text number to assign to this button. * + * * + * OUTPUT: none * + * * + * WARNINGS: The text number information is lost when it is assigned to the button. Once * + * the assignment takes place, the text number is NOT remembered by the button. * + * Only the associated text string is. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Set_Text(int text, bool resize) +{ + if (text != TXT_NONE) { + Set_Text(Text_String(text), resize); + } +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Background -- Draws the background to the text button. * + * * + * This localizes the drawing of the background for the text button. By overriding this * + * function you can give a different background to the button. The text is drawn using * + * a different routine. The mouse is hidden, if necessary, before this routine is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Background(void) +{ + /* + ** Draw a border if selected style. + */ + if (IsBlackBorder) { + LogicPage->Draw_Rect (X-1, Y-1, X+Width+2, Y+Height+2, BLACK); + } + + /* + ** Draw the body & set text color. + */ + BoxStyleEnum style; + + if (IsDisabled) { +#ifdef WOLAPI_INTEGRATION + style = BOXSTYLE_BOX; +#else + style = BOXSTYLE_DIS_RAISED; +#endif + } else { + if (IsPressed) { + style = BOXSTYLE_DOWN; + } else { + style = BOXSTYLE_RAISED; + } + } + + Draw_Box(X, Y, Width, Height, style, true); +} + + +/*********************************************************************************************** + * TextButtonClass::Draw_Text -- This draws the text for the text button. * + * * + * This routine draws the text for the text button. You can override this routine if you * + * wish different text rendering styles or colors. The background has already been drawn * + * by the time this function is called. The mouse is hidden, if necessary, as well. * + * * + * INPUT: text -- Pointer to the text string to print over the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void TextButtonClass::Draw_Text(char const * text) +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Display the text. + */ + if (String) { + TextPrintType flags; + + if (IsDisabled) { + flags = (TextPrintType)0; + } else { + if (IsPressed || IsOn) { + flags = TPF_USE_GRAD_PAL|TPF_BRIGHT_COLOR; + } else { + flags = TPF_USE_GRAD_PAL|TPF_MEDIUM_COLOR; + } + } + + // if height of button is non-comformal, adjust Y + int drawY = Y + 1; + if ( Height != RegularButtonHeight ) + { + drawY += ( Height - RegularButtonHeight) / 2; + } + + Fancy_Text_Print(text, X+(Width>>1)-1, drawY, scheme, TBLACK, PrintFlags|flags|TPF_CENTER); + } +} diff --git a/code/redalert/textbtn.h b/code/redalert/textbtn.h new file mode 100644 index 0000000..504728e --- /dev/null +++ b/code/redalert/textbtn.h @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TEXTBTN.H 1 3/03/97 10:25a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TEXTBTN.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TEXTBTN_H +#define TEXTBTN_H + +#include "toggle.h" + + +class TextButtonClass : public ToggleClass +{ + public: + TextButtonClass(void); + TextButtonClass(unsigned id, char const * text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + TextButtonClass(unsigned id, int text, TextPrintType style, int x, int y, int w=-1, int h=-1, int blackborder=false); + virtual int Draw_Me(int forced=false); + virtual void Set_Text(char const *text, bool resize = false); + virtual void Set_Text(int text, bool resize = false); + virtual void Set_Style (TextPrintType style) {PrintFlags = style;} + + protected: + + virtual void Draw_Background(void); + virtual void Draw_Text(char const * text); + + unsigned IsBlackBorder:1; + + /* + ** This points to a constant string that is used for the button's text. + */ + char const * String; + + /* + ** This is the print flags to use when rendering this button's text. + */ + TextPrintType PrintFlags; + + int RegularButtonHeight = FontHeight + FontYSpacing + 2; +}; + +#endif + diff --git a/code/redalert/theme.cpp b/code/redalert/theme.cpp new file mode 100644 index 0000000..7417a13 --- /dev/null +++ b/code/redalert/theme.cpp @@ -0,0 +1,600 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/THEME.CPP 3 3/11/97 4:03p Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 12, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ThemeClass::AI -- Process the theme engine and restart songs. * + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * ThemeClass::From_Name -- Determines theme number from specified name. * + * ThemeClass::Full_Name -- Retrieves the full score name. * + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * ThemeClass::Scan -- Scans all scores for availability. * + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * ThemeClass::Stop -- Stops the current theme from playing. * + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "theme.h" +#include "AUDIOMIX.H" + +#ifndef WIN32 +extern short StreamLowImpact; +#endif //WIN32 + +/* +** These are the actual filename list for the theme sample files. +*/ +ThemeClass::ThemeControl ThemeClass::_themes[THEME_COUNT] = { + {"BIGF226M", TXT_THEME_BIGF, 0, 307, true, false, true, HOUSEF_ALLIES}, + {"CRUS226M", TXT_THEME_CRUS, 0, 222, true, false, true, HOUSEF_SOVIET}, + {"FAC1226M", TXT_THEME_FAC1, 0, 271, true, false, true, HOUSEF_ALLIES}, + {"FAC2226M", TXT_THEME_FAC2, 0, 328, true, false, true, HOUSEF_SOVIET}, + {"HELL226M", TXT_THEME_HELL, 0, 375, true, false, true, HOUSEF_ALLIES}, + {"RUN1226M", TXT_THEME_RUN1, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"SMSH226M", TXT_THEME_SMSH, 0, 272, true, false, true, HOUSEF_ALLIES}, + {"TREN226M", TXT_THEME_TREN, 0, 312, true, false, true, HOUSEF_SOVIET}, + {"WORK226M", TXT_THEME_WORK, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"AWAIT", TXT_THEME_AWAIT, 0, 259, true, false, true, HOUSEF_ALLIES}, + {"DENSE_R", TXT_THEME_DENSE_R, 0, 294, true, false, true, HOUSEF_ALLIES}, + {"FOGGER1A", TXT_THEME_FOGGER1A, 0, 297, true, false, true, HOUSEF_ALLIES}, + {"MUD1A", TXT_THEME_MUD1A, 0, 280, true, false, true, HOUSEF_ALLIES}, + {"RADIO2", TXT_THEME_RADIO2, 0, 237, true, false, true, HOUSEF_ALLIES}, + {"ROLLOUT", TXT_THEME_ROLLOUT, 0, 227, true, false, true, HOUSEF_ALLIES}, + {"SNAKE", TXT_THEME_SNAKE, 0, 277, true, false, true, HOUSEF_ALLIES}, + {"TERMINAT", TXT_THEME_TERMINAT, 0, 310, true, false, true, HOUSEF_ALLIES}, + {"TWIN", TXT_THEME_TWIN, 0, 229, true, false, true, HOUSEF_ALLIES}, + {"VECTOR1A", TXT_THEME_VECTOR1A, 0, 252, true, false, true, HOUSEF_ALLIES}, + {"MAP", TXT_THEME_MAP, 0, 63, false, true, true, HOUSEF_NONE}, + {"SCORE", TXT_THEME_SCORE, 0, 106, false, true, true, HOUSEF_NONE}, + {"INTRO", TXT_THEME_INTRO, 0, 205, false, true, true, HOUSEF_NONE}, + {"CREDITS", TXT_THEME_CREDITS, 0, 163, false, true, true, HOUSEF_NONE}, + + {"2ND_HAND", TXT_THEME_2ND_HAND, 0, 268, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"ARAZOID", TXT_THEME_ARAZOID, 0, 257, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"BACKSTAB", TXT_THEME_BACKSTAB, 0, 278, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"CHAOS2", TXT_THEME_CHAOS2, 0, 250, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"SHUT_IT", TXT_THEME_SHUT_IT, 0, 261, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"TWINMIX1", TXT_THEME_TWINMIX1, 0, 222, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"UNDER3", TXT_THEME_UNDER3, 0, 246, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"VR2", TXT_THEME_VR2, 0, 255, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + {"BOG", TXT_THEME_BOG, 0, 212, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"FLOAT_V2", TXT_THEME_FLOAT_V2, 0, 274, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"GLOOM", TXT_THEME_GLOOM, 0, 236, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"GRNDWIRE", TXT_THEME_GRNDWIRE, 0, 228, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"RPT", TXT_THEME_RPT, 0, 275, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"SEARCH", TXT_THEME_SEARCH, 0, 276, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, + {"TRACTION", TXT_THEME_TRACTION, 0, 237, true, false, true, HOUSEF_ALLIES|HOUSEF_SPAIN}, + {"WASTELND", TXT_THEME_WASTELND, 0, 242, true, false, true, HOUSEF_SOVIET|HOUSEF_SPAIN}, +#endif +}; + +const char* GetThemeMusicFileName(int index) { + return ThemeClass::_themes[index].Name; +} + +/*********************************************************************************************** + * ThemeClass::Base_Name -- Fetches the base filename for the theme specified. * + * * + * This routine is used to retrieve a pointer to the base filename for the theme * + * specified. * + * * + * INPUT: theme -- The theme number to convert into a base filename. * + * * + * OUTPUT: Returns with a pointer to the base filename for the theme specified. If the * + * theme number is invalid, then a pointer to "No Theme" is returned instead. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Base_Name(ThemeType theme) const +{ + if (theme != THEME_NONE) { + return(_themes[theme].Name); + } + return("No theme"); +} + + +/*********************************************************************************************** + * ThemeClass::ThemeClass -- Default constructor for the theme manager class. * + * * + * This is the default constructor for the theme class object. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +ThemeClass::ThemeClass(void) : + Current(-1), + Score(THEME_NONE), + Pending(THEME_NONE) +{ +} + + +/*********************************************************************************************** + * ThemeClass::Full_Name -- Retrieves the full score name. * + * * + * This routine will fetch and return with a pointer to the full name of the theme * + * specified. * + * * + * INPUT: theme -- The theme to fetch the full name for. * + * * + * OUTPUT: Returns with a pointer to the full name for this score. This pointer may point to * + * EMS memory. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +char const * ThemeClass::Full_Name(ThemeType theme) const +{ + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + return(Text_String(_themes[theme].Fullname)); + } + return(NULL); +} + + +/*********************************************************************************************** + * ThemeClass::AI -- Process the theme engine and restart songs. * + * * + * This is a maintenance function that will restart an appropriate theme if the current one * + * has finished. This routine should be called frequently. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + * 01/23/1995 JLB : Picks new song just as it is about to play it. * + *=============================================================================================*/ +void ThemeClass::AI(void) +{ + if (SampleType && !Debug_Quiet) { + if (ScoresPresent && Options.ScoreVolume != 0 && !Still_Playing() && Pending != THEME_NONE) { + + /* + ** If the pending song needs to be picked, then pick it now. + */ + if (Pending == THEME_PICK_ANOTHER) { + Pending = Next_Song(Score); + } + + /* + ** Start the song playing and then flag it so that a new song will + ** be picked when this one ends. + */ + Play_Song(Pending); + Pending = THEME_PICK_ANOTHER; + } + Sound_Callback(); + } +} + + +/*********************************************************************************************** + * ThemeClass::Next_Song -- Calculates the next song number to play. * + * * + * use this routine to figure out what song number to play. It examines the option settings * + * for repeat and shuffle so that it can return the correct value. * + * * + * INPUT: theme -- The origin (last) index. The new value is related to this for all but * + * the shuffling method of play. * + * * + * OUTPUT: Returns with the song number for the next song to play. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 01/19/1995 JLB : Will not play the same song twice when in shuffle mode. * + *=============================================================================================*/ +ThemeType ThemeClass::Next_Song(ThemeType theme) const +{ + if (theme == THEME_NONE || theme == THEME_PICK_ANOTHER || (theme != THEME_QUIET && !_themes[theme].Repeat && !Options.IsScoreRepeat)) { + if (Options.IsScoreShuffle) { + + /* + ** Shuffle the theme, but never pick the same theme that was just + ** playing. + */ + ThemeType newtheme; + do { + newtheme = Sim_Random_Pick(THEME_FIRST, THEME_LAST); + } while (newtheme == theme || !Is_Allowed(newtheme)); + theme = newtheme; + + } else { + + /* + ** Sequential score playing. + */ + do { + theme++; + if (theme > THEME_LAST) { + theme = THEME_FIRST; + } + } while (!Is_Allowed(theme)); + } + } + return(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Queue_Song -- Queues the song to the play queue. * + * * + * This routine will cause the current song to fade and the specified song to start. This * + * is the normal and friendly method of changing the current song. * + * * + * INPUT: theme -- The song to start playing. If -1 is passed in, then just the current song.* + * is faded. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Queue_Song(ThemeType theme) +{ + /* + ** If the current score volumne is set to silent, then there is no need to play the + ** specified theme. + */ + if (Options.ScoreVolume == 0) return; + + AudMix_PlayMusic(theme); +} + + +/*********************************************************************************************** + * ThemeClass::Play_Song -- Starts the specified song play NOW. * + * * + * This routine is used to start the specified theme playing right now. If there is already * + * a theme playing, it is cut short so that this one may start. * + * * + * INPUT: theme -- The theme number to start playing. * + * * + * OUTPUT: Returns with the sample play handle. * + * * + * WARNINGS: This cuts off any current song in a abrupt manner. Only use this routine when * + * necessary. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Play_Song(ThemeType theme) +{ + AudMix_PlayMusic(theme); + + //if (ScoresPresent && SampleType && !Debug_Quiet && Options.ScoreVolume != 0) { + // Stop(); + // Score = theme; + // if (theme != THEME_NONE && theme != THEME_QUIET) { + // //PG StreamLowImpact = true; + // Current = File_Stream_Sample_Vol(Theme_File_Name(theme), 0xFF, true); + // //PG StreamLowImpact = false; + // } + //} + return(Current); +} + + +/*********************************************************************************************** + * ThemeClass::Theme_File_Name -- Constructs a filename for the specified theme. * + * * + * This routine will construct (into a static buffer) a filename that matches the theme * + * number specified. This constructed filename is returned as a pointer. The filename will * + * remain valid until the next call to this routine. * + * * + * INPUT: theme -- The theme number to convert to a filename. * + * * + * OUTPUT: Returns with a pointer to the constructed filename for the specified theme number. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + * 05/09/1995 JLB : Theme variation support. * + *=============================================================================================*/ +char const * ThemeClass::Theme_File_Name(ThemeType theme) +{ + static char name[_MAX_FNAME+_MAX_EXT]; + + if (theme >= THEME_FIRST && theme < THEME_COUNT) { + _makepath(name, NULL, NULL, _themes[theme].Name, ".AUD"); + return((char const *)(&name[0])); + } + + return(""); +} + + +/*********************************************************************************************** + * ThemeClass::Track_Length -- Calculates the length of the song (in seconds). * + * * + * Use this routine to calculate the length of the song. The length is determined by * + * reading the header of the song and dividing the sample rate into the sample length. * + * * + * INPUT: theme -- The song number to examine to find its length. * + * * + * OUTPUT: Returns with the length of the specified theme. This length is in the form of * + * seconds. * + * * + * WARNINGS: This routine goes to disk to fetch this information. Don't call frivolously. * + * * + * HISTORY: * + * 01/16/1995 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Track_Length(ThemeType theme) const +{ + if ((unsigned)theme < THEME_COUNT) { + return(_themes[theme].Duration); + } + return(0); +} + + +/*********************************************************************************************** + * ThemeClass::Stop -- Stops the current theme from playing. * + * * + * Use this routine to stop the current theme. After this routine is called, no more music * + * will play until the Start() function is called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/08/1994 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Stop(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Score = THEME_NONE; + Pending = THEME_NONE; + } +} + + +void ThemeClass::Suspend(void) +{ + if (ScoresPresent && SampleType && !Debug_Quiet && Current != -1) { + Stop_Sample(Current); + Current = -1; + Pending = Score; + Score = THEME_NONE; + } +} + + +/*********************************************************************************************** + * ThemeClass::Still_Playing -- Determines if music is still playing. * + * * + * Use this routine to determine if music is still playing. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the music still audible? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/20/1994 JLB : Created. * + *=============================================================================================*/ +int ThemeClass::Still_Playing(void) const +{ + if (ScoresPresent && SampleType && Current != -1 && !Debug_Quiet) { + return(Sample_Status(Current)); + } + return(false); +} + + +/*********************************************************************************************** + * ThemeClass::Is_Allowed -- Checks to see if the specified theme is legal. * + * * + * Use this routine to determine if a theme is allowed to be played. A theme is not allowed * + * if the scenario is too early for that score, or the score only is allowed in special * + * cases. * + * * + * INPUT: index -- The score the check to see if it is allowed to play. * + * * + * OUTPUT: Is the specified score allowed to play in the normal score playlist? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 07/04/1996 JLB : Handles alternate playlist checking. * + *=============================================================================================*/ +bool ThemeClass::Is_Allowed(ThemeType index) const +{ + if ((unsigned)index >= THEME_COUNT) return(true); + + /* + ** If the theme is not present, then it certainly isn't allowed. + */ + if (!_themes[index].Available) return(false); + + /* + ** Only normal themes (playable during battle) are considered allowed. + */ + if (!_themes[index].Normal) return(false); + + /* + ** If the theme is not allowed to be played by the player's house, then don't allow + ** it. If the player's house hasn't yet been determined, then presume this test + ** passes. + */ + if (PlayerPtr != NULL && ((1 << PlayerPtr->ActLike) & _themes[index].Owner) == 0) return(false); + + /* + ** If the scenario doesn't allow this theme yet, then return the failure flag. The + ** scenario check only makes sense for solo play. + */ + if (Session.Type == GAME_NORMAL && Scen.Scenario < _themes[index].Scenario) return(false); + + /* + ** Since all tests passed, return with the "is allowed" flag. + */ + return(true); +} + + +/*********************************************************************************************** + * ThemeClass::From_Name -- Determines theme number from specified name. * + * * + * Use this routine to convert a name (either the base filename of the theme, or a partial * + * substring of the full name) into the matching ThemeType value. Typical use of this is * + * when parsing the INI file for theme control values. * + * * + * INPUT: name -- Pointer to base filename of theme or a partial substring of the full * + * theme name. * + * * + * OUTPUT: Returns with the matching theme number. If no match could be found, then * + * THEME_NONE is returned. * + * * + * WARNINGS: If a filename is specified the comparison is case insensitive. When scanning * + * the full theme name, the comparison is case sensitive. * + * * + * HISTORY: * + * 05/29/1995 JLB : Created. * + *=============================================================================================*/ +ThemeType ThemeClass::From_Name(char const * name) const +{ + if (name && strlen(name) > 0) { + /* + ** First search for an exact name match with the filename + ** of the theme. This is guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (stricmp(_themes[theme].Name, name) == 0) { + return(theme); + } + } + + /* + ** If the filename scan failed to find a match, then scan for + ** a substring within the full name of the score. This might + ** yield a match, but is not guaranteed to be unique. + */ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + if (strstr(Text_String(_themes[theme].Fullname), name) != NULL) { + return(theme); + } + } + } + + return(THEME_NONE); +} + + +/*********************************************************************************************** + * ThemeClass::Scan -- Scans all scores for availability. * + * * + * This routine should be called whenever a score mixfile is registered. It will scan * + * to see if any score is unavailable. If this is the case, then the score will be so * + * flagged in order not to appear on the play list. This condition is likely to occur * + * when expansion mission disks contain a different score mix than the release version. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/04/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Scan(void) +{ + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + _themes[theme].Available = CCFileClass(Theme_File_Name(theme)).Is_Available(); + } +} + + + +/*********************************************************************************************** + * ThemeClass::Set_Theme_Data -- Set the theme data for scenario and owner. * + * * + * This is an override function used to set a particular theme's initial scenario and * + * owner values. Typically, the rules control file will be the source of calling this * + * routine. * + * * + * INPUT: theme -- The theme to set these override values for. * + * * + * scenario -- The first scenario when this theme becomes available on the play list. * + * * + * owners -- A bitfield representing the owners allowed to play this song. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/12/1996 JLB : Created. * + *=============================================================================================*/ +void ThemeClass::Set_Theme_Data(ThemeType theme, int scenario, int owners) +{ + if (theme != THEME_NONE) { + _themes[theme].Normal = true; + _themes[theme].Scenario = scenario; + _themes[theme].Owner = owners; + } +} + diff --git a/code/redalert/theme.h b/code/redalert/theme.h new file mode 100644 index 0000000..5c76520 --- /dev/null +++ b/code/redalert/theme.h @@ -0,0 +1,89 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/THEME.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : THEME.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 14, 1994 * + * * + * Last Update : August 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef THEME_H +#define THEME_H + +class ThemeClass +{ + friend const char* GetThemeMusicFileName(int index); + + private: + static char const * Theme_File_Name(ThemeType theme); + + int Current; // Handle to current score. + ThemeType Score; // Score number currently being played. + ThemeType Pending; // Score to play next. + + typedef struct { + char const * Name; // Filename of score. + int Fullname; // Text number for full score name. + int Scenario; // Scenario when it first becomes available. + int Duration; // Duration of theme in seconds. + bool Normal; // Allowed in normal game play? + bool Repeat; // Always repeat this score? + bool Available; // Is the score available? + int Owner; // What houses are allowed to play this theme (bit field)? + } ThemeControl; + + static ThemeControl _themes[THEME_COUNT]; + + enum { + THEME_DELAY=TIMER_SECOND + }; + + public: + ThemeClass(void); + + ThemeType From_Name(char const * name) const; + ThemeType Next_Song(ThemeType index) const; + ThemeType What_Is_Playing(void) const {return Score;} + bool Is_Allowed(ThemeType index) const; + bool Is_Regular(ThemeType theme) const {return(theme != THEME_NONE && _themes[theme].Normal);} + char const * Base_Name(ThemeType index) const; + char const * Full_Name(ThemeType index) const; + int Max_Themes(void) const {return THEME_COUNT;} + int Play_Song(ThemeType index); + int Still_Playing(void) const; + int Track_Length(ThemeType index) const; + static void Scan(void); + void AI(void); + void Fade_Out(void) {Queue_Song(THEME_QUIET);} + void Queue_Song(ThemeType index); + void Set_Theme_Data(ThemeType theme, int scenario, int owners); + void Stop(void); + void Suspend(void); +}; + +#endif diff --git a/code/redalert/tileset.cpp b/code/redalert/tileset.cpp new file mode 100644 index 0000000..0929f67 --- /dev/null +++ b/code/redalert/tileset.cpp @@ -0,0 +1,436 @@ +/** + * @file + * + * @author CCHyper + * @author OmniBlade + * + * @brief Low level functions for loading and rendering C&C terrain tilesets. + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ + /* + ============================ + + This file has been heavily modified by IceColdDuke(Justin Marshall) to render with OpenGL. + + ============================ + */ + +#include "FUNCTION.H" +#include "gbuffer.h" +#include "Image.h" +#include + +using std::memcpy; + +class GraphicViewPortClass; + +#define TD_TILESET_CHECK 0x20 + +/** +* @brief union is to handle the parts of the header which vary between TD and RA format tiles. +*/ +struct IconControlType +{ + uint8_t* Get_Icon_Data() + { + if (td.icons == TD_TILESET_CHECK) { + return reinterpret_cast(this) + td.icons; + } + else { + return reinterpret_cast(this) + ra.icons; + } + } + + uint8_t* Get_Icon_Map() + { + if (td.icons == TD_TILESET_CHECK) { + return reinterpret_cast(this) + td.map; + } + else { + return reinterpret_cast(this) + ra.map; + } + } + + int16_t width; // always 24 (ICON_WIDTH) + int16_t height; // always 24 (ICON_HEIGHT) + int16_t count; // count of cells in set, not same as images + int16_t allocated; // is treated like a bool, always 0 in the file? + + union + { + struct + { + int32_t size; // filesize + int32_t icons; // always 0x00000020 + int32_t palettes; // seems to always be 0x00000000 + int32_t remaps; // unknown, bitfield? + int32_t trans_flag; // array of images length, unknown + int32_t map; // image index for each cell + } td; + + struct + { + int16_t map_width; // tile width in cells + int16_t map_height; // tile height in cells + int32_t size; // filesize + int32_t icons; // always 0x00000028 + int32_t palettes; // seems to always be 0x00000000 + int32_t remaps; // unknown, bitfield? + int32_t trans_flag; // array of images length, unknown + int32_t color_map; // terrain type index, ra only + int32_t map; // image index for each cell + } ra; + }; +}; + +static int IconEntry; +static void* IconData; +static IconControlType* LastIconset; +static uint8_t* StampPtr; +static uint8_t* TransFlagPtr; +static uint8_t* MapPtr; +static int IconWidth; +static int IconHeight; +static int IconSize; +static int IconCount; + +void __cdecl Init_Stamps(IconControlType* iconset) +{ + if (iconset && LastIconset != iconset) { + IconCount = (iconset->count); + IconWidth = (iconset->width); + IconHeight = (iconset->height); + LastIconset = iconset; + IconSize = IconWidth * IconHeight; + + // TD and RA tileset headers are slightly different, so check a constant that only exists in one type. + if ((iconset->td.icons) == TD_TILESET_CHECK) { + MapPtr = reinterpret_cast(iconset) + (iconset->td.map); + StampPtr = reinterpret_cast(iconset) + (iconset->td.icons); + TransFlagPtr = reinterpret_cast(iconset) + (iconset->td.trans_flag); + } + else { + MapPtr = reinterpret_cast(iconset) + (iconset->ra.map); + StampPtr = reinterpret_cast(iconset) + (iconset->ra.icons); + TransFlagPtr = reinterpret_cast(iconset) + (iconset->ra.trans_flag); + } + } +} + +void __cdecl Buffer_Draw_Stamp2(GraphicViewPortClass& viewport, IconControlType* tileset, int icon, int x, int y, const void* remapper) +{ + if (!tileset) { + return; + } + + if (LastIconset != tileset) { + Init_Stamps(tileset); + } + + int32_t icon_index = MapPtr != nullptr ? MapPtr[icon] : icon; + + if (icon_index < IconCount) { + + int32_t fullpitch = viewport.Get_Full_Pitch(); //(viewport.Get_Pitch() + viewport.Get_XAdd() + viewport.Get_Width()); + uint8_t* dst = (x * 4) + (y * 4) * fullpitch + (uint8_t*)(viewport.Get_Offset()); + int32_t blitpitch = fullpitch - IconWidth; + uint8_t* src = &StampPtr[IconSize * icon_index]; + + if (remapper) { + const uint8_t* remap = static_cast(remapper); + for (int i = 0; i < IconHeight; ++i) { + for (int j = 0; j < IconWidth; ++j) { + uint8_t cur_byte = remap[*src++]; + + if (cur_byte) { + dst[0] = backbuffer_data_raw[(cur_byte * 3) + 0]; + dst[1] = backbuffer_data_raw[(cur_byte * 3) + 1]; + dst[2] = backbuffer_data_raw[(cur_byte * 3) + 2]; + dst[3] = 255; + } + + dst+=4; + } + + dst += blitpitch; + } + + } + else if (TransFlagPtr[icon_index]) { + for (int i = 0; i < IconHeight; ++i) { + for (int j = 0; j < IconWidth; ++j) { + uint8_t cur_byte = *src++; + + if (cur_byte) { + dst[0] = backbuffer_data_raw[(cur_byte * 3) + 0]; + dst[1] = backbuffer_data_raw[(cur_byte * 3) + 1]; + dst[2] = backbuffer_data_raw[(cur_byte * 3) + 2]; + dst[3] = 255; + } + + dst += 4; + } + + dst += blitpitch; + } + } + else { + for (int32_t i = 0; i < IconHeight; ++i) { + FastScanlinePaletteBlit(dst, src, IconWidth); + dst += fullpitch; + src += IconWidth * 4; + } + } + } +} + +Image_t* Load_StampHD(int theater, const char* iconName, const void* icondata) { + char filename[2048]; + Image_t* image = NULL; + + IconControlType* tileset = (IconControlType*)icondata; + + if (!tileset) { + return NULL; + } + + if (LastIconset != tileset) { + Init_Stamps(tileset); + } + + // This is because were trying to avoid parsing the XML's and the filenames aren't consistant. + const char* theaterName = Theaters[theater].Name; + for (int i = 0; i < IconCount; i++) + { + char filename[512]; + char fixedIconName[512]; + strcpy(fixedIconName, iconName); + COM_SetExtension(fixedIconName, strlen(fixedIconName), ""); + + int numFrames = Tileset_GetNumFramesForTile(theater, fixedIconName, i); + + for (int d = 0; d < numFrames; d++) + { + const char* tileEntryName = Tileset_FindHDTexture(theater, fixedIconName, i, d); + sprintf(filename, "DATA/ART/TEXTURES/SRGB/RED_ALERT/TERRAIN/%s/%s", Theaters[theater].Name, tileEntryName); + COM_SetExtension(filename, sizeof(filename), ".dds"); + + if (image == NULL) { + image = Image_LoadImage(filename); + + if (image != NULL && i != 0) { + image->HouseImages[0].image[i][d] = image->HouseImages[0].image[0][d]; + image->HouseImages[0].image[0][d] = 0; + } + + if (image != NULL) { + image->numFrames = numFrames; + } + } + else { + if (!Image_Add32BitImage(filename, image, -1, i, d)) { + continue; + } + } + } + } + + //if (image) { + // image->IconMapPtr = MapPtr; + //} + + return image; +} + +void Get_Stamp_Size(const void* icondata, int* width, int* height) { + IconControlType* tileset = (IconControlType*)icondata; + + if (!tileset) { + for (int i = 0; i < MAX_IMAGE_SHAPES; i++) { + *width = -1; + *height = -1; + } + return; + } + + if (LastIconset != tileset) { + Init_Stamps(tileset); + } + + for (int i = 0; i < MAX_IMAGE_SHAPES; i++) { + width[i] = IconWidth; + height[i] = IconHeight; + } +} + +Image_t * Load_Stamp(const char *name, const void* icondata) +{ + IconControlType* tileset = (IconControlType*)icondata; + + if (!tileset) { + return NULL; + } + + if (LastIconset != tileset) { + Init_Stamps(tileset); + } + + + int icon_index = 0; + + if (icon_index >= IconCount) { + return NULL; + } + + uint8_t* src = &StampPtr[IconSize * icon_index]; + + // Check to see if the image is already loaded. + { + Image_t* image = Find_Image(name); + if (image) { + return image; + } + } + + Image_t *image = Image_CreateImageFrom8Bit(name, IconWidth, IconHeight, (unsigned char*)src); + image->IconMapPtr = MapPtr; + if (IconCount == 1) + return image; + + for (int i = 1; i < IconCount; i++) { + icon_index = i; + src = &StampPtr[IconSize * icon_index]; + Image_Add8BitImage(image, 0, i, IconWidth, IconHeight, src, NULL); + } + + return image; +} + +void __cdecl Buffer_Draw_Stamp_Clip2(GraphicViewPortClass& viewport, const void *icondata, int icon, int x, int y, const void* remapper, int left, int top, int right, int bottom) +{ + Image_t* iconImage = (Image_t*)icondata; + uint8_t* MapPtr = (uint8_t * )iconImage->IconMapPtr; + int icon_index = MapPtr != nullptr ? MapPtr[icon] : icon; + { + int blit_height = iconImage->renderheight[0]; + int blit_width = iconImage->renderwidth[0]; + + int width = left + right; + int xstart = left + x; + int height = top + bottom; + int ystart = top + y; + + + if (xstart < width && ystart < height && IconHeight + ystart > top && IconWidth + xstart > left) { + if (xstart < left) { +// src += left - xstart; + blit_width -= left - xstart; + xstart = left; + } + + int src_pitch = IconWidth - blit_width; + + if (blit_width + xstart > width) { + src_pitch += blit_width - (width - xstart); + blit_width = width - xstart; + } + + if (top > ystart) { + blit_height = IconHeight - (top - ystart); + // src += IconWidth * (top - ystart); + ystart = top; + } + + if (blit_height + ystart > height) { + blit_height = height - ystart; + } + + int full_pitch = viewport.Get_Full_Pitch(); //(viewport.Get_Pitch() + viewport.Get_XAdd() + viewport.Get_Width()); + uint8_t* dst = (xstart * 4) + (ystart * 4) * full_pitch + (uint8_t*)(viewport.Get_Offset()); + int dst_pitch = full_pitch - blit_width; + + //if (remapper) { + // const uint8_t* remap = static_cast(remapper); + // for (int i = 0; i < blit_height; ++i) { + // for (int j = 0; j < blit_width; ++j) { + // uint8_t cur_byte = remap[*src++]; + // if (cur_byte) { + // dst[0] = backbuffer_data_raw[(cur_byte * 3) + 0]; + // dst[1] = backbuffer_data_raw[(cur_byte * 3) + 1]; + // dst[2] = backbuffer_data_raw[(cur_byte * 3) + 2]; + // dst[3] = 255; + // } + // + // dst += 4; + // } + // dst += dst_pitch * 4; + // } + // + //} + //else if (TransFlagPtr[icon_index]) { + // for (int i = 0; i < blit_height; ++i) { + // for (int j = 0; j < blit_width; ++j) { + // uint8_t cur_byte = *src++; + // if (cur_byte) { + // dst[0] = backbuffer_data_raw[(cur_byte * 3) + 0]; + // dst[1] = backbuffer_data_raw[(cur_byte * 3) + 1]; + // dst[2] = backbuffer_data_raw[(cur_byte * 3) + 2]; + // dst[3] = 255; + // } + // + // dst += 4; + // } + // src += src_pitch; + // dst += dst_pitch * 4; + // } + // + //} + //else { + { + //for (int i = 0; i < blit_height; ++i) { + // FastScanlinePaletteBlit(dst, src, blit_width); + // dst += full_pitch * 4; + // src += IconWidth; + //} + + if (iconImage->HouseImages[0].image[icon_index] == 0) + return; + + GL_SetClipRect(xstart, ystart, blit_width, blit_height); + GL_RenderImage(iconImage, xstart, ystart, blit_width, blit_height, 0, icon_index); + GL_ResetClipRect(); + } + } + } +} + +extern "C" void __cdecl Buffer_Draw_Stamp_Clip(void const* this_object, void const* icondata, int icon, int x_pixel, int y_pixel, void const* remap, int min_x, int min_y, int max_x, int max_y) { + if (icondata == NULL) + return; + + Buffer_Draw_Stamp_Clip2(*((GraphicViewPortClass*)this_object), icondata, icon, x_pixel, y_pixel, remap, min_x, min_y, max_x, max_y); +} + +extern "C" void __cdecl Buffer_Draw_Stamp(void const* thisptr, void const* icondata, int icon, int x_pixel, int y_pixel, void const* remap) { + Buffer_Draw_Stamp2(*((GraphicViewPortClass*)thisptr), (IconControlType*)icondata, icon, x_pixel, y_pixel, remap); +} + +uint8_t* Get_Icon_Set_Map(void* temp) +{ + if (temp != nullptr) { + if ((static_cast(temp)->td.icons) == TD_TILESET_CHECK) { + return static_cast(temp) + (static_cast(temp)->td.icons); + } + else { + return static_cast(temp) + (static_cast(temp)->ra.icons); + } + } + + return nullptr; +} diff --git a/code/redalert/tilesetxml.cpp b/code/redalert/tilesetxml.cpp new file mode 100644 index 0000000..7dd10e9 --- /dev/null +++ b/code/redalert/tilesetxml.cpp @@ -0,0 +1,89 @@ +// TILESETXML.CPP +// + +#include "FUNCTION.H" +#include "tinyxml2.h" + +#include +#include + +int64_t generateHashValue(const char* fname, const int size); + +struct TilesetTileRule_t { + std::string name; + int64_t hash; + int shape; + std::vector frames; +}; + +struct TilesetXMLInfo_t { + std::vector< TilesetTileRule_t> tiles; +}; + +TilesetXMLInfo_t theaterXMLRules[3]; + +const char* Tileset_FindHDTexture(int theaterType, const char* shapeFileName, int shapeNum, int frameNum) { + int64_t hash = generateHashValue(shapeFileName, strlen(shapeFileName)); + for (int i = 0; i < theaterXMLRules[theaterType].tiles.size(); i++) { + if (theaterXMLRules[theaterType].tiles[i].hash == hash && theaterXMLRules[theaterType].tiles[i].shape == shapeNum) { + return theaterXMLRules[theaterType].tiles[i].frames[frameNum].c_str(); + } + } + return NULL; +} + +int Tileset_GetNumFramesForTile(int theaterType, const char* shapeFileName, int shapeNum) { + int64_t hash = generateHashValue(shapeFileName, strlen(shapeFileName)); + for (int i = 0; i < theaterXMLRules[theaterType].tiles.size(); i++) { + if (theaterXMLRules[theaterType].tiles[i].hash == hash && theaterXMLRules[theaterType].tiles[i].shape == shapeNum) { + return theaterXMLRules[theaterType].tiles[i].frames.size(); + } + } + return 0; +} + +void Tileset_ParseTile(tinyxml2::XMLNode* tile, TilesetTileRule_t& tileRule) { + tinyxml2::XMLNode* KeyNode = tile->FirstChildElement("Key"); + tinyxml2::XMLNode* NameNode = KeyNode->FirstChildElement("Name"); + tinyxml2::XMLNode* ShapeNode = KeyNode->FirstChildElement("Shape"); + tinyxml2::XMLNode* ValueNode = tile->FirstChildElement("Value"); + tinyxml2::XMLNode* FramesNode = ValueNode->FirstChildElement("Frames"); + tinyxml2::XMLNode* FrameNode = FramesNode->FirstChildElement("Frame"); + + tileRule.name = NameNode->FirstChild()->ToText()->Value(); + tileRule.hash = generateHashValue(tileRule.name.c_str(), tileRule.name.size()); + tileRule.shape = atoi(ShapeNode->FirstChild()->ToText()->Value()); + + while (FrameNode != NULL) { + tileRule.frames.push_back(FrameNode->FirstChild()->ToText()->Value()); + FrameNode = FrameNode->NextSiblingElement("Frame"); + } +} + +void Tileset_LoadRuleXML(const char* path, TilesetXMLInfo_t& info) { + tinyxml2::XMLDocument doc; + doc.LoadFile(path); + + tinyxml2::XMLElement* root = doc.FirstChildElement(); + if (root == NULL) + return; + + tinyxml2::XMLNode* tilesetTypeClassNode = root->FirstChild(); + + tinyxml2::XMLNode* tilesParent = tilesetTypeClassNode->FirstChildElement("Tiles"); + tinyxml2::XMLNode* tile = tilesParent->FirstChildElement("Tile"); + while (tile != NULL) { + TilesetTileRule_t tileRule; + Tileset_ParseTile(tile, tileRule); + info.tiles.push_back(tileRule); + tile = tile->NextSiblingElement("Tile"); + } +} + +void Tileset_LoadRules(void) { + for (int i = 0; i < 3; i++) { + char tmp[512]; + sprintf(tmp, "data/xml/tilesets/ra_terrain_%s.xml", Theaters[i].Name); + Tileset_LoadRuleXML(tmp, theaterXMLRules[i]); + } +} \ No newline at end of file diff --git a/code/redalert/toggle.cpp b/code/redalert/toggle.cpp new file mode 100644 index 0000000..7e1f537 --- /dev/null +++ b/code/redalert/toggle.cpp @@ -0,0 +1,198 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TOGGLE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : February 2, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "toggle.h" + + +/*********************************************************************************************** + * ToggleClass::ToggleClass -- Normal constructor for toggle button gadgets. * + * * + * This is the normal constructor for toggle buttons. A toggle button is one that most * + * closely resembles the Windows style. It has an up and down state as well as an on * + * and off state. * + * * + * INPUT: id -- ID number for this button. * + * * + * x,y -- Pixel coordinate of upper left corner of this button. * + * * + * w,h -- Width and height of the button. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +ToggleClass::ToggleClass(unsigned id, int x, int y, int w, int h) : + ControlClass(id, x, y, w, h, LEFTPRESS|LEFTRELEASE, true), + IsPressed(false), + IsOn(false), + IsToggleType(false) +{ +} + + +/*********************************************************************************************** + * ToggleClass::Turn_On -- Turns the toggle button to the "ON" state. * + * * + * This routine will turn the button on. The button will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_On(void) +{ + IsOn = true; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Turn_Off -- Turns the toggle button to the "OFF" state. * + * * + * This routine will turn the toggle button "off". It will also be flagged to be redrawn * + * at the next opportunity. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/15/1995 JLB : Created. * + *=============================================================================================*/ +void ToggleClass::Turn_Off(void) +{ + IsOn = false; + Flag_To_Redraw(); +} + + +/*********************************************************************************************** + * ToggleClass::Action -- Handles mouse clicks on a text button. * + * * + * This routine will process any mouse or keyboard event that is associated with this * + * button object. It detects and flags the text button so that it will properly be drawn * + * in a pressed or raised state. It also handles any toggle state for the button. * + * * + * INPUT: flags -- The event flags that triggered this button. * + * * + * key -- The keyboard code associated with this event. Usually this is KN_LMOUSE * + * or similar, but it could be a regular key if this text button is given * + * a hotkey. * + * * + * OUTPUT: Returns whatever the lower level processing for buttons decides. This is usually * + * true. * + * * + * WARNINGS: The button is flagged to be redrawn by this routine. * + * * + * HISTORY: * + * 01/14/1995 JLB : Created. * + * 02/02/1995 JLB : Left press doesn't get passed to other buttons now * + *=============================================================================================*/ +int ToggleClass::Action(unsigned flags, KeyNumType &key) +{ + /* + ** If there are no action flag bits set, then this must be a forced call. A forced call + ** must never actually function like a real call, but rather only performs any necessary + ** graphic updating. + */ + bool overbutton = ((Get_Mouse_X() - X) < Width && (Get_Mouse_Y() - Y) < Height ); + if (!flags) { + if (overbutton) { + if (!IsPressed) { + IsPressed = true; + Flag_To_Redraw(); + } + } else { + if (IsPressed) { + IsPressed = false; + Flag_To_Redraw(); + } + } + } + + /* + ** Handle the sticky state for this gadget. It must be processed here + ** because the event flags might be cleared before the action function + ** is called. + */ + Sticky_Process(flags); + + /* + ** Flag the button to show the pressed down imagery if this mouse button + ** was pressed over this gadget. + */ + if (flags & LEFTPRESS) { + IsPressed = true; + Flag_To_Redraw(); + flags &= ~LEFTPRESS; + ControlClass::Action(flags, key); + key = KN_NONE; // erase the event + return(true); // stop processing other buttons now + } + + if (flags & LEFTRELEASE) { + if (IsPressed) { + if (IsToggleType && overbutton) { + IsOn = (IsOn == false); + } + IsPressed = false; + Flag_To_Redraw(); + } else { + flags &= ~LEFTRELEASE; + } + } + + /* + ** Do normal button processing. This ends up causing the button's ID number to + ** be returned from the controlling Input() function. + */ + return(ControlClass::Action(flags, key)); +} + + diff --git a/code/redalert/toggle.h b/code/redalert/toggle.h new file mode 100644 index 0000000..4c4a5bb --- /dev/null +++ b/code/redalert/toggle.h @@ -0,0 +1,79 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TOGGLE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TOGGLE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 01/15/95 * + * * + * Last Update : January 15, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TOGGLE_H +#define TOGGLE_H + +#include "control.h" + +/* +** This class handles gadgets that behave like the Windows buttons. That is, once the mouse +** button is clicked over them, they capture the focus until the mouse button is released. +** They have a different imagery for the pressed and released states. They only recognize +** a valid selection when the mouse button is release while over the button. +*/ +class ToggleClass : public ControlClass +{ + public: + ToggleClass(unsigned id, int x, int y, int w, int h); + void Turn_On(void); + void Turn_Off(void); + + /* + ** Is this button in a pressed down state? This occurs when the mouse is clicked on the + ** button and the mouse is still being held down. + */ + unsigned IsPressed:1; + + /* + ** This is the button on/off state. Sometimes a button that is "on" has a different + ** imagery than one that is "off". If the on/off state is not necessary, then just + ** ignore this flag. + */ + unsigned IsOn:1; + + /* + ** If this button can be turned "on" or "off", then this flag should be set to + ** true. Sometimes a button needs to display its on/off state. In the render routine, + ** examine the IsOn flag and display accordingly. If this flag is false, then the + ** IsOn flag will not be changed, regardless of button clicking. + */ + unsigned IsToggleType:1; + + protected: + + virtual int Action(unsigned flags, KeyNumType &key); +}; + +#endif diff --git a/code/redalert/tooltip.cpp b/code/redalert/tooltip.cpp new file mode 100644 index 0000000..9d786fc --- /dev/null +++ b/code/redalert/tooltip.cpp @@ -0,0 +1,294 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// ToolTip.cpp +#if (0)//PG +#include "function.h" +#include "ToolTip.h" +#include "IconList.h" + +//#include "WolDebug.h" + +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ); +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ); + +//*********************************************************************************************** +ToolTipClass::ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, + bool bRightAlign /* = false */, bool bIconList /*= false */ ) + : pGadget( pGadget ), xShow( xShow ), yShow( yShow ), next( NULL ), bShowing( false ), bIconList( bIconList ), + bRightAlign( bRightAlign ) + +{ + if( szText ) + { + if( strlen( szText ) > TOOLTIPTEXT_MAX_LEN ) + strcpy( szTip, "Tooltip too long!" ); + else + strcpy( szTip, szText ); + } + else + *szTip = 0; + + Set_Font( TypeFontPtr ); + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShow = String_Pixel_Width( szTip ) + 2; + hShow = 11; + + if( !bIconList ) + { + pSaveRect = new char[ wShow * hShow ]; // Else it is reallocated on every draw. + if( bRightAlign ) + this->xShow -= wShow; + } + else + pSaveRect = NULL; + + // bIconList is true if tooltips appear for individual line items in an iconlist. + // szText in this case is ignored. + // yShow is the y position of the top row's tooltip - other rows will be offset from here. +} + +//*********************************************************************************************** +ToolTipClass* ToolTipClass::GetToolTipHit() +{ + // Returns 'this' if the mouse is over gadget bound to tooltip. + // Otherwise calls the same function in the next tooltip in the list of which *this is a part. + if( bGadgetHit() ) + return this; + else if( next ) + return next->GetToolTipHit(); + else + return NULL; +} + +//*********************************************************************************************** +bool ToolTipClass::bGadgetHit() +{ + // Returns true if the mouse is currently over the gadget to which *this is bound. + int x = Get_Mouse_X(); + int y = Get_Mouse_Y(); + return ( x > pGadget->X && x < pGadget->X + pGadget->Width && y > pGadget->Y && y < pGadget->Y + pGadget->Height ); +} + +//*********************************************************************************************** +void ToolTipClass::Move( int xShow, int yShow ) +{ + bool bRestoreShow = false; + if( bShowing ) + { + bRestoreShow = true; + Unshow(); + } + this->xShow = xShow; + if( !bIconList ) + { + if( bRightAlign ) + this->xShow -= wShow; + } + this->yShow = yShow; + if( bRestoreShow ) + Show(); +} + +//*********************************************************************************************** +void ToolTipClass::Show() +{ + if( !bShowing ) + { + Set_Font( TypeFontPtr ); + int xShowUse = xShow, yShowUse, wShowUse; + const char* szTipUse; + if( !bIconList ) + { + yShowUse = yShow; + wShowUse = wShow; + szTipUse = szTip; + } + else + { + IconListClass* pIconList = (IconListClass*)pGadget; + iLastIconListIndex = pIconList->IndexUnderMouse(); + if( iLastIconListIndex < 0 ) + { + // Nothing to show. + bShowing = true; + return; + } + yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); + szTipUse = pIconList->Get_Item_Help( iLastIconListIndex ); + if( !szTipUse || *szTipUse == 0 ) + { + // Nothing to show. + bShowing = true; + bLastShowNoText = true; + return; + } + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. + wShowUse = String_Pixel_Width( szTipUse ) + 2; + if( bRightAlign ) + xShowUse -= wShowUse; + delete [] pSaveRect; + pSaveRect = new char[ wShowUse * hShow ]; + bLastShowNoText = false; + xLastShow = xShowUse; + yLastShow = yShowUse; + wLastShow = wShowUse; + } + + // Save rect about to be corrupted. + Hide_Mouse(); + SaveSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + // Draw text. + //Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), ColorRemaps[ PCOLOR_BROWN ].Color, TPF_TYPE ); //TPF_DROPSHADOW ); + Simple_Text_Print( szTipUse, xShowUse, yShowUse, GadgetClass::Get_Color_Scheme(), BLACK, TPF_TYPE ); //TPF_DROPSHADOW ); + // Draw bounding rect. +// LogicPage->Draw_Rect( xShowUse, yShowUse, xShowUse + wShowUse - 1, yShowUse + hShow - 1, ColorRemaps[ PCOLOR_GOLD ].Color ); + Draw_Box( xShowUse, yShowUse, wShowUse, hShow, BOXSTYLE_BOX, false ); + Show_Mouse(); + bShowing = true; + } +} + +//*********************************************************************************************** +void ToolTipClass::Unshow() +{ + if( bShowing ) + { + int xShowUse, yShowUse, wShowUse; + if( !bIconList ) + { + xShowUse = xShow; + wShowUse = wShow; + yShowUse = yShow; + } + else + { + if( iLastIconListIndex == -1 || bLastShowNoText ) + { + // Nothing to restore. + bShowing = false; + return; + } + // (Can't rely on iconlist being the same as when Show() occurred.) +// IconListClass* pIconList = (IconListClass*)pGadget; +// yShowUse = pIconList->OffsetToIndex( iLastIconListIndex, yShow ); +// const char* szTipUsed = pIconList->Get_Item_Help( iLastIconListIndex ); +// if( !szTipUsed || *szTipUsed == 0 ) +// { +// // Nothing to restore. +// bShowing = false; +// return; +// } +// Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TYPE ); // Required before String_Pixel_Width() call, for god's sake. +// wShowUse = String_Pixel_Width( szTipUsed ) + 2; +// if( bRightAlign ) +// xShowUse -= wShowUse; + xShowUse = xLastShow; + yShowUse = yLastShow; + wShowUse = wLastShow; + } + Hide_Mouse(); + RestoreSurfaceRect( xShowUse, yShowUse, wShowUse, hShow, pSaveRect, WINDOW_MAIN ); + Show_Mouse(); + bShowing = false; + } +} + +//*********************************************************************************************** +bool ToolTipClass::bOverDifferentLine() const +{ + // bIconList must be true if this is being used. + // Returns true if the iconlist line that the mouse is over is different than the last time Show() was called. + return ( ((IconListClass*)pGadget)->IndexUnderMouse() != iLastIconListIndex ); +} + +//*********************************************************************************************** +bool SaveSurfaceRect( int xRect, int yRect, int wRect, int hRect, char* pBits, WindowNumberType window ) +{ + // Saves a rect of the LogicPage DirectDraw surface to pBits. +// if( wRect * hRect > iBufferSize ) +// { +// debugprint( "SaveSurfaceRect failed.\n" ); +// return false; +// } + + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + const char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + const char* pSurf = pLineSurf; + char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSave++ = *pSurf++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "SaveSurfaceRect Could not lock surface.\n" ); + return false; + } +} + +//*********************************************************************************************** +bool RestoreSurfaceRect( int xRect, int yRect, int wRect, int hRect, const char* pBits, WindowNumberType window ) +{ + // Copies a saved rect of bits back to the LogicPage DD surface. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[window][WINDOWX] + LogicPage->Get_XPos(), + WindowList[window][WINDOWY] + LogicPage->Get_YPos(), + WindowList[window][WINDOWWIDTH], + WindowList[window][WINDOWHEIGHT] ); + if( draw_window.Lock() ) + { + int iPitchSurf = draw_window.Get_Pitch() + draw_window.Get_Width(); // Meaning of "Pitch" in this class seems to mean the eol skip. + char* pLineSurf = (char*)draw_window.Get_Offset() + xRect + yRect * iPitchSurf; + const char* pLineSave = pBits; + + // ajw - Should copy DWORDs here instead for speed. + for( int y = 0; y != hRect; y++ ) + { + char* pSurf = pLineSurf; + const char* pSave = pLineSave; + for( int x = 0; x != wRect; x++ ) + *pSurf++ = *pSave++; + + pLineSurf += iPitchSurf; + pLineSave += wRect; + } + draw_window.Unlock(); + return true; + } + else + { +// debugprint( "RestoreSurfaceRect Could not lock surface.\n" ); + return false; + } +} +#endif \ No newline at end of file diff --git a/code/redalert/tooltip.h b/code/redalert/tooltip.h new file mode 100644 index 0000000..718174b --- /dev/null +++ b/code/redalert/tooltip.h @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// ToolTip.h + +#ifdef WOLAPI_INTEGRATION + +#ifndef TOOLTIP_H +#define TOOLTIP_H + +#include "Gadget.h" + +#define TOOLTIPTEXT_MAX_LEN 100 + +#define TOOLTIPDELAY 400 // Milliseconds + +class ToolTipClass +{ +public: + ToolTipClass( GadgetClass* pGadget, const char* szText, int xShow, int yShow, bool bRightAlign = false, bool bIconList = false ); + ~ToolTipClass() + { + delete [] pSaveRect; + } + + ToolTipClass* GetToolTipHit(); + void Show(); + void Unshow(); + void Move( int xShow, int yShow ); + bool bOverDifferentLine() const; + + ToolTipClass* next; // Next tooltip in list of which *this is a part. + + GadgetClass* pGadget; // Gadget to which this tooltip is bound. + + bool bRightAlign; + + bool bShowing; + bool bIconList; // True if gadget is iconlist and line-specific tooltips are to be used. + +protected: + bool bGadgetHit() const; + + int xShow; + int yShow; + int wShow; + int hShow; + char szTip[ TOOLTIPTEXT_MAX_LEN + 1 ]; // Text to show as tip. + + char* pSaveRect; + + // Used only if bIconList. + int iLastIconListIndex; + bool bLastShowNoText; + int xLastShow; + int yLastShow; + int wLastShow; +}; + +#endif + +#endif diff --git a/code/redalert/tracker.cpp b/code/redalert/tracker.cpp new file mode 100644 index 0000000..0a4e1c3 --- /dev/null +++ b/code/redalert/tracker.cpp @@ -0,0 +1,131 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRACKER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRACKER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/14/96 * + * * + * Last Update : June 14, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Detach_This_From_All -- Detaches this object from all others. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * Detach_This_From_All -- Detaches this object from all others. * + * * + * This routine sweeps through all game objects and makes sure that it is no longer * + * referenced by them. Typically, this is called in preparation for the object's death * + * or limbo state. * + * * + * INPUT: target -- This object expressed as a target number. * + * * + * all -- Is this object really in truly being removed from the game? The * + * answer would be false if the target was actually a stealth * + * tank that is cloaking. In such a case, the object should be removed * + * from all non-friendly tracking systems, but otherwise left alone. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Detach_This_From_All(TARGET target, bool all) +{ + int index; + if (Target_Legal(target)) { + + for (index = 0; index < Houses.Count(); index++) { + Houses.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Teams.Count(); index++) { + Teams.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TeamTypes.Count(); index++) { + TeamTypes.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Units.Count(); index++) { + Units.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Vessels.Count(); index++) { + Vessels.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Aircraft.Count(); index++) { + Aircraft.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Buildings.Count(); index++) { + Buildings.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Bullets.Count(); index++) { + Bullets.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Infantry.Count(); index++) { + Infantry.Ptr(index)->Detach(target, all); + } + for (index = 0; index < Anims.Count(); index++) { + Anims.Ptr(index)->Detach(target, all); + } + + Map.Detach(target, all); + + Logic.Detach(target, all); + + ChronalVortex.Detach(target); + + /* + ** Removing a trigger type must also remove all triggers that are dependant + ** upon that type. + */ + if (As_TriggerType(target) != NULL) { + for (int index = 0; index < Triggers.Count(); index++) { + TriggerClass * tp = Triggers.Ptr(index); + + if (tp->Class->As_Target() == target) { + Detach_This_From_All(tp->As_Target()); + delete tp; + index--; + } + } + } + + for (index = 0; index < Triggers.Count(); index++) { + Triggers.Ptr(index)->Detach(target, all); + } + for (index = 0; index < TriggerTypes.Count(); index++) { + TriggerTypes.Ptr(index)->Detach(target, all); + } + } +} + + + + + diff --git a/code/redalert/trigger.cpp b/code/redalert/trigger.cpp new file mode 100644 index 0000000..c7c9dcb --- /dev/null +++ b/code/redalert/trigger.cpp @@ -0,0 +1,529 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGGER.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : August 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Find_Or_Make -- Find or create a trigger of the type specified. * + * TriggerClass::As_Target -- Converts trigger to a target value * + * TriggerClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * TriggerClass::Detach -- Detach specified target from this trigger. * + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * TriggerClass::Init -- clears triggers for new scenario * + * TriggerClass::Spring -- Spring the trigger (possibly). * + * TriggerClass::TriggerClass -- constructor * + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * TriggerClass::operator new -- 'new' operator * + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "MapScript.h" + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerClass::Description -- Fetch a one line ASCII description of the trigger. * + * * + * In the rare (possibly never?) case of requiring a description for this trigger, then * + * just have the model class generate a description and return that. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with an ASCII description of this trigger. * + * * + * WARNINGS: see TriggerTypeClass::Description() * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerClass::Description(void) const +{ + return(Class->Description()); +} + + +/*********************************************************************************************** + * TriggerClass::Draw_It -- Draws this trigger as if it were part of a list box. * + * * + * This routine is called when the trigger has been assigned to a list box. It will * + * display a description of the trigger. * + * * + * INPUT: see below... * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerClass::TriggerClass -- constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +TriggerClass::TriggerClass(TriggerTypeClass * trigtype) : + RTTI(RTTI_TRIGGER), + ID(Triggers.ID(this)), + Class(trigtype), + AttachCount(0), + Cell(0) +{ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); +} + + +/*********************************************************************************************** + * TriggerClass::~TriggerClass -- Destructor for trigger objects. * + * * + * This destructor will update the house blockage value if necessary. No other action need * + * be performed on trigger destruction. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +TriggerClass::~TriggerClass(void) +{ + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_GENERAL) != 0) { + if (LogicTriggerID >= LogicTriggers.ID(this)) { + LogicTriggerID--; + if (LogicTriggerID < 0 && LogicTriggers.Count() == 0) { + LogicTriggerID = 0; + } + } + } + + if (GameActive && Class.Is_Valid() && (Class->Attaches_To() & ATTACH_MAP) != 0) { + if (MapTriggerID >= MapTriggers.ID(this)) { + MapTriggerID--; + if (MapTriggerID < 0 && MapTriggers.Count() == 0) { + MapTriggerID = 0; + } + } + } + + if (GameActive && Class->House != HOUSE_NONE && Class->Action1.Action == TACTION_ALLOWWIN) { + if (Houses.Ptr(Class->House)->Blockage) Houses.Ptr(Class->House)->Blockage--; + Houses.Ptr(Class->House)->BorrowedTime = TICKS_PER_SECOND*4; + } + ID = -1; +} + + +/*********************************************************************************************** + * TriggerClass::Init -- clears triggers for new scenario * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/29/1994 BR : Created. * + *=============================================================================================*/ +void TriggerClass::Init(void) +{ + Triggers.Free_All(); +} + + +/*********************************************************************************************** + * TriggerClass::Spring -- Spring the trigger (possibly). * + * * + * This routine is called when a potential trigger even has occurred. The event is matched * + * with the trigger event needed by this trigger. If the condition warrants, the trigger * + * action is performed. * + * * + * INPUT: event -- The event that is occurring. * + * * + * obj -- If the trigger is attached to an object, this points to the object. * + * * + * cell -- If the trigger is attached to a cell, this is the cell number. * + * * + * forced -- Should the trigger be forced to execute regardless of the event? * + * * + * OUTPUT: bool; Was the trigger sprung? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 JLB : Created. * + * 08/13/1996 JLB : Linked triggers supported. * + *=============================================================================================*/ +bool TriggerClass::Spring(TEventType event, ObjectClass * obj, CELL cell, bool forced) +{ + assert(Triggers.ID(this) == ID); + + bool e1 = Class->Event1(Event1, event, Class->House, obj, forced); + bool e2 = false; + bool execute = false; + bool execute_mapScript = false; + + /* + ** Forced triggers must presume that the cell parameter is invalid. It then + ** uses the embedded cell value in the trigger as the official location where + ** the trigger will detonate at. + */ + if (forced) { + cell = Cell; + } else { + + /* + ** Determine if the trigger event is considered to have been sprung according to the + ** event control value. This might require that both events be triggered, one event + ** triggered, or either event triggered to activate the trigger action. + */ + switch (Class->EventControl) { + case MULTI_ONLY: + execute = e1; + break; + + case MULTI_AND: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 && e2); + break; + + case MULTI_LINKED: + case MULTI_OR: + e2 = Class->Event2(Event2, event, Class->House, obj, forced); + execute = (e1 || e2); + break; + } + } + + /* + ** See if the trigger is sprung with a qualifying event. + */ + if (execute || forced) { + + /* + ** Verify that the trigger event should really be sprung. Exceptions + ** would include semi-persistent triggers that don't actually + ** spring until all triggers have sprung. + */ + if (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT) { + + /* + ** Detach ourselves from the object and record that there + ** is one less attachment to keep track of. + */ + if (obj) { + obj->Trigger = NULL; + } + if (cell) { + Map[cell].Trigger = NULL; + } + + /* + ** If we're attached to more objects, don't spring; otherwise, spring. + ** And, mark ourselves as volatile so we'll completely remove ourselves + ** from the game after we go off. + */ + AttachCount--; + if (AttachCount > 0) { + return(false); + } + } + + /* + ** We need to execute this trigger, so is there an attached MapScript callback? + */ + if (Scen.mapScript != NULL && strlen(MapScriptCallback) > 0) { + execute_mapScript = true; + } + + /* + ** For linked trigger events, perform the action associated with the matching + ** trigger event. Otherwise perform the action for both events. + */ + bool ok = false; + HousesType hh = Class->House; + if (Class->EventControl == MULTI_LINKED) { + if (e1 || forced) { + ok |= Class->Action1(hh, obj, ID, cell); + + // Execute MapScript Callback for action 1? + if (ok && execute_mapScript && MapScriptActionIndex <= 1) { + Scen.mapScript->CallFunction(MapScriptCallback); + } + } + if (e2 && !forced) { + ok |= Class->Action2(hh, obj, ID, cell); + + // Execute MapScript Callback for action 2? + if (ok && execute_mapScript && MapScriptActionIndex != 1) { + Scen.mapScript->CallFunction(MapScriptCallback); + } + } + } else { + + switch (Class->ActionControl) { + case MULTI_ONLY: + ok |= Class->Action1(hh, obj, ID, cell); + + // Execute MapScript Callback for action 1? + if (ok && execute_mapScript && MapScriptActionIndex <= 1) { + Scen.mapScript->CallFunction(MapScriptCallback); + } + + break; + + default: + case MULTI_AND: + ok |= Class->Action1(hh, obj, ID, cell); + ok |= Class->Action2(hh, obj, ID, cell); + + // Execute MapScript Callback for action? + if (ok && execute_mapScript) { + Scen.mapScript->CallFunction(MapScriptCallback); + } + + break; + } + } + if (!IsActive) return(true); + + /* + ** If at least one action was performed, then consider this + ** trigger to have completed and thus will be deleted if + ** necessary. + */ + if (ok) { + #ifdef CHEAT_KEYS + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 17, 11); + MonoArray[DMONO_STRESS].Scroll(); + MonoArray[DMONO_STRESS].Sub_Window(61, 1, 18, 11); + MonoArray[DMONO_STRESS].Set_Cursor(0, 10); + MonoArray[DMONO_STRESS].Printf("%02d:%02d:%02d-%s", Scen.Timer / TICKS_PER_HOUR, (Scen.Timer % TICKS_PER_HOUR)/TICKS_PER_MINUTE, (Scen.Timer % TICKS_PER_MINUTE)/TICKS_PER_SECOND, Class->IniName); + MonoArray[DMONO_STRESS].Sub_Window(); + #endif + + if (Class->IsPersistant == TriggerTypeClass::VOLATILE || (Class->IsPersistant == TriggerTypeClass::SEMIPERSISTANT && AttachCount <= 1)) { + Detach_This_From_All(As_Target(), true); + delete this; + return(true); + } else { + + /* + ** Reset event data so that the event will + ** repeat as necessary. + */ + Class->Event1.Reset(Event1); + Class->Event2.Reset(Event2); + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * TriggerClass::operator new -- 'new' operator * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * pointer to new trigger * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void * TriggerClass::operator new(size_t ) +{ + void * ptr = Triggers.Allocate(); + if (ptr) { + ((TriggerClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerClass::operator delete -- Returns a trigger to the special memory pool. * + * * + * This routine will return a previously allocated trigger object back to the memory * + * pool from which it came. * + * * + * INPUT: pointer -- Pointer to the trigger to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::operator delete(void * pointer) +{ + if (pointer) { + ((TriggerClass *)pointer)->IsActive = false; + } + Triggers.Free((TriggerClass *)pointer); +} + + +/*********************************************************************************************** + * TriggerClass::As_Target -- Converts trigger to a target value * + * * + * Converts the trigger object into a target identifier. * + * * + * INPUT: none * + * * + * OUTPUT: TARGET value * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/19/1994 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerClass::As_Target(void) const +{ + assert(Triggers.ID(this) == ID); + + return(Build_Target(RTTI_TRIGGER, ID)); +} + + +/*********************************************************************************************** + * Find_Or_Make -- Find or create a trigger of the type specified. * + * * + * This routine is used when, given a trigger type, an actual trigger object is needed. * + * In this case, an existing trigger of the correct type must be located, or a trigger * + * object must be created. In either case, this routine will return a trigger object that * + * corresponds to the trigger type class specified. * + * * + * INPUT: trigtype -- Pointer to the trigger type to find (or create) a matching trigger * + * object. * + * * + * OUTPUT: Returns a pointer to a matching trigger object. If no more triggers could be * + * allocated and no matching trigger could be found, then this routine will return * + * NULL (a very rare case). * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerClass * Find_Or_Make(TriggerTypeClass * trigtype) +{ + if (!trigtype) return(NULL); + + for (int index = 0; index < Triggers.Count(); index++) { + if (trigtype == Triggers.Ptr(index)->Class) { + return(Triggers.Ptr(index)); + } + } + + /* + ** No trigger was found, so make one. + */ + TriggerClass * trig = new TriggerClass(trigtype); + return(trig); +} + + +/*********************************************************************************************** + * TriggerClass::Detach -- Detach specified target from this trigger. * + * * + * This routine is called when the specified trigger MUST be detached from all references * + * to it. The only reference maintained by a trigger is the reference to the trigger * + * type class it is modeled after. * + * * + * INPUT: target -- The target identifier to remove all attachments to. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must never detach the trigger type class from a trigger. Such a process * + * will leave the trigger orphan and in a 'crash the game immediately if used' * + * state. As such, this routine will throw an assertion if this is tried. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerClass::Detach(TARGET target, bool ) +{ + if (Is_Target_TriggerType(target)) { + assert((TriggerTypeClass*)Class != As_TriggerType(target)); +// if (Class == As_TriggerType(target)) { +// Class = NULL; +// } + } +} diff --git a/code/redalert/trigger.h b/code/redalert/trigger.h new file mode 100644 index 0000000..7259f3d --- /dev/null +++ b/code/redalert/trigger.h @@ -0,0 +1,129 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGGER.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGGER.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 11/12/94 * + * * + * Last Update : November 12, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TRIGGER_H +#define TRIGGER_H + +class TriggerClass { + public: + RTTIType RTTI; + int ID; + + CCPtr Class; + + /* + ** Record of the "already sprung" flag for the events. + */ + TDEventClass Event1; + TDEventClass Event2; + + /* + ** Constructor/Destructor + */ + TriggerClass(TriggerTypeClass * trigtype=NULL); + TriggerClass(NoInitClass const & x) : Class(x), Event1(x), Event2(x) {}; + ~TriggerClass(void); + + /* + ** Initialization: clears all triggers in preparation for new scenario + */ + static void Init(void); + + /* + ** Processing routines + */ + bool Spring(TEventType event=TEVENT_ANY, ObjectClass * object=0, CELL cell=0, bool forced=false); + void Detach(TARGET target, bool all=true); + + /* + ** File I/O routines + */ + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void) {}; + void Decode_Pointers(void) {}; + + /* + ** Utility routines + */ + TARGET As_Target(void) const; + char const * Description(void) const; + void Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const; + char const * Name(void) const {return(Class->Name());} + + /* + ** Overloaded operators + */ + static void * operator new(size_t size); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + /* + ** If this trigger object is active, then this flag will be true. Trigger + ** objects that are not active are either not yet created or have been + ** deleted after fulfilling their action. + */ + unsigned IsActive:1; + + /* + ** This value tells how many objects or cells this trigger is attached + ** to. The Read_INI routine for all classes that point to a trigger must + ** increment this value! + */ + int AttachCount; + + /* + ** This value is used for triggers that can only exist in one cell. It is + ** needed for such triggers that the exact location of the trigger is needed + ** during processing but its location cannot be inferred from other data. + ** For all other triggers, this value is ignored. + */ + CELL Cell; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /* + ** Store MapScript callback function and optional action index (because there can be multiple outcomes) -- JJ 6/14/2020 + */ + char MapScriptCallback[32]; + char MapScriptActionIndex; +}; + + + + +#endif \ No newline at end of file diff --git a/code/redalert/trigtype.cpp b/code/redalert/trigtype.cpp new file mode 100644 index 0000000..a1effc7 --- /dev/null +++ b/code/redalert/trigtype.cpp @@ -0,0 +1,2600 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGTYPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGTYPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "function.h" +#include "trigtype.h" + + +#ifdef SCENARIO_EDITOR +enum { + /* + ** Dialog position and dimensions. + */ + D_DIALOG_W = 320 + 100, + D_DIALOG_H = 200 + 20, + D_DIALOG_X = 0, + D_DIALOG_Y = 0, + + /* + ** Event entry list box coordinates and dimensions. + */ + E1_X = D_DIALOG_X + 45, + E1_Y = D_DIALOG_Y + 65, + E2_X = E1_X, + E2_Y = E1_Y + 22, + E_WIDTH = 160, + E_HEIGHT = 8 * 5, + + /* + ** Event optional data entry coordinates and dimensions. + */ + ED1_X = E1_X + E_WIDTH + 20, + ED1_Y = E1_Y, + ED2_X = ED1_X, + ED2_Y = E2_Y, + + ED_WIDTH = 95, + ED_HEIGHT = 8 * 5, + + /* + ** Action entry list box coordinates. + */ + A1_X = E1_X, + A1_Y = D_DIALOG_Y + 120, + A2_X = E1_X, + A2_Y = A1_Y + 22, + + /* + ** Action optional data entry coordinates. + */ + AD1_X = A1_X + E_WIDTH + 20, + AD1_Y = A1_Y, + AD2_X = AD1_X, + AD2_Y = A2_Y, + + /* + ** Misc control values. + */ + GENERAL_SIZE = 10, // Text length for general data entry fields. + ENTRY_SIZE = 35, // Maximum size of event or action description text. + WAYPOINT_SIZE = 3, // Text length maximum for waypoint entry. + TEAM_SIZE = 10, // Team name text entry field length. + DESC_SIZE = 35 // Maximum length of object full name description. +}; + +/* +** Button enumerations: +*/ +enum { + EVENT_LIST = 100, // Primary event list. + EVENT_LIST2, // Secondary event list. + ACTION_LIST, // Primary action list. + ACTION_LIST2, // Secondary action list. + NAME_EDIT, // Trigger name edit field. + DATA_SPEECH1, // Primary action speech. + DATA_SPEECH2, // Secondary action speech. + DATA_THEME1, // Primary action theme. + DATA_THEME2, // Secondary action theme. + DATA_MOVIE1, // Primary action movie. + DATA_MOVIE2, // Secondary action movie. + DATA_SOUND1, // Primary action sound effect. + DATA_SOUND2, // Secondary action sound effect. + DATA_SPECIAL1, // Primary action special weapon. + DATA_SPECIAL2, // Secondary action special weapon. + DATA_EDIT, // Primary event waypoint data field. + DATA_EDIT2, // Secondary event waypoint data field. + DATA_EDIT3, // Primary action waypoint data field. + DATA_EDIT4, // Secondary action waypoint data field. + DATA_HTYPE1, // Primary event house choice list. + DATA_HTYPE2, // Secondary event house choice list. + DATA_HTYPE3, // Primary action house choice list. + DATA_HTYPE4, // Secondary action house choice list. + DATA_BOOLTYPE1, // Primary action boolean data list. + DATA_BOOLTYPE2, // Secondary action boolean data list. + DATA_GENERAL1, // Primary event general data field. + DATA_GENERAL2, // Secondary event general data field. + DATA_GENERAL3, // Primary action general data field. + DATA_GENERAL4, // Secondary action general data field. + DATA_BTYPE1, // Primary event building type list. + DATA_BTYPE2, // Secondary event building type list. + DATA_ITYPE1, // Primary event infantry type list. + DATA_ITYPE2, // Secondary event infantry type list. + DATA_ATYPE1, // Primary event aircraft type list. + DATA_ATYPE2, // Secondary event aircraft type list. + DATA_UTYPE1, // Primary event unit type list. + DATA_UTYPE2, // Secondary event unit type list. + DATA_TTYPE1, // Primary event team type entry list. + DATA_TTYPE2, // Secondary event team type entry list. + DATA_TTYPE3, // Primary action team type entry list. + DATA_TTYPE4, // Secondary action team type entry list. + DATA_TRTYPE1, // Primary action trigger list. + DATA_TRTYPE2, // Secondary action trigger list. + BUTTON_HOUSE, // House ownership for this trigger. + BUTTON_PERSISTANCE, // Persistence of this trigger. + BUTTON_OK, // Ok button - save and exit. + BUTTON_CANCEL, // Cancel button - just exit. + BUTTON_ACTION, // Multiple action control button. + BUTTON_EVENT, // Multiple event control button. +}; + +char selected_trigger_name[5] = { 0 }; +std::vector trig_houses; +std::vector trig_events; +std::vector trig_actions; +int current_trig_action = 0; +int current_trig_house = 0; +int current_trig_perst = 0; +int current_trig_event = 0; +int current_trig2_event = -1; +int current_trig2_action = -1; + +int current_event1 = 0; +int current_event2 = 0; +int current_event3 = 0; +int current_event4 = 0; + +std::vector typelist; +std::vector triggerentrylist; +std::vector boollist; +std::vector themelist; +std::vector movielist; +std::vector soundlist; +std::vector speechlist; +std::vector buildinglist; +std::vector infrantrylist; +std::vector aircraftlist; +std::vector unitlist; +std::vector quarytypelist; +std::vector specialweaponlist; + +void ImGui_DrawCombo(int eventListType, int *eventId) { + ImGui::SameLine(); + switch (Event_Needs(EventChoices[eventListType])) { + case NEED_MOVIE: + ImGui::Combo("", eventId, &movielist[0], movielist.size()); + break; + case NEED_SOUND: + ImGui::Combo("", eventId, &soundlist[0], soundlist.size()); + break; + case NEED_HOUSE: + ImGui::Combo("", eventId, &trig_houses[0], trig_houses.size()); + break; + case NEED_TEAM: + ImGui::Combo("", eventId, &typelist[0], typelist.size()); + break; + case NEED_WAYPOINT: + + break; + + case NEED_TIME: + case NEED_NUMBER: + ImGui::InputInt("", eventId); + break; + + case NEED_STRUCTURE: + ImGui::Combo("", eventId, &buildinglist[0], buildinglist.size()); + break; + + case NEED_INFANTRY: + ImGui::Combo("", eventId, &infrantrylist[0], infrantrylist.size()); + break; + + case NEED_AIRCRAFT: + ImGui::Combo("", eventId, &aircraftlist[0], aircraftlist.size()); + break; + + case NEED_UNIT: + ImGui::Combo("", eventId, &unitlist[0], unitlist.size()); + break; + default: + ImGui::NewLine(); + break; + } +} + +static char* _perstext[3] = { + "Volatile", + "Semi-persistent", + "Persistent" +}; +static TriggerTypeClass *currentTriggerClass; +bool edittrigger_process = true; + +int FindIndexInVector(std::vector &vec, const char* txt) { + for (int i = 0; i < vec.size(); i++) { + if (!strcmp(vec[i], txt)) { + return i; + } + } + + return -1; +} + +void ImGui_UpdateUITrigger(void) { + //EventChoices + + /* + ** Primary event specific data retrieval. + */ + //EventControl = MultiStyleType(eventflag); + current_trig_event = currentTriggerClass->Event1.Event; + switch (Event_Needs(currentTriggerClass->Event1.Event)) { + case NEED_HOUSE: + current_event1 = currentTriggerClass->Event1.Data.House; + break; + + case NEED_TIME: + current_event1 = currentTriggerClass->Event1.Data.Value; + break; + + case NEED_NUMBER: + current_event1 = currentTriggerClass->Event1.Data.Value; + break; + + case NEED_STRUCTURE: + current_event1 = currentTriggerClass->Event1.Data.Structure; + break; + + case NEED_UNIT: + current_event1 = currentTriggerClass->Event1.Data.Unit; + break; + + case NEED_INFANTRY: + current_event1 = currentTriggerClass->Event1.Data.Infantry; + break; + + case NEED_AIRCRAFT: + current_event1 = currentTriggerClass->Event1.Data.Aircraft; + break; + + case NEED_WAYPOINT: + //Event1.Data.Value = toupper(way1[0]) - 'A'; + //if (way1[1] != '\0') { + // Event1.Data.Value = (Event1.Data.Value + 1) * 26; + // Event1.Data.Value += toupper(way1[1]) - 'A'; + //} + break; + + case NEED_TEAM: + //currentTriggerClass->Event1.Team = TeamTypeClass::From_Name(current_trig_event); + break; + } + + /* + ** Secondary event specific data retrieval. + */ + current_trig2_event = currentTriggerClass->Event2.Event; + switch (Event_Needs(currentTriggerClass->Event2.Event)) { + case NEED_HOUSE: + current_event2 = currentTriggerClass->Event2.Data.House; + break; + + case NEED_TIME: + current_event2 = currentTriggerClass->Event2.Data.Value; + break; + + case NEED_NUMBER: + current_event2 = currentTriggerClass->Event2.Data.Value; + break; + + case NEED_STRUCTURE: + current_event2 = currentTriggerClass->Event2.Data.Structure; + break; + + case NEED_UNIT: + current_event2 = currentTriggerClass->Event2.Data.Unit; + break; + + case NEED_INFANTRY: + current_event2 = currentTriggerClass->Event2.Data.Infantry; + break; + + case NEED_AIRCRAFT: + current_event2 = currentTriggerClass->Event2.Data.Aircraft; + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Event2.Data.Value = toupper(way2[0]) - 'A'; + //if (way2[1] != '\0') { + // Event2.Data.Value = (Event2.Data.Value + 1) * 26; + // Event2.Data.Value += toupper(way2[1]) - 'A'; + //} + break; + + case NEED_TEAM: + //currentTriggerClass->Event2.Team = TeamTypeClass::As_Pointer(ttype2list.Current_Item()); + break; + } + + /* + ** Primary action data retrieval. + */ + //currentTriggerClass->ActionControl = MultiStyleType(actionflag); + current_trig_action = currentTriggerClass->Action1.Action; + switch (Action_Needs(currentTriggerClass->Action1.Action)) { + case NEED_SPECIAL: + current_event3 = currentTriggerClass->Action1.Data.Special; + break; + + case NEED_HOUSE: + current_event3 = currentTriggerClass->Action1.Data.House; + break; + + case NEED_TRIGGER: + current_event3 = FindIndexInVector(triggerentrylist, currentTriggerClass->Action1.Trigger->Description()); + break; + + case NEED_TEAM: + //currentTriggerClass->Action1.Team = TeamTypeClass::From_Name(ttype3list.Current_Item()); + break; + + case NEED_NUMBER: + current_event3 = currentTriggerClass->Action1.Data.Value; + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Action1.Data.Value = toupper(way3[0]) - 'A'; + //if (way3[1] != '\0') { + // Action1.Data.Value = (Action1.Data.Value + 1) * 26; + // Action1.Data.Value += toupper(way3[1]) - 'A'; + //} + break; + + case NEED_BOOL: + current_event3 = currentTriggerClass->Action1.Data.Bool; + break; + + case NEED_THEME: + current_event3 = currentTriggerClass->Action1.Data.Theme; + break; + + case NEED_SOUND: + current_event3 = currentTriggerClass->Action1.Data.Sound; + break; + + case NEED_MOVIE: + current_event3 = currentTriggerClass->Action1.Data.Movie; + break; + + case NEED_SPEECH: + current_event3 = currentTriggerClass->Action1.Data.Speech; + break; + + case NEED_QUARRY: + current_event3 = currentTriggerClass->Action1.Data.Quarry; + break; + } + + /* + ** Secondary action data retrieval. + */ + current_trig2_action = currentTriggerClass->Action2.Action; + switch (Action_Needs(currentTriggerClass->Action2.Action)) { + case NEED_SPECIAL: + current_event4 = currentTriggerClass->Action2.Data.Special; + break; + + case NEED_HOUSE: + current_event4 = currentTriggerClass->Action2.Data.House; + break; + + case NEED_TRIGGER: + current_event4 = FindIndexInVector(triggerentrylist, currentTriggerClass->Action2.Trigger->Description()); + break; + + case NEED_TEAM: + //currentTriggerClass->Action2.Team = TeamTypeClass::From_Name(ttype4list.Current_Item()); + break; + + case NEED_NUMBER: + current_event4 = currentTriggerClass->Action2.Data.Value; + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Action2.Data.Value = toupper(way4[0]) - 'A'; + //if (way4[1] != '\0') { + // currentTriggerClass->Action2.Data.Value = (Action2.Data.Value + 1) * 26; + // currentTriggerClass->Action2.Data.Value += toupper(way4[1]) - 'A'; + //} + break; + + case NEED_BOOL: + current_event4 = currentTriggerClass->Action2.Data.Bool; + break; + + case NEED_THEME: + current_event4 = currentTriggerClass->Action2.Data.Theme; + break; + + case NEED_MOVIE: + current_event4 = currentTriggerClass->Action2.Data.Movie; + break; + + case NEED_SOUND: + current_event4 = currentTriggerClass->Action2.Data.Sound; + break; + + case NEED_SPEECH: + current_event4 = currentTriggerClass->Action2.Data.Speech; + break; + + case NEED_QUARRY: + current_event4 = currentTriggerClass->Action2.Data.Quarry; + break; + } +} + +void ImGui_SaveTrigger(void) { + //EventChoices + + /* + ** Primary event specific data retrieval. + */ + //EventControl = MultiStyleType(eventflag); + currentTriggerClass->Event1.Event = (TEventType)current_trig_event; + switch (Event_Needs(currentTriggerClass->Event1.Event)) { + case NEED_HOUSE: + currentTriggerClass->Event1.Data.House = (HousesType)current_event1; + break; + + case NEED_TIME: + currentTriggerClass->Event1.Data.Value = current_event1; + break; + + case NEED_NUMBER: + currentTriggerClass->Event1.Data.Value = current_event1; + break; + + case NEED_STRUCTURE: + currentTriggerClass->Event1.Data.Structure = StructType(current_event1); + break; + + case NEED_UNIT: + currentTriggerClass->Event1.Data.Unit = UnitType(current_event1); + break; + + case NEED_INFANTRY: + currentTriggerClass->Event1.Data.Infantry = InfantryType(current_event1); + break; + + case NEED_AIRCRAFT: + currentTriggerClass->Event1.Data.Aircraft = AircraftType(current_event1); + break; + + case NEED_WAYPOINT: + //Event1.Data.Value = toupper(way1[0]) - 'A'; + //if (way1[1] != '\0') { + // Event1.Data.Value = (Event1.Data.Value + 1) * 26; + // Event1.Data.Value += toupper(way1[1]) - 'A'; + //} + break; + + case NEED_TEAM: + //currentTriggerClass->Event1.Team = TeamTypeClass::From_Name(current_trig_event); + break; + } + + /* + ** Secondary event specific data retrieval. + */ + currentTriggerClass->Event2.Event = (TEventType)current_trig2_event; + switch (Event_Needs(currentTriggerClass->Event2.Event)) { + case NEED_HOUSE: + currentTriggerClass->Event2.Data.House = HousesType(current_event2); + break; + + case NEED_TIME: + currentTriggerClass->Event2.Data.Value = current_event2; + break; + + case NEED_NUMBER: + currentTriggerClass->Event2.Data.Value = current_event2; + break; + + case NEED_STRUCTURE: + currentTriggerClass->Event2.Data.Structure = StructType(current_event2); + break; + + case NEED_UNIT: + currentTriggerClass->Event2.Data.Unit = UnitType(current_event2); + break; + + case NEED_INFANTRY: + currentTriggerClass->Event2.Data.Infantry = InfantryType(current_event2); + break; + + case NEED_AIRCRAFT: + currentTriggerClass->Event2.Data.Aircraft = AircraftType(current_event2); + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Event2.Data.Value = toupper(way2[0]) - 'A'; + //if (way2[1] != '\0') { + // Event2.Data.Value = (Event2.Data.Value + 1) * 26; + // Event2.Data.Value += toupper(way2[1]) - 'A'; + //} + break; + + case NEED_TEAM: + //currentTriggerClass->Event2.Team = TeamTypeClass::As_Pointer(ttype2list.Current_Item()); + break; + } + + /* + ** Primary action data retrieval. + */ + //currentTriggerClass->ActionControl = MultiStyleType(actionflag); + currentTriggerClass->Action1.Action = (TActionType)current_trig_action; + switch (Action_Needs(currentTriggerClass->Action1.Action)) { + case NEED_SPECIAL: + currentTriggerClass->Action1.Data.Special = SpecialWeaponType(current_event3); + break; + + case NEED_HOUSE: + currentTriggerClass->Action1.Data.House = HousesType(current_event3); + break; + + case NEED_TRIGGER: + currentTriggerClass->Action1.Trigger = TriggerTypeClass::From_Name(typelist[current_event3]); + break; + + case NEED_TEAM: + //currentTriggerClass->Action1.Team = TeamTypeClass::From_Name(ttype3list.Current_Item()); + break; + + case NEED_NUMBER: + currentTriggerClass->Action1.Data.Value = current_event3; + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Action1.Data.Value = toupper(way3[0]) - 'A'; + //if (way3[1] != '\0') { + // Action1.Data.Value = (Action1.Data.Value + 1) * 26; + // Action1.Data.Value += toupper(way3[1]) - 'A'; + //} + break; + + case NEED_BOOL: + currentTriggerClass->Action1.Data.Bool = current_event3; + break; + + case NEED_THEME: + currentTriggerClass->Action1.Data.Theme = ThemeType(current_event3); + break; + + case NEED_SOUND: + currentTriggerClass->Action1.Data.Sound = VocType(current_event3); + break; + + case NEED_MOVIE: + currentTriggerClass->Action1.Data.Movie = VQType(current_event3); + break; + + case NEED_SPEECH: + currentTriggerClass->Action1.Data.Speech = VoxType(current_event3); + break; + + case NEED_QUARRY: + currentTriggerClass->Action1.Data.Quarry = QuarryType(current_event3); + break; + } + + /* + ** Secondary action data retrieval. + */ + currentTriggerClass->Action2.Action = (TActionType)current_trig2_action; + switch (Action_Needs(currentTriggerClass->Action2.Action)) { + case NEED_SPECIAL: + currentTriggerClass->Action2.Data.Special = SpecialWeaponType(current_event4); + break; + + case NEED_HOUSE: + currentTriggerClass->Action2.Data.House = HousesType(current_event4); + break; + + case NEED_TRIGGER: + currentTriggerClass->Action2.Trigger = TriggerTypeClass::From_Name(typelist[current_event4]); + break; + + case NEED_TEAM: + //currentTriggerClass->Action2.Team = TeamTypeClass::From_Name(ttype4list.Current_Item()); + break; + + case NEED_NUMBER: + currentTriggerClass->Action2.Data.Value = current_event4; + break; + + case NEED_WAYPOINT: + //currentTriggerClass->Action2.Data.Value = toupper(way4[0]) - 'A'; + //if (way4[1] != '\0') { + // currentTriggerClass->Action2.Data.Value = (Action2.Data.Value + 1) * 26; + // currentTriggerClass->Action2.Data.Value += toupper(way4[1]) - 'A'; + //} + break; + + case NEED_BOOL: + currentTriggerClass->Action2.Data.Bool = current_event4; + break; + + case NEED_THEME: + currentTriggerClass->Action2.Data.Theme = ThemeType(current_event4); + break; + + case NEED_MOVIE: + currentTriggerClass->Action2.Data.Movie = VQType(current_event4); + break; + + case NEED_SOUND: + currentTriggerClass->Action2.Data.Sound = VocType(current_event4); + break; + + case NEED_SPEECH: + currentTriggerClass->Action2.Data.Speech = VoxType(current_event4); + break; + + case NEED_QUARRY: + currentTriggerClass->Action2.Data.Quarry = QuarryType(current_event4); + break; + } +} + +void ImGui_TriggerEdit(void) { + bool toolActive = false; + current_trig_house = currentTriggerClass->House; + current_trig_perst = currentTriggerClass->IsPersistant; + ImGui_UpdateUITrigger(); + + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(450, 340)); + ImGui::SetNextWindowPos(ImVec2(100, 100)); + ImGui::Begin("Edit Trigger", &toolActive, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Columns(3, "trigger_o3", true); + ImGui::Text("Name"); + ImGui::PushID("trigedit_name"); + ImGui::InputText("", selected_trigger_name, 5); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("House"); + ImGui::PushID("trigedit_house"); + ImGui::Combo("", ¤t_trig_house, &trig_houses[0], trig_houses.size()); + ImGui::PopID(); + ImGui::NextColumn(); + ImGui::Text("Persistence"); + ImGui::PushID("trigedit_persistance"); + ImGui::Combo("", ¤t_trig_perst, &_perstext[0], 3); + ImGui::PopID(); + ImGui::Columns(1); + ImGui::NewLine(); + ImGui::Text("Trigger Event:"); + ImGui::PushID("trigedit_event"); + ImGui::Combo("", ¤t_trig_event, &trig_events[0], trig_events.size()); + ImGui::PopID(); + ImGui::PushID("trigedit_data1"); + ImGui_DrawCombo(current_trig_event, ¤t_event1); + ImGui::PopID(); + if (current_trig2_event == -1) { + ImGui::PushID("trigedit_justevent_btn"); + if (ImGui::Button("Just this event")) { + current_trig2_event = 0; + } + ImGui::PopID(); + } + else { + ImGui::PushID("trigedit_and1_btn"); + if (ImGui::Button("_AND_")) { + current_trig2_event = -1; + } + ImGui::PopID(); + ImGui::PushID("trigedit_event2"); + ImGui::Combo("", ¤t_trig2_event, &trig_events[0], trig_events.size()); + ImGui::PopID(); + + ImGui::PushID("trigedit_data2"); + ImGui_DrawCombo(current_trig2_event, ¤t_event3); + ImGui::PopID(); + } + ImGui::NewLine(); + ImGui::Text("Action to perform:"); + ImGui::PushID("trigedit_action"); + ImGui::Combo("", ¤t_trig_action, &trig_actions[0], trig_actions.size()); + ImGui::PopID(); + ImGui::SameLine(); + ImGui::PushID("trigedit_data3"); + ImGui_DrawCombo(current_trig_action, ¤t_event3); + ImGui::PopID(); + if (current_trig2_action == -1) { + ImGui::PushID("trigedit_justaction_btn"); + if (ImGui::Button("Just this action")) { + current_trig2_action = 0; + } + ImGui::PopID(); + } + else { + ImGui::PushID("trigedit_and2_btn"); + if (ImGui::Button("_AND_")) { + current_trig2_action = -1; + } + ImGui::PopID(); + ImGui::PushID("trigedit_action2"); + ImGui::Combo("", ¤t_trig2_action, &trig_actions[0], trig_actions.size()); + ImGui::PopID(); + + ImGui::PushID("trigedit_data4"); + ImGui_DrawCombo(current_trig2_action, ¤t_event4); + ImGui::PopID(); + } + ImGui::NewLine(); + if (ImGui::Button("Ok")) { + edittrigger_process = false; + } + //ImGui::SameLine(); + //ImGui::Button("Cancel"); + ImGui::End(); + + currentTriggerClass->House = (HousesType)current_trig_house; + currentTriggerClass->IsPersistant = (TriggerTypeClass::PersistantType)current_trig_perst; + + ImGui_SaveTrigger(); +} + + /*********************************************************************************************** + * TriggerTypeClass::Edit -- Edit the trigger type through the scenario editor. * + * * + * This is the scenario editor interface to a trigger type class object. It brings up a * + * fancy schmancy dialog to allow full edit control of the trigger type. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the "OK" button pressed? A false return value indicates that the edits * + * to this trigger type class object should be rejected. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool TriggerTypeClass::Edit(void) +{ + edittrigger_process = true; + currentTriggerClass = this; + trig_houses.clear(); + trig_events.clear(); + trig_actions.clear(); + + typelist.clear(); + triggerentrylist.clear(); + boollist.clear(); + themelist.clear(); + movielist.clear(); + soundlist.clear(); + speechlist.clear(); + buildinglist.clear(); + infrantrylist.clear(); + aircraftlist.clear(); + unitlist.clear(); + quarytypelist.clear(); + specialweaponlist.clear(); + + Imgui_Dialog_Function = ImGui_TriggerEdit; + + /* + ** Dialog variables: + */ + bool cancel = false; // true = user cancels + int i; // loop counter + RemapControlType* scheme = GadgetClass::Get_Color_Scheme(); + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + /* + ** List of events allowed. + */ + char eventtext[ENTRY_SIZE] = ""; + TDropListClass event1list(EVENT_LIST, eventtext, sizeof(eventtext), + TPF_EFNT | TPF_NOSHADOW, + E1_X, E1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char event2text[ENTRY_SIZE] = ""; + TDropListClass event2list(EVENT_LIST2, event2text, sizeof(event2text), + TPF_EFNT | TPF_NOSHADOW, + E2_X, E2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TEventType event = TEVENT_FIRST; event < TEVENT_COUNT; event++) { + event1list.Add_Item(&EventChoices[event]); + event2list.Add_Item(&EventChoices[event]); + + trig_events.push_back(EventChoices[event].Description()); + } + + PBubble_Sort(&event1list[0], event1list.Count()); + PBubble_Sort(&event2list[0], event2list.Count()); + + if (Event1.Event == TEVENT_NONE) Event1.Event = TEVENT_FIRST; + event1list.Set_Selected_Index(&EventChoices[Event1.Event]); + if (Event2.Event == TEVENT_NONE) Event2.Event = TEVENT_FIRST; + event2list.Set_Selected_Index(&EventChoices[Event2.Event]); + + /* + ** List of actions allowed. + */ + char actiontext[ENTRY_SIZE] = ""; + TDropListClass action1list(ACTION_LIST, actiontext, sizeof(actiontext), + TPF_EFNT | TPF_NOSHADOW, + A1_X, A1_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char action2text[ENTRY_SIZE] = ""; + TDropListClass action2list(ACTION_LIST2, action2text, sizeof(action2text), + TPF_EFNT | TPF_NOSHADOW, + A2_X, A2_Y, E_WIDTH, E_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (TActionType action = TACTION_FIRST; action < TACTION_COUNT; action++) { + action1list.Add_Item(&ActionChoices[action]); + action2list.Add_Item(&ActionChoices[action]); + trig_actions.push_back(ActionChoices[action].Description()); + } + + PBubble_Sort(&action1list[0], action1list.Count()); + PBubble_Sort(&action2list[0], action2list.Count()); + + if (Action1.Action == ACTION_NONE) Action1.Action = TACTION_FIRST; + action1list.Set_Selected_Index(&ActionChoices[Action1.Action]); + if (Action2.Action == ACTION_NONE) Action2.Action = TACTION_FIRST; + action2list.Set_Selected_Index(&ActionChoices[Action2.Action]); + + /* + ** Optional waypoint entry field. + */ + char way1[WAYPOINT_SIZE] = "A"; + EditClass way1data(DATA_EDIT, way1, sizeof(way1), TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event1.Event) == NEED_WAYPOINT) { + if (Event1.Data.Value < 26) { + sprintf(way1data.Get_Text(), "%c", Event1.Data.Value + 'A'); + } + else { + sprintf(way1data.Get_Text(), "%c%c", (Event1.Data.Value / 26) + 'A' - 1, (Event1.Data.Value % 26) + 'A'); + } + } + + char way2[WAYPOINT_SIZE] = "A"; + EditClass way2data(DATA_EDIT2, way2, sizeof(way2), TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Event_Needs(Event2.Event) == NEED_WAYPOINT) { + if (Event2.Data.Value < 26) { + sprintf(way2data.Get_Text(), "%c", Event2.Data.Value + 'A'); + } + else { + sprintf(way2data.Get_Text(), "%c%c", (Event2.Data.Value / 26) + 'A' - 1, (Event2.Data.Value % 26) + 'A'); + } + } + + char way3[WAYPOINT_SIZE] = "A"; + EditClass way3data(DATA_EDIT3, way3, sizeof(way3), TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action1.Action) == NEED_WAYPOINT) { + if (Action1.Data.Value < 26) { + sprintf(way3data.Get_Text(), "%c", Action1.Data.Value + 'A'); + } + else { + sprintf(way3data.Get_Text(), "%c%c", (Action1.Data.Value / 26) + 'A' - 1, (Action1.Data.Value % 26) + 'A'); + } + } + + char way4[WAYPOINT_SIZE] = "A"; + EditClass way4data(DATA_EDIT4, way4, sizeof(way4), TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::ALPHA); + if (Action_Needs(Action2.Action) == NEED_WAYPOINT) { + if (Action2.Data.Value < 26) { + sprintf(way4data.Get_Text(), "%c", Action2.Data.Value + 'A'); + } + else { + sprintf(way4data.Get_Text(), "%c%c", (Action2.Data.Value / 26) + 'A' - 1, (Action2.Data.Value % 26) + 'A'); + } + } + + /* + ** Optional event data entry field. + */ + char databuf1[GENERAL_SIZE] = ""; + EditClass event1data(DATA_GENERAL1, databuf1, sizeof(databuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event1.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event1data.Get_Text(), "%d", Event1.Data.Value); + break; + } + + char databuf2[GENERAL_SIZE] = ""; + EditClass event2data(DATA_GENERAL2, databuf2, sizeof(databuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Event_Needs(Event2.Event)) { + case NEED_TIME: + case NEED_NUMBER: + sprintf(event2data.Get_Text(), "%d", Event2.Data.Value); + break; + } + + char actionbuf1[GENERAL_SIZE] = ""; + EditClass action1data(DATA_GENERAL3, actionbuf1, sizeof(actionbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action1.Action)) { + case NEED_NUMBER: + sprintf(action1data.Get_Text(), "%d", Action1.Data.Value); + break; + } + + char actionbuf2[GENERAL_SIZE] = ""; + EditClass action2data(DATA_GENERAL4, actionbuf2, sizeof(actionbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, 9, EditClass::NUMERIC); + switch (Action_Needs(Action2.Action)) { + case NEED_NUMBER: + sprintf(action2data.Get_Text(), "%d", Action2.Data.Value); + break; + } + + /* + ** Optional team entry list. + */ + char tbuf1[TEAM_SIZE] = ""; + DropListClass ttype1list(DATA_TTYPE1, tbuf1, sizeof(tbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf2[TEAM_SIZE] = ""; + DropListClass ttype2list(DATA_TTYPE2, tbuf2, sizeof(tbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf3[TEAM_SIZE] = ""; + DropListClass ttype3list(DATA_TTYPE3, tbuf3, sizeof(tbuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char tbuf4[TEAM_SIZE] = ""; + DropListClass ttype4list(DATA_TTYPE4, tbuf4, sizeof(tbuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (int index = 0; index < TeamTypes.Count(); index++) { + ttype1list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype2list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype3list.Add_Item(TeamTypes.Ptr(index)->IniName); + ttype4list.Add_Item(TeamTypes.Ptr(index)->IniName); + + typelist.push_back(TeamTypes.Ptr(index)->IniName); + } + + if (Event1.Team.Is_Valid()) { + ttype1list.Set_Selected_Index(Event1.Team->IniName); + } + else { + ttype1list.Set_Selected_Index(0); + } + if (Event2.Team.Is_Valid()) { + ttype2list.Set_Selected_Index(Event2.Team->IniName); + } + else { + ttype2list.Set_Selected_Index(0); + } + if (Action1.Team.Is_Valid()) { + ttype3list.Set_Selected_Index(Action1.Team->IniName); + } + else { + ttype3list.Set_Selected_Index(0); + } + if (Action2.Team.Is_Valid()) { + ttype4list.Set_Selected_Index(Action2.Team->IniName); + } + else { + ttype4list.Set_Selected_Index(0); + } + + /* + ** Optional trigger entry list. + */ + char trbuf1[TEAM_SIZE] = ""; + DropListClass trtype1list(DATA_TRTYPE1, trbuf1, sizeof(trbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char trbuf2[TEAM_SIZE] = ""; + DropListClass trtype2list(DATA_TRTYPE2, trbuf2, sizeof(trbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (index = 0; index < TriggerTypes.Count(); index++) { + trtype1list.Add_Item(TriggerTypes.Ptr(index)->IniName); + trtype2list.Add_Item(TriggerTypes.Ptr(index)->IniName); + triggerentrylist.push_back(TriggerTypes.Ptr(index)->IniName); + } + + if (Action1.Trigger.Is_Valid()) { + trtype1list.Set_Selected_Index(Action1.Trigger->IniName); + } + else { + trtype1list.Set_Selected_Index(0); + } + if (Action2.Trigger.Is_Valid()) { + trtype2list.Set_Selected_Index(Action2.Trigger->IniName); + } + else { + trtype2list.Set_Selected_Index(0); + } + + /* + ** Optional boolean value list. + */ + char boolbuf1[TEAM_SIZE] = ""; + DropListClass booltype1list(DATA_BOOLTYPE1, boolbuf1, sizeof(boolbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char boolbuf2[TEAM_SIZE] = ""; + DropListClass booltype2list(DATA_BOOLTYPE2, boolbuf2, sizeof(boolbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + boollist.push_back("OFF"); + boollist.push_back("ON"); + + booltype1list.Add_Item("OFF"); + booltype1list.Add_Item("ON"); + booltype2list.Add_Item("OFF"); + booltype2list.Add_Item("ON"); + + booltype1list.Set_Selected_Index(Action1.Data.Bool); + booltype2list.Set_Selected_Index(Action2.Data.Bool); + + /* + ** Optional musical theme choice list. + */ + char themebuf1[DESC_SIZE] = ""; + DropListClass themetype1list(DATA_THEME1, themebuf1, sizeof(themebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char themebuf2[DESC_SIZE] = ""; + DropListClass themetype2list(DATA_THEME2, themebuf2, sizeof(themebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (ThemeType theme = THEME_FIRST; theme < THEME_COUNT; theme++) { + themetype1list.Add_Item(Theme.Full_Name(theme)); + themetype2list.Add_Item(Theme.Full_Name(theme)); + themelist.push_back(Theme.Full_Name(theme)); + } + + if (Action_Needs(Action1.Action) == NEED_THEME) { + themetype1list.Set_Selected_Index(Action1.Data.Theme); + } + else { + themetype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_THEME) { + themetype2list.Set_Selected_Index(Action2.Data.Theme); + } + else { + themetype2list.Set_Selected_Index(0); + } + + /* + ** Optional movie list. + */ + char moviebuf1[DESC_SIZE] = ""; + DropListClass movietype1list(DATA_MOVIE1, moviebuf1, sizeof(moviebuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char moviebuf2[DESC_SIZE] = ""; + DropListClass movietype2list(DATA_MOVIE2, moviebuf2, sizeof(moviebuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VQType movie = VQ_FIRST; movie < VQ_COUNT; movie++) { + movietype1list.Add_Item(VQName[movie]); + movietype2list.Add_Item(VQName[movie]); + movielist.push_back(VQName[movie]); + } + + if (Action_Needs(Action1.Action) == NEED_MOVIE) { + movietype1list.Set_Selected_Index(Action1.Data.Movie); + } + else { + movietype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_MOVIE) { + movietype2list.Set_Selected_Index(Action2.Data.Movie); + } + else { + movietype2list.Set_Selected_Index(0); + } + + /* + ** Optional sound effect list. + */ + char soundbuf1[DESC_SIZE] = ""; + DropListClass soundtype1list(DATA_SOUND1, soundbuf1, sizeof(soundbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char soundbuf2[DESC_SIZE] = ""; + DropListClass soundtype2list(DATA_SOUND2, soundbuf2, sizeof(soundbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VocType sound = VOC_FIRST; sound < VOC_COUNT; sound++) { + soundtype1list.Add_Item(Voc_Name(sound)); + soundtype2list.Add_Item(Voc_Name(sound)); + soundlist.push_back(Voc_Name(sound)); + } + + if (Action_Needs(Action1.Action) == NEED_SOUND) { + soundtype1list.Set_Selected_Index(Action1.Data.Sound); + } + else { + soundtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SOUND) { + soundtype2list.Set_Selected_Index(Action2.Data.Sound); + } + else { + soundtype2list.Set_Selected_Index(0); + } + + /* + ** Optional speech effect list. + */ + char speechbuf1[DESC_SIZE] = ""; + DropListClass speechtype1list(DATA_SPEECH1, speechbuf1, sizeof(speechbuf1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char speechbuf2[DESC_SIZE] = ""; + DropListClass speechtype2list(DATA_SPEECH2, speechbuf2, sizeof(speechbuf2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (VoxType speech = VOX_FIRST; speech < VOX_COUNT; speech++) { + speechtype1list.Add_Item(Speech_Name(speech)); + speechtype2list.Add_Item(Speech_Name(speech)); + speechlist.push_back(Speech_Name(speech)); + } + + if (Action_Needs(Action1.Action) == NEED_SPEECH) { + speechtype1list.Set_Selected_Index(Action1.Data.Speech); + } + else { + speechtype1list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_SPEECH) { + speechtype2list.Set_Selected_Index(Action2.Data.Speech); + } + else { + speechtype2list.Set_Selected_Index(0); + } + + /* + ** Optional building type entry list. + */ + char bbuf1[DESC_SIZE] = ""; + DropListClass btype1list(DATA_BTYPE1, bbuf1, sizeof(bbuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char bbuf2[DESC_SIZE] = ""; + DropListClass btype2list(DATA_BTYPE2, bbuf2, sizeof(bbuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (StructType ss = STRUCT_FIRST; ss < STRUCT_COUNT; ss++) { + btype1list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + btype2list.Add_Item(Text_String(BuildingTypeClass::As_Reference(ss).Full_Name())); + buildinglist.push_back(BuildingTypeClass::As_Reference(ss).Name()); + } + + if (Event_Needs(Event1.Event) == NEED_STRUCTURE) { + btype1list.Set_Selected_Index(Event1.Data.Structure); + } + else { + btype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_STRUCTURE) { + btype2list.Set_Selected_Index(Event2.Data.Structure); + } + else { + btype2list.Set_Selected_Index(0); + } + + /* + ** Optional infantry type entry list. + */ + char ibuf1[DESC_SIZE] = ""; + DropListClass itype1list(DATA_ITYPE1, ibuf1, sizeof(ibuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ibuf2[DESC_SIZE] = ""; + DropListClass itype2list(DATA_ITYPE2, ibuf2, sizeof(ibuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (InfantryType ii = INFANTRY_FIRST; ii < INFANTRY_COUNT; ii++) { + itype1list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + itype2list.Add_Item(Text_String(InfantryTypeClass::As_Reference(ii).Full_Name())); + infrantrylist.push_back(InfantryTypeClass::As_Reference(ii).Name()); + } + + if (Event_Needs(Event1.Event) == NEED_INFANTRY) { + itype1list.Set_Selected_Index(Event1.Data.Infantry); + } + else { + itype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_INFANTRY) { + itype2list.Set_Selected_Index(Event2.Data.Infantry); + } + else { + itype2list.Set_Selected_Index(0); + } + + /* + ** Optional aircraft type entry list. + */ + char abuf1[DESC_SIZE] = ""; + DropListClass atype1list(DATA_ATYPE1, abuf1, sizeof(abuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char abuf2[DESC_SIZE] = ""; + DropListClass atype2list(DATA_ATYPE2, abuf2, sizeof(abuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (AircraftType aa = AIRCRAFT_FIRST; aa < AIRCRAFT_COUNT; aa++) { + atype1list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + atype2list.Add_Item(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + aircraftlist.push_back(Text_String(AircraftTypeClass::As_Reference(aa).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_AIRCRAFT) { + atype1list.Set_Selected_Index(Event1.Data.Aircraft); + } + else { + atype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_AIRCRAFT) { + atype2list.Set_Selected_Index(Event2.Data.Aircraft); + } + else { + atype2list.Set_Selected_Index(0); + } + + /* + ** Optional unit type entry list. + */ + char ubuf1[DESC_SIZE] = ""; + DropListClass utype1list(DATA_UTYPE1, ubuf1, sizeof(ubuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char ubuf2[DESC_SIZE] = ""; + DropListClass utype2list(DATA_UTYPE2, ubuf2, sizeof(ubuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (UnitType uu = UNIT_FIRST; uu < UNIT_COUNT; uu++) { + utype1list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + utype2list.Add_Item(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + unitlist.push_back(Text_String(UnitTypeClass::As_Reference(uu).Full_Name())); + } + + if (Event_Needs(Event1.Event) == NEED_UNIT) { + utype1list.Set_Selected_Index(Event1.Data.Unit); + } + else { + utype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_UNIT) { + utype2list.Set_Selected_Index(Event2.Data.Unit); + } + else { + utype2list.Set_Selected_Index(0); + } + + /* + ** Optional house type entry list. + */ + char housebuf1[DESC_SIZE] = ""; + DropListClass htype1list(DATA_HTYPE1, housebuf1, sizeof(housebuf1), + TPF_EFNT | TPF_NOSHADOW, + ED1_X, ED1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf2[DESC_SIZE] = ""; + DropListClass htype2list(DATA_HTYPE2, housebuf2, sizeof(housebuf2), + TPF_EFNT | TPF_NOSHADOW, + ED2_X, ED2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf3[DESC_SIZE] = ""; + DropListClass htype3list(DATA_HTYPE3, housebuf3, sizeof(housebuf3), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char housebuf4[DESC_SIZE] = ""; + DropListClass htype4list(DATA_HTYPE4, housebuf4, sizeof(housebuf4), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + + for (HousesType hh = HOUSE_FIRST; hh < HOUSE_COUNT; hh++) { + htype1list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype2list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype3list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + htype4list.Add_Item(HouseTypeClass::As_Reference(hh).IniName); + + trig_houses.push_back(HouseTypeClass::As_Reference(hh).IniName); + } + + if (Event_Needs(Event1.Event) == NEED_HOUSE) { + htype1list.Set_Selected_Index(Event1.Data.House); + } + else { + htype1list.Set_Selected_Index(0); + } + if (Event_Needs(Event2.Event) == NEED_HOUSE) { + htype2list.Set_Selected_Index(Event2.Data.House); + } + else { + htype2list.Set_Selected_Index(0); + } + if (Action_Needs(Action1.Action) == NEED_HOUSE) { + htype3list.Set_Selected_Index(Action1.Data.House); + } + else { + htype3list.Set_Selected_Index(0); + } + if (Action_Needs(Action2.Action) == NEED_HOUSE) { + htype4list.Set_Selected_Index(Action2.Data.House); + } + else { + htype4list.Set_Selected_Index(0); + } + + /* + ** Optional special weapon list. + */ + char special1[DESC_SIZE] = ""; + DropListClass spc1(DATA_SPECIAL1, special1, sizeof(special1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char special2[DESC_SIZE] = ""; + DropListClass spc2(DATA_SPECIAL2, special2, sizeof(special2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (SpecialWeaponType spec = SPC_FIRST; spec < SPC_COUNT; spec++) { + spc1.Add_Item(SpecialWeaponName[spec]); + spc2.Add_Item(SpecialWeaponName[spec]); + specialweaponlist.push_back(SpecialWeaponName[spec]); + } + if ((unsigned)Action1.Data.Special < SPC_COUNT) { + spc1.Set_Selected_Index(Action1.Data.Special); + } + else { + spc1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Special < SPC_COUNT) { + spc2.Set_Selected_Index(Action2.Data.Special); + } + else { + spc2.Set_Selected_Index(0); + } + + /* + ** Optional quarry type. + */ + char quarry1[DESC_SIZE] = ""; + DropListClass qlist1(DATA_SPECIAL1, quarry1, sizeof(quarry1), + TPF_EFNT | TPF_NOSHADOW, + AD1_X, AD1_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + char quarry2[DESC_SIZE] = ""; + DropListClass qlist2(DATA_SPECIAL2, quarry2, sizeof(quarry2), + TPF_EFNT | TPF_NOSHADOW, + AD2_X, AD2_Y, ED_WIDTH, ED_HEIGHT, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (QuarryType q = QUARRY_FIRST; q < QUARRY_COUNT; q++) { + qlist1.Add_Item(QuarryName[q]); + qlist2.Add_Item(QuarryName[q]); + quarytypelist.push_back(SpecialWeaponName[spec]); + } + if ((unsigned)Action1.Data.Quarry < QUARRY_COUNT) { + qlist1.Set_Selected_Index(Action1.Data.Quarry); + } + else { + qlist1.Set_Selected_Index(0); + } + if ((unsigned)Action2.Data.Quarry < QUARRY_COUNT) { + qlist2.Set_Selected_Index(Action2.Data.Quarry); + } + else { + qlist2.Set_Selected_Index(0); + } + + /* + ** Name of this trigger text edit field. + */ +// char namebuf[5] = ""; + EditClass name_edt(NAME_EDIT, selected_trigger_name, sizeof(selected_trigger_name), TPF_EFNT | TPF_NOSHADOW, D_DIALOG_X + 40, D_DIALOG_Y + 30, 40, 9, EditClass::ALPHANUMERIC); + strcpy(selected_trigger_name, IniName); // Name + + /* + ** Create the list of house's allowed for trigger. + */ + char housetext[DESC_SIZE] = ""; + DropListClass housebtn(BUTTON_HOUSE, housetext, sizeof(housetext), + TPF_EFNT | TPF_NOSHADOW, + name_edt.X + name_edt.Width + 20, name_edt.Y, 95, 8 * 5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) { + housebtn.Add_Item(HouseTypeClass::As_Reference(house).IniName); + } + if (House == HOUSE_NONE) House = HOUSE_GOOD; + housebtn.Set_Selected_Index(House); + + /* + ** Must match order and number of PersistantType specified in + ** TriggerTypeClass definition. + */ + char perstext[DESC_SIZE] = ""; + DropListClass persbtn(BUTTON_PERSISTANCE, perstext, sizeof(perstext), + TPF_EFNT | TPF_NOSHADOW, + housebtn.X + housebtn.Width + 20, housebtn.Y, 105, 8 * 5, + MFCD::Retrieve("EBTN-UP.SHP"), + MFCD::Retrieve("EBTN-DN.SHP")); + for (i = 0; i < sizeof(_perstext) / sizeof(_perstext[0]); i++) { + persbtn.Add_Item(_perstext[i]); + } + persbtn.Set_Selected_Index(IsPersistant); + + /* + ** This button controls the existence and relationship of a second trigger + ** event. + */ + int eventflag = EventControl; + TextButtonClass eventbtn(BUTTON_EVENT, TXT_TRIGGER_JUST_EVENT, TPF_EBUTTON, event1list.X, event1list.Y + 11, 100, 9); + + /* + ** This button controls the existence of a secondary action. + */ + bool actionflag = ActionControl; + TextButtonClass actionbtn(BUTTON_ACTION, TXT_TRIGGER_JUST_ACTION, TPF_EBUTTON, action1list.X, action1list.Y + 11, 100, 9); + + /* + ** Create the ubiquitous OK and Cancel buttons. + */ + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_EBUTTON, D_DIALOG_X + 35, D_DIALOG_Y + D_DIALOG_H - 30, 45, 9); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_EBUTTON, D_DIALOG_X + D_DIALOG_W - 80, D_DIALOG_Y + D_DIALOG_H - 30, 45, 9); + + /* + ** Initialize + */ + Set_Logic_Page(SeenBuff); + + /* + ** Build the button list + */ + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + event1list.Add_Tail(*commands); + action1list.Add_Tail(*commands); + eventbtn.Add_Tail(*commands); + actionbtn.Add_Tail(*commands); + name_edt.Add_Tail(*commands); + persbtn.Add_Tail(*commands); + housebtn.Add_Tail(*commands); + + /* + ** Main Processing Loop + */ + bool display = true; + while (edittrigger_process) { + + /* + ** Invoke game callback + */ + Call_Back(); + + /* + ** Refresh display if needed + */ + if (display /*&& LogicPage->Lock()*/) { + + /* + ** Display the dialog box + */ + Hide_Mouse(); + Dialog_Box(D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W, D_DIALOG_H); + Draw_Caption(TXT_TRIGGER_EDITOR, D_DIALOG_X, D_DIALOG_Y, D_DIALOG_W); + + /* + ** Draw the captions + */ + Fancy_Text_Print("Trigger Event:", event1list.X, event1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Action to Perform:", action1list.X, action1list.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("House:", housebtn.X, housebtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Name:", name_edt.X, name_edt.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + Fancy_Text_Print("Persistence:", persbtn.X, persbtn.Y - 7, scheme, TBLACK, TPF_EFNT | TPF_NOSHADOW); + + if (eventflag == 3) { + LogicPage->Draw_Line(event1list.X - 1, event1list.Y + 3, event1list.X - 2 * RESFACTOR, event1list.Y + 3, WHITE); + LogicPage->Draw_Line(event1list.X - 2 * RESFACTOR, event1list.Y + 3, action1list.X - 2 * RESFACTOR, action1list.Y + 3, WHITE); + LogicPage->Draw_Line(action1list.X - 1, action1list.Y + 3, action1list.X - 2 * RESFACTOR, action1list.Y + 3, WHITE); + + LogicPage->Draw_Line(event2list.X - 1, event2list.Y + 3, event2list.X - 5 * RESFACTOR, event2list.Y + 3, WHITE); + LogicPage->Draw_Line(event2list.X - 5 * RESFACTOR, event2list.Y + 3, action2list.X - 5 * RESFACTOR, action2list.Y + 3, WHITE); + LogicPage->Draw_Line(action2list.X - 1, action2list.Y + 3, action2list.X - 5 * RESFACTOR, action2list.Y + 3, WHITE); + } + + /* + ** Adjust the button list to match current control settings. + */ + event2list.Remove(); + switch (eventflag) { + case 0: + eventbtn.Set_Text(TXT_TRIGGER_JUST_EVENT); + break; + + case 1: + eventbtn.Set_Text(TXT_TRIGGER_AND); + event2list.Add(*commands); + break; + + case 2: + eventbtn.Set_Text(TXT_TRIGGER_OR); + event2list.Add(*commands); + break; + + case 3: + eventbtn.Set_Text(TXT_TRIGGER_LINKED); + event2list.Add(*commands); + break; + } + + /* + ** Prepare the primary event data field. + */ + htype1list.Remove(); + way1data.Remove(); + event1data.Remove(); + btype1list.Remove(); + itype1list.Remove(); + atype1list.Remove(); + utype1list.Remove(); + ttype1list.Remove(); + switch (Event_Needs(*event1list.Current_Item())) { + case NEED_HOUSE: + htype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype1list.Add(*commands); + break; + + case NEED_WAYPOINT: + way1data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event1data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype1list.Add(*commands); + break; + + case NEED_INFANTRY: + itype1list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype1list.Add(*commands); + break; + + case NEED_UNIT: + utype1list.Add(*commands); + break; + + default: + break; + } + + /* + ** Prepare the secondary event data field. + */ + htype2list.Remove(); + way2data.Remove(); + event2data.Remove(); + btype2list.Remove(); + itype2list.Remove(); + atype2list.Remove(); + utype2list.Remove(); + ttype2list.Remove(); + if (commands->Extract_Gadget(EVENT_LIST2)) { + switch (Event_Needs(*event2list.Current_Item())) { + case NEED_HOUSE: + htype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype2list.Add(*commands); + break; + + case NEED_WAYPOINT: + way2data.Add(*commands); + break; + + case NEED_TIME: + case NEED_NUMBER: + event2data.Add(*commands); + break; + + case NEED_STRUCTURE: + btype2list.Add(*commands); + break; + + case NEED_INFANTRY: + itype2list.Add(*commands); + break; + + case NEED_AIRCRAFT: + atype2list.Add(*commands); + break; + + case NEED_UNIT: + utype2list.Add(*commands); + break; + + default: + break; + } + } + + /* + ** Setup the action buttons and associated data entry fields. + */ + actionbtn.Remove(); + action2list.Remove(); + if (eventflag == 3) { + action2list.Add(*commands); + } + else { + actionbtn.Add(*commands); + if (actionflag) { + actionbtn.Set_Text(TXT_TRIGGER_AND); + action2list.Add(*commands); + } + else { + actionbtn.Set_Text(TXT_TRIGGER_JUST_ACTION); + } + } + + qlist1.Remove(); + spc1.Remove(); + htype3list.Remove(); + booltype1list.Remove(); + trtype1list.Remove(); + way3data.Remove(); + action1data.Remove(); + ttype3list.Remove(); + themetype1list.Remove(); + soundtype1list.Remove(); + movietype1list.Remove(); + speechtype1list.Remove(); + switch (Action_Needs(*action1list.Current_Item())) { + case NEED_MOVIE: + movietype1list.Add(*commands); + break; + + case NEED_SPECIAL: + spc1.Add(*commands); + break; + + case NEED_HOUSE: + htype3list.Add(*commands); + break; + + case NEED_BOOL: + booltype1list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype1list.Add(*commands); + break; + + case NEED_TEAM: + ttype3list.Add(*commands); + break; + + case NEED_NUMBER: + action1data.Add(*commands); + break; + + case NEED_WAYPOINT: + way3data.Add(*commands); + break; + + case NEED_THEME: + themetype1list.Add(*commands); + break; + + case NEED_SOUND: + soundtype1list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype1list.Add(*commands); + break; + + case NEED_QUARRY: + qlist1.Add(*commands); + break; + } + + qlist2.Remove(); + spc2.Remove(); + htype4list.Remove(); + booltype2list.Remove(); + trtype2list.Remove(); + way4data.Remove(); + action2data.Remove(); + ttype4list.Remove(); + themetype2list.Remove(); + soundtype2list.Remove(); + movietype2list.Remove(); + speechtype2list.Remove(); + if (commands->Extract_Gadget(ACTION_LIST2)) { + switch (Action_Needs(*action2list.Current_Item())) { + case NEED_MOVIE: + movietype2list.Add(*commands); + break; + + case NEED_SPECIAL: + spc2.Add(*commands); + break; + + case NEED_HOUSE: + htype4list.Add(*commands); + break; + + case NEED_BOOL: + booltype2list.Add(*commands); + break; + + case NEED_TRIGGER: + trtype2list.Add(*commands); + break; + + case NEED_TEAM: + ttype4list.Add(*commands); + break; + + case NEED_NUMBER: + action2data.Add(*commands); + break; + + case NEED_WAYPOINT: + way4data.Add(*commands); + break; + + case NEED_THEME: + themetype2list.Add(*commands); + break; + + case NEED_SOUND: + soundtype2list.Add(*commands); + break; + + case NEED_SPEECH: + speechtype2list.Add(*commands); + break; + + case NEED_QUARRY: + qlist2.Add(*commands); + break; + } + } + + /* + ** Collapse any dropped down list boxes. + */ + spc1.Collapse(); + spc2.Collapse(); + qlist1.Collapse(); + qlist2.Collapse(); + htype1list.Collapse(); + htype2list.Collapse(); + htype3list.Collapse(); + htype4list.Collapse(); + ttype1list.Collapse(); + ttype2list.Collapse(); + ttype3list.Collapse(); + ttype4list.Collapse(); + btype1list.Collapse(); + btype2list.Collapse(); + utype1list.Collapse(); + utype2list.Collapse(); + itype1list.Collapse(); + itype2list.Collapse(); + atype1list.Collapse(); + atype2list.Collapse(); + trtype1list.Collapse(); + trtype2list.Collapse(); + action1list.Collapse(); + action2list.Collapse(); + event1list.Collapse(); + event2list.Collapse(); + housebtn.Collapse(); + persbtn.Collapse(); + booltype1list.Collapse(); + booltype2list.Collapse(); + themetype1list.Collapse(); + themetype2list.Collapse(); + soundtype1list.Collapse(); + soundtype2list.Collapse(); + movietype1list.Collapse(); + movietype2list.Collapse(); + speechtype1list.Collapse(); + speechtype2list.Collapse(); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + // LogicPage->Unlock(); + } + + /* + ** Get user input + */ + KeyNumType input = commands->Input(); + + /* + ** Process input + */ + switch (input) { + case BUTTON_EVENT | KN_BUTTON: + eventflag = (eventflag + 1) % 4; + display = true; + break; + + case BUTTON_ACTION | KN_BUTTON: + actionflag = (actionflag == false); + display = true; + break; + + case DATA_SPEECH1 | KN_BUTTON: + Speak(VoxType(speechtype1list.Current_Index())); + display = true; + break; + + case DATA_SPEECH2 | KN_BUTTON: + Speak(VoxType(speechtype2list.Current_Index())); + display = true; + break; + + case DATA_SOUND1 | KN_BUTTON: + Sound_Effect(VocType(soundtype1list.Current_Index())); + display = true; + break; + + case DATA_SOUND2 | KN_BUTTON: + Sound_Effect(VocType(soundtype2list.Current_Index())); + display = true; + break; + + /* + ** Transfer all the necessary values from the edit fields into their + ** respective positions within the trigger object. + */ + case KN_RETURN: + case BUTTON_OK | KN_BUTTON: + + return(true); + + case KN_ESC: + case BUTTON_CANCEL | KN_BUTTON: + edittrigger_process = false; + + /* + ** Always signal a redraw if any of the buttons were touched. This + ** can be determined by examining the button bit flag in the input + ** return value. + */ + default: + if (input & KN_BUTTON) { + display = true; + } + break; + } + } + return(true); +} +#endif + +/*********************************************************************************************** + * TriggerTypeClass::TriggerTypeClass -- Constructor for trigger class object. * + * * + * This is the normal constructor for a trigger object. The trigger starts with no team * + * members, no mission, and default values for all settings. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/10/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::TriggerTypeClass(void) : + AbstractTypeClass(RTTI_TRIGGERTYPE, TriggerTypes.ID(this), TXT_NONE, "x"), + IsPersistant(VOLATILE), + EventControl(MULTI_ONLY), + ActionControl(MULTI_ONLY), + House(HOUSE_SPAIN) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::~TriggerTypeClass -- Deleting a trigger type deletes associated triggers. * + * * + * When a trigger type is deleted, then all triggers that refer to that type must also * + * be deleted as well. There can be no 'orphan' triggers in existence. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass::~TriggerTypeClass(void) +{ +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator new -- Allocates a trigger type class object. * + * * + * This routine will allocate a block of memory from the special trigger type object * + * pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated trigger type memory block. If there is * + * no more block available in the pool, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * TriggerTypeClass::operator new(size_t ) +{ + void * ptr = TriggerTypes.Allocate(); + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = true; + } + + return(ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::operator delete -- Returns a trigger type class object back to the pool * + * * + * This routine will return a previously allocated trigger type object to the private * + * memory pool from which it was allocated. * + * * + * INPUT: ptr -- Pointer to the trigger type class to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::operator delete(void * ptr) +{ + if (ptr) { + ((TriggerTypeClass *)ptr)->IsActive = false; + } + TriggerTypes.Free((TriggerTypeClass *)ptr); +} + + +/*********************************************************************************************** + * TriggerTypeClass::As_Target -- Convert this trigger type object into a target value. * + * * + * Use this routine to take this trigger type class object and convert it into a * + * target number. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the target number that represents this trigger type class object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET TriggerTypeClass::As_Target(void) const +{ + return(Build_Target(RTTI_TRIGGERTYPE, ID)); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Detach -- Removes attachments to the target object specified. * + * * + * When an object disappears from the game, it must be detached from all other objects that * + * may be referring to it. This routine will detach the specified target object from any * + * references to it in this trigger type class. * + * * + * INPUT: target -- The target object to be detached from this trigger type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Detach(TARGET target, bool) +{ + Action1.Detach(target); + Action2.Detach(target); +} + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Description -- Build a text description of the trigger type. * + * * + * This will build a (static) text description of the trigger type. Use this description * + * when displaying this trigger in a list box. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to a one line text description of this trigger. * + * * + * WARNINGS: The pointer returned actually points to a static buffer. The pointer is only * + * valid until this routine is called again. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +char const * TriggerTypeClass::Description(void) const +{ + char special; + switch (EventControl) { + case MULTI_AND: + special = '&'; + break; + + case MULTI_OR: + special = '|'; + break; + + case MULTI_LINKED: + special = '='; + break; + + default: + special = '.'; + break; + } + + char special2 = '.'; + if (ActionControl == MULTI_AND) { + special2 = '&'; + } + + char tbuf[32]; + char const * added = ""; + switch (Event_Needs(Event1.Event)) { + case NEED_NUMBER: + sprintf(tbuf, "%d", Event1.Data.Value); + added = tbuf; + break; + + case NEED_UNIT: + added = Text_String(UnitTypeClass::As_Reference(Event1.Data.Unit).Full_Name());; + break; + + case NEED_AIRCRAFT: + added = Text_String(AircraftTypeClass::As_Reference(Event1.Data.Aircraft).Full_Name());; + break; + + case NEED_STRUCTURE: + added = Text_String(BuildingTypeClass::As_Reference(Event1.Data.Structure).Full_Name()); + break; + + case NEED_INFANTRY: + added = Text_String(InfantryTypeClass::As_Reference(Event1.Data.Infantry).Full_Name()); + break; + + case NEED_WAYPOINT: + if (Event1.Data.Value < 26) { + sprintf(tbuf, "'%c'", Event1.Data.Value + 'A'); + } else { + sprintf(tbuf, "'%c%c'", (Event1.Data.Value / 26) + 'A'-1, (Event1.Data.Value % 26) + 'A'); + } + added = tbuf; + break; + + default: + break; + } + + /* + ** Persistence indicator value. + */ + char pers = 'V'; + if (IsPersistant == SEMIPERSISTANT) pers = 'S'; + if (IsPersistant == PERSISTANT) pers = 'P'; + + sprintf((char * const)&_buffer[0], "%4.4s\t %s %c%c%c %s%s", + IniName, + HouseTypeClass::As_Reference(House).Suffix, + pers, + special, + special2, + Name_From_Event(Event1.Event), + added); + return(_buffer); +} + +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Attaches_To -- Determines what trigger can attach to. * + * * + * This routine will examine the trigger events and return with a composit bitfield that * + * indicates what this trigger can be attached to. This is used for trigger placement * + * and logic processing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with AttachType bitfield representing what this trigger can be attached * + * to. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/30/1995 JLB : Created. * + *=============================================================================================*/ +AttachType TriggerTypeClass::Attaches_To(void) const +{ + AttachType attach = ::Attaches_To(Event1.Event); + + if (EventControl != MULTI_ONLY) { + attach = attach | ::Attaches_To(Event2.Event); + } + return(attach); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Read_INI -- reads triggers from the INI file * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * This routine reads in the triggers & creates them. Then, other classes can * + * get pointers to the triggers they're linked to. * + * * + * The routine relies on the TeamTypeClasses already being loaded so it can resolve * + * references to teams in this function. * + * * + * Cell Trigger pointers & IsTrigger flags are set in DisplayClass::Read_INI(), * + * and cleared in the Map::Init() routine (which clears all cell objects to 0's). * + * * + * Object's pointers are set in: * + * InfantryClass::Read_INI() * + * BuildingClass::Read_INI() * + * UnitClass::Read_INI() * + * TerrainClass::Read_INI() * + * The object trigger pointers are cleared in the ObjectClass constructor. * + * * + * The House's EMSListOf triggers is set in this routine, and cleared in the * + * HouseClass::Init() routine. * + * * + * INPUT: * + * buffer buffer to hold the INI data * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * This function must be called before any other class's Read_INI. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Read_INI(CCINIClass & ini) +{ + TriggerTypeClass *trigger; // Working trigger pointer. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + /* + ** Create a new trigger. + */ + trigger = new TriggerTypeClass(); + + /* + ** Get the trigger entry. + */ + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + /* + ** Fill in the trigger. + */ + trigger->Fill_In((char *)entry, buf); + } + + if (NewINIFormat < 2) { + /* + ** Fix up the self-referential trigger pointers. + */ + for (int trig_index = 0; trig_index < TriggerTypes.Count(); trig_index++) { + TriggerTypeClass * trigger = TriggerTypes.Ptr(trig_index); + + char * ptr = (char *)trigger->Action1.Trigger.Raw(); + if (ptr /*&& trigger->Action1.Trigger.Raw() != -1*/) { + trigger->Action1.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + + ptr = (char *)trigger->Action2.Trigger.Raw(); + if (ptr /*&& trigger->Action2.Trigger.Raw() != -1*/) { + trigger->Action2.Trigger = TriggerTypeClass::From_Name(ptr); + free(ptr); + } + } + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Fill_In -- fills in trigger from the given INI entry * + * * + * This routine fills in the given trigger with the given name, and values from * + * the given INI entry. * + * * + * (This routine is used by the scenario editor, to import teams from the MASTER.INI file.) * + * * + * INI entry format: * + * Triggername = Eventname, Actionname, Data, Housename, TeamName, IsPersistant * + * * + * INPUT: * + * name mnemonic for the desired trigger * + * entry INI entry to parse * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 11/28/1994 BR : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Fill_In(char * name, char * entry) +{ + assert(TriggerTypes.ID(this) == ID); + + /* + ** Set its name. + */ + Set_Name(name); + + IsPersistant = PersistantType(atoi(strtok(entry, ","))); + House = HousesType(atoi(strtok(NULL, ","))); + EventControl = MultiStyleType(atoi(strtok(NULL, ","))); + ActionControl = MultiStyleType(atoi(strtok(NULL, ","))); + + Event1.Read_INI(); + Event2.Read_INI(); + Action1.Read_INI(); + Action2.Read_INI(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::Write_INI -- Stores all trigger types to the INI database specified. * + * * + * This routine will write out all trigger type objects to the INI database. Any existing * + * trigger types in the database will be cleared out. * + * * + * INPUT: ini -- Reference to the INI database to have the trigger types added. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Write_INI(CCINIClass & ini) +{ + ini.Clear("Triggers"); + ini.Clear(INI_Name()); + + /* + ** Now write all the trigger data out + */ + for (int index = 0; index < TriggerTypes.Count(); index++) { +// for (int index = TriggerTypes.Count()-1; index >= 0; index--) { + char buf[256]; + TriggerTypeClass * trigger = TriggerTypes.Ptr(index); + + trigger->Build_INI_Entry(buf); + ini.Put_String(INI_Name(), trigger->IniName, buf); + } +} + + +/*********************************************************************************************** + * TriggerTypeClass::Build_INI_Entry -- Construct the INI entry into the buffer specified. * + * * + * This low level routine will take the information in this trigger type and store it * + * into a buffer such that the resultant string can be stored into an INI database for * + * later retrieval. * + * * + * INPUT: buffer -- Pointer to the buffer to store the INI entry string. * + * * + * OUTPUT: none * + * * + * WARNINGS: Be sure the buffer is big enough. Usually 128 bytes is more than sufficient. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Build_INI_Entry(char * buffer) const +{ + /* + ** Build the root portion of the trigger event. + */ + sprintf(buffer, "%d,%d,%d,%d,", IsPersistant, House, EventControl, ActionControl); + + /* + ** Append the event and action values. + */ + buffer += strlen(buffer); + Event1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Event2.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action1.Build_INI_Entry(buffer); + + strcat(buffer, ","); + buffer += strlen(buffer); + Action2.Build_INI_Entry(buffer); +} + + +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) +/*********************************************************************************************** + * TriggerTypeClass::Draw_It -- Draws this trigger as if it were a line in a list box. * + * * + * This routine is called when triggers are assigned to a list box and then must be drawn. * + * It will display an identifying text string with as much information as is useful. * + * * + * INPUT: index -- The index number of this line in the list box. * + * * + * x,y -- The pixel coordinate of the upper left corner of the text box. * + * * + * width,height -- The dimensions of the text box to display the description in. * + * * + * selected -- Is this a selected line? If so, then it should be highlighted. * + * * + * flags -- The text print flags to use to display this text string. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Draw_It(int , int x, int y, int width, int height, bool selected, TextPrintType flags) const +{ + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + static int _tabs[] = {13,40}; + if ((flags & 0x0F) == TPF_6PT_GRAD || (flags & 0x0F) == TPF_EFNT) { + + if (selected) { + flags = flags | TPF_BRIGHT_COLOR; + LogicPage->Fill_Rect(x, y, x + width - 1, y + height - 1, scheme->Shadow); + } else { + if (!(flags & TPF_USE_GRAD_PAL)) { + flags = flags | TPF_MEDIUM_COLOR; + } + } + + Conquer_Clip_Text_Print(Description(), x, y, scheme, TBLACK, flags, width, _tabs); + } else { + Conquer_Clip_Text_Print(Description(), x, y, (selected ? &ColorRemaps[PCOLOR_DIALOG_BLUE] : &ColorRemaps[PCOLOR_GREY]), TBLACK, flags, width, _tabs); + } +} +#endif + + +/*********************************************************************************************** + * TriggerTypeClass::Init -- Initialize the trigger type object management system. * + * * + * This routine should be called to initialize the trigger type object system. It should * + * be called when clearing out a scenario. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All trigger types will be destroyed by this routine. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void TriggerTypeClass::Init(void) +{ + TriggerTypes.Free_All(); +} + + +/*********************************************************************************************** + * TriggerTypeClass::From_Name -- Convert an ASCII name into a trigger type pointer. * + * * + * Given just an ASCII representation of the trigger type, this routine will return with * + * a pointer to the trigger type it refers to. Typical use of this is when parsing * + * scenario INI files. * + * * + * INPUT: name -- Pointer to the name to use to identify the trigger type class object to * + * be looked up. * + * * + * OUTPUT: Returns with a pointer to the trigger type class object that matches the name * + * specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TriggerTypeClass * TriggerTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (int index = 0; index < TriggerTypes.Count(); index++) { + if (stricmp(TriggerTypes.Ptr(index)->Name(), name) == 0) { + return(TriggerTypes.Ptr(index)); + } + } + } + return(NULL); +} diff --git a/code/redalert/trigtype.h b/code/redalert/trigtype.h new file mode 100644 index 0000000..629fe87 --- /dev/null +++ b/code/redalert/trigtype.h @@ -0,0 +1,149 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TRIGTYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TRIGTYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 06/05/96 * + * * + * Last Update : June 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#pragma once + +#include "tevent.h" +#include "taction.h" + +class TriggerClass; + +/* +** There can be multiple trigger events and trigger actions. This enumeration is used to +** indicate if there are multiple events/actions and what their relationship is. +*/ +typedef enum MultiStyleType : unsigned char { + MULTI_ONLY, // "Only" main trigger action/event? + MULTI_AND, // "And" secondary trigger action/event? + MULTI_OR, // "Or" secondary event? + MULTI_LINKED // Cause and effect pairs are linked? +} MultiStyleType; + + +class TriggerTypeClass : public AbstractTypeClass +{ + public: + unsigned IsActive:1; + + typedef enum PersistantType : unsigned char { + VOLATILE = 0, + SEMIPERSISTANT = 1, + PERSISTANT = 2 + } PersistantType; + + /* + ** This flag controls whether the trigger destroys itself after it goes + ** off. + ** 0 = trigger destroys itself immediately after going off, and removes + ** itself from all objects it's attached to + ** 1 = trigger is "Semi-Persistent"; it maintains a count of all objects + ** it's attached to, and only actually "springs" after its been + ** triggered from all the objects; then, it removes itself. + ** 2 = trigger is Fully Persistent; it just won't go away. + */ + PersistantType IsPersistant; + + /* + ** For house-specific events, this is the house for that event. + */ + HousesType House; + + /* + ** Each trigger must have an event which activates it. This is the event that is + ** used to activate this trigger. + */ + TEventClass Event1; + TEventClass Event2; + MultiStyleType EventControl; + + /* + ** This is the action to perform when the trigger event occurs. + */ + TActionClass Action1; + TActionClass Action2; + MultiStyleType ActionControl; + + + TriggerTypeClass(void); + TriggerTypeClass(NoInitClass const & x) : AbstractTypeClass(x), Event1(x), Event2(x), Action1(x), Action2(x) {}; + virtual ~TriggerTypeClass(void); + + static void * operator new(size_t ); + static void * operator new(size_t, void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + + /* + ** Initialization: clears all trigger types in preparation for new scenario + */ + static void Init(void); + + /* + ** File I/O routines + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + void Fill_In(char * name, char * entry); + void Build_INI_Entry(char * buf) const; + + static char * INI_Name(void) {return "Trigs";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + void Code_Pointers(void); + void Decode_Pointers(void); + + /* + ** Processing routines + */ + TriggerClass * Create_One_Of(void) const; + void Destroy_All_Of(void) const; + + /* + ** Utility routines + */ + void Detach(TARGET target, bool all=true); + AttachType Attaches_To(void) const; + TARGET As_Target(void) const; + static TriggerTypeClass * From_Name(char const * name); + bool Edit(void); +#if defined(CHEAT_KEYS) || defined(SCENARIO_EDITOR) + char const * Description(void) const; + operator const char * (void) const {return(Description());}; +#endif + void Draw_It(int index, int x, int y, int width, int height, bool selected, TextPrintType flags) const; +// jmarshall - made this instance based. + char _buffer[128]; +// jmarshall end +}; + diff --git a/code/redalert/turret.cpp b/code/redalert/turret.cpp new file mode 100644 index 0000000..7e2f039 --- /dev/null +++ b/code/redalert/turret.cpp @@ -0,0 +1,111 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\turret.cpv 3.1 13 Mar 1996 09:49:34 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : August 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TurretClass::AI -- Handles the reloading of the turret weapon. * + * TurretClass::Can_Fire -- Determines if turret can fire upon target. * + * TurretClass::Debug_Dump -- Debug printing of turret values. * + * TurretClass::Fire_At -- Try to fire upon the target specified. * + * TurretClass::Fire_Coord -- Determines the coorindate that projectile would appear. * + * TurretClass::Fire_Direction -- Determines the directinon of firing. * + * TurretClass::Ok_To_Move -- Queries whether the vehicle can move. * + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * TurretClass::Unlimbo -- Unlimboes turret object. * + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "turret.h" + + +/*********************************************************************************************** + * TurretClass::~TurretClass -- Default destructor for turret class objects. * + * * + * This is the default destructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::~TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- The default constructor for turret class objects. * + * * + * This is the default constructor for turret class objects. It does nothing. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(void) +{ +} + + +/*********************************************************************************************** + * TurretClass::TurretClass -- Normal constructor for the turret class. * + * * + * This is the normal constructor for the turret class. It merely sets the turret up to * + * face north. * + * * + * INPUT: classid -- The type id for this particular unit. * + * * + * house -- The house that this unit will belong to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 02/02/1995 JLB : Created. * + *=============================================================================================*/ +TurretClass::TurretClass(UnitType classid, HousesType house) : + DriveClass(classid, house) +{ +} + + + diff --git a/code/redalert/turret.h b/code/redalert/turret.h new file mode 100644 index 0000000..e8b3de4 --- /dev/null +++ b/code/redalert/turret.h @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\turret.h_v 3.1 13 Mar 1996 09:43:14 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TURRET.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 25, 1994 * + * * + * Last Update : April 25, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TURRET_H +#define TURRET_H + +#include "drive.h" + +class TurretClass : public DriveClass +{ + public: + + /* + ** File I/O. + */ + virtual void Code_Pointers(void); + virtual void Decode_Pointers(void); + + protected: + TurretClass(UnitType classid, HousesType house); + TurretClass(void); + virtual ~TurretClass(void); +}; + + +#endif diff --git a/code/redalert/txtlabel.cpp b/code/redalert/txtlabel.cpp new file mode 100644 index 0000000..98c0bbd --- /dev/null +++ b/code/redalert/txtlabel.cpp @@ -0,0 +1,98 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TXTLABEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * TextLableClass::Draw_Me -- Graphical update routine * + * TextLableClass::TextLabelClass -- Constructor * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*********************************************************************************************** + * TextLableClass::TextLabelClass -- Constructor * + * * + * INPUT: * + * txt pointer to text buffer to print from * + * x x-coord for text printing * + * y y-coord for text printing * + * color color to print in * + * style style to print (determines the meaning of x & y) * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +TextLabelClass::TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style) : GadgetClass(x, y, 1, 1, 0, 0) +{ + Text = txt; + Color = color; + Style = style; + UserData1 = 0; + UserData2 = 0; + PixWidth = -1; +} + + +/*********************************************************************************************** + * TextLableClass::Draw_Me -- Graphical update routine * + * * + * INPUT: * + * forced true = draw regardless of the current redraw flag state * + * * + * OUTPUT: * + * true = gadget was redrawn, false = wasn't * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 03/24/1995 BRR : Created. * + *=============================================================================================*/ +int TextLabelClass::Draw_Me(int forced) +{ + if (GadgetClass::Draw_Me(forced)) { + if (PixWidth == -1) { + Simple_Text_Print(Text, X, Y, Color, TBLACK, Style); +// Fancy_Text_Print(Text, X, Y, Color, TBLACK, Style); + } else { + Conquer_Clip_Text_Print(Text, X, Y, Color, TBLACK, Style, PixWidth); + } + return(true); + } + return(false); +} diff --git a/code/redalert/txtlabel.h b/code/redalert/txtlabel.h new file mode 100644 index 0000000..66146a0 --- /dev/null +++ b/code/redalert/txtlabel.h @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TXTLABEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TXTLABEL.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : 02/06/95 * + * * + * Last Update : February 6, 1995 [BR] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TXTLABEL_H +#define TXTLABEL_H + +class TextLabelClass : public GadgetClass +{ + public: + /* + ** Constructor/Destructor + */ + TextLabelClass(char *txt, int x, int y, RemapControlType * color, + TextPrintType style); + + /* + ** Overloaded draw routine + */ + virtual int Draw_Me(int forced = false); + + /* + ** Sets the displayed text of the label + */ + virtual void Set_Text(char *txt) {Text = txt;}; + + /* + ** General-purpose data fields + */ + unsigned long UserData1; + unsigned long UserData2; + TextPrintType Style; + char *Text; + RemapControlType * Color; + int PixWidth; +}; + +#endif + diff --git a/code/redalert/txtprnt.cpp b/code/redalert/txtprnt.cpp new file mode 100644 index 0000000..d08ef7f --- /dev/null +++ b/code/redalert/txtprnt.cpp @@ -0,0 +1,292 @@ +/** + * @file + * + * @author CCHyper + * @author OmniBlade + * + * @brief Low level bitmap font string blitter. + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +#include "FUNCTION.H" +#include +#include + +using std::memcpy; +using std::memset; + +#define le16toh(x) x + +#ifdef GAME_DLL +extern char*& g_fontWidthBlockPtr; +#else +uint8_t g_ColorXlat[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +uint8_t byte_608988[] = { + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0 }; + +char* g_FontPtr = nullptr; +char* g_fontWidthBlockPtr = nullptr; +uint8_t g_FontHeight = 8; +uint8_t g_FontWidth = 8; +int g_FontXSpacing; +int g_FontYSpacing; +#endif + +struct FontHeader +{ + uint16_t file_size; // Size of the file + uint8_t file_version; // file version? 0==EOB? 1==RA? 2==TS? what about RA2? + uint8_t unknown; // 5==TD/RA 0==TS? unused in RA + uint16_t start; // offset to start of font description Pad, always 0x0010 + uint16_t data_offset_list; // offset to bitmap data offset list, always 0x0014 as follows header + uint16_t width_list; // points to list of widths + uint16_t data; // points to graphics data, unused by RA code + uint16_t line_list; // points to list of heights (or lines) + uint16_t unknown_const; // Unknown entry (always 0x1012 or 0x1011) unused by RA + uint8_t pad; // padding? + uint8_t number_of_chars; // Number of chars in font - 1 + uint8_t max_height; // Max char height + uint8_t max_width; // Max char width +}; + +extern "C" void __cdecl Set_Font_Palette_Range(const void* colors, int first, int last) +{ + byte* pal = (byte*)colors; + first &= 15; + last &= 15; + + for (int i = first; i < last + 1; ++i) { + g_ColorXlat[i] = *pal++; + } +} + +void* __cdecl Set_Font(void const* fontptr) +{ + FontHeader* header; + + header = (FontHeader*)(fontptr); + + if (fontptr) { + g_FontPtr = (char*)(fontptr); + g_fontWidthBlockPtr = (char*)(fontptr) + le16toh(header->width_list); + FontHeight = g_FontHeight = le16toh(header->max_height); + FontWidth = g_FontWidth = le16toh(header->max_width); + } + + return header; +} + +int __cdecl Char_Pixel_Width(char letter) +{ + // Cast to unsigned as all blocks index from zero to NumberOfChars + 1 + // Original watcom compiler had char as an unsigned type by default. + return g_fontWidthBlockPtr[(uint8_t)letter] + g_FontXSpacing; +} + +unsigned int __cdecl String_Pixel_Width(const char* string) +{ + uint16_t width; + uint16_t linewidth = 0; + + if (string != nullptr) { + width = 0; + + while (*string != '\0') { + if (*string == '\r') { + ++string; + linewidth = max(linewidth, width); + width = 0; + } + else { + width += Char_Pixel_Width(*string++); + } + } + + return max(linewidth, width); + } + + return 0; +} + +int __cdecl Buffer_Print2(GraphicViewPortClass& vp, const char* string, int x, int y, uint8_t fground, uint8_t bground) +{ + FontHeader* fntheader = reinterpret_cast(g_FontPtr); + int pitch = vp.Get_Full_Pitch(); + uint8_t* offset = y * pitch + (uint8_t*)(vp.Get_Offset()); + uint8_t* dst = x + offset; + int char_width = 0; + int base_x = x; + + if (g_FontPtr != nullptr) { + uint16_t* datalist = + reinterpret_cast(reinterpret_cast(g_FontPtr) + le16toh(fntheader->data_offset_list)); + uint8_t* widthlist = reinterpret_cast(g_FontPtr) + le16toh(fntheader->width_list); + uint16_t* linelist = + reinterpret_cast(reinterpret_cast(g_FontPtr) + le16toh(fntheader->line_list)); + + int fntheight = fntheader->max_height; + int ydisplace = g_FontYSpacing + fntheight; + + // Check if we are drawing in bounds, we don't draw clipped text + if (y + fntheight <= vp.Get_Height()) { + int fntbottom = y + fntheight; + // Set colors to draw with + g_ColorXlat[1] = fground; + g_ColorXlat[0] = bground; + + while (true) { + // Handle a new line + uint8_t char_num; + uint8_t* char_dst; + while (true) { + char_num = uint8_t(*string); + + if (char_num == '\0') { + return 0; + } + + char_dst = dst; + ++string; + + if (char_num != '\n' && char_num != '\r') { + break; + } + + // We don't handle clipping text, it either draws or it doesn't + if (ydisplace + fntbottom > vp.Get_Height()) { + return 0; + } + + // If its a new line, we are just going to set the start to an increased y displacement, hence x_pos + // becomes 0 + x = char_num == '\n' ? 0 : base_x; + dst = ydisplace * pitch + offset + x; + offset += ydisplace * pitch; + fntbottom += ydisplace; + } + + // Move to the start of the next char + char_width = widthlist[char_num]; + dst += g_FontXSpacing + char_width; + + // Handle text wrapping for long strings + if (g_FontXSpacing + char_width + x > vp.Get_Width()) { + --string; + char_num = '\0'; + + // We don't handle clipping text, it either draws or it doesn't + if (ydisplace + fntbottom > vp.Get_Height()) { + return 0; + } + + // If its a new line, we are just going to set the start to an increased y displacement, hence x_pos + // becomes 0 + x = char_num == '\n' ? 0 : base_x; + dst = ydisplace * pitch + offset + x; + offset += ydisplace * pitch; + fntbottom += ydisplace; + + continue; + } + + // Prepare variables for drawing + x += g_FontXSpacing + char_width; + int next_line = pitch - char_width; + uint8_t* char_data = reinterpret_cast(g_FontPtr) + datalist[char_num]; + int char_lle = le16toh(linelist[char_num]); + int char_ypos = char_lle & 0xFF; + int char_lines = char_lle >> 8; + int char_height = fntheight - (char_ypos + char_lines); + int blit_width = widthlist[char_num]; + + // Fill unused lines if we have a color other than 0 + if (char_ypos) { + uint8_t color = g_ColorXlat[0]; + if (color) { + for (int i = char_ypos; i; --i) { + memset(char_dst, color, blit_width); + char_dst += pitch; + } + } + else { + char_dst += pitch * char_ypos; + } + } + + // Draw the character + if (char_lines) { + for (int i = 0; i < char_lines; ++i) { + int width_todraw = blit_width; + + while (width_todraw) { + uint8_t color_packed; + color_packed = *char_data++; + uint8_t color = g_ColorXlat[color_packed & 0x0F]; + + if (color) { + *char_dst = color; + } + + ++char_dst; + --width_todraw; + + if (width_todraw == 0) { + break; + } + + color = g_ColorXlat[color_packed >> 4]; + + if (color) { + *char_dst = color; + } + + ++char_dst; + --width_todraw; + } + + char_dst += next_line; + } + + // Fill any remaining unused lines. + if (char_height) { + uint8_t color = g_ColorXlat[0]; + + if (color) { + for (int i = char_height; i; --i) { + memset(char_dst, color, blit_width); + char_dst += pitch; + } + } + } + } + } + } + } + return 0; +} + +extern "C" LONG __cdecl Buffer_Print(void* thisptr, const char* str, int x, int y, int fcolor, int bcolor) +{ + //Buffer_Print2(*(GraphicViewPortClass*)thisptr, str, x, y, fcolor, bcolor); + GL_DrawText(fcolor, x, y, (char *)str); + return 0; +} diff --git a/code/redalert/type.h b/code/redalert/type.h new file mode 100644 index 0000000..2dc0888 --- /dev/null +++ b/code/redalert/type.h @@ -0,0 +1,2061 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/TYPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : TYPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TYPE_H +#define TYPE_H + +#include "mission.h" +#include "target.h" + +class MapEditClass; +class HouseClass; +class WeaponTypeClass; + +/*************************************************************************** +** This is the abstract type class. It holds information common to all +** objects that might exist. This contains the name of the object type. +*/ +class AbstractTypeClass +{ + public: + + /* + ** This serves to identify the object class. The ID corresponds to the + ** variation number (e.g., UNIT_TANK1, UNIT_TANK2, etc.). + */ + RTTIType RTTI; + int ID; + + /* + ** This is the internal control name of the object. This name does + ** not change regardless of language specified. This is the name + ** used in scenario control files and for other text based unique + ** identification purposes. + */ + char IniName[24]; + + /* + ** The translated (language specific) text name number of this object. + ** This number is used to fetch the object's name from the language + ** text file. Whenever the name of the object needs to be displayed, + ** this is used to determine the text string. + */ + int FullName; + + AbstractTypeClass(RTTIType rtti, int id, int name, char const * ini); + AbstractTypeClass(NoInitClass const & ) {}; + ~AbstractTypeClass(void) {}; + + RTTIType What_Am_I(void) const {return(RTTI);}; + TARGET As_Target(void) const {return(Build_Target(RTTI, ID));}; + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Full_Name(void) const; + char const * Name(void) const {return(IniName);} + void Set_Name(char const * buf) const { + strncpy((char *)IniName, buf, sizeof(IniName)); + ((char &)IniName[sizeof(IniName)-1]) = '\0'; + }; + virtual int Get_Ownable(void) const; + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} +}; + + +/********************************************************************** +** Each house has certain unalienable characteristics. This structure +** elaborates these. +*/ +class HouseTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the house number (enum). This is a unique identification + ** number for the house. + */ + HousesType House; + + /* + ** This is the filename suffix to use when creating a house specific + ** file name. It is three characters long. + */ + char Suffix[_MAX_EXT]; + + /* + ** This is the "lemon percentage" to use when determining if a particular + ** object owned by this house is to be flagged as a "lemon". Objects so + ** flagged have a greater break-down chance. The percentage is expressed + ** as a fixed point number with 0x000 meaning 0% and 0x100 meaning 100%. + */ + unsigned Lemon; + + /* + ** This points to the default remap table for this house. + */ + PlayerColorType RemapColor; + + /* + ** This is a unique ASCII character used when constructing filenames. It + ** serves a similar purpose as the "Suffix" element, but is only one + ** character long. + */ + char Prefix; + + /* + ** This controls the various general adjustments to the house owned + ** unit and building ratings. The default value for these ratings is + ** a fixed point number of 1.0. + */ + fixed FirepowerBias; + fixed GroundspeedBias; + fixed AirspeedBias; + fixed ArmorBias; + fixed ROFBias; + fixed CostBias; + fixed BuildSpeedBias; + + //------------------------------------------------------------------------ + HouseTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + HouseTypeClass(HousesType house, + char const * ini, + int fullname, + char const * ext, + int lemon, + PlayerColorType remapcolor, + char prefix); + + unsigned char const * Remap_Table(void) const; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static HousesType From_Name(char const * name); + static HouseTypeClass & As_Reference(HousesType house); + static void One_Time(void); + static void Init_Heap(void); + + virtual bool Read_INI(CCINIClass & ini); +}; + + +/*************************************************************************** +** This the the common base class of game objects. Since these values +** represent the unchanging object TYPES, this data is initialized at game +** start and not changed during play. It is "const" data. +*/ +class ObjectTypeClass : public AbstractTypeClass +{ + public: + /* + ** This is the base name of the graphic data associated with this object + ** type. If the graphic name is a null string, then there is no graphic + ** associated with this object type. + */ + char GraphicName[_MAX_FNAME]; + + /* + ** Is this object squashable by heavy vehicles? If it is, then the vehicle + ** can travel over this object and destroy it in the process. + */ + unsigned IsCrushable:1; + + /* + ** Does this object type NOT show up on radar scans? If true, then in any + ** radar display, only the underlying ground will be show, not this object. + ** Most terrain falls into this category, but only a few special real units/buildings + ** do. + */ + unsigned IsStealthy:1; + + /* + ** It is legal to "select" some objects in the game. If it is legal to select this + ** object type then this flag will be true. Selected game objects typically display + ** a floating health bar and allows special user I/O control. + */ + unsigned IsSelectable:1; + + /* + ** Can this object be the target of an attack or move command? Typically, only objects + ** that take damage or can be destroyed are allowed to be a target. + */ + unsigned IsLegalTarget:1; + + /* + ** "Insignificant" objects will not be announced when they are destroyed or when they + ** appear. Terrain elements and some lesser vehicles have this characteristic. + */ + unsigned IsInsignificant:1; + + /* + ** Is this object immune to normal combat damage? Rocks and other inert type terrain + ** object are typically of this type. + */ + unsigned IsImmune:1; + + /* + ** "Sentient" objects are ones that have logic AI processing performed on them. All + ** vehicles, buildings, infantry, and aircraft are so flagged. Terrain elements also + ** fall under this category, but only because certain animation effects require this. + */ + unsigned IsSentient:1; + + /* + ** If this object type affects the occupation and collision logic associated with + ** cells, then this flag will be true. Typically, this characteristic is limited + ** to buildings, units, terrain objects, and landed aircraft. + */ + unsigned IsFootprint:1; + + /* + ** The defense of this object is greatly affected by the type of armor + ** it possesses. This value specifies the type of armor. + */ + ArmorType Armor; + + /* + ** This is the maximum strength of this object type. + */ + unsigned short MaxStrength; + + /* + ** These point to the shape imagery for this object type. Since the shape imagery + ** exists in a separate file, the data is filled in after this object is constructed. + ** The "mutable" keyword allows easy modification to this otherwise const object. + */ + void const * ImageData; +// jmarshall + struct Image_t *HDImageData; +// jmarshall end + /* + ** Points to the dimension data for each shape in the image list. By using this + ** data, the minimum number of cells will be redrawn when the object changes shape. + */ + Rect * DimensionData; + + /* + ** This points to the radar imagery for this object. + */ + void const * RadarIcon; + + //-------------------------------------------------------------------- + ObjectTypeClass(NoInitClass const & x) : AbstractTypeClass(x) {} + ObjectTypeClass( RTTIType rtti, + int id, + bool is_sentient, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_footprint, + int fullname, + char const * name + ); + + static void One_Time(void); + + bool Is_Foot(void) const {return(RTTI == RTTI_INFANTRYTYPE || RTTI == RTTI_UNITTYPE || RTTI == RTTI_VESSELTYPE || RTTI == RTTI_AIRCRAFTTYPE);}; + char const * Graphic_Name(void) const {if (GraphicName[0] != '\0') return(GraphicName); return(Name());} + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const = 0; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const = 0; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual BuildingClass * Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const; + virtual void const * Get_Cameo_Data(void) const; +// jmarshall + struct Image_t* Get_HDImage_Data(void) const { return HDImageData; } +// jmarshall end + void const * Get_Image_Data(void) const {return ImageData;}; + void const * Get_Radar_Data(void) const {return RadarIcon;}; + + #ifdef SCENARIO_EDITOR + virtual void Display(int, int, WindowNumberType, HousesType) const {}; + #endif + + static void const * SelectShapes; + static void const * PipShapes; +}; + + +/*************************************************************************** +** This class is the common data for all objects that can be owned, produced, +** or delivered as reinforcements. These are objects that typically contain +** crews and weapons -- the fighting objects of the game. +*/ +class TechnoTypeClass : public ObjectTypeClass +{ + public: + + /* + ** This controls how this object type is remapped when it is displayed + ** in the sidebar. + */ + RemapType Remap; + + /* + ** Is this object ownable by all sides in a multiplayer game? There are some + ** special case objects that need this override ability. + */ + unsigned IsDoubleOwned:1; + + /* + ** If this object should be completely and always invisible to the enemy, then + ** this flag will be true. + */ + unsigned IsInvisible:1; + + /* + ** If this object can serve as a good leader for a group selected + ** series of objects, then this flag will be true. Unarmed or + ** ability challenged units do not make good leaders. This flag is + ** also used to indicate the primary factory when dealing with + ** buildings. + */ + unsigned IsLeader:1; + + /* + ** Does this object have the ability to detect the presence of a nearby + ** cloaked object? + */ + unsigned IsScanner:1; + + /* + ** If this object is always given its proper name rather than a generic + ** name, then this flag will be true. Typically, civilians and Dr. Moebius + ** fall under this category. + */ + unsigned IsNominal:1; + + /* + ** If the artwork for this object (only for generics) is theater specific, then + ** this flag will be true. Civilian buildings are a good example of this. + */ + unsigned IsTheater:1; + + /* + ** Does this object type contain a rotating turret? Gun emplacements, SAM launchers, + ** and many vehicles contain a turret. If a turret is present, special rendering and + ** combat logic must be performed. + */ + unsigned IsTurretEquipped:1; + + /* + ** Certain objects can be repaired. For buildings, they repair "in place". For units, + ** they must travel to a repair center to be repaired. If this flag is true, then + ** allow the player or computer AI to repair the object. + */ + unsigned IsRepairable:1; + + /* + ** Does this object contain a crew? If it does, then when the object is destroyed, there + ** is a distinct possibility that infantry will "pop out". Only units with crews can + ** become "heros". + */ + unsigned IsCrew:1; + + /* + ** This tells whether this unit should EVER be remapped when it is displayed + ** on the tactical map. Normally, the unit is remapped, but for certain civilian + ** object, remapping is not to be performed, regardless of owner. + */ + unsigned IsRemappable:1; + + /* + ** Is the unit capable of cloaking? Only Stealth Tank can do so now. + */ + unsigned IsCloakable:1; + + /* + ** Can this object self heal up to half strength? Mammoth tanks from C&C had this + ** feature. + */ + unsigned IsSelfHealing:1; + + /* + ** If this object explodes violently when destroyed, then this flag will be true. + ** The type of explosion is based on the warhead type and the damage generated + ** corresponds to the full strength of the object. + */ + unsigned IsExploding:1; + + /* + ** This specifies the zone that an object of this type should recognize. Zones + ** of this type or lower will be considered "possible to travel to". + */ + MZoneType MZone; + + /* + ** When determining threat, the range can be overridden to be the value + ** specified here. Otherwise, the range for enemy scan is equal to the + ** longest weapon range the object has. If the value is zero, then the + ** weapon range is used. + */ + LEPTON ThreatRange; + + /* + ** If this is a transporter object (e.g., hovercraft, chinook, APC), then this + ** value specifies the maximum number of passengers it may carry. + */ + int MaxPassengers; + + /* + ** Most objects have the ability to reveal the terrain around themselves. + ** This sight range (expressed in cell distance) is specified here. If + ** this value is 0, then this unit never reveals terrain. Bullets are + ** typically of this nature. + */ + int SightRange; + + /* + ** This is the credit cost to produce this object (presuming production is + ** allowed). + */ + int Cost; + + /* + ** The tech level that this object can be produced at. + */ + unsigned Level; + + /* + ** This specifies the building prerequisites required before an object + ** of this type can be produced. + */ + long Prerequisite; + + /* + ** The risk and reward values are used to determine targets and paths + ** toward targets. When heading toward a target, a path of least + ** risk will be followed. When picking a target, the object of + ** greatest reward will be selected. The values assigned are + ** arbitrary. + */ + int Risk,Reward; + + /* + ** This value indicates the maximum speed that this object can achieve. + */ + MPHType MaxSpeed; + + /* + ** This indicates the speed (locomotion) type for this unit. Through this + ** value the movement capabilities are deduced. + */ + SpeedType Speed; + + /* + ** This is the maximum number of ammo shots this object can hold. If + ** this number is -1, then this indicates unlimited ammo. + */ + int MaxAmmo; + + /* + ** This is a bit field representing the houses that are allowed to + ** own (by normal means) this particular object type. This value is + ** typically used in production contexts. It is possible for a side + ** to take possession of an object type otherwise not normally allowed. + ** This event usually occurs as a result of capture. + */ + long Ownable; + + /* + ** This is the small icon image that is used to display the object in + ** the sidebar for construction selection purposes. + */ + void const * CameoData; + + /* + ** The number of animation frames allotted to rotation is specified here. + ** For an object that has no rotation, this value will be 1. For normal + ** vehicles this value will be 32. There are some special case units that + ** have intermediate rotation frames. + */ + int Rotation; + + /* + ** This is the rotational speed of the object. This value represents the + ** turret or body rotation speed expresses as 360/256ths rotation steps per + ** game tick. + */ + int ROT; + + /* + ** These are the weapons that this techno object is armed with. + */ + WeaponTypeClass const * PrimaryWeapon; + WeaponTypeClass const * SecondaryWeapon; + + /* + ** These specify the lepton offsets to locate the exact coordinate of the + ** 'tip of the barrel' for the weapon. This is used for generating the bullet + ** at the proper location. + */ + int HorizontalOffset; // Distance to move east (compensates for offsets in animation from center coordinate). + int VerticalOffset; // Distance to move north (compensates for perspective). + int PrimaryOffset; // Offset along turret centerline and facing. + int PrimaryLateral; // Sideways offset from turret centerline and facing. + int SecondaryOffset; + int SecondaryLateral; + + /* + ** Points you're awarded for destroying an object of this type, and + ** points you lose if you lose an object of this type. + */ + int Points; + + //-------------------------------------------------------------------- + TechnoTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TechnoTypeClass( + RTTIType rtti, + int id, + int name, + char const * ininame, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_nominal, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + bool is_footprint, + int rotation, + SpeedType speed, + int horizontaloffset=0 + ); + + bool Is_Two_Shooter(void) const; + int Legal_Placement(CELL pos) const; + virtual int Raw_Cost(void) const; + virtual int Max_Passengers(void) const {return(MaxPassengers);} + virtual int Repair_Cost(void) const; + virtual int Repair_Step(void) const; + virtual void const * Get_Cameo_Data(void) const; + virtual int Cost_Of(void) const; + virtual int Time_To_Build(void) const; + virtual int Get_Ownable(void) const; + virtual bool Read_INI(CCINIClass & ini); + + /* + ** This is a pointer to the wake shape (as needed by the gunboat). + */ + static void const * WakeShapes; + static void const * TurretShapes; + static void const * SamShapes; + static void const * MGunShapes; +}; + + +/*************************************************************************** +** Building types need some special information custom to buildings. This +** is a derived class that elaborates these additional data elements. +*/ +class BuildingTypeClass : public TechnoTypeClass { + public: + /* + ** Is this building allowed to be considered for building adjacency + ** checking? If false, then building off of (or adjacent to) this building + ** is not considered. + */ + unsigned IsBase:1; + + /* + ** If this building is a fake, this flag will be set. + */ + unsigned IsFake:1; + + /* + ** This flag controls whether the building is equiped with a dirt + ** bib or not. A building with a bib has a dirt patch automatically + ** attached to the structure when it is placed. + */ + unsigned IsBibbed:1; + + /* + ** If this building is a special wall type, such that it exists as a building + ** for purposes of construction but transforms into an overlay wall object when + ** it is placed on the map, then this flag will be true. + */ + unsigned IsWall:1; + + /* + ** Buildings can have either simple or complex damage stages. If simple, + ** then the second to the last frame is the half damage stage, and the last + ** frame is the complete damage stage. For non-simple damage, buildings + ** have a complete animation set for damaged as well as undamaged condition. + ** Turrets, oil pumps, and repair facilities are a few examples. + */ + unsigned IsSimpleDamage:1; + + /* + ** Certain building types can be captures by enemy infantry. For those + ** building types, this flag will be true. Typically, military or hardened + ** structures such as turrets cannot be captured. + */ + unsigned IsCaptureable:1; + + /* + ** If this building really only has cosmetic idle animation, then this flag will be + ** true if this animation should run at a relatively constant rate regardless of game + ** speed setting. + */ + unsigned IsRegulated:1; + + /* + ** Does this building require power to function? Usually, this isn't the case. The building + ** normally either has no effect by power level or is gradually reduced in effectiveness. This + ** flag is for those buildings that completely cease to function when the power drops below + ** full. + */ + unsigned IsPowered:1; + + /* + ** If this flag is true, then the building cannot be sold even if it could have been built. This + ** is especially useful for mines which can be built but cannot be sold. + */ + unsigned IsUnsellable:1; + + /* + ** This is the direction (from the center cell) of the building in order to find a + ** legitimate foundation square. This location will be used for targeting and capture + ** move destination purposes. + */ + FacingType FoundationFace; + + /* + ** Adjacent distance for building next to. + */ + int Adjacent; + + /* + ** This flag specifies the type of object this factory building can "produce". For non + ** factory buildings, this value will be RTTI_NONE. + */ + RTTIType ToBuild; + + /* + ** For building that produce ground units (infantry and vehicles), there is a default + ** exit point defined. This point is where the object is first placed on the map. + ** Typically, this is located next to a door. The unit will then travel on to a clear + ** terrain area and enter normal game processing. + */ + COORDINATE ExitCoordinate; + + /* + ** When determine which cell to head toward when exiting a building, use the + ** list elaborated by this variable. There are directions of exit that are + ** more suitable than others. This list is here to inform the system which + ** directions those are. + */ + short const * ExitList; + + /* + ** This is the structure type identifier. It can serve as a unique + ** identification number for building types. + */ + StructType Type; + + /* + ** This is the starting facing to give this building when it first + ** gets constructed. The facing matches the final stage of the + ** construction animation. + */ + DirType StartFace; + + /* + ** This is the Tiberium storage capacity of the building. The sum of all + ** building's storage capacity is used to determine how much Tiberium can + ** be accumulated. + */ + int Capacity; + + /* + ** Each building type produces and consumes power. These values tell how + ** much. + */ + int Power; + int Drain; + + /* + ** This is the size of the building. This size value is a rough indication + ** of the building's "footprint". + */ + BSizeType Size; + + /********************************************************************** + ** For each stage that a building may be in, its animation is controlled + ** by this structure. It dictates the starting and length of the animation + ** frames needed for the specified state. In addition it specifies how long + ** to delay between changes in animation. With this data it is possible to + ** control the appearance of all normal buildings. Turrets and SAM sites are + ** an exception since their animation is not merely cosmetic. + */ + typedef struct { + int Start; // Starting frame of animation. + int Count; // Number of frames in this animation. + int Rate; // Number of ticks to delay between each frame. + } AnimControlType; + AnimControlType Anims[BSTATE_COUNT]; + + /*--------------------------------------------------------------------------- + ** This is the building type explicit constructor. + */ + BuildingTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + BuildingTypeClass ( + StructType type, + int name, + char const * ininame, + FacingType foundation, + COORDINATE exitpoint, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fake, + bool is_regulated, + bool is_nominal, + bool is_wall, + bool is_simpledamage, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_theater, + bool is_turret_equipped, + bool is_remappable, + RTTIType tobuild, + DirType sframe, + BSizeType size, + short const * exitlist, + short const * sizelist, + short const * overlap + ); + operator StructType(void) const {return(Type);}; + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BuildingTypeClass & As_Reference(StructType type); + static StructType From_Name(char const * name); + static void Init(TheaterType theater); + static void One_Time(void); + static void Prep_For_Add(void); + + int Width(void) const; + int Height(bool bib=false) const; + + virtual int Full_Name(void) const; + virtual bool Read_INI(CCINIClass & ini); + bool Flush_For_Placement(CELL cell, HouseClass * house) const; + virtual int Cost_Of(void) const; + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual int Max_Pips(void) const; + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual void const * Get_Buildup_Data(void) const {return(BuildupData);}; + + bool Is_Factory(void) const {return(ToBuild != RTTI_NONE);} + virtual int Raw_Cost(void) const; + bool Bib_And_Offset(SmudgeType & bib, CELL & cell) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** Special overlay for the weapons factory. + */ + static void const * WarFactoryOverlay; + + private: + + /* + ** This is a pointer to a list of offsets (from the upper left corner) that + ** are used to indicate the building's "footprint". This footprint is used + ** to determine building placement legality and terrain passibility. + */ + short const * OccupyList; + + /* + ** Buildings can often times overlap a cell but not actually "occupy" it for + ** purposes of movement. This points to a list of offsets that indicate which + ** cells the building has visual overlap but does not occupy. + */ + short const * OverlapList; + + /* + ** The construction animation graphic data pointer is + ** pointed to by this element. + */ + void const * BuildupData; + + void Init_Anim(BStateType state, int start, int count, int rate) const; +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class UnitTypeClass : public TechnoTypeClass +{ + public: + /* + ** If this unit can appear out of a crate, then this flag will be true. + */ + unsigned IsCrateGoodie:1; + + /* + ** Can this unit squash infantry? If it can then if the player selects + ** an (enemy) infantry unit as the movement target, it will ride over and + ** squish the infantry unit. + */ + unsigned IsCrusher:1; + + /* + ** Does this unit go into harvesting mode when it stops on a tiberium + ** field? Typically, only one unit does this and that is the harvester. + */ + unsigned IsToHarvest:1; + + /* + ** Some units are equipped with a rotating radar dish. These units have special + ** animation processing. The rotating radar dish is similar to a turret, but + ** always rotates and does not affect combat. + */ + unsigned IsRadarEquipped:1; + + /* + ** If this unit has a firing animation, this flag is true. Infantry and some special + ** vehicles are the ones with firing animations. + */ + unsigned IsFireAnim:1; + + /* + ** Many vehicles have a turret with restricted motion. These vehicles must move the + ** turret into a locked down position while travelling. Rocket launchers and artillery + ** are good examples of this kind of unit. + */ + unsigned IsLockTurret:1; + + /* + ** Is this unit of the humongous size? Harvesters and mobile construction vehicles are + ** of this size. If the vehicle is greater than 24 x 24 but less than 48 x 48, it is + ** considered "Gigundo". + */ + unsigned IsGigundo:1; + + /* + ** Does this unit have a constant animation (like Visceroid?) + */ + unsigned IsAnimating:1; + + /* + ** Does this unit have the ability to jam radar facilities? + */ + unsigned IsJammer:1; + + /* + ** Is this unit a mobile gap generator? + */ + unsigned IsGapper:1; + + /* + ** If this unit cannot fire while moving, then this flag will be + ** true. Such a unit must stop and stabilize for a bit before it + ** can fire. + */ + unsigned IsNoFireWhileMoving:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + UnitType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + UnitTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + UnitTypeClass ( + UnitType type, + int name, + char const * ininame, + AnimType exp, + RemapType remap, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_goodie, + bool is_nominal, + bool is_crusher, + bool is_harvest, + bool is_stealthy, + bool is_insignificant, + bool is_turret_equipped, + bool is_radar_equipped, + bool is_fire_anim, + bool is_lock_turret, + bool is_gigundo, + bool is_animating, + bool is_jammer, + bool is_gapper, + int rotation, + int toffset, + MissionType order + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static UnitType From_Name(char const * name); + static UnitTypeClass & As_Reference(UnitType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + /* + ** The animation stage list for harvester dumping into the refinery. + */ + static const int Harvester_Dump_List[22]; + + /* + ** The animatino stage list for harvester loading up on ore. + */ + static const int Harvester_Load_List[9]; + + /* + ** The number of animation stages when the harvester is loading + ** up on ore in the field. + */ + static const int Harvester_Load_Count; +}; + + +/*************************************************************************** +** This specifies the constant attribute data associated with naval +** vessels. +*/ +class VesselTypeClass : public TechnoTypeClass +{ + public: + /* + ** Does this unit have only 8 facings? Special test units have limited + ** facings. + */ + unsigned IsPieceOfEight:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + VesselType Type; + + /* + ** This is the distance along the centerline heading in the direction the body + ** is facing used to reach the center point of the turret. This distance is + ** in leptons. + */ + signed char TurretOffset; + + /* + ** This value is used to provide the unit with a default mission order when + ** first created. Usually, this is a resting or idle type of order. + */ + MissionType Mission; + + /* + ** This is the default explosion to use when this vehicle is destroyed. + */ + AnimType Explosion; + + /* + ** The width or height of the largest dimension for this unit. + */ + int MaxSize; + + /* + ** This is the explicit unit class constructor. + */ + VesselTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + VesselTypeClass ( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static VesselType From_Name(char const * name); + static VesselTypeClass & As_Reference(VesselType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual int Max_Pips(void) const; + virtual short const * Overlap_List(void) const; + + void Turret_Adjust(DirType dir, int & x, int & y) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/*************************************************************************** +** The various unit types need specific data that is unique to units as +** opposed to buildings. This derived class elaborates these additional +** data types. +*/ +class InfantryTypeClass : public TechnoTypeClass +{ + public: + + /* + ** If this civilian infantry type is female, then this flag + ** will be true. This information is used to get the correct + ** voice response. + */ + unsigned IsFemale:1; + + /* + ** Does this infantry unit have crawling animation? If not, then this + ** means that the "crawling" frames are actually running animation frames. + */ + unsigned IsCrawling:1; + + /* + ** For those infantry types that can capture buildings, this flag + ** will be set to true. Typically, this is the engineer. + */ + unsigned IsCapture:1; + + /* + ** For infantry types that will run away from any damage causing + ** events, this flag will be true. Typically, this is so for all + ** civilians as well as the flame thrower guys. + */ + unsigned IsFraidyCat:1; + + /* + ** This flags whether this infantry is actually a civilian. A + ** civilian uses different voice responses, has less ammunition, + ** and runs from danger more often. + */ + unsigned IsCivilian:1; + + /* + ** If the infantry unit is equipped with C4 explosives, then this + ** flag will be true. Such infantry can enter and destroy enemy + ** buildings. + */ + unsigned IsBomber:1; + + /* + ** This flags whether this infantry is actually a dog. A dog + ** uses different voice responses, has no ammo, and runs instead + ** of walks to attack. + */ + unsigned IsDog:1; + + /* + ** This flag specifies whether this infantry type should use the + ** override remap table, instead of the house remap table. This is + ** used to turn the two civilian animations into a veritable smorgasbord + ** of civilian types, for example. + */ + unsigned IsRemapOverride:1; + + /* + ** This value represents the unit class. It can serve as a unique + ** identification number for this unit class. + */ + InfantryType Type; + + /* + ** When this infantry unit is loaded onto a transport, then this + ** is the pip shape to use. Primarily, this is a color control. + */ + PipEnum Pip; + + /* + ** This is an array of the various animation frame data for the actions that + ** the infantry may perform. + */ + DoInfoStruct const * DoControls; + + /* + ** Alternate animation info for the 'virtual' window which gets rendered on the GlyphX client. + ** The infantry frames here map to the original TD infantry frames, so a different set is needed depending on whether + ** we are rendering in legacy mode or on the GlyphX client. ST - 9/5/2019 12:17PM + */ + DoInfoStruct const * DoControlsVirtual; + + /* + ** There are certain units with special animation sequences built into the + ** shape file. These values tell how many frames are used for the firing animation. + */ + char FireLaunch; + char ProneLaunch; + + /* + ** This is a pointer to the special override remap table, which is + ** used only in conjunction with the IsRemapOverride flag, and is + ** primarily used for the civilians. + */ + unsigned char const * OverrideRemap; + + /* + ** This is the explicit unit class constructor. + */ + InfantryTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + InfantryTypeClass ( + InfantryType type, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + bool is_female, + bool is_crawling, + bool is_civilian, + bool is_remap_override, + bool is_nominal, + bool is_theater, + PipEnum pip, + DoInfoStruct const * controls, + DoInfoStruct const * virtual_controls, + int firelaunch, + int pronelaunch, + unsigned char const * override_remap, + int horizontaloffset=0); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static InfantryType From_Name(char const * name); + static InfantryTypeClass & As_Reference(InfantryType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual void Dimensions(int & width, int & height) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual int Full_Name(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif +}; + + +/**************************************************************************** +** The various aircraft types are controlled by object types of +** this class. +*/ +class AircraftTypeClass : public TechnoTypeClass +{ + public: + + /* + ** Fixed wing aircraft (ones that cannot hover) have this flag set to true. + ** Such aircraft will not vary speed while it is flying. + */ + unsigned IsFixedWing:1; + + /* + ** Can this aircraft land? If it can land it is presumed to be controllable by the player. + */ + unsigned IsLandable:1; + + /* + ** Does this aircraft have a rotor blade (helicopter) type propulsion? + */ + unsigned IsRotorEquipped:1; // Is a rotor attached? + + /* + ** Is there a custom rotor animation stage set for each facing of the aircraft? + */ + unsigned IsRotorCustom:1; // Custom rotor sets for each facing? + + /* + ** This is the kind of aircraft identifier number. + */ + AircraftType Type; + + /* + ** This specifies the default mission order for this aircraft. Some aircraft default + ** to guard mode (e.g., helicopters) while some default to attack mode (e.g., bombers). + */ + MissionType Mission; + + /* + ** This is the preferred landing building. The aircraft will try to land at the + ** building of this type. + */ + StructType Building; + + /* + ** This is the final approach speed of this aircraft type for landing + ** at an airfield. Most aircraft hit it at full speed, but the MIG is + ** an example of a plane that needs a slower approach speed to hit the + ** airfield. + */ + int LandingSpeed; + + AircraftTypeClass(NoInitClass const & x) : TechnoTypeClass(x) {} + AircraftTypeClass( + AircraftType airtype, + int name, + char const * ininame, + int verticaloffset, + int primaryoffset, + int primarylateral, + bool is_fixedwing, + bool is_rotorequipped, + bool is_rotorcustom, + bool is_landable, + bool is_stealthy, + bool is_selectable, + bool is_legal_target, + bool is_insignificant, + bool is_immune, + StructType building, + int landingspeed, + int rotation, + MissionType deforder); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static AircraftType From_Name(char const * name); + static AircraftTypeClass & As_Reference(AircraftType a); + static void Init(TheaterType ) {}; + static void One_Time(void); + static void Prep_For_Add(void); + + virtual void Dimensions(int &width, int &height) const; + virtual bool Create_And_Place(CELL, HousesType) const; + virtual ObjectClass * Create_One_Of(HouseClass * house) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + virtual int Max_Pips(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house) const; + #endif + + static void const * LRotorData; + static void const * RRotorData; +}; + + +/*************************************************************************** +** Bullets and other projectiles need some specific information according +** to their type. +*/ +class BulletTypeClass : public ObjectTypeClass +{ + public: + + /* + ** Does this bullet type fly over walls? + */ + unsigned IsHigh:1; + + /* + ** Does this bullet need a shadow drawn under it? Shadowed bullets + ** use the Height value to offset their Y position. + */ + unsigned IsShadow:1; + + /* + ** If this projectile is one that ballistically arcs from ground level, up into the air and + ** then back to the ground, where it explodes. Typical uses of this are for grenades and + ** artillery shells. + */ + unsigned IsArcing:1; + + /* + ** Certain projectiles do not travel horizontally, but rather, vertically -- they drop + ** from a height. Bombs fall into this category and will have this value set to + ** true. Dropping projectiles do not calculate collision with terrain (such as walls). + */ + unsigned IsDropping:1; + + /* + ** Is this projectile invisible? Some bullets and weapon effects are not directly + ** rendered. Small caliber bullets and flame thrower flames are treated like + ** normal projectiles for damage purposes, but are displayed using custom + ** rules. + */ + unsigned IsInvisible:1; + + /* + ** Does this bullet explode when near the target? Some bullets only explode if + ** it actually hits the target. Some explode even if nearby. + */ + unsigned IsProximityArmed:1; + + /* + ** Does this projectile spew puffs of smoke out its tail while it + ** travels? Missiles are prime examples of this projectile type. + */ + unsigned IsFlameEquipped:1; + + /* + ** Should fuel consumption be tracked for this projectile? Rockets are the primary + ** projectile with this characteristic, but even for bullets it should be checked so that + ** bullets don't travel too far. + */ + unsigned IsFueled:1; + + /* + ** Is this projectile without different facing visuals? Most plain bullets do not change + ** visual imagery if their facing changes. Rockets, on the other hand, are equipped with + ** the full 32 facing imagery. + */ + unsigned IsFaceless:1; + + /* + ** If this is a typically inaccurate projectile, then this flag will be true. Artillery + ** is a prime example of this type. + */ + unsigned IsInaccurate:1; + + /* + ** If the bullet contains translucent pixels, then this flag will be true. These + ** translucent pixels really are "shadow" pixels in the same style as the shadow + ** cast by regular ground units. + */ + unsigned IsTranslucent:1; + + /* + ** If this bullet can be fired on aircraft, then this flag will be true. + */ + unsigned IsAntiAircraft:1; + + /* + ** If this bullet can fire upon ground targets, then this flag will be true. + */ + unsigned IsAntiGround:1; + + /* + ** If this bullet can be fired upon submarines (that are submerged), then + ** this flag will be true. + */ + unsigned IsAntiSub:1; + + /* + ** If this bullet should lose strength as it travels toward the target, then + ** this flag will be true. + */ + unsigned IsDegenerate:1; + + /* + ** Does this projectile travel under the water? If so, then its imagery will be modified + ** to look like it is doing so. + */ + unsigned IsSubSurface:1; + + /* + ** If this projectile is equipped with a parachute, then this flag will be set. Parachute + ** bombs are usually the only one with this flag set. + */ + unsigned IsParachuted:1; + + /* + ** Is this unit of the humongous size? Certain very large projectiles have + ** this flag set. Typically, they require a special offset list so that the cells + ** they overlap will be properly redrawn. + */ + unsigned IsGigundo:1; + + /* + ** This element is a unique identification number for the bullet + ** type. + */ + BulletType Type; + + /* + ** This is the rotation speed of the bullet. It only has practical value + ** for those projectiles that performing homing action during flight -- such + ** as with rockets. If the ROT is zero, then no homing is performed. Otherwise + ** the projectile is considered to be a homing type. + */ + unsigned char ROT; + + /* + ** Some projectiles have a built in arming distance that must elapse before the + ** projectile may explode. If this value is non-zero, then this override is + ** applied. + */ + int Arming; + + /* + ** If this bullet is of the tumbling type, then this is the modulo to factor + ** into the game frame when determining what shape number to use for the + ** imagery. + */ + int Tumble; + + //--------------------------------------------------------------------- + BulletTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + BulletTypeClass(char const * name); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static BulletTypeClass & As_Reference(BulletType type); + static void Init(TheaterType ) {}; + static void One_Time(void); + + virtual bool Read_INI(CCINIClass & ini); + virtual bool Create_And_Place(CELL , HousesType =HOUSE_NONE) const {return false;}; + virtual ObjectClass * Create_One_Of(HouseClass *) const {return 0;}; +}; + + +/**************************************************************************** +** These are the different TYPES of terrain objects. Every terrain object must +** be one of these types. +*/ +class TerrainTypeClass : public ObjectTypeClass +{ + public: + /* + ** Which terrain object does this class type represent. + */ + TerrainType Type; + + /* + ** This is the coordinate offset (from upper left) of where the center base + ** position of the terrain object lies. For trees, this would be the base of + ** the trunk. This is used for sorting purposes. + */ + COORDINATE CenterBase; + + /* + ** This is the bitfield control that tells which theater this terrain object is + ** valid for. If the bit (1 << TheaterType) is true, then this terrain object + ** is allowed. + */ + int Theater; + + /* + ** Does this terrain object get placed on the water instead of the ground? + */ + unsigned IsWaterBased:1; + + //---------------------------------------------------------------- + TerrainTypeClass(NoInitClass const & x) : ObjectTypeClass(x) {} + TerrainTypeClass( + TerrainType terrain, + int theater, + COORDINATE centerbase, + bool is_immune, + bool is_water, + char const * ininame, + int fullname, + short const * occupy, + short const * overlap); + + static void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void * ptr); + + static void Init_Heap(void); + static TerrainType From_Name(char const * name); + static TerrainTypeClass & As_Reference(TerrainType type); + static void Init(TheaterType theater = THEATER_TEMPERATE); + static void One_Time(void); + static void Prep_For_Add(void); + + virtual COORDINATE Coord_Fixup(COORDINATE coord) const; + virtual bool Create_And_Place(CELL cell, HousesType house) const; + virtual ObjectClass * Create_One_Of(HouseClass *) const; + virtual short const * Occupy_List(bool placement=false) const; + virtual short const * Overlap_List(void) const; + + #ifdef SCENARIO_EDITOR + virtual void Display(int x, int y, WindowNumberType window, HousesType house=HOUSE_NONE) const; + #endif + + private: + short const * Occupy; + short const * Overlap; +}; + + +/**************************************************************************** +** The tile type objects are controlled by this class. It specifies the form +** of the tile set for the specified object as well as other related datum. +** It is derived from the ObjectTypeClass solely for the purpose of scenario +** editing and creation. +*/ +class TemplateTypeClass: public ObjectTypeClass +{ + public: + /* + ** What template is this. + */ + TemplateType Type; + + /* + ** A bitfield container that indicates which theaters this template is allowed + ** in. A bit set in the (1<renderwidth[d] = image->renderwidth[d] / 5; + image->renderheight[d] = image->renderheight[d] / 5; + } + + if (numUnitsAnims > 0) + { + for (int i = 1; i < numUnitsAnims; i++) { + imageFileName = Units_FindHDTexture(fullname, 0, i); + for (int d = 0; d < MAX_MPLAYER_COLORS; d++) { + Image_Add32BitImage(imageFileName, image, d, 0, i); + } + } + } + + image->numFrames = numUnitsAnims; + ((void const*&)uclass.HDImageData) = image; + } + } + + + ((int &)uclass.MaxSize) = max(largest, 8); + } + + /* + ** Load any custom shapes at this time. + */ + if (WakeShapes == NULL) { + WakeShapes = MFCD::Retrieve("WAKE.SHP"); + } + if (TurretShapes == NULL) { + TurretShapes = MFCD::Retrieve("TURR.SHP"); + } + if (SamShapes == NULL) { + SamShapes = MFCD::Retrieve("SSAM.SHP"); + } + if (MGunShapes == NULL) { + MGunShapes = MFCD::Retrieve("MGUN.SHP"); + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_And_Place -- Creates and places a unit object onto the map. * + * * + * This routine is used by the scenario editor to create and place a unit object of this * + * type onto the map. * + * * + * INPUT: cell -- The cell that the unit is to be placed into. * + * * + * house -- The house that the unit belongs to. * + * * + * OUTPUT: bool; Was the unit created and placed successfully? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + UnitClass * unit = new UnitClass(Type, house); + if (unit != NULL) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + return(false); +} + + +/*********************************************************************************************** + * UnitTypeClass::Create_One_Of -- Creates a unit in limbo. * + * * + * This function creates a unit of this type and keeps it in limbo. A pointer to the * + * created unit object is returned. It is presumed that this object will later be * + * unlimboed at the correct time and place. * + * * + * INPUT: house -- Pointer to the house that is to own the unit. * + * * + * OUTPUT: Returns with a pointer to the created unit object. If the unit object * + * could not be created, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/07/1994 JLB : Created. * + *=============================================================================================*/ +ObjectClass * UnitTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new UnitClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * UnitTypeClass::As_Reference -- Fetches a reference to the unit type class specified. * + * * + * Use this routine to return a reference to the UnitTypeClass object as indicated by * + * the unit type number specified. * + * * + * INPUT: type -- The unit type number to convert into a UnitTypeClass object reference. * + * * + * OUTPUT: Returns with a reference to the unit type class object specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +UnitTypeClass & UnitTypeClass::As_Reference(UnitType type) +{ + return(*UnitTypes.Ptr(type)); +} + + +/*********************************************************************************************** + * UnitTypeClass::Dimensions -- Determines the unit's pixel dimensions. * + * * + * This routine will fill in the width and height for this unit type. This width and * + * height are used to render the selection rectangle and the positioning of the health * + * bargraph. * + * * + * INPUT: width -- Reference to the width of the unit (to be filled in). * + * * + * height -- Reference to the height of the unit (to be filled in). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/23/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Dimensions(int &width, int &height) const +{ + width = MaxSize-(MaxSize/4); + width = min(width, 48); + height = MaxSize-(MaxSize/4); + height = min(height, 48); +} + + +/*********************************************************************************************** + * UnitTypeClass::Max_Pips -- Fetches the maximum pips allowed for this unit. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed, but for harvesters, it is the * + * number of credits it holds divided by 100. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this unit type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/26/1995 JLB : Created. * + *=============================================================================================*/ +int UnitTypeClass::Max_Pips(void) const +{ + if (Type == UNIT_HARVESTER) { + return(7); + } + + if (Type == UNIT_MINELAYER) { + return(MaxAmmo); + } + return(Max_Passengers()); +} + + +/*********************************************************************************************** + * UnitTypeClass::Turret_Adjust -- Turret adjustment routine for MLRS and MSAM units. * + * * + * This routine adjusts the pixel coordinates specified to account for the displacement of * + * the turret on the MLRS and MSAM vehicles. * + * * + * INPUT: dir -- The direction of the body of the vehicle. * + * * + * x,y -- References to the turret center pixel position. These will be modified as * + * necessary. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void UnitTypeClass::Turret_Adjust(DirType dir, int &x, int &y) const +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {1,2}, // N + {-1,1}, + {-2,0}, + {-3,0}, + {-3,1}, // NW + {-4,-1}, + {-4,-1}, + {-5,-2}, + {-5,-3}, // W + {-5,-3}, + {-3,-3}, + {-3,-4}, + {-3,-4}, // SW + {-3,-5}, + {-2,-5}, + {-1,-5}, + {0,-5}, // S + {1,-6}, + {2,-5}, + {3,-5}, + {4,-5}, // SE + {6,-4}, + {6,-3}, + {6,-3}, + {6,-3}, // E + {5,-1}, + {5,-1}, + {4,0}, + {3,0}, // NE + {2,0}, + {2,1}, + {1,2} + }; + + int index = 0; + switch (Type) { + case UNIT_JEEP: + y -= 4; + break; + + case UNIT_MGG: + index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; + break; + + default: + break; + } +} + + +/*********************************************************************************************** + * UnitTypeClass::Read_INI -- Fetch the unit type data from the INI database. * + * * + * This routine will find the section in the INI database for this unit type object and * + * then fill in the override values specified. * + * * + * INPUT: ini -- Reference to the INI database that will be examined. * + * * + * OUTPUT: bool; Was the section for this unit found in the database and the data extracted? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitTypeClass::Read_INI(CCINIClass & ini) +{ + if (TechnoTypeClass::Read_INI(ini)) { + IsNoFireWhileMoving = ini.Get_Bool(IniName, "NoMovingFire", IsNoFireWhileMoving); + Speed = ini.Get_Bool(IniName, "Tracked", (Speed == SPEED_TRACK)) ? SPEED_TRACK : SPEED_WHEEL; + + /* + ** If this unit can drive over walls, then mark it as recognizing the crusher zone. + */ + if (MZone < MZONE_CRUSHER && IsCrusher) { + MZone = MZONE_CRUSHER; + } + return(true); + } + return(false); +} diff --git a/code/redalert/udpaddr.cpp b/code/redalert/udpaddr.cpp new file mode 100644 index 0000000..61a80e8 --- /dev/null +++ b/code/redalert/udpaddr.cpp @@ -0,0 +1,234 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + + +#include "function.h" +//#include "_WSProto.h" +#include "WSPUDP.h" + + +bool Get_Broadcast_Addresses (void) +{ + return false; +#if (0)//PG + + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 160 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_txt6_h = 6 *RESFACTOR+1; // ht of 6-pt text + int d_margin1 = 5 *RESFACTOR; // large margin + int d_margin2 = 7 *RESFACTOR; // small margin + + int d_ip_address_list_w = 300 *RESFACTOR; + int d_ip_address_list_h = ((20 * 6) + 3) *RESFACTOR; // 6 rows high + int d_ip_address_list_x = d_dialog_cx - d_ip_address_list_w / 2; + int d_ip_address_list_y = d_margin2 + d_dialog_y; + + + int d_ok_w = 40*RESFACTOR; + int d_ok_h = 9*RESFACTOR; + int d_ok_x = d_dialog_cx + d_dialog_w / 4; + int d_ok_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_dialog_w / 4; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum { + BUTTON_IPLIST=100, + BUTTON_OK, + BUTTON_CANCEL, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + int width; + int height; + + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //PG_TO_FIX + //Fancy_Text_Print(TXT_NONE,0,0,TBLACK,TBLACK,TPF_6PT_GRAD | TPF_NOSHADOW); + Format_Window_String("IP Addresses", SeenBuff.Get_Height(), width, height); + + + GadgetClass *commands; // button list + ColorListClass ip_address_list(BUTTON_IPLIST, d_ip_address_list_x, d_ip_address_list_y, d_ip_address_list_w, d_ip_address_list_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + + TextButtonClass okbtn(BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w, d_ok_h); + TextButtonClass cancelbtn(BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h); + + ip_address_list.Set_Selected_Style(ColorListClass::SELECT_NORMAL); + + Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_CENTER | TPF_TEXT); + + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + + + + /* + ** Add all the ip addresses from the ini file to the list box. + */ + CCINIClass ip_ini; + int res=0; + + if (ip_ini.Load(CCFileClass("IP.INI"), false)) { + int entry=0; + char entry_name[16]; + do { + entry++; + char *temp = new char [128]; + sprintf (entry_name, "%d", entry); + res = ip_ini.Get_String("IP_ADDRESSES", entry_name, "", temp, 128); + if ( res ) { + ip_address_list.Add_Item (temp); +char debug[128]; +sprintf (debug, "RA95 - Adding address %s\n", temp); +WWDebugString (debug); + } + }while (res); + } + ip_address_list.Flag_To_Redraw(); + + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) { + + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print("IP Addresses", d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, scheme, TBLACK, TPF_TEXT); + + //............................................................... + // Rebuild the button list + //............................................................... + okbtn.Zap(); + cancelbtn.Zap(); + ip_address_list.Zap(); + + commands = &okbtn; + cancelbtn.Add_Tail(*commands); + ip_address_list.Add_Tail(*commands); + + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + commands->Draw_All(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + //..................................................................... + // Get user input + //..................................................................... + input = commands->Input(); + + //..................................................................... + // Process input + //..................................................................... + switch (input) { + + //.................................................................. + // ESC / CANCEL: send a SIGN_OFF + // - If we're part of a game, stay in this dialog; otherwise, exit + //.................................................................. + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + process = false; + return (false); + + case (BUTTON_OK | KN_BUTTON): + process = false; + break; + } + } + + + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + CCPalette.Set(); //GamePalette.Set(); + Show_Mouse(); + + for ( int i=0 ; iSet_Broadcast_Address ((char*)temp); + } + + return (true); +#endif +} \ No newline at end of file diff --git a/code/redalert/unit.cpp b/code/redalert/unit.cpp new file mode 100644 index 0000000..1d0e4e3 --- /dev/null +++ b/code/redalert/unit.cpp @@ -0,0 +1,5141 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/UNIT.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 10, 1993 * + * * + * Last Update : November 3, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * UnitClass::AI -- AI processing for the unit. * + * UnitClass::APC_Close_Door -- Closes an APC door. * + * UnitClass::APC_Open_Door -- Opens an APC door. * + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * UnitClass::Blocking_Object -- Determines how a object blocks a unit * + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * UnitClass::Click_With -- Handles player map clicking while this unit is selected. * + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * UnitClass::Draw_It -- Draws a unit object. * + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * UnitClass::Goto_Tiberium -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * UnitClass::Init -- Clears all units for scenario preparation. * + * UnitClass::Limbo -- Limbo this unit. * + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * UnitClass::Overlap_List -- Determines overlap list for units. * + * UnitClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * UnitClass::Random_Animate -- Handles random idle animation for the unit. * + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * UnitClass::Set_Speed -- Initiate unit movement physics. * + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * UnitClass::UnitClass -- Constructor for units. * + * UnitClass::Unlimbo -- Removes unit from stasis. * + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * UnitClass::Write_INI -- Store the units to the INI database. * + * UnitClass::delete -- Deletion operator for units. * + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * UnitClass::~UnitClass -- Destructor for unit objects. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "COORDA.h" + +/*********************************************************************************************** + * Recoil_Adjust -- Adjust pixel values in direction specified. * + * * + * This is a helper routine that modifies the pixel coordinates provided according to the * + * direction specified. The effect is the simulate recoil effects by moving an object 'back'* + * one pixel. Since the pixels moved depend on facing, this routine handles the pixel * + * adjustment quickly. * + * * + * INPUT: dir -- The direction to base the recoil on. * + * * + * x,y -- References to the pixel coordinates that will be adjusted. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + *=============================================================================================*/ +void Recoil_Adjust(DirType dir, int &x, int &y) +{ + static struct { + signed char X,Y; + } _adjust[32] = { + {0, 1}, // N + {0, 1}, + {0, 1}, + {-1, 1}, + {-1, 1}, // NE + {-1, 1}, + {-1, 0}, + {-1, 0}, + {-1, 0}, // E + {-1, 0}, + {-1, -1}, + {-1, -1}, + {-1, -1}, // SE + {-1, -1}, + {-1, -1}, + {0, -1}, + {0, -1}, // S + {0, -1}, + {0, -1}, + {1, -1}, + {1, -1}, // SW + {1, -1}, + {1, 0}, + {1, 0}, + {1, 0}, // W + {1, 0}, + {1, 1}, + {1, 1}, + {1, 1}, // NW + {1, 1}, + {0, 1}, + {0, 1} + }; + + int index = Dir_To_32(dir); + x += _adjust[index].X; + y += _adjust[index].Y; +} + + +/*********************************************************************************************** + * UnitClass::new -- Allocate a unit slot and adjust access arrays. * + * * + * This routine will allocate a unit from the available unit pool and * + * fixup all the access lists to match. It will allocate a unit slot * + * from within the range allowed for the specified unit type. If no * + * slot was found, then it will fail. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/11/1994 JLB : Created. * + * 04/21/1994 JLB : Converted to operator new. * + *=============================================================================================*/ +void * UnitClass::operator new(size_t) +{ + void * ptr = Units.Alloc(); + if (ptr != NULL) { + ((UnitClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * UnitClass::delete -- Deletion operator for units. * + * * + * This removes the unit from the local allocation system. Since this * + * is a fixed block of memory, not much has to be done to delete the * + * unit. Merely marking it as inactive is enough. * + * * + * INPUT: ptr -- Pointer to the unit to delete. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + ((UnitClass *)ptr)->IsActive = false; + } + Units.Free((UnitClass *)ptr); +} + + +/*********************************************************************************************** + * UnitClass::~UnitClass -- Destructor for unit objects. * + * * + * This destructor will lower the unit count for the owning house as well as inform any * + * other units in communication, that this unit is about to leave reality. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::~UnitClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * UnitClass::UnitClass -- Constructor for units. * + * * + * This constructor for units will initialize the unit into the game * + * system. It will be placed in all necessary tracking lists. The initial condition will * + * be in a state of limbo. * + * * + * INPUT: classid -- The type of unit to create. * + * * + * house -- The house owner of this unit. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/21/1994 JLB : Created. * + *=============================================================================================*/ +UnitClass::UnitClass(UnitType classid, HousesType house) : + DriveClass(RTTI_UNIT, Units.ID(this), house), + Class(UnitTypes.Ptr((int)classid)), + Flagged(HOUSE_NONE), + IsDumping(false), + Gems(0), + Gold(0), + Tiberium(0), + IsToScatter(false), + ShroudBits(0xFFFFFFFFUL), + ShroudCenter(0), + Reload(0), + SecondaryFacing(PrimaryFacing) +{ + Reload = 0; + House->Tracking_Add(this); + Ammo = Class->MaxAmmo; + IsCloakable = Class->IsCloakable; + if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3)); + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * UnitClass::Debug_Dump -- Displays the status of the unit to the mono monitor. * + * * + * This displays the current status of the unit class to the mono monitor. By this display * + * bugs may be tracked down or prevented. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/02/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Debug_Dump(MonoClass * mono) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_VEHICLE)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Set_Cursor(1, 11);mono->Printf("%03", Gems); + mono->Set_Cursor(7, 11);mono->Printf("%03", Gold); + + mono->Fill_Attrib(66, 13, 12, 1, IsDumping ? MonoClass::INVERSE : MonoClass::NORMAL); + + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * UnitClass::Sort_Y -- Give Y coordinate sort value for unit. * + * * + * This routine is used by the rendering system in order to sort the * + * game objects in a back to front order. This is now the correct * + * overlap effect is achieved. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a coordinate value that can be used for sorting. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/17/1994 JLB : Created. * + *=============================================================================================*/ +COORDINATE UnitClass::Sort_Y(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(Coord_Add(Coord, 0x00800000L)); +} + + +/*********************************************************************************************** + * UnitClass::AI -- AI processing for the unit. * + * * + * This routine will perform the AI processing necessary for the unit. These are non- * + * graphic related operations. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::AI(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (Height == 0 && !IsDumping && !IsDriving && Is_Door_Closed() /*&& Mission != MISSION_UNLOAD*/) { +// if (MissionQueue == MISSION_NONE) Enter_Idle_Mode(); + Commence(); + } + DriveClass::AI(); + if (!IsActive || Height > 0) { + return; + } + + /* + ** Hack check to ensure that a harvester won't harvest if it is not harvesting. + */ + if (Mission != MISSION_HARVEST) { + IsHarvesting = false; + } + + /* + ** Handle combat logic for this unit. It will determine if it has a target and + ** if so, if conditions are favorable for firing. When conditions permit, the + ** unit will fire upon its target. + */ + Firing_AI(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (!IsActive) { + return; + } +#endif + /* + ** Turret rotation processing. Handles rotating radar dish + ** as well as conventional turrets if present. If no turret present, but + ** it decides that the body should face its target, then body rotation + ** would occur by this process as well. + */ + Rotation_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + /* + ** Units will reload every so often if they are under the burden of + ** being required to reload between shots. + */ + Reload_AI(); + + /* + ** Transporters require special logic handled here since there isn't a MISSION_WAIT_FOR_PASSENGERS + ** mission that they can follow. Passenger loading is merely a part of their normal operation. + */ + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER) { + APC_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDumping && !IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** A cloaked object that is carrying the flag will always shimmer. + */ + if (Cloak == CLOAKED && Flagged != HOUSE_NONE) { + Do_Shimmer(); + } + + /* + ** Mobile gap generators regenerate their gap every so often (just in case). + */ + if (Class->IsGapper && !IsDriving && (Frame % TICKS_PER_SECOND) == 0) { + Shroud_Regen(); + } +} + + +/*********************************************************************************************** + * UnitClass::Rotation_AI -- Process any turret or body rotation. * + * * + * This routine will handle the rotation logic for the unit's turret (if it has one) as * + * well as its normal body shape. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } else { + + /* + ** Non turret equipped vehicles will rotate their body to face the target only + ** if the vehicle isn't currently moving or facing the correct direction. This + ** applies only to tracked vehicles. Wheeled vehicles never rotate to face the + ** target, since they aren't maneuverable enough. + */ + if ((Class->Speed == SPEED_TRACK /* || *this == UNIT_BIKE */ ) && !Target_Legal(NavCom) && !IsDriving && PrimaryFacing.Difference(dir)) { + PrimaryFacing.Set_Desired(dir); + } + } + } + + if (Class->IsRadarEquipped) { + Mark(MARK_CHANGE_REDRAW); + SecondaryFacing.Set((DirType)(SecondaryFacing.Current() + 8)); + Mark(MARK_CHANGE_REDRAW); + } else { + + IsRotating = false; + if (Class->IsTurretEquipped) { + if (IsTurretLockedDown) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust(Class->ROT+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + if (!Class->IsRadarEquipped) { + IsRotating = SecondaryFacing.Is_Rotating(); + } + } else { + if (!IsTurretLockedDown && !Target_Legal(TarCom)) { + if (!Target_Legal(NavCom)) { + SecondaryFacing.Set_Desired(PrimaryFacing.Current()); + } else { + SecondaryFacing.Set_Desired(Direction(NavCom)); + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Edge_Of_World_AI -- Check for falling off the edge of the world. * + * * + * When a unit leaves the map it will be eliminated. This routine checks for this case * + * and eliminates the unit accordingly. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the unit eliminated by this routine? * + * * + * WARNINGS: Be sure to check for the return value and if 'true' abort any further processing* + * of the unit since it is dead. Only call this routine once per unit per * + * game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Edge_Of_World_AI(void) +{ + if (Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Reload_AI -- Perform reload logic for this unit. * + * * + * Some units require special reload logic. The V2 rocket launcher in particular. Perform * + * this reload logic with this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Reload_AI(void) +{ + if (*this == UNIT_V2_LAUNCHER && Ammo < Class->MaxAmmo) { + if (IsDriving) { + Reload = Reload + 1; + } else { + if (Reload == 0) { + Ammo++; + if (Ammo < Class->MaxAmmo) { + Reload = TICKS_PER_SECOND*30; + } + Mark(MARK_CHANGE); + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Firing_AI -- Handle firing logic for this unit. * + * * + * This routine wil check for and perform any firing logic required of this unit. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This should be called only once per unit per game logic loop. * + * * + * HISTORY: * + * 07/30/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Firing_AI(void) +{ + if (Target_Legal(TarCom) && Class->PrimaryWeapon != NULL) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + switch (ok) { + case FIRE_OK: + if (!((UnitClass *)this)->Class->IsFireAnim) { + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + } + + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Class->IsLockTurret || Class->Type == UNIT_DEMOTRUCK) { +#else + if (Class->IsLockTurret) { +#endif + if (!Target_Legal(NavCom) && !IsDriving) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + SecondaryFacing.Set_Desired(PrimaryFacing.Desired()); + } + } else { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * UnitClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a unit receives a radio * + * message. Typical use of this is when a unit unloads from a hover * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + switch (message) { + /* + ** Checks to see if this object is in need of service depot processing. + */ + case RADIO_NEED_REPAIR: + if (!IsDriving && !Target_Legal(NavCom) && (Health_Ratio() >= 1 && (*this != UNIT_MINELAYER || Ammo >= Class->MaxAmmo))) return(RADIO_NEGATIVE); + break; +// return(RADIO_ROGER); + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { + return(RADIO_ROGER); + } + return(RADIO_NEGATIVE); + + /* + ** The refinery has told this harvester that it should begin the backup procedure + ** so that proper unloading may take place. + */ + case RADIO_BACKUP_NOW: + DriveClass::Receive_Message(from, message, param); + if (!IsRotating && PrimaryFacing != DIR_W) { + Do_Turn(DIR_W); + } else { + if (!IsDriving) { + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (Transmit_Message(RADIO_IM_IN, whom) == RADIO_ROGER) { + Transmit_Message(RADIO_UNLOADED, whom); + } + } + } + } + } + return(RADIO_ROGER); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: + if (How_Many() == Class->Max_Passengers()) { + APC_Close_Door(); + } + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + if (Class->Max_Passengers() > 0 && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + Do_Turn(dir); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is or needs + ** to rotate. + */ +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_APC || *this == UNIT_PHASE) { +#else + if (*this == UNIT_APC) { +#endif + if (IsRotating) { + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + } else { + if (!Is_Door_Open()) { + APC_Open_Door(); + } + } + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if ( (*this != UNIT_APC && *this != UNIT_PHASE) || Is_Door_Open()) { +#else + if (*this != UNIT_APC || Is_Door_Open()) { +#endif + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * UnitClass::Unlimbo -- Removes unit from stasis. * + * * + * This routine will place a unit into the game and out of its limbo * + * state. This occurs whenever a unit is unloaded from a transport. * + * * + * INPUT: coord -- The coordinate to make the unit appear. * + * * + * dir -- The initial facing to impart upon the unit. * + * * + * OUTPUT: bool; Was the unit unlimboed successfully? If the desired * + * coordinate is illegal, then this might very well return * + * false. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Unlimbo(COORDINATE coord, DirType dir) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** All units must start out facing one of the 8 major directions. + */ + dir = Facing_Dir(Dir_Facing(dir)); + if (DriveClass::Unlimbo(coord, dir)) { + + SecondaryFacing = dir; + /* + ** Ensure that the owning house knows about the + ** new object. + */ + House->UScan |= (1L << Class->Type); + House->ActiveUScan |= (1L << Class->Type); + + /* + ** If it starts off the edge of the map, then it already starts cloaked. + */ + if (IsCloakable && !IsLocked) Cloak = CLOAKED; + + /* + ** Units default to no special animation. + */ + Set_Rate(0); + Set_Stage(0); + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Take_Damage -- Inflicts damage points on a unit. * + * * + * This routine will inflict the specified number of damage points on * + * the given unit. If the unit is destroyed, then this routine will * + * remove the unit cleanly from the game. The return value indicates * + * whether the unit was destroyed. This will allow appropriate death * + * animation or whatever. * + * * + * INPUT: damage-- The number of damage points to inflict. * + * * + * distance -- The distance from the damage center point to the object's center point.* + * * + * warhead--The type of damage to inflict. * + * * + * source -- Who is responsible for this damage? * + * * + * OUTPUT: Returns the result of the damage process. This can range from RESULT_NONE up to * + * RESULT_DESTROYED. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/30/1991 JLB : Created. * + * 07/12/1991 JLB : Script initiated by unit destruction. * + * 04/15/1994 JLB : Converted to member function. * + * 04/16/1994 JLB : Warhead modifier. * + * 06/03/1994 JLB : Added the source of the damage target value. * + * 06/20/1994 JLB : Source is a base class pointer. * + * 11/22/1994 JLB : Shares base damage handler for techno objects. * + * 06/30/1995 JLB : Lasers do maximum damage against gunboat. * + * 08/16/1995 JLB : Harvester crushing doesn't occur on early missions. * + *=============================================================================================*/ +ResultType UnitClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** Remember if this object was selected. If it was and it gets destroyed and it has + ** passengers that pop out, then the passengers will inherit the select state. + */ + //bool select = (IsSelected && House->IsPlayerControl); + bool select = (Is_Selected_By_Player() );//&& House->IsPlayerControl); + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = DriveClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + Shroud_Regen(); // remove the shroud if it's a gap generator + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + /* + ** SSM launchers will really explode big if they are carrying + ** missiles at the time of the explosion. + */ + if (*this == UNIT_V2_LAUNCHER && Ammo) { + anim = ANIM_NAPALM3; + } + + new AnimClass(anim, Coord); + + /* + ** Harvesters explode with a force equal to the amount of + ** Tiberium they are carrying. + */ + if (Tiberium > 0 && Rule.IsExplosiveHarvester) { + Wide_Area_Damage(Coord, CELL_LEPTON_W + CELL_LEPTON_W/2, Credit_Load()+Class->MaxStrength, this, WARHEAD_HE); + } + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + Shake_The_Screen(3, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(3, source->Owner()); + } + } + } + + /* + ** Possibly have the crew member run away. + */ + CELL cell = Coord_Cell(Center_Coord()); + Mark(MARK_UP); + + if (Class->IsCrew && Class->Max_Passengers() == 0) { + if (Percent_Chance(50)) { + InfantryClass * i = 0; + + if (Class->PrimaryWeapon == NULL) { + i = new InfantryClass(INFANTRY_C1, House->Class->House); + if (i != NULL) i->IsTechnician = true; + } else { + i = new InfantryClass(INFANTRY_E1, House->Class->House); + } + if (i != NULL) { + if (i->Unlimbo(Coord, DIR_N)) { + i->Strength = Random_Pick(5, (int)i->Class->MaxStrength/2); + i->Scatter(0, true); + if (!House->IsHuman) { + i->Assign_Mission(MISSION_HUNT); + } else { + i->Assign_Mission(MISSION_GUARD); + } + if (select) i->Select(); + } else { + delete i; + } + } + } + } else { + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + if (object == NULL) break; // How can this happen? + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + if (object->Is_Infantry() && object->Unlimbo(Coord, DIR_N)) { + object->Look(false); + object->Scatter(0, true); + if (select) object->Select(); + } else { + object->Record_The_Kill(source); + delete object; + } + } + } + + /* + ** If this is a truck, there is a possibility that a crate will drop out + ** if the scenario so indicates and there is room. + */ + if (Scen.IsTruckCrate && *this == UNIT_TRUCK) { + cell = Nearby_Location(); + if (cell != 0) { + new OverlayClass(OVERLAY_WOOD_CRATE, cell); + } + } + + if (*this == UNIT_MCV) { + if (House) { + House->Check_Pertinent_Structures(); + } + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking. + */ + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached) { +#ifdef FIXIT_ANTS + if (*this != UNIT_ANT1 && *this != UNIT_ANT2 && *this != UNIT_ANT3) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim) anim->Attach_To(this); +#ifdef FIXIT_ANTS + } +#endif + } + + /* + ** Try to crush anyone that fires on this unit if possible. The harvester + ** typically is the only one that will qualify here. + */ + if (!Team.Is_Valid() && source != NULL && !IsTethered && !House->Is_Ally(source) && (!House->IsHuman || Rule.IsAutoCrush)) { + + /* + ** Try to crush the attacker if it can be crushed by this unit and this unit is + ** not equipped with a flame type weapon. If this unit has a weapon and the target + ** is not very close, then fire on it instead. In easy mode, they never run over the + ** player. In hard mode, they always do. In normal mode, they only overrun past + ** mission #8. + */ + if (Should_Crush_It(source)) { + Assign_Destination(source->As_Target()); + Assign_Mission(MISSION_MOVE); + } else { + + /* + ** Try to return to base if possible. + */ + if (*this == UNIT_HARVESTER && Pip_Count() && Health_Ratio() <= Rule.ConditionYellow) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * building = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (building != NULL && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + } + } + } + } + + /* + ** Computer controlled harvester will radio for help if they are attacked. + */ + if (*this == UNIT_HARVESTER && !House->IsHuman && source) { + Base_Is_Attacked(source); + } + } + return(res); +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Intercepts the active click to see if deployment is possible* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_SABOTAGE: + case ACTION_CAPTURE: + action = ACTION_ATTACK; + break; + + case ACTION_ENTER: + action = ACTION_MOVE; + break; + + default: + break; + } + } + + /* + ** Short circuit out if trying to tell a unit to "nomove" to itself. This bypass of the + ** normal active click with logic prevents any disturbance to the vehicle's state. Without + ** this bypass, a unit on a repair bay would stop repairing because it would break radio + ** contact. + */ + if (object == this && action == ACTION_NOMOVE) { + return; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, object); + } +#else + DriveClass::Active_Click_With(action, object); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + } else { + DriveClass::Active_Click_With(action, cell); + } +#else + DriveClass::Active_Click_With(action, cell); +#endif +} + + +/*********************************************************************************************** + * UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. * + * * + * This routine is called when the unit completes one mission but does not have a clear * + * follow up mission to perform. In such a case, the unit should enter a default idle * + * state. This idle state varies depending on what the current internal computer * + * settings of the unit is as well as what kind of unit it is. * + * * + * INPUT: initial -- Is this called when the unit just leaves a factory or is initially * + * or is initially placed on the map? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1994 JLB : Created. * + * 06/03/1994 JLB : Fixed to handle non-combat vehicles. * + * 06/18/1995 JLB : Allows a harvester to stop harvesting. * + *=============================================================================================*/ +void UnitClass::Enter_Idle_Mode(bool initial) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + if (IsToScatter) { + IsToScatter = false; + Scatter(0, true); + } + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (!Is_Weapon_Equipped()) { + if (Class->IsToHarvest) { + if (!In_Radio_Contact() && Mission != MISSION_HARVEST && MissionQueue != MISSION_HARVEST) { + if (initial || !House->IsHuman || Map[Coord].Land_Type() == LAND_TIBERIUM) { + order = MISSION_HARVEST; + } else { + order = MISSION_GUARD; + } + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } else { + return; + } + } else { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team.Is_Valid()) { + order = MISSION_UNLOAD; + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if(*this == UNIT_MAD && Mission == MISSION_UNLOAD) { + order = MISSION_UNLOAD; + } else { +#endif + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IQ < Rule.IQGuardArea || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Clear_Spot -- Finds a clear spot to deploy. * + * * + * This routine is used by the MCV to find a clear spot to deploy. If a clear spot * + * is found, then the MCV will assign that location to its navigation computer. This only * + * occurs if the MCV isn't already heading toward a spot. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the located at a spot where it can deploy? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/27/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Clear_Spot(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Mark(MARK_UP); + if (!Target_Legal(NavCom) && BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Adjacent_Cell(Coord_Cell(Center_Coord()), FACING_NW))) { + Mark(MARK_DOWN); + return(true); + } + + if (!Target_Legal(NavCom)) { + /* + ** This scan table is skewed to north scanning only. This should + ** probably be converted to a more flexible method. + */ + static int _offsets[] = { + -MAP_CELL_W*1, + -MAP_CELL_W*2, + -(MAP_CELL_W*2)+1, + -(MAP_CELL_W*2)-1, + -MAP_CELL_W*3, + -(MAP_CELL_W*3)+1, + -(MAP_CELL_W*3)-1, + -(MAP_CELL_W*3)+2, + -(MAP_CELL_W*3)-2, + -MAP_CELL_W*4, + -(MAP_CELL_W*4)+1, + -(MAP_CELL_W*4)-1, + -(MAP_CELL_W*4)+2, + -(MAP_CELL_W*4)-2, +//BG: Added south scanning + MAP_CELL_W*1, + MAP_CELL_W*2, + (MAP_CELL_W*2)+1, + (MAP_CELL_W*2)-1, + MAP_CELL_W*3, + (MAP_CELL_W*3)+1, + (MAP_CELL_W*3)-1, + (MAP_CELL_W*3)+2, + (MAP_CELL_W*3)-2, + MAP_CELL_W*4, + (MAP_CELL_W*4)+1, + (MAP_CELL_W*4)-1, + (MAP_CELL_W*4)+2, + (MAP_CELL_W*4)-2, + +//BG: Added some token east/west scanning + -1,-2,-3,-4, + + 1, 2, 3, 4, + 0 + }; + int * ptr; + + ptr = &_offsets[0]; + while (*ptr) { + CELL cell = Coord_Cell(Coord)+*ptr++; + CELL check_cell = Adjacent_Cell(cell, FACING_NW); + if (BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(check_cell)) { + Assign_Destination(::As_Target(cell)); + break; + } + } + } + Mark(MARK_DOWN); + + /* + ** If we couldn't find a destination to go to, let's try random movement + ** to see if that brings us to a better spot. + */ + if(!Target_Legal(NavCom) && !House->IsHuman) { + Scatter(0); + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Try_To_Deploy -- The unit attempts to "deploy" at current location. * + * * + * Certain units have the ability to deploy into a building. When this routine is called * + * for one of those units, it will attempt to deploy at its current location. If the unit * + * is in motion to a destination or it isn't one of the special units that can deploy or * + * it isn't allowed to deploy at this location for some reason it won't deploy. In all * + * other cases, it will begin to deploy and once it begins only a player abort action will * + * stop it. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was deployment begun? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Try_To_Deploy(bool isScriptUnpack) // jmarshall - added isScriptUnpack +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom) && !IsRotating) { + if (*this == UNIT_MCV) { + + /* + ** Determine if it is legal to deploy at this location. If not, tell the + ** player. + */ + Mark(MARK_UP); + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(cell)) { + if (PlayerPtr == House) { + // jmarshall: Don't play failure to deploy if the map script is trying to deploy the unit. + if (!isScriptUnpack) { + Speak(VOX_DEPLOY); + } + } + if (!House->IsHuman) { + BuildingTypeClass::As_Reference(STRUCT_CONST).Flush_For_Placement(cell, House); + } + Mark(MARK_DOWN); + IsDeploying = false; + return(false); + } + Mark(MARK_DOWN); + + /* + ** If the unit is not facing the correct direction, then start it rotating + ** toward the right facing, but still flag it as if it had deployed. This is + ** because it will deploy as soon as it reaches the correct facing. + */ + if (PrimaryFacing.Current() != DIR_SW) { + Do_Turn(DIR_SW); +// PrimaryFacing.Set_Desired(DIR_SW); + IsDeploying = true; + return(true); + } + + /* + ** Since the unit is already facing the correct direction, actually do the + ** deploy logic. If for some reason this cannot occur, then don't delete the + ** unit, just mark it as not deploying. + */ + Mark(MARK_UP); + BuildingClass * building = new BuildingClass(STRUCT_CONST, House->Class->House); + if (building != NULL) { + if (building->Unlimbo(Adjacent_Cell(Coord, FACING_NW))) { + + /* + ** Play the buildup sound for the player if this is the players + ** MCV. + */ + if (building->House == PlayerPtr) { + Sound_Effect(VOC_PLACE_BUILDING_DOWN, Center_Coord()); + } else { + building->IsToRebuild = true; + building->IsToRepair = true; + } + + /* + ** Always reveal the construction yard to the player that owned the + ** mobile construction vehicle. + */ + building->Revealed(House); + + /* + ** When the MCV deploys, always consider production to have started + ** for the owning house. This ensures that in multiplay, computer + ** opponents will begin construction as soon as they start their + ** base. + */ + House->IsStarted = true; + + /* + ** Force the newly placed construction yard to be in the same strength + ** ratio as the MCV that deployed into it. + */ + building->Strength = Health_Ratio() * (int)building->Class->MaxStrength; + + /* + ** Force the MCV to drop any flag it was carrying. This will also set + ** the owner house's flag home cell (since the house's FlagHome is + ** presumably 0 at this point). + */ + Stun(); + + /* + ** If this MCV was teleported here, clear the gray flag so + ** the screen will go back to color. + */ + if (IsMoebius && !Scen.IsFadingColor) { + Scen.IsFadingBW = false; + Scen.IsFadingColor = true; + Scen.FadeTimer = GRAYFADETIME; + } + delete this; + return(true); + } else { + + /* + ** Could not deploy the construction yard at this location! Just revert + ** back to normal "just sitting there" mode and await further instructions. + */ + delete building; + } + } + Mark(MARK_DOWN); + IsDeploying = false; + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Per_Cell_Process -- Performs operations necessary on a per cell basis. * + * * + * This routine will perform the operations necessary that occur when a unit is at the * + * center of a cell. These operations could entail deploying into a construction yard, * + * radioing a transport unit, and looking around for the enemy. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1994 JLB : Created. * + * 06/17/1995 JLB : Handles case when building says "NO!" * + * 06/30/1995 JLB : Gunboats head back and forth now. * + *=============================================================================================*/ +void UnitClass::Per_Cell_Process(PCPType why) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + HousesType house; + + if (why == PCP_END || why == PCP_ROTATION) { + /* + ** Check to see if this is merely the end of a rotation for the MCV as it is + ** preparing to deploy. In this case, it should begin its deploy process. + */ + if (IsDeploying) { + Try_To_Deploy(false); + if (!IsActive) return; // Unit no longer exists -- bail. + } + } + + BStart(BENCH_PCP); + if (why == PCP_END) { + /* + ** If this is a unit that is driving onto a building then the unit must enter + ** the building as the final step. + */ + TechnoClass * whom = Contact_With_Whom(); + if (IsTethered && whom != NULL) { + if (whom->What_Am_I() == RTTI_BUILDING && Mission == MISSION_ENTER) { + if (whom == Map[CELL(cell-MAP_CELL_W)].Cell_Building()) { + switch (Transmit_Message(RADIO_IM_IN, whom)) { + case RADIO_ROGER: + break; + + case RADIO_ATTACH: + break; + + default: + Scatter(0, true); + break; + } + } + } + } + + /* + ** Unit entering a transport vehicle will break radio contact + ** and attach itself to the transporter. + */ + TechnoClass * techno = Contact_With_Whom(); + if (Mission == MISSION_ENTER && techno && Coord_Cell(Coord) == Coord_Cell(techno->Coord) && techno == As_Techno(NavCom)) { + if (Transmit_Message(RADIO_IM_IN) == RADIO_ATTACH) { + Limbo(); + techno->Attach(this); + } + BEnd(BENCH_PCP); + return; + } + + /* + ** When breaking away from a transport object or building, possibly + ** scatter or otherwise begin normal unit operations. + */ + if (IsTethered && (Mission != MISSION_ENTER || + (As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom)) + ) && + Mission != MISSION_UNLOAD) { + + /* + ** Special hack check to make sure that even though it has moved one + ** cell, if it is still on the building (e.g., service depot), have + ** it scatter again. + */ + if (Map[Coord].Cell_Building() != NULL && !Target_Legal(NavCom)) { + Scatter(0, true, true); + } else { + TechnoClass * contact = Contact_With_Whom(); + if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) { + if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING) { + Assign_Mission(MISSION_HARVEST); + } else if (!Target_Legal(NavCom)) { + Scatter(0, true); + } else { + + /* + ** Special case hack to allow automatic transition to loading + ** onto a transport (or other situation) if the destination + ** so indicates. + */ + TechnoClass * techno = As_Techno(NavCom); + if (techno != NULL) { + Transmit_Message(RADIO_DOCKING, techno); + } + } + } else { + if (*this == UNIT_HARVESTER) { + if (Target_Legal(ArchiveTarget)) { + Assign_Mission(MISSION_HARVEST); + Assign_Destination(ArchiveTarget); + ArchiveTarget = TARGET_NONE; + } else { + + /* + ** Since there is no place to go, move away to clear + ** the pad for another harvester. + */ + if (!Target_Legal(NavCom)) { + Scatter(0, true); + } + } + } + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. That is, unless it is carrying cargo. This means that + ** it is probably carrying an incoming reinforcement and it should not be eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + if (IsPlanningToLook) { + IsPlanningToLook = false; + Look(false); + } else { + Look(true); + } + + /* + ** If this is a mobile gap generator, restore the shroud where appropriate + ** and re-shroud around us. + */ + if (Class->IsGapper && !House->IsPlayerControl) { + Shroud_Regen(); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDumping) { + Commence(); + } + + /* + ** Certain units require some setup time after they come to a halt. + */ + if (!Target_Legal(NavCom) && Path[0] == FACING_NONE) { + if (Class->IsNoFireWhileMoving) { + Arm = Rearm_Delay(true)/4; + } + } + + /* + ** If there is a house flag here, then this unit just might pick it up. + */ + if (Flagged == HOUSE_NONE) { + + if (Map[cell].IsFlagged && !House->Is_Ally(Map[cell].Owner)) { + HouseClass::As_Pointer(Map[cell].Owner)->Flag_Attach(this); + } + } + + /* + ** If this is the unit's own flag-home-cell and the unit is carrying + ** a flag, destroy the house of the flag the unit is carrying. + */ + if (Flagged != HOUSE_NONE) { + + /* + ** If this vehicle is carrying your flag, then it will reveal the + ** map for you as well as itself. This gives you and opportunity to + ** attack the unit. + */ + if (!IsOwnedByPlayer && Flagged == PlayerPtr->Class->House) { + Map.Sight_From(Coord_Cell(Coord), Class->SightRange, House, true); + } + + /* + ** If the flag reaches the home cell for the player, then the flag's + ** owner will be destroyed. + */ + if (cell == HouseClass::As_Pointer(Owner())->FlagHome) { + house = Flagged; // Flag_Remove will clear 'Flagged', so save it + HouseClass::As_Pointer(house)->Flag_Remove(As_Target(), true); + HouseClass::As_Pointer(house)->Flag_To_Die(); + } + } + + /* + ** If entering a cell with a land mine in it, blow up the mine. + */ + BuildingClass * bldng = Map[cell].Cell_Building(); + if (bldng != NULL && (*bldng == STRUCT_AVMINE || *bldng == STRUCT_APMINE) && !bldng->House->Is_Ally(this)) { + + /* + ** Special case: if it's a land mine deployer, and it ran over the + ** type of mine it deploys (only possible if it just dropped it + ** down) then ignore the mine. + */ + if (*this != UNIT_MINELAYER || bldng->House != House) { + + COORDINATE blcoord = bldng->Center_Coord(); + + new AnimClass(ANIM_MINE_EXP1, blcoord); +// new AnimClass(Combat_Anim(Rule.AVMineDamage, WARHEAD_HE, Map[cell].Land_Type()), blcoord); + + /* + ** Vehicles blow up both mines, but they only take significant damage from AV mines. + */ + if (*bldng == STRUCT_AVMINE) { + int damage = Rule.AVMineDamage; + Take_Damage(damage, 0, WARHEAD_HE); + } else { + int damage = 10; + Take_Damage(damage, 0, WARHEAD_HE); + } + delete bldng; + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + } + } + + /* + ** If after all is said and done, the unit finishes its move on an impassable cell, then + ** it must presume that it is in the case of a unit driving onto a bridge that blows up + ** before the unit completes it's move. In such a case the unit should have been destroyed + ** anyway, so blow it up now. + */ + LandType land = Map[Coord].Land_Type(); + if (!IsDriving && IsMovingOntoBridge && (land == LAND_ROCK || land == LAND_WATER || land == LAND_RIVER)) { + new AnimClass(Combat_Anim(Strength, WARHEAD_AP, land), Coord); + int damage = Strength; + Take_Damage(damage, 0, WARHEAD_AP, NULL, true); + return; + } + } + + /* + ** Destroy any crushable wall that is driven over by a tracked vehicle. + */ + CellClass * cellptr = &Map[cell]; + if (Class->IsCrusher && cellptr->Overlay != OVERLAY_NONE) { +// if (Class->Speed == SPEED_TRACK && cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrushable) { + if (optr->Type == OVERLAY_SANDBAG_WALL) { + Sound_Effect(VOC_SANDBAG, Center_Coord()); + } else { + Sound_Effect(VOC_WALLKILL2, Center_Coord()); + } + cellptr->Reduce_Wall(-1); + } + } + + /* + ** Check to see if crushing of any unfortunate infantry is warranted. + */ + Overrun_Square(Coord_Cell(Coord), false); + + if (!IsActive) { + BEnd(BENCH_PCP); + return; + } + DriveClass::Per_Cell_Process(why); + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * UnitClass::Shape_Number -- Fetch the shape number to use for this unit. * + * * + * This routine will calculate the shape number for this unit. The shape number is used * + * for the body of the unit. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to be used for this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Shape_Number(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + +#ifdef FIXIT_ANTS + /* + ** This handles the ant case. + */ + if (Class->Rotation == 8) { + + /* + ** The starting frame is based on the facing of the unit. + */ + shapenum = ((UnitClass::BodyShape[facing]+2)/4) & 0x07; + + /* + ** If the unit is driving, then it has an animation adjustment to the frame number. + */ + if (IsDriving) { + shapenum = 8 + (shapenum * 8) + ((::Frame+ID)/2) % 8; + } else { + + /* + ** If in combat, then do combat anims. + */ + if (Arm > 0) { + shapenum = 8 + 64 + (shapenum * 4) + ((::Frame+ID)/2) % 4; + } + } + } else { +#endif + + /* + ** Fetch the harvesting animation stage as appropriate. + */ + if (IsHarvesting && !PrimaryFacing.Is_Rotating() && !NavCom && !IsDriving) { +// static char _hstage[] = {0, 1, 2, 3, 4, 5, 6, 7, 0}; + unsigned stage = Fetch_Stage(); + if (stage >= ARRAY_SIZE(Class->Harvester_Load_List)) stage = ARRAY_SIZE(Class->Harvester_Load_List)-1; + shapenum = 32 + (((UnitClass::BodyShape[facing]+2)/4)*Class->Harvester_Load_Count)+Class->Harvester_Load_List[stage]; + } else { + /* + ** If the harvester's dumping a load of ore, show that animation + */ + if (IsDumping) { + unsigned stage = Fetch_Stage(); +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD) { + if (stage >= 8) { + stage = 7; + } + shapenum = 32 + stage + (UnitClass::BodyShape[facing]/4)*8; + } else { + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; + } +#else + if (stage >= ARRAY_SIZE(Class->Harvester_Dump_List)) stage = ARRAY_SIZE(Class->Harvester_Dump_List)-1; + shapenum = Class->Harvester_Dump_List[stage]+96; +#endif + } else { + shapenum = UnitClass::BodyShape[facing]; + + if (Class->IsAnimating) { + shapenum = Fetch_Stage(); + } + + /* + ** Door opening and closing animation must be handled carefully. There are only + ** certain directions where this door animation will work. + */ + if (!Is_Door_Closed() && (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE)) { + if (PrimaryFacing == DIR_NE) { + shapenum = 32; + } else { + if (PrimaryFacing == DIR_NW) { + shapenum = 35; + } + } + shapenum += Door_Stage(); + } + } + } + +#ifdef FIXIT_ANTS + } +#endif + + /* + ** The body of the V2 launcher indicates whether it is loaded with a missile + ** or not. + */ + if (*this == UNIT_V2_LAUNCHER) { + if (Ammo == 0) shapenum += 32; + } + + return(shapenum); +} + + +/*********************************************************************************************** + * UnitClass::Draw_It -- Draws a unit object. * + * * + * This routine is the one that actually draws a unit object. It displays the unit * + * according to its current state flags and centered at the location specified. * + * * + * INPUT: x,y -- The X and Y coordinate of where to draw the unit. * + * * + * window -- The clipping window to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1994 JLB : Created. * + * 06/27/1994 JLB : Takes a window parameter. * + * 08/15/1994 JLB : Removed infantry support. * + * 01/07/1995 JLB : Harvester animation support. * + * 07/08/1995 JLB : Uses general purpose draw routine. * + *=============================================================================================*/ +void UnitClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int shapenum; // Working shape number. + void const * shapefile; // Working shape file pointer. + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + Image_t* hdShapeFile; + + /* + ** Verify the legality of the unit class. + */ + shapefile = Get_Image_Data(); + hdShapeFile = Get_HDImage_Data(); + if (shapefile == NULL) return; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); + if (!is_hidden) { + shapenum = Shape_Number(); + + /* + ** The artillery unit should have its entire body recoil when it fires. + */ + if (*this == UNIT_ARTY && IsInRecoilState) { + Recoil_Adjust(PrimaryFacing.Current(), x, y); + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + if (hdShapeFile) { + Techno_Draw_Object_HD(hdShapeFile, shapenum, x, y, window, rotation, scale); + } + else { + Techno_Draw_Object(shapefile, shapenum, x, y, window, rotation, scale); + } + + /* + ** If there is a rotating radar dish, draw it now. + */ + if (Class->IsRadarEquipped) { + if (*this == UNIT_MGG) { + int x2 = x, y2 = y; + shapenum = 32 + (Frame & 7); + Class->Turret_Adjust(PrimaryFacing, x2, y2); + if (hdShapeFile) { + Techno_Draw_Object_HD(hdShapeFile, shapenum, x, y, window); + } + else { + Techno_Draw_Object(shapefile, shapenum, x2, y2, window); + } + } else { +//#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 +// if (*this == UNIT_PHASE) { +// shapenum = 38 + (Frame & 7); +// } else { +// shapenum = 32 + (Frame % 32); +// } +//#else + shapenum = 32 + (Frame % 32); +//#endif +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_TESLATANK) { + if (hdShapeFile) { + Techno_Draw_Object_HD(hdShapeFile, shapenum, x, y, window); + } + else { + Techno_Draw_Object(shapefile, shapenum, x, y, window); + } + } else { + if (hdShapeFile) { + Techno_Draw_Object_HD(hdShapeFile, shapenum, x, y - 5, window); + } + else { + Techno_Draw_Object(shapefile, shapenum, x, y - 5, window); + } + } +#else + Techno_Draw_Object(shapefile, shapenum, x, y-5, window); +#endif + } + } + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (/*!Class->IsChunkyShape &&*/ Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + shapenum = TechnoClass::BodyShape[tfacing]+32; +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + if (*this == UNIT_PHASE) { + shapenum += 6; + } +#endif + /* + ** A recoiling turret moves "backward" one pixel. + */ + if (IsInRecoilState) { + Recoil_Adjust(SecondaryFacing, xx, yy); + } + + Class->Turret_Adjust(PrimaryFacing, xx, yy); + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + if (hdShapeFile) { + Techno_Draw_Object_HD(hdShapeFile, shapenum, xx, yy - 4, window); // jmarshall - 4 pixel offset for turrets feels wrong, but visually looks alright. + } + else { + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + } + + /* + ** If this unit is carrying the flag, then draw that on top of everything else. + */ + if (Flagged != HOUSE_NONE) { + CC_Draw_Shape(this, "FLAGFLY", MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, Class->Remap), Map.UnitShadow, DIR_N, 0x0100, Flagged); + } + + DriveClass::Draw_It(x, y, window); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Check -- Search for and head toward nearest available Tiberium patch. * + * * + * This routine is used to move a harvester to a place where it can load up with * + * Tiberium. It will return true only if it can start harvesting. Otherwise, it sets * + * the navigation computer toward the nearest Tiberium and lets the unit head there * + * automatically. * + * * + * INPUT: center -- Reference to the center of the radius scan. * + * * + * x,y -- Relative offset from the center cell to perform the check upon. * + * * + * OUTPUT: bool; Is it located directly over a Tiberium patch? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Tiberium_Check(CELL & center, int x, int y) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** If the specified offset from the origin will cause it + ** to spill past the map edge, then abort this cell check. + */ + if (Cell_X(center)+x < Map.MapCellX) return(false); + if (Cell_X(center)+x >= Map.MapCellX+Map.MapCellWidth) return(false); + if (Cell_Y(center)+y < Map.MapCellY) return(false); + if (Cell_Y(center)+y >= Map.MapCellY+Map.MapCellHeight) return(false); + + center = XY_Cell(Cell_X(center)+x, Cell_Y(center)+y); + + if ((Session.Type != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].IsMapped))) { + if (Map[Coord].Zones[Class->MZone] != Map[center].Zones[Class->MZone]) return(false); + if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Goto_Tiberium -- Searches for and heads toward tiberium. * + * * + * This routine will cause the unit to search for and head toward nearby Tiberium. When * + * the Tiberium is reached, then this routine should not be called again until such time * + * as additional harvesting is required. When this routine returns false, then it should * + * be called again until such time as it returns true. * + * * + * INPUT: rad = size of ring to search * + * * + * OUTPUT: Has the unit reached Tiberium and harvesting should begin? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/22/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Goto_Tiberium(int rad) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!Target_Legal(NavCom)) { + CELL center = Coord_Cell(Center_Coord()); + if (Map[center].Land_Type() == LAND_TIBERIUM) { + return(true); + } else { + + /* + ** Perform a ring search outward from the center. + */ + for (int radius = 1; radius < rad; radius++) { + for (int x = -radius; x <= radius; x++) { + CELL cell = center; + if (Tiberium_Check(cell, x, -radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, x, +radius)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, -radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + + cell = center; + if (Tiberium_Check(cell, +radius, x)) { + Assign_Destination(::As_Target(cell)); + return(false); + } + } + } + } + } + + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Harvesting -- Harvests tiberium at the current location. * + * * + * This routine is used to by the harvester to harvest Tiberium at the current location. * + * When harvesting is complete, this routine will return true. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is harvesting complete? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Harvesting(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + CELL cell = Coord_Cell(Coord); + CellClass * ptr = &Map[cell]; + + /* + ** Keep waiting if still heading toward a spot to harvest. + */ + if (Target_Legal(NavCom)) return(true); + + if (Tiberium_Load() < 1 && ptr->Land_Type() == LAND_TIBERIUM) { + + /* + ** Lift some Tiberium from the ground. Try to lift a complete + ** "level" of Tiberium. A level happens to be 6 steps. If there + ** is a partial level, then lift that instead. Never lift more + ** than the harvester can carry. + */ +// int reducer = (ptr->OverlayData % 6) + 1; + int reducer = 1; + OverlayType overlay = ptr->Overlay; + reducer = ptr->Reduce_Tiberium(min(reducer, Rule.BailCount-Tiberium)); + Tiberium += reducer; + switch (overlay) { + case OVERLAY_GOLD1: + case OVERLAY_GOLD2: + case OVERLAY_GOLD3: + case OVERLAY_GOLD4: + Gold += reducer; + break; + + case OVERLAY_GEMS1: + case OVERLAY_GEMS2: + case OVERLAY_GEMS3: + case OVERLAY_GEMS4: + Gems += reducer; + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + if (Rule.BailCount > Tiberium) {Gems++;Tiberium++;} + break; + + default: + break; + } + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + + } else { + + /* + ** If the harvester is stopped on a non Tiberium field and the harvester + ** isn't loaded with Tiberium, then no further action can be performed + ** by this logic routine. Bail with a failure and thus cause a branch to + ** a better suited logic processor. + */ + Set_Stage(0); + Set_Rate(0); + return(false); + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo and * + * then exit the map. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +extern void Logic_Switch_Player_Context(ObjectClass *object); +extern void Logic_Switch_Player_Context(HouseClass *object); +extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type); + +int UnitClass::Mission_Unload(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case UNIT_HARVESTER: + if (PrimaryFacing != DIR_W) { + if (!IsRotating) { + Do_Turn(DIR_W); + } + return(5); + } + + if (!IsDumping) { + IsDumping = true; + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + break; + } + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Dump_List)-1) break; + + IsDumping = false; + if (Tiberium) { + Tiberium = 0; + int credits = Credit_Load(); + House->Harvested(credits); + Tiberium = Gold = Gems = 0; + } + Transmit_Message(RADIO_OVER_OUT); + + Assign_Mission(MISSION_HARVEST); + break; + + case UNIT_TRUCK: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + Status = UNLOADING; + return(1); + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + Assign_Mission(MISSION_GUARD); + break; + } + break; + + case UNIT_APC: +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + case UNIT_PHASE: +#endif + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + + case UNIT_MCV: + switch (Status) { + case 0: + Path[0] = FACING_NONE; + Status = 1; + break; + + case 1: + if (!IsDriving) { + Try_To_Deploy(true); + if (IsActive) { + if (IsDeploying) { + Status = 2; + } else { + if (!House->IsHuman && Session.Type != GAME_NORMAL) { + Assign_Mission(MISSION_HUNT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + } + } + break; + + case 2: + if (!IsDeploying) { + Assign_Mission(MISSION_GUARD); + } + break; + } + return(1); + + case UNIT_MINELAYER: + switch (Status) { + case INITIAL_CHECK: + dir = DIR_NE; + if (Ammo > 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + Assign_Mission(MISSION_GUARD); + } + break; + + case MANEUVERING: + if (!IsRotating) { + APC_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (Ammo > 0) { + if (!Map[Center_Coord()].Cell_Building()) { + Mark(MARK_UP); + BuildingClass * building = new BuildingClass((House->ActLike == HOUSE_USSR || House->ActLike == HOUSE_UKRAINE || House->ActLike == HOUSE_BAD) ? STRUCT_APMINE : STRUCT_AVMINE, House->Class->House); + if (building != NULL) { + ScenarioInit = true; + if (building->Unlimbo(Coord)) { + Sound_Effect(VOC_MINELAY1, Coord); + ScenarioInit = false; + building->Revealed(House); + Ammo--; + } + ScenarioInit = false; + } + Status = CLOSING_DOOR; + Mark(MARK_DOWN); + } else { + Status = CLOSING_DOOR; + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close APC door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + APC_Close_Door(); + } + if (Is_Door_Closed()) { + Assign_Mission(MISSION_GUARD); + } + break; + } + break; + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + case UNIT_MAD: + if (!Gems && !IsDumping) { + Gems = 1; + Gold = 0; + Arm = QuakeDelay * House->ROFBias; +#ifdef ENGLISH + Speak(VOX_MADTANK_DEPLOYED); // this voice only exists in English +#else + Sound_Effect(VOC_BUZZY1,Center_Coord()); +#endif + Set_Stage(0); + Set_Rate(Rule.OreDumpRate*2); + IsDumping = true; + +#if 1 + InfantryClass *crew = new InfantryClass(INFANTRY_C1, House->Class->House); + if (crew != NULL) crew->IsTechnician = true; + + if (crew != NULL) { + DirType toface = DIR_S + PrimaryFacing; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + if (crew->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + crew->Unlimbo(Coord_Move(Coord, newface, 0x0080), newface); + ScenarioInit--; + crew->Assign_Mission(MISSION_MOVE); + crew->Assign_Destination(::As_Target(newcell)); + break; + } + } + } +#endif + } + + if ( (Arm && !Gold) || IronCurtainCountDown) { + Set_Stage(Fetch_Stage() & 1); + return(1); + } + + if (!Gold) { + Sound_Effect(VOC_MAD_CHARGE, Center_Coord()); + Set_Stage(0); + Gold = 1; + return(1); + } + + if (Fetch_Stage() < 7) { + return(1); + } + + IsDumping = false; + + Sound_Effect(VOC_MAD_EXPLODE, Center_Coord()); + + Strength = 1; // assure destruction + PendingTimeQuake = true; // trigger a time quake + TimeQuakeCenter = ::As_Target(Center_Coord()); + break; + + case UNIT_CHRONOTANK: + if (IsOwnedByPlayer) { + Map.IsTargettingMode = SPC_CHRONO2; + HouseClass* old_player_ptr = PlayerPtr; + Logic_Switch_Player_Context(this); + Unselect_All(); + On_Special_Weapon_Targetting(PlayerPtr, Map.IsTargettingMode); + Logic_Switch_Player_Context(old_player_ptr); + } + House->UnitToTeleport = As_Target(); + + Assign_Mission(MISSION_GUARD); + break; +#endif + default: + break; + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Harvest -- Handles the harvesting process used by harvesters. * + * * + * This is the AI process used by harvesters when they are doing their harvesting action. * + * This entails searching for nearby Tiberium, heading there, harvesting, and then * + * returning to a refinery for unloading. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + * 06/21/1995 JLB : Force guard mode if no Tiberium found. * + * 09/28/1995 JLB : Aborts harvesting if there are no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Harvest(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + enum { + LOOKING, + HARVESTING, + FINDHOME, + HEADINGHOME, + GOINGTOIDLE, + }; + + /* + ** A non-harvesting type unit will just sit still if it is given the harvest mission. This + ** allows combat units to act "brain dead". + */ + if (!Class->IsToHarvest) return(TICKS_PER_SECOND*30); + + /* + ** If there are no more refineries, then drop into guard mode. + */ + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Assign_Mission(MISSION_GUARD); + return(1); + } + + switch (Status) { + + /* + ** Go and find a Tiberium field to harvest. + */ + case LOOKING: + /* + ** When full of tiberium, just skip to finding a free refinery + ** to unload at. + */ + if (Tiberium_Load() == 1) { + Status = FINDHOME; + return(1); + } + + /* + ** Look for ore where we last found some - mine the same patch + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + ArchiveTarget = 0; + } + IsHarvesting = false; + if (Goto_Tiberium(Rule.TiberiumLongScan / CELL_LEPTON_W)) { + IsHarvesting = true; + Set_Rate(2); + Set_Stage(0); + Status = HARVESTING; + return(1); + } else { + + /* + ** If the harvester isn't on Tiberium and it is not heading toward Tiberium, then + ** force it to go into guard mode. This will prevent the harvester from repeatedly + ** searching for Tiberium. + */ + if (!Target_Legal(NavCom)) { + + /* + ** If the archive target is legal, then head there since it is presumed + ** that the archive target points to the last place it harvested at. This might + ** solve the case where the harvester gets stuck and can't find Tiberium just because + ** it is greater than 32 squares away. + */ + if (Target_Legal(ArchiveTarget)) { + Assign_Destination(ArchiveTarget); + } else { + Status = GOINGTOIDLE; + IsUseless = true; + House->IsTiberiumShort = true; + return(TICKS_PER_SECOND*7); + } + } else { + IsUseless = false; + } + } + break; + + /* + ** Harvest at current location until full or Tiberium exhausted. + */ + case HARVESTING: +// if (Fetch_Stage() > ARRAY_SIZE(Class->Harvester_Load_List)) { +// Set_Stage(0); +// } + if (Fetch_Rate() == 0) { + Set_Stage(0); + Set_Rate(Rule.OreDumpRate); + } + + if (Fetch_Stage() < ARRAY_SIZE(Class->Harvester_Load_List)) return(1); + if (!Harvesting()) { + IsHarvesting = false; + if (Tiberium_Load() == 1) { + Status = FINDHOME; + ArchiveTarget = ::As_Target(Coord_Cell(Coord)); + } else { + if (!Goto_Tiberium(Rule.TiberiumShortScan / CELL_LEPTON_W) && !Target_Legal(NavCom)) { + ArchiveTarget = TARGET_NONE; + Status = FINDHOME; + } else { + Status = HARVESTING; + IsHarvesting = true; + } + } + return(1); + } + return(1); +// return(TICKS_PER_SECOND*Rule.OreDumpRate); + + /* + ** Find and head to refinery. + */ + case FINDHOME: + if (!Target_Legal(NavCom)) { + + /* + ** Find nearby refinery and head to it? + */ + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + + /* + ** Since the refinery said it was ok to load, establish radio + ** contact with the refinery and then await docking orders. + */ + if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) { + Speak(VOX_NEED_MO_CAPACITY); + } + } else { + ScenarioInit++; + nearest = Find_Docking_Bay(STRUCT_REFINERY, false); + ScenarioInit--; + if (nearest != NULL) { + Assign_Destination(::As_Target(Nearby_Location(nearest))); + } + } + } + break; + + /* + ** In communication with refinery so that it will successfully dock and + ** unload. If, for some reason, radio contact was lost, then hunt for + ** another refinery to unload at. + */ + case HEADINGHOME: + Assign_Mission(MISSION_ENTER); + return(1); + + /* + ** The harvester has nothing to do. There is no Tiberium nearby and + ** no where to go. + */ + case GOINGTOIDLE: + if (IsUseless) { + if (House->ActiveBScan & STRUCTF_REPAIR) { + Assign_Mission(MISSION_REPAIR); + } else { + Assign_Mission(MISSION_HUNT); + } + } + Assign_Mission(MISSION_GUARD); + break; + + } + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Hunt -- This is the AI process for aggressive enemy units. * + * * + * Computer controlled units must be intelligent enough to find enemies as well as to * + * attack them. This AI process will handle both the simple attack process as well as the * + * scanning for enemy units to attack. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1994 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Hunt(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == UNIT_MCV) { + enum { + FIND_SPOT, + WAITING + }; + + switch (Status) { + + /* + ** This stage handles locating a convenient spot, rotating to face the correct + ** direction and then commencing the deployment operation. + */ + case FIND_SPOT: + if (Goto_Clear_Spot()) { + if (Try_To_Deploy(false)) { + Status = WAITING; + } + } + break; + + /* + ** This stage watchdogs the deployment operation and if for some reason, the deployment + ** is aborted (the IsDeploying flag becomes false), then it reverts back to hunting for + ** a convenient spot to deploy. + */ + case WAITING: + if (!IsDeploying) { + Status = FIND_SPOT; + } + break; + } + } else { + + return(DriveClass::Mission_Hunt()); + } + return(MissionControl[Mission].Normal_Delay()+Random_Pick(0, 2)); +} + + +/*********************************************************************************************** + * UnitClass::Overlap_List -- Determines overlap list for units. * + * * + * The unit overlap list is used to keep track of which cells are to * + * be marked for redraw. This is critical in order to keep the units * + * displayed correctly. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the overlap list pointer for the unit at its * + * present position. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/26/1994 JLB : Created. * + * 06/19/1994 JLB : Uses Coord_Spillable_List function. * + *=============================================================================================*/ +short const * UnitClass::Overlap_List(bool redraw) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (Is_Selected_By_Player()) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + if (Class->IsTurretEquipped || Class->IsRadarEquipped) { + rect = Union(rect, Rect(-15, -15, 30, 30)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + int size = ICON_PIXEL_W; + + if (redraw && (Is_Selected_By_Player() || IsFiring)) { + size += 24; + } + if (Class->IsGigundo || IsAnimAttached) { + size = ICON_PIXEL_W*2; + } + + return(Coord_Spillage_List(Coord, size)+1); +} + + +/*********************************************************************************************** + * UnitClass::Can_Enter_Cell -- Determines cell entry legality. * + * * + * Use this routine to determine if the unit can enter the cell * + * specified and given the direction of entry specified. Typically, * + * this is used when determining unit travel path. * + * * + * INPUT: cell -- The cell to examine. * + * * + * facing -- The facing that the unit would enter the specified * + * cell. If this value is -1, then don't consider * + * facing when performing the check. * + * * + * OUTPUT: Returns the reason why it couldn't enter the cell or MOVE_OK if movement is * + * allowed. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/07/1992 JLB : Created. * + * 04/16/1994 JLB : Converted to member function. * + * 07/04/1995 JLB : Allowed to drive on building trying to enter it. * + *=============================================================================================*/ +MoveType UnitClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + bool cancrush = false; + + CellClass const * cellptr = &Map[cell]; + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map() && IsLocked) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** Certain vehicles can drive over walls. Check for this case and + ** and return the appropriate flag. Other units treat walls as impassable. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const * optr = &OverlayTypeClass::As_Reference(cellptr->Overlay); + + if (optr->IsCrate && !((Session.Type == GAME_NORMAL) ? House->IsPlayerControl : House->IsHuman) && Session.Type == GAME_NORMAL) { + return(MOVE_NO); + } + + if (optr->IsWall) { + + /* + ** If the blocking wall is crushable (and not owned by this player or one of this players + ** allies, then record that it is crushable and let the normal logic take over. The end + ** result should cause this unit to consider the cell passable. + */ + if (optr->IsCrushable && Class->IsCrusher) { + cancrush = !House->Is_Ally(cellptr->Owner); + } + + if (!cancrush && Is_Weapon_Equipped()) { + WarheadTypeClass const * whead = Class->PrimaryWeapon->WarheadPtr; + + if (whead->IsWallDestroyer || (whead->IsWoodDestroyer && optr->IsWooden)) { +// if (!House->IsHuman && !House->Is_Ally(cellptr->Owner)) { + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; +// } else { +// return(MOVE_NO); +// } + } else { + return(MOVE_NO); + } + } + } + } + + /* + ** Loop through all of the objects in the square setting a bit + ** for how they affect movement. + */ + bool crushable = false; + ObjectClass * obj = cellptr->Cell_Occupier(); + while (obj != NULL) { + + if (obj != this) { + + /* + ** If object is a land mine, allow movement if possible. + */ + if (obj->What_Am_I() == RTTI_BUILDING && (!Rule.IsMineAware || !((BuildingClass *)obj)->House->Is_Ally(House))) { + if ((*(BuildingClass *)obj) == STRUCT_APMINE) return(MOVE_OK); + if ((*(BuildingClass *)obj) == STRUCT_AVMINE) return(MOVE_OK); + } + + /* + ** Always allow entry if trying to move on a cell with + ** authorization from the occupier. + */ + if (obj == Contact_With_Whom() && (IsTethered || (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_REPAIR))) { + return(MOVE_OK); + } + + /* + ** Special check to allow entry into the sea transport this vehicle + ** is trying to enter. + */ + if (Mission == MISSION_ENTER && obj->As_Target() == NavCom && IsTethered) { + return(MOVE_OK); + } + + /* + ** Guard area should not allow the guarding unit to enter the cell with the + ** guarded unit. + */ + if (Mission == MISSION_GUARD_AREA && ArchiveTarget == obj->As_Target()) { + return(MOVE_NO); + } + + bool is_moving = obj->Is_Foot() && + (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->PrimaryFacing.Is_Rotating() || ((FootClass *)obj)->IsDriving); +// (((FootClass *)obj)->IsRotating || ((FootClass *)obj)->IsDriving); +// (Target_Legal(((FootClass *)obj)->NavCom) || ((FootClass *)obj)->IsDriving); + + if (House->Is_Ally(obj)) { + if (is_moving) { + int face = Dir_Facing(PrimaryFacing); + int techface = Dir_Facing(((FootClass const *)obj)->PrimaryFacing) ^4; + if (face == techface && Distance((AbstractClass const *)obj) <= 0x1FF) { + return(MOVE_NO); + } + if (retval < MOVE_MOVING_BLOCK) retval = MOVE_MOVING_BLOCK; + } else { + if (obj->What_Am_I() == RTTI_BUILDING) return(MOVE_NO); + + /* + ** If the blocking object is not in the same zone, then it certainly + ** isn't a temporary block, it is a permanent one. + */ + if (Map[Coord].Zones[Class->MZone] != cellptr->Zones[Class->MZone]) return(MOVE_NO); + + if (retval < MOVE_TEMP) retval = MOVE_TEMP; + } + } else { + + /* + ** Cloaked enemy objects are not considered if this is a Find_Path() + ** call. + */ + if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) { + + /* + ** If this unit can crush infantry, and there is an enemy infantry in the + ** cell, don't consider the cell impassible. This is true even if the unit + ** doesn't contain a legitimate weapon. + */ + bool crusher = Class->IsCrusher; + if (!crusher || !obj->Class_Of().IsCrushable) { + + /* + ** Any non-allied blockage is considered impassable if the unit + ** is not equipped with a weapon. + */ + if (Class->PrimaryWeapon == NULL) return(MOVE_NO); + + /* + ** Some kinds of terrain are considered destroyable if the unit is equipped + ** with the weapon that can destroy it. Otherwise, the terrain is considered + ** impassable. + */ + switch (obj->What_Am_I()) { + case RTTI_TERRAIN: + +#ifdef TOFIX + if (((TerrainClass *)obj)->Class->Armor == ARMOR_WOOD && + Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) { + + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + break; +#else + return(MOVE_NO); +#endif + + default: + if (retval < MOVE_DESTROYABLE) retval = MOVE_DESTROYABLE; + break; + } + } else { + crushable = true; + } + } else { + if (retval < MOVE_CLOAK) retval = MOVE_CLOAK; + } + } + } + + /* + ** Move to next object in chain. + */ + obj = obj->Next; + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (!cancrush && retval != MOVE_DESTROYABLE && Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (retval == MOVE_OK && !crushable && (cellptr->Flag.Composite & 0x3F) != 0) { + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } else { + if (cellptr->InfType != HOUSE_NONE && House->Is_Ally(cellptr->InfType)) { + retval = MOVE_MOVING_BLOCK; + } else { + + /* + ** Enemy infantry have reserved the cell. If this unit can crush + ** infantry, consider the cell passable. If not, then consider the + ** cell destroyable if it has a weapon. If neither case applies, then + ** this vehicle should avoid the cell altogether. + */ + if (!Class->IsCrusher) { + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsAntiGround) { + retval = MOVE_DESTROYABLE; + } else { + return(MOVE_NO); + } + } + } + } + } + + /* + ** If its ok to move into the cell because we can crush whats in the cell, then + ** make sure no one else is already moving into the cell to crush something. + */ + if (retval == MOVE_OK && crushable && cellptr->Flag.Occupy.Vehicle) { + + /* + ** However, if the cell is occupied by a crushable vehicle, then we can + ** never be sure if some other friendly vehicle is also trying to crush + ** the cell at the same time. In the case of a crushable vehicle in the + ** cell, then allow entry. + */ + if (!cellptr->Cell_Unit() || !cellptr->Cell_Unit()->Class->IsCrushable) { + return(MOVE_MOVING_BLOCK); + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * UnitClass::Init -- Clears all units for scenario preparation. * + * * + * This routine will zero out the unit list and unit objects. This routine is typically * + * used in preparation for a new scenario load. All units are guaranteed to be eliminated * + * by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/15/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Init(void) +{ + Units.Free_All(); +} + + +/*********************************************************************************************** + * UnitClass::Start_Driver -- Starts driving and reserves destination cell. * + * * + * This routine will start the vehicle moving by marking the destination cell as * + * reserved. Cells must be reserved in this fashion or else they might become occupied as * + * the vehicle is proceeding toward it. * + * * + * INPUT: headto -- The location where the vehicle will be heading. * + * * + * OUTPUT: bool; Was the vehicle started to move? Failure could be the result of the cell * + * being occupied. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/22/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Start_Driver(COORDINATE & headto) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) {//BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/11/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(ObjectClass const * object) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + /* + ** Allow units to move onto land mines. + */ + if (action == ACTION_NONE && object->What_Am_I() == RTTI_BUILDING) { + StructType blah = *((BuildingClass *)object); + if (blah == STRUCT_AVMINE || blah == STRUCT_APMINE) return(ACTION_MOVE); + } + + /* + ** If the unit doesn't have a weapon, but can crush the object, then consider + ** the object as a movable location. + */ + if (action == ACTION_ATTACK && !Can_Player_Fire()) { + if (Class->IsCrusher && object->Class_Of().IsCrushable) { + action = ACTION_MOVE; + } else { + action = ACTION_SELECT; + } + } + + /* + ** Don't allow special deploy action unless there is something to deploy. + */ + if (action == ACTION_SELF) { + if (*this == UNIT_MCV) { + + /* + ** The MCV will get the no-deploy cursor if it couldn't + ** deploy at its current location. + */ + ((ObjectClass &)(*this)).Mark(MARK_UP); + if (!BuildingTypeClass::As_Reference(STRUCT_CONST).Legal_Placement(Coord_Cell(Adjacent_Cell(Center_Coord(), FACING_NW)))) { + action = ACTION_NO_DEPLOY; + } + ((ObjectClass &)(*this)).Mark(MARK_DOWN); + + } else { + + /* + ** The mine layer can "deploy" its mines if it currently isn't + ** sitting on top of a mine and it still has mines available. + */ + if (*this == UNIT_MINELAYER) { + if (!Ammo || Map[Center_Coord()].Cell_Building() || (Map[Center_Coord()].Smudge != SMUDGE_NONE && SmudgeTypeClass::As_Reference(Map[Center_Coord()].Smudge).IsBib)) { + action = ACTION_NO_DEPLOY; + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK || *this == UNIT_MAD) { + if (*this == UNIT_CHRONOTANK) { +// If the chrono tank's counter is still charging up, don't allow deploy. Or, +// if it's a player-controlled chrono tank, and the player's currently trying +// to teleport a different unit, don't allow teleporting this unit. + if(MoebiusCountDown || (IsOwnedByPlayer && House->UnitToTeleport && Map.IsTargettingMode == SPC_CHRONO2)) { + action = ACTION_NO_DEPLOY; + } + } + } else { +#endif + /* + ** All other units can "deploy" their passengers if they in-fact have + ** passengers and are a transport vehicle. Otherwise, they cannot + ** perform any self action. + */ + if (Class->Max_Passengers() > 0) { + if (How_Many() == 0) { + action = ACTION_NO_DEPLOY; + } + } else { + action = ACTION_NONE; + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + } +#endif + } + } + } + + /* + ** Special return to friendly refinery action. + */ + if (House->IsPlayerControl && object->Is_Techno() && ((TechnoClass const *)object)->House->Is_Ally(this)) { + if (object->What_Am_I() == RTTI_BUILDING && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object) == RADIO_ROGER) { + action = ACTION_ENTER; + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + if (building->Class->Type == STRUCT_REPAIR && ((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER && !building->In_Radio_Contact() && !building->Is_Something_Attached()) { + action = ACTION_MOVE; + } + } + + /* + ** Check to see if it can enter a transporter. + */ + if ( + House->Is_Ally(object) && + House->IsPlayerControl && object->Is_Techno() && object->What_Am_I() == RTTI_VESSEL) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if( *(VesselClass *)object != VESSEL_CARRIER) { +#endif + switch (((UnitClass *)this)->Transmit_Message(RADIO_CAN_LOAD, (TechnoClass*)object)) { + case RADIO_ROGER: + action = ACTION_ENTER; + break; + + case RADIO_NEGATIVE: + action = ACTION_NO_ENTER; + break; + + default: + action = ACTION_NONE; + break; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NONE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * UnitClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. At the unit level, only the harvester is checked for. The lower * + * classes determine the regular action response. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +ActionType UnitClass::What_Action(CELL cell) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_MOVE && Map[cell].Land_Type() == LAND_TIBERIUM && Class->IsToHarvest) { + return(ACTION_HARVEST); + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_MAD && (IsDumping || Gold)) { + action = ACTION_NOMOVE; + } +#endif + return(action); +} + + +/*********************************************************************************************** + * UnitClass::Exit_Repair -- Drive the unit off the repair facility. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/03/1995 BWG : Created. * + *=============================================================================================*/ +#define XYCELL(x, y) (y*MAP_CELL_W+x) +void UnitClass::Exit_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + int i; + CELL cell; + bool found = false; + static short const ExitRepair[] = { + XYCELL(0, -2), + XYCELL(1, -1), + XYCELL(2, 0), + XYCELL(1, 1), + XYCELL(0, 2), + XYCELL(-1, 1), + XYCELL(-2, 0), + XYCELL(-1, -1) + }; + + cell = Coord_Cell(Coord) + ExitRepair[Dir_Facing(PrimaryFacing.Current())]; + if (Can_Enter_Cell(cell) == MOVE_OK) found = true; + + if (!found) for (i=0; i<8; i++) { + cell = Coord_Cell(Coord) + ExitRepair[i]; + if (Can_Enter_Cell(cell) == MOVE_OK) { + found = true; + break; + } + } + if (found) { +// DirType dir = Direction(cell); + + Assign_Mission(MISSION_MOVE); + Assign_Destination(::As_Target(cell)); + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard -- Special guard mission override processor. * + * * + * Handles the guard mission for the unit. If the IQ is high enough and the unit is * + * a harvester, it will begin to harvest automatically. An MCV might autodeploy. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the time delay before this command is executed again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/08/1995 JLB : Created. * + * 05/08/1995 JLB : Fixes gunboat problems. * + *=============================================================================================*/ +int UnitClass::Mission_Guard(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + if (/*House->IsBaseBuilding &&*/ !House->IsHuman && Class->IsToHarvest && House->Get_Quantity(STRUCT_REFINERY) > 0 && !House->IsTiberiumShort) { + Assign_Mission(MISSION_HARVEST); + return(1); +// return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + + if (*this == UNIT_MCV && House->IsBaseBuilding) { + Assign_Mission(MISSION_UNLOAD); + return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); + } + return(DriveClass::Mission_Guard()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Move -- Handles special move mission overrides. * + * * + * This routine intercepts the normal move mission and if a gunboat is being processed, * + * changes its mission to hunt. This is an attempt to keep the gunboat on the hunt mission * + * regardless of what the player did. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of ticks before this routine should be called again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/09/1995 JLB : Created. * + * 09/28/1995 JLB : Harvester stick in guard mode if no more refineries. * + *=============================================================================================*/ +int UnitClass::Mission_Move(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + IsHarvesting = false; + + /* + ** Always make sure that that transport door is closed if the vehicle is moving. + */ + if (!Is_Door_Closed()) { + APC_Close_Door(); + } + + return(DriveClass::Mission_Move()); +} + + +/*********************************************************************************************** + * UnitClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; + + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_S); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Attach -- Attaches a house flag to this unit. * + * * + * This routine will attach a house flag to this unit. * + * * + * INPUT: house -- The house that is having its flag attached to it. * + * * + * OUTPUT: Was the house flag successfully attached to this unit? * + * * + * WARNINGS: A unit can only carry one flag at a time. This might be a reason for failure * + * of this routine. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Attach(HousesType house) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (house != HOUSE_NONE && Flagged == HOUSE_NONE) { + Flagged = house; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Flag_Remove -- Removes the house flag from this unit. * + * * + * This routine will remove the house flag that is attached to this unit. * + * * + * INPUT: none * + * * + * OUTPUT: Was the flag successfully removed? * + * * + * WARNINGS: This routine doesn't put the flag into a new location. That operation must * + * be performed or else the house flag will cease to exist. * + * * + * HISTORY: * + * 05/23/1995 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Flag_Remove(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Flagged != HOUSE_NONE) { + Flagged = HOUSE_NONE; + Mark(MARK_CHANGE); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * UnitClass::Pip_Count -- Fetches the number of pips to display on unit. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the unit. * + * This will either be the number of passengers or the percentage full (in 1/5ths) of * + * a harvester. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Pip_Count(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->Max_Passengers() > 0) { + return(How_Many()); + } + + if (*this == UNIT_MINELAYER) { + int retval = 0; + if (Ammo > 0) { + retval = Class->Max_Pips() * fixed(Ammo, Class->MaxAmmo); + if (!retval) retval = 1; + } + return(retval); + } + + if (*this == UNIT_HARVESTER) { + return((Gold + Gems) / 4); + } + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (*this == UNIT_CHRONOTANK) { + int fulldur = ChronoTankDuration * TICKS_PER_MINUTE; + return( (fulldur - MoebiusCountDown) / (fulldur / 5)); + } +#endif + return(0); +} + + +/*********************************************************************************************** + * UnitClass::APC_Close_Door -- Closes an APC door. * + * * + * This routine will initiate closing of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Close_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + Close_Door(10, 2); +} + + +/*********************************************************************************************** + * UnitClass::APC_Open_Door -- Opens an APC door. * + * * + * This routine will initiate opening of the APC door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::APC_Open_Door(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + if (PrimaryFacing == DIR_NW || PrimaryFacing == DIR_NE) { + Open_Door(10, 2); + } else { + Open_Door(1, 2); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Crew_Type -- Fetches the kind of crew that this object produces. * + * * + * When a unit is destroyed, a crew member might be generated. This routine will return * + * with the infantry type to produce for this unit. This routine will be called for every * + * survivor that is generated. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a suggested infantry type to generate as a survivor from this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1995 JLB : Created. * + *=============================================================================================*/ +InfantryType UnitClass::Crew_Type(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->PrimaryWeapon == NULL) { + if (Percent_Chance(50)) { + return(INFANTRY_C1); + } else { + return(INFANTRY_C7); + } + } + return(DriveClass::Crew_Type()); +} + + +/*********************************************************************************************** + * UnitClass::Mission_Repair -- Handles finding and proceeding on a repair mission. * + * * + * This mission handler will look for a repair facility. If one is found then contact * + * is established and then the normal Mission_Enter logic is performed. The repair facility * + * will take over the actual repair coordination process. * + * * + * INPUT: none * + * * + * OUTPUT: Returns the number of game frames to delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Repair(void) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, true); + + IsHarvesting = false; + + /* + ** If there is no available repair facility, then check to see if there + ** are any repair facilities at all. If not, then enter this unit + ** into idle state. + */ + if (nearest == NULL) { + if (!(House->ActiveBScan & STRUCTF_REFINERY)) { + Enter_Idle_Mode(); + } + } else { + + /* + ** Try to establish radio contact with the repair facility. If contact + ** was established, then proceed with normal enter mission, which handles + ** the repair process. + */ + if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Assign_Mission(MISSION_ENTER); + return(1); + } + } + + /* + ** If no action could be performed at this time, then wait + ** around for a bit before trying again. + */ + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * UnitClass::Fire_Direction -- Determines the direction of firing. * + * * + * This routine will return with the facing that a projectile will travel if it was * + * fired at this instant. The facing should match the turret facing for those units * + * equipped with a turret. If the unit doesn't have a turret, then it will be the facing * + * of the body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the default firing direction for a projectile. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +DirType UnitClass::Fire_Direction(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsTurretEquipped) { + if (*this == UNIT_V2_LAUNCHER) { + int diff1 = SecondaryFacing.Difference(DIR_E); + int diff2 = SecondaryFacing.Difference(DIR_W); + diff1 = ABS(diff1); + diff2 = ABS(diff2); + int diff = min(diff1, diff2); + int adj = Fixed_To_Cardinal(ABS(SecondaryFacing.Difference(DIR_N)), 64-diff); + if (SecondaryFacing.Difference(DIR_N) < 0) { + return(DirType)(SecondaryFacing - (DirType)adj); + } else { + return(DirType)(SecondaryFacing + (DirType)adj); + } + } + return(SecondaryFacing.Current()); + } + + return(DriveClass::Fire_Direction()); +} + + +/*********************************************************************************************** + * UnitClass::Ok_To_Move -- Queries whether the vehicle can move. * + * * + * This virtual routine is used to determine if the vehicle is allowed * + * to start moving. It is typically called when the vehicle desires * + * to move but needs confirmation from the turret logic before * + * proceeding. This happens when dealing with a vehicle that must have * + * its turret face the same direction as the body before the vehicle * + * may begin movement. * + * * + * INPUT: dir -- The facing the unit wants to travel in. * + * * + * OUTPUT: bool; Can the unit begin forward movement now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/12/1994 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Ok_To_Move(DirType dir) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + if (Class->IsLockTurret) { + if (IsRotating) { + return(false); + } else { + if (SecondaryFacing.Difference(dir)) { + ((UnitClass *)this)->SecondaryFacing.Set_Desired(dir); + return(false); + } + } + } + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Can_Fire -- Determines if turret can fire upon target. * + * * + * This routine determines if the turret can fire upon the target * + * specified. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use to determine legality to fire. 0=primary, * + * 1=secondary. * + * * + * OUTPUT: Returns the fire status type that indicates if firing is allowed and if not, why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + * 06/01/1994 JLB : Returns reason why it can't fire. * + *=============================================================================================*/ +FireErrorType UnitClass::Can_Fire(TARGET target, int which) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + FireErrorType fire = DriveClass::Can_Fire(target, which); + + if (fire == FIRE_OK) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** If this unit cannot fire while moving, then bail. + */ + if ((Class->IsNoFireWhileMoving /*!Class->IsTurretEquipped || Class->IsLockTurret*/) && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + dir = Direction(target); + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff < 8) { + return(DriveClass::Can_Fire(target, which)); + } + return(FIRE_FACING); + } + return(fire); +} + + +/*********************************************************************************************** + * UnitClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the turret. It will check * + * to see if firing is technically legal given the specified target. * + * If it is legal to fire, it does so. It is safe to call this routine * + * every game tick. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * UnitClass::Fire_At(TARGET target, int which) +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + BulletClass * bullet = NULL; + WeaponTypeClass const * weap = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + if (weap == NULL) return(NULL); + + if (Can_Fire(target, which) == FIRE_OK) { + bullet = DriveClass::Fire_At(target, which); + + if (bullet != NULL) { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +if(Class->Type == UNIT_DEMOTRUCK && IsActive) delete this; +#endif + /* + ** Possible reload timer set. + */ + if ((*this == UNIT_V2_LAUNCHER) && Reload == 0) { + Reload = TICKS_PER_SECOND * 30; + } + } + } + + return(bullet); +} + + +/*********************************************************************************************** + * UnitClass::Class_Of -- Fetches a reference to the class type for this object. * + * * + * This routine will fetch a reference to the TypeClass of this object. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with reference to the type class of this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1995 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & UnitClass::Class_Of(void) const +{ + assert(Units.ID(this) == ID); + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * UnitClass::Tiberium_Load -- Determine the Tiberium load as a percentage. * + * * + * Use this routine to determine what the Tiberium load is (as a fixed point percentage). * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the current "fullness" rating for the object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + *=============================================================================================*/ +fixed UnitClass::Tiberium_Load(void) const +{ + assert(IsActive); + + if (*this == UNIT_HARVESTER) { + return(fixed(Tiberium, Rule.BailCount)); + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. * + * * + * This routine will offload one Tiberium packet/quantum/bail from the object. Multiple * + * calls to this routine are needed in order to fully offload all Tiberium. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of credits offloaded for the one call. If zero is returned,* + * then this indicates that all Tiberium has been offloaded. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1995 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Offload_Tiberium_Bail(void) +{ + assert(IsActive); + + if (Tiberium) { + Tiberium--; + +// MBL 05.15.2020: Note, if the below code is ever reeanbled for some ready, make sure to see fix in +// Tiberian Dawn's DriveClass::Offload_Tiberium_Bail() for AI players + +#ifdef TOFIX + if (House->IsHuman) { + return(UnitTypeClass::FULL_LOAD_CREDITS/UnitTypeClass::STEP_COUNT); + } + return(UnitTypeClass::FULL_LOAD_CREDITS+(UnitTypeClass::FULL_LOAD_CREDITS/3)/UnitTypeClass::STEP_COUNT); +#endif + } + return(0); +} + + +/*********************************************************************************************** + * UnitClass::Approach_Target -- Handles approaching the target in order to attack it. * + * * + * This routine will check to see if the target is infantry and it can be overrun. It will * + * try to overrun the infantry rather than attack it. This only applies to computer * + * controlled vehicles. If it isn't the infantry overrun case, then it falls into the * + * base class for normal (complex) approach algorithm. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/17/1995 JLB : Created. * + * 07/12/1995 JLB : Flamethrower tanks don't overrun -- their weapon is better. * + *=============================================================================================*/ +void UnitClass::Approach_Target(void) +{ + assert(IsActive); + + /* + ** Only if there is a legal target should the approach check occur. + */ + if (!House->IsHuman && Target_Legal(TarCom) && !Target_Legal(NavCom)) { + + /* + ** Special case: + ** If this is for a unit that can crush infantry, and the target is + ** infantry, AND the infantry is pretty darn close, then just try + ** to drive over the infantry instead of firing on it. + */ + TechnoClass * target = As_Techno(TarCom); + if (Class->IsCrusher && Distance(TarCom) < Rule.CrushDistance && target && ((TechnoTypeClass const &)(target->Class_Of())).IsCrushable) { + Assign_Destination(TarCom); + return; + } + } + + /* + ** In the other cases, uses the more complex "get to just within weapon range" + ** algorithm. + */ + DriveClass::Approach_Target(); +} + + +/*********************************************************************************************** + * DriveClass::Overrun_Square -- Handles vehicle overrun of a cell. * + * * + * This routine is called when a vehicle enters a square or when it is about to enter a * + * square (controlled by parameter). When a vehicle that can crush infantry enters a * + * cell that contains infantry, then the infantry will be destroyed (regardless of * + * affiliation). When a vehicle threatens to overrun a square, all occupying infantry * + * will attempt to get out of the way. * + * * + * INPUT: cell -- The cell that is, or soon will be, entered by a vehicle. * + * * + * threaten -- Don't kill, but just threaten to enter the cell. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 01/19/1995 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Overrun_Square(CELL cell, bool threaten) +{ + assert(IsActive); + + CellClass * cellptr = &Map[cell]; + + if (Class->IsCrusher) { + if (threaten) { + + /* + ** If the cell contains infantry, then they will panic when a vehicle tries + ** drive over them. Have the infantry run away instead. + */ + if (cellptr->Flag.Composite & 0x1F) { + + /* + ** Scattering is controlled by the game difficulty level. + */ + cellptr->Incoming(0, true); + } + } else { + ObjectClass * object = cellptr->Cell_Occupier(); + int crushed = false; + while (object != NULL) { + if (object->Class_Of().IsCrushable && !House->Is_Ally(object) && Distance(object->Center_Coord()) < CELL_LEPTON_W/2) { + +#ifdef OBSOLETE + /* + ** If we're running over infantry, let's see if the infantry we're + ** squashing is a thief trying to capture us. If so, let him succeed. + */ + if (object->What_Am_I() == RTTI_INFANTRY && *((InfantryClass *)object) == INFANTRY_THIEF && ((InfantryClass *)object)->NavCom == As_Target()) { + ObjectClass * next = object->Next; + IsOwnedByPlayer = ((InfantryClass *)object)->IsOwnedByPlayer; + House = ((InfantryClass *)object)->House; + delete object; + object = next; + } else { +#endif + ObjectClass * next = object->Next; + crushed = true; + + /* + ** Record credit for the kill(s) + */ + Sound_Effect(VOC_SQUISH, Coord); + if (object->Height == 0) { + AnimClass* anim = new AnimClass(ANIM_CORPSE1, object->Center_Coord()); + if (anim != NULL) { + anim->OwnerHouse = object->Owner(); + } + } + object->Record_The_Kill(this); + object->Mark(MARK_UP); + object->Limbo(); + delete object; + //new OverlayClass(OVERLAY_SQUISH, Coord_Cell(Coord)); + + object = next; +#ifdef OBSOLETE + } +#endif + } else { + object = object->Next; + } + } + if (crushed) Do_Uncloak(); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Assign_Destination -- Assign a destination to a unit. * + * * + * This will assign the specified destination to the unit. It is presumed that doing is * + * is all that is needed in order to cause the unit to move to the specified destination. * + * * + * INPUT: target -- The target (location) to move to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && Contact_With_Whom()->Is_Infantry()) { + Transmit_Message(RADIO_OVER_OUT); + } + + BuildingClass * b = As_Building(target); + + /* + ** Handle entry logic here. + */ + if (Mission == MISSION_ENTER || MissionQueue == MISSION_ENTER) { + + /* + ** If not already in radio contact (presumed with the transport), then + ** either try to establish contact if allowed, or just move close and + ** wait until radio contact can be established. + */ + if (!In_Radio_Contact()) { + if (b != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (b->In_Radio_Contact()) { +// TCTCTC -- call for an update from the transport to get a good rendezvous position. + ArchiveTarget = target; + +/* +** HACK ALERT: The repair bay is counting on the assignment of the NavCom by this routine. +** The refinery must NOT have the navcom assigned by this routine. +*/ +if (*b != STRUCT_REPAIR) { + target = TARGET_NONE; +} + } else { + if (Transmit_Message(RADIO_DOCKING, b) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + if (*b == STRUCT_REPAIR) { + ArchiveTarget = target; + } + } +if (*b != STRUCT_REPAIR) { + ArchiveTarget = target; + target = TARGET_NONE; +} + } + } else { + TechnoClass * techno = As_Techno(target); + if (techno != NULL) { + + /* + ** Determine if the transport is already in radio contact. If so, then just move + ** toward the transport and try to establish contact at a later time. + */ + if (techno->In_Radio_Contact()) { + // TCTCTC -- call for an update from the transport to get a good rendezvous position. + + ArchiveTarget = target; + } else { + if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) { + if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT); + } else { + //BG: keep retransmitted navcom from radio-move-here. + return; + } + } + } + } + + } + } else { + Path[0] = FACING_NONE; + } + } else { + Path[0] = FACING_NONE; + } + + /* + ** If the player clicked on a friendly repair facility and the repair + ** facility is currently not involved with some other unit (radio or unloading). + */ + if (b != NULL && *b == STRUCT_REPAIR) { + if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this) ) { +// if (target != NULL) { + ArchiveTarget = target; +// } +// target = TARGET_NONE; + } else { + + /* + ** Establish radio contact protocol. If the facility responds correctly, + ** then remain in radio contact and proceed toward the desired destination. + */ + if (Transmit_Message(RADIO_HELLO, b) == RADIO_ROGER) { + + /* + ** Last check to make sure that the loading square is free from permanent + ** occupation (such as a building). + */ + CELL cell = (CELL)(Coord_Cell(b->Center_Coord()) + (MAP_CELL_W-1)); + if (Ground[Map[cell].Land_Type()].Cost[Techno_Type_Class()->Speed] > 0) { + if (Transmit_Message(RADIO_DOCKING) == RADIO_ROGER) { + FootClass::Assign_Destination(target); + Path[0] = FACING_NONE; + return; + } + + /* + ** Failure to establish a docking relationship with the refinery. + ** Bail & await further instructions. + */ + Transmit_Message(RADIO_OVER_OUT); + } + } + } + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * UnitClass::Greatest_Threat -- Fetches the greatest threat for this unit. * + * * + * This routine will search the map looking for a good target to attack. It takes into * + * consideration the type of weapon it is equipped with. * + * * + * INPUT: threat -- The threat type to search for. * + * * + * OUTPUT: Returns with a target value of the target that this unit should pursue. If there * + * is no suitable target, then TARGET_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +TARGET UnitClass::Greatest_Threat(ThreatType threat) const +{ + assert(IsActive); + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +#ifdef OBSOLETE + if (House->IsHuman) { + threat = threat & ~THREAT_BUILDINGS; + } +#endif + + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * UnitClass::Read_INI -- Reads units from scenario INI file. * + * * + * This routine is used to read all the starting units from the * + * scenario control INI file. The units are created and placed on the * + * map by this routine. * + * * + * INI entry format: * + * Housename, Typename, Strength, Coord, Facingnum, Missionname, Triggername * + * * + * INPUT: buffer -- Pointer to the loaded scenario INI file. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/24/1994 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Read_INI(CCINIClass & ini) +{ + UnitClass * unit; // Working unit pointer. + HousesType inhouse; // Unit house. + UnitType classid; // Unit class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = UnitTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != UNIT_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + unit = new UnitClass(classid, inhouse); + if (unit != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + unit->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL,",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + unit->Trigger = tt; + } + } + + if (unit->Unlimbo(coord, dir)) { + unit->Strength = (int)unit->Class->MaxStrength * fixed(strength, 256); + if (unit->Strength > unit->Class->MaxStrength-3) unit->Strength = unit->Class->MaxStrength; + if (Session.Type == GAME_NORMAL || unit->House->IsHuman) { + unit->Assign_Mission(mission); + unit->Commence(); + } else { + unit->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the unit could not be unlimboed, then this is a catastrophic error + ** condition. Delete the unit. + */ + delete unit; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * UnitClass::Write_INI -- Store the units to the INI database. * + * * + * This routine will store all the unit data to the INI database. * + * * + * INPUT: ini -- Reference to the INI database object to store to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing unit data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the unit data out. + */ + for (int index = 0; index < Units.Count(); index++) { + UnitClass * unit = Units.Ptr(index); + if (unit != NULL && !unit->IsInLimbo && unit->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + unit->House->Class->IniName, + unit->Class->IniName, + unit->Health_Ratio()*256, + Coord_Cell(unit->Coord), + unit->PrimaryFacing.Current(), + MissionClass::Mission_Name(unit->Mission), + unit->Trigger.Is_Valid() ? unit->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * UnitClass::Credit_Load -- Fetch the full credit value of cargo carried. * + * * + * This will determine the value of the cargo carried (limited to considering only gold * + * and gems) and return that value. Use this to determine how 'valuable' a harvester is. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the credit value of the cargo load of this unit (harvester). * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Credit_Load(void) const +{ + return((Gold * Rule.GoldValue) + (Gems * Rule.GemValue)); +} + + +/*********************************************************************************************** + * UnitClass::Should_Crush_It -- Determines if this unit should crush an object. * + * * + * Call this routine to determine if this unit should crush the object specified. The * + * test for crushable action depends on proximity and ability of the unit. If a unit * + * should crush the object, then it should be given a movement order to enter the cell * + * where the object is located. * + * * + * INPUT: it -- The object to see if it should be crushed. * + * * + * OUTPUT: bool; Should "it" be crushed by this unit? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Should_Crush_It(TechnoClass const * it) const +{ + assert(IsActive); + + /* + ** If this unit cannot crush anything or the candidate object cannot be crushed, + ** then it obviously should not try to crush it -- return negative answer. + */ + if (!Class->IsCrusher || it == NULL || !it->Techno_Type_Class()->IsCrushable) return(false); + + /* + ** Objects that are far away should really be fired upon rather than crushed. + */ + if (Distance(it) > Rule.CrushDistance) return(false); + + /* + ** Human controlled units don't automatically crush. Neither do computer controlled ones + ** if they are at difficult setting. + */ + if (House->IsHuman || House->Difficulty == DIFF_HARD) return(false); + + /* + ** If the weapon this unit is equipped with is very good against crushable objects then + ** fire the weapon instead. It is presumed that a wood destroying weapon is good against + ** most crushable object types (infantry). + */ + if (Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->WarheadPtr->IsWoodDestroyer) return(false); + + /* + ** If the house IQ indicates that crushing should not be allowed, then don't + ** suggest that crushing be done. + */ + if (House->IQ < Rule.IQCrush) return(false); + + /* + ** Don't allow crushing of spies by computer-controlled vehicles. + */ + if (it->What_Am_I() == RTTI_INFANTRY && *(InfantryClass *)it == INFANTRY_SPY) { + return(false); + } + + return(true); +} + + +/*********************************************************************************************** + * UnitClass::Scatter -- Causes the unit to scatter to a nearby location. * + * * + * This scatter logic will actually look for a nearby location rather than an adjacent * + * free location. This is necessary because sometimes a unit is required to scatter more * + * than one cell. A vehicle on a service depot is a prime example. * + * * + * INPUT: threat -- The coordinate that a potential threat resides. If this is a non * + * threat related scatter, then this parameter will be zero. * + * * + * forced -- Should the scatter be performed even if it would be otherwise * + * inconvenient? * + * * + * nokidding-- Should the scatter be performed even if it would otherwise be * + * illegal? * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/02/1996 JLB : Created. * + *=============================================================================================*/ +void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding) +{ + assert(IsActive); + + if (Mission == MISSION_SLEEP || Mission == MISSION_STICKY || Mission == MISSION_UNLOAD) return; + + /* + ** Certain missions prevent scattering regardless of whether it would be + ** a good idea or not. + */ + if (!MissionControl[Mission].IsScatter && !forced) return; + + if (PrimaryFacing.Is_Rotating()) return; +// if (IsRotating) return; + + if (Target_Legal(NavCom) && !nokidding) return; + + if (threat == 0) { + Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord), Class->Speed))); + } else { + DriveClass::Scatter(threat, forced, nokidding); + } +} + + +/*********************************************************************************************** + * UnitClass::Limbo -- Limbo this unit. * + * * + * This will cause the unit to go into a limbo state. If it was carrying a flag, then * + * the flag will be dropped where the unit is at. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was this unit limboed? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 10/08/1996 JLB : Created. * + *=============================================================================================*/ +bool UnitClass::Limbo(void) +{ + if (DriveClass::Limbo()) { + if (Flagged != HOUSE_NONE) { + HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord)); + Flagged = HOUSE_NONE; + } + return(true); + } + return(false); +} + +/* +** Updated for client/server multiplayer - ST 8/12/2019 11:46AM +*/ +void UnitClass::Shroud_Regen(void) +{ + if (Class->IsGapper/*KO && !House->IsPlayerControl*/) { + static int _xtab[]={ + -1, 0, 1, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -2,-1, 0, 1, 2, + -1, 0, 1 + }; + static int _ytab[]={ + -3,-3,-3, + -2,-2,-2,-2,-2, + -1,-1,-1,-1,-1, + 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, + 3, 3, 3 + }; + int index; + int centerx, centery; + CELL trycell; + + // Only restore under the shroud if it's a valid field. + if (ShroudBits != (unsigned)-1L) { + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 30; index >= 0 && ShroudBits; index--) { + if (ShroudBits & 1) { + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); +#if(0) + Map.Map_Cell(trycell, PlayerPtr); +#else + Map.UnJam_Cell(trycell, House); + Map.Map_Cell(trycell, House); +#endif + } + ShroudBits >>= 1; + } + } + + if(IsActive && Strength) { + // Now shroud around the new center + ShroudBits = 0L; + ShroudCenter = Coord_Cell(Center_Coord()); + centerx = Cell_X(ShroudCenter); + centery = Cell_Y(ShroudCenter); + for (index = 0; index < 31; index++) { + ShroudBits <<= 1; + trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]); + if (Map[trycell].Is_Mapped(House)) { + Map.Jam_Cell(trycell, House); + ShroudBits |= 1; + } + } + } + + /* + ** Updated for client/server multiplayer. ST - 8/12/2019 3:25PM + */ + if (Session.Type != GAME_GLYPHX_MULTIPLAYER) { + if (House->IsPlayerControl) { + Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, PlayerPtr); + } + + } else { + + for (int i = 0; i < Session.Players.Count(); i++) { + HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID); + if (player_house->IsHuman && player_house != House) { + Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house); + } + } + } + + } +} + + +/*********************************************************************************************** + * UnitClass::Mission_Guard_Area -- Guard area logic for units. * + * * + * This logic is similar to normal guard area except that APCs owned by the computer will * + * try to load up with nearby infantry. This will give the computer some fake intelligence * + * when playing in skirmish mode. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay to use before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 11/03/1996 JLB : Created. * + *=============================================================================================*/ +int UnitClass::Mission_Guard_Area(void) +{ + assert(IsActive); + + /* + ** Check to see if this is an APC that is largely empty and not otherwise doing anything. + ** Such an APC should load up with infantry. + */ + if (Session.Type != GAME_NORMAL && +#ifdef FIXIT_PHASETRANSPORT // checked - ajw 9/28/98 + (*this == UNIT_APC || *this == UNIT_PHASE ) && +#else + *this == UNIT_APC && +#endif + !Target_Legal(TarCom) && + !In_Radio_Contact() && + House->Which_Zone(this) != ZONE_NONE && + !House->IsHuman) { + + + int needed = Class->Max_Passengers() - How_Many(); + for (int index = 0; index < Infantry.Count(); index++) { + if (needed == 0) break; + + InfantryClass * infantry = Infantry.Ptr(index); + + if (infantry != NULL && + infantry->IsActive && + !infantry->IsInLimbo && + infantry->Strength > 0 && + infantry->House == House && + !Target_Legal(infantry->TarCom) && + !Target_Legal(infantry->NavCom) && + Distance(infantry) < 7 * CELL_LEPTON_W && + (infantry->Mission == MISSION_GUARD || infantry->Mission == MISSION_GUARD_AREA)) { + + infantry->Assign_Mission(MISSION_ENTER); + infantry->ArchiveTarget = As_Target(); + needed--; + } + } + } + return(DriveClass::Mission_Guard_Area()); +} \ No newline at end of file diff --git a/code/redalert/unit.h b/code/redalert/unit.h new file mode 100644 index 0000000..b9b1cb5 --- /dev/null +++ b/code/redalert/unit.h @@ -0,0 +1,250 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/UNIT.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UNIT.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 14, 1994 * + * * + * Last Update : April 14, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef UNIT_H +#define UNIT_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + + +/**************************************************************************** +** For each instance of a unit (vehicle) in the game, there is one of +** these structures. This structure holds information that is specific +** and dynamic for a particular unit. +*/ +class UnitClass : public DriveClass +{ + public: + + /* + ** This points to the static control data that gives 'this' unit its characteristics. + */ + CCPtr Class; + + /* + ** This records the house flag that this object is currently carrying. + */ + HousesType Flagged; + + /* + ** This flag is used for when the harvester dumps ore, to track its + ** special animation. + */ + unsigned IsDumping:1; + + /* + ** This is a count of the # of loads of the various minerals that the + ** unit has harvested. + */ + unsigned Gold:5; + unsigned Gems:5; + + /* + ** This flag tells a unit that, if after reaching its destination, it + ** should scatter away. It's meant to help a LST unload its units by + ** having its previous passengers get out of the way. + */ + unsigned IsToScatter:1; + + /* + ** This records the number of "loads" of Tiberium the unit is carrying. Only + ** harvesters use this field. + */ + int Tiberium; + + /* + ** This is the area where a mobile gap generator stores the previously-held + ** shroud values for the cells surrounding itself. + */ + unsigned long ShroudBits; + + /* + ** This is the center coordinate for the mobile gap generator, as to + ** what cells should be revealed (according to ShroudBits) + */ + CELL ShroudCenter; + + /* + ** This is the timer that controls the reload rate. The MSAM rocket + ** launcher is the primary user of this. + */ + CDTimerClass Reload; + + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + + /*--------------------------------------------------------------------- + ** Constructors, Destructors, and overloaded operators. + */ + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + UnitClass(UnitType classid, HousesType house); + UnitClass(NoInitClass const & x) : DriveClass(x), Class(x), Reload(x), SecondaryFacing(x) {}; + operator UnitType(void) const {return Class->Type;}; + virtual ~UnitClass(void); + + /*--------------------------------------------------------------------- + ** Member function prototypes. + */ + virtual ObjectTypeClass const & Class_Of(void) const; + static void Init(void); + + bool Goto_Clear_Spot(void); + bool Try_To_Deploy(bool isScriptUnpack); + virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false); + + bool Tiberium_Check(CELL ¢er, int x, int y); + bool Flag_Attach(HousesType house); + bool Flag_Remove(void); + bool Goto_Tiberium(int radius); + bool Harvesting(void); + void APC_Close_Door(void); + void APC_Open_Door(void); + + /* + ** Query functions. + */ + bool Should_Crush_It(TechnoClass const * it) const; + int Credit_Load(void) const; + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + int Shape_Number(void) const; + virtual int Pip_Count(void) const; + virtual InfantryType Crew_Type(void) const; + virtual DirType Fire_Direction(void) const; + virtual bool Ok_To_Move(DirType facing) const; + virtual FireErrorType Can_Fire(TARGET target, int which) const; + virtual fixed Tiberium_Load(void) const; + + /* + ** Coordinate inquiry functions. These are used for both display and + ** combat purposes. + */ + virtual COORDINATE Sort_Y(void) const; + + /* + ** Object entry and exit from the game system. + */ + virtual bool Limbo(void); + virtual bool Unlimbo(COORDINATE , DirType facing=DIR_N); + + /* + ** Display and rendering support functionality. Supports imagery and how + ** object interacts with the map and thus indirectly controls rendering. + */ + virtual short const * Overlap_List(bool redraw=false) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + + /* + ** User I/O. + */ + virtual ActionType What_Action(CELL cell) const; + virtual ActionType What_Action(ObjectClass const * object) const; + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual void Active_Click_With(ActionType action, CELL cell); + + /* + ** Combat related. + */ + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual BulletClass * Fire_At(TARGET target, int which=0); + + /* + ** Driver control support functions. These are used to control cell + ** occupation flags and driver instructions. + */ + virtual bool Start_Driver(COORDINATE & coord); + + /* + ** AI. + */ + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual int Mission_Guard_Area(void); + virtual int Mission_Unload(void); + virtual int Mission_Guard(void); + virtual int Mission_Harvest(void); + virtual int Mission_Hunt(void); + virtual int Mission_Repair(void); + virtual int Mission_Move(void); + void Rotation_AI(void); + void Firing_AI(void); + void Reload_AI(void); + bool Edge_Of_World_AI(void); + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + /* + ** Movement and animation. + */ + virtual void Assign_Destination(TARGET target); + virtual void Overrun_Square(CELL cell, bool threaten=true); + virtual void Approach_Target(void); + virtual int Offload_Tiberium_Bail(void); + virtual void Enter_Idle_Mode(bool initial=false); + virtual MoveType Can_Enter_Cell(CELL cell, FacingType facing=FACING_NONE) const; + virtual void Per_Cell_Process(PCPType why); + void Exit_Repair(void); + void Shroud_Regen(void); + + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char *INI_Name(void) {return "UNITS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; +}; + +#endif diff --git a/code/redalert/unitxml.cpp b/code/redalert/unitxml.cpp new file mode 100644 index 0000000..56dfeb4 --- /dev/null +++ b/code/redalert/unitxml.cpp @@ -0,0 +1,99 @@ +// UNITXML.CPP +// + + +#include "FUNCTION.H" +#include "tinyxml2.h" + +#include +#include + +int64_t generateHashValue(const char* fname, const int size); + +struct UnitsTileRule_t { + std::string name; + int64_t hash; + int shape; + std::vector frames; +}; + +struct UnitsXMLInfo_t { + std::vector tiles; +}; + +UnitsXMLInfo_t unitsXmlRules; + +const char* Units_FindHDTexture(const char* shapeFileName, int shapeNum, int frameNum) { + char tmpFileName[2048]; + strcpy(tmpFileName, shapeFileName); + COM_SetExtension(tmpFileName, strlen(tmpFileName), ""); + int64_t hash = generateHashValue(tmpFileName, strlen(tmpFileName)); + for (int i = 0; i < unitsXmlRules.tiles.size(); i++) { + if (unitsXmlRules.tiles[i].hash == hash && unitsXmlRules.tiles[i].shape == shapeNum) { + static char hdTexturePath[2048]; + if (unitsXmlRules.tiles[i].frames.size() == 0) + return NULL; + + sprintf(hdTexturePath, "DATA/ART/TEXTURES/SRGB/RED_ALERT/UNITS/%s", unitsXmlRules.tiles[i].frames[frameNum].c_str()); + return &hdTexturePath[0]; + } + } + return NULL; +} + +int Units_GetNumFramesForTile(const char* shapeFileName, int shapeNum) { + char tmpFileName[2048]; + strcpy(tmpFileName, shapeFileName); + COM_SetExtension(tmpFileName, strlen(tmpFileName), ""); + int64_t hash = generateHashValue(tmpFileName, strlen(tmpFileName)); + for (int i = 0; i < unitsXmlRules.tiles.size(); i++) { + if (unitsXmlRules.tiles[i].hash == hash && unitsXmlRules.tiles[i].shape == shapeNum) { + return unitsXmlRules.tiles[i].frames.size(); + } + } + return 0; +} + +void Units_ParseTile(tinyxml2::XMLNode* tile, UnitsTileRule_t& tileRule) { + tinyxml2::XMLNode* KeyNode = tile->FirstChildElement("Key"); + tinyxml2::XMLNode* NameNode = KeyNode->FirstChildElement("Name"); + tinyxml2::XMLNode* ShapeNode = KeyNode->FirstChildElement("Shape"); + tinyxml2::XMLNode* ValueNode = tile->FirstChildElement("Value"); + tinyxml2::XMLNode* FramesNode = ValueNode->FirstChildElement("Frames"); + tinyxml2::XMLNode* FrameNode = FramesNode->FirstChildElement("Frame"); + + tileRule.name = NameNode->FirstChild()->ToText()->Value(); + tileRule.hash = generateHashValue(tileRule.name.c_str(), tileRule.name.size()); + tileRule.shape = atoi(ShapeNode->FirstChild()->ToText()->Value()); + + while (FrameNode != NULL) { + if (FrameNode->FirstChild() != NULL) { + tileRule.frames.push_back(FrameNode->FirstChild()->ToText()->Value()); + } + FrameNode = FrameNode->NextSiblingElement("Frame"); + } +} + +void Units_LoadRuleXML(const char* path, UnitsXMLInfo_t& info) { + tinyxml2::XMLDocument doc; + doc.LoadFile(path); + + tinyxml2::XMLElement* root = doc.FirstChildElement(); + if (root == NULL) + return; + + tinyxml2::XMLNode* tilesetTypeClassNode = root->FirstChild(); + + tinyxml2::XMLNode* tilesParent = tilesetTypeClassNode->FirstChildElement("Tiles"); + tinyxml2::XMLNode* tile = tilesParent->FirstChildElement("Tile"); + while (tile != NULL) { + UnitsTileRule_t tileRule; + Units_ParseTile(tile, tileRule); + info.tiles.push_back(tileRule); + tile = tile->NextSiblingElement("Tile"); + } +} + +void Units_LoadRules(void) { + Units_LoadRuleXML("data/xml/tilesets/RA_UNITS.XML", unitsXmlRules); +} \ No newline at end of file diff --git a/code/redalert/utracker.cpp b/code/redalert/utracker.cpp new file mode 100644 index 0000000..f2feb58 --- /dev/null +++ b/code/redalert/utracker.cpp @@ -0,0 +1,247 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + * * + * Functions: * + * * + * * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#include "function.h" + +/* +** Define host to network to host functions for DOS +*/ +#ifndef WIN32 + +#define htonl(val) 0 +#define ntohl(val) 0 + +#endif //WIN32 + + +/*********************************************************************************************** + * UTC::UnitTrackerClass -- Class constructor * + * * + * * + * * + * INPUT: Number of unit types to reserve space for * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::UnitTrackerClass (int unit_count) +{ + UnitTotals = new long [unit_count]; // Allocate memory for the unit totals + UnitCount = unit_count; // Keep a record of how many unit entries there are + InNetworkFormat = 0; // The unit entries are in host format + Clear_Unit_Total(); // Clear each entry +} + + +/*********************************************************************************************** + * UTC::~UnitTrackerClass -- Class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:10AM ST : Created * + *=============================================================================================*/ +UnitTrackerClass::~UnitTrackerClass (void) +{ + delete UnitTotals; +} + + + +/*********************************************************************************************** + * UTC::Increment_Unit_Total -- Increment the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:12AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Increment_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]++; +} + + +/*********************************************************************************************** + * UTC::Decrement_Unit_Total -- Decrement the total for the specefied unit * + * * + * * + * * + * INPUT: Unit number * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Decrement_Unit_Total(int unit_type) +{ + UnitTotals[unit_type]--; +} + + +/*********************************************************************************************** + * UTC::Get_All_Totals -- Returns a pointer to the start of the unit totals list * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Ptr to unit totals list * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:13AM ST : Created * + *=============================================================================================*/ +long *UnitTrackerClass::Get_All_Totals (void) +{ + return (UnitTotals); +} + + +/*********************************************************************************************** + * UTC::Clear_Unit_Total -- Clear out all the unit totals * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:14AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::Clear_Unit_Total (void) +{ + memset (UnitTotals, 0, UnitCount * sizeof(long) ); +} + + + +/*********************************************************************************************** + * UTC::To_Network_Format -- Changes all unit totals to network format for the internet * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/7/96 0:15AM ST : Created * + *=============================================================================================*/ +void UnitTrackerClass::To_Network_Format (void) +{ + if (!InNetworkFormat){ + for (int i=0 ; i= 0 && unit_type < UnitCount) + { + return UnitTotals[unit_type]; + } + return 0; +} + diff --git a/code/redalert/utracker.h b/code/redalert/utracker.h new file mode 100644 index 0000000..35c216b --- /dev/null +++ b/code/redalert/utracker.h @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : UTRACKER.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : June 3rd, 1996 * + * * + * Last Update : June 7th, 1996 [ST] * + * * + *-------------------------------------------------------------------------* + * The UnitTracker class exists to track the various statistics * + * required for internet games. * + * * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +/* +** UnitTracker Class +*/ + +class UnitTrackerClass { + + public: + + UnitTrackerClass(int unit_count); + ~UnitTrackerClass(void); + + void Increment_Unit_Total (int unit_type); + void Decrement_Unit_Total (int unit_type); + void Clear_Unit_Total(void); + + int Get_Unit_Total (int unit_type); + long *Get_All_Totals (void); + int Get_Unit_Count (void){return (UnitCount);}; + + void To_Network_Format(void); + void To_PC_Format(void); + + private: + + long *UnitTotals; + int UnitCount; + int InNetworkFormat; + +}; + + + + + + + + + + + + + + + + diff --git a/code/redalert/vdata.cpp b/code/redalert/vdata.cpp new file mode 100644 index 0000000..aad694a --- /dev/null +++ b/code/redalert/vdata.cpp @@ -0,0 +1,708 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VDATA.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VDATA.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * VesselTypeClass::VesselTypeClass -- Constructor for naval vessel types. * + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel.* + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory poo* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" +#include "Image.h" + +// Submarine +static VesselTypeClass const VesselSubmarine( + VESSEL_SS, + TXT_SS, // NAME: Text name of this unit type. + "SS", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Destroyer +static VesselTypeClass const VesselDestroyer( + VESSEL_DD, + TXT_DD, // NAME: Text name of this unit type. + "DD", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Cruiser +static VesselTypeClass const VesselCruiser( + VESSEL_CA, + TXT_CA, // NAME: Text name of this unit type. + "CA", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + +// Transport +static VesselTypeClass const VesselTransport( + VESSEL_TRANSPORT, + TXT_TRANSPORT, // NAME: Text name of this unit type. + "LST", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); + +// Gun Boat +static VesselTypeClass const VesselPTBoat( + VESSEL_PT, + TXT_PT, // NAME: Text name of this unit type. + "PT", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + true, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); + + +#ifdef FIXIT_CSII // checked - ajw 9/28/98 +// Missile Submarine +static VesselTypeClass const VesselMissileSubmarine( + VESSEL_MISSILESUB, + TXT_MISSILESUB, // NAME: Text name of this unit type. + "MSUB", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 8, // Rotation stages. + 14 // Turret center offset along body centerline. +); +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Transport +static VesselTypeClass const VesselCarrier( + VESSEL_CARRIER, + TXT_CARRIER, // NAME: Text name of this unit type. + "CARR", // NAME: Text name of this unit type. + ANIM_FBALL1, // EXPLOSION: Type of explosion when destroyed. + 0x0000, // Vertical offset. + 0x0000, // Primary weapon offset along turret centerline. + 0x0000, // Primary weapon lateral offset along turret centerline. + 0x0000, // Secondary weapon offset along turret centerline. + 0x0000, // Secondary weapon lateral offset along turret centerling. + false, // Only has eight facings? + true, // Always use the given name for the vehicle? + false, // Is it equipped with a combat turret? + 0, // Rotation stages. + 0 // Turret center offset along body centerline. +); +#endif + +/*********************************************************************************************** + * VesselTypeClass::VesselTypeClass -- Constructor for unit types. * + * * + * This is the constructor for the vessel static data. Each vessels is assign a specific * + * variation. This class elaborates what the variation actually is. * + * * + * INPUT: bla bla bla... see below * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created * + *=============================================================================================*/ +VesselTypeClass::VesselTypeClass( + VesselType type, + int name, + char const * ininame, + AnimType exp, + int verticaloffset, + int primaryoffset, + int primarylateral, + int secondaryoffset, + int secondarylateral, + bool is_eight, + bool is_nominal, + bool is_turret_equipped, + int rotation, + int toffset + ) : + TechnoTypeClass( + RTTI_VESSELTYPE, + int(type), + name, + ininame, + REMAP_NORMAL, + verticaloffset, + primaryoffset, + primarylateral, + secondaryoffset, + secondarylateral, + is_nominal, + false, + true, + true, + false, + false, + false, + is_turret_equipped, + true, + true, + rotation, + SPEED_FLOAT + ), + IsPieceOfEight(is_eight), + Type(type), + TurretOffset(toffset), + Mission(MISSION_GUARD), + Explosion(exp), + MaxSize(0) +{ + /* + ** Forced vessel overrides from the default. + */ + IsCrew = false; + Speed = SPEED_FLOAT; + IsScanner = true; +} + + +/*********************************************************************************************** + * VesselTypeClass::operator new -- Allocate a vessel type object from the special memory pool * + * * + * This will allocate a vessel type class object from the memory pool. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the allocated vessel type class object. If memory in the * + * special heap has been exhaused, then NULL will be returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselTypeClass::operator new(size_t) +{ + return(VesselTypes.Alloc()); +} + + +/*********************************************************************************************** + * VesselTypeClass::operator delete -- Returns a vessel type object back to the memory pool. * + * * + * This will return a previously allocated vessel object back to the special pool from * + * whence it was originally allocated. * + * * + * INPUT: pointer -- Pointer to the vessel type object to return to the pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::operator delete(void * pointer) +{ + VesselTypes.Free((VesselTypeClass *)pointer); +} + + +/*********************************************************************************************** + * VesselTypeClass::Init_Heap -- Initialize the vessel heap. * + * * + * This will pre-allocate all the vessel types required. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once and do so before processing the rules.ini file. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Init_Heap(void) +{ + /* + ** These vessel type class objects must be allocated in the exact order that they + ** are specified in the VesselType enumeration. This is necessary because the heap + ** allocation block index serves double duty as the type number index. + */ + new VesselTypeClass(VesselSubmarine); // VESSEL_SS + new VesselTypeClass(VesselDestroyer); // VESSEL_DD + new VesselTypeClass(VesselCruiser); // VESSEL_CA + new VesselTypeClass(VesselTransport); // VESSEL_TRANSPORT + new VesselTypeClass(VesselPTBoat); // VESSEL_PT +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + new VesselTypeClass(VesselMissileSubmarine); // VESSEL_MISSILESUB +#endif +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + new VesselTypeClass(VesselCarrier); // VESSEL_CARRIER +#endif +} + + +/*********************************************************************************************** + * VesselTypeClass::As_Reference -- Converts a vessel type into a VesselTypeClass reference. * + * * + * This routine will fetch a reference to the vessel type that corresponds to the vessel * + * type specified. * + * * + * INPUT: type -- The vessel type number to convert. * + * * + * OUTPUT: Returns with a reference to the vessel type class that corresponds to the vessel * + * type specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselTypeClass & VesselTypeClass::As_Reference(VesselType type) +{ + return(*VesselTypes.Ptr(type)); +} + + +#ifdef NEVER +/*********************************************************************************************** + * VesselTypeClass::Who_Can_Build_Me -- Fetches pointer to available factory for this vessel. * + * * + * Use this routine to fetch a pointer to the vessel factory that can build this vessel * + * type. * + * * + * INPUT: intheory -- If true, then this indicates that if the factory is currently * + * busy doing other things, this won't make in ineligible for searching. * + * Typical use of this is by the sidebar logic which needs only to know * + * if theoretical production is allowed. * + * * + * legal -- If true, then the buildings are checked for specific legality when * + * being scanned. For building placement, this is usually false, for * + * sidebar button adding, this is usually true. * + * * + * house -- The owner of the unit to be produced. This has an effect of legality. * + * * + * OUTPUT: Returns with a pointer to the factory (building) that can produce this vessel type.* + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +BuildingClass * VesselTypeClass::Who_Can_Build_Me(bool intheory, bool legal, HousesType house) const +{ + BuildingClass * anybuilding = NULL; + + for (int index = 0; index < Buildings.Count(); index++) { + BuildingClass * building = Buildings.Ptr(index); + assert(building != NULL); + + if ( !building->IsInLimbo && + building->House->Class->House == house && + building->Class->ToBuild == RTTI_VESSELTYPE && + building->Mission != MISSION_DECONSTRUCTION && + ((1L << building->ActLike) & Ownable) && + (!legal || building->House->Can_Build(Type, building->ActLike)) && + (intheory || !building->In_Radio_Contact())) { + + if (building->IsLeader) return(building); + anybuilding = building; + } + } + return(anybuilding); +} +#endif + + +/*********************************************************************************************** + * VesselTypeClass::Display -- Displays a generic representation of this vessel type. * + * * + * This routine is used by the scenario editor to display a representation of this * + * vessel type in the object placement dialog. * + * * + * INPUT: x,y -- Pixel coordinate to render the center of this vessel type to. * + * * + * window -- The window to clip the shape to. The pixel coordinates are relative * + * to this window. * + * * + * house -- The owner of the vessel. This is used to give the vessel its color. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +#ifdef SCENARIO_EDITOR +void VesselTypeClass::Display(int x, int y, WindowNumberType window, HousesType ) const +{ + int shape = 0; + void const * ptr = Get_Cameo_Data(); + if (ptr == NULL) { + ptr = Get_Image_Data(); + shape = Rotation/6; + } + CC_Draw_Shape(ptr, shape, x, y, window, SHAPE_CENTER|SHAPE_WIN_REL); +} + + +/*********************************************************************************************** + * VesselTypeClass::Prep_For_Add -- Adds vessel types to the scenario editor object list. * + * * + * This routine is called when the scenario editor needs to obtain a list of the * + * vessel object that can be placed down. It will submit all the vessel types that can * + * be placed down. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Prep_For_Add(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + if (As_Reference(index).Get_Image_Data() != NULL) { + Map.Add_To_List(&As_Reference(index)); + } + } +} +#endif //SCENARIO_EDITOR + + +/*********************************************************************************************** + * VesselTypeClass::Create_One_Of -- Creates a vessel object that matches this vessel type. * + * * + * This routine is called when the type of vessel is known (by way of a VesselTypeClass) * + * and a corresponding vessel object needs to be created. * + * * + * INPUT: house -- Pointer to the owner that this vessel will be assigned to. * + * * + * OUTPUT: Returns with a pointer to the vessel object created. If no vessel could be * + * created, then NULL is returned. * + * * + * WARNINGS: The vessel is created in a limbo state. It must first be placed down upon * + * the map before it starts to function. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +ObjectClass * VesselTypeClass::Create_One_Of(HouseClass * house) const +{ + return(new VesselClass(Type, house->Class->House)); +} + + +/*********************************************************************************************** + * VesselTypeClass::Create_And_Place -- Creates a vessel and places it at location. * + * * + * This routine is used to create a vessel and then place it down upon the * + * map. * + * * + * INPUT: cell -- The location to place this vessel down upon. * + * * + * house -- The house to assign this vessel's ownership to. * + * * + * OUTPUT: bool; Was the vessel successfully created and placed down upon the map? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselTypeClass::Create_And_Place(CELL cell, HousesType house) const +{ + VesselClass * unit = new VesselClass(Type, house); + if (unit != NULL) { + return(unit->Unlimbo(Cell_Coord(cell), Random_Pick(DIR_N, DIR_MAX))); + } + delete unit; + return(false); +} + + +/*********************************************************************************************** + * VesselTypeClass::Dimensions -- Fetches the pixel width and height of this vessel type. * + * * + * This routine is used to fetch the width and height of this vessel type. These dimensions * + * are not specific to any particular facing. Rather, they are only for the generic vessel * + * size. * + * * + * INPUT: width, height -- Reference to the integers that are to be initialized with the * + * pixel width and height of this vessel type. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Dimensions(int & width, int & height) const +{ + width = 48; + height = 48; +} + + +/*********************************************************************************************** + * VesselTypeClass::One_Time -- Performs one time initialization for vessel types. * + * * + * This routine will load in the vessel shape data. It should be called only once at the * + * beginning of the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this once. * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::One_Time(void) +{ + for (VesselType index = VESSEL_FIRST; index < VESSEL_COUNT; index++) { + char fullname[_MAX_FNAME+_MAX_EXT]; + char buffer[_MAX_FNAME]; + VesselTypeClass const & uclass = As_Reference(index); +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (uclass.Level != -1 || index==VESSEL_CARRIER) { +#else + if (uclass.Level != -1) { +#endif +// if (uclass.IsBuildable) { + + /* + ** Fetch the supporting data files for the unit. + */ + sprintf(buffer, "%sICON", uclass.Graphic_Name()); + _makepath(fullname, NULL, NULL, buffer, ".SHP"); + ((void const *&)uclass.CameoData) = MFCD::Retrieve(fullname); + } + + /* + ** Fetch a pointer to the unit's shape data. + */ + _makepath(fullname, NULL, NULL, uclass.Graphic_Name(), ".SHP"); + ((void const *&)uclass.ImageData) = MFCD::Retrieve(fullname); + ((int &)uclass.MaxSize) = 26; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Turret_Adjust -- Adjust turret offset according to facing specified. * + * * + * This routine will determine the pixel adjustment necessary for a turret. The direction * + * specified is what the vessel body is facing. * + * * + * INPUT: dir -- The presumed direction of the body facing for the vessel. * + * * + * x,y -- The center pixel position for the vessel. These values should be * + * adjusted (they are references) to match the adjusted offset for the * + * turret. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselTypeClass::Turret_Adjust(DirType dir, int & x, int & y) const +{ + short xx = x; + short yy = y; + + switch (Type) { + case VESSEL_CA: + Normal_Move_Point(xx, yy, dir, 22); + x = xx; + y = yy-4; + break; + + case VESSEL_PT: + Normal_Move_Point(xx, yy, dir, 14); + x = xx; + y = yy+1; + break; + + case VESSEL_DD: + Normal_Move_Point(xx, yy, dir+DIR_S, 8); + x = xx; + y = yy-4; + break; + } +} + + +/*********************************************************************************************** + * VesselTypeClass::Overlap_List -- Figures the overlap list for the vessel type. * + * * + * This routine will return the overlap list for a vessel that is sitting still in the * + * center of a cell. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list that this vessel would use. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselTypeClass::Overlap_List(void) const +{ + static short const _ship[] = {-3, -2, -1, 1, 2, 3, + -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), -(MAP_CELL_W+2), -(MAP_CELL_W-2), + +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +(MAP_CELL_W+2), +(MAP_CELL_W-2), + REFRESH_EOL}; +// static short const _ship[] = {-1, 1, +// -MAP_CELL_W, -(MAP_CELL_W+1), -(MAP_CELL_W-1), +// +MAP_CELL_W, +(MAP_CELL_W+1), +(MAP_CELL_W-1), +// REFRESH_EOL}; + + return(&_ship[0]); +} + + +/*********************************************************************************************** + * VesselTypeClass::From_Name -- Converts a name into a vessel type. * + * * + * Use this routine to convert an ASCII version of a vessel type into the corresponding * + * VesselType id value. Typical use of this would be to parse the INI file. * + * * + * INPUT: name -- Pointer to the ASCII name to be converted into a vessel type. * + * * + * OUTPUT: Returns with the vessel type number that matches the string specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +VesselType VesselTypeClass::From_Name(char const * name) +{ + if (name != NULL) { + for (VesselType classid = VESSEL_FIRST; classid < VESSEL_COUNT; classid++) { + if (stricmp(As_Reference(classid).IniName, name) == 0) { + return(classid); + } + } + } + return(VESSEL_NONE); +} + + +/*********************************************************************************************** + * VesselTypeClass::Max_Pips -- Fetches the maximum pips allowed for this vessel. * + * * + * This routine will determine the number of pips (maximum) allowed for this unit type. * + * Typically, this is the number of passengers allowed. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the maximum number of pips allowed for this vessel type. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselTypeClass::Max_Pips(void) const +{ + return(Max_Passengers()); +} diff --git a/code/redalert/vector.cpp b/code/redalert/vector.cpp new file mode 100644 index 0000000..8777434 --- /dev/null +++ b/code/redalert/vector.cpp @@ -0,0 +1,672 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VECTOR.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : September 21, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BooleanVectorClass::BooleanVectorClass -- Copy constructor for boolean array. * + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * BooleanVectorClass::operator = -- Assignment operator. * + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::ID -- Finds object ID based on value. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::Resize -- Changes the size of the vector. * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#ifdef WINSOCK_IPX +#include "WSProto.h" +#include "WSPUDP.h" +#endif //WINSOCK_IPX +#include "vector.h" +//#include +#include + +/* +** The following template function can be located here ONLY if all the instantiations are +** declared in a header file this module includes. By placing the template functions here, +** it speeds up compiler operation and reduces object module size. +*/ +#if (0) +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) : + Vector(0), + VectorMax(size), + IsAllocated(false) +{ + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) : + Vector(0), + VectorMax(0), + IsAllocated(false) +{ + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + if (this != &vector) { + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } else { + Vector = 0; + IsAllocated = false; + } + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasible) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assignment operator will + ** only work for the simplest of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} +#endif +//---------------------------------------------------------------------------------------------- + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Explicit data buffer constructor. * + * * + * This is the constructor for a boolean array. This constructor takes the memory pointer * + * provided as assigns that as the array data pointer. * + * * + * INPUT: size -- The size of the array (in bits). * + * * + * array -- Pointer to the memory that the array is to use. * + * * + * OUTPUT: none * + * * + * WARNINGS: You must make sure that the memory specified is large enough to contain the * + * bits specified. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(unsigned size, unsigned char * array) +{ + BitArray.Resize(((size + (8-1)) / 8), array); + LastIndex = -1; + BitCount = size; +} + + +/*********************************************************************************************** + * BooleanVectorClass::BooleanVectorClass -- Copy constructor of boolean array. * + * * + * This is the copy constructor for a boolean array. It is used to make a duplicate of the * + * boolean array. * + * * + * INPUT: vector -- Reference to the vector to be duplicated. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass::BooleanVectorClass(BooleanVectorClass const & vector) +{ + LastIndex = -1; + *this = vector; +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator = -- Assignment operator. * + * * + * This routine will make a copy of the specified boolean vector array. The vector is * + * copied into an already constructed existing vector. The values from the existing vector * + * are destroyed by this copy. * + * * + * INPUT: vector -- Reference to the vector to make a copy of. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +BooleanVectorClass & BooleanVectorClass::operator =(BooleanVectorClass const & vector) +{ + Fixup(); + Copy = vector.Copy; + LastIndex = vector.LastIndex; + BitArray = vector.BitArray; + BitCount = vector.BitCount; + return(*this); +} + + +/*********************************************************************************************** + * BooleanVectorClass::operator == -- Comparison operator for boolean vector. * + * * + * This is the comparison operator for a boolean vector class. Boolean vectors are equal * + * if the bit count and bit values are identical. * + * * + * INPUT: vector -- Reference to the vector to compare to. * + * * + * OUTPUT: Are the boolean vectors identical? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::operator == (const BooleanVectorClass & vector) +{ + Fixup(LastIndex); + return(BitCount == vector.BitCount && BitArray == vector.BitArray); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Resize -- Resizes a boolean vector object. * + * * + * This routine will resize the boolean vector object. An index value used with a boolean * + * vector must be less than the value specified in as the new size. * + * * + * INPUT: size -- The new maximum size of this boolean vector. * + * * + * OUTPUT: Was the boolean vector sized successfully? * + * * + * WARNINGS: The boolean array might be reallocated or even deleted by this routine. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +int BooleanVectorClass::Resize(unsigned size) +{ + Fixup(); + + if (size) { + + /* + ** Record the previous bit count of the boolean vector. This is used + ** to determine if the array has grown in size and thus clearing is + ** necessary. + */ + int oldsize = BitCount; + + /* + ** Actually resize the bit array. Since this is a bit packed array, + ** there are 8 elements per byte (rounded up). + */ + int success = BitArray.Resize(((size + (8-1)) / 8)); + + /* + ** Since there is no default constructor for bit packed integers, a manual + ** clearing of the bits is required. + */ + BitCount = size; + if (success && oldsize < (int)size) { + for (int index = oldsize; index < (int)size; index++) { + (*this)[index] = 0; + } + } + + return(success); + } + + /* + ** Resizing to zero is the same as clearing and deallocating the array. + ** This is always successful. + */ + Clear(); + return(true); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Clear -- Resets boolean vector to empty state. * + * * + * This routine will clear out the boolean array. This will free any allocated memory and * + * result in the boolean vector being unusable until the Resize function is subsequently * + * called. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: The boolean vector cannot be used until it is resized to a non null condition. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Clear(void) +{ + Fixup(); + BitCount = 0; + BitArray.Clear(); +} + + +/*********************************************************************************************** + * BooleanVectorClass::Reset -- Clear all boolean values in array. * + * * + * This is the preferred (and quick) method to clear the boolean array to a false condition.* + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Reset(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\0', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Set -- Forces all boolean elements to true. * + * * + * This is the preferred (and fast) way to set all boolean elements to true. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Set(void) +{ + LastIndex = -1; + if (BitArray.Length()) { + memset(&BitArray[0], '\xFF', BitArray.Length()); + } +} + + +/*********************************************************************************************** + * BooleanVectorClass::Fixup -- Updates the boolean vector to a known state. * + * * + * Use this routine to set the boolean value copy to match the appropriate bit in the * + * boolean array. The boolean array will be updated with any changes from the last time * + * a value was fetched from the boolean vector. By using this update method, the boolean * + * array can be treated as a normal array even though the elements are composed of * + * otherwise inaccessible bits. * + * * + * INPUT: index -- The index to set the new copy value to. If the index is -1, then the * + * previous value will be updated into the vector array, but no new value * + * will be fetched from it. * + * * + * OUTPUT: none * + * * + * WARNINGS: Always call this routine with "-1" if any direct manipulation of the bit * + * array is to occur. This ensures that the bit array is accurate. * + * * + * HISTORY: * + * 07/18/1995 JLB : Created. * + *=============================================================================================*/ +void BooleanVectorClass::Fixup(int index) const +{ + /* + ** If the requested index value is illegal, then force the index + ** to be -1. This is the default non-index value. + */ + if (index >= BitCount) { + index = -1; + } + + /* + ** If the new index is different than the previous index, there might + ** be some fixing up required. + */ + if (index != LastIndex) { + + /* + ** If the previously fetched boolean value was changed, then update + ** the boolean array accordingly. + */ + if (LastIndex != -1) { + Set_Bit((void*)&BitArray[0], LastIndex, Copy); + } + + /* + ** If this new current index is valid, then fill in the reference boolean + ** value with the appropriate data from the bit array. + */ + if (index != -1) { + ((unsigned char&)Copy) = Get_Bit(&BitArray[0], index); + } + + ((int &)LastIndex) = index; + } +} + + diff --git a/code/redalert/vector.h b/code/redalert/vector.h new file mode 100644 index 0000000..5b5fe77 --- /dev/null +++ b/code/redalert/vector.h @@ -0,0 +1,1000 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VECTOR.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VECTOR.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 02/19/95 * + * * + * Last Update : March 13, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VectorClass::VectorClass -- Constructor for vector class. * + * VectorClass::~VectorClass -- Default destructor for vector class. * + * VectorClass::VectorClass -- Copy constructor for vector object. * + * VectorClass::operator = -- The assignment operator. * + * VectorClass::operator == -- Equality operator for vector objects. * + * VectorClass::Clear -- Frees and clears the vector. * + * VectorClass::Resize -- Changes the size of the vector. * + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * DynamicVectorClass::Add -- Add an element to the vector. * + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * VectorClass::ID -- Pointer based conversion to index number. * + * VectorClass::ID -- Finds object ID based on value. * + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#pragma once + +#include +#include + +// ST - 5/8/1029 +//inline void * operator new(size_t , void * pointer) {return(pointer);} +//inline void * operator new[](size_t , void * pointer) {return(pointer);} + + +/************************************************************************** +** This is a general purpose vector class. A vector is defined by this +** class, as an array of arbitrary objects where the array can be dynamically +** sized. Because is deals with arbitrary object types, it can handle everything. +** As a result of this, it is not terribly efficient for integral objects (such +** as char or int). It will function correctly, but the copy constructor and +** equality operator could be highly optimized if the integral type were known. +** This efficiency can be implemented by deriving an integral vector template +** from this one in order to supply more efficient routines. +*/ +template +class VectorClass +{ + public: + VectorClass(NoInitClass const & ) {}; + VectorClass(unsigned size=0, T const * array=0); + VectorClass(VectorClass const &); // Copy constructor. + virtual ~VectorClass(void); + + T & operator[](unsigned index) {return(Vector[index]);}; + T const & operator[](unsigned index) const {return(Vector[index]);}; + virtual VectorClass & operator =(VectorClass const &); // Assignment operator. + virtual int operator == (VectorClass const &) const; // Equality operator. + virtual int Resize(unsigned newsize, T const * array=0); + virtual void Clear(void); + unsigned Length(void) const {return VectorMax;}; + virtual int ID(T const * ptr); // Pointer based identification. + virtual int ID(T const & ptr); // Value based identification. + + protected: + + /* + ** This is a pointer to the allocated vector array of elements. + */ + T * Vector; + + /* + ** This is the maximum number of elements allowed in this vector. + */ + unsigned VectorMax; + + /* + ** Does the vector data pointer refer to memory that this class has manually + ** allocated? If so, then this class is responsible for deleting it. + */ + unsigned IsAllocated:1; +}; + + +/************************************************************************** +** This derivative vector class adds the concept of adding and deleting +** objects. The objects are packed to the beginning of the vector array. +** If this is instantiated for a class object, then the assignment operator +** and the equality operator must be supported. If the vector allocates its +** own memory, then the vector can grow if it runs out of room adding items. +** The growth rate is controlled by setting the growth step rate. A growth +** step rate of zero disallows growing. +*/ +template +class DynamicVectorClass : public VectorClass +{ + public: + DynamicVectorClass(unsigned size=0, T const * array=0); + + // Change maximum size of vector. + virtual int Resize(unsigned newsize, T const * array=0); + + // Resets and frees the vector array. + virtual void Clear(void) {ActiveCount = 0;VectorClass::Clear();}; + + // Fetch number of "allocated" vector objects. + int Count(void) const {return(ActiveCount);}; + + // Add object to vector (growing as necessary). + int Add(T const & object); + int Add_Head(T const & object); + + // Delete object just like this from vector. + int Delete(T const & object); + + // Delete object at this vector index. + int Delete(int index); + + // Deletes all objects in the vector. + void Delete_All(void) {ActiveCount = 0;}; + + // Set amount that vector grows by. + int Set_Growth_Step(int step) {return(GrowthStep = step);}; + + // Fetch current growth step rate. + int Growth_Step(void) {return GrowthStep;}; + + virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; + virtual int ID(T const & ptr); + + protected: + + /* + ** This is a count of the number of active objects in this + ** vector. The memory array often times is bigger than this + ** value. + */ + int ActiveCount; + + /* + ** If there is insufficient room in the vector array for a new + ** object to be added, then the vector will grow by the number + ** of objects specified by this value. This is controlled by + ** the Set_Growth_Step() function. + */ + int GrowthStep; +}; + + +/************************************************************************** +** A fixed-size array of dynamic vectors. +*/ +template +class DynamicVectorArrayClass +{ +public: + static const int COUNT = COUNT; + + DynamicVectorArrayClass() : Active(DEFAULT) {} + + void Set_Active_Context(int active) + { + Active = active; + } + + void Clear_All() + { + for (int i = FIRST; i < COUNT; ++i) + { + Clear(i); + } + } + + void Clear() + { + Clear(Active); + } + + int Count() const + { + return Count(Active); + } + + int Add(T const & object) + { + return Add(Active, object); + } + + int Add_Head(T const & object) + { + return Add_Head(Active, object); + } + + int Delete(T const & object) + { + return Delete(Active, object); + } + + int Delete_All(T const & object) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + count += Delete(i, object); + } + return count; + } + + int Delete_All_Except(T const & object, int except) + { + int count = 0; + for (int i = FIRST; i < COUNT; ++i) + { + if (except != i) + { + count += Delete(i, object); + } + } + return count; + } + + int Delete(int index) + { + return Delete(Active, index); + } + + T & operator[](unsigned index) + { + return Collection[Active][index]; + } + + T const & operator[](unsigned index) const + { + return Collection[Active][index]; + } + + void Clear(int context) + { + Collection[context].Clear(); + } + + int Count(int context) const + { + return Collection[context].Count(); + } + + int Add(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Add_Head(int context, T const & object) + { + return Collection[context].Add(object); + } + + int Delete(int context, T const & object) + { + return Collection[context].Delete(object); + } + + int Delete(int context, int index) + { + return Collection[context].Delete(index); + } + + DynamicVectorClass & Raw() + { + return Collection[Active]; + } + + DynamicVectorClass & Raw(int context) + { + return Collection[context]; + } + +private: + DynamicVectorClass Collection[COUNT]; + int Active; +}; + + +/************************************************************************** +** This is a derivative of a vector class that supports boolean flags. Since +** a boolean flag can be represented by a single bit, this class packs the +** array of boolean flags into an array of bytes containing 8 boolean values +** each. For large boolean arrays, this results in an 87.5% savings. Although +** the indexing "[]" operator is supported, DO NOT pass pointers to sub elements +** of this bit vector class. A pointer derived from the indexing operator is +** only valid until the next call. Because of this, only simple +** direct use of the "[]" operator is allowed. +*/ +class BooleanVectorClass +{ + public: + BooleanVectorClass(unsigned size=0, unsigned char * array=0); + BooleanVectorClass(BooleanVectorClass const & vector); + + // Assignment operator. + BooleanVectorClass & operator =(BooleanVectorClass const & vector); + + // Equivalency operator. + int operator == (BooleanVectorClass const & vector); + + // Fetch number of boolean objects in vector. + int Length(void) {return BitCount;}; + + // Set all boolean values to false; + void Reset(void); + + // Set all boolean values to true. + void Set(void); + + // Resets vector to zero length (frees memory). + void Clear(void); + + // Change size of this boolean vector. + int Resize(unsigned size); + + // Fetch reference to specified index. + bool const & operator[](int index) const { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + bool & operator[](int index) { + if (LastIndex != index) Fixup(index); + return(Copy); + }; + + // Quick check on boolean state. + bool Is_True(int index) const { + if (index == LastIndex) return(Copy); + return(Get_Bit(&BitArray[0], index)); + }; + + // Find first index that is false. + int First_False(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_False_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a false boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + // Find first index that is true. + int First_True(void) const { + if (LastIndex != -1) Fixup(-1); + + int retval = First_True_Bit(&BitArray[0]); + if (retval < BitCount) return(retval); + + /* + ** Failure to find a true boolean value in the vector. Return this + ** fact in the form of an invalid index number. + */ + return(-1); + } + + private: + void Fixup(int index=-1) const; + + /* + ** This is the number of boolean values in the vector. This value is + ** not necessarily a multiple of 8, even though the underlying character + ** vector contains a multiple of 8 bits. + */ + int BitCount; + + /* + ** This is a referential copy of an element in the bit vector. The + ** purpose of this copy is to allow normal reference access to this + ** object (for speed reasons). This hides the bit packing scheme from + ** the user of this class. + */ + bool Copy; + + /* + ** This records the index of the value last fetched into the reference + ** boolean variable. This index is used to properly restore the value + ** when the reference copy needs updating. + */ + int LastIndex; + + /* + ** This points to the allocated bitfield array. + */ + VectorClass BitArray; +}; + + + + + + +/*********************************************************************************************** + * DynamicVectorClass::DynamicVectorClass -- Constructor for dynamic vector. * + * * + * This is the normal constructor for the dynamic vector class. It is similar to the normal * + * vector class constructor. The vector is initialized to contain the number of elements * + * specified in the "size" parameter. The memory is allocated from free store unless the * + * optional array parameter is provided. In this case it will place the vector at the * + * memory location specified. * + * * + * INPUT: size -- The maximum number of objects allowed in this vector. * + * * + * array -- Optional pointer to the memory area to place the vector at. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +DynamicVectorClass::DynamicVectorClass(unsigned size, T const * array) + : VectorClass(size, array) +{ + GrowthStep = 10; + ActiveCount = 0; +} + + +/*********************************************************************************************** + * DynamicVectorClass::Resize -- Changes the size of a dynamic vector. * + * * + * Use this routine to change the size of the vector. The size changed is the maximum * + * number of allocated objects within this vector. If a memory buffer is provided, then * + * the vector will be located there. Otherwise, the memory will be allocated out of free * + * store. * + * * + * INPUT: newsize -- The desired maximum size of this vector. * + * * + * array -- Optional pointer to a previously allocated memory array. * + * * + * OUTPUT: bool; Was vector successfully resized according to specifications? * + * * + * WARNINGS: Failure to resize the vector could be the result of lack of free store. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Resize(unsigned newsize, T const * array) +{ + if (VectorClass::Resize(newsize, array)) { + if (Length() < (unsigned)ActiveCount) ActiveCount = Length(); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * DynamicVectorClass::ID -- Find matching value in the dynamic vector. * + * * + * Use this routine to find a matching object (by value) in the vector. Unlike the base * + * class ID function of similar name, this one restricts the scan to the current number * + * of valid objects. * + * * + * INPUT: object -- A reference to the object that a match is to be found in the * + * vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no equivalent object could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::ID(T const & object) +{ + for (int index = 0; index < Count(); index++) { + if ((*this)[index] == object) return(index); + } + return(-1); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add -- Add an element to the vector. * + * * + * Use this routine to add an element to the vector. The vector will automatically be * + * resized to accomodate the new element IF the vector was allocated previously and the * + * growth rate is not zero. * + * * + * INPUT: object -- Reference to the object that will be added to the vector. * + * * + * OUTPUT: bool; Was the object added successfully? If so, the object is added to the end * + * of the vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add(T const & object) +{ + if (ActiveCount >= (int)Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } + else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Add_Head -- Adds element to head of the list. * + * * + * This routine will add the specified element to the head of the vector. If necessary, * + * the vector will be expanded accordingly. * + * * + * INPUT: object -- Reference to the object to add to the head of this vector. * + * * + * OUTPUT: bool; Was the object added without error? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/21/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Add_Head(T const & object) +{ + if (ActiveCount >= (int)Length()) { + if ((IsAllocated || !VectorMax) && GrowthStep > 0) { + if (!Resize(Length() + GrowthStep)) { + + /* + ** Failure to increase the size of the vector is an error condition. + ** Return with the error flag. + */ + return(false); + } + } + else { + + /* + ** Increasing the size of this vector is not allowed! Bail this + ** routine with the error code. + */ + return(false); + } + } + + /* + ** There is room for the new object now. Add it to the end of the object vector. + */ + if (ActiveCount) { + memmove(&(*this)[1], &(*this)[0], ActiveCount * sizeof(T)); + } + (*this)[0] = object; + ActiveCount++; + // (*this)[ActiveCount++] = object; + return(true); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Remove the specified object from the vector. * + * * + * This routine will delete the object referenced from the vector. All objects in the * + * vector that follow the one deleted will be moved "down" to fill the hole. * + * * + * INPUT: object -- Reference to the object in this vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object deleted successfully? This should always be true. * + * * + * WARNINGS: Do no pass a reference to an object that is NOT part of this vector. The * + * results of this are undefined and probably catastrophic. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(T const & object) +{ + return(Delete(ID(object))); +} + + +/*********************************************************************************************** + * DynamicVectorClass::Delete -- Deletes the specified index from the vector. * + * * + * Use this routine to delete the object at the specified index from the objects in the * + * vector. This routine will move all the remaining objects "down" in order to fill the * + * hole. * + * * + * INPUT: index -- The index number of the object in the vector that is to be deleted. * + * * + * OUTPUT: bool; Was the object index deleted successfully? Failure might mean that the index * + * specified was out of bounds. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int DynamicVectorClass::Delete(int index) +{ + if (index >= 0 && index < ActiveCount) { + ActiveCount--; + + /* + ** If there are any objects past the index that was deleted, copy those + ** objects down in order to fill the hole. A simple memory copy is + ** not sufficient since the vector could contain class objects that + ** need to use the assignment operator for movement. + */ + for (int i = index; i < ActiveCount; i++) { + (*this)[i] = (*this)[i + 1]; + } + return(true); + } + return(false); +} + + + + + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Constructor for vector class. * + * * + * This constructor for the vector class is passed the initial size of the vector and an * + * optional pointer to a preallocated block of memory that the vector will be placed in. * + * If this optional pointer is NULL (or not provided), then the vector is allocated out * + * of free store (with the "new" operator). * + * * + * INPUT: size -- The number of elements to initialize this vector to. * + * * + * array -- Optional pointer to a previously allocated memory block to hold the * + * vector. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(unsigned size, T const * array) : + Vector(0), + VectorMax(size), + IsAllocated(false) +{ + /* + ** Allocate the vector. The default constructor will be called for every + ** object in this vector. + */ + if (size) { + if (array) { + Vector = new((void*)array) T[size]; + } + else { + Vector = new T[size]; + IsAllocated = true; + } + } +} + + +/*********************************************************************************************** + * VectorClass::~VectorClass -- Default destructor for vector class. * + * * + * This is the default destructor for the vector class. It will deallocate any memory * + * that it may have allocated. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::~VectorClass(void) +{ + VectorClass::Clear(); +} + + +/*********************************************************************************************** + * VectorClass::VectorClass -- Copy constructor for vector object. * + * * + * This is the copy constructor for the vector class. It will duplicate the provided * + * vector into the new vector being created. * + * * + * INPUT: vector -- Reference to the vector to use as a copy. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass::VectorClass(VectorClass const & vector) : + Vector(0), + VectorMax(0), + IsAllocated(false) +{ + *this = vector; +} + + +/*********************************************************************************************** + * VectorClass::operator = -- The assignment operator. * + * * + * This the the assignment operator for vector objects. It will alter the existing lvalue * + * vector to duplicate the rvalue one. * + * * + * INPUT: vector -- The rvalue vector to copy into the lvalue one. * + * * + * OUTPUT: Returns with reference to the newly copied vector. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +VectorClass & VectorClass::operator =(VectorClass const & vector) +{ + if (this != &vector) { + Clear(); + VectorMax = vector.Length(); + if (VectorMax) { + Vector = new T[VectorMax]; + if (Vector) { + IsAllocated = true; + for (int index = 0; index < (int)VectorMax; index++) { + Vector[index] = vector[index]; + } + } + } + else { + Vector = 0; + IsAllocated = false; + } + } + return(*this); +} + + +/*********************************************************************************************** + * VectorClass::operator == -- Equality operator for vector objects. * + * * + * This operator compares two vectors for equality. It does this by performing an object * + * by object comparison between the two vectors. * + * * + * INPUT: vector -- The right vector expression. * + * * + * OUTPUT: bool; Are the two vectors essentially equal? (do they contain comparable elements * + * in the same order?) * + * * + * WARNINGS: The equality operator must exist for the objects that this vector contains. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::operator == (VectorClass const & vector) const +{ + if (VectorMax == vector.Length()) { + for (int index = 0; index < (int)VectorMax; index++) { + if (Vector[index] != vector[index]) { + return(false); + } + } + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Pointer based conversion to index number. * + * * + * Use this routine to convert a pointer to an element in the vector back into the index * + * number of that object. This routine ONLY works with actual pointers to object within * + * the vector. For "equivalent" object index number (such as with similar integral values) * + * then use the "by value" index number ID function. * + * * + * INPUT: pointer -- Pointer to an actual object in the vector. * + * * + * OUTPUT: Returns with the index number for the object pointed to by the parameter. * + * * + * WARNINGS: This routine is only valid for actual pointers to object that exist within * + * the vector. All other object pointers will yield undefined results. * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +inline int VectorClass::ID(T const * ptr) +{ + return(((unsigned long)ptr - (unsigned long)&(*this)[0]) / sizeof(T)); +} + + +/*********************************************************************************************** + * VectorClass::ID -- Finds object ID based on value. * + * * + * Use this routine to find the index value of an object with equivalent value in the * + * vector. Typical use of this would be for integral types. * + * * + * INPUT: object -- Reference to the object that is to be looked up in the vector. * + * * + * OUTPUT: Returns with the index number of the object that is equivalent to the one * + * specified. If no matching value could be found then -1 is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/13/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::ID(T const & object) +{ + for (int index = 0; index < (int)VectorMax; index++) { + if ((*this)[index] == object) { + return(index); + } + } + return(-1); +} + + +/*********************************************************************************************** + * VectorClass::Clear -- Frees and clears the vector. * + * * + * Use this routine to reset the vector to an empty (non-allocated) state. A vector will * + * free all allocated memory when this routine is called. In order for the vector to be * + * useful after this point, the Resize function must be called to give it element space. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +void VectorClass::Clear(void) +{ + if (Vector && IsAllocated) { + delete[] Vector; + Vector = 0; + } + IsAllocated = false; + VectorMax = 0; +} + + +/*********************************************************************************************** + * VectorClass::Resize -- Changes the size of the vector. * + * * + * This routine is used to change the size (usually to increase) the size of a vector. This * + * is the only way to increase the vector's working room (number of elements). * + * * + * INPUT: newsize -- The desired size of the vector. * + * * + * array -- Optional pointer to a previously allocated memory block that the * + * array will be located in. If this parameter is not supplied, then * + * the array will be allocated from free store. * + * * + * OUTPUT: bool; Was the array resized successfully? * + * * + * WARNINGS: Failure to succeed could be the result of running out of memory. * + * * + * HISTORY: * + * 03/10/1995 JLB : Created. * + *=============================================================================================*/ +template +int VectorClass::Resize(unsigned newsize, T const * array) +{ + if (newsize) { + + /* + ** Allocate a new vector of the size specified. The default constructor + ** will be called for every object in this vector. + */ + T * newptr; + if (!array) { + newptr = new T[newsize]; + } + else { + newptr = new((void*)array) T[newsize]; + } + if (!newptr) { + return(false); + } + + /* + ** If there is an old vector, then it must be copied (as much as is feasible) + ** to the new vector. + */ + if (Vector) { + + /* + ** Copy as much of the old vector into the new vector as possible. This + ** presumes that there is a functional assignment operator for each + ** of the objects in the vector. + */ + int copycount = (newsize < VectorMax) ? newsize : VectorMax; + for (int index = 0; index < copycount; index++) { + newptr[index] = Vector[index]; + } + + /* + ** Delete the old vector. This might cause the destructors to be called + ** for all of the old elements. This makes the implementation of suitable + ** assignment operator very important. The default assignment operator will + ** only work for the simplest of objects. + */ + if (IsAllocated) { + delete[] Vector; + Vector = 0; + } + } + + /* + ** Assign the new vector data to this class. + */ + Vector = newptr; + VectorMax = newsize; + IsAllocated = (Vector && !array); + + } + else { + + /* + ** Resizing to zero is the same as clearing the vector. + */ + Clear(); + } + return(true); +} diff --git a/code/redalert/vectormath.h b/code/redalert/vectormath.h new file mode 100644 index 0000000..3c5c405 --- /dev/null +++ b/code/redalert/vectormath.h @@ -0,0 +1,2020 @@ +//-------------------------------------------------------------------------------------- +// Copyright 2013 Intel Corporation +// All Rights Reserved +// +// Permission is granted to use, copy, distribute and prepare derivative works of this +// software for any purpose and without fee, provided, that the above copyright notice +// and this statement appear in all copies. Intel makes no representations about the +// suitability of this software for any purpose. THIS SOFTWARE IS PROVIDED "AS IS." +// INTEL SPECIFICALLY DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, AND ALL LIABILITY, +// INCLUDING CONSEQUENTIAL AND OTHER INDIRECT DAMAGES, FOR THE USE OF THIS SOFTWARE, +// INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PROPRIETARY RIGHTS, AND INCLUDING THE +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Intel does not +// assume any responsibility for any errors which may appear in this software nor any +// responsibility to update it. +//-------------------------------------------------------------------------------------- + +#ifndef __CPUTMath_h__ +#define __CPUTMath_h__ +#include + +/* + * Constants + */ +static const float kEpsilon = 0.00001f; +static const float kPi = 3.14159265358979323846f; +static const float k2Pi = kPi*2.0f; +static const float kPiDiv2 = kPi/2.0f; +static const float kInvPi = 1.0f/kPi; +static const float kDegToRad = (kPi/180.0f); +static const float kRadToDeg = (180.0f/kPi); +static inline float DegToRad( float fDeg ) { return fDeg * kDegToRad; } +static inline float RadToDeg( float fRad ) { return fRad * kRadToDeg; } + +template +static inline void Swap(T &a, T &b) +{ + T t = b; + b = a; + a = t; +} + +static inline double InvSqrt(const double x) +{ + return 1.0 / sqrt(x); +} + +struct float2; +struct float3; +struct float4; + +/**************************************\ +float2 +\**************************************/ +struct float2 +{ + union + { + struct + { + float x; + float y; + }; + float f[2]; + }; + + /***************************************\ + | Constructors | + \***************************************/ + float2() {} + explicit float2(float f) : x(f), y(f) { } + explicit float2(float _x, float _y) : x(_x), y(_y) { } + explicit float2(float* f) : x(f[0]), y(f[1]) { } + float2(const float2 &v) : x(v.x), y(v.y) { } + const float2 &operator=(const float2 &v) + { + x = v.x; + y = v.y; + return *this; + } + + /***************************************\ + | Basic math operations | + \***************************************/ + float2 operator+(const float2 &r) const + { + return float2(x+r.x, y+r.y); + } + const float2 &operator+=(const float2 &r) + { + x += r.x; + y += r.y; + return *this; + } + float2 operator-(const float2 &r) const + { + return float2(x-r.x, y-r.y); + } + const float2 &operator-=(const float2 &r) + { + x -= r.x; + y -= r.y; + return *this; + } + /***************************************\ + | Basic math operations with scalars | + \***************************************/ + float2 operator+(float f) const + { + return float2(x+f, y+f); + } + const float2 &operator+=(float f) + { + x += f; + y += f; + return *this; + } + float2 operator-(float f) const + { + return float2(x-f, y-f); + } + const float2 &operator-=(float f) + { + x -= f; + y -= f; + return *this; + } + float2 operator*(float f) const + { + return float2(x*f, y*f); + } + const float2 &operator*=(float f) + { + x *= f; + y *= f; + return *this; + } + float2 operator/(float f) const + { + return float2(x/f, y/f); + } + const float2 &operator/=(float f) + { + x /= f; + y /= f; + return *this; + } + + /***************************************\ + | Other math | + \***************************************/ + // Equality + bool operator==(const float2 &r) const + { + return (x==r.x && y == r.y); + } + bool operator!=(const float2 &r) const + { + return !(*this == r); + } + + // Hadd + float hadd(void) const + { + return x + y; + } + + // Length + float lengthSq(void) const + { + return x*x + y*y; + } + float length(void) const + { + return sqrtf(lengthSq()); + } + void normalize(void) + { + *this /= length(); + } +}; + +inline float dot2(const float2 &l, const float2 &r) +{ + return l.x*r.x + l.y*r.y; +} + +inline float2 normalize(const float2 &v) +{ + float length = v.length(); + return v / length; +} + + + + + + + + +/**************************************\ +float3 +\**************************************/ +struct float3 +{ + union + { + struct + { + float x; + float y; + float z; + }; + float f[3]; + }; + + static const float3 Left; + static const float3 Right; + static const float3 Up; + static const float3 Down; + static const float3 Forward; + static const float3 Backward; + static const float3 ZeroF; + + /***************************************\ + | Constructors | + \***************************************/ + float3() {} + explicit float3(float f) : x(f), y(f), z(f) { } + explicit float3(float _x, float _y, float _z) : x(_x), y(_y), z(_z) { } + explicit float3(float* f) : x(f[0]), y(f[1]), z(f[2]) { } + float3(const float3 &v) : x(v.x), y(v.y), z(v.z) { } + float3(const float4 &v); + const float3 &operator=(const float3 &v) + { + x = v.x; + y = v.y; + z = v.z; + return *this; + } + + /***************************************\ + | Basic math operations | + \***************************************/ + float3 operator+(const float3 &r) const + { + return float3(x+r.x, y+r.y, z+r.z); + } + const float3 &operator+=(const float3 &r) + { + x += r.x; + y += r.y; + z += r.z; + return *this; + } + float3 operator-(const float3 &r) const + { + return float3(x-r.x, y-r.y, z-r.z); + } + float operator[](const int index) const { + return (&x)[index]; + } + + + float operator*(const float3 &a) const + { + return x * a.x + y * a.y + z * a.z; + } + + float Dot(const float3 &a) const + { + return x * a.x + y * a.y + z * a.z; + } + + static float Dot(const float3 &a, const float3 &b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + + float &operator[](const int index) { + return (&x)[index]; + } + const float3 &operator-=(const float3 &r) + { + x -= r.x; + y -= r.y; + z -= r.z; + return *this; + } + + float NormalizeFast() + { + float sqrLength, invLength; + + sqrLength = x * x + y * y + z * z; + invLength = InvSqrt(sqrLength); + x *= invLength; + y *= invLength; + z *= invLength; + return invLength * sqrLength; + } + + float3 operator*(float3 &r) + { + return float3(x*r.x, y*r.y, z*r.z); + } + + + float3 operator*(const float a) const { + return float3(x * a, y * a, z * a); + } + + friend float3 operator*(const float a, const float3 b) { + return float3(b.x * a, b.y * a, b.z * a); + } + + const float3 &operator*=(const float3 &r) + { + x *= r.x; + y *= r.y; + z *= r.z; + return *this; + } + float3 &operator*=(float3 &r) + { + x *= r.x; + y *= r.y; + z *= r.z; + return *this; + } + bool Compare(const float3 &a) const { + return ((x == a.x) && (y == a.y) && (z == a.z)); + } + + bool Compare(const float3 &a, const float epsilon) const { + if (fabs(x - a.x) > epsilon) { + return false; + } + + if (fabs(y - a.y) > epsilon) { + return false; + } + + if (fabs(z - a.z) > epsilon) { + return false; + } + + return true; + } + float3 operator/(const float3 &r) const + { + return float3(x/r.x, y/r.y, z/r.z); + } + const float3 &operator/=(const float3 &r) + { + x /= r.x; + y /= r.y; + z /= r.z; + return *this; + } + + /***************************************\ + | Basic math operations with scalars | + \***************************************/ + float3 operator+(float f) const + { + return float3(x+f, y+f, z+f); + } + const float3 &operator+=(float f) + { + x += f; + y += f; + z += f; + return *this; + } + float3 operator-(float f) const + { + return float3(x-f, y-f, z-f); + } + const float3 &operator-=(float f) + { + x -= f; + y -= f; + z -= f; + return *this; + } + + + const float3 &operator*=(float f) + { + x *= f; + y *= f; + z *= f; + return *this; + } + float3 operator/(float f) const + { + return float3(x/f, y/f, z/f); + } + const float3 &operator/=(float f) + { + x /= f; + y /= f; + z /= f; + return *this; + } + + void Zero() + { + x = 0; + y = 0; + z = 0; + } + + /***************************************\ + | Other math | + \***************************************/ + // Equality + bool operator==(const float3 &r) const + { + return x==r.x &&y == r.y &&z == r.z; + } + bool operator!=(const float3 &r) const + { + return !(*this == r); + } + + // Hadd + float hadd(void) const + { + return x + y + z; + } + + // Length + float lengthSq(void) const + { + return x*x + y*y + z*z; + } + float length(void) const + { + return sqrtf(lengthSq()); + } + + float3 normalize(void) + { + return (*this /= length()); + } + + float NormalizeSelf() { + float sqrLength, invLength; + + sqrLength = x * x + y * y + z * z; + invLength = InvSqrt(sqrLength); + x *= invLength; + y *= invLength; + z *= invLength; + return invLength * sqrLength; + } + float3 Cross(const float3 &a) const + { + return float3(y * a.z - z * a.y, z * a.x - x * a.z, x * a.y - y * a.x); + } + + float3 &Cross(const float3 &a, const float3 &b) + { + x = a.y * b.z - a.z * b.y; + y = a.z * b.x - a.x * b.z; + z = a.x * b.y - a.y * b.x; + + return *this; + } +}; + +inline float dot3(const float3 &l, const float3 &r) +{ + return l.x*r.x + l.y*r.y + l.z*r.z; +} +inline float3 cross3(const float3 &l, const float3 &r) +{ + return float3( l.y*r.z-l.z*r.y, + l.z*r.x-l.x*r.z, + l.x*r.y-l.y*r.x); +} +inline float3 normalize(const float3 &v) +{ + float length = v.length(); + return v / length; +} + +inline float3 abs3( const float3 &l ) +{ + float3 tmp; + tmp.x = fabs(l.x); + tmp.y = fabs(l.y); + tmp.z = fabs(l.z); + return tmp; +} + + +/**************************************\ +float4 +\**************************************/ +__declspec(align(16)) struct float4 +{ + union + { + struct + { + float x; + float y; + float z; + float w; + }; + float f[4]; + }; + + /***************************************\ + | Constructors | + \***************************************/ + float4() {} + explicit float4(float f) : x(f), y(f), z(f), w(f) { } + explicit float4(float _x, float _y, float _z, float _w) : x(_x), y(_y), z(_z), w(_w) { } + explicit float4(float* f) : x(f[0]), y(f[1]), z(f[2]), w(f[3]) { } + float4(const float4 &v) : x(v.x), y(v.y), z(v.z), w(v.w) { } + float4(const float3 &v, float _w) : x(v.x), y(v.y), z(v.z), w(_w) { } + const float4 &operator=(const float4 &v) + { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + return *this; + } + + /***************************************\ + | Basic math operations | + \***************************************/ + float4 operator+(const float4 &r) const + { + return float4(x+r.x, y+r.y, z+r.z, w+r.w); + } + const float4 &operator+=(const float4 &r) + { + x += r.x; + y += r.y; + z += r.z; + w += r.w; + return *this; + } + float4 operator-(const float4 &r) const + { + return float4(x-r.x, y-r.y, z-r.z, w-r.w); + } + const float4 &operator-=(const float4 &r) + { + x -= r.x; + y -= r.y; + z -= r.z; + w -= r.w; + return *this; + } + float4 operator*(const float4 &r) const + { + return float4(x*r.x, y*r.y, z*r.z, w*r.w); + } + const float4 &operator*=(const float4 &r) + { + x *= r.x; + y *= r.y; + z *= r.z; + w *= r.w; + return *this; + } + float4 operator/(const float4 &r) const + { + return float4(x/r.x, y/r.y, z/r.z, w/r.w); + } + const float4 &operator/=(const float4 &r) + { + x /= r.x; + y /= r.y; + z /= r.z; + w /= r.w; + return *this; + } + + /***************************************\ + | Basic math operations with scalars | + \***************************************/ + float4 operator+(float f) const + { + return float4(x+f, y+f, z+f, w+f); + } + const float4 &operator+=(float f) + { + x += f; + y += f; + z += f; + return *this; + } + float4 operator-(float f) const + { + return float4(x-f, y-f, z-f, w-f); + } + const float4 &operator-=(float f) + { + x -= f; + y -= f; + z -= f; + w -= f; + return *this; + } + float4 operator*(float f) const + { + return float4(x*f, y*f, z*f, w*f); + } + const float4 &operator*=(float f) + { + x *= f; + y *= f; + z *= f; + w *= f; + return *this; + } + float4 operator/(float f) const + { + return float4(x/f, y/f, z/f, w/f); + } + const float4 &operator/=(float f) + { + x /= f; + y /= f; + z /= f; + w /= f; + return *this; + } + + /***************************************\ + | Other math | + \***************************************/ + // Equality + bool operator==(const float4 &r) const + { + return x==r.x && y == r.y && z == r.z && w == r.w; + } + bool operator!=(const float4 &r) const + { + return !(*this == r); + } + + // Hadd + float hadd(void) const + { + return x + y + z + w; + } + + // Length + float lengthSq(void) const + { + return x*x + y*y + z*z + w*w; + } + float length(void) const + { + return sqrtf(lengthSq()); + } + void normalize(void) + { + *this /= length(); + } +}; + +inline float dot4(const float4 &l, const float4 &r) +{ + return l.x*r.x + l.y*r.y + l.z*r.z + l.w*r.w; +} +inline float4 normalize(const float4 &v) +{ + float length = v.length(); + return v / length; +} + +inline float3::float3(const float4 &v) : x(v.x), y(v.y), z(v.z) { } + + +/**************************************\ +float3x3 +\**************************************/ +struct float4x4; +struct float3x3 +{ + struct + { + float3 r0; + float3 r1; + float3 r2; + }; + + /***************************************\ + | Constructors | + \***************************************/ + float3x3() {} + explicit float3x3(float f) : r0(f), r1(f), r2(f) { } + explicit float3x3(float* f) : r0(f+0), r1(f+3), r2(f+6) { } + float3x3(const float3 &_r0, const float3 &_r1, const float3 &_r2) : r0(_r0), r1(_r1), r2(_r2) { } + float3x3(float _m00, float _m01, float _m02, + float _m10, float _m11, float _m12, + float _m20, float _m21, float _m22) + : r0(_m00,_m01,_m02) + , r1(_m10,_m11,_m12) + , r2(_m20,_m21,_m22) + { + } + float3x3(const float4x4 &m); + const float3x3 &operator=(const float3x3 &m) + { + r0 = m.r0; + r1 = m.r1; + r2 = m.r2; + return *this; + } + + /***************************************\ + | Basic math operations | + \***************************************/ + + #define MTX3_INDEX(f,r,c) ((f)[(r*3)+c]) + inline float3x3 operator*(const float3x3 &r) const + { + float3x3 m(1,0,0,0,1,0,0,0,1); + + const float* left = (const float*)&this->r0; + const float* right = (const float*)&r.r0; + float* result = (float*)&m; + + int ii, jj, kk; + for(ii=0; ii<3; ++ii) /* row */ + { + for(jj=0; jj<3; ++jj) /* column */ + { + float sum = MTX3_INDEX(left,ii,0)*MTX3_INDEX(right,0,jj); + for(kk=1; kk<3; ++kk) + { + sum += (MTX3_INDEX(left,ii,kk)*MTX3_INDEX(right,kk,jj)); + } + MTX3_INDEX(result,ii,jj) = sum; + } + } + return m; + } + #undef MTX3_INDEX + + inline float3 operator*(const float3 &v) const + { + float3 a; + + a.x = (r0*v); + a.y = (r1*v); + a.z = (r2*v); + + return a; + } + + /***************************************\ + | Basic math operations with scalars | + \***************************************/ + float3x3 operator+(float f) const + { + return float3x3(r0+f, r1+f, r2+f); + } + + float3 const &operator[](int index) const { + //assert( ( index >= 0 ) && ( index < 3 ) ); + return (&r0)[index]; + } + + float3 &operator[](int index) { + //assert( ( index >= 0 ) && ( index < 3 ) ); + return (&r0)[index]; + } + + const float3x3 &operator+=(float f) + { + r0 += f; + r1 += f; + r2 += f; + return *this; + } + + + friend float3 operator*(const float3 &vec, const float3x3 &mat) { + return mat * vec; + } + + friend float3x3 operator*(const float a, const float3x3 &mat) { + return mat * a; + } + + friend float3 &operator*=(float3 &vec, const float3x3 &mat) { + float x = mat[0].x * vec.x + mat[1].x * vec.y + mat[2].x * vec.z; + float y = mat[0].y * vec.x + mat[1].y * vec.y + mat[2].y * vec.z; + vec.z = mat[0].z * vec.x + mat[1].z * vec.y + mat[2].z * vec.z; + vec.x = x; + vec.y = y; + return vec; + } + + float3x3 &operator*=(const float3x3 &a) { + int i, j; + const float *m2Ptr; + float *m1Ptr, dst[3]; + + m1Ptr = reinterpret_cast(this); + m2Ptr = reinterpret_cast(&a); + + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + dst[j] = m1Ptr[0] * m2Ptr[0 * 3 + j] + + m1Ptr[1] * m2Ptr[1 * 3 + j] + + m1Ptr[2] * m2Ptr[2 * 3 + j]; + } + m1Ptr[0] = dst[0]; m1Ptr[1] = dst[1]; m1Ptr[2] = dst[2]; + m1Ptr += 3; + } + return *this; + } + + float3x3 operator-(float f) const + { + return float3x3(r0-f, r1-f, r2-f); + } + bool Compare(const float3x3 &a) const { + if (r0.Compare(a[0]) && + r1.Compare(a[1]) && + r2.Compare(a[2])) { + return true; + } + return false; + } + + bool Compare(const float3x3 &a, const float epsilon) const { + if (r0.Compare(a[0], epsilon) && + r1.Compare(a[1], epsilon) && + r2.Compare(a[2], epsilon)) { + return true; + } + return false; + } + + const float3x3 &operator-=(float f) + { + r0 -= f; + r1 -= f; + r2 -= f; + return *this; + } + float3x3 operator*(float f) const + { + return float3x3(r0*f, r1*f, r2*f); + } + const float3x3 &operator*=(float f) + { + r0 *= f; + r1 *= f; + r2 *= f; + return *this; + } + float3x3 operator/(float f) const + { + return float3x3(r0/f, r1/f, r2/f); + } + const float3x3 &operator/=(float f) + { + r0 /= f; + r1 /= f; + r2 /= f; + return *this; + } + + /***************************************\ + | Other math | + \***************************************/ + // Equality + bool operator==(const float3x3 &r) const + { + return r0 == r.r0 && r1 == r.r1 && r2 == r.r2; + } + bool operator!=(const float3x3 &r) const + { + return !(*this == r); + } + + float determinant() const + { + float f0 = r0.x * (r1.y*r2.z-r2.y*r1.z); + float f1 = r0.y * -(r1.x*r2.z-r2.x*r1.z); + float f2 = r0.z * (r1.x*r2.y-r2.x*r1.y); + + return f0 + f1 + f2; + } + + void transpose(void) + { + Swap(r0.y, r1.x); + Swap(r0.z, r2.x); + Swap(r1.z, r2.y); + } + + void invert(void) + { + float det = determinant(); + float3x3 inv; + + inv.r0.x = (r1.y*r2.z) - (r1.z*r2.y); + inv.r0.y = -((r1.x*r2.z) - (r1.z*r2.x)); + inv.r0.z = (r1.x*r2.y) - (r1.y*r2.x); + + inv.r1.x = -((r0.y*r2.z) - (r0.z*r2.y)); + inv.r1.y = (r0.x*r2.z) - (r0.z*r2.x); + inv.r1.z = -((r0.x*r2.y) - (r0.y*r2.x)); + + inv.r2.x = (r0.y*r1.z) - (r0.z*r1.y); + inv.r2.y = -((r0.x*r1.z) - (r0.z*r1.x)); + inv.r2.z = (r0.x*r1.y) - (r0.y*r1.x); + + inv.transpose(); + inv /= det; + + *this = inv; + } +}; +inline float3x3 float3x3Identity(void) +{ + return float3x3(1,0,0, + 0,1,0, + 0,0,1); +} +inline float determinant(const float3x3&m) +{ + return m.determinant(); +} + +inline float3x3 transpose(const float3x3 &m) +{ + float3x3 ret = m; + ret.transpose(); + return ret; +} + +inline float3x3 inverse(const float3x3 &m) +{ + float3x3 ret = m; + ret.invert(); + return ret; +} + +inline float3x3 float3x3RotationX(float rad) +{ + float c = cosf( rad ); + float s = sinf( rad ); + float3x3 m( 1.0f, 0.0f, 0.0f, + 0.0f, c, s, + 0.0f, -s, c ); + return m; +} +inline float3x3 float3x3RotationY(float rad) +{ + float c = cosf( rad ); + float s = sinf( rad ); + float3x3 m( c, 0.0f, -s, + 0.0f, 1.0f, 0.0f, + s, 0.0f, c ); + return m; +} +inline float3x3 float3x3RotationZ(float rad) +{ + + float c = cosf( rad ); + float s = sinf( rad ); + float3x3 m( c, s, 0.0f, + -s, c, 0.0f, + 0.0f, 0.0f, 1.0f ); + return m; +} + +inline float3x3 float3x3RotationAxis(const float3 &axis, float rad ) +{ + float3 normAxis = normalize(axis); + float c = cosf(rad); + float s = sinf(rad); + float t = 1 - c; + + float x = normAxis.x; + float y = normAxis.y; + float z = normAxis.z; + + float3x3 m; + + m.r0.x = (t * x * x) + c; + m.r0.y = (t * x * y) + s * z; + m.r0.z = (t * x * z) - s * y; + + m.r1.x = (t * x * y) - (s * z); + m.r1.y = (t * y * y) + c; + m.r1.z = (t * y * z) + (s * x); + + m.r2.x = (t * x * z) + (s * y); + m.r2.y = (t * y * z) - (s * x); + m.r2.z = (t * z * z) + c; + + return m; +} + +inline float3x3 float3x3Scale(float x, float y, float z) +{ + return float3x3(x,0,0, + 0,y,0, + 0,0,z); +} + +/**************************************\ +float4x4 +\**************************************/ +struct float4x4 +{ + struct + { + float4 r0; + float4 r1; + float4 r2; + float4 r3; + }; + + /***************************************\ + | Constructors | + \***************************************/ + float4x4() {} + explicit float4x4(float f) : r0(f), r1(f), r2(f), r3(f) { } + explicit float4x4(float* f) : r0(f+0), r1(f+4), r2(f+8), r3(f+12) { } + float4x4(const float4 &_r0, const float4 &_r1, const float4 &_r2, const float4 &_r3) : r0(_r0), r1(_r1), r2(_r2), r3(_r3) { } + float4x4(float _m00, float _m01, float _m02, float _m03, + float _m10, float _m11, float _m12, float _m13, + float _m20, float _m21, float _m22, float _m23, + float _m30, float _m31, float _m32, float _m33) + : r0(_m00,_m01,_m02,_m03) + , r1(_m10,_m11,_m12,_m13) + , r2(_m20,_m21,_m22,_m23) + , r3(_m30,_m31,_m32,_m33) + { + } + float4x4(const float3x3 &m) + : r0(m.r0, 0.0f) + , r1(m.r1, 0.0f) + , r2(m.r2, 0.0f) + , r3(0.0f, 0.0f, 0.0f, 1.0f) + { + } + const float4x4 &operator=(const float4x4 &m) + { + r0 = m.r0; + r1 = m.r1; + r2 = m.r2; + r3 = m.r3; + return *this; + } + + /***************************************\ + | Basic math operations | + \***************************************/ + + #define MTX4_INDEX(f,r,c) ((f)[(r*4)+c]) + inline float4x4 operator*(const float4x4 &r) const + { + float4x4 m(1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1); + + const float* right = (const float*)&this->r0; + const float* left = (const float*)&r.r0; + float* result = (float*)&m; + + int ii, jj, kk; + for(ii=0; ii<4; ++ii) /* row */ + { + for(jj=0; jj<4; ++jj) /* column */ + { + float sum = MTX4_INDEX(left,ii,0)*MTX4_INDEX(right,0,jj); + for(kk=1; kk<4; ++kk) + { + sum += (MTX4_INDEX(left,ii,kk)*MTX4_INDEX(right,kk,jj)); + } + MTX4_INDEX(result,ii,jj) = sum; + } + } + return m; + } + #undef MTX4_INDEX + + inline float4 operator*(const float4 &v) const + { + float4 a; + + a.x = (r0*v).hadd(); + a.y = (r1*v).hadd(); + a.z = (r2*v).hadd(); + a.w = (r3*v).hadd(); + + return a; + } + + /***************************************\ + | Basic math operations with scalars | + \***************************************/ + float4x4 operator+(float f) const + { + return float4x4(r0+f, r1+f, r2+f, r3+f); + } + const float4x4 &operator+=(float f) + { + r0 += f; + r1 += f; + r2 += f; + r3 += f; + return *this; + } + float4x4 operator-(float f) const + { + return float4x4(r0-f, r1-f, r2-f, r3-f); + } + const float4x4 &operator-=(float f) + { + r0 -= f; + r1 -= f; + r2 -= f; + r3 -= f; + return *this; + } + float4x4 operator*(float f) const + { + return float4x4(r0*f, r1*f, r2*f, r3*f); + } + const float4x4 &operator*=(float f) + { + r0 *= f; + r1 *= f; + r2 *= f; + r3 *= f; + return *this; + } + float4x4 operator/(float f) const + { + return float4x4(r0/f, r1/f, r2/f, r3/f); + } + const float4x4 &operator/=(float f) + { + r0 /= f; + r1 /= f; + r2 /= f; + r3 /= f; + return *this; + } + + /***************************************\ + | Other math | + \***************************************/ + // Equality + bool operator==(const float4x4 &r) const + { + return r0 == r.r0 && r1 == r.r1 && r2 == r.r2 && r3 == r.r3; + } + bool operator!=(const float4x4 &r) const + { + return !(*this == r); + } + + float determinant() const + { + float det = 0.0f; + + float3x3 a( r1.y,r1.z,r1.w, + r2.y,r2.z,r2.w, + r3.y,r3.z,r3.w); + + float3x3 b( r1.x,r1.z,r1.w, + r2.x,r2.z,r2.w, + r3.x,r3.z,r3.w); + + float3x3 c( r1.x,r1.y,r1.w, + r2.x,r2.y,r2.w, + r3.x,r3.y,r3.w); + + float3x3 d( r1.x,r1.y,r1.z, + r2.x,r2.y,r2.z, + r3.x,r3.y,r3.z); + + + det += r0.x * a.determinant(); + + det -= r0.y * b.determinant(); + + det += r0.z * c.determinant(); + + det -= r0.w * d.determinant(); + + return det; + } + + void transpose(void) + { + Swap(r0.y, r1.x); + Swap(r0.z, r2.x); + Swap(r0.w, r3.x); + Swap(r1.z, r2.y); + Swap(r1.w, r3.y); + Swap(r2.w, r3.z); + } + + void invert(void) + { + float4x4 ret; + float recip; + + /* temp matrices */ + + /* row 1 */ + float3x3 a( r1.y,r1.z,r1.w, + r2.y,r2.z,r2.w, + r3.y,r3.z,r3.w); + + float3x3 b( r1.x,r1.z,r1.w, + r2.x,r2.z,r2.w, + r3.x,r3.z,r3.w); + + float3x3 c( r1.x,r1.y,r1.w, + r2.x,r2.y,r2.w, + r3.x,r3.y,r3.w); + + float3x3 d( r1.x,r1.y,r1.z, + r2.x,r2.y,r2.z, + r3.x,r3.y,r3.z); + + /* row 2 */ + float3x3 e( r0.y,r0.z,r0.w, + r2.y,r2.z,r2.w, + r3.y,r3.z,r3.w); + + float3x3 f( r0.x,r0.z,r0.w, + r2.x,r2.z,r2.w, + r3.x,r3.z,r3.w); + + float3x3 g( r0.x,r0.y,r0.w, + r2.x,r2.y,r2.w, + r3.x,r3.y,r3.w); + + float3x3 h( r0.x,r0.y,r0.z, + r2.x,r2.y,r2.z, + r3.x,r3.y,r3.z); + + + /* row 3 */ + float3x3 i( r0.y,r0.z,r0.w, + r1.y,r1.z,r1.w, + r3.y,r3.z,r3.w); + + float3x3 j( r0.x,r0.z,r0.w, + r1.x,r1.z,r1.w, + r3.x,r3.z,r3.w); + + float3x3 k( r0.x,r0.y,r0.w, + r1.x,r1.y,r1.w, + r3.x,r3.y,r3.w); + + float3x3 l( r0.x,r0.y,r0.z, + r1.x,r1.y,r1.z, + r3.x,r3.y,r3.z); + + + /* row 4 */ + float3x3 m( r0.y, r0.z, r0.w, + r1.y, r1.z, r1.w, + r2.y, r2.z, r2.w); + + float3x3 n( r0.x, r0.z, r0.w, + r1.x, r1.z, r1.w, + r2.x, r2.z, r2.w); + + float3x3 o( r0.x,r0.y,r0.w, + r1.x,r1.y,r1.w, + r2.x,r2.y,r2.w); + + float3x3 p( r0.x,r0.y,r0.z, + r1.x,r1.y,r1.z, + r2.x,r2.y,r2.z); + + /* row 1 */ + ret.r0.x = a.determinant(); + + ret.r0.y = -b.determinant(); + + ret.r0.z = c.determinant(); + + ret.r0.w = -d.determinant(); + + /* row 2 */ + ret.r1.x = -e.determinant(); + + ret.r1.y = f.determinant(); + + ret.r1.z = -g.determinant(); + + ret.r1.w = h.determinant(); + + /* row 3 */ + ret.r2.x = i.determinant(); + + ret.r2.y = -j.determinant(); + + ret.r2.z = k.determinant(); + + ret.r2.w = -l.determinant(); + + /* row 4 */ + ret.r3.x = -m.determinant(); + + ret.r3.y = n.determinant(); + + ret.r3.z = -o.determinant(); + + ret.r3.w = p.determinant(); + + ret.transpose(); + recip = 1.0f/determinant(); + ret *= recip; + + *this = ret; + } + + // Axis access + float3 getXAxis(void) const + { + return r0; + } + float3 getYAxis(void) const + { + return r1; + } + float3 getZAxis(void) const + { + return r2; + } + float3 getPosition(void) const + { + return float3(r3); + } + + void orthonormalize(void) + { + float3 x = getXAxis(); + float3 y = getYAxis(); + float3 z; + + x.normalize(); + z = normalize(cross3(x, y)); + y = normalize(cross3(z, x)); + + r0 = float4(x, 0.0f); + r1 = float4(y, 0.0f); + r2 = float4(z, 0.0f); + } +}; + +inline float3x3::float3x3(const float4x4 &m) + : r0(m.r0) + , r1(m.r1) + , r2(m.r2) +{ +} +inline float determinant(const float4x4&m) +{ + return m.determinant(); +} +inline float4x4 float4x4Identity(void) +{ + return float4x4(1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1); +} +inline float4x4 transpose(const float4x4 &m) +{ + float4x4 ret = m; + ret.transpose(); + return ret; +} + +inline float4x4 inverse(const float4x4 &m) +{ + float4x4 ret = m; + ret.invert(); + return ret; +} + +inline float4x4 float4x4RotationX(float rad) +{ + float c = cosf( rad ); + float s = sinf( rad ); + float4x4 m( 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, c, s, 0.0f, + 0.0f, -s, c, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + return m; +} +inline float4x4 float4x4RotationY(float rad) +{ + float c = cosf( rad ); + float s = sinf( rad ); + float4x4 m( c, 0.0f, -s, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + s, 0.0f, c, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + return m; +} +inline float4x4 float4x4RotationZ(float rad) +{ + + float c = cosf( rad ); + float s = sinf( rad ); + float4x4 m( c, s, 0.0f, 0.0f, + -s, c, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); + return m; +} + +inline float4x4 float4x4RotationAxis(const float3 &axis, float rad ) +{ + float3 normAxis = normalize(axis); + float c = cosf(rad); + float s = sinf(rad); + float t = 1 - c; + + float x = normAxis.x; + float y = normAxis.y; + float z = normAxis.z; + + float4x4 m = float4x4Identity(); + + m.r0.x = (t * x * x) + c; + m.r0.y = (t * x * y) + s * z; + m.r0.z = (t * x * z) - s * y; + + m.r1.x = (t * x * y) - (s * z); + m.r1.y = (t * y * y) + c; + m.r1.z = (t * y * z) + (s * x); + + m.r2.x = (t * x * z) + (s * y); + m.r2.y = (t * y * z) - (s * x); + m.r2.z = (t * z * z) + c; + + return m; +} + +inline float4x4 float4x4Scale(float x, float y, float z) +{ + return float4x4(x,0,0,0, + 0,y,0,0, + 0,0,z,0, + 0,0,0,1); +} + +inline float4x4 float4x4Translation(float x, float y, float z) +{ + float4x4 m = float4x4Identity(); + m.r3.x = x; + m.r3.y = y; + m.r3.z = z; + return m; +} +inline float4x4 float4x4Translation(const float3 &v) +{ + return float4x4Translation(v.x, v.y, v.z); +} + +inline float4x4 float4x4PerspectiveFovLH(float fov, float aspect, float nearPlane, float farPlane) +{ + float width = tanf(fov * 0.5f); + float height = width / aspect; + float diff = farPlane-nearPlane; + float div = farPlane / diff; + + float4x4 m( + 1.0f/width, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f/height, 0.0f, 0.0f, + 0.0f, 0.0f, div, 1.0f, + 0.0f, 0.0f, -nearPlane*div, 0.0f + ); + return m; +} +inline float4x4 float4x4PerspectiveFovRH(float fov, float aspect, float nearPlane, float farPlane) +{ + float4x4 m = float4x4Identity(); + + float f = tanf(kPiDiv2 - (fov/2.0f)); + float diff = nearPlane-farPlane; + float div = farPlane / diff; + + m.r0.x = f/aspect; + m.r1.y = f; + m.r2.z = div; + m.r2.w = -1; + m.r3.z = nearPlane * div; + m.r3.w = 0; + return m; +} +inline float4x4 float4x4PerspectiveLH(float width, float height, float nearPlane, float farPlane) +{ + float4x4 m = float4x4Identity(); + + m.r0.x = 2*nearPlane/width; + m.r1.y = 2*nearPlane/height; + m.r2.z = farPlane/(farPlane-nearPlane); + m.r2.w = 1; + m.r3.z = nearPlane*farPlane/(nearPlane-farPlane); + m.r3.w = 0; + return m; +} +inline float4x4 float4x4PerspectiveRH(float width, float height, float nearPlane, float farPlane) +{ + float4x4 m = float4x4Identity(); + + m.r0.x = 2*nearPlane/width; + m.r1.y = 2*nearPlane/height; + m.r2.z = farPlane/(nearPlane-farPlane); + m.r2.w = -1; + m.r3.z = nearPlane*farPlane/(nearPlane-farPlane); + m.r3.w = 0; + return m; +} +inline float4x4 float4x4OrthographicOffCenterLH(float left, float right, float top, float bottom, float nearPlane, float farPlane) +{ + float4x4 m = float4x4Identity(); + + float diff = farPlane-nearPlane; + + m.r0.x = 2.0f/(right-left); + m.r1.y = 2.0f/(top-bottom); + m.r2.z = 1.0f/diff; + m.r3.x = -((left+right)/(right-left)); + m.r3.y = -((top+bottom)/(top-bottom)); + m.r3.z = -nearPlane/diff; + + return m; +} +inline float4x4 float4x4OrthographicOffCenterRH(float left, float right, float top, float bottom, float nearPlane, float farPlane) +{ + float4x4 m = float4x4Identity(); + float diff = nearPlane-farPlane; + + m.r0.x = 2.0f/(right-left); + m.r1.y = 2.0f/(top-bottom); + m.r2.z = 1.0f/diff; + m.r3.x = -((left+right)/(right-left)); + m.r3.y = -((top+bottom)/(top-bottom)); + m.r3.z = nearPlane/diff; + + return m; +} +inline float4x4 float4x4OrthographicLH(float width, float height, float nearPlane, float farPlane) +{ + float halfWidth = width/2.0f; + float halfHeight = height/2.0f; + + return float4x4OrthographicOffCenterLH(-halfWidth, halfWidth, halfHeight, -halfHeight, nearPlane, farPlane); +} +inline float4x4 float4x4OrthographicRH(float width, float height, float nearPlane, float farPlane) +{ + float halfWidth = width/2.0f; + float halfHeight = height/2.0f; + + return float4x4OrthographicOffCenterRH(-halfWidth, halfWidth, halfHeight, -halfHeight, nearPlane, farPlane); +} + +/**************************************\ +Quaternion +\**************************************/ +struct quaternion : public float4 +{ + + /***************************************\ + | Constructors | + \***************************************/ + quaternion() {} + explicit quaternion(float f) : float4(f) { } + explicit quaternion(float _x, float _y, float _z, float _w) : float4(_x,_y,_z,_w) { } + explicit quaternion(float* f) : float4(f) { } + quaternion(const quaternion &v) : float4(v) { } + quaternion(const float3 &v, float _w) + { + float3 norm = ::normalize(v); + float a = _w*0.5f; + float s = sinf(a); + x = norm.x*s; + y = norm.y*s; + z = norm.z*s; + w = cosf(a); + } + const quaternion &operator=(const quaternion &v) + { + x = v.x; + y = v.y; + z = v.z; + w = v.w; + return *this; + } + + /* Methods */ + float3 getXAxis(void) const + { + float3 ret( 1-2*(y*y+z*z), + 2*(x*y+w*z), + 2*(x*z-y*w)); + return ret; + } + float3 getYAxis(void) const + { + float3 ret( 2*(x*y-z*w), + 1-2*(x*x+z*z), + 2*(y*z+x*w)); + return ret; + } + float3 getZAxis(void) const + { + float3 ret( 2*(x*z+y*w), + 2*(y*z-x*w), + 1-2*(x*x+y*y)); + return ret; + } + + float3x3 getMatrix(void) const + { + quaternion q = *this; + q.normalize(); + float xx = q.x * q.x; + float yy = q.y * q.y; + float zz = q.z * q.z; + + float xy = q.x * q.y; + float zw = q.z * q.w; + float xz = q.x * q.z; + float yw = q.y * q.w; + float yz = q.y * q.z; + float xw = q.x * q.w; + + float3x3 ret(1 - 2*(yy+zz), 2*(xy+zw), 2*(xz-yw), + 2*(xy-zw), 1 - 2*(xx+zz), 2*(yz+xw), + 2*(xz+yw), 2*(yz-xw), 1 - 2*(xx+yy) ); + return ret; + } + + quaternion conjugate(void) const + { + return quaternion(-x, -y, -z, w); + } + quaternion inverse(void) const + { + // Note: Only normalized quaternions are supportted at the moment + quaternion q = *this; + q.normalize(); + return q.conjugate(); + } +}; + +inline quaternion quaternionMultiply(const quaternion &l, const quaternion &r) +{ + quaternion q( r.w*l.x + r.x*l.w + r.y*l.z - r.z*l.y, + r.w*l.y + r.y*l.w + r.z*l.x - r.x*l.z, + r.w*l.z + r.z*l.w + r.x*l.y - r.y*l.x, + r.w*l.w - r.x*l.x - r.y*l.y - r.z*l.z ); + return q; +} + +inline quaternion quaternionIdentity(void) +{ + return quaternion(0.0f, 0.0f, 0.0f, 1.0f); +} + +inline float3 Min( float3 &v0, float3 &v1 ) +{ + float3 result; + result.x = v0.x < v1.x ? v0.x : v1.x; + result.y = v0.y < v1.y ? v0.y : v1.y; + result.z = v0.z < v1.z ? v0.z : v1.z; + return result; +} + +inline float3 Max( float3 &v0, float3 &v1 ) +{ + float3 result; + result.x = v0.x > v1.x ? v0.x : v1.x; + result.y = v0.y > v1.y ? v0.y : v1.y; + result.z = v0.z > v1.z ? v0.z : v1.z; + return result; +} + +inline float4 Min( float4 &v0, float4 &v1 ) +{ + float4 result; + result.x = v0.x < v1.x ? v0.x : v1.x; + result.y = v0.y < v1.y ? v0.y : v1.y; + result.z = v0.z < v1.z ? v0.z : v1.z; + result.w = v0.w < v1.w ? v0.w : v1.w; + return result; +} + +inline float4 Max( float4 &v0, float4 &v1 ) +{ + float4 result; + result.x = v0.x > v1.x ? v0.x : v1.x; + result.y = v0.y > v1.y ? v0.y : v1.y; + result.z = v0.z > v1.z ? v0.z : v1.z; + result.w = v0.w > v1.w ? v0.w : v1.w; + return result; +} + +inline float4 operator*(const float4 &v, const float4x4 &m) +{ + float4 result; + + result = m.r0 * v.x; + result += m.r1 * v.y; + result += m.r2 * v.z; + result += m.r3 * v.w; + + return result; +} + +/* +============================ +CreateLookAtMatrix +============================ +*/ +inline float4x4 CreateLookAtMatrix(float3 cameraPosition, float3 cameraTarget, float3 cameraUpVector) +{ + float3 vector3_1 = (cameraPosition - cameraTarget).normalize(); + float3 vector3_2; + + vector3_2.Cross(cameraUpVector, vector3_1); + vector3_2.NormalizeSelf(); + + float3 vector1; + vector1.Cross(vector3_1, vector3_2); + + float4x4 matrix; + matrix.r0.x = vector3_2.x; + matrix.r0.y = vector1.x; + matrix.r0.z = vector3_1.x; + matrix.r0.w = 0.0f; + matrix.r1.x = vector3_2.y; + matrix.r1.y = vector1.y; + matrix.r1.z = vector3_1.y; + matrix.r1.w = 0.0f; + matrix.r2.x = vector3_2.z; + matrix.r2.y = vector1.z; + matrix.r2.z = vector3_1.z; + matrix.r2.w = 0.0f; + matrix.r3.x = -float3::Dot(vector3_2, cameraPosition); + matrix.r3.y = -float3::Dot(vector1, cameraPosition); + matrix.r3.z = -float3::Dot(vector3_1, cameraPosition); + matrix.r3.w = 1.0f; + + return matrix; +} + +__inline float3 abs(const float3 xyz) +{ + float3 result(fabs(xyz.x), fabs(xyz.y), fabs(xyz.z)); + return result; +} + + +__inline float4 abs(const float4 xyz) +{ + float4 result(fabs(xyz.x), fabs(xyz.y), fabs(xyz.z), fabs(xyz.w)); + return result; +} + +__inline float3 fabs(const float3 xyz) +{ + float3 result(fabs(xyz.x), fabs(xyz.y), fabs(xyz.z)); + return result; +} + + +__inline float4 fabs(const float4 xyz) +{ + float4 result(fabs(xyz.x), fabs(xyz.y), fabs(xyz.z), fabs(xyz.w)); + return result; +} + +static const float Identity[16] = { + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 +}; + +__inline void _math_matrix_rotate(float4x4 &matrix, float angle, float x, float y, float z) +{ + float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; + float m[16]; + bool optimized; + #define M_PI 3.14159265358979323846 + s = sinf(angle * M_PI / 180.0); + c = cosf(angle * M_PI / 180.0); + + memcpy(m, Identity, sizeof(Identity)); + optimized = false; + +#define M(row,col) m[col*4+row] + + if (x == 0.0F) { + if (y == 0.0F) { + if (z != 0.0F) { + optimized = true; + /* rotate only around z-axis */ + M(0, 0) = c; + M(1, 1) = c; + if (z < 0.0F) { + M(0, 1) = s; + M(1, 0) = -s; + } + else { + M(0, 1) = -s; + M(1, 0) = s; + } + } + } + else if (z == 0.0F) { + optimized = true; + /* rotate only around y-axis */ + M(0, 0) = c; + M(2, 2) = c; + if (y < 0.0F) { + M(0, 2) = -s; + M(2, 0) = s; + } + else { + M(0, 2) = s; + M(2, 0) = -s; + } + } + } + else if (y == 0.0F) { + if (z == 0.0F) { + optimized = true; + /* rotate only around x-axis */ + M(1, 1) = c; + M(2, 2) = c; + if (x < 0.0F) { + M(1, 2) = s; + M(2, 1) = -s; + } + else { + M(1, 2) = -s; + M(2, 1) = s; + } + } + } + + if (!optimized) { + const float mag = sqrtf(x * x + y * y + z * z); + + if (mag <= 1.0e-4F) { + /* no rotation, leave mat as-is */ + return; + } + + x /= mag; + y /= mag; + z /= mag; + + + /* + * Arbitrary axis rotation matrix. + * + * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied + * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation + * (which is about the X-axis), and the two composite transforms + * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary + * from the arbitrary axis to the X-axis then back. They are + * all elementary rotations. + * + * Rz' is a rotation about the Z-axis, to bring the axis vector + * into the x-z plane. Then Ry' is applied, rotating about the + * Y-axis to bring the axis vector parallel with the X-axis. The + * rotation about the X-axis is then performed. Ry and Rz are + * simply the respective inverse transforms to bring the arbitrary + * axis back to its original orientation. The first transforms + * Rz' and Ry' are considered inverses, since the data from the + * arbitrary axis gives you info on how to get to it, not how + * to get away from it, and an inverse must be applied. + * + * The basic calculation used is to recognize that the arbitrary + * axis vector (x, y, z), since it is of unit length, actually + * represents the sines and cosines of the angles to rotate the + * X-axis to the same orientation, with theta being the angle about + * Z and phi the angle about Y (in the order described above) + * as follows: + * + * cos ( theta ) = x / sqrt ( 1 - z^2 ) + * sin ( theta ) = y / sqrt ( 1 - z^2 ) + * + * cos ( phi ) = sqrt ( 1 - z^2 ) + * sin ( phi ) = z + * + * Note that cos ( phi ) can further be inserted to the above + * formulas: + * + * cos ( theta ) = x / cos ( phi ) + * sin ( theta ) = y / sin ( phi ) + * + * ...etc. Because of those relations and the standard trigonometric + * relations, it is pssible to reduce the transforms down to what + * is used below. It may be that any primary axis chosen will give the + * same results (modulo a sign convention) using thie method. + * + * Particularly nice is to notice that all divisions that might + * have caused trouble when parallel to certain planes or + * axis go away with care paid to reducing the expressions. + * After checking, it does perform correctly under all cases, since + * in all the cases of division where the denominator would have + * been zero, the numerator would have been zero as well, giving + * the expected result. + */ + + xx = x * x; + yy = y * y; + zz = z * z; + xy = x * y; + yz = y * z; + zx = z * x; + xs = x * s; + ys = y * s; + zs = z * s; + one_c = 1.0F - c; + + /* We already hold the identity-matrix so we can skip some statements */ + M(0, 0) = (one_c * xx) + c; + M(0, 1) = (one_c * xy) - zs; + M(0, 2) = (one_c * zx) + ys; + /* M(0,3) = 0.0F; */ + + M(1, 0) = (one_c * xy) + zs; + M(1, 1) = (one_c * yy) + c; + M(1, 2) = (one_c * yz) - xs; + /* M(1,3) = 0.0F; */ + + M(2, 0) = (one_c * zx) - ys; + M(2, 1) = (one_c * yz) + xs; + M(2, 2) = (one_c * zz) + c; + /* M(2,3) = 0.0F; */ + + /* + M(3,0) = 0.0F; + M(3,1) = 0.0F; + M(3,2) = 0.0F; + M(3,3) = 1.0F; + */ + } +#undef M + float4x4 _internalMatrix(&m[0]); + //_internalMatrix.r[0] = XMVectorSet(m[0], m[1], m[2], m[3]); + //_internalMatrix.r[1] = XMVectorSet(m[4], m[5], m[6], m[7]); + //_internalMatrix.r[2] = XMVectorSet(m[8], m[9], m[10], m[11]); + //_internalMatrix.r[3] = XMVectorSet(m[12], m[13], m[14], m[15]); + float4x4 temp = matrix * _internalMatrix; + matrix = temp; + + //matmul4(mat, mat, m); + //matrix_multf(mat, m, MAT_FLAG_ROTATION); +} + +static void glhFrustumf2(float *matrix, float left, float right, float bottom, float top, float znear, float zfar) +{ + float temp, temp2, temp3, temp4; + temp = 2.0 * znear; + temp2 = right - left; + temp3 = top - bottom; + temp4 = zfar - znear; + matrix[0] = temp / temp2; + matrix[1] = 0.0; + matrix[2] = 0.0; + matrix[3] = 0.0; + matrix[4] = 0.0; + matrix[5] = temp / temp3; + matrix[6] = 0.0; + matrix[7] = 0.0; + matrix[8] = (right + left) / temp2; + matrix[9] = (top + bottom) / temp3; + matrix[10] = (-zfar - znear) / temp4; + matrix[11] = -1.0; + matrix[12] = 0.0; + matrix[13] = 0.0; + matrix[14] = (-temp * zfar) / temp4; + matrix[15] = 0.0; +} + + +static void glhPerspectivef2(float *matrix, float fovyInDegrees, float aspectRatio, float znear, float zfar) +{ + float ymax, xmax; + float temp, temp2, temp3, temp4; + ymax = znear * tanf(fovyInDegrees * M_PI / 360.0); + //ymin = -ymax; + //xmin = -ymax * aspectRatio; + xmax = ymax * aspectRatio; + glhFrustumf2(matrix, -xmax, xmax, -ymax, ymax, znear, zfar); +} + +#endif // #ifndef __CPUTMath_h__ \ No newline at end of file diff --git a/code/redalert/version.cpp b/code/redalert/version.cpp new file mode 100644 index 0000000..5bdba98 --- /dev/null +++ b/code/redalert/version.cpp @@ -0,0 +1,848 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VERSION.CPP 14 3/16/97 10:16p Joe_b $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : VERSION.CPP * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : September 17, 1996 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VersionClass::VersionClass -- Class constructor * + * VersionClass::Version_Number -- Returns program version number * + * VersionClass::Major_Version -- returns major version # * + * VersionClass::Minor_Version -- returns minor version (revision) number* + * VersionClass::Version_Name -- returns version # as char string * + * VersionClass::Read_Text_String -- reads version text string from disk * + * VersionClass::Version_Protocol -- returns default protocol for version* + * VersionClass::Init_Clipping -- Initializes version clipping * + * VersionClass::Clip_Version -- "clips" the given version range * + * VersionClass::Min_Version -- returns lowest version # to connect to * + * VersionClass::Max_Version -- returns highest version # to connect to * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "function.h" + +#ifdef FIXIT_VERSION_3 +#include "rawolapi.h" // For version number. +#endif + +/****************************** Globals ************************************/ +//--------------------------------------------------------------------------- +// This is a table of version numbers # the communications protocol used for +// that version number. It's used by the game owner to determine the +// protocol to be used for a given session. +// +// This table needs to be updated every time a new communications protocol +// is implemented, not every time a new version is created. +// +// A given protocol is used from its corresponding version #, up to (but not +// including) the next version number in the table. The last protocol in +// the table is the default protocol for this version. +//--------------------------------------------------------------------------- +static VersionProtocolType VersionProtocol[] = { + {0x00001000,COMM_PROTOCOL_SINGLE_NO_COMP}, // (obsolete) + {0x00002000,COMM_PROTOCOL_SINGLE_E_COMP}, // (obsolete) + {0x00010000,COMM_PROTOCOL_MULTI_E_COMP}, +}; + + +/*************************************************************************** + * VersionClass::VersionClass -- Class constructor * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + * 09/17/1996 JLB : Converted to used initializer list. * + *=========================================================================*/ +VersionClass::VersionClass(void) : + Version(0), + MajorVer(0), + MinorVer(0), + MinClipVer(0), + MaxClipVer(0), + VersionInit(false), + MajorInit(false), + MinorInit(false), + TextInit(false) +{ + VersionText[0] = '\0'; + VersionName[0] = '\0'; +} + + +/*************************************************************************** + * VersionClass::Version_Number -- Returns program version number * + * * + * Version Number Format: * + * Non-CHEAT format: * + * Byte 3,2: major version (printed to the left of a decimal) * + * Byte 1,0: minor version (printed to the right of a decimal) * + * Thus, version 1.07 would appear as 0x0001 0700 * + * * + * This format guarantees that a greater-than or less-than comparison * + * will work on version numbers. * + * * + * CHEAT format: * + * Byte 3: Month # * + * Byte 2: Day # * + * Byte 1: Hour # * + * Byte 0: Minute # * + * * + * This format guarantees a unique version number for each compile (as * + * long as they're a minute or more apart), with increasing version #'s * + * for later times. * + * * + * Either format should be printed in hex. * + * * + * This routine also fills in a text string (retrieved with Version_Text), * + * which may contain a custom string (such as "Beta"); this string is * + * read from the file VERSION.TXT. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ + +//ajw Note: This function is no longer called. MIN_VERSION is now incorrect, but I don't have time +// for a full rebuild (3 hrs!), and as MIN_VERSION is no longer referred to, I'm going to leave it. +// Really, it should be deleted or commented out. +// Version number used is now GAME_VERSION. +// Note also that VERSION_RA_300 is wrong, but not used. + +unsigned long VersionClass::Version_Number(void) +{ + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the version has already been set, just return it. + //------------------------------------------------------------------------ + if (VersionInit) { + return (Version); + } + + //------------------------------------------------------------------------ + // Generate the version # + //------------------------------------------------------------------------ + Version = ((Major_Version() << 16) | Minor_Version()); + VersionInit = 1; + + return (Version); + +} /* end of Version_Number */ + + +/*************************************************************************** + * VersionClass::Major_Version -- returns major version # * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Major Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Major_Version(void) +{ +#ifdef DEV_VERSION + static char * date = __DATE__; // format: Mmm dd yyyy + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char buf[10]; + char * ptr; + char * tok; + int monthnum; + int daynum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the major version # is already set, just return it. + //------------------------------------------------------------------------ + if (MajorInit) { + return (MajorVer); + } + + //------------------------------------------------------------------------ + // For a development version, use the date (month & day) as the major + // version number. + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the month and place in the high byte. + //........................................................................ + strupr(date); + tok = strtok(date, " "); + ptr = strstr(months, tok); + if (ptr) { + monthnum = (((ptr - months) / 3) + 1); + } else { + monthnum = 0; + } + + //........................................................................ + // Convert the month number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",monthnum); + sscanf(buf,"%x",&monthnum); + + //........................................................................ + // Fetch the date and place that in the low byte. + //........................................................................ + tok = strtok(NULL, " "); + if (tok) { + daynum = atoi(tok); + } else { + daynum = 0; + } + + //........................................................................ + // Convert the day number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",daynum); + sscanf(buf,"%x",&daynum); + + MajorVer = ((monthnum << 8) | daynum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor version number. + //------------------------------------------------------------------------ +#else + MajorVer = MAJOR_VERSION; +#endif + + MajorInit = 1; + + return (MajorVer); + +} /* end of Major_Version */ + + +/*************************************************************************** + * VersionClass::Minor_Version -- returns minor version (revision) number * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * Minor Version number * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned short VersionClass::Minor_Version(void) +{ +#ifdef DEV_VERSION + static char * time = __TIME__; // format: hh:mm:ss + char * tok; + char buf[10]; + int hournum; + int minnum; +#endif + + //------------------------------------------------------------------------ + // Read the text description, if there is one + //------------------------------------------------------------------------ + if (!TextInit) { + Read_Text_String(); + TextInit = 1; + } + + //------------------------------------------------------------------------ + // If the minor version # is already set, just return it. + //------------------------------------------------------------------------ + if (MinorInit) { + return (MinorVer); + } + + //------------------------------------------------------------------------ + // For in-development versions, use the time (hour & min) as the minor + // version + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + //........................................................................ + // Fetch the hour and place that in the last two digit positions. + //........................................................................ + tok = strtok(time, ": "); + if (tok) { + hournum = atoi(tok); + } else { + hournum = 0; + } + + //........................................................................ + // Convert the hour number to a hex counterpart (so, when it's printed + // in hex, it will read correctly.) + //........................................................................ + sprintf(buf,"%d",hournum); + sscanf(buf,"%x",&hournum); + + //........................................................................ + // Fetch the minute and place that in the last two digit positions. + //........................................................................ + tok = strtok(NULL, ": "); + if (tok) { + minnum = atoi(tok); + } else { + minnum = 0; + } + + //........................................................................ + // Convert the minute number to a hex counterpart + //........................................................................ + sprintf(buf,"%d",minnum); + sscanf(buf,"%x",&minnum); + + MinorVer = ((hournum << 8) | minnum); + + //------------------------------------------------------------------------ + // For a non-development version, use the hard-coded minor revision number. + //------------------------------------------------------------------------ +#else + +#ifdef FIXIT_VERSION_3 // Insanity. CS installation should not have affected version number. ajw + + MinorVer = MINOR_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_CSII + MinorVer = MINOR_VERSION; + if (Is_Counterstrike_Installed()) { + MinorVer = MINOR_VERSION - 1; + } +#else + #ifdef FIXIT_VERSION + /* If counterstrike is not installed then we report version 1.06 + * otherwise we report ourselves as 1.08 + */ + if (Is_Counterstrike_Installed() == false) { + MinorVer = (MINOR_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + MinorVer = MINOR_VERSION; + } + #else + MinorVer = MINOR_VERSION; + #endif +#endif + +#endif // FIXIT_VERSION_3 + +#endif + + MinorInit = 1; + + return (MinorVer); + +} /* end of Minor_Version */ + + +/*************************************************************************** + * VersionClass::Version_Name -- returns version # as char string * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * ptr to name * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/30/1995 BRR : Created. * + *=========================================================================*/ +char * VersionClass::Version_Name(void) +{ + //------------------------------------------------------------------------ + // For developmental versions, just use the major & minor version #'s + //------------------------------------------------------------------------ +#ifdef DEV_VERSION + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), VerNum.Minor_Version()); + + //------------------------------------------------------------------------ + // For final versions, trim 0's off the minor version + //------------------------------------------------------------------------ +#else + unsigned short adjusted_minor; + int i; + + adjusted_minor = Minor_Version(); + for (i = 0; i < 4; i++) { + if ( (adjusted_minor & 0x000f) != 0) { + break; + } + adjusted_minor >>= 4; + } + + sprintf(VersionName, "%x.%x", VerNum.Major_Version(), adjusted_minor); +#endif + + return (VersionName); + +} /* end of Version_Name */ + + +/*************************************************************************** + * VersionClass::Read_Text_String -- reads version # text string from disk * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * Don't call this function until the file system has been init'd! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Read_Text_String(void) +{ + RawFileClass file("VERSION.TXT"); + + if (file.Is_Available()) { + file.Read(VersionText, sizeof(VersionText)); + VersionText[sizeof(VersionText)-1] = '\0'; + while (VersionText[strlen(VersionText)-1] == '\r') { + VersionText[strlen(VersionText)-1] = '\0'; + } + } else { + VersionText[0] = '\0'; + } + +} /* end of Read_Text_String */ + + +/*************************************************************************** + * VersionClass::Version_Protocol -- returns default protocol for version * + * * + * INPUT: * + * version version # to look up * + * * + * OUTPUT: * + * protocol value to use for that version # * + * * + * WARNINGS: * + * none. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +CommProtocolType VersionClass::Version_Protocol(unsigned long version) +{ + int i,j; + + //------------------------------------------------------------------------ + // Compute # entries in the VersionProtocol table + //------------------------------------------------------------------------ + j = sizeof (VersionProtocol) / sizeof(VersionProtocolType); + + //------------------------------------------------------------------------ + // Search backwards through the table, finding the first entry for which + // the given version # is >= the table's; this is the range containing + // the given version number. + //------------------------------------------------------------------------ + for (i = j - 1; i >= 0; i--) { + if (version >= VersionProtocol[i].Version) { + return (VersionProtocol[i].Protocol); + } + } + + //------------------------------------------------------------------------ + // If no range was found for the given version, return the highest + // possible protocol. (If version clipping is being done properly, this + // case should never happen, but never say never.) + //------------------------------------------------------------------------ + return (VersionProtocol[j-1].Protocol); + +} /* end of Version_Protocol */ + + +/*************************************************************************** + * VersionClass::Init_Clipping -- Initializes version clipping * + * * + * Initializes the Min & Max clip version #'s to the min & max values * + * defined for this program. This sets the initial range for use by * + * the Clip_Version routine. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * none. * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +void VersionClass::Init_Clipping(void) +{ + MinClipVer = Min_Version(); + MaxClipVer = Max_Version(); + +} /* end of Init_Clipping */ + + +/*************************************************************************** + * VersionClass::Clip_Version -- "clips" the given version range * + * * + * This routine compares another program's supported min/max version * + * range with the range currently defined by 'MinClipVer' and 'MaxClipVer'.* + * If there is overlap in the two ranges, Min & MaxClipVer are adjusted * + * to the bounds of the overlap. The routine returns the largest version * + * number shared by the ranges (MaxClipVer). * + * * + * Thus, by calling Init_Clipping(), then a series of Clip_Version() calls,* + * a mutually-acceptable range of version #'s may be negotiated between * + * different versions of this program. The max shared version may then * + * be used to decide upon a communications protocol that all programs * + * support. * + * * + * INPUT: * + * minver min version to clip to * + * maxver max version to clip to * + * * + * OUTPUT: * + * highest clipped version # * + * 0 = given range is below our current range * + * 0xFFFFFFFF = given range is above our current range * + * * + * WARNINGS: * + * Be sure Init_Clipping() was called before performing a clipping * + * session. * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Clip_Version(unsigned long minver, + unsigned long maxver) +{ + //------------------------------------------------------------------------ + // If the given range is outside & above our own, return an error. + //------------------------------------------------------------------------ + if (minver > MaxClipVer) + return (0xffffffff); + + //------------------------------------------------------------------------ + // If the given range is outside & below our own, return an error. + //------------------------------------------------------------------------ + if (maxver < MinClipVer) + return (0); + + //------------------------------------------------------------------------ + // Clip the lower range value + //------------------------------------------------------------------------ + if (minver > MinClipVer) + MinClipVer = minver; + + //------------------------------------------------------------------------ + // Clip the upper range value + //------------------------------------------------------------------------ + if (maxver < MaxClipVer) + MaxClipVer = maxver; + + //------------------------------------------------------------------------ + // Return the highest version supported by the newly-adjusted range. + //------------------------------------------------------------------------ + return (MaxClipVer); + +} /* end of Clip_Version */ + + +/*************************************************************************** + * VersionClass::Min_Version -- returns lowest version # to connect to * + * * + * Returns the minimum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * min version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Min_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_VERSION + if ( Is_Counterstrike_Installed() ) { + return (MIN_VERSION - 1); + } + return (MIN_VERSION); +#else + if ( Is_Counterstrike_Installed() ){ + return (MIN_VERSION - CS_MINOR_VERSION_MODIFIER); + }else{ + return (MIN_VERSION); + } +#endif + +#endif // FIXIT_VERSION_3 + +#endif + +} /* end of Min_Version */ + + +/*************************************************************************** + * VersionClass::Max_Version -- returns highest version # to connect to * + * * + * Returns the maximum version # this program will connect to. * + * * + * If DEV_VERSION is defined, this routine returns the current version, so * + * this program will only connect to an exact copy of itself. * + * * + * INPUT: * + * none. * + * * + * OUTPUT: * + * max version # * + * * + * WARNINGS: * + * The DEV_VERSION version of this routine calls Version_Number(), so * + * don't call this routine until the file system has been initialized! * + * * + * HISTORY: * + * 10/26/1995 BRR : Created. * + *=========================================================================*/ +unsigned long VersionClass::Max_Version(void) +{ +#ifdef DEV_VERSION + return (Version_Number()); +#else + +#ifdef FIXIT_VERSION_3 + + // Note! I'm no longer using MIN_VERSION, MAX_VERSION, or VERSION_RA_300! + // But no time to do three full rebuilds right now, so I'm not deleting them from the header file... ajw + return GAME_VERSION; + +#else + +#ifdef FIXIT_CSII // checked - ajw + return (MAX_VERSION); +#else + #ifdef FIXIT_VERSION + if (Is_Counterstrike_Installed() == false) { + return (MAX_VERSION - CS_MINOR_VERSION_MODIFIER); + } else { + return (MAX_VERSION); + } + #else + if ( Is_Counterstrike_Installed() ){ + return (MAX_VERSION + CS_MINOR_VERSION_MODIFIER); + }else{ + return (MAX_VERSION); + } + #endif +#endif +#endif + +#endif // FIXIT_VERSION_3 + +} /* end of Max_Version */ + + +char const * Version_Name(void) +{ +#ifdef NEVER + static char buffer[32]; + + /* + ** Fetch the day and month components from the current + ** build date. + */ + static char * date = __DATE__; // format: Mmm dd yyyy + strupr(date); + char const * tok = strtok(date, " "); + static char const * months = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; + char const * ptr = strstr(months, tok); + int monthnum = 0; + if (ptr != NULL) { + monthnum = (((ptr - months) / 3) + 1); + } + + tok = strtok(NULL, " "); + int daynum = 0; + if (tok != NULL) { + daynum = atoi(tok); + } + + /* + ** Fetch the time components from the current build time. + */ + static char * time = __TIME__; // format: hh:mm:ss + tok = strtok(time, ": "); + int hournum = 0; + if (tok != NULL) { + hournum = atoi(tok); + } + + tok = strtok(NULL, ": "); + int minnum = 0; + if (tok != NULL) { + minnum = atoi(tok); + } + + sprintf(buffer, "%02d%02d%02d", monthnum, daynum, (hournum*4) + (minnum / 15)); + return(buffer); +#else + + static char buffer[128]; + + memset(buffer, '\0', sizeof(buffer)); + +#ifdef FIXIT_VERSION_3 + strcpy( buffer, "3.03" ); + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else // FIXIT_VERSION_3 + +#ifdef FIXIT_PATCH_108 + //strcpy(buffer, "1.08PE"); + strcpy(buffer, "1.08P"); + +#ifdef FIXIT_CSII + strcpy(buffer,"2.00"); +#ifdef DEV_VERSION + strcpy(buffer,VerNum.Version_Name()); +#endif +#ifdef DEV_VER_NAME + strcpy(buffer,__DATE__); // format: Mmm dd yyyy +#endif +#endif + + #ifdef ENGLISH + strcat(buffer, "E"); + #else + #ifdef GERMAN + strcat(buffer, "G"); + #else + #ifdef FRENCH + strcat(buffer, "F"); + #endif + #endif + #endif + +#else + strcpy(buffer, "1.07E"); +#endif + +#endif // FIXIT_VERSION_3 + + if (Is_Counterstrike_Installed ()){ + strcat (buffer, "CS"); + } + if (Is_Aftermath_Installed()) { + strcat (buffer, "AM"); + } + +#if(TEN) + strcat(buffer, "Ten"); // Ten version +#endif + +#if(MPATH) + strcat(buffer, "MPath"); // MPath version +#endif + + RawFileClass file("VERSION.TXT"); + if (file.Is_Available()) { + strcat(buffer, "\r"); + file.Read(&buffer[strlen(buffer)], 25); + } + return(buffer); +#endif +} diff --git a/code/redalert/version.h b/code/redalert/version.h new file mode 100644 index 0000000..eab28b1 --- /dev/null +++ b/code/redalert/version.h @@ -0,0 +1,199 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /counterstrike/VERSION.H 2 3/10/97 6:22p Steve_tall $ */ +/*************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + * * + * Project Name : Command & Conquer * + * * + * File Name : VERSION.H * + * * + * Programmer : Bill R. Randolph * + * * + * Start Date : 10/26/95 * + * * + * Last Update : October 26, 1995 [BRR] * + * * + * This class maintains version information, and communications protocol * + * information. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_RED_ALERT_104 0x00010000 //Shipped US version number +#define VERSION_RED_ALERT_107 0x00011000 //Shipped Counterstrike number +#define VERSION_RED_ALERT_106 0x00010500 //Patch without CS installed +#define VERSION_RED_ALERT_108 0x00011500 //Patch with CS installed +#define VERSION_AFTERMATH_CS 0x00011FFF //Aftermath with CS installed +#define VERSION_AFTERMATH 0x00012000 //Aftermath + +// Aftermath has, in a sense, used version 2.00. (Because of the text on title screen.) Call ourselves version 3. +#define VERSION_RA_300 0x00030000 // RA, CS, AM executables unified into one. All are now the same version. -ajw +// It seems that extra information, that didn't belong there, was being stuffed into version number. Namely, whether or not +// Counterstrike is installed. I'm going to change things back to the way they should be, as I see it. Version will describe +// the version of the executable only. When it comes to communicating whether or not a player has expansions present, separate +// data will be transmitted. + +// However, having said that, a caveat. I'm going to have to use the same communication method that was used previously, because +// I need to have prior versions of the game recognize that they can't play against this version. What I'll do is encode +// "does player have aftermath" (which is actually the only fact that matters, in multiplayer) in the communicated version +// number, as a high bit set/unset. This version of the game will receive this communicated value and pull out the Aftermath +// bit. Older version will reject us as a possible opponent, because, whether or not AM is installed, our version number will +// be too high for them. + +// These horrible things are no longer used. ajw +#define CS_MAJOR_VERSION_MODIFIER 0x0000 +#define CS_MINOR_VERSION_MODIFIER 0x1000 + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- +typedef enum CommProtocolEnum { + COMM_PROTOCOL_SINGLE_NO_COMP = 0, // single frame with no compression + COMM_PROTOCOL_SINGLE_E_COMP, // single frame with event compression + COMM_PROTOCOL_MULTI_E_COMP, // multiple frame with event compression + COMM_PROTOCOL_COUNT, + DEFAULT_COMM_PROTOCOL = COMM_PROTOCOL_MULTI_E_COMP +} CommProtocolType; + +typedef struct { + unsigned long Version; + CommProtocolType Protocol; +} VersionProtocolType; + +class VersionClass { + public: + //..................................................................... + // Constructor/Destructor + //..................................................................... + VersionClass(void); + virtual ~VersionClass() {}; + + //..................................................................... + // These routines return the current version number. The long version + // number contains the major version in the high word, and the minor + // version in the low word. They should be interpreted in hex. + //..................................................................... + unsigned long Version_Number(void); + unsigned short Major_Version(void); + unsigned short Minor_Version(void); + + //..................................................................... + // Retrieves a pointer to the version # as a text string (#.#), with + // the trailing 0's trimmed off. + //..................................................................... + char *Version_Name(void); + + //..................................................................... + // Retrieves a pointer to the current version text. + //..................................................................... + char *Version_Text() {return (VersionText);} + + //..................................................................... + // Returns the default comm protocol for a given version number. + //..................................................................... + CommProtocolType Version_Protocol(unsigned long version); + + //..................................................................... + // These routines support "version clipping". + //..................................................................... + void Init_Clipping(void); + unsigned long Clip_Version(unsigned long minver, unsigned long maxver); + unsigned long Get_Clipped_Version(void) {return (MaxClipVer);} + + //..................................................................... + // These routines return the theoretical lowest & highest version #'s + // that this program will connect to; this does not take any previous + // version clipping into account. + //..................................................................... + unsigned long Min_Version(void); + unsigned long Max_Version(void); + + private: + //..................................................................... + // Fills in a 'VersionText' with a descriptive version name. + //..................................................................... + void Read_Text_String(void); + + //..................................................................... + // These values define the major & minor version #'s for the current + // version. Change these values to change the game's version #! + //..................................................................... + enum VersionEnum { +#ifdef FIXIT_VERSION_3 + MAJOR_VERSION = 0x0003, + MINOR_VERSION = 0x0000 +#else + MAJOR_VERSION = 0x0001, + MINOR_VERSION = 0x2000 +#endif + }; + + //..................................................................... + // These values control which other versions this program will connect + // to. Keep them current! + // If CHEAT is defined, the program will only connect to itself; these + // values aren't used. + //..................................................................... + enum VersionRangeEnum { +#ifdef FIXIT_VERSION_3 + // ajw - We can only play against same version. + MIN_VERSION = VERSION_RA_300, + MAX_VERSION = VERSION_RA_300 +#else + MIN_VERSION = VERSION_RED_ALERT_104, //0x00010000, // Version: 1.0 + MAX_VERSION = VERSION_AFTERMATH //0x00012000 // Version: 1.2 +#endif + }; + + //..................................................................... + // This is the program's version number, stored internally. + //..................................................................... + unsigned long Version; + unsigned short MajorVer; + unsigned short MinorVer; + + //..................................................................... + // This array is used for formatting the version # as a string + //..................................................................... + char VersionName[30]; + + //..................................................................... + // This array contains special version labels (such as "Beta"), stored + // in the file VERSION.TXT. If the file isn't present, no label is + // shown. + //..................................................................... + char VersionText[16]; + + //..................................................................... + // Values used for "Version Clipping" + //..................................................................... + unsigned long MinClipVer; + unsigned long MaxClipVer; + + //..................................................................... + // Bitfield Flags + // IsInitialized: is set if the VERSION.TXT file has been read + //..................................................................... + unsigned VersionInit : 1; + unsigned MajorInit : 1; + unsigned MinorInit : 1; + unsigned TextInit : 1; +}; +#endif +/************************** end of version.h *******************************/ + diff --git a/code/redalert/vessel.cpp b/code/redalert/vessel.cpp new file mode 100644 index 0000000..08163f2 --- /dev/null +++ b/code/redalert/vessel.cpp @@ -0,0 +1,2425 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VESSEL.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VESSEL.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : July 31, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * VesselClass::Draw_It -- Draws the vessel. * + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vesse* + * VesselClass::Init -- Initialize the vessel heap system. * + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * operator delete -- Deletes a vessel's memory block. * + * operator new -- Allocates a vessel object memory block. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*********************************************************************************************** + * VesselClass::VesselClass -- Constructor for vessel class objects. * + * * + * This is the normal constructor for vessel class objects. It will set up a vessel that * + * is valid excepting that it won't be placed on the map. * + * * + * INPUT: classid -- The type of vessel this will be. * + * * + * house -- The owner of this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::VesselClass(VesselType classid, HousesType house) : + DriveClass(RTTI_VESSEL, Vessels.ID(this), house), + Class(VesselTypes.Ptr((int)classid)), + IsToSelfRepair(false), + IsSelfRepairing(false), + DoorShutCountDown(0), + PulseCountDown(0), + SecondaryFacing(PrimaryFacing) +{ + House->Tracking_Add(this); + + /* + ** The ammo member is actually part of the techno class, but must be initialized + ** manually here because this is where we first have access to the class pointer. + */ + Ammo = Class->MaxAmmo; + + /* + ** For two shooters, clear out the second shot flag -- it will be set the first time + ** the object fires. For non two shooters, set the flag since it will never be cleared + ** and the second shot flag tells the system that normal rearm times apply -- this is + ** what is desired for non two shooters. + */ + IsSecondShot = !Class->Is_Two_Shooter(); + Strength = Class->MaxStrength; + + /* + ** The techno class cloakabilty flag is set according to the type + ** class cloakability flag. + */ + IsCloakable = Class->IsCloakable; + + /* + ** Keep count of the number of units created. + */ +// if (Session.Type == GAME_INTERNET) { +// House->UnitTotals->Increment_Unit_Total((int)classid); +// } +} + + +/*********************************************************************************************** + * VesselClass::~VesselClass -- Destructor for vessel objects. * + * * + * The destructor will destroy the vessel and ensure that it is properly removed from the * + * game engine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +VesselClass::~VesselClass(void) +{ + if (GameActive && Class.Is_Valid()) { + + /* + ** Remove this member from any team it may be associated with. This must occur at the + ** top most level of the inheritance hierarchy because it may call virtual functions. + */ + if (Team.Is_Valid()) { + Team->Remove(this); + Team = NULL; + } + + House->Tracking_Remove(this); + + /* + ** If there are any cargo members, delete them. + */ + while (Is_Something_Attached()) { + delete Detach_Object(); + } + + Limbo(); + } + ID = -1; +} + + +/*********************************************************************************************** + * operator new -- Allocates a vessel object memory block. * + * * + * This routine is used to allocate a block of memory from the vessel heap. If there is * + * no more space in the heap, then this routine will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the pointer to the allocated block of memory. * + * * + * WARNINGS: This routine could return NULL. * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void * VesselClass::operator new(size_t) +{ + void * ptr = Vessels.Alloc(); + if (ptr != NULL) { + ((VesselClass *)ptr)->Set_Active(); + } + return(ptr); +} + + +/*********************************************************************************************** + * operator delete -- Deletes a vessel's memory block. * + * * + * This overloaded delete operator will return the vessel's memory back to the pool of * + * memory used for vessel allocation. * + * * + * INPUT: ptr -- Pointer to the vessel's memory block. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::operator delete(void * ptr) +{ + if (ptr != NULL) { + assert(((VesselClass *)ptr)->IsActive); + ((VesselClass *)ptr)->IsActive = false; + } + Vessels.Free((VesselClass *)ptr); +} + + +/*********************************************************************************************** + * VesselClass::Class_Of -- Fetches a reference to the vessel's class data. * + * * + * This routine will return with a reference to the static class data for this vessel. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a reference to the class data structure associated with this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +ObjectTypeClass const & VesselClass::Class_Of(void) const +{ + assert(IsActive); + + return(*Class); +} + + +/*********************************************************************************************** + * VesselClass::Can_Enter_Cell -- Determines if the vessel can enter the cell specified. * + * * + * This routine is used by find path and other movement logic to determine if this * + * vessel can enter the cell specified. * + * * + * INPUT: cell -- The cell to check this vessel against. * + * * + * OUTPUT: Returns with the movement restriction associated with movement into this object. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +MoveType VesselClass::Can_Enter_Cell(CELL cell, FacingType ) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if ((unsigned)cell >= MAP_CELL_TOTAL) return(MOVE_NO); + + CellClass const * cellptr = &Map[cell]; + + /* + ** Moving off the edge of the map is not allowed unless + ** this is a loaner vehicle. + */ + if (!ScenarioInit && !Map.In_Radar(cell) && !Is_Allowed_To_Leave_Map()) { + return(MOVE_NO); + } + + MoveType retval = MOVE_OK; + + /* + ** If there is blocking terrain (such as ice), then the vessel + ** can't move there. + */ + if (cellptr->Cell_Terrain() != NULL) { + return(MOVE_NO); + } + + /* + ** If the cell is out and out impassable because of underlying terrain, then + ** return this immutable fact. + */ + if (Ground[cellptr->Land_Type()].Cost[Class->Speed] == 0) { + return(MOVE_NO); + } + + /* + ** If some allied object has reserved the cell, then consider the cell + ** as blocked by a moving object. + */ + if (cellptr->Flag.Composite) { + + if (cellptr->Flag.Occupy.Building) { + return(MOVE_NO); + } + + TechnoClass * techno = cellptr->Cell_Techno(); + if (techno != NULL && techno->Cloak == CLOAKED && !House->Is_Ally(techno)) { + return(MOVE_CLOAK); + } + + /* + ** If reserved by a vehicle, then consider this blocked terrain. + */ + if (cellptr->Flag.Occupy.Vehicle) { + retval = MOVE_MOVING_BLOCK; + } + } + + /* + ** Return with the most severe reason why this cell would be impassable. + */ + return(retval); +} + + +/*********************************************************************************************** + * VesselClass::Shape_Number -- Calculates the shape number for the ship body. * + * * + * This routine will return with the shape number to use for the ship's body. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the shape number to use for the ship's body when drawing. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/31/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Shape_Number(void) const +{ + /* + ** For eight facing units, adjust the facing number accordingly. + */ + FacingType facing = Dir_Facing(PrimaryFacing.Current()); + + int shapenum = UnitClass::BodyShape[Dir_To_16(PrimaryFacing)*2]>>1; + + /* + ** Special case code for transport. The north/south facing is in frame + ** 0. The east/west facing is in frame 3. + */ + if (*this == VESSEL_TRANSPORT) { + shapenum = 0; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + shapenum = 0; + } +#endif + /* + ** Door opening and closing animation stage check. + */ + if (!Is_Door_Closed()) { + shapenum = Door_Stage(); + } + + return(shapenum); +} + + +/*********************************************************************************************** + * VesselClass::Draw_It -- Draws the vessel. * + * * + * Draws the vessel on the tactical display. This routine is called by the map rendering * + * process to display the vessel. * + * * + * INPUT: x,y -- The pixel coordinate to draw this vessel at. * + * * + * window-- The window to base clipping and coordinates upon when drawing. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/14/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Draw_It(int x, int y, WindowNumberType window) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Verify the legality of the unit class. + */ + void const * shapefile = Get_Image_Data(); + if (shapefile == NULL) return; + + /* + ** Need to know the shape name for the overlay now. ST - 8/19/2019 1:37PM + */ + const char *turret_shape_name = NULL; + + /* + ** If drawing of this unit is not explicitly prohibited, then proceed + ** with the render process. + */ + const bool is_hidden = (Visual_Character() == VISUAL_HIDDEN) && (window != WINDOW_VIRTUAL); + if (!is_hidden) { + int facing = Dir_To_32(PrimaryFacing); + int tfacing = Dir_To_32(SecondaryFacing); + DirType rotation = DIR_N; + int scale = 0x0100; + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + Techno_Draw_Object(shapefile, Shape_Number(), x, y, window, rotation, scale); + + /* + ** If there is a turret, then it must be rendered as well. This may include + ** firing animation if required. + */ + if (Class->IsTurretEquipped) { + int xx = x; + int yy = y; + + /* + ** Determine which turret shape to use. This depends on if there + ** is any firing animation in progress. + */ + int shapenum = TechnoClass::BodyShape[tfacing]+32; + DirType turdir = DirType(Dir_To_16(PrimaryFacing)*16); + + switch (Class->Type) { + case VESSEL_CA: + turret_shape_name = "TURR"; + shapefile = Class->TurretShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM + //Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); + xx = x; + yy = y; + turdir = DirType(Dir_To_16(PrimaryFacing+DIR_S)*16); + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_DD: + turret_shape_name = "SSAM"; + shapefile = Class->SamShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + case VESSEL_PT: + turret_shape_name = "MGUN"; + shapefile = Class->MGunShapes; + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + + default: + shapenum = TechnoClass::BodyShape[Dir_To_32(SecondaryFacing)]; + Class->Turret_Adjust(turdir, xx, yy); + break; + } + + /* + ** Actually perform the draw. Overlay an optional shimmer effect as necessary. + */ + // Add shape file name forl new shape draw intercept. ST - 8/19/2019 1:42PM + if (turret_shape_name) { + Techno_Draw_Object_Virtual(shapefile, shapenum, xx, yy, window, DIR_N, 0x0100, turret_shape_name); + } else { + Techno_Draw_Object(shapefile, shapenum, xx, yy, window); + } + } + } + + DriveClass::Draw_It(x, y, window); + + /* + ** Patch so the transport will draw its passengers on top of itself. + */ + if (!Is_Door_Closed() && IsTethered && In_Radio_Contact() && !Contact_With_Whom()->IsInLimbo) { + TechnoClass * contact = Contact_With_Whom(); + assert(contact->IsActive); + + int xxx = x + ((int)Lepton_To_Pixel((int)Coord_X(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_X(Render_Coord()))); + int yyy = y + ((int)Lepton_To_Pixel((int)Coord_Y(contact->Render_Coord())) - (int)Lepton_To_Pixel((int)Coord_Y(Render_Coord()))); + contact->Draw_It(xxx, yyy, window); + contact->IsToDisplay = false; + } +} + + +#ifdef CHEAT_KEYS +/*********************************************************************************************** + * VesselClass::Debug_Dump -- Dumps the vessel status information to the mono monitor. * + * * + * This routine will display the vessel's status information. The information is dumped to * + * the monochrome monitor. * + * * + * INPUT: mono -- Pointer to the monochrome screen that the information will be displayed * + * to. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Debug_Dump(MonoClass * mono) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + mono->Set_Cursor(0, 0); + mono->Print(Text_String(TXT_DEBUG_SHIP)); + mono->Set_Cursor(47, 5);mono->Printf("%02X:%02X", SecondaryFacing.Current(), SecondaryFacing.Desired()); + + mono->Fill_Attrib(66, 13, 12, 1, IsSelfRepairing ? MonoClass::INVERSE : MonoClass::NORMAL); + mono->Fill_Attrib(66, 14, 12, 1, IsToSelfRepair ? MonoClass::INVERSE : MonoClass::NORMAL); + DriveClass::Debug_Dump(mono); +} +#endif + + +/*********************************************************************************************** + * VesselClass::Overlap_List -- Fetches the overlap list for this vessel object. * + * * + * This routine will fetch the overlap list for this vessel type. It takes into * + * consideration any movement the vessel may be doing. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the overlap list for this vessel. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + *=============================================================================================*/ +short const * VesselClass::Overlap_List(bool redraw) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +#ifdef PARTIAL + if (Height == 0 && redraw && Class->DimensionData != NULL) { + Rect rect; + int shapenum = Shape_Number(); + if (Class->DimensionData[shapenum].Is_Valid()) { + rect = Class->DimensionData[shapenum]; + } else { + rect = Class->DimensionData[shapenum] = Shape_Dimensions(Get_Image_Data(), shapenum); + } + + if (IsSelected) { + rect = Union(rect, Rect(-32, 32, 64, 64)); + } + + return(Coord_Spillage_List(Coord, rect, true)); + } +#else + redraw = redraw; +#endif + + return(Coord_Spillage_List(Coord, 56)+1); +} + + +/*********************************************************************************************** + * VesselClass::AI -- Handles the AI processing for vessel objects. * + * * + * This routine is called once for each vessel object during each main game loop. All * + * normal AI processing is handled here. This includes dispatching and maintaining any * + * processing that is specific to vessel objects. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/20/1996 JLB : Created. * + * 07/16/1996 JLB : Prefers anti-sub weapons if firing on subs. * + *=============================================================================================*/ +void VesselClass::AI(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (Mission == MISSION_NONE && MissionQueue == MISSION_NONE) { + Enter_Idle_Mode(); + } + + /* + ** HACK ALERT: + ** If the ship finds itself in a hunt order but it has no weapons, then tell it + ** to sail off the map instead. + */ + if (Mission == MISSION_HUNT && !Is_Weapon_Equipped()) { + Assign_Mission(MISSION_RETREAT); + } + + /* + ** Act on new orders if the unit is at a good position to do so. + */ + if (!IsDriving && Is_Door_Closed() /*Mission != MISSION_UNLOAD*/) { + Commence(); + } + +#ifndef CLIPDRAW + if (Map.In_View(Coord_Cell(Center_Coord()))) { + Mark(MARK_CHANGE); + } +#endif + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +// Re-stock the ammo of any on-board helicopters on an aircraft carrier. + if (*this == VESSEL_CARRIER && How_Many()) { + if (!MoebiusCountDown) { + MoebiusCountDown = Rule.ReloadRate * TICKS_PER_MINUTE; + ObjectClass *obj = Attached_Object(); + while (obj) { + long bogus; + ((AircraftClass *)obj)->Receive_Message(this,RADIO_RELOAD,bogus); + obj = (obj->Next); + } + } + } +#endif + /* + ** Process base class AI routine. If as a result of this, the vessel gets + ** destroyed, then detect this fact and bail early. + */ + DriveClass::AI(); + if (!IsActive) { + return; + } + + /* + ** Handle body and turret rotation. + */ + Rotation_AI(); + + /* + ** Handle any combat processing required. + */ + Combat_AI(); + + /* + ** Delete this unit if it finds itself off the edge of the map and it is in + ** guard or other static mission mode. + */ + if (Edge_Of_World_AI()) { + return; + } + + if (Class->Max_Passengers() > 0) { + + /* + ** Double check that there is a passenger that is trying to load or unload. + ** If not, then close the door. + */ + if (!Is_Door_Closed() && Mission != MISSION_UNLOAD && Transmit_Message(RADIO_TRYING_TO_LOAD) != RADIO_ROGER && !(long)DoorShutCountDown) { + LST_Close_Door(); + } + } + + /* + ** Don't start a new mission unless the vehicle is in the center of + ** a cell (not driving) and the door (if any) is closed. + */ + if (!IsDriving && Is_Door_Closed()/*&& Mission != MISSION_UNLOAD*/) { + Commence(); + } + + /* + ** Do a step of repair here, if appropriate. + */ + Repair_AI(); +} + + +/*********************************************************************************************** + * VesselClass::Per_Cell_Process -- Performs once-per-cell action. * + * * + * This routine is called when the vessel travels one cell. It handles any processes that * + * must occur on a per-cell basis. * + * * + * INPUT: why -- Specifies the circumstances under which this routine was called. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 03/19/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Per_Cell_Process(PCPType why) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + BStart(BENCH_PCP); + + if (why == PCP_END) { + CELL cell = Coord_Cell(Coord); + + /* + ** The unit performs looking around at this time. If the + ** unit moved further than one square during the last track + ** move, don't do an incremental look. Do a full look around + ** instead. + */ + Look(!IsPlanningToLook); + IsPlanningToLook = false; + + if (IsToSelfRepair) { + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CELL cell = Coord_Cell(Adjacent_Cell(Center_Coord(), face)); + SmartPtr whom; + whom = Map[cell].Cell_Building(); + if (whom != NULL && ((*whom == STRUCT_SHIP_YARD) || (*whom == STRUCT_SUB_PEN)) ) { + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_REPAIRING); + if (IsOwnedByPlayer) Speak(VOX_REPAIRING, House); + + IsSelfRepairing = true; + IsToSelfRepair = false; + break; + } + } + } + + /* + ** If this is a loaner unit and is is off the edge of the + ** map, then it gets eliminated. + */ + if (Edge_Of_World_AI()) { + BEnd(BENCH_PCP); + return; + } + } + + if (IsActive) { + DriveClass::Per_Cell_Process(why); + } + BEnd(BENCH_PCP); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines what action would occur if clicked on object. * + * * + * Use this function to determine what action would likely occur if the specified object * + * were clicked on while this unit was selected as current. This function controls, not * + * only the action to perform, but indirectly controls the cursor shape to use as well. * + * * + * INPUT: object -- The object that to check for against "this" object. * + * * + * OUTPUT: Returns with the default action to perform. If no clear action can be determined, * + * then ACTION_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(ObjectClass const * object) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(object); + + if (action == ACTION_SELF) { + if (Class->Max_Passengers() == 0 || !How_Many() ) { + action = ACTION_NONE; + } else { +// check to see if the transporter can unload. + bool found = 0; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this != VESSEL_CARRIER) +#endif + for (FacingType face = FACING_N; face < FACING_COUNT && !found; face++) { + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + CellClass * cell = &Map[cellnum]; + if (Map.In_Radar(cellnum)) { + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + continue; + } else { + found = true; + } + } + } + if (!found) { + action = ACTION_NONE; + } + } + } + + /* + ** Special return to friendly repair factory action. + */ + if (House->IsPlayerControl && action == ACTION_SELECT && object->What_Am_I() == RTTI_BUILDING) { + BuildingClass * building = (BuildingClass *)object; + + if (building->Class->ToBuild == RTTI_VESSELTYPE && building->House->Is_Ally(this)) { + action = ACTION_ENTER; + } + } +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (action == ACTION_ATTACK && object->What_Am_I() == RTTI_VESSEL && + (*this == VESSEL_MISSILESUB || *this == VESSEL_CA) ) { + action = ACTION_NOMOVE; + } +#endif + /* + ** If it doesn't know what to do with the object, then just + ** say it can't move there. + */ + if (action == ACTION_NONE) action = ACTION_NOMOVE; + + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Intercepts the active click to see if deployment is possib* + * * + * This routine intercepts the active click operation. It check to see if this is a self * + * deployment request (MCV's have this ability). If it is, then the object is initiated * + * to self deploy. In the other cases, it passes the operation down to the lower * + * classes for processing. * + * * + * INPUT: action -- The action requested of the unit. * + * * + * object -- The object that the mouse pointer is over. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, ObjectClass * object) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + +// if (action != What_Action(object)) { + action = What_Action(object); + switch (action) { + case ACTION_ENTER: + action = ACTION_MOVE; + // BRR 10/18/96 IsToSelfRepair = true; + break; + + default: +// action = ACTION_NONE; + break; + } +// } +// if (action == ACTION_ENTER) { + // BRR 10/18/96 IsToSelfRepair = true; +// action = ACTION_MOVE; +// } else { +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = IsToSelfRepair = false; +// } +// } + + DriveClass::Active_Click_With(action, object); +} + + +/*********************************************************************************************** + * VesselClass::Active_Click_With -- Performs specified action on specified cell. * + * * + * This routine is called when the mouse has been clicked over a cell and this unit must * + * now respond. Notice that this is merely a placeholder function that exists because there * + * is another function of the same name that needs to be overloaded. C++ has scoping * + * restrictions when there are two identically named functions that are overridden in * + * different classes -- it handles it badly, hence the existence of this routine. * + * * + * INPUT: action -- The action to perform on the cell specified. * + * * + * cell -- The cell that the action is to be performed on. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/16/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Active_Click_With(ActionType action, CELL cell) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + // BRR 10/18/96 IsToSelfRepair = false; +// if (action != ACTION_NONE) { + // BRR 10/18/96 IsSelfRepairing = false; +// } + DriveClass::Active_Click_With(action, cell); +} + + +/*********************************************************************************************** + * VesselClass::Take_Damage -- Assign damage to the vessel. * + * * + * This routine is called to apply damage to this vessel. The amount and type of damage * + * to apply is passed as parameters. This routine could end up destroying the vessel. * + * * + * INPUT: damage -- Reference to the amount of damage to apply to this vessel. The damage * + * value will be adjusted so that the actual damage applied will be * + * stored into this variable for possible subsequent examination. * + * * + * distance -- The distance from the center of the damage to the vessel itself. * + * * + * warhead -- The warhead type of damage to apply. * + * * + * source -- The perpetrator of this damage. Knowing who was responsible allows * + * retaliation logic. * + * * + * forced -- Is this damage forced upon the vessel by some supernatural means? * + * * + * OUTPUT: Returns with the result of the damage applied. This enumeration indicates the * + * general effect of the damage. Examine this return value to see if the vessel * + * has been destroyed. * + * * + * WARNINGS: The vessel could be destroyed by the call to this routine! * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +ResultType VesselClass::Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source, bool forced) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ResultType res = RESULT_NONE; + + /* + ** In order for a this to be damaged, it must either be a unit + ** with a crew or a sandworm. + */ + res = FootClass::Take_Damage(damage, distance, warhead, source, forced); + + if (res == RESULT_DESTROYED) { + Death_Announcement(source); + if (Class->Explosion != ANIM_NONE) { + AnimType anim = Class->Explosion; + + new AnimClass(anim, Coord); + + /* + ** Very strong units that have an explosion will also rock the + ** screen when they are destroyed. + */ + if (Class->MaxStrength > 400) { + int shakes = Class->MaxStrength / 150; + Shake_The_Screen(shakes, Owner()); + if (source && Owner() != source->Owner()) { + Shake_The_Screen(shakes, source->Owner()); + } + } + } + + /* + ** Possibly have the crew member run away. + */ + Mark(MARK_UP); + while (Is_Something_Attached()) { + FootClass * object = Detach_Object(); + + /* + ** Only infantry can run from a destroyed vehicle. Even then, it is not a sure + ** thing. + */ + object->Record_The_Kill(source); + delete object; + } + + /* + ** Finally, delete the vehicle. + */ + delete this; + + } else { + + /* + ** When damaged and below half strength, start smoking if + ** it isn't already smoking (and it's not a submarine). + */ +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS && *this != VESSEL_MISSILESUB) ) { +#else + if (Health_Ratio() <= Rule.ConditionYellow && !IsAnimAttached && (*this != VESSEL_SS) ) { +#endif + AnimClass * anim = new AnimClass(ANIM_SMOKE_M, Coord_Add(Coord, XYP_Coord(0, -8))); + if (anim != NULL) anim->Attach_To(this); + } + } + return(res); +} + + +/*********************************************************************************************** + * VesselClass::Can_Fire -- Determines if this vessel can fire its weapon. * + * * + * This routine is used to determine if this vessel can fire its weapon at the target * + * specified. * + * * + * INPUT: target -- The target candidate to determine if firing upon is valid. * + * * + * which -- Which weapon to use when considering the candidate as a potential * + * target. * + * * + * OUTPUT: Returns with the fire error type. This enum indicates if the vessel and fire. If * + * it can't fire, then the enum indicates why. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +FireErrorType VesselClass::Can_Fire(TARGET target, int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + DirType dir; // The facing to impart upon the projectile. + int diff; + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + if(!How_Many() || Arm) { + return(FIRE_REARM); + } else { + return(FIRE_OK); + } + } +#endif + FireErrorType fire = DriveClass::Can_Fire(target, which); +if(*this==VESSEL_DD) { +Mono_Set_Cursor(0,0); +} + if (fire == FIRE_OK || fire == FIRE_CLOAKED) { + WeaponTypeClass const * weapon = (which == 0) ? Class->PrimaryWeapon : Class->SecondaryWeapon; + + /* + ** Ensure that a torpedo will never be fired upon a non naval target. + ** Unless that non-naval target is a naval building (sub pen/ship yard) + */ + bool isseatarget = Is_Target_Vessel(target); + bool isbridgetarget = false; + if (weapon->Bullet->IsSubSurface) { + isbridgetarget = Is_Target_Cell(target); // enable shooting at bridges + isseatarget |= isbridgetarget; + } + BuildingClass * bldg = ::As_Building(target); + if (bldg != NULL && bldg->Class->Speed == SPEED_FLOAT) { + isseatarget = true; + } + + dir = Direction(target); + + if (weapon->Bullet->IsSubSurface) { + if (!isseatarget && Is_Target_Object(target)) { + return(FIRE_CANT); + } + + /* + ** If it's a torpedo, let's check line-of-sight to make sure that + ** there's only water squares between us and the target. + */ + ObjectClass * obj = As_Object(target); + COORDINATE coord = Center_Coord(); + if (obj != NULL) { + int totaldist = ::Distance(coord, obj->Center_Coord()); + while (totaldist > CELL_LEPTON_W) { + coord = Coord_Move(coord, dir, CELL_LEPTON_W); + if (Map[coord].Land_Type() != LAND_WATER) { + if (!isbridgetarget) { + return(FIRE_RANGE); + } + } + + /* + ** Check for friendly boats in the way. + */ + TechnoClass * tech = Map[coord].Cell_Techno(); + if (tech != NULL && tech != this && House->Is_Ally(tech)) { + return(FIRE_RANGE); + } + totaldist -= CELL_LEPTON_W; + } + } + } + + /* + ** Depth charges are only good against submarines. + */ + if (weapon->Bullet->IsAntiSub) { + if (!isseatarget) { + return(FIRE_CANT); + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) != VESSEL_SS && *As_Vessel(target) != VESSEL_MISSILESUB) ) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) != VESSEL_SS) { +#endif + if (!Is_Target_Vessel(target) || !weapon->Bullet->IsSubSurface) { + return(FIRE_CANT); + } + } + } + } else { +#ifdef FIXIT_CSII // checked - ajw 9/28/98 + if (Is_Target_Vessel(target) && (*As_Vessel(target) == VESSEL_SS || *As_Vessel(target) == VESSEL_MISSILESUB)) { +#else + if (Is_Target_Vessel(target) && *As_Vessel(target) == VESSEL_SS) { +#endif + return(FIRE_CANT); + } + } + + /* + ** If this unit cannot fire while moving, then bail. + */ + if (!Class->IsTurretEquipped && Target_Legal(NavCom)) { + return(FIRE_MOVING); + } + + /* + ** If the turret is rotating and the projectile isn't a homing type, then + ** firing must be delayed until the rotation stops. + */ + if (!IsFiring && IsRotating && weapon->Bullet->ROT == 0) { + return(FIRE_ROTATING); + } + + /* + ** Determine if the turret facing isn't too far off of facing the target. + */ + if (Class->IsTurretEquipped) { + diff = SecondaryFacing.Difference(dir); + } else { + diff = PrimaryFacing.Difference(dir); + } + diff = ABS(diff); + + if (weapon->Bullet->ROT != 0) { + diff >>= 2; + } + if (diff > 8) { + return(FIRE_FACING); + } + } + return(fire); +} + + +/*********************************************************************************************** + * VesselClass::Fire_Coord -- Fetches the coordinate the firing originates from. * + * * + * This routine is called to determine the coordinate that a fired projectile will * + * originate from. * + * * + * INPUT: which -- Which weapon is this query directed at? * + * * + * OUTPUT: Returns with the coordinate where a projectile would appear if it were fired. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +COORDINATE VesselClass::Fire_Coord(int which) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + COORDINATE coord = Center_Coord(); + + if (*this == VESSEL_CA) { + if (IsSecondShot) { + coord = Coord_Move(coord, PrimaryFacing + DIR_S, 0x0100); + } else { + coord = Coord_Move(coord, PrimaryFacing, 0x0100); + } + coord = Coord_Move(coord, DIR_N, 0x0030); + coord = Coord_Move(coord, Turret_Facing(), 0x0040); + return(coord); + } + + if (*this == VESSEL_PT) { + coord = Coord_Move(coord, PrimaryFacing, 0x0080); + coord = Coord_Move(coord, DIR_N, 0x0020); + coord = Coord_Move(coord, Turret_Facing(), 0x0010); + return(coord); + } + + return(DriveClass::Fire_Coord(which)); +} + + +/*********************************************************************************************** + * VesselClass::Init -- Initialize the vessel heap system. * + * * + * This routine is used to clear out the vessel heap. It is called whenever a scenario is * + * being initialized prior to scenario or saved game loading. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: All vessel objects are invalid after this routine is called. * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Init(void) +{ + Vessels.Free_All(); +} + + +/*********************************************************************************************** + * VesselClass::Greatest_Threat -- Determines the greatest threat (best target) for the vessel * + * * + * This routine is used by ships to determine what target they should go after. * + * * + * INPUT: threat -- The threat type that this ship should go after (as determined by the * + * team mission or general self defense principles). * + * * + * OUTPUT: Returns with the target that this ship should attack. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/13/1996 JLB : Created. * + *=============================================================================================*/ +TARGET VesselClass::Greatest_Threat(ThreatType threat) const +{ + if (*this == VESSEL_SS) { + threat = threat & ThreatType(THREAT_RANGE|THREAT_AREA); + threat = threat | THREAT_BOATS; + + //BG: get subs to attack buildings also. + threat = threat | THREAT_BUILDINGS; + threat = threat | THREAT_FACTORIES; + } else { + if ((threat & (THREAT_GROUND|THREAT_POWER|THREAT_FACTORIES|THREAT_TIBERIUM|THREAT_BASE_DEFENSE|THREAT_BOATS)) == 0) { + if (Class->PrimaryWeapon != NULL) { + threat = threat | Class->PrimaryWeapon->Allowed_Threats(); + } + + if (Class->SecondaryWeapon != NULL) { + threat = threat | Class->SecondaryWeapon->Allowed_Threats(); + } + +// threat = threat | THREAT_GROUND | THREAT_BOATS; + } + + // Cruisers can never hit infantry anyway, so take 'em out of the list + // of possible targets. + if (*this == VESSEL_CA) { + threat = (ThreatType) (threat & (~THREAT_INFANTRY)); + } + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (*this == VESSEL_CARRIER) { + return(TARGET_NONE); + } +#endif + return(FootClass::Greatest_Threat(threat)); +} + + +/*********************************************************************************************** + * VesselClass::Enter_Idle_Mode -- Causes the vessel to enter its default idle mode. * + * * + * This routine is called when the vessel is finished with what it is doing, but the next * + * action is not known. This routine will determine what is the appropriate course of * + * action for this vessel and then start it doing that. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Enter_Idle_Mode(bool ) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + MissionType order = MISSION_GUARD; + + /* + ** A movement mission without a NavCom would be pointless to have a radio contact since + ** no radio coordination occurs on a just a simple movement mission. + */ + if (Mission == MISSION_MOVE && !Target_Legal(NavCom)) { + Transmit_Message(RADIO_OVER_OUT); + } + + Handle_Navigation_List(); + if (Target_Legal(NavCom)) { + order = MISSION_MOVE; + } else { + + if (Class->PrimaryWeapon == NULL) { + if (IsALoaner && Class->Max_Passengers() > 0 && Is_Something_Attached() && !Team) { + order = MISSION_UNLOAD; + } else { + order = MISSION_GUARD; + Assign_Target(TARGET_NONE); + Assign_Destination(TARGET_NONE); + } + + } else { + + if (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA || MissionControl[Mission].IsParalyzed || MissionControl[Mission].IsZombie) { + return; + } + + if (House->IsHuman || Team.Is_Valid()) { + order = MISSION_GUARD; + } else { + if (House->IQ < Rule.IQGuardArea) { + order = MISSION_GUARD; + } else { + order = MISSION_GUARD_AREA; + } + } + } + } + Assign_Mission(order); +} + + +/*********************************************************************************************** + * VesselClass::Receive_Message -- Handles receiving a radio message. * + * * + * This is the handler function for when a vessel receives a radio * + * message. Typical use of this is when a unit unloads from a lst * + * class so that clearing of the transport is successful. * + * * + * INPUT: from -- Pointer to the originator of the message. * + * * + * message -- The radio message received. * + * * + * param -- Reference to an optional parameter the might be needed to return * + * information back to the originator of the message. * + * * + * OUTPUT: Returns with the radio message response. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/31/1996 BWG : Created. * + *=============================================================================================*/ +RadioMessageType VesselClass::Receive_Message(RadioClass * from, RadioMessageType message, long & param) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + switch (message) { + + /* + ** Asks if the passenger can load on this transport. + */ + case RADIO_CAN_LOAD: + if (Class->Max_Passengers() == 0 || from == NULL || !House->Is_Ally(from->Owner())) return(RADIO_STATIC); + if (How_Many() < Class->Max_Passengers()) { +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this == VESSEL_CARRIER && from->What_Am_I() == RTTI_AIRCRAFT) { + return(RADIO_ROGER); + } +#endif + /* + ** Before saying "Sure, come on board", make sure we're adjacent to + ** the shore. + */ + CELL cell; + Desired_Load_Dir(from, cell); + if(cell) { + return(RADIO_ROGER); + } + } + return(RADIO_NEGATIVE); + + /* + ** This message is sent by the passenger when it determines that it has + ** entered the transport. + */ + case RADIO_IM_IN: +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if(*this != VESSEL_CARRIER) { +#endif + if (How_Many() == Class->Max_Passengers()) { + LST_Close_Door(); + } else { + DoorShutCountDown = TICKS_PER_SECOND; + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + } +#endif + return(RADIO_ATTACH); + + /* + ** Docking maintenance message received. Check to see if new orders should be given + ** to the impatient unit. + */ + case RADIO_DOCKING: + + /* + ** If this transport is moving, then always abort the docking request. + */ + if (IsDriving || Target_Legal(NavCom)) { + return(RADIO_NEGATIVE); + } + + /* + ** Check for the case of a docking message arriving from a unit that does not + ** have formal radio contact established. This might be a unit that is standing + ** by. If this transport is free to proceed with normal docking operation, then + ** establish formal contact now. If the transport is completely full, then break + ** off contact. In all other cases, just tell the pending unit to stand by. + */ + if (Contact_With_Whom() != from) { + + /* + ** Can't ever load up so tell the passenger to bug off. + */ + if (How_Many() >= Class->Max_Passengers()) { + return(RADIO_NEGATIVE); + } + + /* + ** Establish contact and let the loading process proceed normally. + */ + if (!In_Radio_Contact()) { + param = TARGET_NONE; + Transmit_Message(RADIO_HELLO, from); + Transmit_Message(RADIO_MOVE_HERE, param); + return(RADIO_ROGER); + } else { + + /* + ** This causes the potential passenger to think that all is ok and to + ** hold on for a bit. + */ + return(RADIO_ROGER); + } + } + + /* + ** + */ + if (Class->Max_Passengers() > 0 && *this == VESSEL_TRANSPORT && How_Many() < Class->Max_Passengers()) { + DriveClass::Receive_Message(from, message, param); + + if (!IsDriving && !IsRotating) { +// if (!IsDriving && !IsRotating && !IsTethered) { + + /* + ** If the potential passenger needs someplace to go, then figure out a good + ** spot and tell it to go. + */ + if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) { + + CELL cell; + DirType dir = Desired_Load_Dir(from, cell); + + /* + ** If no adjacent free cells are detected, then passenger loading + ** cannot occur. Break radio contact. + */ + if (cell == 0) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + param = (long)::As_Target(cell); + + /* + ** If it is now facing the correct direction, then open the + ** transport doors. Close the doors if the transport is full or needs + ** to rotate. + */ + if (!Is_Door_Open()) { + LST_Open_Door(); + } + + /* + ** Tell the potential passenger where it should go. If the passenger is + ** already at the staging location, then tell it to move onto the transport + ** directly. + */ + if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) { + if (Is_Door_Open()) { + param = (long)As_Target(); + Transmit_Message(RADIO_TETHER); + if (Transmit_Message(RADIO_MOVE_HERE, param, from) != RADIO_ROGER) { + Transmit_Message(RADIO_OVER_OUT, from); + } else { + Contact_With_Whom()->Unselect(); + } + } + } + } + } + } + return(RADIO_ROGER); + } +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + if (Class->Max_Passengers() > 0 && *this == VESSEL_CARRIER && How_Many() < Class->Max_Passengers()) { + TechnoClass::Receive_Message(from, message, param); + /* + ** Establish contact with the object if this building isn't already in contact + ** with another. + */ + if (!In_Radio_Contact()) { + Transmit_Message(RADIO_HELLO, from); + } + if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) { + param = As_Target(); + if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) { + Transmit_Message(RADIO_TETHER); + } + } + return(RADIO_ROGER); + } +#endif + break; + + /* + ** When this message is received, it means that the other object + ** has already turned its radio off. Turn this radio off as well. + */ + case RADIO_OVER_OUT: + if (Mission == MISSION_RETURN) { + Assign_Mission(MISSION_GUARD); + } + DriveClass::Receive_Message(from, message, param); + return(RADIO_ROGER); + + } + return(DriveClass::Receive_Message(from, message, param)); +} + + +/*********************************************************************************************** + * VesselClass::Desired_Load_Dir -- Determines the best cell and facing for loading. * + * * + * This routine examines the unit and adjacent cells in order to find the best facing * + * for the transport and best staging cell for the potential passengers. This location is * + * modified by adjacent cell passability and direction of the potential passenger. * + * * + * INPUT: passenger -- Pointer to the potential passenger. * + * * + * moveto -- Reference to the cell number that specifies where the potential * + * passenger should move to first. * + * * + * OUTPUT: Returns with the direction the transport should face before opening the transport * + * door. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +DirType VesselClass::Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + /* + ** Determine the ideal facing that provides the least resistance. This would be the direction + ** of the potential passenger or the current transport facing if it is going to unload. + */ + DirType faceto; + if (passenger != NULL) { + faceto = Direction(passenger); + } else { + faceto = PrimaryFacing.Current() + DIR_S; + } + + /* + ** Sweep through the adjacent cells in order to find the best candidate. + */ + FacingType bestdir = FACING_N; + int bestval = -1; + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + int value = 0; + CELL cellnum = Adjacent_Cell(Coord_Cell(Coord), face); + + /* + ** Base the initial value of the potential cell according to whether the passenger is + ** allowed to enter the cell. If it can't, then give such a negative value to the + ** cell so that it is prevented from ever choosing that cell for load/unload. + */ + if (passenger != NULL) { + value = (passenger->Can_Enter_Cell(cellnum) == MOVE_OK || Coord_Cell(passenger->Coord) == cellnum) ? 128 : -128; + } else { + CellClass * cell = &Map[cellnum]; + if (Ground[cell->Land_Type()].Cost[SPEED_FOOT] == 0 || cell->Flag.Occupy.Building || cell->Flag.Occupy.Vehicle || cell->Flag.Occupy.Monolith || (cell->Flag.Composite & 0x01F) == 0x01F) { + value = -128; + } else { + if (cell->Cell_Techno() && !House->Is_Ally(cell->Cell_Techno())) { + value = -128; + } else { + value = 128; + } + } + } + +#if(0) + /* + ** Give more weight to the cells that require the least rotation of the transport or the + ** least roundabout movement for the potential passenger. + */ + value -= (int)ABS((int)(signed char)Facing_Dir(face) - (int)(signed char)faceto); + if (face == FACING_S) { + value -= 100; + } + if (face == FACING_SW || face == FACING_SE) value += 64; +#endif + /* + ** If the value for the potential cell is greater than the last recorded potential + ** value, then record this cell as the best candidate. + */ + if (bestval == -1 || value > bestval) { + bestval = value; + bestdir = face; +// } else { +// ObjectClass * obj = Map[cellnum].Cell_Occupier(); +// if (obj) obj->Scatter(Coord, true); + } + } + + /* + ** If a suitable direction was found, then return with the direction value. + */ + moveto = 0; + if (bestval > 0) { + static DirType _desired_to_actual[FACING_COUNT] = {DIR_S, DIR_SW, DIR_NW, DIR_NW, DIR_NE, DIR_NE, DIR_NE, DIR_SE}; + + moveto = Adjacent_Cell(Coord_Cell(Coord), bestdir); + return(_desired_to_actual[bestdir]); + } + return(DIR_N); +} + + +/*********************************************************************************************** + * VesselClass::LST_Open_Door -- Opens a LST door. * + * * + * This routine will initiate opening of the doors on the LST. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Open_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (!IsDriving && !IsRotating) { + Open_Door(5, 6); + } +} + + +/*********************************************************************************************** + * VesselClass::LST_Close_Door -- Closes a LST door. * + * * + * This routine will initiate closing of the LST door. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::LST_Close_Door(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + Close_Door(5, 6); +} + + +/*********************************************************************************************** + * VesselClass::Mission_Unload -- Handles unloading cargo. * + * * + * This is the AI control sequence for when a transport desires to unload its cargo. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the delay before calling this routine again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/01/1996 BWG : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Unload(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + INITIAL_CHECK, + MANEUVERING, + OPENING_DOOR, + UNLOADING, + CLOSING_DOOR + }; + DirType dir; + CELL cell; + + switch (Class->Type) { + case VESSEL_TRANSPORT: + switch (Status) { + case INITIAL_CHECK: + dir = Desired_Load_Dir(NULL, cell); + if (How_Many() > 0 && cell != 0) { + Do_Turn(dir); + Status = MANEUVERING; + return(1); + } else { + if (!How_Many()) { // don't break out if still carrying passengers + Assign_Mission(MISSION_GUARD); + } + } + break; + + case MANEUVERING: + if (!IsRotating) { + LST_Open_Door(); + if (Is_Door_Opening()) { + Status = OPENING_DOOR; + return(1); + } + } + break; + + case OPENING_DOOR: + if (Is_Door_Open()) { + Status = UNLOADING; + return(1); + } else { + if (!Is_Door_Opening()) { + Status = INITIAL_CHECK; + } + } + break; + + case UNLOADING: + if (How_Many()) { + + /* + ** Don't do anything if still in radio contact. + */ + if (In_Radio_Contact()) return(TICKS_PER_SECOND); + + FootClass * passenger = Detach_Object(); + + if (passenger != NULL) { + DirType toface = DIR_S + PrimaryFacing; + bool placed = false; + + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + DirType newface = toface + Facing_Dir(face); + CELL newcell = Adjacent_Cell(Coord_Cell(Coord), newface); + + if (passenger->Can_Enter_Cell(newcell) == MOVE_OK) { + ScenarioInit++; + passenger->Unlimbo(Coord_Move(Coord, newface, CELL_LEPTON_W/2), newface); + ScenarioInit--; + passenger->Assign_Mission(MISSION_MOVE); + passenger->Assign_Destination(::As_Target(newcell)); + passenger->Commence(); + Transmit_Message(RADIO_HELLO, passenger); + Transmit_Message(RADIO_TETHER, passenger); + if (passenger->What_Am_I() == RTTI_UNIT) { + ((UnitClass *)passenger)->IsToScatter = true; + } + placed = true; + break; + } + } + + /* + ** If the attached unit could NOT be deployed, then re-attach + ** it and then bail out of this deploy process. + */ + if (!placed) { + Attach(passenger); + + /* + ** Tell everyone around the transport to scatter. + */ + for (FacingType face = FACING_N; face < FACING_COUNT; face++) { + CellClass * cellptr = &Map[Coord].Adjacent_Cell(face); + if (cellptr->Is_Clear_To_Move(SPEED_TRACK, true, true)) { + cellptr->Incoming(0, true); + } + } + +// Status = CLOSING_DOOR; + } + else { + passenger->Look(false); + } + } + } else { + Status = CLOSING_DOOR; + } + break; + + /* + ** Close LST door in preparation for normal operation. + */ + case CLOSING_DOOR: + if (Is_Door_Open()) { + LST_Close_Door(); + } + if (Is_Door_Closed()) { + if (IsALoaner) { + Assign_Mission(MISSION_RETREAT); + } else { + Assign_Mission(MISSION_GUARD); + } + } + break; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Assign_Destination -- Assign a destination for this vessel. * + * * + * This routine is called when a destination is to be assigned to this vessel. * + * * + * INPUT: target -- The destination to assign to this vessel. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Assign_Destination(TARGET target) +{ + assert(IsActive); + + /* + ** Abort early if there is anything wrong with the parameters + ** or the unit already is assigned the specified destination. + */ + if (target == NavCom) return; + + /* + ** Transport vehicles must tell all passengers that are about to load, that they + ** cannot proceed. This is accomplished with a radio message to this effect. + */ + if (In_Radio_Contact() && Class->Max_Passengers() > 0 && (Contact_With_Whom()->Is_Infantry() || Contact_With_Whom()->What_Am_I() == RTTI_UNIT)) { + long param = TARGET_NONE; + Transmit_Message(RADIO_MOVE_HERE, param); // should stop objects heading toward this transport. + Transmit_Message(RADIO_OVER_OUT); + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + } + + if (!Is_Door_Closed()) { + LST_Close_Door(); + } + + DriveClass::Assign_Destination(target); +} + + +/*********************************************************************************************** + * VesselClass::Pip_Count -- Fetches the number of pips to display on vessel. * + * * + * This routine is used to fetch the number of "fullness" pips to display on the vessel. * + * This will be the number of passengers on a transport. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of pips to draw on this unit. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/25/1995 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Pip_Count(void) const +{ + if (Techno_Type_Class()->Max_Passengers() > 0) { + int passengers = 0; + ObjectClass const * object = Attached_Object(); + for (int index = 0; index < Class_Of().Max_Pips(); index++) { + if (object != NULL) { + passengers++; + object = object->Next; + } + } + return passengers; + } + + return 0; +} + + +/*********************************************************************************************** + * VesselClass::Mission_Retreat -- Perform the retreat mission. * + * * + * This will cause the vessel to run away from the battlefield. It searches for an escape * + * map edge according to the reinforcement edge specified in the house. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of game frames to delay before this routine is called * + * again. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +int VesselClass::Mission_Retreat(void) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + enum { + PICK_RETREAT_POINT, + TRAVEL + }; + switch (Status) { + case PICK_RETREAT_POINT: + IsALoaner = true; + if (!Target_Legal(NavCom)) { +// CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, -1, Class->Speed); + CELL cell = Map.Calculated_Cell(House->Control.Edge, (Team.Is_Valid()) ? Team->Class->Origin : -1, Coord_Cell(Center_Coord()), Class->Speed); + if (Team.Is_Valid()) { + Team->Remove(this); + } + Assign_Destination(::As_Target(cell)); + } + Status = TRAVEL; + return(1); + + case TRAVEL: + if (!Target_Legal(NavCom)) { + Status = PICK_RETREAT_POINT; + } + break; + + default: + break; + } + return(MissionControl[Mission].Normal_Delay()); +} + + +/*********************************************************************************************** + * VesselClass::Is_Allowed_To_Recloak -- Can the vessel recloak now? * + * * + * Asking this question is part of the recloak process. If the answer is no, then * + * recloaking is postponed. This facilitates keeping submarines visible for longer than * + * they otherwise would be. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Can this vessel recloak now? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 BWG : Created. * + *=============================================================================================*/ +bool VesselClass::Is_Allowed_To_Recloak(void) const +{ + return(PulseCountDown == 0); +} + + +/*********************************************************************************************** + * VesselClass::Read_INI -- Read the vessel data from the INI database. * + * * + * This will read and create all vessels specified in the INI database. This routine is * + * called when the scenario starts. * + * * + * INPUT: ini -- Reference to the INI database to read the vessel data from. * + * * + * OUTPUT: none * + * * + * WARNINGS: Vessels will be created and placed on the map by this function. * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Read_INI(CCINIClass & ini) +{ + VesselClass * vessel; // Working vessel pointer. + HousesType inhouse; // Vessel house. + VesselType classid; // Vessel class. + char buf[128]; + + int len = ini.Entry_Count(INI_Name()); + for (int index = 0; index < len; index++) { + char const * entry = ini.Get_Entry(INI_Name(), index); + + ini.Get_String(INI_Name(), entry, NULL, buf, sizeof(buf)); + inhouse = HouseTypeClass::From_Name(strtok(buf, ",")); + if (inhouse != HOUSE_NONE) { + classid = VesselTypeClass::From_Name(strtok(NULL, ",")); + + if (classid != VESSEL_NONE) { + + if (HouseClass::As_Pointer(inhouse) != NULL) { + vessel = new VesselClass(classid, inhouse); + if (vessel != NULL) { + + /* + ** Read the raw data. + */ + int strength = atoi(strtok(NULL, ",\r\n")); + + CELL cell = atoi(strtok(NULL, ",\r\n")); + + COORDINATE coord = Cell_Coord(cell); + + DirType dir = (DirType)atoi(strtok(NULL, ",\r\n")); + MissionType mission = MissionClass::Mission_From_Name(strtok(NULL, ",\n\r")); + + vessel->Trigger = NULL; + TriggerTypeClass * tp = TriggerTypeClass::From_Name(strtok(NULL, ",\r\n")); + if (tp != NULL) { + TriggerClass * tt = Find_Or_Make(tp); + if (tt != NULL) { + tt->AttachCount++; + vessel->Trigger = tt; + } + } + + if (vessel->Unlimbo(coord, dir)) { + vessel->Strength = (int)vessel->Class->MaxStrength * fixed(strength, 256); + if (vessel->Strength > vessel->Class->MaxStrength-3) vessel->Strength = vessel->Class->MaxStrength; + // vessel->Strength = Fixed_To_Cardinal(vessel->Class->MaxStrength, strength); + if (Session.Type == GAME_NORMAL || vessel->House->IsHuman) { + vessel->Assign_Mission(mission); + vessel->Commence(); + } else { + vessel->Enter_Idle_Mode(); + } + + } else { + + /* + ** If the vessel could not be unlimboed, then this is a catastrophic error + ** condition. Delete the vessel. + */ + delete vessel; + } + } + } + } + } + } +} + + +/*********************************************************************************************** + * VesselClass::Write_INI -- Write all vessel scenario data to the INI database. * + * * + * This routine is used to add the vessel data (needed for scenario start) to the INI * + * database specified. If there was any preexisting vessel data in the database, it will * + * be cleared * + * * + * INPUT: ini -- Reference to the ini database to store the vessel data into. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Write_INI(CCINIClass & ini) +{ + /* + ** First, clear out all existing vessel data from the ini file. + */ + ini.Clear(INI_Name()); + + /* + ** Write the vessel data out. + */ + for (int index = 0; index < Vessels.Count(); index++) { + VesselClass * vessel = Vessels.Ptr(index); + if (vessel != NULL && !vessel->IsInLimbo && vessel->IsActive) { + char uname[10]; + char buf[128]; + + sprintf(uname, "%d", index); + sprintf(buf, "%s,%s,%d,%u,%d,%s,%s", + vessel->House->Class->IniName, + vessel->Class->IniName, + vessel->Health_Ratio()*256, + Coord_Cell(vessel->Coord), + vessel->PrimaryFacing.Current(), + MissionClass::Mission_Name(vessel->Mission), + vessel->Trigger.Is_Valid() ? vessel->Trigger->Class->IniName : "None" + ); + ini.Put_String(INI_Name(), uname, buf); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Start_Driver -- Starts the vessel by reserving the location it is moving to. * + * * + * This routine is called when the vessel starts moving. It will reserve the destination * + * cell so that it won't be occupied by another vessel as this one is travelling. * + * * + * INPUT: headto -- The coordinate that will be headed to. * + * * + * OUTPUT: bool; Was the destination location successfully marked? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/09/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Start_Driver(COORDINATE & headto) +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + if (DriveClass::Start_Driver(headto) && IsActive) { //BG IsActive can be cleared by Start_Driver + Mark_Track(headto, MARK_DOWN); + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::What_Action -- Determines action to perform on specified cell. * + * * + * This routine will determine what action to perform if the mouse were clicked over the * + * cell specified. * + * * + * INPUT: cell -- The cell that the mouse might be clicked on. * + * * + * OUTPUT: Returns with the action type that this unit will perform if the mouse were * + * clicked of the cell specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/11/1996 BWG : Created. * + *=============================================================================================*/ +ActionType VesselClass::What_Action(CELL cell) const +{ + assert(Vessels.ID(this) == ID); + assert(IsActive); + + ActionType action = DriveClass::What_Action(cell); + if (action == ACTION_NOMOVE && Map[cell].Land_Type() == LAND_BEACH) { + return(ACTION_MOVE); + } + + if (action == ACTION_NOMOVE && Class->PrimaryWeapon != NULL && Class->PrimaryWeapon->Bullet->IsSubSurface && Map[cell].Is_Bridge_Here()) { + return(ACTION_ATTACK); + } + return(action); +} + + +/*********************************************************************************************** + * VesselClass::Rotation_AI -- Handles turret and body rotation for this vessel. * + * * + * Any turret or body rotation for this vessel will be handled by this routine. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Rotation_AI(void) +{ + if (Target_Legal(TarCom) && !IsRotating) { + DirType dir = Direction(TarCom); + + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(dir); + } + } + + IsRotating = false; + if (Class->IsTurretEquipped) { + + if (SecondaryFacing.Is_Rotating()) { + Mark(MARK_CHANGE_REDRAW); + if (SecondaryFacing.Rotation_Adjust((Class->ROT * House->GroundspeedBias)+1)) { + Mark(MARK_CHANGE_REDRAW); + } + + /* + ** If no further rotation is necessary, flag that the rotation + ** has stopped. + */ + IsRotating = SecondaryFacing.Is_Rotating(); + } + } +} + + +/*********************************************************************************************** + * VesselClass::Combat_AI -- Handles firing and target selection for the vessel. * + * * + * This routine will process firing logic for the vessel. It includes searching for targets * + * and performing any adjustments necessary to bring the target to bear. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Only call this routine once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +void VesselClass::Combat_AI(void) +{ + if (Target_Legal(TarCom) && Is_Weapon_Equipped()) { + + /* + ** Determine which weapon can fire. First check for the primary weapon. If that weapon + ** cannot fire, then check any secondary weapon. If neither weapon can fire, then the + ** failure code returned is that from the primary weapon. + */ + int primary = What_Weapon_Should_I_Use(TarCom); + FireErrorType ok = Can_Fire(TarCom, primary); + + switch (ok) { + case FIRE_OK: + Fire_At(TarCom, primary); + break; + + case FIRE_FACING: + if (Class->IsTurretEquipped) { + SecondaryFacing.Set_Desired(Direction(TarCom)); + } else { + if (!PrimaryFacing.Is_Rotating()) { + PrimaryFacing.Set_Desired(Direction(TarCom)); + } + } + break; + + case FIRE_CLOAKED: + Mark(MARK_OVERLAP_UP); + IsFiring = false; + Mark(MARK_OVERLAP_DOWN); + Do_Uncloak(); + break; + } + } +} + + +/*********************************************************************************************** + * VesselClass::Edge_Of_World_AI -- Determine if vessel is off the edge of the world. * + * * + * In addition to detecting the edge of world case, this routine will delete the vessel * + * if it occurs. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Was the vessel deleted by this routine? * + * * + * WARNINGS: Be sure to examine the return value and if true, abort any further processing * + * for this vessel since it has been deleted. This routine should be called once * + * per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 JLB : Created. * + *=============================================================================================*/ +bool VesselClass::Edge_Of_World_AI(void) +{ + if (!IsDriving && !Map.In_Radar(Coord_Cell(Coord)) && IsLocked) { + if (Team.Is_Valid()) Team->IsLeaveMap = true; + Stun(); + delete this; + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * VesselClass::Repair_AI -- Process any self-repair required of this vessel. * + * * + * When a vessel repairs, it does so 'by itself' and not under direct control of another * + * object. This self repair logic is processed here. Upon repair completion of money * + * exhuastion, the repair process will terminate. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: Call this routine only once per vessel per game logic loop. * + * * + * HISTORY: * + * 07/29/1996 BWG : Created. * + *=============================================================================================*/ +void VesselClass::Repair_AI(void) +{ + if (IsSelfRepairing) { + if ((Frame % (TICKS_PER_MINUTE * Rule.RepairRate)) == 0) { + Mark(MARK_CHANGE); + int cost = Class->Repair_Cost(); + int step = Class->Repair_Step(); + + if (House->Available_Money() >= cost) { + House->Spend_Money(cost); + Strength += step; + if (Strength >= Class->MaxStrength) { + Strength = Class->MaxStrength; + IsSelfRepairing = IsToSelfRepair = false; + + // MBL 04.27.2020: Make only audible to the correct player + // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); + if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED, House); + } + } + } + } +} + +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 +/*********************************************************************************************** + * VesselClass::Fire_At -- Try to fire upon the target specified. * + * * + * This routine is the auto-fire logic for the ship. It will check * + * to see if we're an aircraft carrier, and if so, launch one of our * + * aircraft. If we're not an aircraft carrier, it lets the higher-level * + * Fire_At logic take over. * + * * + * INPUT: target -- The target to fire upon. * + * * + * which -- Which weapon to use when firing. 0=primary, 1=secondary. * + * * + * OUTPUT: bool; Did firing occur? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 04/26/1994 JLB : Created. * + *=============================================================================================*/ +BulletClass * VesselClass::Fire_At(TARGET target, int which) +{ + //PG assert(Units.ID(this) == ID); + assert(IsActive); + + if (*this == VESSEL_CARRIER) { + Arm = CarrierLaunchDelay; + FootClass * passenger = Detach_Object(); + if (passenger != NULL) { + ScenarioInit++; + passenger->Unlimbo(Center_Coord()); + ScenarioInit--; + passenger->Assign_Mission(MISSION_ATTACK); + passenger->Assign_Target(TarCom); + passenger->Commence(); +// If we've launched our last aircraft, discontinue attacking. + if (!How_Many()) Assign_Target(TARGET_NONE); + } + } else { + return DriveClass::Fire_At(target, which); + } + return (NULL); +} + +#endif \ No newline at end of file diff --git a/code/redalert/vessel.h b/code/redalert/vessel.h new file mode 100644 index 0000000..dc383d2 --- /dev/null +++ b/code/redalert/vessel.h @@ -0,0 +1,154 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VESSEL.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VESSEL.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/13/96 * + * * + * Last Update : March 13, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VESSEL_H +#define VESSEL_H + +#include "drive.h" +#include "radio.h" +#include "cargo.h" +#include "mission.h" +#include "target.h" + +class VesselClass : public DriveClass +{ + public: + /* + ** This points to the controling static characteristic data associated with + ** this vessel. + */ + CCPtr Class; + + /* + ** Has this sea vessel been told to move to a shipyard? If so, then + ** when we get there, start the repair process. + */ + unsigned IsToSelfRepair:1; + + /* + ** Is this sea vessel parked next to a shipyard/subpen, and therefore + ** in the special self-repair mode? + */ + unsigned IsSelfRepairing:1; + + /* + ** If this is an LST, is it time to shut the door? + */ + CDTimerClass DoorShutCountDown; + + /* + ** If this is a sub, has the sonar pulse worn off, such that we can + ** re-submerge? + */ + CDTimerClass PulseCountDown; + + VesselClass(VesselType classid, HousesType house); + VesselClass(NoInitClass const & x) : DriveClass(x), Class(x), SecondaryFacing(x) {}; + static void * operator new(size_t size); + static void * operator new(size_t , void * ptr) {return(ptr);}; + static void operator delete(void *ptr); + operator VesselType(void) const {return Class->Type;}; + + static void Init(void); + + virtual ~VesselClass(void); + virtual ObjectTypeClass const & Class_Of(void) const; + + virtual MZoneType Zone_Check_Type(void) const {return(MZONE_WATER);} + int Shape_Number(void) const; + void Rotation_AI(void); + void Combat_AI(void); + bool Edge_Of_World_AI(void); + void Repair_AI(void); + virtual DirType Turret_Facing(void) const {if (Class->IsTurretEquipped) return(SecondaryFacing.Current());return(PrimaryFacing.Current());} + virtual bool Start_Driver(COORDINATE & headto); + virtual int Mission_Retreat(void); + virtual int Mission_Unload(void); + void LST_Open_Door(void); + void LST_Close_Door(void); + virtual COORDINATE Fire_Coord(int which) const; + virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; + virtual void Draw_It(int x, int y, WindowNumberType window) const; + virtual short const * Overlap_List(bool redraw=false) const; + virtual DirType Desired_Load_Dir(ObjectClass * passenger, CELL & moveto) const; + virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param); + virtual void AI(void); + virtual void Per_Cell_Process(PCPType why); + virtual void Assign_Destination(TARGET target); + virtual int Pip_Count(void) const; + + virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source=0, bool forced=false); + virtual FireErrorType Can_Fire(TARGET target, int which) const; + + virtual void Enter_Idle_Mode(bool initial=false); + virtual ActionType What_Action(ObjectClass const * object) const; + virtual ActionType What_Action(CELL cell) const; + virtual void Active_Click_With(ActionType action, CELL cell); + virtual void Active_Click_With(ActionType action, ObjectClass * object); + virtual TARGET Greatest_Threat(ThreatType threat) const; + virtual bool Is_Allowed_To_Recloak(void) const; +#ifdef FIXIT_CARRIER // checked - ajw 9/28/98 + virtual BulletClass * Fire_At(TARGET target, int which=0); +#endif + /* + ** File I/O. + */ + static void Read_INI(CCINIClass & ini); + static void Write_INI(CCINIClass & ini); + static char * INI_Name(void) {return "SHIPS";}; + bool Load(Straw & file); + bool Save(Pipe & file) const; + + /* + ** Scenario and debug support. + */ + #ifdef CHEAT_KEYS + virtual void Debug_Dump(MonoClass *mono) const; + #endif + + protected: + /* + ** This is the facing of the turret. It can be, and usually is, + ** rotated independently of the body it is attached to. + */ + FacingClass SecondaryFacing; + + /* + ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load + */ + unsigned char SaveLoadPadding[32]; + +}; + +#endif \ No newline at end of file diff --git a/code/redalert/visudlg.cpp b/code/redalert/visudlg.cpp new file mode 100644 index 0000000..43cb954 --- /dev/null +++ b/code/redalert/visudlg.cpp @@ -0,0 +1,413 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VISUDLG.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.CPP * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : June 18, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * VisualControlsClass::Process -- Process the visual control dialog box. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "visudlg.h" + + +/*********************************************************************************************** + * VisualControlsClass::Process -- Process the visual control dialog box. * + * * + * This routine displays and processes the visual controls dialog box. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/18/1995 JLB : Created. * + *=============================================================================================*/ +void VisualControlsClass::Process(void) +{ + static int _titles[4] = { + TXT_BRIGHTNESS, + TXT_COLOR, + TXT_CONTRAST, + TXT_TINT + }; + + enum { + NUM_OF_BUTTONS = 6, + }; + + /* + ** Make em resolution independent + */ + int option_width = OPTION_WIDTH * RESFACTOR; // Width of dialog box. + int option_height = OPTION_HEIGHT * RESFACTOR; // Height of dialog box. + int option_x = OPTION_X * RESFACTOR; + int option_y = OPTION_Y * RESFACTOR; + int text_x = TEXT_X * RESFACTOR; + int text_y = TEXT_Y * RESFACTOR; + int slider_x = SLIDER_X * RESFACTOR; + int slider_y = SLIDER_Y * RESFACTOR; + int slider_width = SLIDER_WIDTH * RESFACTOR; // Width of each control slider. + int slider_height = SLIDER_HEIGHT * RESFACTOR; // Height of each control slider. + int slider_y_spacing = SLIDER_Y_SPACING * RESFACTOR; // Vertical spacing between sliders. + int button_x = BUTTON_X * RESFACTOR; // Options button x pos + int button_y = BUTTON_Y * RESFACTOR; // Options button y pos + + + /* + ** Variables. + */ + int selection; + bool pressed; + int curbutton; + TextButtonClass * buttons[NUM_OF_BUTTONS]; + SliderClass * buttonsliders[NUM_OF_BUTTONS]; + + Set_Logic_Page(SeenBuff); + + /* + ** Create Buttons. Button coords are in pixels, but are window-relative. + */ + TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OK, TPF_BUTTON, 0, button_y, 60*RESFACTOR); +// TextButtonClass optionsbtn(BUTTON_OPTIONS, TXT_OPTIONS_MENU, TPF_BUTTON, 0, button_y); + TextButtonClass resetbtn(BUTTON_RESET, TXT_RESET_MENU, TPF_BUTTON, 0, button_y, 80*RESFACTOR); + + /* + ** Centers options button. + */ + optionsbtn.X = option_x + (option_width - optionsbtn.Width - (17 * RESFACTOR)); + resetbtn.X = option_x + (17 * RESFACTOR); + + resetbtn.Add_Tail(optionsbtn); + + /* + ** Brightness (value) control. + */ + SliderClass brightness(BUTTON_BRIGHTNESS, slider_x, slider_y + (slider_y_spacing*0), slider_width, slider_height, true); + brightness.Set_Thumb_Size(20 * RESFACTOR); + brightness.Set_Value(Options.Get_Brightness() * 256); + brightness.Add_Tail(optionsbtn); + + /* + ** Color (saturation) control. + */ + SliderClass color(BUTTON_COLOR, slider_x, slider_y + (slider_y_spacing*1), slider_width, slider_height, true); + color.Set_Thumb_Size(20 * RESFACTOR); + color.Set_Value(Options.Get_Saturation() * 256); + color.Add_Tail(optionsbtn); + + /* + ** Contrast control. + */ + SliderClass contrast(BUTTON_CONTRAST, slider_x, slider_y + (slider_y_spacing*2), slider_width, slider_height, true); + contrast.Set_Thumb_Size(20 * RESFACTOR); + contrast.Set_Value(Options.Get_Contrast() * 256); + contrast.Add_Tail(optionsbtn); + + /* + ** Tint (hue) control. + */ + SliderClass tint(BUTTON_TINT, slider_x, slider_y + (slider_y_spacing*3), slider_width, slider_height, true); + tint.Set_Thumb_Size(20 * RESFACTOR); + tint.Set_Value(Options.Get_Tint() * 256); + tint.Add_Tail(optionsbtn); + + /* + ** This causes left mouse button clicking within the confines of the dialog to + ** be ignored if it wasn't recognized by any other button or slider. + */ + GadgetClass dialog(option_x, option_y, option_width, option_height, GadgetClass::LEFTPRESS); + dialog.Add_Tail(optionsbtn); + + /* + ** This causes a right click anywhere or a left click outside the dialog region + ** to be equivalent to clicking on the return to options dialog. + */ + ControlClass background(BUTTON_OPTIONS, 0, 0, SeenBuff.Get_Width(), SeenBuff.Get_Height(), + GadgetClass::LEFTPRESS|GadgetClass::RIGHTPRESS); + background.Add_Tail(optionsbtn); + + curbutton = 0; + buttons[0] = NULL; + buttons[1] = NULL; + buttons[2] = NULL; + buttons[3] = NULL; + buttons[4] = &resetbtn; + buttons[5] = &optionsbtn; + + buttonsliders[0] = &brightness; + buttonsliders[1] = &color; + buttonsliders[2] = &contrast; + buttonsliders[3] = ∭ + buttonsliders[4] = NULL; + buttonsliders[5] = NULL; + + /* + ** Main Processing Loop. + */ + bool display = true; + bool process = true; + bool partial = true; + pressed = false; + while (process) { + + /* + ** Invoke game callback. + */ + if (Session.Type == GAME_NORMAL || Session.Type == GAME_SKIRMISH) { + Call_Back(); + } else { + if (Main_Loop()) { + process = false; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + /* + ** Refresh display if needed. + */ + if (display) { + Hide_Mouse(); + Dialog_Box(option_x, option_y, option_width, option_height); + Draw_Caption(TXT_VISUAL_CONTROLS, option_x, option_y, option_width); + Show_Mouse(); + display = false; + partial = true; + } + + /* + ** If just the buttons and captions need to be redrawn, then do so now. + */ + if (partial) { + Hide_Mouse(); + + /* + ** Draw the titles. + */ + for (int i = 0; i < (sizeof(_titles)/sizeof(_titles[0])); i++) { + Fancy_Text_Print(_titles[i], slider_x - (8 * RESFACTOR), text_y + (i*slider_y_spacing), + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT|TPF_RIGHT| ((curbutton == i) ? TPF_BRIGHT_COLOR : TPF_TEXT)); + } + optionsbtn.Draw_All(); + Show_Mouse(); + partial = false; + } + + /* + ** Get and process player input. + */ + KeyNumType input = optionsbtn.Input(); + switch (input) { + case (BUTTON_BRIGHTNESS | KN_BUTTON): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR | KN_BUTTON): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST | KN_BUTTON): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT | KN_BUTTON): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + + case (BUTTON_RESET | KN_BUTTON): + selection = BUTTON_RESET; + pressed = true; + break; + + case KN_ESC: + case BUTTON_OPTIONS|KN_BUTTON: + selection = BUTTON_OPTIONS; + pressed = true; + break; + + case (KN_LEFT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(1); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton--; + if (curbutton < (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_OPTIONS - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RIGHT): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS)) { + buttonsliders[curbutton]->Bump(0); + switch (curbutton) { + case (BUTTON_BRIGHTNESS - BUTTON_BRIGHTNESS): + Options.Set_Brightness(fixed(brightness.Get_Value(), 256)); + break; + + case (BUTTON_COLOR - BUTTON_BRIGHTNESS): + Options.Set_Saturation(fixed(color.Get_Value(), 256)); + break; + + case (BUTTON_CONTRAST - BUTTON_BRIGHTNESS): + Options.Set_Contrast(fixed(contrast.Get_Value(), 256)); + break; + + case (BUTTON_TINT - BUTTON_BRIGHTNESS): + Options.Set_Tint(fixed(tint.Get_Value(), 256)); + break; + } + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + + curbutton++; + if (curbutton > (BUTTON_OPTIONS - BUTTON_BRIGHTNESS) ) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_UP): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton--; + if (curbutton == (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton--; + } + + if (curbutton < 0) { + curbutton = (BUTTON_RESET - BUTTON_BRIGHTNESS); + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_DOWN): + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_Off(); + buttons[curbutton]->Flag_To_Redraw(); + } + + curbutton++; + if (curbutton > (BUTTON_RESET - BUTTON_BRIGHTNESS) ) { + curbutton = 0; + } + + if (curbutton <= (BUTTON_TINT - BUTTON_BRIGHTNESS) ) { + partial = true; + } else { + buttons[curbutton]->Turn_On(); + buttons[curbutton]->Flag_To_Redraw(); + } + break; + + case (KN_RETURN): + selection = curbutton + BUTTON_BRIGHTNESS; + pressed = true; + break; + + default: + break; + } + + + if (pressed) { + switch (selection) { + case (BUTTON_RESET): + brightness.Set_Value(128); + contrast.Set_Value(128); + color.Set_Value(128); + tint.Set_Value(128); + + Options.Set_Brightness(fixed::_1_2); + Options.Set_Contrast(fixed::_1_2); + Options.Set_Saturation(fixed::_1_2); + Options.Set_Tint(fixed::_1_2); + break; + + case (BUTTON_OPTIONS): + process = false; + break; + } + + pressed = false; + } + } +} + diff --git a/code/redalert/visudlg.h b/code/redalert/visudlg.h new file mode 100644 index 0000000..328bc7d --- /dev/null +++ b/code/redalert/visudlg.h @@ -0,0 +1,73 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/VISUDLG.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : VISUDLG.H * + * * + * Programmer : Maria del Mar McCready Legg * + * Joe L. Bostic * + * * + * Start Date : Jan 8, 1995 * + * * + * Last Update : Jan 18, 1995 [MML] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + *---------------------------------------------------------------------------------------------*/ + +#ifndef VISUDLG_H +#define VISUDLG_H + +#include "gadget.h" + +class VisualControlsClass +{ + private: + + enum VisualControlEnums { + BUTTON_BRIGHTNESS=1, + BUTTON_COLOR, + BUTTON_CONTRAST, + BUTTON_TINT, + BUTTON_RESET, + BUTTON_OPTIONS, // Button number for "Options menu" + OPTION_WIDTH=216, // Width of dialog box. + OPTION_HEIGHT=122, // Height of dialog box. + OPTION_X=(((320 - OPTION_WIDTH) / 2)), + OPTION_Y=((200 - OPTION_HEIGHT) / 2), + TEXT_X=OPTION_X+28, // Title's x pos + TEXT_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_X=OPTION_X+105, // Slider's x pos + SLIDER_Y=OPTION_Y+30, // Add 11 for each following line + SLIDER_WIDTH=70, // Width of each control slider. + SLIDER_HEIGHT=5, // Height of each control slider. + SLIDER_Y_SPACING=11, // Vertical spacing between sliders. + BUTTON_X=OPTION_X+63, // Options button x pos + BUTTON_Y=OPTION_Y+102, // Options button y pos + }; + + public: + + VisualControlsClass(void) {}; + void Process(void); +}; + +#endif diff --git a/code/redalert/vortex.cpp b/code/redalert/vortex.cpp new file mode 100644 index 0000000..da3dbe9 --- /dev/null +++ b/code/redalert/vortex.cpp @@ -0,0 +1,1259 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/************************************************************************************* + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + ************************************************************************************* + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : VORTEX.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : August 12th, 1996 * + * * + * Last Update : September 6th, 1996 [ST] * + * * + *-----------------------------------------------------------------------------------* + * Overview: * + * * + * Circley vortexy swirly type thing. (Really just a pixel & color remap). * + * * + *-----------------------------------------------------------------------------------* + * Functions: * + * * + * CVC::ChronalVortexClass -- vortex class constructor * + * CVC::~ChronalVortexClass -- vortex class destructor * + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * CVC::Disappear -- Makes the chronal vortex go away. * + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * CVC::Show -- Makes a hidden vortex visible again. * + * CVC::Stop -- Stops the vortex without going through the hide animation * + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * CVC::Save -- Saves the vortex class data to a savegame file * + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * CVC::Movement -- Movement AI for the vortex. * + * CVC::Set_Target -- Make the vortex zap a particular object. * + * CVC::Attack -- look for objects to attack * + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * CVC::Coordinate_Remap -- Draws the vortex * + * CVC::Render -- Renders the vortex at its current position. * + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * CVC::Build_Fading_Table -- Builds a fading color lookup table. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "vortex.h" + + +/* +** Instance of chronal vortex class. This must be the only instance. +*/ +ChronalVortexClass ChronalVortex; + + + +/*********************************************************************************************** + * CVC::ChronalVortexClass -- vortex class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::ChronalVortexClass (void) +{ + Active = 0; + Theater = THEATER_NONE; + Speed = 10; + Range = 10; + Damage = 200; + RenderBuffer = NULL; //We havn't allocated it yet. It will be allocated as needed. +} + + + +/*********************************************************************************************** + * CVC::~ChronalVortexClass -- vortex class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:25PM ST : Created * + *=============================================================================================*/ +ChronalVortexClass::~ChronalVortexClass (void) +{ + if (RenderBuffer) delete RenderBuffer; + Active = 0; +} + + + +/*********************************************************************************************** + * CVC::Appear -- Makes a chronal vortex appear at the given coordinate. * + * * + * * + * * + * INPUT: Coordinate that vortex should appear at. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This member does nothing if the vortex is already active * + * * + * HISTORY: * + * 8/29/96 4:27PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Appear (COORDINATE coordinate) +{ + if (Active) return; + + /* + ** Adjust the given coordinate so the vortex appears in a central position + */ + int x = Lepton_To_Pixel(Coord_X(coordinate)); + int y = Lepton_To_Pixel(Coord_Y(coordinate)); + + x -= 32; + y -= 32; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + Position = XY_Coord (lx, ly); + + /* + ** Initialise the vortex variables. + */ + AnimateDir = 1; + AnimateFrame = 0; + State = STATE_GROW; + Active = true; + Animate = 0; + StartShutdown = false; + LastAttackFrame= Frame; + TargetObject = TARGET_NONE; + ZapFrame = 0; + Hidden = false; + StartHiding = false; + XDir = 0; + YDir = 0; + + /* + ** Vortex starts off in a random direction. + */ + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + +} + + +/*********************************************************************************************** + * CVC::Disappear -- Makes the chronal vortex go away. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Disappear (void) +{ + if (Hidden) { + Active = false; + } else { + StartShutdown = true; + } +} + + + +/*********************************************************************************************** + * CVC::Hide -- Makes the vortex hide. It might come back later. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: This doesnt deactivate the vortex. Use Disappear to get rid of it permanently. * + * * + * HISTORY: * + * 8/29/96 4:30PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Hide (void) +{ + if (!StartShutdown) { + StartHiding = true; + } +} + + +/*********************************************************************************************** + * CVC::Show -- Makes a hidden vortex visible again. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:31PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Show (void) +{ + /* + ** Dont do anything if vortx is dying. + */ + if (!StartShutdown) { + + /* + ** If the vortex is hidden then show it again. + */ + if (Hidden) { + Hidden = false; + StartHiding = false; + AnimateFrame = 0; + State = STATE_GROW; + XDir = 0; + YDir = 0; + } else { + /* + ** If the vortex is in the process of hiding then reverse it. + */ + StartHiding = false; + if (State == STATE_SHRINK) { + State = STATE_GROW; + AnimateFrame = VORTEX_FRAMES - AnimateFrame; + } + } + } +} + + + +/*********************************************************************************************** + * CVC::Stop -- Stops the vortex without going through the hide animation * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Stop(void) +{ + if (Active) Active = false; +} + + + + +/*********************************************************************************************** + * CVC::Load -- Loads the chronal vortex from a savegame file. * + * * + * * + * * + * INPUT: ptr to file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:32PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Load(Straw &file) +{ + /* + ** Delete the render buffer as we are going to lose the pointer anyway. + ** It will be re-allocated when needed. + */ + if (RenderBuffer) delete RenderBuffer; + + file.Get (this, sizeof (ChronalVortexClass)); +} + + + +/*********************************************************************************************** + * CVC::Save -- Saves the vortex class data to a savegame file * + * * + * * + * * + * INPUT: file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:33PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Save(Pipe &file) +{ + GraphicBufferClass *save_ptr = NULL; + + if (RenderBuffer){ + /* + ** Save the ptr to the render buffer so we can null it for the save + */ + save_ptr = RenderBuffer; + RenderBuffer = NULL; + } + + file.Put (this, sizeof (ChronalVortexClass)); + + /* + ** Restore the render buffer ptr + */ + if (save_ptr){ + RenderBuffer = save_ptr; + } +} + + + +/*********************************************************************************************** + * CVC::AI -- AI for the vortex. Includes movement and firing. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:34PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::AI(void) +{ + + int chance; + + /* + ** No AI if vortex isnt active + */ + if (Active) { + + /* + ** Do the movement AI + */ + Movement(); + + /* + ** Do the attack AI + */ + Zap_Target(); + + + if (Hidden && (Frame - HiddenFrame > 50) ) { + /* + ** Vortex is hidden. Chance of it showing itself increases the longer its stays hidden. + */ + chance = Random_Pick(0,2000); + if (chance <= Frame - HiddenFrame) { + Show(); + } + } else { + + if (Animate == 0) { + + /* + ** Its time to animate the vortex. + */ + AnimateFrame += AnimateDir; + + if (AnimateFrame > VORTEX_FRAMES) { + /* + ** State changes can only occur on final animation frames. + */ + AnimateFrame = 1; + + if (StartShutdown) { + + /* + ** Vortex is in the process of dying. + */ + if (State == STATE_SHRINK) { + Set_Redraw(); + Active = false; + AnimateFrame = 0; + } else { + Attack(); + State = STATE_SHRINK; + } + } else { + + if (StartHiding) { + /* + ** Vortex wants to hide. + */ + if (State == STATE_SHRINK) { + /* + ** Hide the vortex now. + */ + Set_Redraw(); + StartHiding = false; + Hidden = true; + HiddenFrame = Frame; + if (Random_Pick(0,4) == 4) { + Disappear(); + } + } else { + /* + ** Start hiding the vortex. + */ + Attack(); + State = STATE_SHRINK; + } + } else { + + Attack(); + if (State == STATE_GROW) { + State = STATE_ROTATE; + } else { + //Attack(); + } + } + } + } else { + if (AnimateFrame == VORTEX_FRAMES / 2) Attack(); + } + } + Animate++; + Animate &= 1; + } + } +} + + + + + +/*********************************************************************************************** + * CVC::Movement -- Movement AI for the vortex. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:39PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Movement (void) +{ + bool newpick = true; + + /* + ** Update the vortex position by applying the x and y direction variables + */ + LEPTON x = Coord_X(Position); + LEPTON y = Coord_Y(Position); + + x += XDir; + y += YDir; + + Position = XY_Coord (x,y); + + /* + ** Reverse the direction of the vortex if its drifting off the map. + */ + if (x > CELL_LEPTON_W *(Map.MapCellX + Map.MapCellWidth -4)) { + newpick = false; + if (DesiredXDir >0 ) DesiredXDir = -DesiredXDir; + } + + if (y > CELL_LEPTON_H *(Map.MapCellY + Map.MapCellHeight -4)) { + newpick = false; + if (DesiredYDir >0 ) DesiredYDir = -DesiredYDir; + } + + if (x < CELL_LEPTON_W *Map.MapCellX + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredXDir <0 ) DesiredXDir = -DesiredXDir; + } + + if (y < CELL_LEPTON_H *Map.MapCellY + 2*CELL_LEPTON_W) { + newpick = false; + if (DesiredYDir <0 ) DesiredYDir = -DesiredYDir; + } + + /* + ** Vortex direction tends towards the desired direction unless the vortex is shutting down or + ** appearing in which case the direction tends towards 0. + */ + if (State == STATE_ROTATE || Hidden) { + if (XDir < DesiredXDir) XDir ++; + if (XDir > DesiredXDir) XDir --; + if (YDir < DesiredYDir) YDir ++; + if (YDir > DesiredYDir) YDir --; + } else { + if (XDir > 0) XDir -= Speed/8; + if (XDir < 0) XDir += Speed/8; + if (YDir > 0) YDir -= Speed/8; + if (YDir < 0) YDir += Speed/8; + } + + /* + ** Occasionally change the direction of the vortex. + */ + if (newpick && Random_Pick (0, 100) == 100) { + DesiredXDir = Random_Pick (-Speed, Speed); + DesiredYDir = Random_Pick (-Speed, Speed); + } +} + + + +/*********************************************************************************************** + * CVC::Set_Target -- Make the vortex zap a particular object. * + * * + * * + * * + * INPUT: ptr to object to zap * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Target (ObjectClass *target) +{ + if (Active){ + ZapFrame = 0; + TargetObject = TARGET_NONE; + if (target != NULL) TargetObject = target->As_Target(); + LastAttackFrame = Frame; + TargetDistance = (target != NULL) ? Distance (target->Center_Coord(), Position) : 0; + } +} + + +/*********************************************************************************************** + * CVC::Attack -- look for objects to attack * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:42PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Attack(void) +{ + int distance; +// if(TargetObject) return; +// if(!TargetObject) return; + /* + ** Calculate the position of the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Scan through the ground layer objects and see who we should attack + */ + + /* + ** First scan - find any object directly above the vortex. + */ + for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj->Is_Techno() && obj->Strength > 0 ) { + + distance = Distance (obj->Center_Coord(), here); + + if (distance <= CELL_LEPTON_W*2) { + Set_Target (obj); + break; + } + } + } + + /* + ** If we found something to attack then just return + */ + if (!Target_Legal(TargetObject)) return; + + + /* + ** Scan through all ground level objects. + ** + ** Objects within range have a chance of being selected based on their distance from the vortex. + */ + + int chance = Random_Pick (0, 1000); + if (chance > Frame - LastAttackFrame) return; + + for (int i= 0; i < Map.Layer[LAYER_GROUND].Count(); i++) { + ObjectClass * obj = Map.Layer[LAYER_GROUND][i]; + + if ( obj && obj->Is_Techno() ) { + + distance = Distance (obj->Center_Coord(), Position); + + if (distance < CELL_LEPTON_W * Range) { + chance = Random_Pick (0, distance); + if (chance < CELL_LEPTON_W) { + Set_Target (obj); + break; + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Zap_Target -- If the vortex has a target object then zap it with lightning. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:45PM ST : Created * + *=============================================================================================*/ +#define ZAP_COUNT 1 +void ChronalVortexClass::Zap_Target (void) +{ + if (!Hidden && Target_Legal(TargetObject) && ZapFrame < ZAP_COUNT) { + + /* + ** Get the center of the vortex. + */ + int x = Lepton_To_Pixel(Coord_X(Position)); + int y = Lepton_To_Pixel(Coord_Y(Position)); + + x += 32; + y += 12; + + LEPTON lx = Pixel_To_Lepton (x); + LEPTON ly = Pixel_To_Lepton (y); + + COORDINATE here = XY_Coord (lx, ly); + + /* + ** Create a temporary techno object se we can access the lightning ability of the tesla. + */ + TechnoClass *temptech = new BuildingClass (STRUCT_TESLA, HOUSE_GOOD); + if (temptech != NULL) { + temptech->Coord = here; + ObjectClass * obj = As_Object(TargetObject); + TARGET target = As_Target (obj->Center_Coord()); + Sound_Effect(VOC_TESLA_ZAP, obj->Center_Coord()); + temptech->Electric_Zap (target, 0, WINDOW_TACTICAL, here, LightningRemap); + delete temptech; + + /* + ** Flag the whole map to redraw to cover the lightning. + */ + Map.Flag_To_Redraw(true); + + /* + ** Zap the target 3 times but only do damage on the last frame. + */ + ZapFrame++; + + if (ZapFrame == ZAP_COUNT) { + ZapFrame = 0; + int damage = Damage; + obj->Take_Damage(damage, TargetDistance, WARHEAD_TESLA, NULL, 1); + TargetObject = TARGET_NONE; + } + } + + /* + ** Vortex might pretend to go away after zapping the target. + */ + if (Random_Pick (0,2) == 2) Hide(); + } +} + + + + + +/*********************************************************************************************** + * CVC::Coordinate_Remap -- Draws the vortex * + * * + * * + * * + * INPUT: ptr to view port to draw the vortex into * + * x offset * + * y offset * + * width of vortex * + * height of vortex * + * ptr to shading remap tables * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:48PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table) +{ + unsigned char getx,gety, remap_color, pixel_color; + + + BufferClass destbuf (width * height); + + unsigned char *destptr = (unsigned char*) destbuf.Get_Buffer(); + + int destx = x; + int desty = y; + + int dest_width = width; + int dest_height = height; + + if (inbuffer->Lock()) { + + /* + ** Get a pointer to the section of buffer we are going to work on. + */ + unsigned char *bufptr = (unsigned char *) inbuffer->Get_Offset() + + destx +#ifdef WIN32 + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd() + inbuffer->Get_Pitch()); +#else + + desty* (inbuffer->Get_Width() + inbuffer->Get_XAdd()); +#endif + + +#ifdef WIN32 + int modulo = inbuffer->Get_Pitch() + inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#else + int modulo = inbuffer->Get_XAdd() + inbuffer->Get_Width(); +#endif + + + for (int yy = desty ; yy < desty+dest_height ; yy++) { + for (int xx = destx ; xx < destx+dest_width ; xx++) { + /* + ** Get the coordinates of the pixel to draw + */ + getx = *(remap_table++); + gety = *(remap_table++); + remap_color = *(remap_table++); + + pixel_color = * (bufptr + getx + (gety * modulo) ); + + *(destptr++) = VortexRemapTables [remap_color] [pixel_color]; + } + + remap_table += 3*(width - dest_width); + destptr += width - dest_width; + + } + + destbuf.To_Page(destx, desty, dest_width, dest_height, *inbuffer); + + + inbuffer->Unlock(); + } +} + + + +/*********************************************************************************************** + * CVC::Render -- Renders the vortex at its current position. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:49PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Render (void) +{ + if (Active && !Hidden) { + char fname [80]; + + int frame; + + /* + ** Calculate which coordinate lookup table we should be using for this frame. + */ + switch (State) { + case STATE_GROW: + frame = 0; + break; + + case STATE_ROTATE: + frame = VORTEX_FRAMES; + break; + + case STATE_SHRINK: + frame = VORTEX_FRAMES*2; + break; + } + + frame += AnimateFrame; + + sprintf (fname, "HOLE%04d.lut", frame); + + void const *lut_ptr = MFCD::Retrieve(fname); + if (lut_ptr) { + + /* + ** Build a representation of the area of the screen where the vortex will be + ** in an off-screen buffer. + ** This is necessary for clipping as we cant remap pixels from off screen if we build + ** the image from the hidpage. + */ + if (!RenderBuffer) { + RenderBuffer = new GraphicBufferClass(CELL_PIXEL_W * 4, CELL_PIXEL_H * 4, (void*)NULL); + } + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + CellClass *cellptr; + CELL cell; + TemplateTypeClass const * ttype = 0; + int icon; // The icon number to use from the template set. + + +#ifdef WIN32 + GraphicViewPortClass * oldpage = Set_Logic_Page(RenderBuffer); +#else + GraphicBufferClass * oldpage = Set_Logic_Page(RenderBuffer); +#endif + + /* + ** Temporarily modify the tactical window so it works with our offscreen buffer + */ + int wx = WindowList[WINDOW_TACTICAL][WINDOWX]; + int wy = WindowList[WINDOW_TACTICAL][WINDOWY]; + int ww = WindowList[WINDOW_TACTICAL][WINDOWWIDTH]; + int wh = WindowList[WINDOW_TACTICAL][WINDOWHEIGHT]; + + WindowList[WINDOW_TACTICAL][WINDOWX] = 0; + WindowList[WINDOW_TACTICAL][WINDOWY] = 0; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = RenderBuffer->Get_Width(); + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = RenderBuffer->Get_Height(); + + + /* + ** Loop through all the cells that the vortex overlaps and render the template, smudge + ** and overlay for each cell. + */ + for (int y = 0 ; y<4 ; y++) { + for (int x = 0 ; x<4 ; x++) { + + cell = XY_Cell (xc+x,yc+y); + if (cell != -1) { + + //cellptr = &Map[ Coord_Whole (Cell_Coord(cell)) ]; + cellptr = &Map [cell]; + + /* + ** Fetch a pointer to the template type associated with this cell. + */ + if (cellptr->TType != TEMPLATE_NONE && cellptr->TType != TEMPLATE_CLEAR1 && cellptr->TType != 255) { + ttype = &TemplateTypeClass::As_Reference(cellptr->TType); + icon = cellptr->TIcon; + } else { + ttype = &TemplateTypeClass::As_Reference(TEMPLATE_CLEAR1); + icon = cellptr->Clear_Icon(); + } + + /* + ** Draw the template + */ + if (ttype->Get_Image_Data()) { +// jmarshall - send down the raw ttype + RenderBuffer->Draw_Stamp(ttype, icon, x*CELL_PIXEL_W, y*CELL_PIXEL_H, NULL, WINDOW_MAIN); +// jmarshall end + } + + /* + ** Draw any smudge. + */ + if (cellptr->Smudge != SMUDGE_NONE) { + SmudgeTypeClass::As_Reference(cellptr->Smudge).Draw_It(x*CELL_PIXEL_W, y*CELL_PIXEL_H, cellptr->SmudgeData); + } + + /* + ** Draw the overlay object. + */ + if (cellptr->Overlay != OVERLAY_NONE) { + OverlayTypeClass const & otype = OverlayTypeClass::As_Reference(cellptr->Overlay); + IsTheaterShape = (bool)otype.IsTheater; //Tell Build_Frame if this overlay is theater specific + CC_Draw_Shape(otype.Get_Image_Data(), + cellptr->OverlayData, + x*CELL_PIXEL_W + (CELL_PIXEL_W >> 1), + y*CELL_PIXEL_H + (CELL_PIXEL_H >> 1), + WINDOW_TACTICAL, + SHAPE_CENTER|SHAPE_WIN_REL|SHAPE_GHOST, + NULL, + DisplayClass::UnitShadow); + + IsTheaterShape = false; + } + + /* + ** Draw the flag if there is one located at this cell. + */ + if (cellptr->IsFlagged) { + void const * flag_remap = HouseClass::As_Pointer(cellptr->Owner)->Remap_Table(false, REMAP_NORMAL); + CC_Draw_Shape(MFCD::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, flag_remap, DisplayClass::UnitShadow); + } + } + } + } + + + Set_Logic_Page(oldpage); + + /* + ** Restore the tactical window to its correct value + */ + WindowList[WINDOW_TACTICAL][WINDOWX] = wx; + WindowList[WINDOW_TACTICAL][WINDOWY] = wy; + WindowList[WINDOW_TACTICAL][WINDOWWIDTH] = ww; + WindowList[WINDOW_TACTICAL][WINDOWHEIGHT] = wh; + + /* + ** Render the vortex over the cells we just rendered to our buffer + */ + Coordinate_Remap (RenderBuffer, Lepton_To_Pixel(Coord_X(Coord_Fraction(Position))), + Lepton_To_Pixel(Coord_Y(Coord_Fraction(Position))), + 64, + 64, + (unsigned char*) lut_ptr); + + + /* + ** Calculate the pixel position of our fresh block of cells on the tactical map so + ** we can blit it to the hid page. + */ + COORDINATE render_pos = XY_Coord(xc * CELL_LEPTON_W, yc * CELL_LEPTON_H); //Coord_Whole(Position); + + int x, y; + Map.Coord_To_Pixel(render_pos, x, y); + + /* + ** Create a view port to blit to + */ + GraphicViewPortClass target (LogicPage->Get_Graphic_Buffer(), + 0, + LogicPage->Get_YPos(), + Lepton_To_Pixel (Map.TacLeptonWidth), + Lepton_To_Pixel (Map.TacLeptonHeight)); + + + /* + ** Do some clipping since the library clipping gets it wrong. + */ + int diff; + + int source_x = 0; + int source_y = 0; + int source_width = CELL_PIXEL_W*4; + int source_height = CELL_PIXEL_H*4; + + int dest_x = x; + int dest_y = y; + int dest_width = source_width; + int dest_height = source_height; + + if (dest_x < 0) { + source_width += dest_x; + dest_width += dest_x; + source_x -= dest_x; + dest_x = 0; + } + + if (dest_y <0) { + source_height += dest_y; + dest_height += dest_y; + source_y -= dest_y; + dest_y = 0; + } + + if (dest_x + dest_width > target.Get_Width() ) { + diff = dest_x + dest_width - target.Get_Width(); + dest_width -= diff; + source_width -= diff; + } + + if (dest_y + dest_height > target.Get_Height() ) { + diff = dest_y + dest_height - target.Get_Height(); + dest_height -= diff; + source_height -= diff; + } + + + /* + ** Blit our freshly draw cells and vortex into their correct position on the hidpage + */ + if (dest_width > 0 && dest_height > 0) { + RenderBuffer->Blit (target, source_x, source_y, dest_x, dest_y, dest_width, dest_height, false); + } + + } + } +} + + + +/*********************************************************************************************** + * CVC::Set_Redraw -- Flags the cells under to vortex to redraw. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:50PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Set_Redraw(void) +{ + + if (Active) { + + CELL xc = Coord_XCell (Position); + CELL yc = Coord_YCell (Position); + + CELL cell; + + for (int y = MAX(0,yc - 1) ; y< yc+4 ; y++) { + for (int x = MAX(0, xc-1) ; x< xc + 4 ; x++) { + cell = XY_Cell (x,y); + if (cell != -1) { + Map[cell].Redraw_Objects(); + } + } + } + } +} + + + + +/*********************************************************************************************** + * CVC::Setup_Remap_Tables -- Initialises the color remap tables based on theater. * + * * + * * + * * + * INPUT: Theater * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/29/96 4:51PM ST : Created * + *=============================================================================================*/ +void ChronalVortexClass::Setup_Remap_Tables (TheaterType theater) +{ + /* + ** The names of the remap files for each theater + */ + static char _remaps[3][13] ={ + "TEMP_VTX.PAL", + "SNOW_VTX.PAL", + "INTR_VTX.PAL" + }; + + int i; + + /* + ** If the theater has changed then load the remap tables from disk if they exist or create + ** them if they dont. + */ + if (theater != Theater) { + + Theater = theater; + + CCFileClass file (_remaps[(int)Theater]); + + if (file.Is_Available()) { + file.Read (VortexRemapTables, MAX_REMAP_SHADES*256); + } else { + + for (i=0 ; i= CYCLE_COLOR_START && index < (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) || + index == CC_PULSE_COLOR || + index == CC_EMBER_COLOR) { + *ptr++ = index; + } else { + + /* + ** Find the color that, ideally, the working color should be remapped + ** to in the special remap range. + */ + RGBClass trycolor = palette[index]; + trycolor.Adjust(frac, palette[color]); // Try to match this color. + + /* + ** Search through the remap range to find the color that should be remapped + ** to. + */ + int best = -1; + int bvalue = 0; + for (int id = 0; id < PaletteClass::COLOR_COUNT; id++) { + + if (id != 0 && + (id < CYCLE_COLOR_START || id >= (CYCLE_COLOR_START + CYCLE_COLOR_COUNT)) && + id != CC_PULSE_COLOR && + id != CC_EMBER_COLOR) { + + int diff = palette[id].Difference(trycolor); + if (best == -1 || diff < bvalue) { + best = id; + bvalue = diff; + } + } + } + *ptr++ = best; + } + } + } +} + + +void ChronalVortexClass::Detach(TARGET target) +{ + if (Target_Legal(target) && target == TargetObject) { + Set_Target(NULL); + } +} diff --git a/code/redalert/vortex.h b/code/redalert/vortex.h new file mode 100644 index 0000000..173f3c6 --- /dev/null +++ b/code/redalert/vortex.h @@ -0,0 +1,270 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer - Red Alert * + * * + * File Name : VORTEX.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 8/12/96 * + * * + * Last Update : August 29th, 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * Definition of ChronalVortexClass. The Chronal vortex sometimes appears when the * + * chronosphere is used. * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VORTEX_H +#define VORTEX_H + + +#define MAX_REMAP_SHADES 16 //Number of lookup tables required for vortex shading. +#define VORTEX_FRAMES 16 //Number of frames in one complete rotation of the vortex. + + + +class ChronalVortexClass { + + public: + + /* + ** Constructor and destructor. + */ + ChronalVortexClass(void); + ~ChronalVortexClass(void); + + void Detach(TARGET target); + + /* + ** Makes the vortex appear at the specified coordinate. + */ + void Appear (COORDINATE coordinate); + + /* + ** Makes the vortex go away. + */ + void Disappear (void); + + /* + ** Call this every frame. + */ + void AI (void); + + /* + ** Render the vortex + */ + void Render (void); + + /* + ** Flags cells under the vortex to be redrawn + */ + void Set_Redraw (void); + + /* + ** Call whenever the theater changes to recalculate the shading lookup tables + */ + void Setup_Remap_Tables (TheaterType theater); + + /* + ** Functions to load and save the vortex. + */ + void Load(Straw &file); + void Save(Pipe &file); + + /* + ** Returns true of vortex is currently active. + */ + bool Is_Active(void) {return (Active);}; + + /* + ** Makes the vortex attack the specified target. Target must be in range of the vortex. + */ + void Set_Target (ObjectClass *target); + + /* + ** Disables the vortex. + */ + void Stop(void); + + /* + ** Members to allow read access to private data + */ + COORDINATE Get_Position (void) {return (Position);}; + int Get_Range (void) {return (Range);}; + int Get_Speed (void) {return (Speed);}; + int Get_Damage (void) {return (Damage);}; + + /* + ** Members to allow write access to private data. + */ + void Set_Range (int range) {Range = range;}; + void Set_Speed (int speed) {Speed = speed;}; + void Set_Damage (int damage) {Damage = damage;}; + + /* + ** Possible states the vortex can be in. + */ + typedef enum AnimStateType : unsigned char { + STATE_GROW, //Vortex has just appeared and is growing larger + STATE_ROTATE, //Vortex is rotating + STATE_SHRINK //Vortex is shrinking and about to disappear + }AnimStateType; + + private: + + /* + ** Members for setting up the lookup tables. + */ + void Build_Fading_Table (PaletteClass const & palette, void * dest, int color, int frac); + void Coordinate_Remap ( GraphicViewPortClass *inbuffer, int x, int y, int width, int height, unsigned char *remap_table); + + /* + ** Misc internal functions + */ + void Attack(void); + void Zap_Target(void); + void Movement(void); + void Hide(void); + void Show(void); + + /* + ** Position of the top left of the vortex + */ + COORDINATE Position; + + /* + ** Direction of rotation + */ + int AnimateDir; + + /* + ** Current frame of animation + */ + int AnimateFrame; + + /* + ** Animation flag. When 0 vortex will animate 1 frame. + */ + int Animate; + + /* + ** State of vortex. See ENUM for info. + */ + AnimStateType State; + + /* + ** Color lookup tables for shading on vortex. + */ + unsigned char VortexRemapTables [MAX_REMAP_SHADES][256]; + + /* + ** Color lookup table to make the blue lightning orange. + */ + unsigned char LightningRemap[256]; + + /* + ** Is vortex currently active? + */ + int Active : 1; + + /* + ** Is the vortex winding down? + */ + int StartShutdown : 1; + + /* + ** Is the vortex about to hide from view? + */ + int StartHiding : 1; + + /* + ** Is the vortex active but hidden? + */ + int Hidden : 1; + + /* + ** Theater that lookup table is good for. + */ + TheaterType Theater; + + /* + ** Last frame that vortex attacked on + */ + int LastAttackFrame; + + /* + ** How many times lightning has zapped on this attack + */ + int ZapFrame; + + /* + ** Ptr to object that the vortex is zapping + */ + TARGET TargetObject; +// ObjectClass *TargetObject; + + /* + ** Distance to the target object + */ + int TargetDistance; + + /* + ** Game frame that vortex hid on. + */ + int HiddenFrame; + + /* + ** Direction vortex is going in. + */ + int XDir; + int YDir; + + /* + ** Direction vortex should be going in + */ + int DesiredXDir; + int DesiredYDir; + + /* + ** Range in cells of the vortex lightning + */ + int Range; + + /* + ** Max speed in leptons per frame of the vortex. + */ + int Speed; + + /* + ** Damge of vortex lightning zap. + */ + int Damage; + + /* + ** Offscreen buffer to render vortex into. This is needed so we can handle clipping. + */ + GraphicBufferClass *RenderBuffer; +}; + + +#endif diff --git a/code/redalert/vqamovie.cpp b/code/redalert/vqamovie.cpp new file mode 100644 index 0000000..66234ce --- /dev/null +++ b/code/redalert/vqamovie.cpp @@ -0,0 +1,162 @@ +// VQAMOVIE.CPP +// + +#include "FUNCTION.H" +#include "VQAMOVIE.H" +#include "AUDIOMIX.H" +#include +#include + +byte* raw_image_buffer = nullptr; +int vqa_upscale_hack_width = 0; +int vqa_upscale_hack_height = 0; +byte* vqa_output_buffer = nullptr; + +byte* VQA_Dropsample(const byte* in, int inwidth, int inheight, int outwidth, int outheight) { + int i, j, k; + const byte* inrow; + const byte* pix1; + byte* out, * out_p; + + out = vqa_output_buffer; + out_p = out; + + for (i = 0; i < outheight; i++, out_p += outwidth * 1) { + inrow = in + 1 * inwidth * (int)((i + 0.25) * inheight / outheight); + for (j = 0; j < outwidth; j++) { + k = j * inwidth / outwidth; + pix1 = inrow + k * 1; + out_p[j * 1 + 0] = pix1[0]; + //out_p[j * 3 + 1] = pix1[1]; + //out_p[j * 3 + 2] = pix1[2]; + } + } + + return out; +} + +/* +================== +_VQAHandle::_VQAHandle +================== +*/ +_VQAHandle::_VQAHandle() { + +} + +/* +================== +_VQAHandle::~_VQAHandle +================== +*/ +_VQAHandle::~_VQAHandle() { + +} + +/* +================== +_VQAHandle::VQA_Alloc +================== +*/ +_VQAHandle* VQA_Alloc(void) { + return new _VQAHandle(); +} + +char smk_info_all(const smk object, unsigned long* frame, unsigned long* frame_count, double* usf); +char smk_info_video(const smk object, unsigned long* w, unsigned long* h, unsigned char* y_scale_mode); + +long VQA_Open(_VQAHandle *videoHandle, char const* filename) { + int ret; + char fullpath[512]; + + sprintf(fullpath, "movies/%s", filename); + videoHandle->smacker_video = smk_open_file(fullpath, 0); + if (videoHandle->smacker_video == NULL) + return -1; + unsigned char yscalemode; + unsigned long frame; + double usf; + smk_info_video(videoHandle->smacker_video, &videoHandle->width, &videoHandle->height, &yscalemode); + smk_info_all(videoHandle->smacker_video, &frame, &videoHandle->frame_count, &usf); + smk_enable_all(videoHandle->smacker_video, 65536); + smk_info_audio(videoHandle->smacker_video, &videoHandle->track_mask, &videoHandle->channels[0], &videoHandle->bitdepth[0], &videoHandle->audio_rate[0]); + videoHandle->video_graphics_buffer = &VisiblePage;//new GraphicBufferClass(); + if (vqa_output_buffer == nullptr) + vqa_output_buffer = new byte[ScreenWidth * ScreenHeight * 3]; + + //videoHandle->video_graphics_buffer->Init(videoHandle->width, videoHandle->height, NULL, 0, (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM)); + + return (0); +} + +void VQA_Free(_VQAHandle* vqaHandle) { + delete vqaHandle; +} + +void VQA_Close(_VQAHandle* vqaHandle) { + +} +long VQA_Play(_VQAHandle* vqaHandle) { + int curTime = clock(); //get the current time + int currentFrame = 0; + bool shouldSampleAudio = true; + + smk_first(vqaHandle->smacker_video); + + while (currentFrame < vqaHandle->frame_count) { + SDL_Event event; + + // Check for any new SDL events. + while (SDL_PollEvent(&event)) { + if (event.type == SDL_KEYDOWN) { + //delete raw_image_buffer; + raw_image_buffer = nullptr; + return 0; + } + } + + smk_render_frame(vqaHandle->smacker_video, currentFrame); + + if (shouldSampleAudio) { + unsigned char* audio_track = smk_get_audio(vqaHandle->smacker_video, 0); + int audio_track_len = smk_get_audio_size(vqaHandle->smacker_video, 0); + + AudMix_QueueVideoAudio((const char*)audio_track, audio_track_len, vqaHandle->audio_rate[0]); + + shouldSampleAudio = false; + } + //// Grab the frame from the movie. + //VQA_ImageForTime(vqaHandle, Sys_Milliseconds() - vqaHandle->startTime); + // + ////memcpy(raw_image_buffer, vqaHandle->image, vqaHandle->CIN_WIDTH * vqaHandle->CIN_HEIGHT * 4 * 2); + //for (int i = 0; i < vqaHandle->CIN_WIDTH * vqaHandle->CIN_HEIGHT; i++) { + // raw_image_buffer[(i * 4) + 0] = vqaHandle->image[(i * 3) + 0]; + // raw_image_buffer[(i * 4) + 1] = vqaHandle->image[(i * 3) + 1]; + // raw_image_buffer[(i * 4) + 2] = vqaHandle->image[(i * 3) + 2]; + //} + + raw_image_buffer = smk_get_palette(vqaHandle->smacker_video); + Set_DD_Palette(raw_image_buffer, false); + vqaHandle->video_graphics_buffer->Lock(); + + VQA_Dropsample(smk_get_video(vqaHandle->smacker_video), vqaHandle->width, vqaHandle->height, ScreenWidth, ScreenHeight); + + Buffer_To_Page(0, 0, ScreenWidth, ScreenHeight, vqa_output_buffer, *vqaHandle->video_graphics_buffer); + vqaHandle->video_graphics_buffer->Unlock(); + + Device_Present(); + + if (clock() - curTime >= 30) { + currentFrame++; + curTime = clock(); + + shouldSampleAudio = true; + } + + } + + //delete raw_image_buffer; + raw_image_buffer = nullptr; + + return (0); +} \ No newline at end of file diff --git a/code/redalert/vqamovie.h b/code/redalert/vqamovie.h new file mode 100644 index 0000000..945a915 --- /dev/null +++ b/code/redalert/vqamovie.h @@ -0,0 +1,43 @@ +// VQAMOVIE.H +// + +#pragma once + +extern "C" { + #include "../external/libsmacker/smacker.h" +}; + +typedef enum { + FMV_IDLE, + FMV_PLAY, // play + FMV_EOF, // all other conditions, i.e. stop/EOF/abort + FMV_ID_BLT, + FMV_ID_IDLE, + FMV_LOOPED, + FMV_ID_WAIT +} cinStatus_t; + +struct _VQAHandle { + _VQAHandle(); + ~_VQAHandle(); + + GraphicBufferClass* video_graphics_buffer; + + unsigned long width; + unsigned long height; + unsigned long frame_count; + unsigned char track_mask; + unsigned char channels[7]; + unsigned char bitdepth[7]; + unsigned long audio_rate[7]; + + smk smacker_video; +}; + +long VQA_Play(_VQAHandle* vqaHandle); +void VQA_Close(_VQAHandle* vqaHandle); +void VQA_Free(_VQAHandle* vqaHandle); +long VQA_Open(_VQAHandle* videoHandle, char const* filename); +void VQA_ImageForTime(_VQAHandle* videoHandle, int milliseconds); +void VQA_FFMPEGReset(_VQAHandle* videoHandle); +_VQAHandle* VQA_Alloc(void); \ No newline at end of file diff --git a/code/redalert/w95trace.cpp b/code/redalert/w95trace.cpp new file mode 100644 index 0000000..453cfc0 --- /dev/null +++ b/code/redalert/w95trace.cpp @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/* + Implementation of Win95 tracing facility to mimic that of NT +*/ + +#include +#include +#include +#include +#include "w95trace.h" + + +void OutputDebugStringW95( LPCTSTR /*lpOutputString*/, ...) +{ +#if 0 + + HANDLE heventDBWIN; /* DBWIN32 synchronization object */ + HANDLE heventData; /* data passing synch object */ + HANDLE hSharedFile; /* memory mapped file shared data */ + LPSTR lpszSharedMem; + char achBuffer[500]; + + /* create the output buffer */ + va_list args; + va_start(args, lpOutputString); + vsprintf(achBuffer, lpOutputString, args); + va_end(args); + + achBuffer[499] = 0; // Null-terminate here, just in case vsprintf didn't do it because lpOutputString was too long. + + /* + Do a regular OutputDebugString so that the output is + still seen in the debugger window if it exists. + + This ifdef is necessary to avoid infinite recursion + from the inclusion of W95TRACE.H + */ +#ifdef _UNICODE + ::OutputDebugStringW(achBuffer); +#else + ::OutputDebugStringA(achBuffer); +#endif + + // added by ajw + //FILE* pFile = fopen( "debugout.wri", "a" ); + FILE* pFile = fopen( "wolapi.out", "a" ); + fprintf( pFile, achBuffer ); + fclose( pFile ); + +// /* bail if it's not Win95 */ +// { +// OSVERSIONINFO VerInfo; +// VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); +// GetVersionEx(&VerInfo); +// if ( VerInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS ) +// return; +// } + + /* make sure DBWIN is open and waiting */ + heventDBWIN = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_BUFFER_READY"); + if ( !heventDBWIN ) + { + //MessageBox(NULL, "DBWIN_BUFFER_READY nonexistent", NULL, MB_OK); + return; + } + + /* get a handle to the data synch object */ + heventData = OpenEvent(EVENT_MODIFY_STATE, FALSE, "DBWIN_DATA_READY"); + if ( !heventData ) + { + // MessageBox(NULL, "DBWIN_DATA_READY nonexistent", NULL, MB_OK); + CloseHandle(heventDBWIN); + return; + } + + hSharedFile = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, 4096, "DBWIN_BUFFER"); + if (!hSharedFile) + { + //MessageBox(NULL, "DebugTrace: Unable to create file mapping object DBWIN_BUFFER", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + lpszSharedMem = (LPSTR)MapViewOfFile(hSharedFile, FILE_MAP_WRITE, 0, 0, 512); + if (!lpszSharedMem) + { + //MessageBox(NULL, "DebugTrace: Unable to map shared memory", "Error", MB_OK); + CloseHandle(heventDBWIN); + CloseHandle(heventData); + return; + } + + /* wait for buffer event */ + WaitForSingleObject(heventDBWIN, INFINITE); + + /* write it to the shared memory */ + *((LPDWORD)lpszSharedMem) = getpid(); + wsprintf(lpszSharedMem + sizeof(DWORD), "%s", achBuffer); + + /* signal data ready event */ + SetEvent(heventData); + + /* clean up handles */ + CloseHandle(hSharedFile); + CloseHandle(heventData); + CloseHandle(heventDBWIN); + + return; +#endif +} diff --git a/code/redalert/w95trace.h b/code/redalert/w95trace.h new file mode 100644 index 0000000..e581cdd --- /dev/null +++ b/code/redalert/w95trace.h @@ -0,0 +1,55 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + declarations for Win95 tracing facility +*/ + +#ifdef _DEBUG + +#ifndef __TRACEW95__ +#define __TRACEW95__ + +#include + +// redefine all the MFC macros to point to us + +#undef TRACE +#define TRACE OutputDebugStringW95 + +#undef TRACE0 +#define TRACE0 OutputDebugStringW95 + +#undef TRACE1 +#define TRACE1 OutputDebugStringW95 + +#undef TRACE2 +#define TRACE2 OutputDebugStringW95 + +#undef TRACE3 +#define TRACE3 OutputDebugStringW95 + +// redefine OutputDebugString so that it works with +// API calls +#undef OutputDebugString +#define OutputDebugString OutputDebugStringW95 + + +// function declarations +void OutputDebugStringW95( LPCTSTR lpOutputString, ... ); + +#endif //__TRACEW95__ + +#endif // _DEBUG diff --git a/code/redalert/warhead.cpp b/code/redalert/warhead.cpp new file mode 100644 index 0000000..c084457 --- /dev/null +++ b/code/redalert/warhead.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WARHEAD.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WARHEAD.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : July 19, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "function.h" + + +/*************************************************************************** +** This is the warhead data object array. +*/ +TFixedIHeapClass Warheads; + + +/*********************************************************************************************** + * WarheadTypeClass::WarheadTypeClass -- Default constructor for warhead objects. * + * * + * This default constructor for a warhead object will fill in all the default values * + * for a warhead. It is presumed that these values will be normal unless specifically * + * overridden by the INI database. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass::WarheadTypeClass(char const * name) : + ID(Warheads.ID(this)), + IniName(name), + SpreadFactor(1), + IsWallDestroyer(false), + IsWoodDestroyer(false), + IsTiberiumDestroyer(false), + IsOrganic(false), + ExplosionSet(0), + InfantryDeath(0) +{ + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = 1; + } +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator new -- Allocate a warhead object from the special heap. * + * * + * This will allocate a warhead object from the special heap that is maintained for * + * this purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the newly allocated warhead type object. If there is * + * insufficient memory for the allocation, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void * WarheadTypeClass::operator new(size_t) +{ + return(Warheads.Alloc()); +} + + +/*********************************************************************************************** + * WarheadTypeClass::operator delete -- Returns warhead object back to special memory pool. * + * * + * This routine will return the warhead object to the memory pool from whence it came. * + * * + * INPUT: pointer -- Pointer to the warhead object to return to the memory pool. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +void WarheadTypeClass::operator delete(void * pointer) +{ + Warheads.Free((WarheadTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WarheadTypeClass::As_Pointer -- Convert a warhead type number into a pointer. * + * * + * This will locate the warhead type specified and return a pointer to it. * + * * + * INPUT: warhead -- The warhead to convert into a pointer. * + * * + * OUTPUT: Returns with a pointer to the warhead type object that is represented by the * + * warhead type number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +WarheadTypeClass * WarheadTypeClass::As_Pointer(WarheadType warhead) +{ + if (warhead != WARHEAD_NONE) { + return(Warheads.Ptr(warhead)); + } + return(NULL); +} + + +/*********************************************************************************************** + * WarheadTypeClass::Read_INI -- Fetches the warhead data from the INI database. * + * * + * Use this routine to retrieve the data specific to this warhead type class object from * + * the INI database specified. Typical use of this is when processing the rules.ini * + * file. * + * * + * INPUT: ini -- Reference to the INI database to fetch the values from. * + * * + * OUTPUT: bool; Was the warhead entry found and the data retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WarheadTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + SpreadFactor = ini.Get_Int(Name(), "Spread", SpreadFactor); + IsWallDestroyer = ini.Get_Bool(Name(), "Wall", IsWallDestroyer); + IsWoodDestroyer = ini.Get_Bool(Name(), "Wood", IsWoodDestroyer); + IsTiberiumDestroyer = ini.Get_Bool(Name(), "Ore", IsTiberiumDestroyer); + ExplosionSet = ini.Get_Int(Name(), "Explosion", ExplosionSet); + InfantryDeath = ini.Get_Int(Name(), "InfDeath", InfantryDeath); + + char buffer[128]; + if (ini.Get_String(Name(), "Verses", "100%%,100%%,100%%,100%%,100%%", buffer, sizeof(buffer))) { + char * aval = strtok(buffer, ","); + for (ArmorType armor = ARMOR_FIRST; armor < ARMOR_COUNT; armor++) { + Modifier[armor] = fixed(aval); + aval = strtok(NULL, ","); + } + } + + IsOrganic = (Modifier[ARMOR_STEEL] == 0); + return(true); + } + return(false); +} + diff --git a/code/redalert/warhead.h b/code/redalert/warhead.h new file mode 100644 index 0000000..c400af9 --- /dev/null +++ b/code/redalert/warhead.h @@ -0,0 +1,117 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WARHEAD.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WARHEAD.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WARHEAD_H +#define WARHEAD_H + + +/********************************************************************** +** Each of the warhead types has specific characteristics. This structure +** holds these characteristics. +*/ +class WarheadTypeClass +{ + public: + WarheadTypeClass(char const * name); + WarheadTypeClass(NoInitClass const &) {} + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WarheadTypeClass * As_Pointer(WarheadType weapon); + + /* + ** This is the ID number of the weapon type. It is the weapon + ** type ID enum as well as the index number into the weapon + ** heap. + */ + int ID; + + /* + ** This is the identifying name of this warhead type. + */ + char const * IniName; + + /* + ** This value control how damage from this warhead type will reduce + ** over distance. The larger the number, the less the damage is reduced + ** the further the distance from the source of the damage. + */ + int SpreadFactor; + + /* + ** If this warhead type can destroy walls, then this flag will be true. + */ + bool IsWallDestroyer:1; + + /* + ** If this warhead can destroy wooden walls, then this flag will be true. + */ + bool IsWoodDestroyer:1; + + /* + ** Does this warhead damage tiberium? + */ + bool IsTiberiumDestroyer:1; + + /* + ** Only effective against infantry? + */ + bool IsOrganic:1; + + /* + ** The warhead damage is reduced depending on the the type of armor the + ** defender has. This table is what gives weapons their "character". + */ + fixed Modifier[ARMOR_COUNT]; + + /* + ** Which explosion set to use for warhead impact. + */ + int ExplosionSet; + + /* + ** This specifies the infantry death animation to use if the infantry dies as + ** a result of a warhead of this type. + */ + int InfantryDeath; +}; + + +#endif diff --git a/code/redalert/watcom.h b/code/redalert/watcom.h new file mode 100644 index 0000000..c8ded8d --- /dev/null +++ b/code/redalert/watcom.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WATCOM.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WATCOM.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 03/12/95 * + * * + * Last Update : March 12, 1995 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WATCOM_H +#define WATCOM_H +#if (0) + +// Turn all warnings into errors. +#pragma warning * 0 + +// Disables warning when "sizeof" is used on an object with virtual functions. +#pragma warning 549 9 + +// Disable the "Integral value may be truncated during assignment or initialization". +#pragma warning 389 9 + +// Allow constructing a temporary to be used as a parameter. +#pragma warning 604 9 + +// Disable the construct resolved as an expression warning. +#pragma warning 595 9 + +// Disable the strange "construct resolved as a declaration/type" warning. +#pragma warning 594 9 + +// Disable the "pre-compiled header file cannot be used" warning. +#pragma warning 698 9 + +// Disable the "temporary object used to initialize a non-constant reference" warning. +#pragma warning 665 9 + +// Disable the "pointer or reference truncated by cast. Cast is supposed to REMOVE warnings, not create them. +#pragma warning 579 9 + +// Disable the warning that suggests a null destructor be placed in class definition. +#pragma warning 656 9 + +// Disable the warning about moving empty constructors/destructors to the class declaration. +#pragma warning 657 9 + +// No virtual destructor is not an error in C&C. +#pragma warning 004 9 + +// Turns off unreferenced function parameter warning. +//#pragma off(unreferenced) + + +// Fix deficiency in Watcom so that true/false will be defined. +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + +#endif + +#endif diff --git a/code/redalert/weapon.cpp b/code/redalert/weapon.cpp new file mode 100644 index 0000000..6eb90db --- /dev/null +++ b/code/redalert/weapon.cpp @@ -0,0 +1,333 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WEAPON.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WEAPON.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/20/96 * + * * + * Last Update : September 9, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * Armor_From_Name -- Convert ASCII name into armor type number. * + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object.* + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" + + +/*************************************************************************** +** These are the various weapons and their characteristics. +*/ +TFixedIHeapClass Weapons; + + +/*********************************************************************************************** + * WeaponTypeClass::WeaponTypeClass -- Default constructor for weapon type objects. * + * * + * This default constructor will initialize all the values of the weapon to the default * + * state. Thus, if any of these settings are not specifically overridden by the rules.ini * + * file, they will remain this value in the game. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::WeaponTypeClass(char const * name) : + ID(Weapons.ID(this)), + IniName(name), + IsSupressed(false), + IsCamera(false), + IsElectric(false), + Burst(1), + Bullet(NULL), + Attack(0), + MaxSpeed(MPH_IMMOBILE), + WarheadPtr(NULL), + ROF(0), + Range(0), + Sound(VOC_NONE), + Anim(ANIM_NONE) +{ +} + + +/*********************************************************************************************** + * WeaponTypeClass::~WeaponTypeClass -- Destructor for weapon type class objects. * + * * + * This destructor really doesn't do anything but set the pointers to NULL. This is a * + * general purposes safety tactic but is otherwise useless. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass::~WeaponTypeClass(void) +{ + IniName = NULL; + Bullet = NULL; + WarheadPtr = NULL; +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator new -- Allocates a weapon type object form the special heap. * + * * + * This will allocate a weapon type object from a special heap that has been set up for * + * that purpose. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the weapon type object allocated. If there was * + * insufficient memory available, NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void * WeaponTypeClass::operator new(size_t) +{ + return(Weapons.Alloc()); +} + + +/*********************************************************************************************** + * WeaponTypeClass::operator delete -- Returns weapon type object back to special heap. * + * * + * This routine will return the weapon type object back to the heap so that the memory * + * can be reused for subsiquent allocations of weapon type objects. * + * * + * INPUT: pointer -- Pointer to the weapon type object to return to the special heap. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +void WeaponTypeClass::operator delete(void * pointer) +{ + Weapons.Free((WeaponTypeClass *)pointer); +} + + +/*********************************************************************************************** + * WeaponTypeClass::As_Pointer -- Give a weapon type ID, fetch pointer to weapon type object. * + * * + * This routine will conver the weapon type ID specified into a pointer to the weapon type * + * object it represents. * + * * + * INPUT: weapon -- The weapon type object ID to convert to a pointer. * + * * + * OUTPUT: Returns with a pointer to the weapon type class object that is represented by the * + * weapon ID number specified. If no match could be found, then NULL is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponTypeClass * WeaponTypeClass::As_Pointer(WeaponType weapon) +{ + if (weapon != WEAPON_NONE) { + return(Weapons.Ptr(weapon)); +// for (int index = 0; index < Weapons.Count(); index++) { +// WeaponTypeClass * ptr = Weapons.Ptr(index); +// if (ptr->ID == weapon) { +// return(ptr); +// } +// } + } + return(NULL); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Read_INI -- Fetch the weapon data from the INI database. * + * * + * This routine will fetch the weapon data for this weapon type object from the INI * + * database specified. * + * * + * INPUT: ini -- Reference to the INI database that the weapon data will be fetched * + * from. * + * * + * OUTPUT: bool; Was this weapon type described in the database and the values retrieved? * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/19/1996 JLB : Created. * + *=============================================================================================*/ +bool WeaponTypeClass::Read_INI(CCINIClass & ini) +{ + if (ini.Is_Present(Name())) { + IsSupressed = ini.Get_Bool(Name(), "Supress", IsSupressed); + Burst = ini.Get_Int(Name(), "Burst", Burst); + Attack = ini.Get_Int(Name(), "Damage", Attack); + MaxSpeed = ini.Get_MPHType(Name(), "Speed", MaxSpeed); + ROF = ini.Get_Int(Name(), "ROF", ROF); + Range = ini.Get_Lepton(Name(), "Range", Range); + Sound = ini.Get_VocType(Name(), "Report", Sound); + Anim = ini.Get_AnimType(Name(), "Anim", Anim); + IsCamera = ini.Get_Bool(Name(), "Camera", IsCamera); + IsElectric = ini.Get_Bool(Name(), "Charges", IsElectric); + IsTurboBoosted = ini.Get_Bool(Name(), "TurboBoost", IsTurboBoosted); + + WarheadType wtype = (WarheadPtr != NULL) ? WarheadType(WarheadPtr->ID) : WARHEAD_NONE; + wtype = ini.Get_WarheadType(Name(), "Warhead", wtype); + if (wtype != WARHEAD_NONE) { + WarheadPtr = WarheadTypeClass::As_Pointer(wtype); +// WarheadPtr = &Warheads[wtype]; + } else { + WarheadPtr = NULL; + } + + BulletType btype = (Bullet != NULL) ? BulletType(Bullet->ID) : BULLET_NONE; + btype = ini.Get_BulletType(Name(), "Projectile", btype); + if (btype != BULLET_NONE) { + Bullet = &BulletTypeClass::As_Reference(btype); + } else { + Bullet = NULL; + } + + return(true); + } + return(false); +} + + +/*********************************************************************************************** + * Weapon_From_Name -- Conver ASCII name to weapon type ID number. * + * * + * This will find the weapon whos name matches that specified and then it will return the * + * weapon ID number associated with it. * + * * + * INPUT: name -- Pointer to the ASCII name of the weapon type. * + * * + * OUTPUT: Returns with the weapon type ID number that matches the name specified. If no * + * match could be found, then WEAPON_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +WeaponType Weapon_From_Name(char const * name) +{ + if (!name) return(WEAPON_NONE); + + for (int index = 0; index < Weapons.Count(); index++) { + if (stricmp(Weapons.Ptr(index)->Name(), name) == 0) { + return(WeaponType(Weapons.Ptr(index)->ID)); + } + } + + return(WEAPON_NONE); +} + + +/*********************************************************************************************** + * Armor_From_Name -- Convert ASCII name into armor type number. * + * * + * This will find the armor type that matches the ASCII name specified and then will return * + * the armor ID number of it. * + * * + * INPUT: name -- Pointer to the ASCII name of the armor to find. * + * * + * OUTPUT: Returns with the armor ID number of the armor that matches the name specified. If * + * no match could be found, then ARMOR_NONE is returned. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/17/1996 JLB : Created. * + *=============================================================================================*/ +ArmorType Armor_From_Name(char const * name) +{ + if (!name) return(ARMOR_NONE); + + for (ArmorType index = ARMOR_FIRST; index < ARMOR_COUNT; index++) { + if (stricmp(ArmorName[index], name) == 0) { + return(index); + } + } + + return(ARMOR_NONE); +} + + +/*********************************************************************************************** + * WeaponTypeClass::Allowed_Threats -- Determine what threats this weapon can address. * + * * + * This routine will examine the capabilities of this weapon and return with the threat * + * types that it can address. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the threat types that this weapon can address. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 09/09/1996 JLB : Created. * + *=============================================================================================*/ +ThreatType WeaponTypeClass::Allowed_Threats(void) const +{ + ThreatType threat = THREAT_NORMAL; + if (Bullet->IsAntiAircraft) { + threat = threat | THREAT_AIR; + } + if (Bullet->IsAntiGround) { + threat = threat | THREAT_INFANTRY|THREAT_VEHICLES|THREAT_BOATS|THREAT_BUILDINGS; + } + return(threat); +} + + +bool WeaponTypeClass::Is_Wall_Destroyer(void) const +{ + if (WarheadPtr != NULL && WarheadPtr->IsWallDestroyer) { + return(true); + } + return(false); +} diff --git a/code/redalert/weapon.h b/code/redalert/weapon.h new file mode 100644 index 0000000..966d5c9 --- /dev/null +++ b/code/redalert/weapon.h @@ -0,0 +1,162 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WEAPON.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WEAPON.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 05/17/96 * + * * + * Last Update : May 17, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WEAPON_H +#define WEAPON_H + + +/********************************************************************** +** This is the constant data associated with a weapon. Some objects +** can have multiple weapons and this class is used to isolate and +** specify this data in a convenient and selfcontained way. +*/ +class WeaponTypeClass +{ + public: + WeaponTypeClass(char const * name); + WeaponTypeClass(NoInitClass const &) {} + ~WeaponTypeClass(void); + + void * operator new(size_t); + static void * operator new(size_t , void * ptr) {return(ptr);}; + void operator delete(void * pointer); + + char const * Name(void) const {return(IniName);} + bool Read_INI(CCINIClass & ini); + static WeaponTypeClass * As_Pointer(WeaponType weapon); + void Code_Pointers(void) {} + void Decode_Pointers(void) {} + ThreatType Allowed_Threats(void) const; + bool Is_Wall_Destroyer(void) const; + + /* + ** This is both the weapon type number and the index number into + ** the weapon array. + */ + int ID; + + /* + ** This is the identifying name of this weapon. + */ + char const * IniName; + + /* + ** Increase the weapon speed if the target is flying. + */ + unsigned IsTurboBoosted:1; + + /* + ** If potential targets of this weapon should be scanned for + ** nearby friendly structures and if found, firing upon the target + ** would be discouraged, then this flag will be true. + */ + unsigned IsSupressed:1; + + /* + ** If this weapon is equipped with a camera that reveals the + ** area around the firer, then this flag will be true. + */ + unsigned IsCamera:1; + + /* + ** If this weapon requires charging before it can fire, then this + ** flag is true. In actuality, this only applies to the Tesla coil + ** which has specific charging animation. The normal rate of fire + ** value suffices for all other cases. + */ + unsigned IsElectric:1; + + /* + ** This is the number of shots this weapon first (in rapid succession). + ** The normal value is 1, but for the case of two shooter weapons such as + ** the double barreled gun turrets of the Mammoth tank, this value will be + ** set to 2. + */ + int Burst; + + /* + ** This is the unit class of the projectile fired. A subset of the unit types + ** represent projectiles. It is one of these classes that is specified here. + ** If this object does not fire anything, then this value will be BULLET_NONE. + */ + BulletTypeClass const * Bullet; + + /* + ** This is the damage (explosive load) to be assigned to the projectile that + ** this object fires. For the rare healing weapon, this value is negative. + */ + int Attack; + + /* + ** Speed of the projectile launched. + */ + MPHType MaxSpeed; + + /* + ** Warhead to attach to the projectile. + */ + WarheadTypeClass const * WarheadPtr; + + /* + ** Objects that fire (which can be buildings as well) will fire at a + ** frequency controlled by this value. This value serves as a count + ** down timer between shots. The smaller the value, the faster the + ** rate of fire. + */ + int ROF; + + /* + ** When this object fires, the range at which it's projectiles travel is + ** controlled by this value. The value represents the number of cells the + ** projectile will travel. Objects outside of this range will not be fired + ** upon (in normal circumstances). + */ + LEPTON Range; + + /* + ** This is the typical sound generated when firing. + */ + VocType Sound; + + /* + ** This is the animation to display at the firing coordinate. + */ + AnimType Anim; +}; + + +#endif + + diff --git a/code/redalert/win32lib/_diptabl.cpp b/code/redalert/win32lib/_diptabl.cpp new file mode 100644 index 0000000..d3cf784 --- /dev/null +++ b/code/redalert/win32lib/_diptabl.cpp @@ -0,0 +1,55 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./_diptabl.c 1.11 1994/05/20 15:36:04 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : _DIPTABL.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 3, 1991 * + * * + * Last Update : July 3, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +char Common[16]={' ','e','t','a','i','n','o','s','r','l','h','c','d','u','p','m'}; + +char Dipthong[16][8]={ + {'t','a','s','i','o',' ','w','b'}, + {' ','r','n','s','d','a','l','m'}, + {'h',' ','i','e','o','r','a','s'}, + {'n','r','t','l','c',' ','s','y'}, + {'n','s','t','c','l','o','e','r'}, + {' ','d','t','g','e','s','i','o'}, + {'n','r',' ','u','f','m','s','w'}, + {' ','t','e','p','.','i','c','a'}, + {'e',' ','o','i','a','d','u','r'}, + {' ','l','a','e','i','y','o','d'}, + {'e','i','a',' ','o','t','r','u'}, + {'e','t','o','a','k','h','l','r'}, + {' ','e','i','u',',','.','o','a'}, + {'n','s','r','c','t','l','a','i'}, + {'l','e','o','i','r','a','t','p'}, + {'e','a','o','i','p',' ','b','m'}, +}; + \ No newline at end of file diff --git a/code/redalert/win32lib/alloc.cpp b/code/redalert/win32lib/alloc.cpp new file mode 100644 index 0000000..cd3f15f --- /dev/null +++ b/code/redalert/win32lib/alloc.cpp @@ -0,0 +1,509 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : ALLOC.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 1, 1992 * + * * + * Last Update : March 9, 1995 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Alloc -- Allocates system RAM. * + * Ram_Free -- Determines the largest free chunk of RAM. * + * Free -- Free an Alloc'ed block of RAM. * + * Resize_Alloc -- Change the size of an allocated block. * + * Heap_Size -- Size of the heap we have. * + * Total_Ram_Free -- Total amount of free RAM. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +//#include +//#include + + +#ifndef WWMEM_H +#include "wwmem.h" +#endif + + +extern "C" unsigned long Largest_Mem_Block ( void ) ; + +/* +** Define the equates necessary to call a DPMI interrupt. +*/ +#define DPMI_INT 0x0031 +#define DPMI_LOCK_MEM 0x0600 +#define DPMI_UNLOCK_MEM 0x0601 + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +unsigned long MinRam=0L; // Record of least memory at worst case. +unsigned long MaxRam=0L; // Record of total allocated at worst case. +static unsigned long TotalRam = 0L; +static unsigned long Memory_Calls = 0L; + +void (*Memory_Error)(void) = NULL; +extern void (*Memory_Error_Exit)(char *string)=NULL; + + +//#define MEM_CHECK + +#ifdef MEM_CHECK +extern "C"{ + extern void __cdecl Int3(void); +} +#endif //MEM_CHECK + +/*************************************************************************** + * DPMI_LOCK -- handles locking a block of DPMI memory * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +#include"mono.h" +void DPMI_Lock(VOID const *, long const ) +{ +} + +/*************************************************************************** + * DPMI_UNLOCK -- Handles unlocking a locked block of DPMI * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/23/1995 PWG : Created. * + *=========================================================================*/ +void DPMI_Unlock(void const *, long const ) +{ +} + +/*************************************************************************** + * Alloc -- Allocates system RAM. * + * * + * This is the basic RAM allocation function. It is used for all * + * memory allocations needed by the system or the main program. * + * * + * INPUT: bytes_to_alloc -- LONG value of the number of bytes to alloc. * + * * + * flags -- Memory allocation control flags. * + * MEM_NORMAL: No special flags. * + * MEM_CLEAR: Zero out memory block. * + * MEM_NEW: Called by a new. * + * * + * OUTPUT: Returns with pointer to allocated block. If NULL was returned * + * it indicates a failure to allocate. Note: NULL will never be * + * returned if the standard library allocation error routine is * + * used. * + * * + * WARNINGS: If you replace the standard memory allocation error routine * + * and make it so that Alloc CAN return with a NULL, be sure * + * and check for this in your code. * + * * + * HISTORY: * + * 09/03/1991 JLB : Documented. * + * 08/09/1993 JLB : Updated with EMS memory support. * + * 04/28/1994 JAW : Updated to 32bit Protected mode. * + * 03/09/1995 JLB : Fixed * + * 09/28/1995 ST : Simplified for win95 * + *=========================================================================*/ +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags) +{ + +#ifdef WIN32 + + void *mem_ptr; + +#ifdef MEM_CHECK + bytes_to_alloc += 32; +#endif //MEM_CHECK + + mem_ptr = malloc ( bytes_to_alloc ); + + if ( !mem_ptr && Memory_Error ){ + Memory_Error(); + } + + if ( mem_ptr && ( flags & MEM_CLEAR ) ){ + memset ( mem_ptr , 0 , bytes_to_alloc ); + } + +#ifdef MEM_CHECK + mem_ptr = (void*)((char*)mem_ptr + 16); + unsigned long *magic_ptr =(unsigned long*) ( ((char *)mem_ptr) - 16 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = bytes_to_alloc - 32; + magic_ptr = (unsigned long*) ( ((char*)mem_ptr) + bytes_to_alloc - 32 ); + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr++ = (unsigned long)mem_ptr; + *magic_ptr = (unsigned long)mem_ptr; +#endif //MEM_CHECK + + Memory_Calls++; + return ( mem_ptr ); + +#else + + + + union REGS regs ; + struct SREGS sregs ; + unsigned char *retval=NULL; // Pointer to allocated block. + unsigned long original_size; // Original allocation size. + unsigned long bytesfree; // Number of free bytes. + long *longptr=NULL; // Pointer used to store selector + + /* + ** Save the original allocated space size so that we can clear the + ** exact amount of RAM if they specified MEM_CLEAR. + */ + original_size = bytes_to_alloc; + + /* + ** Reserve one byte for the header of the memory we allocated. + ** We will store the flags variable there for later use. + */ + bytes_to_alloc += (flags & MEM_LOCK) ? 5 : 1; + + /* + ** Initialize the total ram available value. + */ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + + + // Try to allocate the memory out of the protected mode memory + // chain if we did not require a real mode allocation. If this + // fails we will have to try to allocate it out of real mode memory. + // Real mode memory is a last resort because some types of applications + // require real mode memory. + if (!(flags & MEM_REAL)) { + retval = (unsigned char*)malloc(bytes_to_alloc); + } + + // Try to allocate the memory out of the real mode memory using DPMI + // service 0x100. Note that retval will be null if we are requesting + // real mode memory so that we do not have to explicitly check for the + // real mode flag. Remember we need to reserve room for the dos + // selector value at the beginning of our allocated block so rather than + // adding fifteen and rounding, we need to add 19 and round. + if (!retval) { + flags = (MemoryFlagType)(flags | MEM_REAL); + regs.x.eax = 0x100; + regs.x.ebx = (bytes_to_alloc + 19) >> 4; + if (regs.x.ebx & 0xFFFF0000) { + retval = NULL; + } else { + segread ( & sregs ) ; + int386x ( 0x31 , & regs, & regs , & sregs ) ; + if (regs.x.cflag) + retval = NULL; + else { + longptr = (long *)(((regs.x.eax & 0xFFFF) << 4)+ 1); + *longptr++ = regs.x.edx & 0xFFFF; + retval = (unsigned char *)longptr; + } + } + } + + // If the alloc failed then we need to signify a memory error. + if (retval == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + // If the memory needs to be DPMI locked then we should store the + // original size in the header before we store the flags. + if (flags & MEM_LOCK) { + longptr = (long *)retval; + *longptr++ = original_size; + retval = (unsigned char *)longptr; + } + + + // Now that we know the alloc was sucessful (and for an extra byte + // more than the user wanted) we need to stick in the memory flags. + *retval++ = flags; + + // If the memory needed to be DPMI locked then set it up so it + // is locked. + if (flags & MEM_LOCK) { + DPMI_Lock(retval, original_size); + + } + + + /* Clear the space if they wanted it clear */ + + if (flags & MEM_CLEAR) { + unsigned char *ptr; // Working memory block pointer. + + ptr = retval; + memset(ptr, '\0', original_size); + } + + bytesfree = Total_Ram_Free(MEM_NORMAL); + if (bytesfree < MinRam) { + MinRam = bytesfree; + } + if (TotalRam-bytesfree > MaxRam) { + MaxRam = TotalRam-bytesfree; + } + + Memory_Calls++; + + return(retval); + +#endif +} + + +/*************************************************************************** + * Free -- Free an Alloc'ed block of RAM. * + * * + * FUNCTION: * + * * + * INPUT: A pointer to a block of RAM from Alloc. * + * * + * OUTPUT: None. * + * * + * WARNINGS: Don't use this for an Alloc_Block'ed RAM block. * + * * + * HISTORY: * + * 05/25/1990 : Created. * + ***************************************************************************/ +#ifdef WIN32 + +void Free(void const *pointer) +{ + + if ( pointer ){ + +#ifdef MEM_CHECK + + unsigned long *magic_ptr = (unsigned long*) ( ((char*)pointer) - 16 ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + magic_ptr = (unsigned long*) ( ((char*)pointer) + *magic_ptr ); + + if (*magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer || + *magic_ptr++ != (unsigned long)pointer ){ + Int3(); + } + + pointer = (void*) (((char*)pointer)-16); +#endif //MEM_CHECK + + free ( (void*)pointer ); + Memory_Calls--; + } + +#else + +void Free(void const *pointer) +{ + + union REGS regs ; + struct SREGS sregs ; + + + if (pointer) { + /* + ** Get a pointer to the flags that we stored off. + */ + char *byteptr = ((char *)pointer) - 1; + + /* + ** Check to see if this was locked me and if it was unlock it. + */ + if (*byteptr & MEM_LOCK) { + long *longptr = ((long *)byteptr) - 1; + DPMI_Unlock(pointer, *longptr); + pointer = (void *)longptr; + } else + pointer = (void *)byteptr; + + + // If the pointer is a real mode pointer than it will point to the + // first megabyte of system memory. If it does than we need to + // use DPMI to free it. + if (*byteptr & MEM_REAL) { + regs.x.eax = 0x101; + regs.x.edx = *(((long *)pointer) - 1); + segread ( & sregs ) ; + int386x(0x31, ®s, ®s, &sregs); + } else { + free((void *)pointer); + } + Memory_Calls--; + } + +#endif +} + + + +/*************************************************************************** + * Resize_Alloc -- Change the size of an allocated block. * + * * + * This routine will take a previously allocated block and change its * + * size without unnecessarily altering its contents. * + * * + * INPUT: pointer -- Pointer to the original memory allocation. * + * * + * new_size -- Size in bytes that it will be converted to. * + * * + * OUTPUT: Returns with a pointer to the new allocation. * + * * + * WARNINGS: ??? * + * * + * HISTORY: * + * 02/01/1992 JLB : Commented. * + *=========================================================================*/ +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes) +{ + + unsigned long *temp; + + temp = (unsigned long*)original_ptr; + + /* ReAlloc the space */ + temp = (unsigned long *)realloc(temp, new_size_in_bytes); + if (temp == NULL) { + if(Memory_Error != NULL) + Memory_Error(); + return NULL; + } + + return(temp); +} + + +/*************************************************************************** + * Ram_Free -- Determines the largest free chunk of RAM. * + * * + * Use this routine to determine the largest free chunk of available * + * RAM for allocation. It also performs a check of the memory chain. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the size of the largest free chunk of RAM. * + * * + * WARNINGS: This does not return the TOTAL memory free, only the * + * largest free chunk. * + * * + * HISTORY: * + * 09/03/1991 JLB : Commented. * + *=========================================================================*/ +long Ram_Free(MemoryFlagType) +{ +// return(_memmax()); +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + return ( 64*1024*1024 ); +} + + +/*************************************************************************** + * Heap_Size -- Size of the heap we have. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + *=========================================================================*/ +long Heap_Size(MemoryFlagType ) +{ + if (!TotalRam) { + TotalRam = Total_Ram_Free(MEM_NORMAL); + } + return(TotalRam); +} + + +/*************************************************************************** + * Total_Ram_Free -- Total amount of free RAM. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/21/1994 SKB : Created. * + * 03/09/1995 JLB : Uses prerecorded heap size maximum. * + *=========================================================================*/ +long Total_Ram_Free(MemoryFlagType ) +{ +#if(0) + MEMORYSTATUS mem_info; + mem_info.dwLength=sizeof(mem_info); + GlobalMemoryStatus(&mem_info); + return ( mem_info.dwAvailPhys ); +#endif + + return ( 64*1024*1024 ); +} + diff --git a/code/redalert/win32lib/audio.h b/code/redalert/win32lib/audio.h new file mode 100644 index 0000000..1e5d9ad --- /dev/null +++ b/code/redalert/win32lib/audio.h @@ -0,0 +1,159 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : AUDIO.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : March 10, 1995 * + * * + * Last Update : March 10, 1995 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" + +/*=========================================================================*/ +/* AUD file header type */ +/*=========================================================================*/ +#define AUD_FLAG_STEREO 1 +#define AUD_FLAG_16BIT 2 + +// PWG 3-14-95: This structure used to have bit fields defined for Stereo +// and Bits. These were removed because watcom packs them into a 32 bit +// flag entry even though they could have fit in a 8 bit entry. +//#pragma pack(1); +#pragma pack(push,1) +typedef struct { + unsigned short int Rate; // Playback rate (hertz). + long Size; // Size of data (bytes). + long UncompSize; // Size of data (bytes). + unsigned char Flags; // Holds flags for info + // 1: Is the sample stereo? + // 2: Is the sample 16 bits? + unsigned char Compression; // What kind of compression for this sample? +} AUDHeaderType; +#pragma pack(pop) + + +/*=========================================================================*/ +/* There can be a different sound driver for sound effects, digitized */ +/* samples, and musical scores. Each one must be of these specified */ +/* types. */ +/*=========================================================================*/ +typedef enum { + SAMPLE_NONE, // No digitized sounds will be played. + SAMPLE_SB, // Sound Blaster digitized driver. + SAMPLE_SBPRO, // Sound Blaster Pro digitized driver. + SAMPLE_PAS, // Pro-Audio Spectrum digitized driver. + SAMPLE_ADLIBG, // Adlib-Gold digitized driver. + SAMPLE_TANDY, // Tandy 'compatible' driver. + SAMPLE_PCSPKR, // PC speaker digitized driver (The Audio Solution driver). + SAMPLE_ADLIB, // Adlib digitized driver (The Audio Solution driver). + SAMPLE_TEMP=0x1000, + SAMPLE_LAST +} Sample_Type; + +typedef enum { + SCORE_NONE, // No scores will be played. + SCORE_ALFX, // Westwood's ALFX adlib compatable driver. + SCORE_WWPCSPKR, // Westwood's PC-speaker driver (obsolete). + SCORE_WWTANDY, // Westwood's PC-speaker driver with Tandy mod (obsolete). + SCORE_PCSPKR, // PC speaker XMIDI driver. + SCORE_TANDY, // Tandy XMIDI driver. + SCORE_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SCORE_CANVAS, // Sound Canvas SC-55. + SCORE_ADLIB, // Adlib XMIDI driver. + SCORE_ADLIBG, // Adlib Gold XMIDI driver. + SCORE_PASFM, // Pro Audio Spectrum XMIDI driver. + SCORE_SBFM, // Sound Blaster XMIDI driver. + SCORE_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SCORE_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver (Can't use with SFX_ALFX). + SCORE_TEMP=0x1000, + SCORE_LAST +} Score_Type; + +typedef enum { + SFX_NONE, // No sound effects will be played. + SFX_ALFX, // Westwood's ALFX adlib compatable driver. + SFX_WWPCSPKR, // Westwood's PC-speaker driver. + SFX_WWTANDY, // Westwood's PC-speaker driver with Tandy mod. + SFX_PCSPKR, // PC speaker XMIDI driver. + SFX_TANDY, // Tandy XMIDI driver. + SFX_MT32, // MT-32 / LAPC-1 Roland XMIDI driver. + SFX_CANVAS, // Sound Canvas SC-55. + SFX_ADLIB, // Adlib XMIDI driver. + SFX_ADLIBG, // Adlib Gold XMIDI driver. + SFX_PASFM, // Pro Audio Spectrum XMIDI driver. + SFX_SBFM, // Sound Blaster XMIDI driver. + SFX_SBP1FM, // Sound Blaster Pro (YM3812) XMIDI driver. + SFX_SBP2FM, // Sound Blaster Pro (OPL3) XMIDI driver. + SFX_TEMP=0x1000, + SFX_LAST +} SFX_Type; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ +int File_Stream_Sample(char const *filename, BOOL real_time_start = FALSE); +int File_Stream_Sample_Vol(char const *filename, int volume, BOOL real_time_start = FALSE); +void __cdecl Sound_Callback(void); +void __cdecl far maintenance_callback(void); +void *Load_Sample(char const *filename); +long Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long Sample_Read(int fh, void *buffer, long size); +void Free_Sample(void const *sample); +BOOL Audio_Init( HWND window , int bits_per_sample, BOOL stereo , int rate , int reverse_channels); +void Sound_End(void); +void Stop_Sample(int handle); +BOOL Sample_Status(int handle); +BOOL Is_Sample_Playing(void const * sample); +void Stop_Sample_Playing(void const * sample); +int Play_Sample(void const *sample, int priority=0xFF, int volume=0xFF, signed short panloc = 0x0); +int Play_Sample_Handle(void const *sample, int priority, int volume, signed short panloc, int id); +int Set_Sound_Vol(int volume); +int Set_Score_Vol(int volume); +void Fade_Sample(int handle, int ticks); +int Get_Free_Sample_Handle(int priority); +int Get_Digi_Handle(void); +long Sample_Length(void const *sample); +void Restore_Sound_Buffers (void); +BOOL Set_Primary_Buffer_Format(void); +BOOL Start_Primary_Sound_Buffer (BOOL forced); +void Stop_Primary_Sound_Buffer (void); + +/* +** Function to call if we detect focus loss +*/ +extern void (*Audio_Focus_Loss_Function)(void); + + +extern int Misc; +extern SFX_Type SoundType; +extern Sample_Type SampleType; + +extern CRITICAL_SECTION GlobalAudioCriticalSection; + +extern int StreamLowImpact; \ No newline at end of file diff --git a/code/redalert/win32lib/buffer.cpp b/code/redalert/win32lib/buffer.cpp new file mode 100644 index 0000000..7affb08 --- /dev/null +++ b/code/redalert/win32lib/buffer.cpp @@ -0,0 +1,128 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : BUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 18, 1994 * + * * + * Last Update : June 1, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::BufferClass -- The default (void) constructor for a buffer class * + * BC::~BufferClass -- The destructor for the buffer class * + * BC::BufferClass -- The standard constructor for a buffer class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#include "buffer.h" +#endif + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * BC::BufferClass -- The standard constructor for a buffer class * + * * + * INPUT: VOID * buffer to which should be included in buffer class * + * LONG size of the buffer which we included * + * * + * OUTPUT: NONE * + * * + * WARNINGS: If the buffer passed to this function is equal to NULL, * + * the buffer will be allocated using new. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID *buffer, LONG size) +{ + Size = size; // find size of physical buffer + + if (buffer) { // if buffer is specified + Buffer = (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } +} + +/*************************************************************************** + * BC::BufferClass -- constructor for BufferClass with size only * + * * + * INPUT: LONG the size of the buffer that needs to be allocated * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(LONG size) +{ + Size = size; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced +} + +/*************************************************************************** + * BC::BufferClass -- The default (void) constructor for a buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * NOTES: The primary function of this class is to be called by a * + * derived class which will fill in the values after the * + * fact. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::BufferClass(VOID) +{ + Buffer = NULL; + Size = 0; + Allocated = FALSE; +} + +/*************************************************************************** + * BC::~BUFFERCLASS -- The destructor for the buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +BufferClass::~BufferClass(VOID) +{ + if (Allocated) { + delete[] Buffer; + } +} diff --git a/code/redalert/win32lib/buffer.h b/code/redalert/win32lib/buffer.h new file mode 100644 index 0000000..39ce894 --- /dev/null +++ b/code/redalert/win32lib/buffer.h @@ -0,0 +1,121 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : July 5, 1994 [PWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * BC::Get_Size -- Returns the buffer size of the BufferClass instance * + * BC::Get_Buffer -- Returns pointer to buffer inherent to BufferClass * + * BC::BufferClass -- inline constructor for BufferClass with size only * + * BC::To_Page -- Copys a buffer class to a page with definable x, y, w, h* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef BUFFER_H +#define BUFFER_H + + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; + +/*=========================================================================*/ +/* BufferClass - A base class which holds buffer information including a */ +/* pointer and the size of the buffer. */ +/*=========================================================================*/ +class BufferClass { + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + BufferClass(void *ptr, long size); + BufferClass(long size); + BufferClass(); + ~BufferClass(); + /*===================================================================*/ + /* Define functions which work with the buffer class. */ + /*===================================================================*/ + long To_Page(GraphicViewPortClass &view); + long To_Page(int w, int h, GraphicViewPortClass &view); + long To_Page(int x, int y, int w, int h, GraphicViewPortClass &view); + + /*===================================================================*/ + /* define functions to get at the protected data members */ + /*===================================================================*/ + void *Get_Buffer(void); + long Get_Size(void); + + private: + /*===================================================================*/ + /* Define the operators we do not want to happen which are the copy */ + /* and equal constructors. These are bad because the Allocated flag */ + /* could be copied and the associated buffer freed. If this were to */ + /* gappen it could cause weird general protection fault. */ + /*===================================================================*/ + BufferClass(BufferClass const &); + BufferClass &operator=(BufferClass const &); + + protected: + void *Buffer; + long Size; + BOOL Allocated; +}; +/*************************************************************************** + * BC::GET_SIZE -- Returns the buffer size of the BufferClass instance * + * * + * INPUT: none * + * * + * OUTPUT: long the size of the buffer * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::Get_Size(void) +{ + return(Size); +} +/*************************************************************************** + * BC::GET_BUFFER -- Returns pointer to buffer inherent to BufferClass * + * * + * INPUT: none * + * * + * OUTPUT: void * to the inherent buffer. * + * * + * HISTORY: * + * 06/01/1994 PWG : Created. * + *=========================================================================*/ +inline void *BufferClass::Get_Buffer(void) +{ + return(Buffer); +} +#endif diff --git a/code/redalert/win32lib/buffglbl.cpp b/code/redalert/win32lib/buffglbl.cpp new file mode 100644 index 0000000..6c16b9d --- /dev/null +++ b/code/redalert/win32lib/buffglbl.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : BUFFGLBL.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : January 10, 1995 * + * * + * Last Update : January 10, 1995 [PWG] * + * * + * This module holds the global fixup tables for the MCGA buffer class. * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "gbuffer.h" + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*=========================================================================*/ +/* Globals required by GraphicBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +BOOL (*GVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*GVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + +#ifdef not_any_more_it_doesnt +/*=========================================================================*/ +/* Globals required by VideoBufferClass for function pointers. These */ +/* pointers will be set to the proper function when set mode is called. */ +/*=========================================================================*/ +void (*VVPC_Clear_Func)(void *, unsigned char); +long (*VVPC_To_Buffer_Func)(void *,int x, int y, int w, int h, void *buff, long size); +void (*VVPC_Put_Pixel_Func)(void *,int x, int y, unsigned char color); +int (*VVPC_Get_Pixel_Func)(void *, int x, int y); +long (*VVPC_Buffer_To_Page)(int x, int y, int w, int h, void *Buffer, void *view); +BOOL (*VVPC_Blit_to_GVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Blit_to_VVPC_Func)(void *, void *, int, int, int, int, int, int, BOOL); +BOOL (*VVPC_Scale_To_GVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +BOOL (*VVPC_Scale_To_VVPC)( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); +LONG (*VVPC_Print_Func)( void *, const char *, int, int, int, int); +void (*VVPC_Draw_Stamp)(void *, void *, int, int, int, void *); +long (*VVPC_Size_Of_Region)(void *, int, int); + +#endif //not_any_more_it_doesnt + +/*=========================================================================*/ +/* We need to keep a pointer to the logic page hanging around somewhere */ +/*=========================================================================*/ +GraphicViewPortClass *LogicPage; + +BOOL IconCacheAllowed = TRUE; + +/* +** Pointer to a function we will call if we detect loss of focus +*/ +void (*Gbuffer_Focus_Loss_Function)(void) = NULL; diff --git a/code/redalert/win32lib/defines.h b/code/redalert/win32lib/defines.h new file mode 100644 index 0000000..954e91d --- /dev/null +++ b/code/redalert/win32lib/defines.h @@ -0,0 +1,36 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : DEFINES.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +#define USER_TIMER_FREQ 60 diff --git a/code/redalert/win32lib/delay.cpp b/code/redalert/win32lib/delay.cpp new file mode 100644 index 0000000..51cf579 --- /dev/null +++ b/code/redalert/win32lib/delay.cpp @@ -0,0 +1,59 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : DELAY.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : 27 March, 1991 [CY] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "wwstd.h" +#include + +void Delay(int duration) +{ + unsigned long count; + TimerClass timer(BT_SYSTEM,TRUE); + + while (duration--) { + count = timer.Time() + 1L; + while (count >= (unsigned)timer.Time()) { + ; + } + } + +#if(FALSE) + while (duration--) + Wait_Vert_Blank(VertBlank); +#endif +} + +#if(FALSE) +void Vsync() +{ + Wait_Vert_Blank(VertBlank); +} +#endif + \ No newline at end of file diff --git a/code/redalert/win32lib/descmgmt.h b/code/redalert/win32lib/descmgmt.h new file mode 100644 index 0000000..f810255 --- /dev/null +++ b/code/redalert/win32lib/descmgmt.h @@ -0,0 +1,90 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 library * + * * + * File Name : DESCMGMT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef DESCMGMT_H +#define DESCMGMT_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +//===================================================================== +// C type include files +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +// ==================================================================== + + +// types +// These where taken from dos.h +//========================================== + +// external functions +// =================================================== +extern ULONG Map_Segment_To_Address(ULONG address, ULONG length); + +extern "C" { + // Assemble functions + extern UWORD FixSelector(UWORD sel); + extern UWORD GetDs(void); + extern UWORD GetCs(void); + extern VOID GetDefaultSelectors(VOID); + extern UWORD Get_Standard_Selector(void); + + + // Assembly data variables + extern UWORD CodeSelector; + extern UWORD DataSelector; + extern UWORD ScreenSelector; + extern UWORD GraphicsSelector; + extern UWORD PspSelector; + extern UWORD EnvSelector; + extern UWORD DosMemSelector; + extern UWORD Fp1167Selector; + extern UWORD FpWeitekSelector; + extern UWORD FpCyrixSelector; +} + +#endif // DESCMGMT_H + + diff --git a/code/redalert/win32lib/difftb.inc b/code/redalert/win32lib/difftb.inc new file mode 100644 index 0000000..0c345aa --- /dev/null +++ b/code/redalert/win32lib/difftb.inc @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* dwDiffTable - ADPCM Lookup table for sample differences +;**************************************************************************** + + align 4 + +dwDiffTable DD 0 ; Index = 0 Token = 0 + DD 1 ; Index = 0 Token = 1 + DD 3 ; Index = 0 Token = 2 + DD 4 ; Index = 0 Token = 3 + DD 7 ; Index = 0 Token = 4 + DD 8 ; Index = 0 Token = 5 + DD 10 ; Index = 0 Token = 6 + DD 11 ; Index = 0 Token = 7 + DD 0 ; Index = 0 Token = 8 + DD -1 ; Index = 0 Token = 9 + DD -3 ; Index = 0 Token = 10 + DD -4 ; Index = 0 Token = 11 + DD -7 ; Index = 0 Token = 12 + DD -8 ; Index = 0 Token = 13 + DD -10 ; Index = 0 Token = 14 + DD -11 ; Index = 0 Token = 15 + DD 1 ; Index = 1 Token = 0 + DD 3 ; Index = 1 Token = 1 + DD 5 ; Index = 1 Token = 2 + DD 7 ; Index = 1 Token = 3 + DD 9 ; Index = 1 Token = 4 + DD 11 ; Index = 1 Token = 5 + DD 13 ; Index = 1 Token = 6 + DD 15 ; Index = 1 Token = 7 + DD -1 ; Index = 1 Token = 8 + DD -3 ; Index = 1 Token = 9 + DD -5 ; Index = 1 Token = 10 + DD -7 ; Index = 1 Token = 11 + DD -9 ; Index = 1 Token = 12 + DD -11 ; Index = 1 Token = 13 + DD -13 ; Index = 1 Token = 14 + DD -15 ; Index = 1 Token = 15 + DD 1 ; Index = 2 Token = 0 + DD 3 ; Index = 2 Token = 1 + DD 5 ; Index = 2 Token = 2 + DD 7 ; Index = 2 Token = 3 + DD 10 ; Index = 2 Token = 4 + DD 12 ; Index = 2 Token = 5 + DD 14 ; Index = 2 Token = 6 + DD 16 ; Index = 2 Token = 7 + DD -1 ; Index = 2 Token = 8 + DD -3 ; Index = 2 Token = 9 + DD -5 ; Index = 2 Token = 10 + DD -7 ; Index = 2 Token = 11 + DD -10 ; Index = 2 Token = 12 + DD -12 ; Index = 2 Token = 13 + DD -14 ; Index = 2 Token = 14 + DD -16 ; Index = 2 Token = 15 + DD 1 ; Index = 3 Token = 0 + DD 3 ; Index = 3 Token = 1 + DD 6 ; Index = 3 Token = 2 + DD 8 ; Index = 3 Token = 3 + DD 11 ; Index = 3 Token = 4 + DD 13 ; Index = 3 Token = 5 + DD 16 ; Index = 3 Token = 6 + DD 18 ; Index = 3 Token = 7 + DD -1 ; Index = 3 Token = 8 + DD -3 ; Index = 3 Token = 9 + DD -6 ; Index = 3 Token = 10 + DD -8 ; Index = 3 Token = 11 + DD -11 ; Index = 3 Token = 12 + DD -13 ; Index = 3 Token = 13 + DD -16 ; Index = 3 Token = 14 + DD -18 ; Index = 3 Token = 15 + DD 1 ; Index = 4 Token = 0 + DD 3 ; Index = 4 Token = 1 + DD 6 ; Index = 4 Token = 2 + DD 8 ; Index = 4 Token = 3 + DD 12 ; Index = 4 Token = 4 + DD 14 ; Index = 4 Token = 5 + DD 17 ; Index = 4 Token = 6 + DD 19 ; Index = 4 Token = 7 + DD -1 ; Index = 4 Token = 8 + DD -3 ; Index = 4 Token = 9 + DD -6 ; Index = 4 Token = 10 + DD -8 ; Index = 4 Token = 11 + DD -12 ; Index = 4 Token = 12 + DD -14 ; Index = 4 Token = 13 + DD -17 ; Index = 4 Token = 14 + DD -19 ; Index = 4 Token = 15 + DD 1 ; Index = 5 Token = 0 + DD 4 ; Index = 5 Token = 1 + DD 7 ; Index = 5 Token = 2 + DD 10 ; Index = 5 Token = 3 + DD 13 ; Index = 5 Token = 4 + DD 16 ; Index = 5 Token = 5 + DD 19 ; Index = 5 Token = 6 + DD 22 ; Index = 5 Token = 7 + DD -1 ; Index = 5 Token = 8 + DD -4 ; Index = 5 Token = 9 + DD -7 ; Index = 5 Token = 10 + DD -10 ; Index = 5 Token = 11 + DD -13 ; Index = 5 Token = 12 + DD -16 ; Index = 5 Token = 13 + DD -19 ; Index = 5 Token = 14 + DD -22 ; Index = 5 Token = 15 + DD 1 ; Index = 6 Token = 0 + DD 4 ; Index = 6 Token = 1 + DD 7 ; Index = 6 Token = 2 + DD 10 ; Index = 6 Token = 3 + DD 14 ; Index = 6 Token = 4 + DD 17 ; Index = 6 Token = 5 + DD 20 ; Index = 6 Token = 6 + DD 23 ; Index = 6 Token = 7 + DD -1 ; Index = 6 Token = 8 + DD -4 ; Index = 6 Token = 9 + DD -7 ; Index = 6 Token = 10 + DD -10 ; Index = 6 Token = 11 + DD -14 ; Index = 6 Token = 12 + DD -17 ; Index = 6 Token = 13 + DD -20 ; Index = 6 Token = 14 + DD -23 ; Index = 6 Token = 15 + DD 1 ; Index = 7 Token = 0 + DD 4 ; Index = 7 Token = 1 + DD 8 ; Index = 7 Token = 2 + DD 11 ; Index = 7 Token = 3 + DD 15 ; Index = 7 Token = 4 + DD 18 ; Index = 7 Token = 5 + DD 22 ; Index = 7 Token = 6 + DD 25 ; Index = 7 Token = 7 + DD -1 ; Index = 7 Token = 8 + DD -4 ; Index = 7 Token = 9 + DD -8 ; Index = 7 Token = 10 + DD -11 ; Index = 7 Token = 11 + DD -15 ; Index = 7 Token = 12 + DD -18 ; Index = 7 Token = 13 + DD -22 ; Index = 7 Token = 14 + DD -25 ; Index = 7 Token = 15 + DD 2 ; Index = 8 Token = 0 + DD 6 ; Index = 8 Token = 1 + DD 10 ; Index = 8 Token = 2 + DD 14 ; Index = 8 Token = 3 + DD 18 ; Index = 8 Token = 4 + DD 22 ; Index = 8 Token = 5 + DD 26 ; Index = 8 Token = 6 + DD 30 ; Index = 8 Token = 7 + DD -2 ; Index = 8 Token = 8 + DD -6 ; Index = 8 Token = 9 + DD -10 ; Index = 8 Token = 10 + DD -14 ; Index = 8 Token = 11 + DD -18 ; Index = 8 Token = 12 + DD -22 ; Index = 8 Token = 13 + DD -26 ; Index = 8 Token = 14 + DD -30 ; Index = 8 Token = 15 + DD 2 ; Index = 9 Token = 0 + DD 6 ; Index = 9 Token = 1 + DD 10 ; Index = 9 Token = 2 + DD 14 ; Index = 9 Token = 3 + DD 19 ; Index = 9 Token = 4 + DD 23 ; Index = 9 Token = 5 + DD 27 ; Index = 9 Token = 6 + DD 31 ; Index = 9 Token = 7 + DD -2 ; Index = 9 Token = 8 + DD -6 ; Index = 9 Token = 9 + DD -10 ; Index = 9 Token = 10 + DD -14 ; Index = 9 Token = 11 + DD -19 ; Index = 9 Token = 12 + DD -23 ; Index = 9 Token = 13 + DD -27 ; Index = 9 Token = 14 + DD -31 ; Index = 9 Token = 15 + DD 2 ; Index = 10 Token = 0 + DD 6 ; Index = 10 Token = 1 + DD 11 ; Index = 10 Token = 2 + DD 15 ; Index = 10 Token = 3 + DD 21 ; Index = 10 Token = 4 + DD 25 ; Index = 10 Token = 5 + DD 30 ; Index = 10 Token = 6 + DD 34 ; Index = 10 Token = 7 + DD -2 ; Index = 10 Token = 8 + DD -6 ; Index = 10 Token = 9 + DD -11 ; Index = 10 Token = 10 + DD -15 ; Index = 10 Token = 11 + DD -21 ; Index = 10 Token = 12 + DD -25 ; Index = 10 Token = 13 + DD -30 ; Index = 10 Token = 14 + DD -34 ; Index = 10 Token = 15 + DD 2 ; Index = 11 Token = 0 + DD 7 ; Index = 11 Token = 1 + DD 12 ; Index = 11 Token = 2 + DD 17 ; Index = 11 Token = 3 + DD 23 ; Index = 11 Token = 4 + DD 28 ; Index = 11 Token = 5 + DD 33 ; Index = 11 Token = 6 + DD 38 ; Index = 11 Token = 7 + DD -2 ; Index = 11 Token = 8 + DD -7 ; Index = 11 Token = 9 + DD -12 ; Index = 11 Token = 10 + DD -17 ; Index = 11 Token = 11 + DD -23 ; Index = 11 Token = 12 + DD -28 ; Index = 11 Token = 13 + DD -33 ; Index = 11 Token = 14 + DD -38 ; Index = 11 Token = 15 + DD 2 ; Index = 12 Token = 0 + DD 7 ; Index = 12 Token = 1 + DD 13 ; Index = 12 Token = 2 + DD 18 ; Index = 12 Token = 3 + DD 25 ; Index = 12 Token = 4 + DD 30 ; Index = 12 Token = 5 + DD 36 ; Index = 12 Token = 6 + DD 41 ; Index = 12 Token = 7 + DD -2 ; Index = 12 Token = 8 + DD -7 ; Index = 12 Token = 9 + DD -13 ; Index = 12 Token = 10 + DD -18 ; Index = 12 Token = 11 + DD -25 ; Index = 12 Token = 12 + DD -30 ; Index = 12 Token = 13 + DD -36 ; Index = 12 Token = 14 + DD -41 ; Index = 12 Token = 15 + DD 3 ; Index = 13 Token = 0 + DD 9 ; Index = 13 Token = 1 + DD 15 ; Index = 13 Token = 2 + DD 21 ; Index = 13 Token = 3 + DD 28 ; Index = 13 Token = 4 + DD 34 ; Index = 13 Token = 5 + DD 40 ; Index = 13 Token = 6 + DD 46 ; Index = 13 Token = 7 + DD -3 ; Index = 13 Token = 8 + DD -9 ; Index = 13 Token = 9 + DD -15 ; Index = 13 Token = 10 + DD -21 ; Index = 13 Token = 11 + DD -28 ; Index = 13 Token = 12 + DD -34 ; Index = 13 Token = 13 + DD -40 ; Index = 13 Token = 14 + DD -46 ; Index = 13 Token = 15 + DD 3 ; Index = 14 Token = 0 + DD 10 ; Index = 14 Token = 1 + DD 17 ; Index = 14 Token = 2 + DD 24 ; Index = 14 Token = 3 + DD 31 ; Index = 14 Token = 4 + DD 38 ; Index = 14 Token = 5 + DD 45 ; Index = 14 Token = 6 + DD 52 ; Index = 14 Token = 7 + DD -3 ; Index = 14 Token = 8 + DD -10 ; Index = 14 Token = 9 + DD -17 ; Index = 14 Token = 10 + DD -24 ; Index = 14 Token = 11 + DD -31 ; Index = 14 Token = 12 + DD -38 ; Index = 14 Token = 13 + DD -45 ; Index = 14 Token = 14 + DD -52 ; Index = 14 Token = 15 + DD 3 ; Index = 15 Token = 0 + DD 10 ; Index = 15 Token = 1 + DD 18 ; Index = 15 Token = 2 + DD 25 ; Index = 15 Token = 3 + DD 34 ; Index = 15 Token = 4 + DD 41 ; Index = 15 Token = 5 + DD 49 ; Index = 15 Token = 6 + DD 56 ; Index = 15 Token = 7 + DD -3 ; Index = 15 Token = 8 + DD -10 ; Index = 15 Token = 9 + DD -18 ; Index = 15 Token = 10 + DD -25 ; Index = 15 Token = 11 + DD -34 ; Index = 15 Token = 12 + DD -41 ; Index = 15 Token = 13 + DD -49 ; Index = 15 Token = 14 + DD -56 ; Index = 15 Token = 15 + DD 4 ; Index = 16 Token = 0 + DD 12 ; Index = 16 Token = 1 + DD 21 ; Index = 16 Token = 2 + DD 29 ; Index = 16 Token = 3 + DD 38 ; Index = 16 Token = 4 + DD 46 ; Index = 16 Token = 5 + DD 55 ; Index = 16 Token = 6 + DD 63 ; Index = 16 Token = 7 + DD -4 ; Index = 16 Token = 8 + DD -12 ; Index = 16 Token = 9 + DD -21 ; Index = 16 Token = 10 + DD -29 ; Index = 16 Token = 11 + DD -38 ; Index = 16 Token = 12 + DD -46 ; Index = 16 Token = 13 + DD -55 ; Index = 16 Token = 14 + DD -63 ; Index = 16 Token = 15 + DD 4 ; Index = 17 Token = 0 + DD 13 ; Index = 17 Token = 1 + DD 22 ; Index = 17 Token = 2 + DD 31 ; Index = 17 Token = 3 + DD 41 ; Index = 17 Token = 4 + DD 50 ; Index = 17 Token = 5 + DD 59 ; Index = 17 Token = 6 + DD 68 ; Index = 17 Token = 7 + DD -4 ; Index = 17 Token = 8 + DD -13 ; Index = 17 Token = 9 + DD -22 ; Index = 17 Token = 10 + DD -31 ; Index = 17 Token = 11 + DD -41 ; Index = 17 Token = 12 + DD -50 ; Index = 17 Token = 13 + DD -59 ; Index = 17 Token = 14 + DD -68 ; Index = 17 Token = 15 + DD 5 ; Index = 18 Token = 0 + DD 15 ; Index = 18 Token = 1 + DD 25 ; Index = 18 Token = 2 + DD 35 ; Index = 18 Token = 3 + DD 46 ; Index = 18 Token = 4 + DD 56 ; Index = 18 Token = 5 + DD 66 ; Index = 18 Token = 6 + DD 76 ; Index = 18 Token = 7 + DD -5 ; Index = 18 Token = 8 + DD -15 ; Index = 18 Token = 9 + DD -25 ; Index = 18 Token = 10 + DD -35 ; Index = 18 Token = 11 + DD -46 ; Index = 18 Token = 12 + DD -56 ; Index = 18 Token = 13 + DD -66 ; Index = 18 Token = 14 + DD -76 ; Index = 18 Token = 15 + DD 5 ; Index = 19 Token = 0 + DD 16 ; Index = 19 Token = 1 + DD 27 ; Index = 19 Token = 2 + DD 38 ; Index = 19 Token = 3 + DD 50 ; Index = 19 Token = 4 + DD 61 ; Index = 19 Token = 5 + DD 72 ; Index = 19 Token = 6 + DD 83 ; Index = 19 Token = 7 + DD -5 ; Index = 19 Token = 8 + DD -16 ; Index = 19 Token = 9 + DD -27 ; Index = 19 Token = 10 + DD -38 ; Index = 19 Token = 11 + DD -50 ; Index = 19 Token = 12 + DD -61 ; Index = 19 Token = 13 + DD -72 ; Index = 19 Token = 14 + DD -83 ; Index = 19 Token = 15 + DD 6 ; Index = 20 Token = 0 + DD 18 ; Index = 20 Token = 1 + DD 31 ; Index = 20 Token = 2 + DD 43 ; Index = 20 Token = 3 + DD 56 ; Index = 20 Token = 4 + DD 68 ; Index = 20 Token = 5 + DD 81 ; Index = 20 Token = 6 + DD 93 ; Index = 20 Token = 7 + DD -6 ; Index = 20 Token = 8 + DD -18 ; Index = 20 Token = 9 + DD -31 ; Index = 20 Token = 10 + DD -43 ; Index = 20 Token = 11 + DD -56 ; Index = 20 Token = 12 + DD -68 ; Index = 20 Token = 13 + DD -81 ; Index = 20 Token = 14 + DD -93 ; Index = 20 Token = 15 + DD 6 ; Index = 21 Token = 0 + DD 19 ; Index = 21 Token = 1 + DD 33 ; Index = 21 Token = 2 + DD 46 ; Index = 21 Token = 3 + DD 61 ; Index = 21 Token = 4 + DD 74 ; Index = 21 Token = 5 + DD 88 ; Index = 21 Token = 6 + DD 101 ; Index = 21 Token = 7 + DD -6 ; Index = 21 Token = 8 + DD -19 ; Index = 21 Token = 9 + DD -33 ; Index = 21 Token = 10 + DD -46 ; Index = 21 Token = 11 + DD -61 ; Index = 21 Token = 12 + DD -74 ; Index = 21 Token = 13 + DD -88 ; Index = 21 Token = 14 + DD -101 ; Index = 21 Token = 15 + DD 7 ; Index = 22 Token = 0 + DD 22 ; Index = 22 Token = 1 + DD 37 ; Index = 22 Token = 2 + DD 52 ; Index = 22 Token = 3 + DD 67 ; Index = 22 Token = 4 + DD 82 ; Index = 22 Token = 5 + DD 97 ; Index = 22 Token = 6 + DD 112 ; Index = 22 Token = 7 + DD -7 ; Index = 22 Token = 8 + DD -22 ; Index = 22 Token = 9 + DD -37 ; Index = 22 Token = 10 + DD -52 ; Index = 22 Token = 11 + DD -67 ; Index = 22 Token = 12 + DD -82 ; Index = 22 Token = 13 + DD -97 ; Index = 22 Token = 14 + DD -112 ; Index = 22 Token = 15 + DD 8 ; Index = 23 Token = 0 + DD 24 ; Index = 23 Token = 1 + DD 41 ; Index = 23 Token = 2 + DD 57 ; Index = 23 Token = 3 + DD 74 ; Index = 23 Token = 4 + DD 90 ; Index = 23 Token = 5 + DD 107 ; Index = 23 Token = 6 + DD 123 ; Index = 23 Token = 7 + DD -8 ; Index = 23 Token = 8 + DD -24 ; Index = 23 Token = 9 + DD -41 ; Index = 23 Token = 10 + DD -57 ; Index = 23 Token = 11 + DD -74 ; Index = 23 Token = 12 + DD -90 ; Index = 23 Token = 13 + DD -107 ; Index = 23 Token = 14 + DD -123 ; Index = 23 Token = 15 + DD 9 ; Index = 24 Token = 0 + DD 27 ; Index = 24 Token = 1 + DD 45 ; Index = 24 Token = 2 + DD 63 ; Index = 24 Token = 3 + DD 82 ; Index = 24 Token = 4 + DD 100 ; Index = 24 Token = 5 + DD 118 ; Index = 24 Token = 6 + DD 136 ; Index = 24 Token = 7 + DD -9 ; Index = 24 Token = 8 + DD -27 ; Index = 24 Token = 9 + DD -45 ; Index = 24 Token = 10 + DD -63 ; Index = 24 Token = 11 + DD -82 ; Index = 24 Token = 12 + DD -100 ; Index = 24 Token = 13 + DD -118 ; Index = 24 Token = 14 + DD -136 ; Index = 24 Token = 15 + DD 10 ; Index = 25 Token = 0 + DD 30 ; Index = 25 Token = 1 + DD 50 ; Index = 25 Token = 2 + DD 70 ; Index = 25 Token = 3 + DD 90 ; Index = 25 Token = 4 + DD 110 ; Index = 25 Token = 5 + DD 130 ; Index = 25 Token = 6 + DD 150 ; Index = 25 Token = 7 + DD -10 ; Index = 25 Token = 8 + DD -30 ; Index = 25 Token = 9 + DD -50 ; Index = 25 Token = 10 + DD -70 ; Index = 25 Token = 11 + DD -90 ; Index = 25 Token = 12 + DD -110 ; Index = 25 Token = 13 + DD -130 ; Index = 25 Token = 14 + DD -150 ; Index = 25 Token = 15 + DD 11 ; Index = 26 Token = 0 + DD 33 ; Index = 26 Token = 1 + DD 55 ; Index = 26 Token = 2 + DD 77 ; Index = 26 Token = 3 + DD 99 ; Index = 26 Token = 4 + DD 121 ; Index = 26 Token = 5 + DD 143 ; Index = 26 Token = 6 + DD 165 ; Index = 26 Token = 7 + DD -11 ; Index = 26 Token = 8 + DD -33 ; Index = 26 Token = 9 + DD -55 ; Index = 26 Token = 10 + DD -77 ; Index = 26 Token = 11 + DD -99 ; Index = 26 Token = 12 + DD -121 ; Index = 26 Token = 13 + DD -143 ; Index = 26 Token = 14 + DD -165 ; Index = 26 Token = 15 + DD 12 ; Index = 27 Token = 0 + DD 36 ; Index = 27 Token = 1 + DD 60 ; Index = 27 Token = 2 + DD 84 ; Index = 27 Token = 3 + DD 109 ; Index = 27 Token = 4 + DD 133 ; Index = 27 Token = 5 + DD 157 ; Index = 27 Token = 6 + DD 181 ; Index = 27 Token = 7 + DD -12 ; Index = 27 Token = 8 + DD -36 ; Index = 27 Token = 9 + DD -60 ; Index = 27 Token = 10 + DD -84 ; Index = 27 Token = 11 + DD -109 ; Index = 27 Token = 12 + DD -133 ; Index = 27 Token = 13 + DD -157 ; Index = 27 Token = 14 + DD -181 ; Index = 27 Token = 15 + DD 13 ; Index = 28 Token = 0 + DD 39 ; Index = 28 Token = 1 + DD 66 ; Index = 28 Token = 2 + DD 92 ; Index = 28 Token = 3 + DD 120 ; Index = 28 Token = 4 + DD 146 ; Index = 28 Token = 5 + DD 173 ; Index = 28 Token = 6 + DD 199 ; Index = 28 Token = 7 + DD -13 ; Index = 28 Token = 8 + DD -39 ; Index = 28 Token = 9 + DD -66 ; Index = 28 Token = 10 + DD -92 ; Index = 28 Token = 11 + DD -120 ; Index = 28 Token = 12 + DD -146 ; Index = 28 Token = 13 + DD -173 ; Index = 28 Token = 14 + DD -199 ; Index = 28 Token = 15 + DD 14 ; Index = 29 Token = 0 + DD 43 ; Index = 29 Token = 1 + DD 73 ; Index = 29 Token = 2 + DD 102 ; Index = 29 Token = 3 + DD 132 ; Index = 29 Token = 4 + DD 161 ; Index = 29 Token = 5 + DD 191 ; Index = 29 Token = 6 + DD 220 ; Index = 29 Token = 7 + DD -14 ; Index = 29 Token = 8 + DD -43 ; Index = 29 Token = 9 + DD -73 ; Index = 29 Token = 10 + DD -102 ; Index = 29 Token = 11 + DD -132 ; Index = 29 Token = 12 + DD -161 ; Index = 29 Token = 13 + DD -191 ; Index = 29 Token = 14 + DD -220 ; Index = 29 Token = 15 + DD 16 ; Index = 30 Token = 0 + DD 48 ; Index = 30 Token = 1 + DD 81 ; Index = 30 Token = 2 + DD 113 ; Index = 30 Token = 3 + DD 146 ; Index = 30 Token = 4 + DD 178 ; Index = 30 Token = 5 + DD 211 ; Index = 30 Token = 6 + DD 243 ; Index = 30 Token = 7 + DD -16 ; Index = 30 Token = 8 + DD -48 ; Index = 30 Token = 9 + DD -81 ; Index = 30 Token = 10 + DD -113 ; Index = 30 Token = 11 + DD -146 ; Index = 30 Token = 12 + DD -178 ; Index = 30 Token = 13 + DD -211 ; Index = 30 Token = 14 + DD -243 ; Index = 30 Token = 15 + DD 17 ; Index = 31 Token = 0 + DD 52 ; Index = 31 Token = 1 + DD 88 ; Index = 31 Token = 2 + DD 123 ; Index = 31 Token = 3 + DD 160 ; Index = 31 Token = 4 + DD 195 ; Index = 31 Token = 5 + DD 231 ; Index = 31 Token = 6 + DD 266 ; Index = 31 Token = 7 + DD -17 ; Index = 31 Token = 8 + DD -52 ; Index = 31 Token = 9 + DD -88 ; Index = 31 Token = 10 + DD -123 ; Index = 31 Token = 11 + DD -160 ; Index = 31 Token = 12 + DD -195 ; Index = 31 Token = 13 + DD -231 ; Index = 31 Token = 14 + DD -266 ; Index = 31 Token = 15 + DD 19 ; Index = 32 Token = 0 + DD 58 ; Index = 32 Token = 1 + DD 97 ; Index = 32 Token = 2 + DD 136 ; Index = 32 Token = 3 + DD 176 ; Index = 32 Token = 4 + DD 215 ; Index = 32 Token = 5 + DD 254 ; Index = 32 Token = 6 + DD 293 ; Index = 32 Token = 7 + DD -19 ; Index = 32 Token = 8 + DD -58 ; Index = 32 Token = 9 + DD -97 ; Index = 32 Token = 10 + DD -136 ; Index = 32 Token = 11 + DD -176 ; Index = 32 Token = 12 + DD -215 ; Index = 32 Token = 13 + DD -254 ; Index = 32 Token = 14 + DD -293 ; Index = 32 Token = 15 + DD 21 ; Index = 33 Token = 0 + DD 64 ; Index = 33 Token = 1 + DD 107 ; Index = 33 Token = 2 + DD 150 ; Index = 33 Token = 3 + DD 194 ; Index = 33 Token = 4 + DD 237 ; Index = 33 Token = 5 + DD 280 ; Index = 33 Token = 6 + DD 323 ; Index = 33 Token = 7 + DD -21 ; Index = 33 Token = 8 + DD -64 ; Index = 33 Token = 9 + DD -107 ; Index = 33 Token = 10 + DD -150 ; Index = 33 Token = 11 + DD -194 ; Index = 33 Token = 12 + DD -237 ; Index = 33 Token = 13 + DD -280 ; Index = 33 Token = 14 + DD -323 ; Index = 33 Token = 15 + DD 23 ; Index = 34 Token = 0 + DD 70 ; Index = 34 Token = 1 + DD 118 ; Index = 34 Token = 2 + DD 165 ; Index = 34 Token = 3 + DD 213 ; Index = 34 Token = 4 + DD 260 ; Index = 34 Token = 5 + DD 308 ; Index = 34 Token = 6 + DD 355 ; Index = 34 Token = 7 + DD -23 ; Index = 34 Token = 8 + DD -70 ; Index = 34 Token = 9 + DD -118 ; Index = 34 Token = 10 + DD -165 ; Index = 34 Token = 11 + DD -213 ; Index = 34 Token = 12 + DD -260 ; Index = 34 Token = 13 + DD -308 ; Index = 34 Token = 14 + DD -355 ; Index = 34 Token = 15 + DD 26 ; Index = 35 Token = 0 + DD 78 ; Index = 35 Token = 1 + DD 130 ; Index = 35 Token = 2 + DD 182 ; Index = 35 Token = 3 + DD 235 ; Index = 35 Token = 4 + DD 287 ; Index = 35 Token = 5 + DD 339 ; Index = 35 Token = 6 + DD 391 ; Index = 35 Token = 7 + DD -26 ; Index = 35 Token = 8 + DD -78 ; Index = 35 Token = 9 + DD -130 ; Index = 35 Token = 10 + DD -182 ; Index = 35 Token = 11 + DD -235 ; Index = 35 Token = 12 + DD -287 ; Index = 35 Token = 13 + DD -339 ; Index = 35 Token = 14 + DD -391 ; Index = 35 Token = 15 + DD 28 ; Index = 36 Token = 0 + DD 85 ; Index = 36 Token = 1 + DD 143 ; Index = 36 Token = 2 + DD 200 ; Index = 36 Token = 3 + DD 258 ; Index = 36 Token = 4 + DD 315 ; Index = 36 Token = 5 + DD 373 ; Index = 36 Token = 6 + DD 430 ; Index = 36 Token = 7 + DD -28 ; Index = 36 Token = 8 + DD -85 ; Index = 36 Token = 9 + DD -143 ; Index = 36 Token = 10 + DD -200 ; Index = 36 Token = 11 + DD -258 ; Index = 36 Token = 12 + DD -315 ; Index = 36 Token = 13 + DD -373 ; Index = 36 Token = 14 + DD -430 ; Index = 36 Token = 15 + DD 31 ; Index = 37 Token = 0 + DD 94 ; Index = 37 Token = 1 + DD 157 ; Index = 37 Token = 2 + DD 220 ; Index = 37 Token = 3 + DD 284 ; Index = 37 Token = 4 + DD 347 ; Index = 37 Token = 5 + DD 410 ; Index = 37 Token = 6 + DD 473 ; Index = 37 Token = 7 + DD -31 ; Index = 37 Token = 8 + DD -94 ; Index = 37 Token = 9 + DD -157 ; Index = 37 Token = 10 + DD -220 ; Index = 37 Token = 11 + DD -284 ; Index = 37 Token = 12 + DD -347 ; Index = 37 Token = 13 + DD -410 ; Index = 37 Token = 14 + DD -473 ; Index = 37 Token = 15 + DD 34 ; Index = 38 Token = 0 + DD 103 ; Index = 38 Token = 1 + DD 173 ; Index = 38 Token = 2 + DD 242 ; Index = 38 Token = 3 + DD 313 ; Index = 38 Token = 4 + DD 382 ; Index = 38 Token = 5 + DD 452 ; Index = 38 Token = 6 + DD 521 ; Index = 38 Token = 7 + DD -34 ; Index = 38 Token = 8 + DD -103 ; Index = 38 Token = 9 + DD -173 ; Index = 38 Token = 10 + DD -242 ; Index = 38 Token = 11 + DD -313 ; Index = 38 Token = 12 + DD -382 ; Index = 38 Token = 13 + DD -452 ; Index = 38 Token = 14 + DD -521 ; Index = 38 Token = 15 + DD 38 ; Index = 39 Token = 0 + DD 114 ; Index = 39 Token = 1 + DD 191 ; Index = 39 Token = 2 + DD 267 ; Index = 39 Token = 3 + DD 345 ; Index = 39 Token = 4 + DD 421 ; Index = 39 Token = 5 + DD 498 ; Index = 39 Token = 6 + DD 574 ; Index = 39 Token = 7 + DD -38 ; Index = 39 Token = 8 + DD -114 ; Index = 39 Token = 9 + DD -191 ; Index = 39 Token = 10 + DD -267 ; Index = 39 Token = 11 + DD -345 ; Index = 39 Token = 12 + DD -421 ; Index = 39 Token = 13 + DD -498 ; Index = 39 Token = 14 + DD -574 ; Index = 39 Token = 15 + DD 42 ; Index = 40 Token = 0 + DD 126 ; Index = 40 Token = 1 + DD 210 ; Index = 40 Token = 2 + DD 294 ; Index = 40 Token = 3 + DD 379 ; Index = 40 Token = 4 + DD 463 ; Index = 40 Token = 5 + DD 547 ; Index = 40 Token = 6 + DD 631 ; Index = 40 Token = 7 + DD -42 ; Index = 40 Token = 8 + DD -126 ; Index = 40 Token = 9 + DD -210 ; Index = 40 Token = 10 + DD -294 ; Index = 40 Token = 11 + DD -379 ; Index = 40 Token = 12 + DD -463 ; Index = 40 Token = 13 + DD -547 ; Index = 40 Token = 14 + DD -631 ; Index = 40 Token = 15 + DD 46 ; Index = 41 Token = 0 + DD 138 ; Index = 41 Token = 1 + DD 231 ; Index = 41 Token = 2 + DD 323 ; Index = 41 Token = 3 + DD 417 ; Index = 41 Token = 4 + DD 509 ; Index = 41 Token = 5 + DD 602 ; Index = 41 Token = 6 + DD 694 ; Index = 41 Token = 7 + DD -46 ; Index = 41 Token = 8 + DD -138 ; Index = 41 Token = 9 + DD -231 ; Index = 41 Token = 10 + DD -323 ; Index = 41 Token = 11 + DD -417 ; Index = 41 Token = 12 + DD -509 ; Index = 41 Token = 13 + DD -602 ; Index = 41 Token = 14 + DD -694 ; Index = 41 Token = 15 + DD 51 ; Index = 42 Token = 0 + DD 153 ; Index = 42 Token = 1 + DD 255 ; Index = 42 Token = 2 + DD 357 ; Index = 42 Token = 3 + DD 459 ; Index = 42 Token = 4 + DD 561 ; Index = 42 Token = 5 + DD 663 ; Index = 42 Token = 6 + DD 765 ; Index = 42 Token = 7 + DD -51 ; Index = 42 Token = 8 + DD -153 ; Index = 42 Token = 9 + DD -255 ; Index = 42 Token = 10 + DD -357 ; Index = 42 Token = 11 + DD -459 ; Index = 42 Token = 12 + DD -561 ; Index = 42 Token = 13 + DD -663 ; Index = 42 Token = 14 + DD -765 ; Index = 42 Token = 15 + DD 56 ; Index = 43 Token = 0 + DD 168 ; Index = 43 Token = 1 + DD 280 ; Index = 43 Token = 2 + DD 392 ; Index = 43 Token = 3 + DD 505 ; Index = 43 Token = 4 + DD 617 ; Index = 43 Token = 5 + DD 729 ; Index = 43 Token = 6 + DD 841 ; Index = 43 Token = 7 + DD -56 ; Index = 43 Token = 8 + DD -168 ; Index = 43 Token = 9 + DD -280 ; Index = 43 Token = 10 + DD -392 ; Index = 43 Token = 11 + DD -505 ; Index = 43 Token = 12 + DD -617 ; Index = 43 Token = 13 + DD -729 ; Index = 43 Token = 14 + DD -841 ; Index = 43 Token = 15 + DD 61 ; Index = 44 Token = 0 + DD 184 ; Index = 44 Token = 1 + DD 308 ; Index = 44 Token = 2 + DD 431 ; Index = 44 Token = 3 + DD 555 ; Index = 44 Token = 4 + DD 678 ; Index = 44 Token = 5 + DD 802 ; Index = 44 Token = 6 + DD 925 ; Index = 44 Token = 7 + DD -61 ; Index = 44 Token = 8 + DD -184 ; Index = 44 Token = 9 + DD -308 ; Index = 44 Token = 10 + DD -431 ; Index = 44 Token = 11 + DD -555 ; Index = 44 Token = 12 + DD -678 ; Index = 44 Token = 13 + DD -802 ; Index = 44 Token = 14 + DD -925 ; Index = 44 Token = 15 + DD 68 ; Index = 45 Token = 0 + DD 204 ; Index = 45 Token = 1 + DD 340 ; Index = 45 Token = 2 + DD 476 ; Index = 45 Token = 3 + DD 612 ; Index = 45 Token = 4 + DD 748 ; Index = 45 Token = 5 + DD 884 ; Index = 45 Token = 6 + DD 1020 ; Index = 45 Token = 7 + DD -68 ; Index = 45 Token = 8 + DD -204 ; Index = 45 Token = 9 + DD -340 ; Index = 45 Token = 10 + DD -476 ; Index = 45 Token = 11 + DD -612 ; Index = 45 Token = 12 + DD -748 ; Index = 45 Token = 13 + DD -884 ; Index = 45 Token = 14 + DD -1020 ; Index = 45 Token = 15 + DD 74 ; Index = 46 Token = 0 + DD 223 ; Index = 46 Token = 1 + DD 373 ; Index = 46 Token = 2 + DD 522 ; Index = 46 Token = 3 + DD 672 ; Index = 46 Token = 4 + DD 821 ; Index = 46 Token = 5 + DD 971 ; Index = 46 Token = 6 + DD 1120 ; Index = 46 Token = 7 + DD -74 ; Index = 46 Token = 8 + DD -223 ; Index = 46 Token = 9 + DD -373 ; Index = 46 Token = 10 + DD -522 ; Index = 46 Token = 11 + DD -672 ; Index = 46 Token = 12 + DD -821 ; Index = 46 Token = 13 + DD -971 ; Index = 46 Token = 14 + DD -1120 ; Index = 46 Token = 15 + DD 82 ; Index = 47 Token = 0 + DD 246 ; Index = 47 Token = 1 + DD 411 ; Index = 47 Token = 2 + DD 575 ; Index = 47 Token = 3 + DD 740 ; Index = 47 Token = 4 + DD 904 ; Index = 47 Token = 5 + DD 1069 ; Index = 47 Token = 6 + DD 1233 ; Index = 47 Token = 7 + DD -82 ; Index = 47 Token = 8 + DD -246 ; Index = 47 Token = 9 + DD -411 ; Index = 47 Token = 10 + DD -575 ; Index = 47 Token = 11 + DD -740 ; Index = 47 Token = 12 + DD -904 ; Index = 47 Token = 13 + DD -1069 ; Index = 47 Token = 14 + DD -1233 ; Index = 47 Token = 15 + DD 90 ; Index = 48 Token = 0 + DD 271 ; Index = 48 Token = 1 + DD 452 ; Index = 48 Token = 2 + DD 633 ; Index = 48 Token = 3 + DD 814 ; Index = 48 Token = 4 + DD 995 ; Index = 48 Token = 5 + DD 1176 ; Index = 48 Token = 6 + DD 1357 ; Index = 48 Token = 7 + DD -90 ; Index = 48 Token = 8 + DD -271 ; Index = 48 Token = 9 + DD -452 ; Index = 48 Token = 10 + DD -633 ; Index = 48 Token = 11 + DD -814 ; Index = 48 Token = 12 + DD -995 ; Index = 48 Token = 13 + DD -1176 ; Index = 48 Token = 14 + DD -1357 ; Index = 48 Token = 15 + DD 99 ; Index = 49 Token = 0 + DD 298 ; Index = 49 Token = 1 + DD 497 ; Index = 49 Token = 2 + DD 696 ; Index = 49 Token = 3 + DD 895 ; Index = 49 Token = 4 + DD 1094 ; Index = 49 Token = 5 + DD 1293 ; Index = 49 Token = 6 + DD 1492 ; Index = 49 Token = 7 + DD -99 ; Index = 49 Token = 8 + DD -298 ; Index = 49 Token = 9 + DD -497 ; Index = 49 Token = 10 + DD -696 ; Index = 49 Token = 11 + DD -895 ; Index = 49 Token = 12 + DD -1094 ; Index = 49 Token = 13 + DD -1293 ; Index = 49 Token = 14 + DD -1492 ; Index = 49 Token = 15 + DD 109 ; Index = 50 Token = 0 + DD 328 ; Index = 50 Token = 1 + DD 547 ; Index = 50 Token = 2 + DD 766 ; Index = 50 Token = 3 + DD 985 ; Index = 50 Token = 4 + DD 1204 ; Index = 50 Token = 5 + DD 1423 ; Index = 50 Token = 6 + DD 1642 ; Index = 50 Token = 7 + DD -109 ; Index = 50 Token = 8 + DD -328 ; Index = 50 Token = 9 + DD -547 ; Index = 50 Token = 10 + DD -766 ; Index = 50 Token = 11 + DD -985 ; Index = 50 Token = 12 + DD -1204 ; Index = 50 Token = 13 + DD -1423 ; Index = 50 Token = 14 + DD -1642 ; Index = 50 Token = 15 + DD 120 ; Index = 51 Token = 0 + DD 360 ; Index = 51 Token = 1 + DD 601 ; Index = 51 Token = 2 + DD 841 ; Index = 51 Token = 3 + DD 1083 ; Index = 51 Token = 4 + DD 1323 ; Index = 51 Token = 5 + DD 1564 ; Index = 51 Token = 6 + DD 1804 ; Index = 51 Token = 7 + DD -120 ; Index = 51 Token = 8 + DD -360 ; Index = 51 Token = 9 + DD -601 ; Index = 51 Token = 10 + DD -841 ; Index = 51 Token = 11 + DD -1083 ; Index = 51 Token = 12 + DD -1323 ; Index = 51 Token = 13 + DD -1564 ; Index = 51 Token = 14 + DD -1804 ; Index = 51 Token = 15 + DD 132 ; Index = 52 Token = 0 + DD 397 ; Index = 52 Token = 1 + DD 662 ; Index = 52 Token = 2 + DD 927 ; Index = 52 Token = 3 + DD 1192 ; Index = 52 Token = 4 + DD 1457 ; Index = 52 Token = 5 + DD 1722 ; Index = 52 Token = 6 + DD 1987 ; Index = 52 Token = 7 + DD -132 ; Index = 52 Token = 8 + DD -397 ; Index = 52 Token = 9 + DD -662 ; Index = 52 Token = 10 + DD -927 ; Index = 52 Token = 11 + DD -1192 ; Index = 52 Token = 12 + DD -1457 ; Index = 52 Token = 13 + DD -1722 ; Index = 52 Token = 14 + DD -1987 ; Index = 52 Token = 15 + DD 145 ; Index = 53 Token = 0 + DD 436 ; Index = 53 Token = 1 + DD 728 ; Index = 53 Token = 2 + DD 1019 ; Index = 53 Token = 3 + DD 1311 ; Index = 53 Token = 4 + DD 1602 ; Index = 53 Token = 5 + DD 1894 ; Index = 53 Token = 6 + DD 2185 ; Index = 53 Token = 7 + DD -145 ; Index = 53 Token = 8 + DD -436 ; Index = 53 Token = 9 + DD -728 ; Index = 53 Token = 10 + DD -1019 ; Index = 53 Token = 11 + DD -1311 ; Index = 53 Token = 12 + DD -1602 ; Index = 53 Token = 13 + DD -1894 ; Index = 53 Token = 14 + DD -2185 ; Index = 53 Token = 15 + DD 160 ; Index = 54 Token = 0 + DD 480 ; Index = 54 Token = 1 + DD 801 ; Index = 54 Token = 2 + DD 1121 ; Index = 54 Token = 3 + DD 1442 ; Index = 54 Token = 4 + DD 1762 ; Index = 54 Token = 5 + DD 2083 ; Index = 54 Token = 6 + DD 2403 ; Index = 54 Token = 7 + DD -160 ; Index = 54 Token = 8 + DD -480 ; Index = 54 Token = 9 + DD -801 ; Index = 54 Token = 10 + DD -1121 ; Index = 54 Token = 11 + DD -1442 ; Index = 54 Token = 12 + DD -1762 ; Index = 54 Token = 13 + DD -2083 ; Index = 54 Token = 14 + DD -2403 ; Index = 54 Token = 15 + DD 176 ; Index = 55 Token = 0 + DD 528 ; Index = 55 Token = 1 + DD 881 ; Index = 55 Token = 2 + DD 1233 ; Index = 55 Token = 3 + DD 1587 ; Index = 55 Token = 4 + DD 1939 ; Index = 55 Token = 5 + DD 2292 ; Index = 55 Token = 6 + DD 2644 ; Index = 55 Token = 7 + DD -176 ; Index = 55 Token = 8 + DD -528 ; Index = 55 Token = 9 + DD -881 ; Index = 55 Token = 10 + DD -1233 ; Index = 55 Token = 11 + DD -1587 ; Index = 55 Token = 12 + DD -1939 ; Index = 55 Token = 13 + DD -2292 ; Index = 55 Token = 14 + DD -2644 ; Index = 55 Token = 15 + DD 194 ; Index = 56 Token = 0 + DD 582 ; Index = 56 Token = 1 + DD 970 ; Index = 56 Token = 2 + DD 1358 ; Index = 56 Token = 3 + DD 1746 ; Index = 56 Token = 4 + DD 2134 ; Index = 56 Token = 5 + DD 2522 ; Index = 56 Token = 6 + DD 2910 ; Index = 56 Token = 7 + DD -194 ; Index = 56 Token = 8 + DD -582 ; Index = 56 Token = 9 + DD -970 ; Index = 56 Token = 10 + DD -1358 ; Index = 56 Token = 11 + DD -1746 ; Index = 56 Token = 12 + DD -2134 ; Index = 56 Token = 13 + DD -2522 ; Index = 56 Token = 14 + DD -2910 ; Index = 56 Token = 15 + DD 213 ; Index = 57 Token = 0 + DD 639 ; Index = 57 Token = 1 + DD 1066 ; Index = 57 Token = 2 + DD 1492 ; Index = 57 Token = 3 + DD 1920 ; Index = 57 Token = 4 + DD 2346 ; Index = 57 Token = 5 + DD 2773 ; Index = 57 Token = 6 + DD 3199 ; Index = 57 Token = 7 + DD -213 ; Index = 57 Token = 8 + DD -639 ; Index = 57 Token = 9 + DD -1066 ; Index = 57 Token = 10 + DD -1492 ; Index = 57 Token = 11 + DD -1920 ; Index = 57 Token = 12 + DD -2346 ; Index = 57 Token = 13 + DD -2773 ; Index = 57 Token = 14 + DD -3199 ; Index = 57 Token = 15 + DD 234 ; Index = 58 Token = 0 + DD 703 ; Index = 58 Token = 1 + DD 1173 ; Index = 58 Token = 2 + DD 1642 ; Index = 58 Token = 3 + DD 2112 ; Index = 58 Token = 4 + DD 2581 ; Index = 58 Token = 5 + DD 3051 ; Index = 58 Token = 6 + DD 3520 ; Index = 58 Token = 7 + DD -234 ; Index = 58 Token = 8 + DD -703 ; Index = 58 Token = 9 + DD -1173 ; Index = 58 Token = 10 + DD -1642 ; Index = 58 Token = 11 + DD -2112 ; Index = 58 Token = 12 + DD -2581 ; Index = 58 Token = 13 + DD -3051 ; Index = 58 Token = 14 + DD -3520 ; Index = 58 Token = 15 + DD 258 ; Index = 59 Token = 0 + DD 774 ; Index = 59 Token = 1 + DD 1291 ; Index = 59 Token = 2 + DD 1807 ; Index = 59 Token = 3 + DD 2324 ; Index = 59 Token = 4 + DD 2840 ; Index = 59 Token = 5 + DD 3357 ; Index = 59 Token = 6 + DD 3873 ; Index = 59 Token = 7 + DD -258 ; Index = 59 Token = 8 + DD -774 ; Index = 59 Token = 9 + DD -1291 ; Index = 59 Token = 10 + DD -1807 ; Index = 59 Token = 11 + DD -2324 ; Index = 59 Token = 12 + DD -2840 ; Index = 59 Token = 13 + DD -3357 ; Index = 59 Token = 14 + DD -3873 ; Index = 59 Token = 15 + DD 284 ; Index = 60 Token = 0 + DD 852 ; Index = 60 Token = 1 + DD 1420 ; Index = 60 Token = 2 + DD 1988 ; Index = 60 Token = 3 + DD 2556 ; Index = 60 Token = 4 + DD 3124 ; Index = 60 Token = 5 + DD 3692 ; Index = 60 Token = 6 + DD 4260 ; Index = 60 Token = 7 + DD -284 ; Index = 60 Token = 8 + DD -852 ; Index = 60 Token = 9 + DD -1420 ; Index = 60 Token = 10 + DD -1988 ; Index = 60 Token = 11 + DD -2556 ; Index = 60 Token = 12 + DD -3124 ; Index = 60 Token = 13 + DD -3692 ; Index = 60 Token = 14 + DD -4260 ; Index = 60 Token = 15 + DD 312 ; Index = 61 Token = 0 + DD 936 ; Index = 61 Token = 1 + DD 1561 ; Index = 61 Token = 2 + DD 2185 ; Index = 61 Token = 3 + DD 2811 ; Index = 61 Token = 4 + DD 3435 ; Index = 61 Token = 5 + DD 4060 ; Index = 61 Token = 6 + DD 4684 ; Index = 61 Token = 7 + DD -312 ; Index = 61 Token = 8 + DD -936 ; Index = 61 Token = 9 + DD -1561 ; Index = 61 Token = 10 + DD -2185 ; Index = 61 Token = 11 + DD -2811 ; Index = 61 Token = 12 + DD -3435 ; Index = 61 Token = 13 + DD -4060 ; Index = 61 Token = 14 + DD -4684 ; Index = 61 Token = 15 + DD 343 ; Index = 62 Token = 0 + DD 1030 ; Index = 62 Token = 1 + DD 1717 ; Index = 62 Token = 2 + DD 2404 ; Index = 62 Token = 3 + DD 3092 ; Index = 62 Token = 4 + DD 3779 ; Index = 62 Token = 5 + DD 4466 ; Index = 62 Token = 6 + DD 5153 ; Index = 62 Token = 7 + DD -343 ; Index = 62 Token = 8 + DD -1030 ; Index = 62 Token = 9 + DD -1717 ; Index = 62 Token = 10 + DD -2404 ; Index = 62 Token = 11 + DD -3092 ; Index = 62 Token = 12 + DD -3779 ; Index = 62 Token = 13 + DD -4466 ; Index = 62 Token = 14 + DD -5153 ; Index = 62 Token = 15 + DD 378 ; Index = 63 Token = 0 + DD 1134 ; Index = 63 Token = 1 + DD 1890 ; Index = 63 Token = 2 + DD 2646 ; Index = 63 Token = 3 + DD 3402 ; Index = 63 Token = 4 + DD 4158 ; Index = 63 Token = 5 + DD 4914 ; Index = 63 Token = 6 + DD 5670 ; Index = 63 Token = 7 + DD -378 ; Index = 63 Token = 8 + DD -1134 ; Index = 63 Token = 9 + DD -1890 ; Index = 63 Token = 10 + DD -2646 ; Index = 63 Token = 11 + DD -3402 ; Index = 63 Token = 12 + DD -4158 ; Index = 63 Token = 13 + DD -4914 ; Index = 63 Token = 14 + DD -5670 ; Index = 63 Token = 15 + DD 415 ; Index = 64 Token = 0 + DD 1246 ; Index = 64 Token = 1 + DD 2078 ; Index = 64 Token = 2 + DD 2909 ; Index = 64 Token = 3 + DD 3742 ; Index = 64 Token = 4 + DD 4573 ; Index = 64 Token = 5 + DD 5405 ; Index = 64 Token = 6 + DD 6236 ; Index = 64 Token = 7 + DD -415 ; Index = 64 Token = 8 + DD -1246 ; Index = 64 Token = 9 + DD -2078 ; Index = 64 Token = 10 + DD -2909 ; Index = 64 Token = 11 + DD -3742 ; Index = 64 Token = 12 + DD -4573 ; Index = 64 Token = 13 + DD -5405 ; Index = 64 Token = 14 + DD -6236 ; Index = 64 Token = 15 + DD 457 ; Index = 65 Token = 0 + DD 1372 ; Index = 65 Token = 1 + DD 2287 ; Index = 65 Token = 2 + DD 3202 ; Index = 65 Token = 3 + DD 4117 ; Index = 65 Token = 4 + DD 5032 ; Index = 65 Token = 5 + DD 5947 ; Index = 65 Token = 6 + DD 6862 ; Index = 65 Token = 7 + DD -457 ; Index = 65 Token = 8 + DD -1372 ; Index = 65 Token = 9 + DD -2287 ; Index = 65 Token = 10 + DD -3202 ; Index = 65 Token = 11 + DD -4117 ; Index = 65 Token = 12 + DD -5032 ; Index = 65 Token = 13 + DD -5947 ; Index = 65 Token = 14 + DD -6862 ; Index = 65 Token = 15 + DD 503 ; Index = 66 Token = 0 + DD 1509 ; Index = 66 Token = 1 + DD 2516 ; Index = 66 Token = 2 + DD 3522 ; Index = 66 Token = 3 + DD 4529 ; Index = 66 Token = 4 + DD 5535 ; Index = 66 Token = 5 + DD 6542 ; Index = 66 Token = 6 + DD 7548 ; Index = 66 Token = 7 + DD -503 ; Index = 66 Token = 8 + DD -1509 ; Index = 66 Token = 9 + DD -2516 ; Index = 66 Token = 10 + DD -3522 ; Index = 66 Token = 11 + DD -4529 ; Index = 66 Token = 12 + DD -5535 ; Index = 66 Token = 13 + DD -6542 ; Index = 66 Token = 14 + DD -7548 ; Index = 66 Token = 15 + DD 553 ; Index = 67 Token = 0 + DD 1660 ; Index = 67 Token = 1 + DD 2767 ; Index = 67 Token = 2 + DD 3874 ; Index = 67 Token = 3 + DD 4981 ; Index = 67 Token = 4 + DD 6088 ; Index = 67 Token = 5 + DD 7195 ; Index = 67 Token = 6 + DD 8302 ; Index = 67 Token = 7 + DD -553 ; Index = 67 Token = 8 + DD -1660 ; Index = 67 Token = 9 + DD -2767 ; Index = 67 Token = 10 + DD -3874 ; Index = 67 Token = 11 + DD -4981 ; Index = 67 Token = 12 + DD -6088 ; Index = 67 Token = 13 + DD -7195 ; Index = 67 Token = 14 + DD -8302 ; Index = 67 Token = 15 + DD 608 ; Index = 68 Token = 0 + DD 1825 ; Index = 68 Token = 1 + DD 3043 ; Index = 68 Token = 2 + DD 4260 ; Index = 68 Token = 3 + DD 5479 ; Index = 68 Token = 4 + DD 6696 ; Index = 68 Token = 5 + DD 7914 ; Index = 68 Token = 6 + DD 9131 ; Index = 68 Token = 7 + DD -608 ; Index = 68 Token = 8 + DD -1825 ; Index = 68 Token = 9 + DD -3043 ; Index = 68 Token = 10 + DD -4260 ; Index = 68 Token = 11 + DD -5479 ; Index = 68 Token = 12 + DD -6696 ; Index = 68 Token = 13 + DD -7914 ; Index = 68 Token = 14 + DD -9131 ; Index = 68 Token = 15 + DD 669 ; Index = 69 Token = 0 + DD 2008 ; Index = 69 Token = 1 + DD 3348 ; Index = 69 Token = 2 + DD 4687 ; Index = 69 Token = 3 + DD 6027 ; Index = 69 Token = 4 + DD 7366 ; Index = 69 Token = 5 + DD 8706 ; Index = 69 Token = 6 + DD 10045 ; Index = 69 Token = 7 + DD -669 ; Index = 69 Token = 8 + DD -2008 ; Index = 69 Token = 9 + DD -3348 ; Index = 69 Token = 10 + DD -4687 ; Index = 69 Token = 11 + DD -6027 ; Index = 69 Token = 12 + DD -7366 ; Index = 69 Token = 13 + DD -8706 ; Index = 69 Token = 14 + DD -10045 ; Index = 69 Token = 15 + DD 736 ; Index = 70 Token = 0 + DD 2209 ; Index = 70 Token = 1 + DD 3683 ; Index = 70 Token = 2 + DD 5156 ; Index = 70 Token = 3 + DD 6630 ; Index = 70 Token = 4 + DD 8103 ; Index = 70 Token = 5 + DD 9577 ; Index = 70 Token = 6 + DD 11050 ; Index = 70 Token = 7 + DD -736 ; Index = 70 Token = 8 + DD -2209 ; Index = 70 Token = 9 + DD -3683 ; Index = 70 Token = 10 + DD -5156 ; Index = 70 Token = 11 + DD -6630 ; Index = 70 Token = 12 + DD -8103 ; Index = 70 Token = 13 + DD -9577 ; Index = 70 Token = 14 + DD -11050 ; Index = 70 Token = 15 + DD 810 ; Index = 71 Token = 0 + DD 2431 ; Index = 71 Token = 1 + DD 4052 ; Index = 71 Token = 2 + DD 5673 ; Index = 71 Token = 3 + DD 7294 ; Index = 71 Token = 4 + DD 8915 ; Index = 71 Token = 5 + DD 10536 ; Index = 71 Token = 6 + DD 12157 ; Index = 71 Token = 7 + DD -810 ; Index = 71 Token = 8 + DD -2431 ; Index = 71 Token = 9 + DD -4052 ; Index = 71 Token = 10 + DD -5673 ; Index = 71 Token = 11 + DD -7294 ; Index = 71 Token = 12 + DD -8915 ; Index = 71 Token = 13 + DD -10536 ; Index = 71 Token = 14 + DD -12157 ; Index = 71 Token = 15 + DD 891 ; Index = 72 Token = 0 + DD 2674 ; Index = 72 Token = 1 + DD 4457 ; Index = 72 Token = 2 + DD 6240 ; Index = 72 Token = 3 + DD 8023 ; Index = 72 Token = 4 + DD 9806 ; Index = 72 Token = 5 + DD 11589 ; Index = 72 Token = 6 + DD 13372 ; Index = 72 Token = 7 + DD -891 ; Index = 72 Token = 8 + DD -2674 ; Index = 72 Token = 9 + DD -4457 ; Index = 72 Token = 10 + DD -6240 ; Index = 72 Token = 11 + DD -8023 ; Index = 72 Token = 12 + DD -9806 ; Index = 72 Token = 13 + DD -11589 ; Index = 72 Token = 14 + DD -13372 ; Index = 72 Token = 15 + DD 980 ; Index = 73 Token = 0 + DD 2941 ; Index = 73 Token = 1 + DD 4902 ; Index = 73 Token = 2 + DD 6863 ; Index = 73 Token = 3 + DD 8825 ; Index = 73 Token = 4 + DD 10786 ; Index = 73 Token = 5 + DD 12747 ; Index = 73 Token = 6 + DD 14708 ; Index = 73 Token = 7 + DD -980 ; Index = 73 Token = 8 + DD -2941 ; Index = 73 Token = 9 + DD -4902 ; Index = 73 Token = 10 + DD -6863 ; Index = 73 Token = 11 + DD -8825 ; Index = 73 Token = 12 + DD -10786 ; Index = 73 Token = 13 + DD -12747 ; Index = 73 Token = 14 + DD -14708 ; Index = 73 Token = 15 + DD 1078 ; Index = 74 Token = 0 + DD 3235 ; Index = 74 Token = 1 + DD 5393 ; Index = 74 Token = 2 + DD 7550 ; Index = 74 Token = 3 + DD 9708 ; Index = 74 Token = 4 + DD 11865 ; Index = 74 Token = 5 + DD 14023 ; Index = 74 Token = 6 + DD 16180 ; Index = 74 Token = 7 + DD -1078 ; Index = 74 Token = 8 + DD -3235 ; Index = 74 Token = 9 + DD -5393 ; Index = 74 Token = 10 + DD -7550 ; Index = 74 Token = 11 + DD -9708 ; Index = 74 Token = 12 + DD -11865 ; Index = 74 Token = 13 + DD -14023 ; Index = 74 Token = 14 + DD -16180 ; Index = 74 Token = 15 + DD 1186 ; Index = 75 Token = 0 + DD 3559 ; Index = 75 Token = 1 + DD 5932 ; Index = 75 Token = 2 + DD 8305 ; Index = 75 Token = 3 + DD 10679 ; Index = 75 Token = 4 + DD 13052 ; Index = 75 Token = 5 + DD 15425 ; Index = 75 Token = 6 + DD 17798 ; Index = 75 Token = 7 + DD -1186 ; Index = 75 Token = 8 + DD -3559 ; Index = 75 Token = 9 + DD -5932 ; Index = 75 Token = 10 + DD -8305 ; Index = 75 Token = 11 + DD -10679 ; Index = 75 Token = 12 + DD -13052 ; Index = 75 Token = 13 + DD -15425 ; Index = 75 Token = 14 + DD -17798 ; Index = 75 Token = 15 + DD 1305 ; Index = 76 Token = 0 + DD 3915 ; Index = 76 Token = 1 + DD 6526 ; Index = 76 Token = 2 + DD 9136 ; Index = 76 Token = 3 + DD 11747 ; Index = 76 Token = 4 + DD 14357 ; Index = 76 Token = 5 + DD 16968 ; Index = 76 Token = 6 + DD 19578 ; Index = 76 Token = 7 + DD -1305 ; Index = 76 Token = 8 + DD -3915 ; Index = 76 Token = 9 + DD -6526 ; Index = 76 Token = 10 + DD -9136 ; Index = 76 Token = 11 + DD -11747 ; Index = 76 Token = 12 + DD -14357 ; Index = 76 Token = 13 + DD -16968 ; Index = 76 Token = 14 + DD -19578 ; Index = 76 Token = 15 + DD 1435 ; Index = 77 Token = 0 + DD 4306 ; Index = 77 Token = 1 + DD 7178 ; Index = 77 Token = 2 + DD 10049 ; Index = 77 Token = 3 + DD 12922 ; Index = 77 Token = 4 + DD 15793 ; Index = 77 Token = 5 + DD 18665 ; Index = 77 Token = 6 + DD 21536 ; Index = 77 Token = 7 + DD -1435 ; Index = 77 Token = 8 + DD -4306 ; Index = 77 Token = 9 + DD -7178 ; Index = 77 Token = 10 + DD -10049 ; Index = 77 Token = 11 + DD -12922 ; Index = 77 Token = 12 + DD -15793 ; Index = 77 Token = 13 + DD -18665 ; Index = 77 Token = 14 + DD -21536 ; Index = 77 Token = 15 + DD 1579 ; Index = 78 Token = 0 + DD 4737 ; Index = 78 Token = 1 + DD 7896 ; Index = 78 Token = 2 + DD 11054 ; Index = 78 Token = 3 + DD 14214 ; Index = 78 Token = 4 + DD 17372 ; Index = 78 Token = 5 + DD 20531 ; Index = 78 Token = 6 + DD 23689 ; Index = 78 Token = 7 + DD -1579 ; Index = 78 Token = 8 + DD -4737 ; Index = 78 Token = 9 + DD -7896 ; Index = 78 Token = 10 + DD -11054 ; Index = 78 Token = 11 + DD -14214 ; Index = 78 Token = 12 + DD -17372 ; Index = 78 Token = 13 + DD -20531 ; Index = 78 Token = 14 + DD -23689 ; Index = 78 Token = 15 + DD 1737 ; Index = 79 Token = 0 + DD 5211 ; Index = 79 Token = 1 + DD 8686 ; Index = 79 Token = 2 + DD 12160 ; Index = 79 Token = 3 + DD 15636 ; Index = 79 Token = 4 + DD 19110 ; Index = 79 Token = 5 + DD 22585 ; Index = 79 Token = 6 + DD 26059 ; Index = 79 Token = 7 + DD -1737 ; Index = 79 Token = 8 + DD -5211 ; Index = 79 Token = 9 + DD -8686 ; Index = 79 Token = 10 + DD -12160 ; Index = 79 Token = 11 + DD -15636 ; Index = 79 Token = 12 + DD -19110 ; Index = 79 Token = 13 + DD -22585 ; Index = 79 Token = 14 + DD -26059 ; Index = 79 Token = 15 + DD 1911 ; Index = 80 Token = 0 + DD 5733 ; Index = 80 Token = 1 + DD 9555 ; Index = 80 Token = 2 + DD 13377 ; Index = 80 Token = 3 + DD 17200 ; Index = 80 Token = 4 + DD 21022 ; Index = 80 Token = 5 + DD 24844 ; Index = 80 Token = 6 + DD 28666 ; Index = 80 Token = 7 + DD -1911 ; Index = 80 Token = 8 + DD -5733 ; Index = 80 Token = 9 + DD -9555 ; Index = 80 Token = 10 + DD -13377 ; Index = 80 Token = 11 + DD -17200 ; Index = 80 Token = 12 + DD -21022 ; Index = 80 Token = 13 + DD -24844 ; Index = 80 Token = 14 + DD -28666 ; Index = 80 Token = 15 + DD 2102 ; Index = 81 Token = 0 + DD 6306 ; Index = 81 Token = 1 + DD 10511 ; Index = 81 Token = 2 + DD 14715 ; Index = 81 Token = 3 + DD 18920 ; Index = 81 Token = 4 + DD 23124 ; Index = 81 Token = 5 + DD 27329 ; Index = 81 Token = 6 + DD 31533 ; Index = 81 Token = 7 + DD -2102 ; Index = 81 Token = 8 + DD -6306 ; Index = 81 Token = 9 + DD -10511 ; Index = 81 Token = 10 + DD -14715 ; Index = 81 Token = 11 + DD -18920 ; Index = 81 Token = 12 + DD -23124 ; Index = 81 Token = 13 + DD -27329 ; Index = 81 Token = 14 + DD -31533 ; Index = 81 Token = 15 + DD 2312 ; Index = 82 Token = 0 + DD 6937 ; Index = 82 Token = 1 + DD 11562 ; Index = 82 Token = 2 + DD 16187 ; Index = 82 Token = 3 + DD 20812 ; Index = 82 Token = 4 + DD 25437 ; Index = 82 Token = 5 + DD 30062 ; Index = 82 Token = 6 + DD 34687 ; Index = 82 Token = 7 + DD -2312 ; Index = 82 Token = 8 + DD -6937 ; Index = 82 Token = 9 + DD -11562 ; Index = 82 Token = 10 + DD -16187 ; Index = 82 Token = 11 + DD -20812 ; Index = 82 Token = 12 + DD -25437 ; Index = 82 Token = 13 + DD -30062 ; Index = 82 Token = 14 + DD -34687 ; Index = 82 Token = 15 + DD 2543 ; Index = 83 Token = 0 + DD 7630 ; Index = 83 Token = 1 + DD 12718 ; Index = 83 Token = 2 + DD 17805 ; Index = 83 Token = 3 + DD 22893 ; Index = 83 Token = 4 + DD 27980 ; Index = 83 Token = 5 + DD 33068 ; Index = 83 Token = 6 + DD 38155 ; Index = 83 Token = 7 + DD -2543 ; Index = 83 Token = 8 + DD -7630 ; Index = 83 Token = 9 + DD -12718 ; Index = 83 Token = 10 + DD -17805 ; Index = 83 Token = 11 + DD -22893 ; Index = 83 Token = 12 + DD -27980 ; Index = 83 Token = 13 + DD -33068 ; Index = 83 Token = 14 + DD -38155 ; Index = 83 Token = 15 + DD 2798 ; Index = 84 Token = 0 + DD 8394 ; Index = 84 Token = 1 + DD 13990 ; Index = 84 Token = 2 + DD 19586 ; Index = 84 Token = 3 + DD 25183 ; Index = 84 Token = 4 + DD 30779 ; Index = 84 Token = 5 + DD 36375 ; Index = 84 Token = 6 + DD 41971 ; Index = 84 Token = 7 + DD -2798 ; Index = 84 Token = 8 + DD -8394 ; Index = 84 Token = 9 + DD -13990 ; Index = 84 Token = 10 + DD -19586 ; Index = 84 Token = 11 + DD -25183 ; Index = 84 Token = 12 + DD -30779 ; Index = 84 Token = 13 + DD -36375 ; Index = 84 Token = 14 + DD -41971 ; Index = 84 Token = 15 + DD 3077 ; Index = 85 Token = 0 + DD 9232 ; Index = 85 Token = 1 + DD 15388 ; Index = 85 Token = 2 + DD 21543 ; Index = 85 Token = 3 + DD 27700 ; Index = 85 Token = 4 + DD 33855 ; Index = 85 Token = 5 + DD 40011 ; Index = 85 Token = 6 + DD 46166 ; Index = 85 Token = 7 + DD -3077 ; Index = 85 Token = 8 + DD -9232 ; Index = 85 Token = 9 + DD -15388 ; Index = 85 Token = 10 + DD -21543 ; Index = 85 Token = 11 + DD -27700 ; Index = 85 Token = 12 + DD -33855 ; Index = 85 Token = 13 + DD -40011 ; Index = 85 Token = 14 + DD -46166 ; Index = 85 Token = 15 + DD 3385 ; Index = 86 Token = 0 + DD 10156 ; Index = 86 Token = 1 + DD 16928 ; Index = 86 Token = 2 + DD 23699 ; Index = 86 Token = 3 + DD 30471 ; Index = 86 Token = 4 + DD 37242 ; Index = 86 Token = 5 + DD 44014 ; Index = 86 Token = 6 + DD 50785 ; Index = 86 Token = 7 + DD -3385 ; Index = 86 Token = 8 + DD -10156 ; Index = 86 Token = 9 + DD -16928 ; Index = 86 Token = 10 + DD -23699 ; Index = 86 Token = 11 + DD -30471 ; Index = 86 Token = 12 + DD -37242 ; Index = 86 Token = 13 + DD -44014 ; Index = 86 Token = 14 + DD -50785 ; Index = 86 Token = 15 + DD 3724 ; Index = 87 Token = 0 + DD 11172 ; Index = 87 Token = 1 + DD 18621 ; Index = 87 Token = 2 + DD 26069 ; Index = 87 Token = 3 + DD 33518 ; Index = 87 Token = 4 + DD 40966 ; Index = 87 Token = 5 + DD 48415 ; Index = 87 Token = 6 + DD 55863 ; Index = 87 Token = 7 + DD -3724 ; Index = 87 Token = 8 + DD -11172 ; Index = 87 Token = 9 + DD -18621 ; Index = 87 Token = 10 + DD -26069 ; Index = 87 Token = 11 + DD -33518 ; Index = 87 Token = 12 + DD -40966 ; Index = 87 Token = 13 + DD -48415 ; Index = 87 Token = 14 + DD -55863 ; Index = 87 Token = 15 + DD 4095 ; Index = 88 Token = 0 + DD 12286 ; Index = 88 Token = 1 + DD 20478 ; Index = 88 Token = 2 + DD 28669 ; Index = 88 Token = 3 + DD 36862 ; Index = 88 Token = 4 + DD 45053 ; Index = 88 Token = 5 + DD 53245 ; Index = 88 Token = 6 + DD 61436 ; Index = 88 Token = 7 + DD -4095 ; Index = 88 Token = 8 + DD -12286 ; Index = 88 Token = 9 + DD -20478 ; Index = 88 Token = 10 + DD -28669 ; Index = 88 Token = 11 + DD -36862 ; Index = 88 Token = 12 + DD -45053 ; Index = 88 Token = 13 + DD -53245 ; Index = 88 Token = 14 + DD -61436 ; Index = 88 Token = 15 diff --git a/code/redalert/win32lib/dipthong.cpp b/code/redalert/win32lib/dipthong.cpp new file mode 100644 index 0000000..7202f9b --- /dev/null +++ b/code/redalert/win32lib/dipthong.cpp @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./dipthong.c 1.15 1994/05/20 15:35:17 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : DIPTHONG.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : February 23, 1992 * + * * + * Last Update : February 13, 1995 [BWG] * + * * + * DIGRAM or DIATOMIC encoding is the correct term for this method. * + * This is a fixed dictionary digram encoding optimized for English text. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Extract_String -- Extracts a string pointer from a string data block. * + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * Dip_Text -- Compresses text by using dipthonging. * + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" +//#include "ems.h" +#include +#include "dipthong.h" + +/*************************************************************************** + * Fixup_Text -- Converts dipthonged foreign text into normal text. * + * * + * Takes text that has been processed (or undipped) to hold foriegn * + * language character pairs (needed for Window_Print) and converts it * + * so that Text_Print will print it properly. Typically this would be * + * used after text has been undipped but before it will be Text_Printed.* + * Text that is to be Window_Printed doesn't and mustn't have its text * + * processed by this routine. * + * * + * INPUT: source -- Pointer to the source string to process. * + * * + * dest -- Destination buffer to hold the processed string. * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine will only reduce the size of the string if it * + * modifies it at all. Because of this it is quite legal to * + * pass the same pointers to this routine so that it will * + * modify the string "in place". * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +void Fixup_Text(char const *source, char *dest) +{ + if (source && dest) { + char const *src; + char temp; + + src = source; + while (*src) { + if (*src == KA_EXTEND) { + src++; + temp = *src++; + temp += 127; + *dest++ = temp; + } else { + *dest++ = *src++; + } + } + *dest = '\0'; + + } +} + + +/*************************************************************************** + * Dip_Text -- Compresses text by using dipthonging. * + * * + * This routine is used to compress text by using dipthonging. Text * + * that is compressed in this fashion usually is reduced in size by * + * approximately 40%. * + * * + * INPUT: source -- Pointer to the source string to compress. * + * * + * dest -- Pointer to the buffer that will hold the dipthong * + * text output. * + * * + * OUTPUT: Returns the number of bytes output into the output buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + *=========================================================================*/ +int Dip_Text(char const *source, char *dest) +{ + unsigned char first, // First character in pair. + next; // Second character in pair. + int common, // Common character index. + dipthong; // Dipthong character index. + + unsigned long length=0; // Length of output string + + first = *source++; + next = *source; + while (first) { + + if (first > 127) { + + /* + ** Characters greater than 127 cannot be dipthonged. They must + ** be preceeded with an extended character code. + */ + *dest++ = (char)KA_EXTEND; + first -= 127; + length++; + + } else { + + /* + ** Normal characters can be dipthonged. First see if there is a + ** match in the Common table. + */ + for (common = 0; common < 16; common++) { + if (Common[common] == first) { + + /* + ** Common character found. See if there is a matching + ** Dipthong character. + */ + for (dipthong = 0; dipthong < 8; dipthong++) { + if (Dipthong[common][dipthong] == next) { + first = (unsigned char) (common << 3); + first |= (unsigned char)dipthong; + first |= (unsigned char)0x80; + source++; + } + } + } + } + } + + /* + ** Output the translated character to the destination buffer. + */ + *dest++ = first; + length++; + + first = *source++; + next = *source; + } + + *dest = '\0'; + + return(length); +} + + +/*************************************************************************** + * UnDip_Text -- Undipthongs a text string into specified buffer. * + * * + * This routine is used to undipthong a text string and place the * + * undipped text into the buffer specified. Since dipthonged text is * + * compressed, in order for the text to be used it must be undipped * + * first. * + * * + * INPUT: source -- Pointer to the dipped string. * + * * + * dest -- Pointer to the destination buffer. * + * * + * OUTPUT: Returns the number of bytes placed into the destination * + * buffer. * + * * + * WARNINGS: Be sure the destination buffer is big enough to hold the * + * undipped text. * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 10/06/1994 JLB : Handles source string in EMS. * + *=========================================================================*/ +int UnDip_Text(char const *source, char *dest) +{ + int c; // Source input character. + int common; // Common character index. + int len; // Length of output string. + char const *src; + + len = 0; // Presume no translation. + + /* + ** Sweep through the source text and dipthong it. + */ + src = source; + c = *src++; + while (c) { + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + + common = (c & 0x78) >> 3; + + *dest++ = Common[common]; + len++; + + c = Dipthong[common][c & 0x07]; + } + + *dest++ = (unsigned char)c; + len++; + + c = *src++; + } + + /* + ** End the output text with a '\0'. + */ + *dest++ = '\0'; + + return(len); +} + + +/*************************************************************************** + * Extract_String -- Extracts a string pointer from a string data block. * + * * + * This routine is used to find a pointer to the specified string * + * inside a string block. String data blocks are created with the * + * TEXTMAKE utility. The data block my reside in XMS or EMS memory, * + * but of course the returned string pointer will also point to * + * such memory. In this case, the string must be placed in real * + * memory before it can be used. * + * * + * INPUT: data -- Pointer to the string data block. * + * * + * string -- The string number to extract (if < 0 then NULL * + * is returned). * + * * + * OUTPUT: Returns with pointer to the string number specified. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 08/13/1993 JLB : Created. * + * 08/13/1993 JLB : Handles EMS or XMS data pointer. * + *=========================================================================*/ + +#define TXT_GUEST 4567+3 +#define TXT_LOGIN 4567+4 +#define TXT_LOGIN_TO_INTERNET 4567+5 +#define TXT_YOUR_HANDLE 4567+6 +#define TXT_YOUR_PASSWORD 4567+7 +#define TXT_INTERNET_HOST 4567+8 +#define TXT_INTERNET_JOIN 4567+9 +#define TXT_INTERNET_GAME_TYPE 4567+10 +#define TXT_JOIN_INTERNET_GAME 4567+11 +#define TXT_ENTER_IP_ADDRESS 4567+12 +#define TXT_WINSOCK_CONNECTING 4567+13 +#define TXT_WINSOCK_NOT_CONNECTING 4567+14 +#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15 +#define TXT_WINSOCK_CONTACTING_SERVER 4567+16 +#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED 4567+17 +#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT 4567+18 +#define TXT_WINSOCK_UNABLE_TO_CONNECT 4567+19 +#define TXT_WINSOCK_CONNECTION_LOST 4567+20 +#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS 4567+21 + +static char InternetTxt[22][40]={ + "Internet H2H", + "Host Internet Game", + "Join Internet Game", + "Guest", + "Login", + "Login to Planet Westwood", + "Planet Westwood Handle", + "Planet Westwood Password", + "Host Game", + "Join Game", + "Choose Type of Internet Game", + "Join Internet Game", + "Address of Host", + "Connecting...", + "Connection Error!", + "Unable to connect to host!", + "Connecting to host...", + "Unable to resolve host address!", + "Unable to accept client connection", + "Unable to connect!", + "Connection lost!", + "Resolving address of host..." +}; + +char *Extract_String(void const *data, int string) +{ + unsigned short int const *ptr; + + if (!data || string < 0) return(NULL); + + if (string >= 4567) return (InternetTxt[string-4567]); + + ptr = (unsigned short int const *)data; + return (((char*)data) + ptr[string]); +} diff --git a/code/redalert/win32lib/dipthong.h b/code/redalert/win32lib/dipthong.h new file mode 100644 index 0000000..c8ab804 --- /dev/null +++ b/code/redalert/win32lib/dipthong.h @@ -0,0 +1,21 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +int Dip_Text(char const *source, char *dest); +int UnDip_Text(char const *source, char *dest); +char *Extract_String(void const *data, int string); +void Fixup_Text(char const *source, char *dest); +extern char Common[]; +extern char Dipthong[16][8]; diff --git a/code/redalert/win32lib/dplay.h b/code/redalert/win32lib/dplay.h new file mode 100644 index 0000000..c8e703d --- /dev/null +++ b/code/redalert/win32lib/dplay.h @@ -0,0 +1,323 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1994-1995 Microsoft Corporation. All Rights Reserved. + * + * File: dplay.h + * Content: DirectPlay include file + * + ***************************************************************************/ + +#ifndef __DPLAY_INCLUDED__ +#define __DPLAY_INCLUDED__ +#ifdef _WIN32 +/* for DECLARE_INTERFACE and HRESULT. */ +#include +#endif + +#define _FACDP 0x877 +#define MAKE_DPHRESULT( code ) MAKE_HRESULT( 1, _FACDP, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(push, 1) + + +/*============================================================================ + * + * DirectPlay Structures + * + * Various structures used to invoke DirectPlay. + * + *==========================================================================*/ + +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectPlay; +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#else +typedef struct IDirectPlay FAR *LPDIRECTPLAY; +#endif + +typedef DWORD DPID, FAR *LPDPID; + +typedef struct _DPCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMaxBufferSize; + DWORD dwMaxQueueSize; // Function of DPlay, not SP. + DWORD dwMaxPlayers; + DWORD dwHundredBaud; // 24 is 2400, 96 is 9600, etc. + DWORD dwLatency; +} DPCAPS; + +typedef DPCAPS FAR *LPDPCAPS; + +#define DPLONGNAMELEN 52 +#define DPSHORTNAMELEN 20 +#define DPSESSIONNAMELEN 32 +#define DPPASSWORDLEN 16 +#define DPUSERRESERVED 16 + +typedef struct +{ + DWORD dwSize; + GUID guidSession; // Id for Game. Null is all games. + DWORD dwSession; // session identifier + DWORD dwMaxPlayers; // Maximum players allowed in game. + DWORD dwCurrentPlayers; // Current players in Game. + DWORD dwFlags; // DPOPEN_* flags + char szSessionName[DPSESSIONNAMELEN];// Human readable name for Game + char szUserField[DPUSERRESERVED]; + DWORD dwReserved1; // Reserved for future MS use. + char szPassword[DPPASSWORDLEN]; // Password to be allowed into game. + DWORD dwReserved2; // Reserved for future MS use. + DWORD dwUser1; + DWORD dwUser2; + DWORD dwUser3; + DWORD dwUser4; +} DPSESSIONDESC; +typedef DPSESSIONDESC FAR *LPDPSESSIONDESC; + + +/* + * Create API + */ +typedef BOOL (FAR PASCAL * LPDPENUMDPCALLBACK)( + LPGUID lpSPGuid, + LPSTR lpFriendlyName, + DWORD dwMajorVersion, + DWORD dwMinorVersion, + LPVOID lpContext); + +typedef BOOL (FAR PASCAL * LPDPENUMSESSIONSCALLBACK)( + LPDPSESSIONDESC lpDPSGameDesc, + LPVOID lpContext, + LPDWORD lpdwTimeOut, + DWORD dwFlags); + + + +extern HRESULT WINAPI DirectPlayCreate( LPGUID lpGUID, LPDIRECTPLAY FAR *lplpDP, IUnknown FAR *pUnk); +extern HRESULT WINAPI DirectPlayEnumerate( LPDPENUMDPCALLBACK, LPVOID ); + + +/* Player enumeration callback prototype */ +typedef BOOL (FAR PASCAL *LPDPENUMPLAYERSCALLBACK)( + DPID dpId, + LPSTR lpFriendlyName, + LPSTR lpFormalName, + DWORD dwFlags, + LPVOID lpContext ); + +/* + * IDirectPlay + */ +#undef INTERFACE +#define INTERFACE IDirectPlay +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectPlay, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectPlay methods ***/ + STDMETHOD(AddPlayerToGroup) (THIS_ DPID, DPID) PURE; + STDMETHOD(Close) (THIS) PURE; + STDMETHOD(CreatePlayer) (THIS_ LPDPID,LPSTR,LPSTR,LPHANDLE) PURE; + STDMETHOD(CreateGroup) (THIS_ LPDPID,LPSTR,LPSTR) PURE; + STDMETHOD(DeletePlayerFromGroup)(THIS_ DPID,DPID) PURE; + STDMETHOD(DestroyPlayer) (THIS_ DPID) PURE; + STDMETHOD(DestroyGroup) (THIS_ DPID) PURE; + STDMETHOD(EnableNewPlayers) (THIS_ BOOL) PURE; + STDMETHOD(EnumGroupPlayers) (THIS_ DPID, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumGroups) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumPlayers) (THIS_ DWORD, LPDPENUMPLAYERSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(EnumSessions) (THIS_ LPDPSESSIONDESC,DWORD,LPDPENUMSESSIONSCALLBACK,LPVOID,DWORD) PURE; + STDMETHOD(GetCaps) (THIS_ LPDPCAPS) PURE; + STDMETHOD(GetMessageCount) (THIS_ DPID, LPDWORD) PURE; + STDMETHOD(GetPlayerCaps) (THIS_ DPID, LPDPCAPS) PURE; + STDMETHOD(GetPlayerName) (THIS_ DPID,LPSTR,LPDWORD,LPSTR,LPDWORD) PURE; + STDMETHOD(Initialize) (THIS_ LPGUID) PURE; + STDMETHOD(Open) (THIS_ LPDPSESSIONDESC) PURE; + STDMETHOD(Receive) (THIS_ LPDPID,LPDPID,DWORD,LPVOID,LPDWORD) PURE; + STDMETHOD(SaveSession) (THIS_ LPSTR) PURE; + STDMETHOD(Send) (THIS_ DPID, DPID, DWORD, LPVOID, DWORD) PURE; + STDMETHOD(SetPlayerName) (THIS_ DPID,LPSTR,LPSTR) PURE; +}; +#endif + + + + +/**************************************************************************** + * + * DIRECTPLAY ERRORS + * + * Errors are represented by negative values and cannot be combined. + * + ****************************************************************************/ +#define DP_OK 0 +#define DPERR_ALREADYINITIALIZED MAKE_DPHRESULT( 5 ) +#define DPERR_ACCESSDENIED MAKE_DPHRESULT( 10 ) +#define DPERR_ACTIVEPLAYERS MAKE_DPHRESULT( 20 ) +#define DPERR_BUFFERTOOSMALL MAKE_DPHRESULT( 30 ) +#define DPERR_CANTADDPLAYER MAKE_DPHRESULT( 40 ) +#define DPERR_CANTCREATEGROUP MAKE_DPHRESULT( 50 ) +#define DPERR_CANTCREATEPLAYER MAKE_DPHRESULT( 60 ) +#define DPERR_CANTCREATESESSION MAKE_DPHRESULT( 70 ) +#define DPERR_CAPSNOTAVAILABLEYET MAKE_DPHRESULT( 80 ) +#define DPERR_EXCEPTION MAKE_DPHRESULT( 90 ) +#define DPERR_GENERIC E_FAIL + +#define DPERR_INVALIDFLAGS MAKE_DPHRESULT( 120 ) +#define DPERR_INVALIDOBJECT MAKE_DPHRESULT( 130 ) +#define DPERR_INVALIDPARAM E_INVALIDARG +#define DPERR_INVALIDPARAMS DPERR_INVALIDPARAM +#define DPERR_INVALIDPLAYER MAKE_DPHRESULT( 150 ) +#define DPERR_NOCAPS MAKE_DPHRESULT( 160 ) +#define DPERR_NOCONNECTION MAKE_DPHRESULT( 170 ) +#define DPERR_NOMEMORY E_OUTOFMEMORY +#define DPERR_OUTOFMEMORY DPERR_NOMEMORY +#define DPERR_NOMESSAGES MAKE_DPHRESULT( 190 ) +#define DPERR_NONAMESERVERFOUND MAKE_DPHRESULT( 200 ) +#define DPERR_NOPLAYERS MAKE_DPHRESULT( 210 ) +#define DPERR_NOSESSIONS MAKE_DPHRESULT( 220 ) +#define DPERR_SENDTOOBIG MAKE_DPHRESULT( 230 ) +#define DPERR_TIMEOUT MAKE_DPHRESULT( 240 ) +#define DPERR_UNAVAILABLE MAKE_DPHRESULT( 250 ) +#define DPERR_UNSUPPORTED E_NOTIMPL +#define DPERR_BUSY MAKE_DPHRESULT( 270 ) +#define DPERR_USERCANCEL MAKE_DPHRESULT( 280 ) + + +#define DPOPEN_OPENSESSION 0x00000001 +#define DPOPEN_CREATESESSION 0x00000002 + +#define DPSEND_GUARANTEE 0x00000001 +#define DPSEND_HIGHPRIORITY 0x00000002 +#define DPSEND_TRYONCE 0x00000004 + +#define DPRECEIVE_ALL 0x00000001 +#define DPRECEIVE_TOPLAYER 0x00000002 +#define DPRECEIVE_FROMPLAYER 0x00000004 +#define DPRECEIVE_PEEK 0x00000008 + +#define DPCAPS_NAMESERVICE 0x00000001 // A name server is supported. +#define DPCAPS_NAMESERVER 0x00000002 // You are the name server. +#define DPCAPS_GUARANTEED 0x00000004 // SP's don't have to implement guarantees. + +#define DPENUMSESSIONS_AVAILABLE 0x00000001 // All games that match password (if given) + // and have openings. +#define DPENUMSESSIONS_ALL 0x00000002 +#define DPENUMSESSIONS_PREVIOUS 0x00000004 + +#define DPENUMPLAYERS_ALL 0x00000000 +#define DPENUMPLAYERS_PREVIOUS 0x00000004 +#define DPENUMPLAYERS_LOCAL 0x00000008 +#define DPENUMPLAYERS_REMOTE 0x00000010 +#define DPENUMPLAYERS_GROUP 0x00000020 +#define DPENUMPLAYERS_SESSION 0x00000080 + +// +// This flag is set on the enumsessions callback when the time out has occured. +// This means that there is no session data for this callback. +// If lpdwTimeOut is set to a non-zero value and the EnumSessionsCallback returns +// TRUE then EnumSessions will continue until the next timeout occurs. +// Timeouts are in milliseconds. + +#define DPESC_TIMEDOUT 0x00000001 + + +// +// System message structures and types. +// +// System messages have a leading 4 byte type code to identify the message. +// an app knows it is a system message because it is addressed 'To' player 0. +// + + +#define DPSYS_ADDPLAYER 0x0003 // DPMSG_ADDPLAYER +#define DPSYS_DELETEPLAYER 0x0005 // DPMSG_DELETEPLAYER + +#define DPSYS_ADDPLAYERTOGROUP 0x0007 // DPMSG_GROUPADD + +#define DPSYS_INVITE 0x000e // DPMSG_INVITE, Net only. + +#define DPSYS_DELETEGROUP 0x0020 // DPMSG_DELETEPLAYER +#define DPSYS_DELETEPLAYERFROMGRP 0x0021 // DPMSG_GROUPDELETE +#define DPSYS_SESSIONLOST 0x0031 + +#define DPSYS_CONNECT 0x484b // DPMSG_GENERIC + + + +typedef struct +{ + DWORD dwType; + DWORD dwPlayerType; + DPID dpId; + char szLongName[DPLONGNAMELEN]; + char szShortName[DPSHORTNAMELEN]; + DWORD dwCurrentPlayers; +} DPMSG_ADDPLAYER; + +typedef DPMSG_ADDPLAYER DPMSG_ADDGROUP; + +typedef struct +{ + DWORD dwType; + DPID dpIdGroup; + DPID dpIdPlayer; +} DPMSG_GROUPADD; + +typedef DPMSG_GROUPADD DPMSG_GROUPDELETE; +typedef struct +{ + DWORD dwType; + DPID dpId; +} DPMSG_DELETEPLAYER; + +typedef struct +{ + DWORD dwType; + DPSESSIONDESC dpsDesc; +} DPMSG_INVITE; + + + +typedef struct +{ + DWORD dwType; +} DPMSG_GENERIC; + +#pragma pack(pop) + + +DEFINE_GUID( IID_IDirectPlay, 0x5454e9a0, 0xdb65, 0x11ce, 0x92, 0x1c, 0x00, 0xaa, 0x00, 0x6c, 0x49, 0x72); + + + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/code/redalert/win32lib/drawbuff.h b/code/redalert/win32lib/drawbuff.h new file mode 100644 index 0000000..8f5ff9e --- /dev/null +++ b/code/redalert/win32lib/drawbuff.h @@ -0,0 +1,72 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef DRAWBUFF_H +#define DRAWBUFF_H + + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +class GraphicViewPortClass; +class GraphicBufferClass; +/*=========================================================================*/ +/* Define functions which have not under-gone name mangling */ +/*=========================================================================*/ + +long __cdecl Buffer_To_Buffer(GraphicViewPortClass* vp, int x, int y, int w, int h, void* buffer, int length); + +extern "C" { + /*======================================================================*/ + /* Externs for all of the common functions between the video buffer */ + /* class and the graphic buffer class. */ + /*======================================================================*/ + long __cdecl Buffer_Size_Of_Region(void *thisptr, int w, int h); + + void __cdecl Buffer_Put_Pixel(void * thisptr, int x, int y, unsigned char color); + int __cdecl Buffer_Get_Pixel(void * thisptr, int x, int y); + void __cdecl Buffer_Clear(void *thisptr, unsigned char color); + long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *Buffer, void *view); + BOOL __cdecl Linear_Blit_To_Linear( void *thisptr, void * dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans); + BOOL __cdecl Linear_Blit_To_Linear_Pal(void* this_object, void* dest, int src_x, int src_y, int dst_x, int dst_y, int w, int h, BOOL use_key); + BOOL __cdecl Linear_Scale_To_Linear( void *, void *, int, int, int, int, int, int, int, int, BOOL, char *); + + LONG __cdecl Buffer_Print(void *thisptr, const char *str, int x, int y, int fcolor, int bcolor); + + /*======================================================================*/ + /* Externs for all of the graphic buffer class only functions */ + /*======================================================================*/ + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); +} + +struct Image_t; + +Image_t* Load_Stamp(const char* name, const void* icondata); +void Get_Stamp_Size(const void* icondata, int *width, int *height); +Image_t* Load_StampHD(int theater, const char* iconName, const void *icondata); + +extern GraphicViewPortClass *LogicPage; +extern BOOL AllowHardwareBlitFills; +#endif diff --git a/code/redalert/win32lib/drawbuff.inc b/code/redalert/win32lib/drawbuff.inc new file mode 100644 index 0000000..fcc061c --- /dev/null +++ b/code/redalert/win32lib/drawbuff.inc @@ -0,0 +1,90 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWBUFF.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Clear :NEAR + +; Externs from BITBLIT.ASM module of the DRAWBUFF library +GLOBAL C Linear_Blit_To_Linear :NEAR + +; Externs from TOBUFF.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the DRAWBUFF library +GLOBAL C Linear_Scale_To_Linear :NEAR + +; Externs from TXTPRNT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Print :NEAR + + +;*-------------------------------------------------------------------------* +;* Define Buffer only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Line:NEAR + +; Externs from FILLQUAD.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Remap :NEAR + +; Externs from STAMP.ASM module of the DRAWBUFF library +GLOBAL C Buffer_Draw_Stamp :NEAR + +GLOBAL C get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + diff --git a/code/redalert/win32lib/drawmisc.cpp b/code/redalert/win32lib/drawmisc.cpp new file mode 100644 index 0000000..ad65e30 --- /dev/null +++ b/code/redalert/win32lib/drawmisc.cpp @@ -0,0 +1,3533 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* +** +** +** Misc asm functions from ww lib +** ST - 12/19/2018 1:20PM +** +** +** +** +** +** +** +** +** +** +** +*/ + +#include +#include "gbuffer.h" +#include "MISC.H" +#include "WSA.H" + +IconCacheClass::IconCacheClass (void) +{ + IsCached =FALSE; + SurfaceLost =FALSE; + DrawFrequency =0; + //CacheSurface =NULL; + IconSource =NULL; +} + +IconCacheClass::~IconCacheClass (void) +{ +} + +IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern "C"{ +IconSetType IconSetList[MAX_ICON_SETS]; +short IconCacheLookup[MAX_LOOKUP_ENTRIES]; +} + +int CachedIconsDrawn=0; //Counter of number of cache hits +int UnCachedIconsDrawn=0; //Counter of number of cache misses +BOOL CacheMemoryExhausted; //Flag set if we have run out of video RAM + + +void Invalidate_Cached_Icons (void) {} +void Restore_Cached_Icons (void) {} +void Register_Icon_Set (void *icon_data , BOOL pre_cache) {}; + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void __cdecl Clear_Icon_Pointers (void) {}; +extern "C" void __cdecl Cache_Copy_Icon (void const *icon_ptr ,void * , int) {}; +extern "C" int __cdecl Is_Icon_Cached (void const *icon_data , int icon) {return -1;}; +extern "C" int __cdecl Get_Icon_Index (void *icon_ptr) {return 0;}; +extern "C" int __cdecl Get_Free_Index (void) {return 0;}; +extern "C" BOOL __cdecl Cache_New_Icon (int icon_index, void *icon_ptr) {return -1;}; +extern "C" int __cdecl Get_Free_Cache_Slot(void) {return -1;} + +void IconCacheClass::Draw_It (void *dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height) {} + + +extern bool renderHDTexture; +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +void __cdecl Buffer_Draw_Line(void *this_object, int sx, int sy, int dx, int dy, unsigned char color) +{ + unsigned int clip_min_x; + unsigned int clip_max_x; + unsigned int clip_min_y; + unsigned int clip_max_y; + unsigned int clip_var; + unsigned int accum; + unsigned int bpr; + + static int _one_time_init = 0; + + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + static void *_clip_table [4*4] = {0}; + + unsigned int int_color = color; + unsigned int x1_pixel = (unsigned int) sx; + unsigned int y1_pixel = (unsigned int) sy; + unsigned int x2_pixel = (unsigned int) dx; + unsigned int y2_pixel = (unsigned int) dy; + + __asm { + mov eax,_one_time_init + and eax,eax + jnz init_done + + call do_init + +init_done: + + //;*================================================================== + //;* Take care of find the clip minimum and maximums + //;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[ebx]GraphicViewPortClass.Width + mov [clip_max_x],eax + add eax,[ebx]GraphicViewPortClass.XAdd + add eax,[ebx]GraphicViewPortClass.Pitch + mov [bpr],eax + mov eax,[ebx]GraphicViewPortClass.Height + mov [clip_max_y],eax + + //;*================================================================== + //;* Adjust max pixels as they are tested inclusively. + //;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + //;*================================================================== + //;* Set the registers with the data for drawing the line + //;*================================================================== + mov eax,[x1_pixel] //; eax = start x pixel position + mov ebx,[y1_pixel] //; ebx = start y pixel position + mov ecx,[x2_pixel] //; ecx = dest x pixel position + mov edx,[y2_pixel] //; edx = dest y pixel position + + //;*================================================================== + //;* This is the section that "pushes" the line into bounds. + //;* I have marked the section with PORTABLE start and end to signify + //;* how much of this routine is 100% portable between graphics modes. + //;* It was just as easy to have variables as it would be for constants + //;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + //;* to clip the line (default is the screen) + //;* PORTABLE start + //;*================================================================== + + cmp eax,[clip_min_x] + jl short clip_it + cmp eax,[clip_max_x] + jg short clip_it + cmp ebx,[clip_min_y] + jl short clip_it + cmp ebx,[clip_max_y] + jg short clip_it + cmp ecx,[clip_min_x] + jl short clip_it + cmp ecx,[clip_max_x] + jg short clip_it + cmp edx,[clip_min_y] + jl short clip_it + cmp edx,[clip_max_y] + jle short on_screen + + //;*================================================================== + //;* Takes care off clipping the line. + //;*================================================================== + clip_it: + call set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call set_bits + mov [clip_var],edi + or [clip_var],esi + jz short on_screen + test edi,esi + jne short off_screen + shl esi,2 + //call [clip_tbl+esi] + call [_clip_table+esi] + jc clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + //call [clip_tbl+edi] + call [_clip_table+edi] + jmp clip_it + + on_screen: + jmp draw_it + + off_screen: + jmp and_out + + //;*================================================================== + //;* Jump table for clipping conditions + //;*================================================================== + //clip_tbl DD nada,a_up,a_dwn,nada + // DD a_lft,a_lft,a_dwn,nada + // DD a_rgt,a_up,a_rgt,nada + // DD nada,nada,nada,nada + + nada: + clc + ret + + a_up: + mov esi,[clip_min_y] + call clip_vert + stc + ret + + a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call clip_vert + neg ebx + neg edx + stc + ret + + //;*================================================================== + //;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + //;*================================================================== + clip_vert: + push edx + push eax + mov [clip_var],edx //; clip_var = yb + sub [clip_var],ebx //; clip_var = (yb-ya) + neg eax //; eax=-xa + add eax,ecx //; (ebx-xa) + mov edx,esi //; edx=miny + sub edx,ebx //; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + ret + + a_lft: + mov esi,[clip_min_x] + call clip_horiz + stc + ret + + a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call clip_horiz + neg eax + neg ecx + stc + ret + + //;*================================================================== + //;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + //;*================================================================== + clip_horiz: + push edx + mov [clip_var],ecx //; clip_var = xb + sub [clip_var],eax //; clip_var = (xb-xa) + sub edx,ebx //; edx = (yb-ya) + neg eax //; eax = -xa + add eax,esi //; eax = (minx-xa) + imul edx //; eax = (minx-xa)(yb-ya) + idiv [clip_var] //; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax //; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + ret + + //;*================================================================== + //;* Sets the condition bits + //;*================================================================== + set_bits: + xor esi,esi + cmp ebx,[clip_min_y] //; if y >= top its not up + jge short a_not_up + or esi,1 + + a_not_up: + cmp ebx,[clip_max_y] //; if y <= bottom its not down + jle short a_not_down + or esi,2 + + a_not_down: + cmp eax,[clip_min_x] //; if x >= left its not left + jge short a_not_left + or esi,4 + + a_not_left: + cmp eax,[clip_max_x] //; if x <= right its not right + jle short a_not_right + or esi,8 + + a_not_right: + ret + + //;*================================================================== + //;* Draw the line to the screen. + //;* PORTABLE end + //;*================================================================== + draw_it: + sub edx,ebx //; see if line is being draw down + jnz short not_hline //; if not then its not a hline + jmp short hline //; do special case h line + + not_hline: + jg short down //; if so there is no need to rev it + neg edx //; negate for actual pixel length + xchg eax,ecx //; swap x's to rev line draw + sub ebx,edx //; get old edx + + down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + + mov esi,1 //; assume a right mover + sub ecx,eax //; see if line is right + jnz short not_vline //; see if its a vertical line + jmp vline + + not_vline: + jg short right //; if so, the difference = length + + //left: + neg ecx //; else negate for actual pixel length + neg esi //; negate counter to move left + + right: + cmp ecx,edx //; is it a horiz or vert line + jge short horiz //; if ecx > edx then |x|>|y| or horiz + + //vert: + xchg ecx,edx //; make ecx greater and edx lesser + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //vert_loop: + add ebx,eax + mov eax,[int_color] + + v_midloop: + mov [ebx],al + dec ecx + jl and_out + add ebx,[bpr] + sub [accum],edx //; sub the lesser + jge v_midloop //; any line could be new + add [accum],edi //; add greater for new accum + add ebx,esi //; next pixel over + jmp v_midloop + + horiz: + mov edi,ecx //; set greater + mov [accum],ecx //; set accumulator to 1/2 greater + shr [accum],1 + + //;*================================================================== + //;* at this point ... + //;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + //;* esi=adder; accum=accumulator + //;* in a vertical loop the adder is conditional and the inc constant + //;*================================================================== + //horiz_loop: + add ebx,eax + mov eax,[int_color] + + h_midloop: + mov [ebx],al + dec ecx //; dec counter + jl and_out //; end of line + add ebx,esi + sub [accum],edx //; sub the lesser + jge h_midloop + add [accum],edi //; add greater for new accum + add ebx,[bpr] //; goto next line + jmp h_midloop + + //;*================================================================== + //;* Special case routine for horizontal line draws + //;*================================================================== + hline: + cmp eax,ecx //; make eax < ecx + jl short hl_ac + xchg eax,ecx + + hl_ac: + sub ecx,eax //; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[eax]GraphicViewPortClass.Offset + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg big_line + mov al,[color] + rep stosb //; write as many words as possible + jmp short and_out //; get outt + + + big_line: + mov al,[color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz aligned + mov [edi],al + inc edi + dec ecx + + aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp and_out + + + //;*================================================================== + //;* a special case routine for vertical line draws + //;*================================================================== + vline: + mov ecx,edx //; get length of line to draw + inc ecx + add ebx,eax + mov eax,[int_color] + + vl_loop: + mov [ebx],al //; store bit + add ebx,[bpr] + dec ecx + jnz vl_loop + jmp and_out + + +do_init: + mov edi, offset _clip_table + + lea esi, nada + mov [edi], esi + mov [edi+12], esi + lea esi, a_up + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + + add edi, 16 + + lea esi, a_lft + mov [edi], esi + mov [edi+4], esi + lea esi, a_dwn + mov [edi+8], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, a_rgt + mov [edi], esi + mov [edi+8], esi + lea esi, a_up + mov [edi+4], esi + lea esi, nada + mov [edi+12], esi + + add edi, 16 + + lea esi, nada + mov [edi], esi + mov [edi+4], esi + mov [edi+8], esi + mov [edi+12], esi + + mov [_one_time_init], 1 + ret + + and_out: + } +} + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : DRAWLINE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 16, 1994 * +;* * +;* Last Update : August 30, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVC::Scale -- Scales a virtual viewport to another virtual viewport * +;* Normal_Draw -- jump loc for drawing scaled line of normal pixel * +;* __DRAW_LINE -- Assembly routine to draw a line * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + + +/* +;*************************************************************************** +;* VVC::DRAW_LINE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: WORD sx_pixel - the starting x pixel position * +;* WORD sy_pixel - the starting y pixel position * +;* WORD dx_pixel - the destination x pixel position * +;* WORD dy_pixel - the destination y pixel position * +;* WORD color - the color of the line to draw * +;* * +;* Bounds Checking: Compares sx_pixel, sy_pixel, dx_pixel and dy_pixel * +;* with the graphic viewport it has been assigned to. * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;* 08/30/1994 IML : Fixed clipping bug. * +;*=========================================================================* + PROC Buffer_Draw_Line C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*================================================================== + ;* Define the arguements that the function takes. + ;*================================================================== + ARG this_object:DWORD ; associated graphic view port + ARG x1_pixel:DWORD ; the start x pixel position + ARG y1_pixel:DWORD ; the start y pixel position + ARG x2_pixel:DWORD ; the dest x pixel position + ARG y2_pixel:DWORD ; the dest y pixel position + ARG color:DWORD ; the color we are drawing + + ;*================================================================== + ;* Define the local variables that we will use on the stack + ;*================================================================== + LOCAL clip_min_x:DWORD + LOCAL clip_max_x:DWORD + LOCAL clip_min_y:DWORD + LOCAL clip_max_y:DWORD + LOCAL clip_var:DWORD + LOCAL accum:DWORD + LOCAL bpr:DWORD + + ;*================================================================== + ;* Take care of find the clip minimum and maximums + ;*================================================================== + mov ebx,[this_object] + xor eax,eax + mov [clip_min_x],eax + mov [clip_min_y],eax + mov eax,[(GraphicViewPort ebx).GVPWidth] + mov [clip_max_x],eax + add eax,[(GraphicViewPort ebx).GVPXAdd] + add eax,[(GraphicViewPort ebx).GVPPitch] + mov [bpr],eax + mov eax,[(GraphicViewPort ebx).GVPHeight] + mov [clip_max_y],eax + + ;*================================================================== + ;* Adjust max pixels as they are tested inclusively. + ;*================================================================== + dec [clip_max_x] + dec [clip_max_y] + + ;*================================================================== + ;* Set the registers with the data for drawing the line + ;*================================================================== + mov eax,[x1_pixel] ; eax = start x pixel position + mov ebx,[y1_pixel] ; ebx = start y pixel position + mov ecx,[x2_pixel] ; ecx = dest x pixel position + mov edx,[y2_pixel] ; edx = dest y pixel position + + ;*================================================================== + ;* This is the section that "pushes" the line into bounds. + ;* I have marked the section with PORTABLE start and end to signify + ;* how much of this routine is 100% portable between graphics modes. + ;* It was just as easy to have variables as it would be for constants + ;* so the global vars ClipMaxX,ClipMinY,ClipMaxX,ClipMinY are used + ;* to clip the line (default is the screen) + ;* PORTABLE start + ;*================================================================== + + cmp eax,[clip_min_x] + jl short ??clip_it + cmp eax,[clip_max_x] + jg short ??clip_it + cmp ebx,[clip_min_y] + jl short ??clip_it + cmp ebx,[clip_max_y] + jg short ??clip_it + cmp ecx,[clip_min_x] + jl short ??clip_it + cmp ecx,[clip_max_x] + jg short ??clip_it + cmp edx,[clip_min_y] + jl short ??clip_it + cmp edx,[clip_max_y] + jle short ??on_screen + + ;*================================================================== + ;* Takes care off clipping the line. + ;*================================================================== +??clip_it: + call NEAR PTR ??set_bits + xchg eax,ecx + xchg ebx,edx + mov edi,esi + call NEAR PTR ??set_bits + mov [clip_var],edi + or [clip_var],esi + jz short ??on_screen + test edi,esi + jne short ??off_screen + shl esi,2 + call [DWORD PTR cs:??clip_tbl+esi] + jc ??clip_it + xchg eax,ecx + xchg ebx,edx + shl edi,2 + call [DWORD PTR cs:??clip_tbl+edi] + jmp ??clip_it + +??on_screen: + jmp ??draw_it + +??off_screen: + jmp ??out + + ;*================================================================== + ;* Jump table for clipping conditions + ;*================================================================== +??clip_tbl DD ??nada,??a_up,??a_dwn,??nada + DD ??a_lft,??a_lft,??a_dwn,??nada + DD ??a_rgt,??a_up,??a_rgt,??nada + DD ??nada,??nada,??nada,??nada + +??nada: + clc + retn + +??a_up: + mov esi,[clip_min_y] + call NEAR PTR ??clip_vert + stc + retn + +??a_dwn: + mov esi,[clip_max_y] + neg esi + neg ebx + neg edx + call NEAR PTR ??clip_vert + neg ebx + neg edx + stc + retn + + ;*================================================================== + ;* xa'=xa+[(miny-ya)(xb-xa)/(yb-ya)] + ;*================================================================== +??clip_vert: + push edx + push eax + mov [clip_var],edx ; clip_var = yb + sub [clip_var],ebx ; clip_var = (yb-ya) + neg eax ; eax=-xa + add eax,ecx ; (ebx-xa) + mov edx,esi ; edx=miny + sub edx,ebx ; edx=(miny-ya) + imul edx + idiv [clip_var] + pop edx + add eax,edx + pop edx + mov ebx,esi + retn + +??a_lft: + mov esi,[clip_min_x] + call NEAR PTR ??clip_horiz + stc + retn + +??a_rgt: + mov esi,[clip_max_x] + neg eax + neg ecx + neg esi + call NEAR PTR ??clip_horiz + neg eax + neg ecx + stc + retn + + ;*================================================================== + ;* ya'=ya+[(minx-xa)(yb-ya)/(xb-xa)] + ;*================================================================== +??clip_horiz: + push edx + mov [clip_var],ecx ; clip_var = xb + sub [clip_var],eax ; clip_var = (xb-xa) + sub edx,ebx ; edx = (yb-ya) + neg eax ; eax = -xa + add eax,esi ; eax = (minx-xa) + imul edx ; eax = (minx-xa)(yb-ya) + idiv [clip_var] ; eax = (minx-xa)(yb-ya)/(xb-xa) + add ebx,eax ; ebx = xa+[(minx-xa)(yb-ya)/(xb-xa)] + pop edx + mov eax,esi + retn + + ;*================================================================== + ;* Sets the condition bits + ;*================================================================== +??set_bits: + xor esi,esi + cmp ebx,[clip_min_y] ; if y >= top its not up + jge short ??a_not_up + or esi,1 + +??a_not_up: + cmp ebx,[clip_max_y] ; if y <= bottom its not down + jle short ??a_not_down + or esi,2 + +??a_not_down: + cmp eax,[clip_min_x] ; if x >= left its not left + jge short ??a_not_left + or esi,4 + +??a_not_left: + cmp eax,[clip_max_x] ; if x <= right its not right + jle short ??a_not_right + or esi,8 + +??a_not_right: + retn + + ;*================================================================== + ;* Draw the line to the screen. + ;* PORTABLE end + ;*================================================================== +??draw_it: + sub edx,ebx ; see if line is being draw down + jnz short ??not_hline ; if not then its not a hline + jmp short ??hline ; do special case h line + +??not_hline: + jg short ??down ; if so there is no need to rev it + neg edx ; negate for actual pixel length + xchg eax,ecx ; swap x's to rev line draw + sub ebx,edx ; get old edx + +??down: + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + + mov esi,1 ; assume a right mover + sub ecx,eax ; see if line is right + jnz short ??not_vline ; see if its a vertical line + jmp ??vline + +??not_vline: + jg short ??right ; if so, the difference = length + +??left: + neg ecx ; else negate for actual pixel length + neg esi ; negate counter to move left + +??right: + cmp ecx,edx ; is it a horiz or vert line + jge short ??horiz ; if ecx > edx then |x|>|y| or horiz + +??vert: + xchg ecx,edx ; make ecx greater and edx lesser + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??vert_loop: + add ebx,eax + mov eax,[color] + +??v_midloop: + mov [ebx],al + dec ecx + jl ??out + add ebx,[bpr] + sub [accum],edx ; sub the lesser + jge ??v_midloop ; any line could be new + add [accum],edi ; add greater for new accum + add ebx,esi ; next pixel over + jmp ??v_midloop + +??horiz: + mov edi,ecx ; set greater + mov [accum],ecx ; set accumulator to 1/2 greater + shr [accum],1 + + ;*================================================================== + ;* at this point ... + ;* eax=xpos ; ebx=page line offset; ecx=counter; edx=lesser; edi=greater; + ;* esi=adder; accum=accumulator + ;* in a vertical loop the adder is conditional and the inc constant + ;*================================================================== +??horiz_loop: + add ebx,eax + mov eax,[color] + +??h_midloop: + mov [ebx],al + dec ecx ; dec counter + jl ??out ; end of line + add ebx,esi + sub [accum],edx ; sub the lesser + jge ??h_midloop + add [accum],edi ; add greater for new accum + add ebx,[bpr] ; goto next line + jmp ??h_midloop + + ;*================================================================== + ;* Special case routine for horizontal line draws + ;*================================================================== +??hline: + cmp eax,ecx ; make eax < ecx + jl short ??hl_ac + xchg eax,ecx + +??hl_ac: + sub ecx,eax ; get len + inc ecx + + push edx + push eax + mov eax,[bpr] + mul ebx + mov ebx,eax + mov eax,[this_object] + add ebx,[(GraphicViewPort eax).GVPOffset] + pop eax + pop edx + add ebx,eax + mov edi,ebx + cmp ecx,15 + jg ??big_line + mov al,[byte color] + rep stosb ; write as many words as possible + jmp short ??out ; get outt + + +??big_line: + mov al,[byte color] + mov ah,al + mov ebx,eax + shl eax,16 + mov ax,bx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + test edi,3 + jz ??aligned + mov [edi],al + inc edi + dec ecx + +??aligned: + mov ebx,ecx + shr ecx,2 + rep stosd + mov ecx,ebx + and ecx,3 + rep stosb + jmp ??out + + + ;*================================================================== + ;* a special case routine for vertical line draws + ;*================================================================== +??vline: + mov ecx,edx ; get length of line to draw + inc ecx + add ebx,eax + mov eax,[color] + +??vl_loop: + mov [ebx],al ; store bit + add ebx,[bpr] + dec ecx + jnz ??vl_loop + +??out: + ret + ENDP Buffer_Draw_Line + + +*/ + + + + + + + + + + + + + + + +/* + +;*************************************************************************** +;* GVPC::FILL_RECT -- Fills a rectangular region of a graphic view port * +;* * +;* INPUT: WORD the left hand x pixel position of region * +;* WORD the upper x pixel position of region * +;* WORD the right hand x pixel position of region * +;* WORD the lower x pixel position of region * +;* UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* +*/ + +/* +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +*/ +#define OPTIMAL_BYTE_COPY 14 + + +void __cdecl Buffer_Fill_Rect(void *thisptr, int x, int y, int w, int h, unsigned char color) +{ + GraphicViewPortClass& vp = (*(GraphicViewPortClass *)thisptr); + + // If we aren't drawing within the viewport, return + if (x >= vp.Get_Width() || y >= vp.Get_Height() || w < 0 || h < 0) { + return; + } + + // Clipping + if (x < 0) { + x = 0; + } + + if (y < 0) { + y = 0; + } + + w = min(w, vp.Get_Width() - 1); + h = min(h, vp.Get_Height() - 1); + + uint8_t* offset = (y * 4) * vp.Get_Full_Pitch() + (x * 4) + (uint8_t*)(vp.Get_Offset()); + int height = h - y + 1; + int width = w - x + 1; + + for (int i = 0; i < height; ++i) { + FastScanlinePaletteBlit(offset, color, width); + offset += vp.Get_Full_Pitch() * 4; + } +} + + + + +/* +;*************************************************************************** +;* VVPC::CLEAR -- Clears a virtual viewport instance * +;* * +;* INPUT: UBYTE the color (optional) to clear the view port to * +;* * +;* OUTPUT: none * +;* * +;* NOTE: This function is optimized to handle viewport with no XAdd * +;* value. It also handles DWORD aligning the destination * +;* when speed can be gained by doing it. * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;* 08/23/1994 SKB : Clear the direction flag to always go forward. * +;*=========================================================================* +*/ +void __cdecl Buffer_Clear(void *this_object, unsigned char color) +{ + GraphicViewPortClass* cls = (GraphicViewPortClass*)this_object; + if (cls->Get_Graphic_Buffer()->GetMemoryBuffer() == NULL) + return; + + memset(cls->Get_Graphic_Buffer()->GetMemoryBuffer(), color, cls->Get_Width() * cls->Get_Height()); +} + +/* +;*************************************************************************** +;* VVC::SCALE -- Scales a virtual viewport to another virtual viewport * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 06/16/1994 PWG : Created. * +;*=========================================================================* + PROC Linear_Scale_To_Linear C NEAR + USES eax,ebx,ecx,edx,esi,edi +*/ + +// jmarshall - ported to c + + +unsigned char* Draw_Dropsample(const unsigned char* in, int inwidth, int inheight, int outwidth, int outheight) { + int i, j, k; + const unsigned char* inrow; + const unsigned char* pix1; + unsigned char* out, * out_p; + static unsigned char ViewportPixelBuffer[4096 * 4096 * 4]; + + out = &ViewportPixelBuffer[0]; + out_p = out; + + int bpp = 1; + if (renderHDTexture) { + bpp = 4; + } + + for (i = 0; i < outheight; i++, out_p += outwidth * bpp) { + inrow = in + bpp * inwidth * (int)((i + 0.25) * inheight / outheight); + for (j = 0; j < outwidth; j++) { + k = j * inwidth / outwidth; + pix1 = inrow + k * bpp; + if (renderHDTexture) { + out_p[j * 4 + 0] = pix1[0]; + out_p[j * 4 + 1] = pix1[1]; + out_p[j * 4 + 2] = pix1[2]; + out_p[j * 4 + 3] = pix1[3]; + } + else { + out_p[j * 1 + 0] = pix1[0]; + } + //out_p[j * 3 + 1] = pix1[1]; + //out_p[j * 3 + 2] = pix1[2]; + } + } + + return out; +} + + +/* +============== +R_CopyImage +============== +*/ +void Nearest_CopyImage(unsigned char* source, int sourceX, int sourceY, int sourceWidth, unsigned char* dest, int destX, int destY, int destWidth, int width, int height, BOOL trans) +{ + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + + int destPos = ((destWidth * (y + destY)) * 4) + ((x + destX) * 4); + int sourcePos = (sourceWidth * (y + (sourceY))) + (x + (sourceX)); + if (renderHDTexture) { + sourcePos = ((sourceWidth * (y + sourceY)) * 4) + ((x + sourceX) * 4); + + // This needs some work! + if (source[0] == 0 && source[1] == 0 && source[2] == 0 && trans) + continue; + + dest[destPos + 0] = source[sourcePos + 0]; + dest[destPos + 1] = source[sourcePos + 1]; + dest[destPos + 2] = source[sourcePos + 2]; + dest[destPos + 3] = 255; + } + else + { + unsigned char c = source[sourcePos]; + + if (c == 0 && trans) { + continue; + } + + dest[destPos + 0] = backbuffer_palette[(c * 3) + 0]; + dest[destPos + 1] = backbuffer_palette[(c * 3) + 1]; + dest[destPos + 2] = backbuffer_palette[(c * 3) + 2]; + dest[destPos + 3] = 255; + } + } + } +} + + +BOOL __cdecl Linear_Scale_To_Linear(void* this_object, void* dest, int src_x, int src_y, int dst_x, int dst_y, int src_width, int src_height, int dst_width, int dst_height, BOOL trans, char* remap) +{ + GraphicViewPortClass* viewportClass = (GraphicViewPortClass*)this_object; + GraphicViewPortClass* destViewportClass = (GraphicViewPortClass*)dest; + + unsigned char* viewportBuffer = ((unsigned char*)viewportClass->Get_Offset()); + + // Scale the GraphicViewPortClass to dest_x, dest_y + unsigned char* scaled_buffer = Draw_Dropsample((unsigned char*)viewportBuffer, viewportClass->Get_Width(), viewportClass->Get_Height(), dst_width, dst_height); + + // Blit the scaled_buffer to dest. + Nearest_CopyImage(scaled_buffer, src_x, src_y, dst_width, (unsigned char*)destViewportClass->Get_Offset(), dst_x, dst_y, destViewportClass->Get_Width(), dst_width, dst_height, trans); + + return true; +} + +BOOL __cdecl Linear_Blit_To_Linear(void* this_object, void* dest, int x_pixel, int y_pixel, int dest_x0, int dest_y0, int pixel_width, int pixel_height, BOOL trans) +{ + GraphicViewPortClass* viewportClass = (GraphicViewPortClass*)this_object; + GraphicViewPortClass* destViewportClass = (GraphicViewPortClass*)dest; + bool needsUnlock = false; + bool sourceNeedsUnlock = false; + + unsigned char* viewportBuffer = NULL; + viewportBuffer = ((unsigned char*)viewportClass->Get_Offset()); + + unsigned char* destBuffer = NULL; + destBuffer = ((unsigned char*)destViewportClass->Get_Offset()); + + //if (destViewportClass->Get_Graphic_Buffer()->Get_Buffer() != NULL) { + // destBuffer = (byte*)destViewportClass->Get_Graphic_Buffer()->Get_Buffer(); + //} + //else { + // destViewportClass->Get_Graphic_Buffer()->Lock(); + // destBuffer = ((byte*)destViewportClass->Get_Graphic_Buffer()->GetMemoryBuffer()); + // needsUnlock = true; + //} + // + //if (viewportBuffer == NULL) { + // viewportClass->Get_Graphic_Buffer()->Lock(); + // viewportBuffer = ((byte*)viewportClass->Get_Graphic_Buffer()->GetMemoryBuffer()); + // sourceNeedsUnlock = true; + //} + + // Blit the buffer to dest. + Nearest_CopyImage(viewportBuffer, x_pixel, y_pixel, viewportClass->Get_Width(), (unsigned char*)destBuffer, dest_x0, dest_y0, destViewportClass->Get_Width(), pixel_width, pixel_height, trans); + + //if (needsUnlock) { + // destViewportClass->Get_Graphic_Buffer()->Unlock(); + //} + // + //if (sourceNeedsUnlock) { + // viewportClass->Get_Graphic_Buffer()->Unlock(); + //} + return true; +} + +BOOL __cdecl Linear_Blit_To_Linear_Pal(void* this_object, void* dest, int src_x, int src_y, int dst_x, int dst_y, int w, int h, BOOL use_key) +{ + GraphicViewPortClass& src_vp = *(GraphicViewPortClass*)this_object; + GraphicViewPortClass& dst_vp = *(GraphicViewPortClass*)dest; + + uint8_t* src = (uint8_t*)(src_vp.Get_Offset()); + uint8_t* dst = (uint8_t*)(dst_vp.Get_Offset()); + int src_pitch = src_vp.Get_Full_Pitch(); + int dst_pitch = dst_vp.Get_Full_Pitch(); + + if (src_x >= src_vp.Get_Width() || src_y >= src_vp.Get_Height() || dst_x >= dst_vp.Get_Width() + || dst_y >= dst_vp.Get_Height() || h < 0 || w < 1) { + return 0; + } + + src_x = max(0, src_x); + src_y = max(0, src_y); + dst_x = max(0, dst_x); + dst_y = max(0, dst_y); + + h = (dst_y + h) > dst_vp.Get_Height() ? dst_vp.Get_Height() - 1 - dst_y : h; + w = (dst_x + w) > dst_vp.Get_Width() ? dst_vp.Get_Width() - 1 - dst_x : w; + + // move our pointers to the start locations + src += src_x + src_y * src_pitch; + dst += dst_x + dst_y * dst_pitch; + + // If src is before dst, we run the risk of overlapping memory regions so we + // need to move src and dst to the last line and work backwards + if (src < dst) { + uint8_t* esrc = src + (h - 1) * src_pitch; + uint8_t* edst = dst + (h - 1) * dst_pitch; + if (use_key) { + char key_colour = 0; + while (h-- != 0) { + // Can't optimize as we need to check every pixel against key colour :( + for (int i = w - 1; i >= 0; --i) { + if (esrc[i] != key_colour) { + edst[i] = esrc[i]; + } + } + + edst -= dst_pitch; + esrc -= src_pitch; + } + } + else { + while (h-- != 0) { + memmove(edst, esrc, w); + edst -= dst_pitch; + esrc -= src_pitch; + } + } + } + else { + if (use_key) { + uint8_t key_colour = 0; + while (h-- != 0) { + // Can't optimize as we need to check every pixel against key colour :( + for (int i = 0; i < w; ++i) { + if (src[i] != key_colour) { + dst[i] = src[i]; + } + } + + dst += dst_pitch; + src += src_pitch; + } + } + else { + while (h-- != 0) { + memmove(dst, src, w); + dst += dst_pitch; + src += src_pitch; + } + } + } + return true; +} + +// jmarshall end + + + + + + + + + + + + + + + + + + + + + +unsigned int LastIconset = 0; +unsigned int StampPtr = 0; // DD 0 ; Pointer to icon data. + +unsigned int IsTrans = 0; // DD 0 ; Pointer to transparent icon flag table. + +unsigned int MapPtr = 0; // DD 0 ; Pointer to icon map. +unsigned int IconWidth = 0; // DD 0 ; Width of icon in pixels. +unsigned int IconHeight = 0; // DD 0 ; Height of icon in pixels. +unsigned int IconSize = 0; // DD 0 ; Number of bytes for each icon data. +unsigned int IconCount = 0; // DD 0 ; Number of icons in the set. + + + +#if (0) +LastIconset DD 0 ; Pointer to last iconset initialized. +StampPtr DD 0 ; Pointer to icon data. + +IsTrans DD 0 ; Pointer to transparent icon flag table. + +MapPtr DD 0 ; Pointer to icon map. +IconWidth DD 0 ; Width of icon in pixels. +IconHeight DD 0 ; Height of icon in pixels. +IconSize DD 0 ; Number of bytes for each icon data. +IconCount DD 0 ; Number of icons in the set. + + +GLOBAL C Buffer_Draw_Stamp:near +GLOBAL C Buffer_Draw_Stamp_Clip:near + +; 256 color icon system. +#endif + + +/* +;*********************************************************** +; INIT_STAMPS +; +; VOID cdecl Init_Stamps(VOID *icondata); +; +; This routine initializes the stamp data. +; Bounds Checking: NONE +; +;* +*/ +extern "C" void __cdecl Init_Stamps(unsigned int icondata) +{ + + __asm { + pushad // ST - 12/20/2018 10:30AM + + ; Verify legality of parameter. + cmp [icondata],0 + je short fini + + ; Don't initialize if already initialized to this set (speed reasons). + mov edi,[icondata] + cmp [LastIconset],edi + je short fini + mov [LastIconset],edi + + ; Record number of icons in set. + movzx eax,[edi]IControl_Type.Count + mov [IconCount],eax + + ; Record width of icon. + movzx eax,[edi]IControl_Type.Width + mov [IconWidth],eax + + ; Record height of icon. + movzx ebx,[edi]IControl_Type.Height + mov [IconHeight],ebx + + ; Record size of icon (in bytes). + mul ebx + mov [IconSize],eax + + ; Record hard pointer to icon map data. + mov eax,[edi]IControl_Type.Map + add eax,edi + mov [MapPtr],eax + +//nomap: + ; Record hard pointer to icon data. + mov eax,edi + add eax,[edi]IControl_Type.Icons + mov [StampPtr],eax + + ; Record the transparent table. + mov eax,edi + add eax,[edi]IControl_Type.TransFlag + mov [IsTrans],eax + +fini: + popad // ST - 12/20/2018 10:30AM + + } +} + + + VOID __cdecl Buffer_Draw_Line(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Fill_Rect(void *thisptr, int sx, int sy, int dx, int dy, unsigned char color); + VOID __cdecl Buffer_Remap(void * thisptr, int sx, int sy, int width, int height, void *remap); + VOID __cdecl Buffer_Fill_Quad(void * thisptr, VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + void __cdecl Buffer_Draw_Stamp(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void __cdecl Buffer_Draw_Stamp_Clip(void const *thisptr, void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int ,int,int,int); + void * __cdecl Get_Font_Palette_Ptr ( void ); + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : REMAP.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 1, 1994 * +;* * +;* Last Update : July 1, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +*/ + + +VOID __cdecl Buffer_Remap(void * this_object, int sx, int sy, int width, int height, void *remap) +{ +/* + PROC Buffer_Remap C NEAR + USES eax,ebx,ecx,edx,esi,edi + + ;*=================================================================== + ;* Define the arguements that our function takes. + ;*=================================================================== + ARG this_object:DWORD + ARG x0_pixel:DWORD + ARG y0_pixel:DWORD + ARG region_width:DWORD + ARG region_height:DWORD + ARG remap :DWORD + + ;*=================================================================== + ; Define some locals so that we can handle things quickly + ;*=================================================================== + local x1_pixel : DWORD + local y1_pixel : DWORD + local win_width : dword + local counter_x : dword +*/ + + unsigned int x0_pixel = (unsigned int) sx; + unsigned int y0_pixel = (unsigned int) sy; + unsigned int region_width = (unsigned int) width; + unsigned int region_height = (unsigned int) height; + + unsigned int x1_pixel = 0; + unsigned int y1_pixel = 0; + unsigned int win_width = 0; + unsigned int counter_x = 0; + + __asm { + + cmp [ remap ] , 0 + jz real_out + + ; Clip Source Rectangle against source Window boundaries. + mov esi , [ this_object ] ; get ptr to src + xor ecx , ecx + xor edx , edx + mov edi , [esi]GraphicViewPortClass.Width ; get width into register + mov ebx , [ x0_pixel ] + mov eax , [ x0_pixel ] + add ebx , [ region_width ] + shld ecx , eax , 1 + mov [ x1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + mov edi,[esi]GraphicViewPortClass.Height ; get height into register + mov ebx , [ y0_pixel ] + mov eax , [ y0_pixel ] + add ebx , [ region_height ] + shld ecx , eax , 1 + mov [ y1_pixel ] , ebx + inc edi + shld edx , ebx , 1 + sub eax , edi + sub ebx , edi + shld ecx , eax , 1 + shld edx , ebx , 1 + + xor cl , 5 + xor dl , 5 + mov al , cl + test dl , cl + jnz real_out + or al , dl + jz do_remap + + test cl , 1000b + jz scr_left_ok + mov [ x0_pixel ] , 0 + +scr_left_ok: + test cl , 0010b + jz scr_bottom_ok + mov [ y0_pixel ] , 0 + +scr_bottom_ok: + test dl , 0100b + jz scr_right_ok + mov eax , [esi]GraphicViewPortClass.Width ; get width into register + mov [ x1_pixel ] , eax +scr_right_ok: + test dl , 0001b + jz do_remap + mov eax , [esi]GraphicViewPortClass.Height ; get width into register + mov [ y1_pixel ] , eax + + +do_remap: + cld + mov edi , [esi]GraphicViewPortClass.Offset + mov eax , [esi]GraphicViewPortClass.XAdd + mov ebx , [ x1_pixel ] + add eax , [esi]GraphicViewPortClass.Width + add eax , [esi]GraphicViewPortClass.Pitch + mov esi , eax + mul [ y0_pixel ] + add edi , [ x0_pixel ] + sub ebx , [ x0_pixel ] + jle real_out + add edi , eax + sub esi , ebx + + mov ecx , [ y1_pixel ] + sub ecx , [ y0_pixel ] + jle real_out + mov eax , [ remap ] + mov [ counter_x ] , ebx + xor edx , edx + +outer_loop: + mov ebx , [ counter_x ] +inner_loop: + mov dl , [ edi ] + mov dl , [ eax + edx ] + mov [ edi ] , dl + inc edi + dec ebx + jnz inner_loop + add edi , esi + dec ecx + jnz outer_loop + + + + +real_out: +// ret + } +} + + + + + + + + + + + + + + + +/* +; ************************************************************************** +; ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S * +; ************************************************************************** +; * * +; * Project Name : WSA Support routines * +; * * +; * File Name : XORDELTA.ASM * +; * * +; * Programmer : Scott K. Bowen * +; * * +; * Last Update :May 23, 1994 [SKB] * +; * * +; *------------------------------------------------------------------------* +; * Functions: * +;* Apply_XOR_Delta -- Apply XOR delta data to a buffer. * +;* Apply_XOR_Delta_To_Page_Or_Viewport -- Calls the copy or the XOR funti* +;* Copy_Delta_buffer -- Copies XOR Delta Data to a section of a page. * +;* XOR_Delta_Buffer -- Xor's the data in a XOR Delta format to a page. * +; * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -* + +IDEAL +P386 +MODEL USE32 FLAT +*/ + + +/* +LOCALS ?? + +; These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +; These change, make sure and change their values in wsa.cpp. +DO_XOR equ 0 +DO_COPY equ 1 +TO_VIEWPORT equ 0 +TO_PAGE equ 2 + +; +; Routines defined in this module +; +; +; UWORD Apply_XOR_Delta(UWORD page_seg, BYTE *delta_ptr); +; PUBLIC Apply_XOR_Delta_To_Page_Or_Viewport(UWORD page_seg, BYTE *delta_ptr, WORD width, WORD copy) +; +; PROC C XOR_Delta_Buffer +; PROC C Copy_Delta_Buffer +; + +GLOBAL C Apply_XOR_Delta:NEAR +GLOBAL C Apply_XOR_Delta_To_Page_Or_Viewport:NEAR +*/ + +#define DO_XOR 0 +#define DO_COPY 1 +#define TO_VIEWPORT 0 +#define TO_PAGE 2 + +void __cdecl XOR_Delta_Buffer(int nextrow); +void __cdecl Copy_Delta_Buffer(int nextrow); + + +/* +;*************************************************************************** +;* APPLY_XOR_DELTA -- Apply XOR delta data to a linear buffer. * +;* AN example of this in C is at the botton of the file commented out. * +;* * +;* INPUT: BYTE *target - destination buffer. * +;* BYTE *delta - xor data to be delta uncompress. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 05/23/1994 SKB : Created. * +;*=========================================================================* +*/ +unsigned int __cdecl Apply_XOR_Delta(char *target, char *delta) +{ +/* +PROC Apply_XOR_Delta C near + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointers. + ARG delta:DWORD ; pointers. +*/ + + __asm { + + ; Optimized for 486/pentium by rearanging instructions. + mov edi,[target] ; get our pointers into offset registers. + mov esi,[delta] + + cld ; make sure we go forward + xor ecx,ecx ; use cx for loop + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ;get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edi + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edi ; go to next dest pixel + dec ecx ; one less to go. + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] + lea esi,[esi+2] ; get word code in ax + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + add edi,eax ; do the skip. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + } +} + + +/* +;---------------------------------------------------------------------------- + +;*************************************************************************** +;* APPLY_XOR_DELTA_To_Page_Or_Viewport -- Calls the copy or the XOR funtion. * +;* * +;* * +;* This funtion is call to either xor or copy XOR_Delta data onto a * +;* page instead of a buffer. The routine will set up the registers * +;* need for the actual routines that will perform the copy or xor. * +;* * +;* The registers are setup as follows : * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ + +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy) +{ + /* + USES ebx,ecx,edx,edi,esi + ARG target:DWORD ; pointer to the destination buffer. + ARG delta:DWORD ; pointer to the delta buffer. + ARG width:DWORD ; width of animation. + ARG nextrow:DWORD ; Page/Buffer width - anim width. + ARG copy:DWORD ; should it be copied or xor'd? + */ + + __asm { + + mov edi,[target] ; Get the target pointer. + mov esi,[delta] ; Get the destination pointer. + + xor eax,eax ; clear eax, later put them into ecx and edx. + + cld ; make sure we go forward + + mov ebx,[nextrow] ; get the amount to add to get to next row from end. push it later... + + mov ecx,eax ; use cx for loop + mov edx,eax ; use dx to count the relative column. + + push ebx ; push nextrow onto the stack for Copy/XOR_Delta_Buffer. + mov ebx,[width] ; bx will hold the max column for speed compares + + ; At this point, all the registers have been set up. Now call the correct function + ; to either copy or xor the data. + + cmp [copy],DO_XOR ; Do we want to copy or XOR + je xorfunct ; Jump to XOR if not copy + call Copy_Delta_Buffer ; Call the function to copy the delta buffer. + jmp didcopy ; jump past XOR + xorfunct: + call XOR_Delta_Buffer ; Call funtion to XOR the deltat buffer. + didcopy: + pop ebx ; remove the push done to pass a value. + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* XOR_DELTA_BUFFER -- Xor's the data in a XOR Delta format to a page. * +;* This will only work right if the page has the previous data on it. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* edx,ecx,eax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl XOR_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + xor [edi],al ; xor that byte on the dest + inc esi + inc edx ; increment our count on current column + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + xor [edi],al ; xor that byte. + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} + + +/* +;---------------------------------------------------------------------------- + + +;*************************************************************************** +;* COPY_DELTA_BUFFER -- Copies XOR Delta Data to a section of a page. * +;* This function should only be called by XOR_Delta_Buffer_To_Page_Or_Viewport. * +;* The registers must be setup as follows : * +;* * +;* INPUT: * +;* es:edi - destination segment:offset onto page. * +;* ds:esi - source buffer segment:offset of delta data. * +;* dx,cx,ax - are all zeroed out before entry. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 03/09/1992 SB : Created. * +;*=========================================================================* +*/ +void __cdecl Copy_Delta_Buffer(int nextrow) +{ + /* + ARG nextrow:DWORD + */ + + __asm { + + top_loop: + xor eax,eax ; clear out eax. + mov al,[esi] ; get delta source byte + inc esi + + test al,al ; check for a SHORTDUMP ; check al incase of sign value. + je short_run + js check_others + + ; + ; SHORTDUMP + ; + mov ecx,eax ; stick count in cx + + dump_loop: + mov al,[esi] ; get delta XOR byte + + mov [edi],al ; store that byte on the dest + + inc edx ; increment our count on current column + inc esi + inc edi + cmp edx,ebx ; are we at the final column + jne end_col1 ; if not the jmp over the code + + sub edi,edx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col1: + + dec ecx + jnz dump_loop + jmp top_loop + + ; + ; SHORTRUN + ; + + short_run: + mov cl,[esi] ; get count + inc esi ; inc delta source + + do_run: + mov al,[esi] ; get XOR byte + inc esi + + run_loop: + mov [edi],al ; store the byte (instead of XOR against current color) + + inc edx ; increment our count on current column + inc edi ; go to next dest pixel + cmp edx,ebx ; are we at the final column + jne end_col2 ; if not the jmp over the code + + sub edi,ebx ; get our column back to the beginning. + xor edx,edx ; zero out our column counter + add edi,[nextrow] ; jump to start of next row + end_col2: + + + dec ecx + jnz run_loop + jmp top_loop + + ; + ; By now, we know it must be a LONGDUMP, SHORTSKIP, LONGRUN, or LONGSKIP + ; + + check_others: + sub eax,080h ; opcode -= 0x80 + jnz do_skip ; if zero then get next word, otherwise use remainder. + + mov ax,[esi] ; get word code in ax + lea esi,[esi+2] + test ax,ax ; set flags. (not 32bit register so neg flag works) + jle not_long_skip + + ; + ; SHORTSKIP AND LONGSKIP + ; + do_skip: + sub edi,edx ; go back to beginning or row. + add edx,eax ; incriment our count on current row + recheck3: + cmp edx,ebx ; are we past the end of the row + jb end_col3 ; if not the jmp over the code + + sub edx,ebx ; Subtract width from the col counter + add edi,[nextrow] ; jump to start of next row + jmp recheck3 ; jump up to see if we are at the right row + end_col3: + add edi,edx ; get to correct position in row. + jmp top_loop + + + not_long_skip: + jz stop ; long count of zero means stop + sub eax,08000h ; opcode -= 0x8000 + test eax,04000h ; is it a LONGRUN (code & 0x4000)? + je long_dump + + ; + ; LONGRUN + ; + sub eax,04000h ; opcode -= 0x4000 + mov ecx,eax ; use cx as loop count + jmp do_run ; jump to run code. + + + ; + ; LONGDUMP + ; + + long_dump: + mov ecx,eax ; use cx as loop count + jmp dump_loop ; go to the dump loop. + + stop: + + } +} +/* +;---------------------------------------------------------------------------- +*/ + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : FADING.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : August 20, 1993 * +;* * +;* Last Update : August 20, 1993 [JLB] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Build_Fading_Table :NEAR + + CODESEG + +;*********************************************************** +; BUILD_FADING_TABLE +; +; void *Build_Fading_Table(void *palette, void *dest, long int color, long int frac); +; +; This routine will create the fading effect table used to coerce colors +; from toward a common value. This table is used when Fading_Effect is +; active. +; +; Bounds Checking: None +;* +*/ +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac) +{ + /* + PROC Build_Fading_Table C near + USES ebx, ecx, edi, esi + ARG palette:DWORD + ARG dest:DWORD + ARG color:DWORD + ARG frac:DWORD + + LOCAL matchvalue:DWORD ; Last recorded match value. + LOCAL targetred:BYTE ; Target gun red. + LOCAL targetgreen:BYTE ; Target gun green. + LOCAL targetblue:BYTE ; Target gun blue. + LOCAL idealred:BYTE + LOCAL idealgreen:BYTE + LOCAL idealblue:BYTE + LOCAL matchcolor:BYTE ; Tentative match color. + */ + + int matchvalue = 0; //:DWORD ; Last recorded match value. + unsigned char targetred = 0; //BYTE ; Target gun red. + unsigned char targetgreen = 0; //BYTE ; Target gun green. + unsigned char targetblue = 0; //BYTE ; Target gun blue. + unsigned char idealred = 0; //BYTE + unsigned char idealgreen = 0; //BYTE + unsigned char idealblue = 0; //BYTE + unsigned char matchcolor = 0; //:BYTE ; Tentative match color. + + __asm { + cld + + ; If the source palette is NULL, then just return with current fading table pointer. + cmp [palette],0 + je fini + cmp [dest],0 + je fini + + ; Fractions above 255 become 255. + mov eax,[frac] + cmp eax,0100h + jb short ok + mov [frac],0FFh + ok: + + ; Record the target gun values. + mov esi,[palette] + mov ebx,[color] + add esi,ebx + add esi,ebx + add esi,ebx + lodsb + mov [targetred],al + lodsb + mov [targetgreen],al + lodsb + mov [targetblue],al + + ; Main loop. + xor ebx,ebx ; Remap table index. + + ; Transparent black never gets remapped. + mov edi,[dest] + mov [edi],bl + inc edi + + ; EBX = source palette logical number (1..255). + ; EDI = running pointer into dest remap table. + mainloop: + inc ebx + mov esi,[palette] + add esi,ebx + add esi,ebx + add esi,ebx + + mov edx,[frac] + shr edx,1 + ; new = orig - ((orig-target) * fraction); + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetred] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealred],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetgreen] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealgreen],dh ; preserve ideal color gun value. + + lodsb ; orig + mov dh,al ; preserve it for later. + sub al,[targetblue] ; al = (orig-target) + imul dl ; ax = (orig-target)*fraction + shl ax,1 + sub dh,ah ; dh = orig - ((orig-target) * fraction) + mov [idealblue],dh ; preserve ideal color gun value. + + ; Sweep through the entire existing palette to find the closest + ; matching color. Never matches with color 0. + + mov eax,[color] + mov [matchcolor],al ; Default color (self). + mov [matchvalue],-1 ; Ridiculous match value init. + mov ecx,255 + + mov esi,[palette] ; Pointer to original palette. + add esi,3 + + ; BH = color index. + mov bh,1 + innerloop: + + ; Recursion through the fading table won't work if a color is allowed + ; to remap to itself. Prevent this from occuring. + add esi,3 + cmp bh,bl + je short notclose + sub esi,3 + + xor edx,edx ; Comparison value starts null. + mov eax,edx + ; Build the comparison value based on the sum of the differences of the color + ; guns squared. + lodsb + sub al,[idealred] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealgreen] + mov ah,al + imul ah + add edx,eax + + lodsb + sub al,[idealblue] + mov ah,al + imul ah + add edx,eax + jz short perfect ; If perfect match found then quit early. + + cmp edx,[matchvalue] + ja short notclose + mov [matchvalue],edx ; Record new possible color. + mov [matchcolor],bh + notclose: + inc bh ; Checking color index. + loop innerloop + mov bh,[matchcolor] + perfect: + mov [matchcolor],bh + xor bh,bh ; Make BX valid main index again. + + ; When the loop exits, we have found the closest match. + mov al,[matchcolor] + stosb + cmp ebx,255 + jne mainloop + + fini: + mov eax,[dest] +// ret + + + } +} + + + + + + + + + + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood Library * +;* * +;* File Name : PAL.ASM * +;* * +;* Programmer : Joe L. Bostic * +;* * +;* Start Date : May 30, 1992 * +;* * +;* Last Update : April 27, 1994 [BR] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Set_Palette_Range -- Sets changed values in the palette. * +;* Bump_Color -- adjusts specified color in specified palette * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;********************** Model & Processor Directives ************************ +IDEAL +P386 +MODEL USE32 FLAT + + +;include "keyboard.inc" +FALSE = 0 +TRUE = 1 + +;****************************** Declarations ******************************** +GLOBAL C Set_Palette_Range:NEAR +GLOBAL C Bump_Color:NEAR +GLOBAL C CurrentPalette:BYTE:768 +GLOBAL C PaletteTable:byte:1024 + + +;********************************** Data ************************************ +LOCALS ?? + + DATASEG + +CurrentPalette DB 768 DUP(255) ; copy of current values of DAC regs +PaletteTable DB 1024 DUP(0) + +IFNDEF LIB_EXTERNS_RESOLVED +VertBlank DW 0 ; !!!! this should go away +ENDIF + + +;********************************** Code ************************************ + CODESEG +*/ + +extern "C" unsigned char CurrentPalette[768] = {255}; // DB 768 DUP(255) ; copy of current values of DAC regs +extern "C" unsigned char PaletteTable[1024] = {0}; // DB 1024 DUP(0) + + +/* +;*************************************************************************** +;* SET_PALETTE_RANGE -- Sets a palette range to the new pal * +;* * +;* INPUT: * +;* * +;* OUTPUT: * +;* * +;* PROTO: * +;* * +;* WARNINGS: This routine is optimized for changing a small number of * +;* colors in the palette. +;* * +;* HISTORY: * +;* 03/07/1995 PWG : Created. * +;*=========================================================================* +*/ +void __cdecl Set_Palette_Range(void *palette) +{ + memcpy(CurrentPalette, palette, 768); + Set_DD_Palette(palette); + + /* + PROC Set_Palette_Range C NEAR + ARG palette:DWORD + + GLOBAL Set_DD_Palette_:near + GLOBAL Wait_Vert_Blank_:near + + pushad + mov esi,[palette] + mov ecx,768/4 + mov edi,offset CurrentPalette + cld + rep movsd + ;call Wait_Vert_Blank_ + mov eax,[palette] + push eax + call Set_DD_Palette_ + pop eax + popad + ret + */ +} + + +/* +;*************************************************************************** +;* Bump_Color -- adjusts specified color in specified palette * +;* * +;* INPUT: * +;* VOID *palette - palette to modify * +;* WORD changable - color # to change * +;* WORD target - color to bend toward * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 04/27/1994 BR : Converted to 32-bit. * +;*=========================================================================* +; BOOL cdecl Bump_Color(VOID *palette, WORD changable, WORD target); +*/ +BOOL __cdecl Bump_Color(void *pal, int color, int desired) +{ + /* +PROC Bump_Color C NEAR + USES ebx,ecx,edi,esi + ARG pal:DWORD, color:WORD, desired:WORD + LOCAL changed:WORD ; Has palette changed? + */ + + short short_color = (short) color; + short short_desired = (short) desired; + bool changed = false; + + __asm { + mov edi,[pal] ; Original palette pointer. + mov esi,edi + mov eax,0 + mov ax,[short_color] + add edi,eax + add edi,eax + add edi,eax ; Offset to changable color. + mov ax,[short_desired] + add esi,eax + add esi,eax + add esi,eax ; Offset to target color. + + mov [changed],FALSE ; Presume no change. + mov ecx,3 ; Three color guns. + + ; Check the color gun. + colorloop: + mov al,[BYTE PTR esi] + sub al,[BYTE PTR edi] ; Carry flag is set if subtraction needed. + jz short gotit + mov [changed],TRUE + inc [BYTE PTR edi] ; Presume addition. + jnc short gotit ; oops, subtraction needed so dec twice. + dec [BYTE PTR edi] + dec [BYTE PTR edi] + gotit: + inc edi + inc esi + loop colorloop + + movzx eax,[changed] + } +} + + + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : GraphicViewPortClass * +;* * +;* File Name : PUTPIXEL.ASM * +;* * +;* Programmer : Phil Gorrow * +;* * +;* Start Date : June 7, 1994 * +;* * +;* Last Update : June 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* VVPC::Put_Pixel -- Puts a pixel on a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + + +CODESEG +*/ + +/* +;*************************************************************************** +;* VVPC::PUT_PIXEL -- Puts a pixel on a virtual viewport * +;* * +;* INPUT: WORD the x position for the pixel relative to the upper * +;* left corner of the viewport * +;* WORD the y pos for the pixel relative to the upper left * +;* corner of the viewport * +;* UBYTE the color of the pixel to write * +;* * +;* OUTPUT: none * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/08/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Put_Pixel C near + USES eax,ebx,ecx,edx,edi +*/ + +void __cdecl Buffer_Put_Pixel(void * this_object, int x_pixel, int y_pixel, unsigned char color) +{ + + /* + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set + ARG color:BYTE ; what color should we clear to + */ + + __asm { + + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short done ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae done ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; add in direct draw pitch + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + mov al,[color] ; read in color value + mov [edi],al ; write it to the screen + done: + } +} + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Support Library * +;* * +;* File Name : cliprect.asm * +;* * +;* Programmer : Julio R Jerez * +;* * +;* Start Date : Mar, 2 1995 * +;* * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* int Clip_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* int Confine_Rect ( int * x , int * y , int * dw , int * dh , * +;* int width , int height ) ; * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C Clip_Rect :NEAR +GLOBAL C Confine_Rect :NEAR + +CODESEG + +;*************************************************************************** +;* Clip_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x , &y , &w , &h -> Pointer to rectangle being clipped * +;* width , height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* b) A negative value if the rectangle is totally outside the * +;* the clipping window * +;* c) A positive value if the rectangle was clipped against the * +;* clipping window, also the values pointed by x, y, w, h will * +;* be modified to new clipped values * +;* * +;* 05/03/1995 JRJ : added comment * +;*=========================================================================* +; int Clip_Rect (int* x, int* y, int* dw, int* dh, int width, int height); * +*/ + +extern "C" int __cdecl Clip_Rect ( int * x , int * y , int * w , int * h , int width , int height ) +{ + +/* + PROC Clip_Rect C near + uses ebx,ecx,edx,esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width:dword + arg height:dword +*/ + + __asm { + + ;This Clipping algorithm is a derivation of the very well known + ;Cohen-Sutherland Line-Clipping test. Due to its simplicity and efficiency + ;it is probably the most commontly implemented algorithm both in software + ;and hardware for clipping lines, rectangles, and convex polygons against + ;a rectagular clipping window. For reference see + ;"COMPUTER GRAPHICS principles and practice by Foley, Vandam, Feiner, Hughes + ; pages 113 to 177". + ; Briefly consist in computing the Sutherland code for both end point of + ; the rectangle to find out if the rectangle is: + ; - trivially accepted (no further clipping test, return the oroginal data) + ; - trivially rejected (return with no action, return error code) + ; - retangle must be iteratively clipped again edges of the clipping window + ; and return the clipped rectangle + + ; get all four pointer into regisnters + mov esi,[x] ; esi = pointer to x + mov edi,[y] ; edi = pointer to x + mov eax,[w] ; eax = pointer to dw + mov ebx,[h] ; ebx = pointer to dh + + ; load the actual data into reg + mov esi,[esi] ; esi = x0 + mov edi,[edi] ; edi = y0 + mov eax,[eax] ; eax = dw + mov ebx,[ebx] ; ebx = dh + + ; create a wire frame of the type [x0,y0] , [x1,y1] + add eax,esi ; eax = x1 = x0 + dw + add ebx,edi ; ebx = y1 = y0 + dh + + ; we start we suthenland code0 and code1 set to zero + xor ecx,ecx ; cl = sutherland boolean code0 + xor edx,edx ; dl = sutherland boolean code0 + + ; now we start computing the to suthenland boolean code for x0 , x1 + shld ecx,esi,1 ; bit3 of code0 = sign bit of (x0 - 0) + shld edx,eax,1 ; bit3 of code1 = sign bit of (x1 - 0) + sub esi,[width] ; get the difference (x0 - (width + 1)) + sub eax,[width] ; get the difference (x1 - (width + 1)) + dec esi + dec eax + shld ecx,esi,1 ; bit2 of code0 = sign bit of (x0 - (width + 1)) + shld edx,eax,1 ; bit2 of code1 = sign bit of (x0 - (width + 1)) + + ; now we start computing the to suthenland boolean code for y0 , y1 + shld ecx,edi,1 ; bit1 of code0 = sign bit of (y0 - 0) + shld edx,ebx,1 ; bit1 of code1 = sign bit of (y0 - 0) + sub edi,[height] ; get the difference (y0 - (height + 1)) + sub ebx,[height] ; get the difference (y1 - (height + 1)) + dec edi + dec ebx + shld ecx,edi,1 ; bit0 of code0 = sign bit of (y0 - (height + 1)) + shld edx,ebx,1 ; bit0 of code1 = sign bit of (y1 - (height + 1)) + + ; Bit 2 and 0 of cl and bl are complemented + xor cl,5 ; reverse bit2 and bit0 in code0 + xor dl,5 ; reverse bit2 and bit0 in code1 + + ; now perform the rejection test + mov eax,-1 ; set return code to false + mov bl,cl ; save code0 for future use + test dl,cl ; if any two pair of bit in code0 and code1 is set + jnz clip_out ; then rectangle is outside the window + + ; now perform the aceptance test + xor eax,eax ; set return code to true + or bl,dl ; if all pair of bits in code0 and code1 are reset + jz clip_out ; then rectangle is insize the window. ' + + ; we need to clip the rectangle iteratively + mov eax,-1 ; set return code to false + test cl,1000b ; if bit3 of code0 is set then the rectangle + jz left_ok ; spill out the left edge of the window + mov edi,[x] ; edi = a pointer to x0 + mov ebx,[w] ; ebx = a pointer to dw + mov esi,[edi] ; esi = x0 + mov [dword ptr edi],0 ; set x0 to 0 "this the left edge value" + add [ebx],esi ; adjust dw by x0, since x0 must be negative + + left_ok: + test cl,0010b ; if bit1 of code0 is set then the rectangle + jz bottom_ok ; spill out the bottom edge of the window + mov edi,[y] ; edi = a pointer to y0 + mov ebx,[h] ; ebx = a pointer to dh + mov esi,[edi] ; esi = y0 + mov [dword ptr edi],0 ; set y0 to 0 "this the bottom edge value" + add [ebx],esi ; adjust dh by y0, since y0 must be negative + + bottom_ok: + test dl,0100b ; if bit2 of code1 is set then the rectangle + jz right_ok ; spill out the right edge of the window + mov edi,[w] ; edi = a pointer to dw + mov esi,[x] ; esi = a pointer to x + mov ebx,[width] ; ebx = the width of the window + sub ebx,[esi] ; the new dw is the difference (width-x0) + mov [edi],ebx ; adjust dw to (width - x0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + right_ok: + test dl,0001b ; if bit0 of code1 is set then the rectangle + jz clip_ok ; spill out the top edge of the window + mov edi,[h] ; edi = a pointer to dh + mov esi,[y] ; esi = a pointer to y0 + mov ebx,[height] ; ebx = the height of the window + sub ebx,[esi] ; the new dh is the difference (height-y0) + mov [edi],ebx ; adjust dh to (height-y0) + jle clip_out ; if (width-x0) = 0 then the clipped retangle + ; has no width we are done + clip_ok: + mov eax,1 ; signal the calling program that the rectangle was modify + clip_out: + //ret + } + + //ENDP Clip_Rect +} + +/* +;*************************************************************************** +;* Confine_Rect -- clip a given rectangle against a given window * +;* * +;* INPUT: &x,&y,w,h -> Pointer to rectangle being clipped * +;* width,height -> dimension of clipping window * +;* * +;* OUTPUT: a) Zero if the rectangle is totally contained by the * +;* clipping window. * +;* c) A positive value if the rectangle was shifted in position * +;* to fix inside the clipping window, also the values pointed * +;* by x, y, will adjusted to a new values * +;* * +;* NOTE: this function make not attempt to verify if the rectangle is * +;* bigger than the clipping window and at the same time wrap around* +;* it. If that is the case the result is meaningless * +;*=========================================================================* +; int Confine_Rect (int* x, int* y, int dw, int dh, int width, int height); * +*/ + +extern "C" int __cdecl Confine_Rect ( int * x , int * y , int w , int h , int width , int height ) +{ + +/* + PROC Confine_Rect C near + uses ebx, esi,edi + arg x:dword + arg y:dword + arg w:dword + arg h:dword + arg width :dword + arg height:dword +*/ + __asm { + + xor eax,eax + mov ebx,[x] + mov edi,[w] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[width] + neg esi + dec edi + + test esi,edi + jl x_axix_ok + mov eax,1 + + test esi,esi + jl shift_right + mov [dword ptr ebx],0 + jmp x_axix_ok + shift_right: + inc edi + sub [ebx],edi + x_axix_ok: + mov ebx,[y] + mov edi,[h] + + mov esi,[ebx] + add edi,[ebx] + + sub edi,[height] + neg esi + dec edi + + test esi,edi + jl confi_out + mov eax,1 + + test esi,esi + jl shift_top + mov [dword ptr ebx],0 + + //ret + jmp confi_out + shift_top: + inc edi + sub [ebx],edi + confi_out: + //ret + + //ENDP Confine_Rect + } +} + + + + + + + + + +/* +; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#33 $ +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Library routine * +;* * +;* File Name : UNCOMP.ASM * +;* * +;* Programmer : Christopher Yates * +;* * +;* Last Update : 20 August, 1990 [CY] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* * +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +GLOBAL C LCW_Uncompress :NEAR + +CODESEG + +; ---------------------------------------------------------------- +; +; Here are prototypes for the routines defined within this module: +; +; ULONG LCW_Uncompress(BYTE *source, BYTE *dest, ULONG length); +; +; ---------------------------------------------------------------- +*/ +#if (0)//ST 5/10/2019 +extern "C" unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length_) +{ +//PROC LCW_Uncompress C near +// +// USES ebx,ecx,edx,edi,esi +// +// ARG source:DWORD +// ARG dest:DWORD +// ARG length:DWORD +//;LOCALS +// LOCAL a1stdest:DWORD +// LOCAL maxlen:DWORD +// LOCAL lastbyte:DWORD +// LOCAL lastcom:DWORD +// LOCAL lastcom1:DWORD + + unsigned long a1stdest; + unsigned long maxlen; + unsigned long lastbyte; + //unsigned long lastcom; + //unsigned long lastcom1; + + __asm { + + + mov edi,[dest] + mov esi,[source] + mov edx,[length_] + + ; + ; + ; uncompress data to the following codes in the format b = byte, w = word + ; n = byte code pulled from compressed data + ; Bit field of n command description + ; n=0xxxyyyy,yyyyyyyy short run back y bytes and run x+3 + ; n=10xxxxxx,n1,n2,...,nx+1 med length copy the next x+1 bytes + ; n=11xxxxxx,w1 med run run x+3 bytes from offset w1 + ; n=11111111,w1,w2 long copy copy w1 bytes from offset w2 + ; n=11111110,w1,b1 long run run byte b1 for w1 bytes + ; n=10000000 end end of data reached + ; + + mov [a1stdest],edi + add edx,edi + mov [lastbyte],edx + cld ; make sure all lod and sto are forward + mov ebx,esi ; save the source offset + + loop_label: + mov eax,[lastbyte] + sub eax,edi ; get the remaining byte to uncomp + jz short out_label ; were done + + mov [maxlen],eax ; save for string commands + mov esi,ebx ; mov in the source index + + xor eax,eax + mov al,[esi] + inc esi + test al,al ; see if its a short run + js short notshort + + mov ecx,eax ;put count nibble in cl + + mov ah,al ; put rel offset high nibble in ah + and ah,0Fh ; only 4 bits count + + shr cl,4 ; get run -3 + add ecx,3 ; get actual run length + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short rsok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + rsok: + mov al,[esi] ; get rel offset low byte + lea ebx,[esi+1] ; save the source offset + mov esi,edi ; get the current dest + sub esi,eax ; get relative offset + + rep movsb + + jmp loop_label + + notshort: + test al,40h ; is it a length? + jne short notlength ; if not it could be med or long run + + cmp al,80h ; is it the end? + je short out_label ; if so its over + + mov cl,al ; put the byte in count register + and ecx,3Fh ; and off the extra bits + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short lenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + lenok: + rep movsb + + mov ebx,esi ; save the source offset + jmp loop_label + + out_label: + mov eax,edi + sub eax,[a1stdest] + jmp label_exit + + notlength: + mov cl,al ; get the entire code + and ecx,3Fh ; and off all but the size -3 + add ecx,3 ; add 3 for byte count + + cmp al,0FEh + jne short notrunlength + + xor ecx,ecx + mov cx,[esi] + + xor eax,eax + mov al,[esi+2] + lea ebx,[esi+3] ;save the source offset + + cmp ecx,[maxlen] ; is it too big to fit? + jbe short runlenok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runlenok: + test ecx,0ffe0h + jnz dont_use_stosb + rep stosb + jmp loop_label + + + dont_use_stosb: + mov ah,al + mov edx,eax + shl eax,16 + or eax,edx + + test edi,3 + jz aligned + + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + + aligned: + mov edx,ecx + shr ecx,2 + rep stosd + + and edx,3 + jz loop_label + mov ecx,edx + rep stosb + jmp loop_label + + + + + + + notrunlength: + cmp al,0FFh ; is it a long run? + jne short notlong ; if not use the code as the size + + xor ecx,ecx + xor eax,eax + mov cx,[esi] ; if so, get the size + lea esi,[esi+2] + + notlong: + mov ax,[esi] ;get the real index + add eax,[a1stdest] ;add in the 1st index + lea ebx,[esi+2] ;save the source offset + cmp ecx,[maxlen] ;compare for overrun + mov esi,eax ;use eax as new source + jbe short runok ; if not, its ok + + mov ecx,[maxlen] ; if so, max it out so it dosen't overrun + + runok: + test ecx,0ffe0h + jnz dont_use_movsb + rep movsb + jmp loop_label + + + + + dont_use_movsb: + lea edx,[edi+0fffffffch] + cmp esi,edx + ja use_movsb + + test edi,3 + jz aligned2 + + mov eax,[esi] + mov [edi],eax + mov edx,edi + and edi,0fffffffch + lea edi,[edi+4] + and edx,3 + dec dl + xor dl,3 + sub ecx,edx + add esi,edx + + aligned2: + mov edx,ecx + shr ecx,2 + and edx,3 + rep movsd + mov ecx,edx + use_movsb: + rep movsb + jmp loop_label + + + + + label_exit: + mov eax,edi + mov ebx,[dest] + sub eax,ebx + + //ret + + } +} +#endif + + + + + + + + + + + + + +/* +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : TOPAGE.ASM * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : June 8, 1994 * +;* * +;* Last Update : June 15, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* Buffer_To_Page -- Copies a linear buffer to a virtual viewport * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +IDEAL +P386 +MODEL USE32 FLAT + +TRANSP equ 0 + + +INCLUDE ".\drawbuff.inc" +INCLUDE ".\gbuffer.inc" + +CODESEG + +;*************************************************************************** +;* VVC::TOPAGE -- Copies a linear buffer to a virtual viewport * +;* * +;* INPUT: WORD x_pixel - x pixel on viewport to copy from * +;* WORD y_pixel - y pixel on viewport to copy from * +;* WORD pixel_width - the width of copy region * +;* WORD pixel_height - the height of copy region * +;* BYTE * src - buffer to copy from * +;* VVPC * dest - virtual viewport to copy to * +;* * +;* OUTPUT: none * +;* * +;* WARNINGS: Coordinates and dimensions will be adjusted if they exceed * +;* the boundaries. In the event that no adjustment is * +;* possible this routine will abort. If the size of the * +;* region to copy exceeds the size passed in for the buffer * +;* the routine will automatically abort. * +;* * +;* HISTORY: * +;* 06/15/1994 PWG : Created. * +;*=========================================================================* + */ + +extern "C" long __cdecl Buffer_To_Page(int x, int y, int w, int h, void *buffer, void *dest) +{ + GraphicViewPortClass& vp = *(GraphicViewPortClass*)dest; + // from chronoshift + int xstart = x; + int ystart = y; + int xend = x + w - 1; + int yend = y + h - 1; + + int xoffset = 0; + int yoffset = 0; + + // If we aren't drawing within the viewport, return + if (!buffer || xstart >= vp.Get_Width() || ystart >= vp.Get_Height() || xend < 0 || yend < 0) { + } + + // Clipping + if (xstart < 0) { + xoffset = -xstart; + xstart = 0; + } + + if (ystart < 0) { + yoffset += h * (-ystart); + ystart = 0; + } + + xend = min(xend, vp.Get_Width() - 1); + yend = min(yend, vp.Get_Height() - 1); + + int pitch = vp.Get_Pitch() + vp.Get_Width() + vp.Get_XAdd(); + uint8_t* dst = (y * 4) * pitch + (x * 4) + (uint8_t*)(vp.Get_Offset()); + uint8_t* src = xoffset + w * yoffset + static_cast(buffer); + // int dst_pitch = x_pos + pitch - xend; + // int src_pitch = x_pos + width - xend; + int lines = yend - ystart + 1; + int blit_width = xend - xstart + 1; + + // blit + while (lines--) { + FastScanlinePaletteBlit(dst, src, blit_width); + src += w; + dst += pitch * 4; + } + + return 0; +} + + //ENDP Buffer_To_Page + //END + + + + + + + + + +/* + +;*************************************************************************** +;* VVPC::GET_PIXEL -- Gets a pixel from the current view port * +;* * +;* INPUT: WORD the x pixel on the screen. * +;* WORD the y pixel on the screen. * +;* * +;* OUTPUT: UBYTE the pixel at the specified location * +;* * +;* WARNING: If pixel is to be placed outside of the viewport then * +;* this routine will abort. * +;* * +;* HISTORY: * +;* 06/07/1994 PWG : Created. * +;*=========================================================================* + PROC Buffer_Get_Pixel C near + USES ebx,ecx,edx,edi + + ARG this_object:DWORD ; this is a member function + ARG x_pixel:DWORD ; x position of pixel to set + ARG y_pixel:DWORD ; y position of pixel to set +*/ + +extern "C" int __cdecl Buffer_Get_Pixel(void * this_object, int x_pixel, int y_pixel) +{ + __asm { + + ;*=================================================================== + ; Get the viewport information and put bytes per row in ecx + ;*=================================================================== + mov ebx,[this_object] ; get a pointer to viewport + xor eax,eax + mov edi,[ebx]GraphicViewPortClass.Offset ; get the correct offset + mov ecx,[ebx]GraphicViewPortClass.Height ; edx = height of viewport + mov edx,[ebx]GraphicViewPortClass.Width ; ecx = width of viewport + + ;*=================================================================== + ; Verify that the X pixel offset if legal + ;*=================================================================== + mov eax,[x_pixel] ; find the x position + cmp eax,edx ; is it out of bounds + jae short exit_label ; if so then get out + add edi,eax ; otherwise add in offset + + ;*=================================================================== + ; Verify that the Y pixel offset if legal + ;*=================================================================== + mov eax,[y_pixel] ; get the y position + cmp eax,ecx ; is it out of bounds + jae exit_label ; if so then get out + add edx,[ebx]GraphicViewPortClass.XAdd ; otherwise find bytes per row + add edx,[ebx]GraphicViewPortClass.Pitch ; otherwise find bytes per row + mul edx ; offset = bytes per row * y + add edi,eax ; add it into the offset + + ;*=================================================================== + ; Write the pixel to the screen + ;*=================================================================== + xor eax,eax ; clear the word + mov al,[edi] ; read in the pixel + exit_label: + //ret + //ENDP Buffer_Get_Pixel + + } +} + + diff --git a/code/redalert/win32lib/drawrect.cpp b/code/redalert/win32lib/drawrect.cpp new file mode 100644 index 0000000..beaaacd --- /dev/null +++ b/code/redalert/win32lib/drawrect.cpp @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : DRAWRECT.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : August 20, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif + +/*************************************************************************** + * Draw_Rect -- Draws a rectangle to the LogicPage. * + * * + * This routine will draw a rectangle to the LogicPage. The rectangle * + * doesn't have to be aligned on the vertical or horizontal axis. In * + * fact, it doesn't even have to be a rectangle. The "square" can be * + * skewed. * + * * + * INPUT: x1_pixel, y1_pixel -- One corner. * + * * + * x2_pixel, y2_pixel -- The other corner. * + * * + * color -- The color to draw the lines. * + * * + * OUTPUT: none * + * * + * WARNINGS: None, but the rectangle will be clipped to the current * + * draw line clipping rectangle. * + * * + * HISTORY: * + * 08/20/1993 JLB : Created. * + *=========================================================================*/ +VOID GraphicViewPortClass::Draw_Rect(int x1_pixel, int y1_pixel, int x2_pixel, int y2_pixel, unsigned char color) +{ + Lock(); + Draw_Line(x1_pixel, y1_pixel, x2_pixel, y1_pixel, color); + Draw_Line(x1_pixel, y2_pixel, x2_pixel, y2_pixel, color); + Draw_Line(x1_pixel, y1_pixel, x1_pixel, y2_pixel, color); + Draw_Line(x2_pixel, y1_pixel, x2_pixel, y2_pixel, color); + Unlock(); +} + diff --git a/code/redalert/win32lib/dsetup.h b/code/redalert/win32lib/dsetup.h new file mode 100644 index 0000000..54ea585 --- /dev/null +++ b/code/redalert/win32lib/dsetup.h @@ -0,0 +1,71 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsetup.h + * Content: DirectXSetup, error codes and flags + ***************************************************************************/ + +#ifndef __DSETUP_H__ +#define __DSETUP_H__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#else +#define GUID void +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define DSETUPERR_BADWINDOWSVERSION -1 +#define DSETUPERR_SOURCEFILENOTFOUND -2 +#define DSETUPERR_BADSOURCESIZE -3 +#define DSETUPERR_BADSOURCETIME -4 +#define DSETUPERR_NOCOPY -5 +#define DSETUPERR_OUTOFDISKSPACE -6 +#define DSETUPERR_CANTFINDINF -7 +#define DSETUPERR_CANTFINDDIR -8 +#define DSETUPERR_INTERNAL -9 + + +#define MAX_INFLINE (16*1024) +#define MAX_DESCRIPTION 256 + +#define DSETUP_DDRAW 0x00000001 /* install DirectDraw */ +#define DSETUP_DSOUND 0x00000002 /* install DirectSound */ +#define DSETUP_DPLAY 0x00000004 /* install DirectPlay */ +#define DSETUP_DDRAWDRV 0x00000008 /* install DirectDraw Drivers */ +#define DSETUP_DSOUNDDRV 0x00000010 /* install DirectSound Drivers */ +#define DSETUP_DPLAYSP 0x00000020 /* install DirectPlay Providers */ +#define DSETUP_DIRECTX DSETUP_DDRAW | DSETUP_DSOUND | DSETUP_DPLAY | DSETUP_DDRAWDRV | DSETUP_DSOUNDDRV | DSETUP_DPLAYSP +#define DSETUP_REINSTALL 0x00000080 /* install DirectX even if existing components have the same version */ + +int WINAPI DirectXSetup( HWND hwnd, LPSTR root_path, DWORD flags ); +int WINAPI DirectXDeviceDriverSetup( HWND hwnd, LPSTR driver_class, LPSTR inf_path, LPSTR driver_path, DWORD flags ); + +typedef int (WINAPI * LPDIRECTXSETUP)( HWND, LPSTR, DWORD ); +typedef int (WINAPI * LPDIRECTXDEVICEDRIVERSETUP)( HWND, LPSTR, LPSTR, LPSTR, DWORD ); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/code/redalert/win32lib/dsound.h b/code/redalert/win32lib/dsound.h new file mode 100644 index 0000000..5c31e9b --- /dev/null +++ b/code/redalert/win32lib/dsound.h @@ -0,0 +1,380 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*==========================================================================; + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: dsound.h + * Content: DirectSound include file + * + ***************************************************************************/ + +#ifndef __DSOUND_INCLUDED__ +#define __DSOUND_INCLUDED__ + +#ifdef _WIN32 +#define COM_NO_WINDOWS_H +#include +#endif + +#define _FACDS 0x878 +#define MAKE_DSHRESULT( code ) MAKE_HRESULT( 1, _FACDS, code ) + +#ifdef __cplusplus +extern "C" { +#endif + +// Direct Sound Component GUID {47D4D946-62E8-11cf-93BC-444553540000} +DEFINE_GUID(CLSID_DirectSound, +0x47d4d946, 0x62e8, 0x11cf, 0x93, 0xbc, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); + +// DirectSound 279afa83-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSound,0x279AFA83,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); +// DirectSoundBuffer 279afa85-4981-11ce-a521-0020af0be560 +DEFINE_GUID(IID_IDirectSoundBuffer,0x279AFA85,0x4981,0x11CE,0xA5,0x21,0x00,0x20,0xAF,0x0B,0xE5,0x60); + + + +//==========================================================================; +// +// Structures... +// +//==========================================================================; +#ifdef __cplusplus +/* 'struct' not 'class' per the way DECLARE_INTERFACE_ is defined */ +struct IDirectSound; +struct IDirectSoundBuffer; +#endif + +typedef struct IDirectSound *LPDIRECTSOUND; +typedef struct IDirectSoundBuffer *LPDIRECTSOUNDBUFFER; +typedef struct IDirectSoundBuffer **LPLPDIRECTSOUNDBUFFER; + + +typedef struct _DSCAPS +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwMinSecondarySampleRate; + DWORD dwMaxSecondarySampleRate; + DWORD dwPrimaryBuffers; + DWORD dwMaxHwMixingAllBuffers; + DWORD dwMaxHwMixingStaticBuffers; + DWORD dwMaxHwMixingStreamingBuffers; + DWORD dwFreeHwMixingAllBuffers; + DWORD dwFreeHwMixingStaticBuffers; + DWORD dwFreeHwMixingStreamingBuffers; + DWORD dwMaxHw3DAllBuffers; + DWORD dwMaxHw3DStaticBuffers; + DWORD dwMaxHw3DStreamingBuffers; + DWORD dwFreeHw3DAllBuffers; + DWORD dwFreeHw3DStaticBuffers; + DWORD dwFreeHw3DStreamingBuffers; + DWORD dwTotalHwMemBytes; + DWORD dwFreeHwMemBytes; + DWORD dwMaxContigFreeHwMemBytes; + DWORD dwUnlockTransferRateHwBuffers; + DWORD dwPlayCpuOverheadSwBuffers; + DWORD dwReserved1; + DWORD dwReserved2; +} DSCAPS, *LPDSCAPS; + +typedef struct _DSBCAPS +{ + + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwUnlockTransferRate; + DWORD dwPlayCpuOverhead; +} DSBCAPS, *LPDSBCAPS; + +typedef struct _DSBUFFERDESC +{ + DWORD dwSize; + DWORD dwFlags; + DWORD dwBufferBytes; + DWORD dwReserved; + LPWAVEFORMATEX lpwfxFormat; +} DSBUFFERDESC, *LPDSBUFFERDESC; + + + +typedef LPVOID* LPLPVOID; + + +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKW)(GUID FAR *, LPWSTR, LPWSTR, LPVOID); +typedef BOOL (FAR PASCAL * LPDSENUMCALLBACKA)(GUID FAR *, LPSTR, LPSTR, LPVOID); + +extern HRESULT WINAPI DirectSoundCreate(GUID FAR * lpGUID, LPDIRECTSOUND * ppDS, IUnknown FAR *pUnkOuter ); +extern HRESULT WINAPI DirectSoundEnumerateW(LPDSENUMCALLBACKW lpCallback, LPVOID lpContext ); +extern HRESULT WINAPI DirectSoundEnumerateA(LPDSENUMCALLBACKA lpCallback, LPVOID lpContext ); + +#ifdef UNICODE +#define LPDSENUMCALLBACK LPDSENUMCALLBACKW +#define DirectSoundEnumerate DirectSoundEnumerateW +#else +#define LPDSENUMCALLBACK LPDSENUMCALLBACKA +#define DirectSoundEnumerate DirectSoundEnumerateA +#endif + +// +// IDirectSound +// +#undef INTERFACE +#define INTERFACE IDirectSound +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSound, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectSound methods ***/ + + STDMETHOD( CreateSoundBuffer)(THIS_ LPDSBUFFERDESC, LPLPDIRECTSOUNDBUFFER, IUnknown FAR *) PURE; + STDMETHOD( GetCaps)(THIS_ LPDSCAPS ) PURE; + STDMETHOD( DuplicateSoundBuffer)(THIS_ LPDIRECTSOUNDBUFFER, LPLPDIRECTSOUNDBUFFER ) PURE; + STDMETHOD( SetCooperativeLevel)(THIS_ HWND, DWORD ) PURE; + STDMETHOD( Compact)(THIS ) PURE; + STDMETHOD( GetSpeakerConfig)(THIS_ LPDWORD ) PURE; + STDMETHOD( SetSpeakerConfig)(THIS_ DWORD ) PURE; + STDMETHOD( Initialize)(THIS_ GUID FAR * ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSound_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSound_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSound_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSound_CreateSoundBuffer(p,a,b,c) (p)->lpVtbl->CreateSoundBuffer(p,a,b,c) +#define IDirectSound_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSound_DuplicateSoundBuffer(p,a,b) (p)->lpVtbl->DuplicateSoundBuffer(p,a,b) +#define IDirectSound_SetCooperativeLevel(p,a,b) (p)->lpVtbl->SetCooperativeLevel(p,a,b) +#define IDirectSound_Compact(p) (p)->lpVtbl->Compact(p) +#define IDirectSound_GetSpeakerConfig(p,a) (p)->lpVtbl->GetSpeakerConfig(p,a) +#define IDirectSound_SetSpeakerConfig(p,b) (p)->lpVtbl->SetSpeakerConfig(p,b) +#define IDirectSound_Initialize(p,a) (p)->lpVtbl->Initialize(p,a) +#endif + +#endif + +// +// IDirectSoundBuffer +// +#undef INTERFACE +#define INTERFACE IDirectSoundBuffer +#ifdef _WIN32 +DECLARE_INTERFACE_( IDirectSoundBuffer, IUnknown ) +{ + /*** IUnknown methods ***/ + STDMETHOD(QueryInterface) (THIS_ REFIID riid, LPVOID * ppvObj) PURE; + STDMETHOD_(ULONG,AddRef) (THIS) PURE; + STDMETHOD_(ULONG,Release) (THIS) PURE; + /*** IDirectSoundBuffer methods ***/ + + STDMETHOD( GetCaps)(THIS_ LPDSBCAPS ) PURE; + STDMETHOD(GetCurrentPosition)(THIS_ LPDWORD,LPDWORD ) PURE; + STDMETHOD( GetFormat)(THIS_ LPWAVEFORMATEX, DWORD, LPDWORD ) PURE; + STDMETHOD( GetVolume)(THIS_ LPLONG ) PURE; + STDMETHOD( GetPan)(THIS_ LPLONG ) PURE; + STDMETHOD( GetFrequency)(THIS_ LPDWORD ) PURE; + STDMETHOD( GetStatus)(THIS_ LPDWORD ) PURE; + STDMETHOD( Initialize)(THIS_ LPDIRECTSOUND, LPDSBUFFERDESC ) PURE; + STDMETHOD( Lock)(THIS_ DWORD,DWORD,LPVOID,LPDWORD,LPVOID,LPDWORD,DWORD ) PURE; + STDMETHOD( Play)(THIS_ DWORD,DWORD,DWORD ) PURE; + STDMETHOD(SetCurrentPosition)(THIS_ DWORD ) PURE; + STDMETHOD( SetFormat)(THIS_ LPWAVEFORMATEX ) PURE; + STDMETHOD( SetVolume)(THIS_ LONG ) PURE; + STDMETHOD( SetPan)(THIS_ LONG ) PURE; + STDMETHOD( SetFrequency)(THIS_ DWORD ) PURE; + STDMETHOD( Stop)(THIS ) PURE; + STDMETHOD( Unlock)(THIS_ LPVOID,DWORD,LPVOID,DWORD ) PURE; + STDMETHOD( Restore)(THIS ) PURE; +}; + +#if !defined(__cplusplus) || defined(CINTERFACE) +#define IDirectSoundBuffer_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IDirectSoundBuffer_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IDirectSoundBuffer_Release(p) (p)->lpVtbl->Release(p) +#define IDirectSoundBuffer_GetCaps(p,a) (p)->lpVtbl->GetCaps(p,a) +#define IDirectSoundBuffer_GetCurrentPosition(p,a,b) (p)->lpVtbl->GetCurrentPosition(p,a,b) +#define IDirectSoundBuffer_GetFormat(p,a,b,c) (p)->lpVtbl->GetFormat(p,a,b,c) +#define IDirectSoundBuffer_GetVolume(p,a) (p)->lpVtbl->GetVolume(p,a) +#define IDirectSoundBuffer_GetPan(p,a) (p)->lpVtbl->GetPan(p,a) +#define IDirectSoundBuffer_GetFrequency(p,a) (p)->lpVtbl->GetFrequency(p,a) +#define IDirectSoundBuffer_GetStatus(p,a) (p)->lpVtbl->GetStatus(p,a) +#define IDirectSoundBuffer_Initialize(p,a,b) (p)->lpVtbl->Initialize(p,a,b) +#define IDirectSoundBuffer_Lock(p,a,b,c,d,e,f,g) (p)->lpVtbl->Lock(p,a,b,c,d,e,f,g) +#define IDirectSoundBuffer_Play(p,a,b,c) (p)->lpVtbl->Play(p,a,b,c) +#define IDirectSoundBuffer_SetCurrentPosition(p,a) (p)->lpVtbl->SetCurrentPosition(p,a) +#define IDirectSoundBuffer_SetFormat(p,a) (p)->lpVtbl->SetFormat(p,a) +#define IDirectSoundBuffer_SetVolume(p,a) (p)->lpVtbl->SetVolume(p,a) +#define IDirectSoundBuffer_SetPan(p,a) (p)->lpVtbl->SetPan(p,a) +#define IDirectSoundBuffer_SetFrequency(p,a) (p)->lpVtbl->SetFrequency(p,a) +#define IDirectSoundBuffer_Stop(p) (p)->lpVtbl->Stop(p) +#define IDirectSoundBuffer_Unlock(p,a,b,c,d) (p)->lpVtbl->Unlock(p,a,b,c,d) +#define IDirectSoundBuffer_Restore(p) (p)->lpVtbl->Restore(p) +#endif + +#endif + + + +/* + * Return Codes + */ + +#define DS_OK 0 + +/* + * The call failed because resources (such as a priority level) + * were already being used by another caller. + */ +#define DSERR_ALLOCATED MAKE_DSHRESULT( 10 ) +/* + * The control (vol,pan,etc.) requested by the caller is not available. + */ +#define DSERR_CONTROLUNAVAIL MAKE_DSHRESULT( 30 ) +/* + * An invalid parameter was passed to the returning function + */ +#define DSERR_INVALIDPARAM E_INVALIDARG +/* + * This call is not valid for the current state of this object + */ +#define DSERR_INVALIDCALL MAKE_DSHRESULT( 50 ) +/* + * An undetermined error occured inside the DSound subsystem + */ +#define DSERR_GENERIC E_FAIL +/* + * The caller does not have the priority level required for the function to + * succeed. + */ +#define DSERR_PRIOLEVELNEEDED MAKE_DSHRESULT( 70 ) +/* + * The DSound subsystem couldn't allocate sufficient memory to complete the + * caller's request. + */ +#define DSERR_OUTOFMEMORY E_OUTOFMEMORY +/* + * The specified WAVE format is not supported + */ +#define DSERR_BADFORMAT MAKE_DSHRESULT( 100 ) +/* + * The function called is not supported at this time + */ +#define DSERR_UNSUPPORTED E_NOTIMPL +/* + * No sound driver is available for use + */ +#define DSERR_NODRIVER MAKE_DSHRESULT( 120 ) +/* + * This object is already initialized + */ +#define DSERR_ALREADYINITIALIZED MAKE_DSHRESULT( 130 ) +/* + * This object does not support aggregation + */ +#define DSERR_NOAGGREGATION CLASS_E_NOAGGREGATION +/* + * The buffer memory has been lost, and must be Restored. + */ +#define DSERR_BUFFERLOST MAKE_DSHRESULT( 150 ) +/* + * Another app has a higher priority level, preventing this call from + * succeeding. + */ +#define DSERR_OTHERAPPHASPRIO MAKE_DSHRESULT( 160 ) +/* + * The Initialize() member on the Direct Sound Object has not been + * called or called successfully before calls to other members. + */ +#define DSERR_UNINITIALIZED MAKE_DSHRESULT( 170 ) + + + + +//==========================================================================; +// +// Flags... +// +//==========================================================================; + +#define DSCAPS_PRIMARYMONO 0x00000001 +#define DSCAPS_PRIMARYSTEREO 0x00000002 +#define DSCAPS_PRIMARY8BIT 0x00000004 +#define DSCAPS_PRIMARY16BIT 0x00000008 +#define DSCAPS_CONTINUOUSRATE 0x00000010 +#define DSCAPS_EMULDRIVER 0x00000020 +#define DSCAPS_CERTIFIED 0x00000040 +#define DSCAPS_SECONDARYMONO 0x00000100 +#define DSCAPS_SECONDARYSTEREO 0x00000200 +#define DSCAPS_SECONDARY8BIT 0x00000400 +#define DSCAPS_SECONDARY16BIT 0x00000800 + + + +#define DSBPLAY_LOOPING 0x00000001 + + + +#define DSBSTATUS_PLAYING 0x00000001 +#define DSBSTATUS_BUFFERLOST 0x00000002 +#define DSBSTATUS_LOOPING 0x00000004 + + +#define DSBLOCK_FROMWRITECURSOR 0x00000001 + + + +#define DSSCL_NORMAL 1 +#define DSSCL_PRIORITY 2 +#define DSSCL_EXCLUSIVE 3 +#define DSSCL_WRITEPRIMARY 4 + + + +#define DSBCAPS_PRIMARYBUFFER 0x00000001 +#define DSBCAPS_STATIC 0x00000002 +#define DSBCAPS_LOCHARDWARE 0x00000004 +#define DSBCAPS_LOCSOFTWARE 0x00000008 +#define DSBCAPS_CTRLFREQUENCY 0x00000020 +#define DSBCAPS_CTRLPAN 0x00000040 +#define DSBCAPS_CTRLVOLUME 0x00000080 +#define DSBCAPS_CTRLDEFAULT 0x000000E0 // Pan + volume + frequency. +#define DSBCAPS_CTRLALL 0x000000E0 // All control capabilities +#define DSBCAPS_STICKYFOCUS 0x00004000 + + + + +#define DSSPEAKER_HEADPHONE 1 +#define DSSPEAKER_MONO 2 +#define DSSPEAKER_QUAD 3 +#define DSSPEAKER_STEREO 4 +#define DSSPEAKER_SURROUND 5 + + + + + + +#ifdef __cplusplus +}; +#endif + +#endif /* __DSOUND_INCLUDED__ */ diff --git a/code/redalert/win32lib/externs.h b/code/redalert/win32lib/externs.h new file mode 100644 index 0000000..4414091 --- /dev/null +++ b/code/redalert/win32lib/externs.h @@ -0,0 +1,37 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Example * + * * + * File Name : EXTERNS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + +extern char NoTimer; +extern char NoKeyBoard; diff --git a/code/redalert/win32lib/fastfile.h b/code/redalert/win32lib/fastfile.h new file mode 100644 index 0000000..2ba372b --- /dev/null +++ b/code/redalert/win32lib/fastfile.h @@ -0,0 +1,39 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*========================================================================== + * + * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. + * + * File: fastfile.h + * Content: Definitions for fastfile access. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, + * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED + * WARRANTIES OF MERCHANTBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. + * + ***************************************************************************/ + +typedef LPVOID HFASTFILE; + +extern BOOL FastFileInit( LPSTR fname, int max_handles ); +extern void FastFileFini( void ); +extern HFASTFILE FastFileOpen( LPSTR name ); +extern BOOL FastFileClose( HFASTFILE pfe ); +extern BOOL FastFileRead( HFASTFILE pfh, LPVOID ptr, int size ); +extern BOOL FastFileSeek( HFASTFILE pfe, int off, int how ); +extern long FastFileTell( HFASTFILE pfe ); +extern LPVOID FastFileLock( HFASTFILE pfe, int off, int len ); +extern BOOL FastFileUnlock( HFASTFILE pfe, int off, int len ); diff --git a/code/redalert/win32lib/file.h b/code/redalert/win32lib/file.h new file mode 100644 index 0000000..e9009fd --- /dev/null +++ b/code/redalert/win32lib/file.h @@ -0,0 +1,257 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library - Filio header stuff. * + * * + * File Name : FILE.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : September 13, 1993 * + * * + * Last Update : April 11, 1994 * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILE_H +#define FILE_H + +#ifndef FILETEMP_H +// This should be removed once the library is all intacked. +#include "filetemp.h" +#endif + +/*=========================================================================*/ +/* File IO system defines and enumerations */ +/*=========================================================================*/ + +#define XMAXPATH 80 + +/* +** These are the Open_File, Read_File, and Seek_File constants. +*/ +#ifndef READ +#define READ 1 // Read access. +#endif +#ifndef WRITE +#define WRITE 2 // Write access. +#endif +#ifndef SEEK_SET +#define SEEK_SET 0 // Seek from start of file. +#define SEEK_CUR 1 // Seek relative from current location. +#define SEEK_END 2 // Seek from end of file. +#endif + + +typedef enum { + FILEB_PROCESSED=8,// Was the packed file header of this file processed? + FILEB_PRELOAD, // Scan for and make file resident at WWDOS_Init time? + FILEB_RESIDENT, // Make resident at Open_File time? + FILEB_FLUSH, // Un-resident at Close_File time? + FILEB_PACKED, // Is this file packed? + FILEB_KEEP, // Don't ever flush this resident file? + FILEB_PRIORITY, // Flush this file last? + + FILEB_LAST +} FileFlags_Type; + +#define FILEF_NONE 0 +#define FILEF_PROCESSED (1< + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cdecl Find_First(unsigned char *fname, unsigned int mode, struct find_t *ffblk); +extern int __cdecl Find_Next(struct find_t *ffblk); + +#ifdef __cplusplus +} +#endif + + + + +#endif diff --git a/code/redalert/win32lib/filepcx.h b/code/redalert/win32lib/filepcx.h new file mode 100644 index 0000000..0fa4695 --- /dev/null +++ b/code/redalert/win32lib/filepcx.h @@ -0,0 +1,74 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : iff * +;* * +;* File Name : FILEPCX.H * +;* * +;* Programmer : Julio R. Jerez * +;* * +;* Start Date : May 2, 1995 * +;* * +;* Last Update : May 2, 1995 [JRJ] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette,void *buff, long size); +;* GraphicBufferClass* Read_PCX_File (char* name, BYTE* palette, BufferClass& Buff); +;* int Write_PCX_File (char* name, GraphicViewPortClass& pic, BYTE* palette );* +;*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ +#include +#include +#include +#include + +#ifndef PCX_H +#define PCX_H + + +typedef struct { + char red ; + char green ; + char blue ; + } RGB ; + +typedef struct { + char id ; + char version ; + char encoding ; + char pixelsize ; + short x ; + short y ; + short width ; + short height ; + short xres ; + short yres ; + RGB ega_palette [ 16 ] ; + char nothing ; + char color_planes ; + short byte_per_line ; + short palette_type ; + char filler [ 58 ] ; + } PCX_HEADER ; + +GraphicBufferClass* Read_PCX_File (char* name, char* palette= NULL,void *buff=NULL, long size=0); +GraphicBufferClass* Read_PCX_File (char* name, BufferClass& Buff,char* palette= NULL) ; +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ); + +#endif diff --git a/code/redalert/win32lib/filetemp.h b/code/redalert/win32lib/filetemp.h new file mode 100644 index 0000000..9a36b1b --- /dev/null +++ b/code/redalert/win32lib/filetemp.h @@ -0,0 +1,57 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Temp header for file routines. * + * * + * File Name : FILETEMP.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FILETEMP_H +#define FILETEMP_H + +///////////////////////////////////////////////////////////////////// +// THIS DOES NOT BELONG HERE. IT WAS PUT HERE JUST TO GET THE THING +// TO COMPILE. ONCE THE BUFFER AND PAGE SYSTEMS ARE PUT IN, THESE +// WILL NEED TO BE TAKEN OUT AND MODS MADE TO ANY FUNCTION USING BuffType. +// SKB 4/20/94. + + + +/*=========================================================================*/ +/* Defines and such that must go into wwstd.h */ +/*=========================================================================*/ +// Look at FileErrorType below for the IO_Error function. +//extern WORD __cdecl ( __cdecl IO_Error)(FileErrorType error, BYTE const *filename); +void __cdecl Prog_End(const char *why, bool fatal); // Added why and fatal. ST - 8/7/2019 10:54AM +extern WORD Hard_Error_Occured; + + + +//////////////////////// END OF DON'T BELONG ////////////////////////////////// + +#endif //FILETEMP_H diff --git a/code/redalert/win32lib/font.h b/code/redalert/win32lib/font.h new file mode 100644 index 0000000..1160f5f --- /dev/null +++ b/code/redalert/win32lib/font.h @@ -0,0 +1,115 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Font and text print 32 bit library * + * * + * File Name : FONT.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : June 27, 1994 * + * * + * Last Update : June 29, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::Text_Print -- Text print into a virtual viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef FONT_H +#define FONT_H + +#ifndef GBUFFER_H +#include +#endif + + +//////////////////////////////////////// Defines ////////////////////////////////////////// + +// defines for font header, offsets to block offsets + +#define FONTINFOBLOCK 4 +#define FONTOFFSETBLOCK 6 +#define FONTWIDTHBLOCK 8 +#define FONTDATABLOCK 10 +#define FONTHEIGHTBLOCK 12 + +// defines for font info block + +#define FONTINFOMAXHEIGHT 4 +#define FONTINFOMAXWIDTH 5 + +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +/*=========================================================================*/ +/* The following prototypes are for the file: SET_FONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Set_Font(void const *fontptr); + +/*=========================================================================*/ +/* The following prototypes are for the file: FONT.CPP */ +/*=========================================================================*/ + +int __cdecl Char_Pixel_Width(char chr); +unsigned int __cdecl String_Pixel_Width(char const *string); +void __cdecl Get_Next_Text_Print_XY(GraphicViewPortClass& vp, unsigned long offset, INT *x, INT *y); + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADFONT.CPP */ +/*=========================================================================*/ + +void * __cdecl Load_Font(char const *name); + +/*=========================================================================*/ +/* The following prototypes are for the file: TEXTPRNT.ASM */ +/*=========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +void __cdecl Set_Font_Palette_Range(void const *palette, INT start_idx, INT end_idx); + + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + + + + + +//////////////////////////////////////// External varables /////////////////////////////////////// +extern "C" int FontXSpacing; +extern "C" int FontYSpacing; +extern char FontWidth ; +extern char FontHeight; +extern char *FontWidthBlockPtr; + + +extern "C" void const *FontPtr; + + + + +#endif // FONT_H diff --git a/code/redalert/win32lib/function.h b/code/redalert/win32lib/function.h new file mode 100644 index 0000000..0f0eea9 --- /dev/null +++ b/code/redalert/win32lib/function.h @@ -0,0 +1,44 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*=========================================================================*/ +/* The following prototypes are for the file: SOUNDIO.CPP */ +/*=========================================================================*/ + +short Decompress_Frame(void * source, void * dest, short size); +int __cdecl Stream_Sample_Vol(void *buffer, long size, BOOL (*callback); +int __cdecl Stream_Sample(void *buffer, long size, BOOL (*callback); +int __cdecl File_Stream_Sample(char const *filename); +int __cdecl File_Stream_Sample_Vol(char const *filename, int volume); +void __cdecl _saveregs _loadds Sound_Callback(void); +void __cdecl far _saveregs _loadds maintenance_callback(void); +void __cdecl Load_Sample(char const *filename); +long __cdecl Load_Sample_Into_Buffer(char const *filename, void *buffer, long size); +long __cdecl Sample_Read(int fh, void *buffer, long size); +void __cdecl Free_Sample(void const *sample); +BOOL __cdecl Sound_Init(int sfx, int score, int sample); +void far VQA_TimerCallback(void); +BOOL Audio_Init(int sample, int address, int inter, int dma); +void __cdecl Sound_End(void); +void __cdecl Stop_Sample(int handle); +BOOL __cdecl Sample_Status(int handle); +BOOL __cdecl Is_Sample_Playing(void const * sample); +void __cdecl Stop_Sample_Playing(void const * sample); +int __cdecl Play_Sample(void const *sample); +int __cdecl Play_Sample_Vol(void const *sample, int priority, int volume); +int __cdecl Set_Sound_Vol(int volume); +int __cdecl Set_Score_Vol(int volume); +void __cdecl Fade_Sample(int handle, int ticks); \ No newline at end of file diff --git a/code/redalert/win32lib/gbuffer.cpp b/code/redalert/win32lib/gbuffer.cpp new file mode 100644 index 0000000..19d7cf8 --- /dev/null +++ b/code/redalert/win32lib/gbuffer.cpp @@ -0,0 +1,555 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : GBUFFER.CPP * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 3, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * VVPC::VirtualViewPort -- Default constructor for a virtual viewport * + * VVPC:~VirtualViewPortClass -- Destructor for a virtual viewport * + * VVPC::Clear -- Clears a graphic page to correct color * + * VBC::VideoBufferClass -- Lowlevel constructor for video buffer class * + * GVPC::Change -- Changes position and size of a Graphic View Port * + * VVPC::Change -- Changes position and size of a Video View Port * + * Set_Logic_Page -- Sets LogicPage to new buffer * + * GBC::DD_Init -- Inits a direct draw surface for a GBC * + * GBC::Init -- Core function responsible for initing a GBC * + * GBC::Lock -- Locks a Direct Draw Surface * + * GBC::Unlock -- Unlocks a direct draw surface * + * GBC::GraphicBufferClass -- Default constructor (requires explicit init)* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef GBUFFER_H +#include "gbuffer.h" +#include "misc.h" +#endif +//#pragma inline + +int TotalLocks; +BOOL AllowHardwareBlitFills = TRUE; + + +//int CacheAllowed; + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Constructor for basic view port class * + * m * + * INPUT: GraphicBufferClass * gbuffer - buffer to attach to * + * int x - x offset into buffer * + * int y - y offset into buffer * + * int w - view port width in pixels * + * int h - view port height in pixels * + * * + * OUTPUT: Constructors may not have a return value * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(GraphicBufferClass *gbuffer, int x, int y, int w, int h) : + LockCount(0), + GraphicBuff(NULL) +{ + Attach(gbuffer, x, y, w, h); +} + +/*************************************************************************** + * GVPC::GRAPHICVIEWPORTCLASS -- Default constructor for view port class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/09/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::GraphicViewPortClass(void) +{ +} + +/*************************************************************************** + * GVPC::~GRAPHICVIEWPORTCLASS -- Destructor for GraphicViewPortClass * + * * + * INPUT: none * + * * + * OUTPUT: A destructor may not return a value. * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass::~GraphicViewPortClass(void) +{ + Offset = 0; + Width = 0; // Record width of Buffer + Height = 0; // Record height of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + Pitch = 0; // Record width of Buffer + IsDirectDraw = FALSE; + LockCount = 0; + GraphicBuff = NULL; +} + +/*************************************************************************** + * GVPC::ATTACH -- Attaches a viewport to a buffer class * + * * + * INPUT: GraphicBufferClass *g_buff - pointer to gbuff to attach to * + * int x - x position to attach to * + * int y - y position to attach to * + * int w - width of the view port * + * int h - height of the view port * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/10/1994 PWG : Created. * + *=========================================================================*/ +void GraphicViewPortClass::Attach(GraphicBufferClass *gbuffer, int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not attach a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return; + } + + /*======================================================================*/ + /* Verify that the x and y coordinates are valid and placed within the */ + /* physical buffer. */ + /*======================================================================*/ + if (x < 0) // you cannot place view port off + x = 0; // the left edge of physical buf + if (x >= gbuffer->Get_Width()) // you cannot place left edge off + x = gbuffer->Get_Width() - 1; // the right edge of physical buf + if (y < 0) // you cannot place view port off + y = 0; // the top edge of physical buf + if (y >= gbuffer->Get_Height()) // you cannot place view port off + y = gbuffer->Get_Height() - 1; // bottom edge of physical buf + + /*======================================================================*/ + /* Adjust the width and height of necessary */ + /*======================================================================*/ + if (x + w > gbuffer->Get_Width()) // if the x plus width is larger + w = gbuffer->Get_Width() - x; // than physical, fix width + + if (y + h > gbuffer->Get_Height()) // if the y plus height is larger + h = gbuffer->Get_Height() - y; // than physical, fix height + + /*======================================================================*/ + /* Get a pointer to the top left edge of the buffer. */ + /*======================================================================*/ + Offset = gbuffer->Get_Offset() + ((gbuffer->Get_Width()+gbuffer->Get_Pitch()) * y * 4) + (x * 4); + + /*======================================================================*/ + /* Copy over all of the variables that we need to store. */ + /*======================================================================*/ + XPos = x; + YPos = y; + XAdd = gbuffer->Get_Width() - w; + Width = w; + Height = h; + Pitch = gbuffer->Get_Pitch(); + GraphicBuff = gbuffer; + IsDirectDraw= gbuffer->IsDirectDraw; +} + + +/*************************************************************************** + * GVPC::CHANGE -- Changes position and size of a Graphic View Port * + * * + * INPUT: int the new x pixel position of the graphic view port * + * int the new y pixel position of the graphic view port * + * int the new width of the viewport in pixels * + * int the new height of the viewport in pixels * + * * + * OUTPUT: BOOL whether the Graphic View Port could be sucessfully * + * resized. * + * * + * WARNINGS: You may not resize a Graphic View Port which is derived * + * from a Graphic View Port Buffer, * + * * + * HISTORY: * + * 09/14/1994 SKB : Created. * + *=========================================================================*/ +BOOL GraphicViewPortClass::Change(int x, int y, int w, int h) +{ + /*======================================================================*/ + /* Can not change a Graphic View Port if it is actually the physical */ + /* representation of a Graphic Buffer. */ + /*======================================================================*/ + if (this == Get_Graphic_Buffer()) { + return(FALSE); + } + + /*======================================================================*/ + /* Since there is no allocated information, just re-attach it to the */ + /* existing graphic buffer as if we were creating the */ + /* GraphicViewPort. */ + /*======================================================================*/ + Attach(Get_Graphic_Buffer(), x, y, w, h); + return(TRUE); +} + + +/*************************************************************************** + * GBC::DD_INIT -- Inits a direct draw surface for a GBC * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::DD_Init(GBC_Enum flags) +{ + rawCpuBuffer = new unsigned char[Width * Height * 4]; + + Allocated = FALSE; // even if system alloced, dont flag it cuz + // we dont want it freed. + IsDirectDraw = TRUE; // flag it as a video surface + Offset = NOT_LOCKED; // flag it as unavailable for reading or writing + LockCount = 0; // surface is not locked +} + +/*************************************************************************** + * GBC::INIT -- Core function responsible for initing a GBC * + * * + * INPUT: int - the width in pixels of the GraphicBufferClass * + * int - the heigh in pixels of the GraphicBufferClass * + * void * - pointer to user supplied buffer (system will * + * allocate space if buffer is NULL) * + * long - size of the user provided buffer * + * GBC_Enum - flags if this is defined as a direct draw * + * surface * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +void GraphicBufferClass::Init(int w, int h, void *buffer, long size, GBC_Enum flags) +{ + Size = size; // find size of physical buffer + Width = w; // Record width of Buffer + Height = h; // Record height of Buffer + rawCpuBuffer = nullptr; + // + // If the surface we are creating is a direct draw object then + // we need to do a direct draw init. Otherwise we will do + // a normal alloc. + // + if (flags & (GBC_VIDEOMEM | GBC_VISIBLE)) { + DD_Init(flags); + } else { + if (buffer) { // if buffer is specified + Buffer= (BYTE *)buffer; // point to it and mark + Allocated = FALSE; // it as user allocated + } else { + if (!Size) Size = w*h*4; + Buffer = new BYTE[Size]; // otherwise allocate it and + Allocated = TRUE; // mark it system alloced + } + Offset = (long)Buffer; // Get offset to the buffer + IsDirectDraw = FALSE; + } + + Pitch = 0; // Record width of Buffer + XAdd = 0; // Record XAdd of Buffer + XPos = 0; // Record XPos of Buffer + YPos = 0; // Record YPos of Buffer + GraphicBuff = this; // Get a pointer to our self +} + + +/*********************************************************************************************** + * GBC::Un_Init -- releases the video surface belonging to this gbuffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 6/6/96 12:44PM ST : Created * + *=============================================================================================*/ + +void GraphicBufferClass::Un_Init (void) +{ + if ( IsDirectDraw ){ + delete rawCpuBuffer; + rawCpuBuffer = nullptr; + } +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Default constructor (requires explicit init) * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(void) +{ + GraphicBuff = this; // Get a pointer to our self + rawCpuBuffer = NULL; +} + + +/*************************************************************************** + * GBC::GRAPHICBUFFERCLASS -- Constructor for fixed size buffers * + * * + * INPUT: long size - size of the buffer to create * + * int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/13/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer, long size) +{ + Init(w, h, buffer, size, GBC_NONE); +} +/*=========================================================================* + * GBC::GRAPHICBUFFERCLASS -- inline constructor for GraphicBufferClass * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - a pointer to the buffer if any (optional) * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, void *buffer) +{ + Init(w, h, buffer, w * h, GBC_NONE); +} + +/*====================================================================================* + * GBC::GRAPHICBUFFERCLASS -- contructor for GraphicsBufferClass with special flags * + * * + * INPUT: int w - width of buffer in pixels (default = 320) * + * int h - height of buffer in pixels (default = 200) * + * void *buffer - unused * + * unsigned flags - flags for creation of special buffer types * + * GBC_VISIBLE - buffer is a visible screen surface * + * GBC_VIDEOMEM - buffer resides in video memory * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 09-21-95 04:19pm ST : Created * + *====================================================================================*/ +GraphicBufferClass::GraphicBufferClass(int w, int h, GBC_Enum flags) +{ + Init(w, h, NULL, w * h, flags); +} + +/*=========================================================================* + * GBC::~GRAPHICBUFFERCLASS -- Destructor for the graphic buffer class * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 05/03/1994 PWG : Created. * + *=========================================================================*/ +GraphicBufferClass::~GraphicBufferClass() +{ + +// +// Release the direct draw surface if it exists +// + Un_Init(); +} + + + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass * the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = ptr; + return(old); +} + +/*************************************************************************** + * SET_LOGIC_PAGE -- Sets LogicPage to new buffer * + * * + * INPUT: GraphicBufferClass & the buffer we are going to set * + * * + * OUTPUT: GraphicBufferClass * the previous buffer type * + * * + * WARNINGS: * + * * + * HISTORY: * + * 02/23/1995 PWG : Created. * + *=========================================================================*/ +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr) +{ + GraphicViewPortClass *old = LogicPage; + LogicPage = &ptr; + return(old); +} + + +/*************************************************************************** + * GBC::LOCK -- Locks a Direct Draw Surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ +extern void Colour_Debug (int call_number); +extern bool GameInFocus; + +extern void Block_Mouse(GraphicBufferClass *buffer); +extern void Unblock_Mouse(GraphicBufferClass *buffer); + +BOOL GraphicBufferClass::Lock(void) +{ + HRESULT result; + int restore_attempts=0; + + // + // If its not a direct draw surface then the lock is always sucessful. + // + if (!IsDirectDraw) return(TRUE); + + /* + ** If the video surface pointer is null then return + */ + if (!rawCpuBuffer) return (FALSE); + + /* + ** If we dont have focus then return failure + */ + if (!GameInFocus) return (FALSE); + + + Block_Mouse(this); + + // + // If surface is already locked then inc the lock count and return true + // + if (LockCount){ + LockCount++; + Unblock_Mouse(this); + return(TRUE); + } + + Offset = (unsigned long)backbuffer_data_raw; // 32bit to 64bit issue here. + //Pitch = Width; + + LockCount++; // increment count so we can track if + TotalLocks++; // Total number of times we have locked (for debugging) + + //Colour_Debug(1); + Unblock_Mouse(this); + return (TRUE); //Return false because we couldnt lock or restore the surface +} + +/*************************************************************************** + * GBC::UNLOCK -- Unlocks a direct draw surface * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 10/09/1995 : Created. * + * 10/09/1995 : Code stolen from Steve Tall * + *=========================================================================*/ + + +BOOL GraphicBufferClass::Unlock(void) +{ + //Video_Set_Data((unsigned char *)Get_Graphic_Buffer()->GetMemoryBuffer(), Width, Height); + + // + // If there is no lock count or this is not a direct draw surface + // then just return true as there is no harm done. + // + if (!(LockCount && IsDirectDraw)) { + return(TRUE); + } + + // + // If lock count is directly equal to one then we actually need to + // unlock so just give it a shot. + // + if (LockCount == 1) { + Block_Mouse(this); + Offset = NOT_LOCKED; + LockCount--; + Unblock_Mouse(this); + return(TRUE); + } + //Colour_Debug (0); + LockCount--; + return(TRUE); +} diff --git a/code/redalert/win32lib/gbuffer.h b/code/redalert/win32lib/gbuffer.h new file mode 100644 index 0000000..ec7fb5d --- /dev/null +++ b/code/redalert/win32lib/gbuffer.h @@ -0,0 +1,1358 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : GBUFFER.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : May 26, 1994 * + * * + * Last Update : October 9, 1995 [] * + * * + *************************************************************************** + * * + * This module contains the definition for the graphic buffer class. The * + * primary functionality of the graphic buffer class is handled by inline * + * functions that make a call through function pointers to the correct * + * routine. This has two benefits: * + * * + * * + * 1) C++ name mangling is not a big deal since the function pointers * + * point to functions in standard C format. * + * 2) The function pointers can be changed when we set a different * + * graphic mode. This allows us to have both supervga and mcga * + * routines present in memory at once. * + * * + * In the basic library, these functions point to stub routines which just * + * return. This makes a product that just uses a graphic buffer take the * + * minimum amount of code space. For programs that require MCGA or VESA * + * support, all that is necessary to do is link either the MCGA or VESA * + * specific libraries in, previous to WWLIB32. The linker will then * + * overide the the necessary stub functions automatically. * + * * + * In addition, there are helpful inline function calls for parameter * + * ellimination. This header file gives the defintion for all * + * GraphicViewPort and GraphicBuffer classes. * + * * + * Terminology: * + * * + * Buffer Class - A class which consists of a pointer to an allocated * + * buffer and the size of the buffer that was allocated. * + * * + * Graphic ViewPort - The Graphic ViewPort defines a window into a * + * Graphic Buffer. This means that although a Graphic Buffer * + * represents linear memory, this may not be true with a Graphic * + * Viewport. All low level functions that act directly on a graphic * + * viewport are included within this class. This includes but is not * + * limited to most of the functions which can act on a Video Viewport * + * Video Buffer. * + * * + * Graphic Buffer - A Graphic Buffer is an instance of an allocated buffer * + * used to represent a rectangular region of graphics memory. * + * The HidBuff and BackBuff are excellent examples of a Graphic Buffer. * + * * + * Below is a tree which shows the relationship of the VideoBuffer and * + * Buffer classes to the GraphicBuffer class: * + * * + * BUFFER.H GBUFFER.H BUFFER.H VBUFFER.H * + * ---------- ---------- ---------- ---------- * + * | Buffer | | Graphic | | Buffer | | Video | * + * | Class | | ViewPort | | Class | | ViewPort | * + * ---------- ---------- ---------- ---------- * + * \ / \ / * + * \ / \ / * + * ---------- ---------- * + * | Graphic | | Video | * + * | Buffer | | Buffer | * + * ---------- ---------- * + * GBUFFER.H VBUFFER.H * + *-------------------------------------------------------------------------* + * Functions: * + * GBC::GraphicBufferClass -- inline constructor for GraphicBufferClass * + * GVPC::Remap -- Short form to remap an entire graphic view port * + * GVPC::Get_XPos -- Returns x offset for a graphic viewport class * + * GVPC::Get_Ypos -- Return y offset in a GraphicViewPortClass * + * VVPC::Get_XPos -- Get the x pos of the VP on the Video * + * VVPC::Get_YPos -- Get the y pos of the VP on the video * + * GBC::Get_Graphic_Buffer -- Get the graphic buffer of the VP. * + * GVPC::Draw_Line -- Stub function to draw line in Graphic Viewport Class* + * GVPC::Fill_Rect -- Stub function to fill rectangle in a GVPC * + * GVPC::Remap -- Stub function to remap a GVPC * + * GVPC::Print -- stub func to print a text string * + * GVPC::Print -- Stub function to print an integer * + * GVPC::Print -- Stub function to print a short to a graphic viewport * + * GVPC::Print -- stub function to print a long on a graphic view port * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 + +#define WIN32_LEAN_AND_MEAN +//#include + + +#ifndef GBUFFER_H +#define GBUFFER_H + +/*=========================================================================*/ +/* If we have not already loaded the standard library header, than we can */ +/* load it. */ +/*=========================================================================*/ + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef DRAWBUFF_H +#include "drawbuff.h" +#endif + +//#ifndef BUFFER_H +#include "buffer.h" +//#endif + +#ifndef WINDOWS_H +#include "ww_win.h" +#endif +#include +#include +#include "iconcach.h" +#include "../NEWBLIT.H" + +#include + +extern unsigned char* backbuffer_data_raw; +extern unsigned char backbuffer_palette[768]; +__forceinline void FastScanlinePaletteBlit(uint8_t* dst, uint8_t* src, int length) { + for (int i = 0; i < length; i++) { + uint8_t color = src[i]; + if (color == 0) + continue; + + dst[(i * 4) + 0] = backbuffer_palette[(color * 3) + 0]; + dst[(i * 4) + 1] = backbuffer_palette[(color * 3) + 1]; + dst[(i * 4) + 2] = backbuffer_palette[(color * 3) + 2]; + dst[(i * 4) + 3] = 255; + } +} + +__forceinline void FastScanlinePaletteBlit(uint8_t* dst, uint8_t color, int length) { + for (int i = 0; i < length; i++) { + dst[(i * 4) + 0] = backbuffer_palette[(color * 3) + 0]; + dst[(i * 4) + 1] = backbuffer_palette[(color * 3) + 1]; + dst[(i * 4) + 2] = backbuffer_palette[(color * 3) + 2]; + dst[(i * 4) + 3] = 255; + } +} + +#ifndef FUNCTION_H +//#pragma off (unreferenced) + +#ifndef BITMAPCLASS +#define BITMAPCLASS +class BitmapClass +{ + public: + BitmapClass(int w, int h, unsigned char * data) : + Width(w), Height(h), Data(data) {}; + + int Width; + int Height; + unsigned char * Data; +}; + +class TPoint2D +{ + public: + TPoint2D(int xx, int yy) : x(xx), y(yy) {}; + TPoint2D(void) : x(0), y(0) {}; + + int x; + int y; +}; +#endif + +//#pragma on (unreferenced) +#endif + + +////////////////////////////////////////////////////////////////////////// +// +// Defines for direct draw +// +// +extern HWND MainWindow; //handle to programs main window + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Gbuffer_Focus_Loss_Function)(void); + +enum GBC_Enum { + GBC_NONE = 0, + GBC_VIDEOMEM = 1, + GBC_VISIBLE = 2, +}; + +#define NOT_LOCKED NULL + +/*=========================================================================*/ +/* Define the screen width and height to make portability to other modules */ +/* easier. */ +/*=========================================================================*/ +#define DEFAULT_SCREEN_WIDTH 356 +#define DEFAULT_SCREEN_HEIGHT 200 + +/*=========================================================================*/ +/* Let the compiler know that a GraphicBufferClass exists so that it can */ +/* keep a pointer to it in a VideoViewPortClass. */ +/*=========================================================================*/ +class GraphicViewPortClass; +class GraphicBufferClass; +class VideoViewPortClass; +class VideoBufferClass; + +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass *ptr); +GraphicViewPortClass *Set_Logic_Page(GraphicViewPortClass &ptr); + +/*=========================================================================*/ +/* GraphicViewPortClass - Holds viewport information on a viewport which */ +/* has been attached to a GraphicBuffer. A viewport is effectively a */ +/* rectangular subset of the full buffer which is used for clipping and */ +/* the like. */ +/* */ +/* char *Buffer - is the offset to view port buffer */ +/* int Width - is the width of view port */ +/* int Height - is the height of view port */ +/* int XAdd - is add value to go from the end of a line */ +/* to the beginning of the next line */ +/* int XPos; - x offset into its associated VideoBuffer */ +/* int YPos; - y offset into its associated VideoBuffer */ +/*=========================================================================*/ +class GraphicViewPortClass { + public: + + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + GraphicViewPortClass(GraphicBufferClass* graphic_buff, int x, int y, int w, int h); + GraphicViewPortClass(); + ~GraphicViewPortClass(); + + /*===================================================================*/ + /* define functions to get at the private data members */ + /*===================================================================*/ + long Get_Offset(void); + int Get_Height(void); + int Get_Width(void); + int Get_XAdd(void); + int Get_XPos(void); + int Get_YPos(void); + int Get_Pitch(void); + inline BOOL Get_IsDirectDraw(void); + GraphicBufferClass *Get_Graphic_Buffer(void); + + /*===================================================================*/ + /* Define a function which allows us to change a video viewport on */ + /* the fly. */ + /*===================================================================*/ + BOOL Change(int x, int y, int w, int h); + + /*===================================================================*/ + /* Define the set of common graphic functions that are supported by */ + /* both Graphic ViewPorts and VideoViewPorts. */ + /*===================================================================*/ + long Size_Of_Region(int w, int h); + void Put_Pixel(int x, int y, unsigned char color); + int Get_Pixel(int x, int y); + void Clear(unsigned char color = 0); + long To_Buffer(int x, int y, int w, int h, void *buff, long size); + long To_Buffer(int x, int y, int w, int h, BufferClass *buff); + long To_Buffer(BufferClass *buff); + HRESULT Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT BlitLegacy(GraphicViewPortClass& dest, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, int dx, int dy, BOOL trans = FALSE); + HRESULT Blit( VideoViewPortClass& dest, BOOL trans = FALSE); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( GraphicViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( GraphicViewPortClass &dest, char *remap); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap); + BOOL Scale( VideoViewPortClass &dest, BOOL trans = FALSE, char *remap = NULL); + BOOL Scale( VideoViewPortClass &dest, char *remap); + unsigned long Print(char const *string, int x_pixel, int y_pixel, int fcolor, int bcolor); + unsigned long Print(short num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(int num, int x_pixel, int y_pixel, int fcol, int bcol); + unsigned long Print(long num, int x_pixel, int y_pixel, int fcol, int bcol); + + int Get_Full_Pitch() { return Get_Pitch() + Get_XAdd() + Get_Width(); } + + /*===================================================================*/ + /* Define the list of graphic functions which work only with a */ + /* graphic buffer. */ + /*===================================================================*/ + VOID Draw_Line(int sx, int sy, int dx, int dy, unsigned char color); + VOID Draw_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color); + VOID Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color); + VOID Remap(int sx, int sy, int width, int height, VOID *remap); + VOID Remap(VOID *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap); + void Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap, int clip_window); + +// This doesnt seem to exist anywhere?? - Steve T 9/26/95 6:05PM +// VOID Grey_Out_Region(int x, int y, int width, int height, int color); + + // + // New members to lock and unlock the direct draw video memory + // + inline BOOL Lock (); + inline BOOL Unlock(); + inline int Get_LockCount(); + + /*===================================================================*/ + /* Define functions to attach the viewport to a graphicbuffer */ + /*===================================================================*/ + VOID Attach(GraphicBufferClass *graphic_buff, int x, int y, int w, int h); + void Attach(GraphicBufferClass *video_buff, int w, int h); + + protected: + + /*===================================================================*/ + /* Define the data used by a GraphicViewPortClass */ + /*===================================================================*/ + long Offset; // offset to graphic page + int Width; // width of graphic page + int Height; // height of graphic page + int XAdd; // xadd for graphic page (0) + int XPos; // x offset in relation to graphicbuff + int YPos; // y offset in relation to graphicbuff + long Pitch; //Distance from one line to the next + GraphicBufferClass *GraphicBuff; // related graphic buff + BOOL IsDirectDraw; //Flag to let us know if it is a direct draw surface + int LockCount; // Count for stacking locks if non-zero the buffer +}; // is a locked DD surface + +/*=========================================================================*/ +/* GraphicBufferClass - A GraphicBuffer refers to an actual instance of an */ +/* allocated buffer. The GraphicBuffer may be drawn to directly */ +/* becuase it inherits a ViewPort which represents its physcial size. */ +/* */ +/* BYTE *Buffer - is the offset to graphic buffer */ +/* int Width - is the width of graphic buffer */ +/* int Height - is the height of graphic buffer */ +/* int XAdd - is the xadd of graphic buffer */ +/* int XPos; - will be 0 because it is graphicbuff */ +/* int YPos; - will be 0 because it is graphicbuff */ +/* long Pitch - modulo of buffer for reading and writing */ +/* BOOL IsDirectDraw - flag if its a direct draw surface */ +/*=========================================================================*/ +class GraphicBufferClass : public GraphicViewPortClass, public BufferClass { + + public: + GraphicBufferClass(int w, int h, GBC_Enum flags); + GraphicBufferClass(int w, int h, void *buffer, long size); + GraphicBufferClass(int w, int h, void *buffer = 0); + GraphicBufferClass(void); + ~GraphicBufferClass(); + + void DD_Init(GBC_Enum flags); + void Init(int w, int h, void *buffer, long size, GBC_Enum flags); + void Un_Init(void); + //void Attach_DD_Surface (GraphicBufferClass * attach_buffer); + BOOL Lock(void); + BOOL Unlock(void); + + void Scale_Rotate(BitmapClass &bmp,TPoint2D const &pt,long scale,unsigned char angle); + + // Member to get a pointer to a direct draw surface + unsigned char* Get_DD_Surface(void) { + if (!rawCpuBuffer) + return (unsigned char* )Buffer; + + return backbuffer_data_raw; + } +// jmarshall + void* GetMemoryBuffer(void) { + if (!rawCpuBuffer) + return Buffer; + return backbuffer_data_raw; + } +// jmarshall end + +protected: + unsigned char* rawCpuBuffer; +}; + + + +inline int GraphicViewPortClass::Get_LockCount(void) +{ + return (LockCount); +} + + + +/*********************************************************************************************** + * GVPC::Get_IsDirectDraw -- provide read access to the IsDirectDraw flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsDirectDraw * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/29/95 1:02PM ST : Created * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Get_IsDirectDraw(void) +{ + return (IsDirectDraw); +} + + + +/*********************************************************************************************** + * GVPC::Lock -- lock the graphics buffer for reading or writing * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully locked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 12:33pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Lock(void) +{ + BOOL lock = GraphicBuff->Lock(); + if ( !lock ) return(FALSE); + + if (this != GraphicBuff) { + Attach(GraphicBuff, XPos, YPos, Width, Height); + } + return(TRUE); +} + +/*********************************************************************************************** + * GVPC::Unlock -- unlock the video buffer * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: TRUE if surface was successfully unlocked * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 09-19-95 02:20pm ST : Created * + * 10/09/1995 : Moved actually functionality to GraphicBuffer * + *=============================================================================================*/ +inline BOOL GraphicViewPortClass::Unlock(void) +{ + BOOL unlock = GraphicBuff->Unlock(); + if (!unlock) return(FALSE); + if (this != GraphicBuff && IsDirectDraw && !GraphicBuff->LockCount) { + Offset = 0; + } + return(TRUE); +} + + +/*************************************************************************** + * GVPC::GET_OFFSET -- Get offset for virtual view port class instance * + * * + * INPUT: none * + * * + * OUTPUT: long the offset for the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Get_Offset(void) +{ + if(Offset == 0) { + return (long)Get_Graphic_Buffer()->GetMemoryBuffer(); + } + + return(Offset); +} + +/*************************************************************************** + * GVPC::GET_HEIGHT -- Gets the height of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the height of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Height(void) +{ + return(Height); +} + +/*************************************************************************** + * GVPC::GET_WIDTH -- Get the width of a virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the width of the virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Width(void) +{ + return(Width); +} + + +/*************************************************************************** + * GVPC::GET_XADD -- Get the X add offset for virtual viewport instance * + * * + * INPUT: none * + * * + * OUTPUT: WORD the xadd for a virtual viewport instance * + * * + * HISTORY: * + * 06/07/1994 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XAdd(void) +{ + return(XAdd); +} +/*************************************************************************** + * GVPC::GET_XPOS -- Get the x pos of the VP on the Video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_XPos(void) +{ + return(XPos); +} + + +/*************************************************************************** + * GVPC::GET_YPOS -- Get the y pos of the VP on the video * + * * + * INPUT: none * + * * + * OUTPUT: WORD the x offset to VideoBufferClass * + * * + * WARNINGS: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_YPos(void) +{ + return(YPos); +} + +/*************************************************************************** + * GVPC::GET_GRAPHIC_BUFFER -- Get the graphic buffer of the VP. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * HISTORY: * + * 08/22/1994 SKB : Created. * + *=========================================================================*/ +inline GraphicBufferClass *GraphicViewPortClass::Get_Graphic_Buffer(void) +{ + return (GraphicBuff); +} + +/*************************************************************************** + * GVPC::SIZE_OF_REGION -- stub to call curr graphic mode Size_Of_Region * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 03/01/1995 BWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::Size_Of_Region(int w, int h) +{ + return Buffer_Size_Of_Region(this, w, h); +} + + +/*************************************************************************** + * GVPC::PUT_PIXEL -- stub to call curr graphic mode Put_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Put_Pixel(int x, int y, unsigned char color) +{ + + if (Lock()){ + Buffer_Put_Pixel(this, x, y, color); + } + Unlock(); + + +} + +/*************************************************************************** + * GVPC::GET_PIXEL -- stub to call curr graphic mode Get_Pixel * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline int GraphicViewPortClass::Get_Pixel(int x, int y) +{ + int return_code=0; + + if (Lock()){ + return_code=(Buffer_Get_Pixel(this, x, y)); + } + Unlock(); + return(return_code); + +} + +/*************************************************************************** + * GVPC::CLEAR -- stub to call curr graphic mode Clear * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline void GraphicViewPortClass::Clear(unsigned char color) +{ + if (Lock()){ + Buffer_Clear(this, color); + } + Unlock(); + +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 1 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, void *buff, long size) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff, size)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 2 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(int x, int y, int w, int h, BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, x, y, w, h, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::TO_BUFFER -- stub 3 to call curr graphic mode To_Buffer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline long GraphicViewPortClass::To_Buffer(BufferClass *buff) +{ + long return_code=0; + if (Lock()){ + return_code = (Buffer_To_Buffer(this, 0, 0, Width, Height, buff->Get_Buffer(), buff->Get_Size())); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- stub 1 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int x_pixel, int y_pixel, int dx_pixel, + int dy_pixel, int pixel_width, int pixel_height, BOOL trans) +{ + HRESULT return_code=0; + + if (Lock()) { + if (dest.Lock()) { + return_code = (Linear_Blit_To_Linear(this, &dest, x_pixel, y_pixel + , dx_pixel, dy_pixel + , pixel_width, pixel_height, trans)); + } + dest.Unlock(); + } + Unlock(); + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::BLIT -- Stub 2 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, int dx, int dy, BOOL trans) +{ + HRESULT return_code=0; + + + if (Lock()) { + if (dest.Lock()) { + return_code = (Linear_Blit_To_Linear(this, &dest, 0, 0 + , dx, dy + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + + return (return_code); + +} + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::BlitLegacy(GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code = 0; + + + if (Lock()) { + if (dest.Lock()) { + + return_code = (Linear_Blit_To_Linear_Pal(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + + } + dest.Unlock(); + } + Unlock(); + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::BLIT -- stub 3 to call curr graphic mode Blit to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline HRESULT GraphicViewPortClass::Blit( GraphicViewPortClass& dest, BOOL trans) +{ + HRESULT return_code = 0; + + + if (Lock()) { + if (dest.Lock()) { + return_code = (Linear_Blit_To_Linear(this, &dest, 0, 0 + , 0, 0 + , Width, Height, trans)); + } + dest.Unlock(); + } + Unlock(); + + return (return_code); + +} + + +/*************************************************************************** + * GVPC::SCALE -- stub 1 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 2 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, int src_x, int src_y, int dst_x, + int dst_y, int src_w, int src_h, int dst_w, int dst_h, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, src_x, src_y, dst_x, dst_y, src_w, src_h, dst_w, dst_h, FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 3 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, BOOL trans, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), trans, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::SCALE -- stub 4 to call curr graphic mode Scale to GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/06/1995 PWG : Created. * + *=========================================================================*/ +inline BOOL GraphicViewPortClass::Scale( GraphicViewPortClass &dest, char *remap) +{ + BOOL return_code=0; + if (Lock()){ + if (dest.Lock()){ + return_code = (Linear_Scale_To_Linear(this, &dest, 0, 0, 0, 0, Width, Height, dest.Get_Width(), dest.Get_Height(), FALSE, remap)); + } + dest.Unlock(); + } + Unlock(); + return ( return_code ); +} +/*************************************************************************** + * GVPC::PRINT -- stub func to print a text string * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/17/1995 PWG : Created. * + *=========================================================================*/ + +void Device_Present(void); +inline unsigned long GraphicViewPortClass::Print(char const *str, int x, int y, int fcol, int bcol) +{ + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, str, x, y, fcol, bcol)); + } + Unlock(); + + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print an integer * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(int num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- Stub function to print a short to a graphic viewport * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(short num, int x, int y, int fcol, int bcol) +{ + char str[17]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, itoa(num, str, 10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::PRINT -- stub function to print a long on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline unsigned long GraphicViewPortClass::Print(long num, int x, int y, int fcol, int bcol) +{ + char str[33]; + + unsigned long return_code=0; + if (Lock()){ + return_code = (Buffer_Print(this, ltoa(num, str,10), x, y, fcol, bcol)); + } + Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + *=========================================================================*/ +inline void GraphicViewPortClass::Draw_Stamp(void const *icondata, int icon, int x_pixel, int y_pixel, void const *remap) +{ + if (Lock()){ + Buffer_Draw_Stamp(this, icondata, icon, x_pixel, y_pixel, remap); + } + Unlock(); +} + + + +/*************************************************************************** + * GVPC::DRAW_STAMP -- stub function to draw a tile on a graphic view port * + * This version clips the tile to a window * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/31/1995 BWG : Created. * + *=========================================================================*/ +extern BOOL IconCacheAllowed; +inline void GraphicViewPortClass::Draw_Stamp(void const * icondata, int icon, int x_pixel, int y_pixel, void const * remap, int clip_window) +{ + int cache_index=-1; + + int drewit = 0; + //if (IconCacheAllowed){ + // if (IsDirectDraw){ + // if (!remap){ + // cache_index = Is_Icon_Cached(icondata,icon); + // } + // + // if (cache_index != -1){ + // if (CachedIcons[cache_index].Get_Is_Cached() ){ + // CachedIcons[cache_index].Draw_It (GraphicBuff->Get_DD_Surface() , x_pixel, y_pixel, + // WindowList[clip_window][WINDOWX] + XPos, + // WindowList[clip_window][WINDOWY] +YPos, + // WindowList[clip_window][WINDOWWIDTH], + // WindowList[clip_window][WINDOWHEIGHT]); + // CachedIconsDrawn++; + // drewit = 1; + // } + // } + // } + //} + + + { + if (Lock()){ + UnCachedIconsDrawn++; + extern int ScreenHeight, ScreenWidth; + if(y_pixel < ScreenHeight && x_pixel < ScreenWidth) + Buffer_Draw_Stamp_Clip(this, icondata, icon, x_pixel, y_pixel, remap, WindowList[clip_window][WINDOWX], WindowList[clip_window][WINDOWY], WindowList[clip_window][WINDOWWIDTH], WindowList[clip_window][WINDOWHEIGHT]); + } + } + Unlock(); +} + + +/*************************************************************************** + * GVPC::DRAW_LINE -- Stub function to draw line in Graphic Viewport Class * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Draw_Line(int sx, int sy, int dx, int dy, unsigned char color) +{ + //if (Lock()){ + // Buffer_Draw_Line(this, sx, sy, dx, dy, color); + //} + //Unlock(); + sx += XPos; + sy += YPos; + dx += XPos; + dy += YPos; + GL_DrawLine(color, sx, sy, dx, dy); +} + +/*************************************************************************** + * GVPC::FILL_RECT -- Stub function to fill rectangle in a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Fill_Rect(int sx, int sy, int dx, int dy, unsigned char color) +{ + sx += XPos; + sy += YPos; + dx += XPos; + dy += YPos; + GL_FillRect(color, sx - 1, sy - 1, (dx - sx) + 1, ((dy - sy) + 1)); +} + + +/*************************************************************************** + * GVPC::REMAP -- Stub function to remap a GVPC * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/16/1995 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(int sx, int sy, int width, int height, VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, sx, sy, width, height, remap); + } + Unlock(); +} + + +inline VOID GraphicViewPortClass::Fill_Quad(VOID *span_buff, int x0, int y0, int x1, int y1, + int x2, int y2, int x3, int y3, int color) +{ + if (Lock()){ + Buffer_Fill_Quad(this, span_buff, x0, y0, x1, y1, x2, y2, x3, y3, color); + } + Unlock(); +} + +/*************************************************************************** + * GVPC::REMAP -- Short form to remap an entire graphic view port * + * * + * INPUT: BYTE * to the remap table to use * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline VOID GraphicViewPortClass::Remap(VOID *remap) +{ + if (Lock()){ + Buffer_Remap(this, 0, 0, Width, Height, remap); + } + Unlock(); +} + +inline int GraphicViewPortClass::Get_Pitch(void) +{ + return(Pitch); +} +/*=========================================================================*/ +/* The following BufferClass functions are defined here because they act */ +/* on graphic viewports. */ +/*=========================================================================*/ + + +/*************************************************************************** + * BUFFER_TO_PAGE -- Generic 'c' callable form of Buffer_To_Page * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 01/12/1995 PWG : Created. * + *=========================================================================*/ +inline long Buffer_To_Page(int x, int y, int w, int h, void *Buffer, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable w, h * + * * + * INPUT: GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * WARNINGS: x and y position are the upper left corner of the dest * + * viewport. width and height are assumed to be the * + * viewport's width and height. * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(0, 0, view.Get_Width(), view.Get_Height(), Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} +/*************************************************************************** + * BC::TO_PAGE -- Copys a buffer class to a page with definable x, y, w, h * + * * + * INPUT: int x - x pixel on viewport to copy from * + * int y - y pixel on viewport to copy from * + * int width - the width of copy region * + * int height - the height of copy region * + * GVPC& dest - virtual viewport to copy to * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 07/01/1994 PWG : Created. * + *=========================================================================*/ +inline long BufferClass::To_Page(int x, int y, int w, int h, GraphicViewPortClass &view) +{ + long return_code=0; + if (view.Lock()){ + return_code = (Buffer_To_Page(x, y, w, h, Buffer, &view)); + } + view.Unlock(); + return ( return_code ); +} + +void Show_OldFrameBuffer(bool show); + +#endif diff --git a/code/redalert/win32lib/gbuffer.inc b/code/redalert/win32lib/gbuffer.inc new file mode 100644 index 0000000..09f2e45 --- /dev/null +++ b/code/redalert/win32lib/gbuffer.inc @@ -0,0 +1,48 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 Bit Library * +;* * +;* File Name : GBUFFER.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : May 26, 1994 * +;* * +;* Last Update : May 26, 1994 [PWG] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +;****************************************************************************** +; Much testing was done to determine that only when there are 14 or more bytes +; being copied does it speed the time it takes to do copies in this algorithm. +; For this reason and because 1 and 2 byte copies crash, is the special case +; used. SKB 4/21/94. Tested on 486 66mhz. Copied by PWG 6/7/04. +OPTIMAL_BYTE_COPY equ 14 + +GraphicViewPort struct +GVPOffset DD ? ; offset to virtual viewport +GVPWidth DD ? ; width of virtual viewport +GVPHeight DD ? ; height of virtual viewport +GVPXAdd DD ? ; x mod to get to next line +GVPXPos DD ? ; x pos relative to Graphic Buff +GVPYPos DD ? ; y pos relative to Graphic Buff +GVPPitch dd ? ; modulo of graphic view port +GVPBuffPtr DD ? ; ptr to associated Graphic Buff +GraphicViewPort ends \ No newline at end of file diff --git a/code/redalert/win32lib/getshape.cpp b/code/redalert/win32lib/getshape.cpp new file mode 100644 index 0000000..30299dc --- /dev/null +++ b/code/redalert/win32lib/getshape.cpp @@ -0,0 +1,359 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : GETSHAPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : April 5, 1992 * + * * + * Last Update : May 25, 1994 [BR] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * Get_Shape_Data -- retrieves a shape's special prefix data * + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * Extract_Shape -- Gets pointer to shape in given shape block * + * Get_Shape_Width -- gets shape width in pixels * + * Get_Shape_Height -- gets shape height in pixels * + * Set_Shape_Height -- modifies shape's height * + * Restore_Shape_Height -- restores a shape to its original height * + * Get_Shape_Original_Height -- gets shape's unmodified height * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +#include "wwstd.h" +#include "shape.h" + + +/*************************************************************************** + * Get_Shape_Size -- Fetch the size of the shape in memory. * + * * + * The shape size returned includes both the shape header & its data. * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in memory * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Get_Shape_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + /* + ------------------------- Return if NULL pointer ------------------------- + */ + if (!shape) + return(0); + + /* + -------------------------- Returns shape's size -------------------------- + */ + return (shp->ShapeSize); + +} /* end of Get_Shape_Size */ + + +/*************************************************************************** + * Get_Shape_Uncomp_Size -- gets shape's uncompressed size in bytes * + * * + * INPUT: * + * shape pointer to shape * + * * + * OUTPUT: * + * shape's size in bytes when uncompressed * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Uncomp_Size(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->DataLength); + +} /* end of Get_Shape_Uncomp_Size */ + + +/*************************************************************************** + * Get_Shape_Data -- retrieves a shape's special prefix data * + * * + * MAKESHPS.EXE can store special data values along with a shape. These * + * values are inserted in the shape table >before< the shape's header. * + * So, this routine uses the 'data' parameter as a negative index from * + * the given shape pointer. * + * * + * INPUT: * + * shape pointer to shape * + * data index of WORD data value to get * + * * + * OUTPUT: * + * data value * + * * + * WARNINGS: * + * The shape pointer must be a pointer into a shape table created by * + * MAKESHPS.EXE; it >cannot< be a pointer to shape returned by Make_Shape! * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +WORD cdecl Get_Shape_Data(VOID const *shape, WORD data) +{ + WORD *word_ptr = (WORD *)shape; + WORD retval; + + retval = *(word_ptr - (data+1)); + + return (retval); + +} /* end of Get_Shape_Data */ + + +/*************************************************************************** + * Extract_Shape_Count -- returns # of shapes in the given shape block * + * * + * The # of shapes in a shape block is the first WORD in the block, so * + * this is the value returned. * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * * + * OUTPUT: * + * # shapes in the block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Extract_Shape_Count(VOID const *buffer) +{ + ShapeBlock_Type *block = (ShapeBlock_Type *)buffer; + + return (block->NumShapes); + +} /* end of Extract_Shape_Count */ + + +/*************************************************************************** + * Extract_Shape -- Gets pointer to shape in given shape block * + * * + * INPUT: * + * buffer pointer to shape block, created with MAKESHPS.EXE * + * shape index of shape to get * + * * + * OUTPUT: * + * pointer to shape in the shape block * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +VOID * cdecl Extract_Shape(VOID const *buffer, int shape) +{ + ShapeBlock_Type *block = (ShapeBlock_Type*) buffer; +//PG int numshapes; // Number of shapes + long offset; // Offset of shape data, from start of block + char *bytebuf = (char*) buffer; + + /* + ----------------------- Return if invalid argument ----------------------- + */ + if (!buffer || shape < 0 || shape >= block->NumShapes) + return(NULL); + + offset = block->Offsets[shape]; + + return(bytebuf + 2 + offset); + +} /* end of Extract_Shape */ + + +/*************************************************************************** + * Get_Shape_Width -- gets shape width in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape width in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Width(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Width); + +} /* end of Get_Shape_Width */ + + +/*************************************************************************** + * Get_Shape_Height -- gets shape height in pixels * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape height in pixels * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->Height); + +} /* end of Get_Shape_Height */ + + +/*************************************************************************** + * Set_Shape_Height -- modifies shape's height * + * * + * The new height must be shorter than the original height. This effect * + * chops off the lower portion of the shape, like it's sinking into the * + * ground. * + * * + * INPUT: * + * shape pointer to a shape * + * newheight new shape height * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Set_Shape_Height(VOID const *shape, WORD newheight) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = newheight; + + return(oldheight); + +} /* end of Set_Shape_Height */ + + +/*************************************************************************** + * Restore_Shape_Height -- restores a shape to its original height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * old shape height * + * * + * WARNINGS: * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int cdecl Restore_Shape_Height(VOID *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + WORD oldheight; + + oldheight = shp->Height; + shp->Height = shp->OriginalHeight; + + return(oldheight); + +} /* end of Restore_Shape_Height */ + + +/*************************************************************************** + * Get_Shape_Original_Height -- gets shape's unmodified height * + * * + * INPUT: * + * shape pointer to a shape * + * * + * OUTPUT: * + * shape's unmodified height * + * * + * WARNINGS: * + * none * + * * + * HISTORY: * + * 06/09/1992 JLB : Created. * + * 08/19/1993 SKB : Split drawshp.asm into several modules. * + * 05/25/1994 BR : Converted to 32-bit * + *=========================================================================*/ +int Get_Shape_Original_Height(VOID const *shape) +{ + Shape_Type *shp = (Shape_Type *)shape; + + return (shp->OriginalHeight); + +} /* end of Get_Shape_Original_Height */ + + +/************************* end of getshape.cpp *****************************/ + diff --git a/code/redalert/win32lib/iconcach.h b/code/redalert/win32lib/iconcach.h new file mode 100644 index 0000000..cc625ad --- /dev/null +++ b/code/redalert/win32lib/iconcach.h @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Drawbuff - Westwood win95 library * + * * + * File Name : Iconcach.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : November 8th, 1995 * + * * + * Last Update : November 16th, 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: This file cantains definition of the IconCacheClass and associated non member * + * function prototypes. * + * * + * Functions: * + * IconCacheClass::Get_Is_Cached -- member to allow access to private IsCached flag * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + + + +#ifndef ICONCACH_H +#define ICONCACH_H + +#include + +#define ICON_WIDTH 24 // Icons must be this width to be cached +#define ICON_HEIGHT 24 // Icons must be this height to be cached +#define MAX_CACHED_ICONS 500 // Maximum number of icons that can be cached +#define MAX_ICON_SETS 100 // Maximum number of icon sets that can be registered +#define MAX_LOOKUP_ENTRIES 3000 // Size of icon index table + + +/* +** IconCacheClass for tracking individual icons cached into video memory +** +** Use Register_Icon_Set to identify a set of icons as cachable. Once registered, the icons +** will be cached automatically when drawn. +** Use Invalidate_Cached_Icons at the end of icon drawing to release the video memory used by the +** caching system. +** Restore_Cached_Icons may be used to reload the icons into video memory after a focus loss. +** +*/ + +class IconCacheClass { + + public: + + IconCacheClass (void); // class constructor + ~IconCacheClass (void); // class destructor + + void Restore(void); // restore the surface + BOOL Cache_It (void * icon_ptr); // Cache the icon to video memory + void Uncache_It (void); // Restore the video memory and flag the icon as uncached + void Draw_It (void *dest_surface , int x_pixel, int y_pixel, int window_left , int window_top , int window_width , int window_height); + inline BOOL Get_Is_Cached(void); // Return the IsCached member + + int TimesDrawn; // counter of times cached icon has been drawn + int TimesFailed; // counter of times cached icon has failed to draw + + + private: + + // LPDIRECTDRAWSURFACE CacheSurface; // Ptr to direct draw surface where icon resides + BOOL IsCached; // Flag to say whether an icon is cached + BOOL SurfaceLost; // Flag to indicate that our icons surface has been lost + int DrawFrequency; // Number of times icon has been drawn + void *IconSource; // Ptr to original icon data in system memory + +}; + + + +/* +** Structure to keep track of registered icon sets +** +*/ + +typedef struct tIconSetType{ + IControl_Type *IconSetPtr; // Ptr to icon set data + int IconListOffset; // Offset into icon index table for this icon set +}IconSetType; + + +extern IconCacheClass CachedIcons[MAX_CACHED_ICONS]; + +extern void Invalidate_Cached_Icons (void); +extern void Restore_Cached_Icons (void); +extern void Register_Icon_Set (void *icon_data , BOOL pre_cache); + +// +// Prototypes for assembly language procedures in STMPCACH.ASM +// +extern "C" void Clear_Icon_Pointers (void); +extern "C" void Cache_Copy_Icon (void const *icon_ptr ,void * , int); +extern "C" int Is_Icon_Cached (void const *icon_data , int icon); +extern "C" int Get_Icon_Index (void *icon_ptr); +extern "C" int Get_Free_Index (void); +extern "C" BOOL Cache_New_Icon (int icon_index, void *icon_ptr); +extern "C" int Get_Free_Cache_Slot(void); + + +extern int CachedIconsDrawn; +extern int UnCachedIconsDrawn; + + +/*********************************************************************************************** + * ICC::Get_Is_Cached -- member to allow access to the private IsCached flag * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: IsCached * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 11/13/95 9:42AM ST : Created * + *=============================================================================================*/ +inline BOOL IconCacheClass::Get_Is_Cached (void) +{ + return (IsCached); +} + + + + + +#endif //ICONCACH_H + diff --git a/code/redalert/win32lib/iconset.cpp b/code/redalert/win32lib/iconset.cpp new file mode 100644 index 0000000..5b3301c --- /dev/null +++ b/code/redalert/win32lib/iconset.cpp @@ -0,0 +1,341 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library * + * * + * File Name : ICONSET.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : June 9, 1991 * + * * + * Last Update : September 15, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Icon_Set -- Loads an icons set and initializes it. * + * Free_Icon_Set -- Frees allocations made by Load_Icon_Set(). * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +//#include "function.h" + +//#define _WIN32 +//#define WIN32_LEAN_AND_MEAN + +//#include +#include +#include +//#include +#include +#include +#include "tile.h" +#include + + +// Misc? ST - 1/3/2019 10:40AM +//extern int Misc; +int Misc; + + +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); + + +//#define ICON_PALETTE_BYTES 16 +//#define ICON_MAX 256 + +/*************************************************************************** +** The terrain is rendered by using icons. These are the buffers that hold +** the icon data, remap tables, and remap index arrays. +*/ +//PRIVATE char *IconPalette = NULL; // MCGA only. +//PRIVATE char *IconRemap = NULL; // MCGA only. + +#define FORM_RPAL MAKE_ID('R','P','A','L') +#define FORM_RTBL MAKE_ID('R','T','B','L') +#define FORM_SSET MAKE_ID('S','S','E','T') +#define FORM_SINF MAKE_ID('S','I','N','F') +#define FORM_ICON MAKE_ID('I','C','O','N') +#define FORM_TRNS MAKE_ID('T','R','N','S') +#define FORM_MAP MAKE_ID('M','A','P',' ') + + + +/*************************************************************************** + * LOAD_ICON_SET -- Loads an icons set and initializes it. * + * * + * This routine will load an IFF icon set from disk. It handles all * + * of the necessary allocations. * + * * + * INPUT: filename -- Name of the icon file. * + * * + * buffer -- Pointer to paragraph aligned buffer to hold data. * + * * + * size -- Size of the buffer (in bytes). * + * * + * OUTPUT: none * + * * + * WARNINGS: In EEGA mode the iconset buffer will be free because the * + * icons will have been transferred to card ram. * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + * 07/01/1991 JLB : Determines icon size from file. * + * 07/15/1991 JLB : Load and uncompress onto the same buffer. * + * 09/15/1993 JLB : Added EMS support. * + *=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize) +{ + int fh; // File handle of iconset. + int bytespericon; // The number of bytes per icon. + unsigned long icons=0; // Number of icons loaded. + unsigned long size; // Size of the icon chunk (raw). + + unsigned long transsize; + void *transptr=NULL; + + unsigned long mapsize; // Icon map chunk size. + void *mapptr=NULL; // Icon map pointer. + void *returnptr=NULL; // Iconset pointer returned by routine. + BOOL allocated=FALSE; // Was the iconset block allocated? + IControl_Type *idata=NULL; // Icon data loaded. + long id; // ID of file openned. + struct { + char Width; // Width of icon in bytes. + char Height; // Height of icon in bytes. + char Format; // Graphic mode. + //lint -esym(754,Format) + char Bitplanes; // Number of bitplanes per icon. + } sinf; + + /* + ** Open the icon set for loading. If it is not a legal icon set + ** data file, then abort. + */ + fh = Open_Iff_File(filename); + if (fh != WW_ERROR) { + Read_File(fh, &id, sizeof(long)); + if (id == FORM_ICON) { + + /* + ** Determine the size of the icons and set up the graphic + ** system accordingly. Also get the sizes of the various + ** data blocks that have to be loaded. + */ + Read_Iff_Chunk(fh, FORM_SINF, &sinf, sizeof(sinf)); + bytespericon = ((((int)sinf.Width)<<3)*(((int)sinf.Height)<<3)*(int)sinf.Bitplanes)>>3; + + size = Get_Iff_Chunk_Size(fh,FORM_SSET); + transsize = Get_Iff_Chunk_Size(fh, FORM_TRNS); + mapsize = Get_Iff_Chunk_Size(fh, FORM_MAP); + + /* + ** Allocate the icon buffer if one isn't provided. First try EMS and + ** then try conventional RAM. + */ + allocated = FALSE; + if (!iconsetptr) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + + Misc = buffsize; + iconsetptr = Alloc(buffsize, MEM_NORMAL); + allocated = (iconsetptr != NULL); + } + + if (iconsetptr && (size+transsize+mapsize+sizeof(IControl_Type)) <= (unsigned long)buffsize) { + + idata = (IControl_Type *)iconsetptr; + + memset(idata, 0, sizeof(IControl_Type)); + + /* + ** Initialize the iconset header structure. + */ + idata->Width = (short)(((short)sinf.Width)<<3); + idata->Height = (short)(((short)sinf.Height)<<3); + idata->Allocated = (short)allocated; + idata->Icons = (long)iconsetptr + sizeof(IControl_Type); + idata->Map = idata->Icons + size; + idata->TransFlag = sizeof(IControl_Type) + size + mapsize; + idata->Size = buffsize; + + { + long val; + + val = Read_Iff_Chunk(fh, FORM_SSET, Add_Long_To_Pointer(iconsetptr, sizeof(IControl_Type)), size); + icons = (int)(val/(long)bytespericon); + idata = (IControl_Type *)iconsetptr; + } + + if (mapsize) { + icons = mapsize; + } + idata->Count = (short)icons; + + /* + ** Limit buffer to only the size needed. This is done AFTER loading of the + ** raw icon data because it might have been compressed and thus need any + ** extra space to perform an overlapped decompression. + */ + if ((unsigned long)buffsize > size + transsize + mapsize + sizeof(IControl_Type)) { + buffsize = size + transsize + mapsize + sizeof(IControl_Type); + } + + transptr = Add_Long_To_Pointer(iconsetptr, idata->TransFlag); + Read_Iff_Chunk(fh, FORM_TRNS, transptr, transsize); + idata = (IControl_Type *)iconsetptr; + + mapptr = (void*)idata->Map; + Read_Iff_Chunk(fh, FORM_MAP, mapptr, mapsize); + + /* + ** Let the graphic overlay know of the icon data. This could involve + ** translation and other data manipulations. + */ + //Init_Stamps(iconsetptr); + + returnptr = iconsetptr; + } + } + Close_Iff_File(fh); + } + + return (returnptr); // Return with icon pointer. +} + + +/*************************************************************************** + * FREE_ICON_SET -- Frees allocations made by Load_Icon_Set(). * + * * + * This routine is used to free up any allocations by Load_Icon_Set(). * + * Use this routine when a new icon set is to be loaded. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/21/1991 JLB : Created. * + *=========================================================================*/ +void Free_Icon_Set(void const *iconset) +{ + IControl_Type *icontrol; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + if (icontrol->Allocated) { + Free((void *)iconset); + } + } +} + + +long Get_Icon_Set_Size(void const *iconset) +{ + IControl_Type *icontrol; + long size=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + size = icontrol->Size; + } + return(size); +} + + +int Get_Icon_Set_Width(void const *iconset) +{ + IControl_Type *icontrol; + int width=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + width = icontrol->Width; + } + return(width); +} + + +int Get_Icon_Set_Height(void const *iconset) +{ + IControl_Type *icontrol; + int height=0; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + height = icontrol->Height; + } + return(height); +} + + +void * Get_Icon_Set_Icondata(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Icons)); + return(NULL); +} + +void * Get_Icon_Set_Trans(void const *iconset) +{ + IControl_Type *icontrol; + void *ptr=NULL; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + ptr = Add_Long_To_Pointer((void *)iconset, icontrol->TransFlag); + } + return(ptr); +} + + +int Get_Icon_Set_Count(void const *iconset) +{ + IControl_Type *icontrol; + int count; + + icontrol = (IControl_Type *)iconset; + if (icontrol) { + count = icontrol->Count; + } + return(count); +} + + +void * Get_Icon_Set_Map(void const *iconset) +{ + IControl_Type *icontrol; + icontrol = (IControl_Type *)iconset; + if (icontrol) + return(Add_Long_To_Pointer(iconset, (LONG)icontrol->Map)); + return(NULL); +} diff --git a/code/redalert/win32lib/iff.cpp b/code/redalert/win32lib/iff.cpp new file mode 100644 index 0000000..b0a47a1 --- /dev/null +++ b/code/redalert/win32lib/iff.cpp @@ -0,0 +1,325 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : IFF.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 16, 1991 * + * * + * Last Update : April 19, 1994 [SKB] * + * * + * * + * IFF reader code designed for loading pictures (ILBM or PBM). * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Close_Iff_File -- Closes an IFF file handle. * + * Get_Iff_Chunk_Size -- Get the size of the given IFF chunk. * + * Open_Iff_File -- Opens an IFF file for reading. * + * Read_Iff_Chunk -- Reads a chunk from an IFF file. * + * Write_Iff_Chunk -- Writes an IFF chuck out. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" + +#define ID_FORM MAKE_ID('F','O','R','M') + +#ifdef MIN +#undef MIN +#endif + +/*************************************************************************** + * OPEN_IFF_FILE -- Opens an IFF file for reading. * + * * + * This function will open an IFF file for reading. It will perform * + * a the simple validity test of checking the first four bytes to make * + * sure they are "FORM". The value returned is the filehandle of the * + * opened file. * + * * + * INPUT: filename - ASCII name of the IFF file to be opened. * + * * + * OUTPUT: Returns the filehandle. If there is an error or the file * + * is not an IFF FORM then -1 will be returned. * + * * + * WARNINGS: You are responsible for error handling if this function * + * returns -1 (not an IFF file). * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +int __cdecl Open_Iff_File(char const *filename) +{ + int fh; // File handle. + long type; // IFF file type. + + + /* We want to be able to open the file for READ | WRITE, but we do not + want the Open_File to create it. So check to see if it exists before + the Open_File */ + +// fh = Open_File(filename, READ); // Open the source file for READ +// Close_File(fh); + + //fh = Open_File(filename, READ | WRITE); // Open the source file again + fh = Open_File(filename, READ); // Open the source file again + + // Validate that it is a FORM type. + + Read_File(fh, &type, 4L); + + if (type == ID_FORM) { + + // The file is valid (so far). Position the read so that the actual + // IFF file type code can be read. + + Seek_File(fh, 4L, SEEK_CUR); // Skip the filesize bytes. + + } else { + + // This is NOT an IFF file. Close the source file and return with + // the error code. + Close_File(fh); + fh = WW_ERROR; + } + return fh; +} + + +/*************************************************************************** + * CLOSE_IFF_FILE -- Closes an IFF file handle. * + * * + * The routine will close the file that was opened with the * + * Open_Iff_File() function. * + * * + * INPUT: fh - File handle that was returned from Open_Iff_File(). * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +void __cdecl Close_Iff_File(int fh) +{ + if (fh != WW_ERROR) Close_File(fh); +} + + +/*************************************************************************** + * GET_IFF_CHUNK_SIZE -- Get the size of the given IFF chunk. * + * * + * INPUT: int file handle to open IFF file, long id to get size of * + * * + * OUTPUT: long size of the chunk or 0L if it was not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/03/1991 CY : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id) +{ + long form; // Chunk iff form name. + long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + Seek_File(fh, -8L, SEEK_CUR); // Seek back to the start of + return(chunksize); // the chunk & return size + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + +/*************************************************************************** + * READ_IFF_CHUNK -- Reads a chunk from an IFF file. * + * * + * Once an IFF file is opened, various chunks must be read from it. * + * This routine will search through the IFF file and load in the * + * specified chunk. It will scan through the entire file when * + * searching for the chunk. It will load the FIRST chunk of the given * + * type. * + * * + * INPUT: fh - File handle of IFF file. * + * * + * id - Chunk ID code. * + * * + * buffer - Pointer to buffer to load the chunk. * + * * + * maxsize - Maximum data bytes to read. * + * * + * OUTPUT: Returns with the number of bytes read from the chunk. * + * If 0 is returned, this indicates that the chunk wasn't * + * found. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/16/1991 JLB : Created. * + * 04/19/1994 SKB : Update to 32 bit library. * + *=========================================================================*/ +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize) +{ + long form; // Chunk iff form name. + unsigned long chunksize; // Size of the chunk. + char first_iteration; // Check once the current chunk name + + first_iteration = TRUE; + + for (;;) { + if (Read_File(fh, &form, 4L) != 4L && !first_iteration) break; + + if (Read_File(fh, (char *) &chunksize, 4L) != 4L && !first_iteration) break; + +#if(IBM) + chunksize = Reverse_Long(chunksize); +#endif + + if (id == form) { + + maxsize = MIN(maxsize, chunksize); + Read_File(fh, buffer, maxsize); // Read the buffer. + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + if (maxsize < chunksize) { + Seek_File(fh, chunksize - maxsize, SEEK_CUR); + } + return(maxsize); + } else { + + if (first_iteration) { + Seek_File(fh, 12L, SEEK_SET); // Start at beginning of file. + first_iteration = FALSE; // Don't do this again + + } else { + + /* Otherwise, go to the next chunk in the file */ + + chunksize = (chunksize + 1) & 0xFFFFFFFEL; + Seek_File(fh, chunksize, SEEK_CUR); + } + } + } + + return(0L); +} + + + +/*************************************************************************** + * WRITE_IFF_CHUNK -- Writes an IFF chuck out. * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/19/1994 SKB : Created. * + *=========================================================================*/ +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length) +{ + long pos; // Current position in the IFF file. + long oldpos; // Record of start of chunk offset. + long endpos; // end of file offset before we write our data + long value; + BOOL odd; // Is length odd? + char pad = 0; // Optional padding byte for even sized chunks. + + /* + ** Get the current end of file (before we write more data to the file) + */ + pos = Seek_File (file, 0L, SEEK_CUR); + endpos = Seek_File (file, 0L, SEEK_END); + Seek_File (file, pos, SEEK_SET); + + if (length) { + value = id; + odd = (short)length & 0x01; + + Write_File(file, &value, 4L); + oldpos = Seek_File(file, 0L, SEEK_CUR); + Write_File(file, &value, 4L); + Write_File(file, buffer, length); + pos = Seek_File(file, 0L, SEEK_CUR); + if (odd) { + Write_File(file, &pad, 1L); + } + + /* + ** Update the chunk size long. + */ + Seek_File(file, oldpos, SEEK_SET); + value = IFFize_LONG((pos - oldpos)-4); + Write_File(file, &value, 4L); + + /* + ** Update the file size LONG. if we are not just overwriting existing data + */ + // (MCC) + if ( endpos < pos ) { + Seek_File(file, 4L, SEEK_SET); + value = IFFize_LONG((pos+odd) - 8); + Write_File(file, &value, 4L); + } + + /* + ** Return to end of file. + */ + Seek_File(file, 0L, SEEK_END); + } +} + + diff --git a/code/redalert/win32lib/iff.h b/code/redalert/win32lib/iff.h new file mode 100644 index 0000000..51f8c57 --- /dev/null +++ b/code/redalert/win32lib/iff.h @@ -0,0 +1,164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the FILEIO Library * + * * + * File Name : IFF.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : April 20, 1994 * + * * + * Last Update : April 20, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef IFF_H +#define IFF_H + +#ifndef GBUFFER_H +#include +#endif + +#ifndef MISC_H +#include // This is needed fro Reverse_WORD and _LONG +#endif + +#ifndef MEMFLAGS_H +#include // This is needed for MemoryFlagType. +#endif + +#define LZW_SUPPORTED FALSE + +/*=========================================================================*/ +/* Iff and Load Picture system defines and enumerations */ +/*=========================================================================*/ + +#define MAKE_ID(a,b,c,d) ((long) ((long) d << 24) | ((long) c << 16) | ((long) b << 8) | (long)(a)) +#define IFFize_WORD(a) Reverse_Word(a) +#define IFFize_LONG(a) Reverse_Long(a) + + +//lint -strong(AJX,PicturePlaneType) +typedef enum { + BM_AMIGA, // Bit plane format (8K per bitplane). + BM_MCGA, // Byte per pixel format (64K). + + BM_DEFAULT=BM_MCGA // Default picture format. +} PicturePlaneType; + +/* +** This is the compression type code. This value is used in the compressed +** file header to indicate the method of compression used. Note that the +** LZW method may not be supported. +*/ +//lint -strong(AJX,CompressionType) +typedef enum { + NOCOMPRESS, // No compression (raw data). + LZW12, // LZW 12 bit codes. + LZW14, // LZW 14 bit codes. + HORIZONTAL, // Run length encoding (RLE). + LCW // Westwood proprietary compression. +} CompressionType; + +/* +** Compressed blocks of data must start with this header structure. +** Note that disk based compressed files have an additional two +** leading bytes that indicate the size of the entire file. +*/ +//lint -strong(AJX,CompHeaderType) +typedef struct { + char Method; // Compression method (CompressionType). + char pad; // Reserved pad byte (always 0). + long Size; // Size of the uncompressed data. + short Skip; // Number of bytes to skip before data. +} CompHeaderType; + + +/*=========================================================================*/ +/* The following prototypes are for the file: IFF.CPP */ +/*=========================================================================*/ + +int __cdecl Open_Iff_File(char const *filename); +void __cdecl Close_Iff_File(int fh); +unsigned long __cdecl Get_Iff_Chunk_Size(int fh, long id); +unsigned long __cdecl Read_Iff_Chunk(int fh, long id, void *buffer, unsigned long maxsize); +void __cdecl Write_Iff_Chunk(int file, long id, void *buffer, long length); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOADPICT.CPP */ +/*=========================================================================*/ + +int __cdecl Load_Picture(char const *filename, BufferClass& scratchbuf, BufferClass& destbuf, unsigned char *palette=NULL, PicturePlaneType format=BM_DEFAULT); + + +/*=========================================================================*/ +/* The following prototypes are for the file: LOAD.CPP */ +/*=========================================================================*/ + +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size); +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size); +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags); +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data=NULL); +unsigned long __cdecl Uncompress_Data(void const *src, void *dst); +void __cdecl Set_Uncomp_Buffer(int buffer_segment, int size_of_buffer); + +/*=========================================================================*/ +/* The following prototypes are for the file: WRITELBM.CPP */ +/*=========================================================================*/ + +PUBLIC BOOL Write_LBM_File(int lbmhandle, BufferClass& buff, int bitplanes, unsigned char *palette); + + + +/*========================= Assembly Functions ============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: PACK2PLN.ASM */ +/*=========================================================================*/ + +extern void __cdecl Pack_2_Plane(void *buffer, void * pageptr, int planebit); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWCOMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Compress(void *source, void *dest, unsigned long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: LCWUNCMP.ASM */ +/*=========================================================================*/ + +extern unsigned long __cdecl LCW_Uncompress(void *source, void *dest, unsigned long length); + +#ifdef __cplusplus +} +#endif +/*=========================================================================*/ + + + +#endif //IFF_H diff --git a/code/redalert/win32lib/indextb.inc b/code/redalert/win32lib/indextb.inc new file mode 100644 index 0000000..36d8e03 --- /dev/null +++ b/code/redalert/win32lib/indextb.inc @@ -0,0 +1,1445 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* wIndexTable - ADPCM Lookup table for indexes +;**************************************************************************** + + align 4 + +wIndexTable DW 0 * 2 ; Index = 0 Token = 0 + DW 0 * 2 ; Index = 0 Token = 1 + DW 0 * 2 ; Index = 0 Token = 2 + DW 0 * 2 ; Index = 0 Token = 3 + DW 32 * 2 ; Index = 0 Token = 4 + DW 64 * 2 ; Index = 0 Token = 5 + DW 96 * 2 ; Index = 0 Token = 6 + DW 128 * 2 ; Index = 0 Token = 7 + DW 0 * 2 ; Index = 0 Token = 8 + DW 0 * 2 ; Index = 0 Token = 9 + DW 0 * 2 ; Index = 0 Token = 10 + DW 0 * 2 ; Index = 0 Token = 11 + DW 32 * 2 ; Index = 0 Token = 12 + DW 64 * 2 ; Index = 0 Token = 13 + DW 96 * 2 ; Index = 0 Token = 14 + DW 128 * 2 ; Index = 0 Token = 15 + DW 0 * 2 ; Index = 1 Token = 0 + DW 0 * 2 ; Index = 1 Token = 1 + DW 0 * 2 ; Index = 1 Token = 2 + DW 0 * 2 ; Index = 1 Token = 3 + DW 48 * 2 ; Index = 1 Token = 4 + DW 80 * 2 ; Index = 1 Token = 5 + DW 112 * 2 ; Index = 1 Token = 6 + DW 144 * 2 ; Index = 1 Token = 7 + DW 0 * 2 ; Index = 1 Token = 8 + DW 0 * 2 ; Index = 1 Token = 9 + DW 0 * 2 ; Index = 1 Token = 10 + DW 0 * 2 ; Index = 1 Token = 11 + DW 48 * 2 ; Index = 1 Token = 12 + DW 80 * 2 ; Index = 1 Token = 13 + DW 112 * 2 ; Index = 1 Token = 14 + DW 144 * 2 ; Index = 1 Token = 15 + DW 16 * 2 ; Index = 2 Token = 0 + DW 16 * 2 ; Index = 2 Token = 1 + DW 16 * 2 ; Index = 2 Token = 2 + DW 16 * 2 ; Index = 2 Token = 3 + DW 64 * 2 ; Index = 2 Token = 4 + DW 96 * 2 ; Index = 2 Token = 5 + DW 128 * 2 ; Index = 2 Token = 6 + DW 160 * 2 ; Index = 2 Token = 7 + DW 16 * 2 ; Index = 2 Token = 8 + DW 16 * 2 ; Index = 2 Token = 9 + DW 16 * 2 ; Index = 2 Token = 10 + DW 16 * 2 ; Index = 2 Token = 11 + DW 64 * 2 ; Index = 2 Token = 12 + DW 96 * 2 ; Index = 2 Token = 13 + DW 128 * 2 ; Index = 2 Token = 14 + DW 160 * 2 ; Index = 2 Token = 15 + DW 32 * 2 ; Index = 3 Token = 0 + DW 32 * 2 ; Index = 3 Token = 1 + DW 32 * 2 ; Index = 3 Token = 2 + DW 32 * 2 ; Index = 3 Token = 3 + DW 80 * 2 ; Index = 3 Token = 4 + DW 112 * 2 ; Index = 3 Token = 5 + DW 144 * 2 ; Index = 3 Token = 6 + DW 176 * 2 ; Index = 3 Token = 7 + DW 32 * 2 ; Index = 3 Token = 8 + DW 32 * 2 ; Index = 3 Token = 9 + DW 32 * 2 ; Index = 3 Token = 10 + DW 32 * 2 ; Index = 3 Token = 11 + DW 80 * 2 ; Index = 3 Token = 12 + DW 112 * 2 ; Index = 3 Token = 13 + DW 144 * 2 ; Index = 3 Token = 14 + DW 176 * 2 ; Index = 3 Token = 15 + DW 48 * 2 ; Index = 4 Token = 0 + DW 48 * 2 ; Index = 4 Token = 1 + DW 48 * 2 ; Index = 4 Token = 2 + DW 48 * 2 ; Index = 4 Token = 3 + DW 96 * 2 ; Index = 4 Token = 4 + DW 128 * 2 ; Index = 4 Token = 5 + DW 160 * 2 ; Index = 4 Token = 6 + DW 192 * 2 ; Index = 4 Token = 7 + DW 48 * 2 ; Index = 4 Token = 8 + DW 48 * 2 ; Index = 4 Token = 9 + DW 48 * 2 ; Index = 4 Token = 10 + DW 48 * 2 ; Index = 4 Token = 11 + DW 96 * 2 ; Index = 4 Token = 12 + DW 128 * 2 ; Index = 4 Token = 13 + DW 160 * 2 ; Index = 4 Token = 14 + DW 192 * 2 ; Index = 4 Token = 15 + DW 64 * 2 ; Index = 5 Token = 0 + DW 64 * 2 ; Index = 5 Token = 1 + DW 64 * 2 ; Index = 5 Token = 2 + DW 64 * 2 ; Index = 5 Token = 3 + DW 112 * 2 ; Index = 5 Token = 4 + DW 144 * 2 ; Index = 5 Token = 5 + DW 176 * 2 ; Index = 5 Token = 6 + DW 208 * 2 ; Index = 5 Token = 7 + DW 64 * 2 ; Index = 5 Token = 8 + DW 64 * 2 ; Index = 5 Token = 9 + DW 64 * 2 ; Index = 5 Token = 10 + DW 64 * 2 ; Index = 5 Token = 11 + DW 112 * 2 ; Index = 5 Token = 12 + DW 144 * 2 ; Index = 5 Token = 13 + DW 176 * 2 ; Index = 5 Token = 14 + DW 208 * 2 ; Index = 5 Token = 15 + DW 80 * 2 ; Index = 6 Token = 0 + DW 80 * 2 ; Index = 6 Token = 1 + DW 80 * 2 ; Index = 6 Token = 2 + DW 80 * 2 ; Index = 6 Token = 3 + DW 128 * 2 ; Index = 6 Token = 4 + DW 160 * 2 ; Index = 6 Token = 5 + DW 192 * 2 ; Index = 6 Token = 6 + DW 224 * 2 ; Index = 6 Token = 7 + DW 80 * 2 ; Index = 6 Token = 8 + DW 80 * 2 ; Index = 6 Token = 9 + DW 80 * 2 ; Index = 6 Token = 10 + DW 80 * 2 ; Index = 6 Token = 11 + DW 128 * 2 ; Index = 6 Token = 12 + DW 160 * 2 ; Index = 6 Token = 13 + DW 192 * 2 ; Index = 6 Token = 14 + DW 224 * 2 ; Index = 6 Token = 15 + DW 96 * 2 ; Index = 7 Token = 0 + DW 96 * 2 ; Index = 7 Token = 1 + DW 96 * 2 ; Index = 7 Token = 2 + DW 96 * 2 ; Index = 7 Token = 3 + DW 144 * 2 ; Index = 7 Token = 4 + DW 176 * 2 ; Index = 7 Token = 5 + DW 208 * 2 ; Index = 7 Token = 6 + DW 240 * 2 ; Index = 7 Token = 7 + DW 96 * 2 ; Index = 7 Token = 8 + DW 96 * 2 ; Index = 7 Token = 9 + DW 96 * 2 ; Index = 7 Token = 10 + DW 96 * 2 ; Index = 7 Token = 11 + DW 144 * 2 ; Index = 7 Token = 12 + DW 176 * 2 ; Index = 7 Token = 13 + DW 208 * 2 ; Index = 7 Token = 14 + DW 240 * 2 ; Index = 7 Token = 15 + DW 112 * 2 ; Index = 8 Token = 0 + DW 112 * 2 ; Index = 8 Token = 1 + DW 112 * 2 ; Index = 8 Token = 2 + DW 112 * 2 ; Index = 8 Token = 3 + DW 160 * 2 ; Index = 8 Token = 4 + DW 192 * 2 ; Index = 8 Token = 5 + DW 224 * 2 ; Index = 8 Token = 6 + DW 256 * 2 ; Index = 8 Token = 7 + DW 112 * 2 ; Index = 8 Token = 8 + DW 112 * 2 ; Index = 8 Token = 9 + DW 112 * 2 ; Index = 8 Token = 10 + DW 112 * 2 ; Index = 8 Token = 11 + DW 160 * 2 ; Index = 8 Token = 12 + DW 192 * 2 ; Index = 8 Token = 13 + DW 224 * 2 ; Index = 8 Token = 14 + DW 256 * 2 ; Index = 8 Token = 15 + DW 128 * 2 ; Index = 9 Token = 0 + DW 128 * 2 ; Index = 9 Token = 1 + DW 128 * 2 ; Index = 9 Token = 2 + DW 128 * 2 ; Index = 9 Token = 3 + DW 176 * 2 ; Index = 9 Token = 4 + DW 208 * 2 ; Index = 9 Token = 5 + DW 240 * 2 ; Index = 9 Token = 6 + DW 272 * 2 ; Index = 9 Token = 7 + DW 128 * 2 ; Index = 9 Token = 8 + DW 128 * 2 ; Index = 9 Token = 9 + DW 128 * 2 ; Index = 9 Token = 10 + DW 128 * 2 ; Index = 9 Token = 11 + DW 176 * 2 ; Index = 9 Token = 12 + DW 208 * 2 ; Index = 9 Token = 13 + DW 240 * 2 ; Index = 9 Token = 14 + DW 272 * 2 ; Index = 9 Token = 15 + DW 144 * 2 ; Index = 10 Token = 0 + DW 144 * 2 ; Index = 10 Token = 1 + DW 144 * 2 ; Index = 10 Token = 2 + DW 144 * 2 ; Index = 10 Token = 3 + DW 192 * 2 ; Index = 10 Token = 4 + DW 224 * 2 ; Index = 10 Token = 5 + DW 256 * 2 ; Index = 10 Token = 6 + DW 288 * 2 ; Index = 10 Token = 7 + DW 144 * 2 ; Index = 10 Token = 8 + DW 144 * 2 ; Index = 10 Token = 9 + DW 144 * 2 ; Index = 10 Token = 10 + DW 144 * 2 ; Index = 10 Token = 11 + DW 192 * 2 ; Index = 10 Token = 12 + DW 224 * 2 ; Index = 10 Token = 13 + DW 256 * 2 ; Index = 10 Token = 14 + DW 288 * 2 ; Index = 10 Token = 15 + DW 160 * 2 ; Index = 11 Token = 0 + DW 160 * 2 ; Index = 11 Token = 1 + DW 160 * 2 ; Index = 11 Token = 2 + DW 160 * 2 ; Index = 11 Token = 3 + DW 208 * 2 ; Index = 11 Token = 4 + DW 240 * 2 ; Index = 11 Token = 5 + DW 272 * 2 ; Index = 11 Token = 6 + DW 304 * 2 ; Index = 11 Token = 7 + DW 160 * 2 ; Index = 11 Token = 8 + DW 160 * 2 ; Index = 11 Token = 9 + DW 160 * 2 ; Index = 11 Token = 10 + DW 160 * 2 ; Index = 11 Token = 11 + DW 208 * 2 ; Index = 11 Token = 12 + DW 240 * 2 ; Index = 11 Token = 13 + DW 272 * 2 ; Index = 11 Token = 14 + DW 304 * 2 ; Index = 11 Token = 15 + DW 176 * 2 ; Index = 12 Token = 0 + DW 176 * 2 ; Index = 12 Token = 1 + DW 176 * 2 ; Index = 12 Token = 2 + DW 176 * 2 ; Index = 12 Token = 3 + DW 224 * 2 ; Index = 12 Token = 4 + DW 256 * 2 ; Index = 12 Token = 5 + DW 288 * 2 ; Index = 12 Token = 6 + DW 320 * 2 ; Index = 12 Token = 7 + DW 176 * 2 ; Index = 12 Token = 8 + DW 176 * 2 ; Index = 12 Token = 9 + DW 176 * 2 ; Index = 12 Token = 10 + DW 176 * 2 ; Index = 12 Token = 11 + DW 224 * 2 ; Index = 12 Token = 12 + DW 256 * 2 ; Index = 12 Token = 13 + DW 288 * 2 ; Index = 12 Token = 14 + DW 320 * 2 ; Index = 12 Token = 15 + DW 192 * 2 ; Index = 13 Token = 0 + DW 192 * 2 ; Index = 13 Token = 1 + DW 192 * 2 ; Index = 13 Token = 2 + DW 192 * 2 ; Index = 13 Token = 3 + DW 240 * 2 ; Index = 13 Token = 4 + DW 272 * 2 ; Index = 13 Token = 5 + DW 304 * 2 ; Index = 13 Token = 6 + DW 336 * 2 ; Index = 13 Token = 7 + DW 192 * 2 ; Index = 13 Token = 8 + DW 192 * 2 ; Index = 13 Token = 9 + DW 192 * 2 ; Index = 13 Token = 10 + DW 192 * 2 ; Index = 13 Token = 11 + DW 240 * 2 ; Index = 13 Token = 12 + DW 272 * 2 ; Index = 13 Token = 13 + DW 304 * 2 ; Index = 13 Token = 14 + DW 336 * 2 ; Index = 13 Token = 15 + DW 208 * 2 ; Index = 14 Token = 0 + DW 208 * 2 ; Index = 14 Token = 1 + DW 208 * 2 ; Index = 14 Token = 2 + DW 208 * 2 ; Index = 14 Token = 3 + DW 256 * 2 ; Index = 14 Token = 4 + DW 288 * 2 ; Index = 14 Token = 5 + DW 320 * 2 ; Index = 14 Token = 6 + DW 352 * 2 ; Index = 14 Token = 7 + DW 208 * 2 ; Index = 14 Token = 8 + DW 208 * 2 ; Index = 14 Token = 9 + DW 208 * 2 ; Index = 14 Token = 10 + DW 208 * 2 ; Index = 14 Token = 11 + DW 256 * 2 ; Index = 14 Token = 12 + DW 288 * 2 ; Index = 14 Token = 13 + DW 320 * 2 ; Index = 14 Token = 14 + DW 352 * 2 ; Index = 14 Token = 15 + DW 224 * 2 ; Index = 15 Token = 0 + DW 224 * 2 ; Index = 15 Token = 1 + DW 224 * 2 ; Index = 15 Token = 2 + DW 224 * 2 ; Index = 15 Token = 3 + DW 272 * 2 ; Index = 15 Token = 4 + DW 304 * 2 ; Index = 15 Token = 5 + DW 336 * 2 ; Index = 15 Token = 6 + DW 368 * 2 ; Index = 15 Token = 7 + DW 224 * 2 ; Index = 15 Token = 8 + DW 224 * 2 ; Index = 15 Token = 9 + DW 224 * 2 ; Index = 15 Token = 10 + DW 224 * 2 ; Index = 15 Token = 11 + DW 272 * 2 ; Index = 15 Token = 12 + DW 304 * 2 ; Index = 15 Token = 13 + DW 336 * 2 ; Index = 15 Token = 14 + DW 368 * 2 ; Index = 15 Token = 15 + DW 240 * 2 ; Index = 16 Token = 0 + DW 240 * 2 ; Index = 16 Token = 1 + DW 240 * 2 ; Index = 16 Token = 2 + DW 240 * 2 ; Index = 16 Token = 3 + DW 288 * 2 ; Index = 16 Token = 4 + DW 320 * 2 ; Index = 16 Token = 5 + DW 352 * 2 ; Index = 16 Token = 6 + DW 384 * 2 ; Index = 16 Token = 7 + DW 240 * 2 ; Index = 16 Token = 8 + DW 240 * 2 ; Index = 16 Token = 9 + DW 240 * 2 ; Index = 16 Token = 10 + DW 240 * 2 ; Index = 16 Token = 11 + DW 288 * 2 ; Index = 16 Token = 12 + DW 320 * 2 ; Index = 16 Token = 13 + DW 352 * 2 ; Index = 16 Token = 14 + DW 384 * 2 ; Index = 16 Token = 15 + DW 256 * 2 ; Index = 17 Token = 0 + DW 256 * 2 ; Index = 17 Token = 1 + DW 256 * 2 ; Index = 17 Token = 2 + DW 256 * 2 ; Index = 17 Token = 3 + DW 304 * 2 ; Index = 17 Token = 4 + DW 336 * 2 ; Index = 17 Token = 5 + DW 368 * 2 ; Index = 17 Token = 6 + DW 400 * 2 ; Index = 17 Token = 7 + DW 256 * 2 ; Index = 17 Token = 8 + DW 256 * 2 ; Index = 17 Token = 9 + DW 256 * 2 ; Index = 17 Token = 10 + DW 256 * 2 ; Index = 17 Token = 11 + DW 304 * 2 ; Index = 17 Token = 12 + DW 336 * 2 ; Index = 17 Token = 13 + DW 368 * 2 ; Index = 17 Token = 14 + DW 400 * 2 ; Index = 17 Token = 15 + DW 272 * 2 ; Index = 18 Token = 0 + DW 272 * 2 ; Index = 18 Token = 1 + DW 272 * 2 ; Index = 18 Token = 2 + DW 272 * 2 ; Index = 18 Token = 3 + DW 320 * 2 ; Index = 18 Token = 4 + DW 352 * 2 ; Index = 18 Token = 5 + DW 384 * 2 ; Index = 18 Token = 6 + DW 416 * 2 ; Index = 18 Token = 7 + DW 272 * 2 ; Index = 18 Token = 8 + DW 272 * 2 ; Index = 18 Token = 9 + DW 272 * 2 ; Index = 18 Token = 10 + DW 272 * 2 ; Index = 18 Token = 11 + DW 320 * 2 ; Index = 18 Token = 12 + DW 352 * 2 ; Index = 18 Token = 13 + DW 384 * 2 ; Index = 18 Token = 14 + DW 416 * 2 ; Index = 18 Token = 15 + DW 288 * 2 ; Index = 19 Token = 0 + DW 288 * 2 ; Index = 19 Token = 1 + DW 288 * 2 ; Index = 19 Token = 2 + DW 288 * 2 ; Index = 19 Token = 3 + DW 336 * 2 ; Index = 19 Token = 4 + DW 368 * 2 ; Index = 19 Token = 5 + DW 400 * 2 ; Index = 19 Token = 6 + DW 432 * 2 ; Index = 19 Token = 7 + DW 288 * 2 ; Index = 19 Token = 8 + DW 288 * 2 ; Index = 19 Token = 9 + DW 288 * 2 ; Index = 19 Token = 10 + DW 288 * 2 ; Index = 19 Token = 11 + DW 336 * 2 ; Index = 19 Token = 12 + DW 368 * 2 ; Index = 19 Token = 13 + DW 400 * 2 ; Index = 19 Token = 14 + DW 432 * 2 ; Index = 19 Token = 15 + DW 304 * 2 ; Index = 20 Token = 0 + DW 304 * 2 ; Index = 20 Token = 1 + DW 304 * 2 ; Index = 20 Token = 2 + DW 304 * 2 ; Index = 20 Token = 3 + DW 352 * 2 ; Index = 20 Token = 4 + DW 384 * 2 ; Index = 20 Token = 5 + DW 416 * 2 ; Index = 20 Token = 6 + DW 448 * 2 ; Index = 20 Token = 7 + DW 304 * 2 ; Index = 20 Token = 8 + DW 304 * 2 ; Index = 20 Token = 9 + DW 304 * 2 ; Index = 20 Token = 10 + DW 304 * 2 ; Index = 20 Token = 11 + DW 352 * 2 ; Index = 20 Token = 12 + DW 384 * 2 ; Index = 20 Token = 13 + DW 416 * 2 ; Index = 20 Token = 14 + DW 448 * 2 ; Index = 20 Token = 15 + DW 320 * 2 ; Index = 21 Token = 0 + DW 320 * 2 ; Index = 21 Token = 1 + DW 320 * 2 ; Index = 21 Token = 2 + DW 320 * 2 ; Index = 21 Token = 3 + DW 368 * 2 ; Index = 21 Token = 4 + DW 400 * 2 ; Index = 21 Token = 5 + DW 432 * 2 ; Index = 21 Token = 6 + DW 464 * 2 ; Index = 21 Token = 7 + DW 320 * 2 ; Index = 21 Token = 8 + DW 320 * 2 ; Index = 21 Token = 9 + DW 320 * 2 ; Index = 21 Token = 10 + DW 320 * 2 ; Index = 21 Token = 11 + DW 368 * 2 ; Index = 21 Token = 12 + DW 400 * 2 ; Index = 21 Token = 13 + DW 432 * 2 ; Index = 21 Token = 14 + DW 464 * 2 ; Index = 21 Token = 15 + DW 336 * 2 ; Index = 22 Token = 0 + DW 336 * 2 ; Index = 22 Token = 1 + DW 336 * 2 ; Index = 22 Token = 2 + DW 336 * 2 ; Index = 22 Token = 3 + DW 384 * 2 ; Index = 22 Token = 4 + DW 416 * 2 ; Index = 22 Token = 5 + DW 448 * 2 ; Index = 22 Token = 6 + DW 480 * 2 ; Index = 22 Token = 7 + DW 336 * 2 ; Index = 22 Token = 8 + DW 336 * 2 ; Index = 22 Token = 9 + DW 336 * 2 ; Index = 22 Token = 10 + DW 336 * 2 ; Index = 22 Token = 11 + DW 384 * 2 ; Index = 22 Token = 12 + DW 416 * 2 ; Index = 22 Token = 13 + DW 448 * 2 ; Index = 22 Token = 14 + DW 480 * 2 ; Index = 22 Token = 15 + DW 352 * 2 ; Index = 23 Token = 0 + DW 352 * 2 ; Index = 23 Token = 1 + DW 352 * 2 ; Index = 23 Token = 2 + DW 352 * 2 ; Index = 23 Token = 3 + DW 400 * 2 ; Index = 23 Token = 4 + DW 432 * 2 ; Index = 23 Token = 5 + DW 464 * 2 ; Index = 23 Token = 6 + DW 496 * 2 ; Index = 23 Token = 7 + DW 352 * 2 ; Index = 23 Token = 8 + DW 352 * 2 ; Index = 23 Token = 9 + DW 352 * 2 ; Index = 23 Token = 10 + DW 352 * 2 ; Index = 23 Token = 11 + DW 400 * 2 ; Index = 23 Token = 12 + DW 432 * 2 ; Index = 23 Token = 13 + DW 464 * 2 ; Index = 23 Token = 14 + DW 496 * 2 ; Index = 23 Token = 15 + DW 368 * 2 ; Index = 24 Token = 0 + DW 368 * 2 ; Index = 24 Token = 1 + DW 368 * 2 ; Index = 24 Token = 2 + DW 368 * 2 ; Index = 24 Token = 3 + DW 416 * 2 ; Index = 24 Token = 4 + DW 448 * 2 ; Index = 24 Token = 5 + DW 480 * 2 ; Index = 24 Token = 6 + DW 512 * 2 ; Index = 24 Token = 7 + DW 368 * 2 ; Index = 24 Token = 8 + DW 368 * 2 ; Index = 24 Token = 9 + DW 368 * 2 ; Index = 24 Token = 10 + DW 368 * 2 ; Index = 24 Token = 11 + DW 416 * 2 ; Index = 24 Token = 12 + DW 448 * 2 ; Index = 24 Token = 13 + DW 480 * 2 ; Index = 24 Token = 14 + DW 512 * 2 ; Index = 24 Token = 15 + DW 384 * 2 ; Index = 25 Token = 0 + DW 384 * 2 ; Index = 25 Token = 1 + DW 384 * 2 ; Index = 25 Token = 2 + DW 384 * 2 ; Index = 25 Token = 3 + DW 432 * 2 ; Index = 25 Token = 4 + DW 464 * 2 ; Index = 25 Token = 5 + DW 496 * 2 ; Index = 25 Token = 6 + DW 528 * 2 ; Index = 25 Token = 7 + DW 384 * 2 ; Index = 25 Token = 8 + DW 384 * 2 ; Index = 25 Token = 9 + DW 384 * 2 ; Index = 25 Token = 10 + DW 384 * 2 ; Index = 25 Token = 11 + DW 432 * 2 ; Index = 25 Token = 12 + DW 464 * 2 ; Index = 25 Token = 13 + DW 496 * 2 ; Index = 25 Token = 14 + DW 528 * 2 ; Index = 25 Token = 15 + DW 400 * 2 ; Index = 26 Token = 0 + DW 400 * 2 ; Index = 26 Token = 1 + DW 400 * 2 ; Index = 26 Token = 2 + DW 400 * 2 ; Index = 26 Token = 3 + DW 448 * 2 ; Index = 26 Token = 4 + DW 480 * 2 ; Index = 26 Token = 5 + DW 512 * 2 ; Index = 26 Token = 6 + DW 544 * 2 ; Index = 26 Token = 7 + DW 400 * 2 ; Index = 26 Token = 8 + DW 400 * 2 ; Index = 26 Token = 9 + DW 400 * 2 ; Index = 26 Token = 10 + DW 400 * 2 ; Index = 26 Token = 11 + DW 448 * 2 ; Index = 26 Token = 12 + DW 480 * 2 ; Index = 26 Token = 13 + DW 512 * 2 ; Index = 26 Token = 14 + DW 544 * 2 ; Index = 26 Token = 15 + DW 416 * 2 ; Index = 27 Token = 0 + DW 416 * 2 ; Index = 27 Token = 1 + DW 416 * 2 ; Index = 27 Token = 2 + DW 416 * 2 ; Index = 27 Token = 3 + DW 464 * 2 ; Index = 27 Token = 4 + DW 496 * 2 ; Index = 27 Token = 5 + DW 528 * 2 ; Index = 27 Token = 6 + DW 560 * 2 ; Index = 27 Token = 7 + DW 416 * 2 ; Index = 27 Token = 8 + DW 416 * 2 ; Index = 27 Token = 9 + DW 416 * 2 ; Index = 27 Token = 10 + DW 416 * 2 ; Index = 27 Token = 11 + DW 464 * 2 ; Index = 27 Token = 12 + DW 496 * 2 ; Index = 27 Token = 13 + DW 528 * 2 ; Index = 27 Token = 14 + DW 560 * 2 ; Index = 27 Token = 15 + DW 432 * 2 ; Index = 28 Token = 0 + DW 432 * 2 ; Index = 28 Token = 1 + DW 432 * 2 ; Index = 28 Token = 2 + DW 432 * 2 ; Index = 28 Token = 3 + DW 480 * 2 ; Index = 28 Token = 4 + DW 512 * 2 ; Index = 28 Token = 5 + DW 544 * 2 ; Index = 28 Token = 6 + DW 576 * 2 ; Index = 28 Token = 7 + DW 432 * 2 ; Index = 28 Token = 8 + DW 432 * 2 ; Index = 28 Token = 9 + DW 432 * 2 ; Index = 28 Token = 10 + DW 432 * 2 ; Index = 28 Token = 11 + DW 480 * 2 ; Index = 28 Token = 12 + DW 512 * 2 ; Index = 28 Token = 13 + DW 544 * 2 ; Index = 28 Token = 14 + DW 576 * 2 ; Index = 28 Token = 15 + DW 448 * 2 ; Index = 29 Token = 0 + DW 448 * 2 ; Index = 29 Token = 1 + DW 448 * 2 ; Index = 29 Token = 2 + DW 448 * 2 ; Index = 29 Token = 3 + DW 496 * 2 ; Index = 29 Token = 4 + DW 528 * 2 ; Index = 29 Token = 5 + DW 560 * 2 ; Index = 29 Token = 6 + DW 592 * 2 ; Index = 29 Token = 7 + DW 448 * 2 ; Index = 29 Token = 8 + DW 448 * 2 ; Index = 29 Token = 9 + DW 448 * 2 ; Index = 29 Token = 10 + DW 448 * 2 ; Index = 29 Token = 11 + DW 496 * 2 ; Index = 29 Token = 12 + DW 528 * 2 ; Index = 29 Token = 13 + DW 560 * 2 ; Index = 29 Token = 14 + DW 592 * 2 ; Index = 29 Token = 15 + DW 464 * 2 ; Index = 30 Token = 0 + DW 464 * 2 ; Index = 30 Token = 1 + DW 464 * 2 ; Index = 30 Token = 2 + DW 464 * 2 ; Index = 30 Token = 3 + DW 512 * 2 ; Index = 30 Token = 4 + DW 544 * 2 ; Index = 30 Token = 5 + DW 576 * 2 ; Index = 30 Token = 6 + DW 608 * 2 ; Index = 30 Token = 7 + DW 464 * 2 ; Index = 30 Token = 8 + DW 464 * 2 ; Index = 30 Token = 9 + DW 464 * 2 ; Index = 30 Token = 10 + DW 464 * 2 ; Index = 30 Token = 11 + DW 512 * 2 ; Index = 30 Token = 12 + DW 544 * 2 ; Index = 30 Token = 13 + DW 576 * 2 ; Index = 30 Token = 14 + DW 608 * 2 ; Index = 30 Token = 15 + DW 480 * 2 ; Index = 31 Token = 0 + DW 480 * 2 ; Index = 31 Token = 1 + DW 480 * 2 ; Index = 31 Token = 2 + DW 480 * 2 ; Index = 31 Token = 3 + DW 528 * 2 ; Index = 31 Token = 4 + DW 560 * 2 ; Index = 31 Token = 5 + DW 592 * 2 ; Index = 31 Token = 6 + DW 624 * 2 ; Index = 31 Token = 7 + DW 480 * 2 ; Index = 31 Token = 8 + DW 480 * 2 ; Index = 31 Token = 9 + DW 480 * 2 ; Index = 31 Token = 10 + DW 480 * 2 ; Index = 31 Token = 11 + DW 528 * 2 ; Index = 31 Token = 12 + DW 560 * 2 ; Index = 31 Token = 13 + DW 592 * 2 ; Index = 31 Token = 14 + DW 624 * 2 ; Index = 31 Token = 15 + DW 496 * 2 ; Index = 32 Token = 0 + DW 496 * 2 ; Index = 32 Token = 1 + DW 496 * 2 ; Index = 32 Token = 2 + DW 496 * 2 ; Index = 32 Token = 3 + DW 544 * 2 ; Index = 32 Token = 4 + DW 576 * 2 ; Index = 32 Token = 5 + DW 608 * 2 ; Index = 32 Token = 6 + DW 640 * 2 ; Index = 32 Token = 7 + DW 496 * 2 ; Index = 32 Token = 8 + DW 496 * 2 ; Index = 32 Token = 9 + DW 496 * 2 ; Index = 32 Token = 10 + DW 496 * 2 ; Index = 32 Token = 11 + DW 544 * 2 ; Index = 32 Token = 12 + DW 576 * 2 ; Index = 32 Token = 13 + DW 608 * 2 ; Index = 32 Token = 14 + DW 640 * 2 ; Index = 32 Token = 15 + DW 512 * 2 ; Index = 33 Token = 0 + DW 512 * 2 ; Index = 33 Token = 1 + DW 512 * 2 ; Index = 33 Token = 2 + DW 512 * 2 ; Index = 33 Token = 3 + DW 560 * 2 ; Index = 33 Token = 4 + DW 592 * 2 ; Index = 33 Token = 5 + DW 624 * 2 ; Index = 33 Token = 6 + DW 656 * 2 ; Index = 33 Token = 7 + DW 512 * 2 ; Index = 33 Token = 8 + DW 512 * 2 ; Index = 33 Token = 9 + DW 512 * 2 ; Index = 33 Token = 10 + DW 512 * 2 ; Index = 33 Token = 11 + DW 560 * 2 ; Index = 33 Token = 12 + DW 592 * 2 ; Index = 33 Token = 13 + DW 624 * 2 ; Index = 33 Token = 14 + DW 656 * 2 ; Index = 33 Token = 15 + DW 528 * 2 ; Index = 34 Token = 0 + DW 528 * 2 ; Index = 34 Token = 1 + DW 528 * 2 ; Index = 34 Token = 2 + DW 528 * 2 ; Index = 34 Token = 3 + DW 576 * 2 ; Index = 34 Token = 4 + DW 608 * 2 ; Index = 34 Token = 5 + DW 640 * 2 ; Index = 34 Token = 6 + DW 672 * 2 ; Index = 34 Token = 7 + DW 528 * 2 ; Index = 34 Token = 8 + DW 528 * 2 ; Index = 34 Token = 9 + DW 528 * 2 ; Index = 34 Token = 10 + DW 528 * 2 ; Index = 34 Token = 11 + DW 576 * 2 ; Index = 34 Token = 12 + DW 608 * 2 ; Index = 34 Token = 13 + DW 640 * 2 ; Index = 34 Token = 14 + DW 672 * 2 ; Index = 34 Token = 15 + DW 544 * 2 ; Index = 35 Token = 0 + DW 544 * 2 ; Index = 35 Token = 1 + DW 544 * 2 ; Index = 35 Token = 2 + DW 544 * 2 ; Index = 35 Token = 3 + DW 592 * 2 ; Index = 35 Token = 4 + DW 624 * 2 ; Index = 35 Token = 5 + DW 656 * 2 ; Index = 35 Token = 6 + DW 688 * 2 ; Index = 35 Token = 7 + DW 544 * 2 ; Index = 35 Token = 8 + DW 544 * 2 ; Index = 35 Token = 9 + DW 544 * 2 ; Index = 35 Token = 10 + DW 544 * 2 ; Index = 35 Token = 11 + DW 592 * 2 ; Index = 35 Token = 12 + DW 624 * 2 ; Index = 35 Token = 13 + DW 656 * 2 ; Index = 35 Token = 14 + DW 688 * 2 ; Index = 35 Token = 15 + DW 560 * 2 ; Index = 36 Token = 0 + DW 560 * 2 ; Index = 36 Token = 1 + DW 560 * 2 ; Index = 36 Token = 2 + DW 560 * 2 ; Index = 36 Token = 3 + DW 608 * 2 ; Index = 36 Token = 4 + DW 640 * 2 ; Index = 36 Token = 5 + DW 672 * 2 ; Index = 36 Token = 6 + DW 704 * 2 ; Index = 36 Token = 7 + DW 560 * 2 ; Index = 36 Token = 8 + DW 560 * 2 ; Index = 36 Token = 9 + DW 560 * 2 ; Index = 36 Token = 10 + DW 560 * 2 ; Index = 36 Token = 11 + DW 608 * 2 ; Index = 36 Token = 12 + DW 640 * 2 ; Index = 36 Token = 13 + DW 672 * 2 ; Index = 36 Token = 14 + DW 704 * 2 ; Index = 36 Token = 15 + DW 576 * 2 ; Index = 37 Token = 0 + DW 576 * 2 ; Index = 37 Token = 1 + DW 576 * 2 ; Index = 37 Token = 2 + DW 576 * 2 ; Index = 37 Token = 3 + DW 624 * 2 ; Index = 37 Token = 4 + DW 656 * 2 ; Index = 37 Token = 5 + DW 688 * 2 ; Index = 37 Token = 6 + DW 720 * 2 ; Index = 37 Token = 7 + DW 576 * 2 ; Index = 37 Token = 8 + DW 576 * 2 ; Index = 37 Token = 9 + DW 576 * 2 ; Index = 37 Token = 10 + DW 576 * 2 ; Index = 37 Token = 11 + DW 624 * 2 ; Index = 37 Token = 12 + DW 656 * 2 ; Index = 37 Token = 13 + DW 688 * 2 ; Index = 37 Token = 14 + DW 720 * 2 ; Index = 37 Token = 15 + DW 592 * 2 ; Index = 38 Token = 0 + DW 592 * 2 ; Index = 38 Token = 1 + DW 592 * 2 ; Index = 38 Token = 2 + DW 592 * 2 ; Index = 38 Token = 3 + DW 640 * 2 ; Index = 38 Token = 4 + DW 672 * 2 ; Index = 38 Token = 5 + DW 704 * 2 ; Index = 38 Token = 6 + DW 736 * 2 ; Index = 38 Token = 7 + DW 592 * 2 ; Index = 38 Token = 8 + DW 592 * 2 ; Index = 38 Token = 9 + DW 592 * 2 ; Index = 38 Token = 10 + DW 592 * 2 ; Index = 38 Token = 11 + DW 640 * 2 ; Index = 38 Token = 12 + DW 672 * 2 ; Index = 38 Token = 13 + DW 704 * 2 ; Index = 38 Token = 14 + DW 736 * 2 ; Index = 38 Token = 15 + DW 608 * 2 ; Index = 39 Token = 0 + DW 608 * 2 ; Index = 39 Token = 1 + DW 608 * 2 ; Index = 39 Token = 2 + DW 608 * 2 ; Index = 39 Token = 3 + DW 656 * 2 ; Index = 39 Token = 4 + DW 688 * 2 ; Index = 39 Token = 5 + DW 720 * 2 ; Index = 39 Token = 6 + DW 752 * 2 ; Index = 39 Token = 7 + DW 608 * 2 ; Index = 39 Token = 8 + DW 608 * 2 ; Index = 39 Token = 9 + DW 608 * 2 ; Index = 39 Token = 10 + DW 608 * 2 ; Index = 39 Token = 11 + DW 656 * 2 ; Index = 39 Token = 12 + DW 688 * 2 ; Index = 39 Token = 13 + DW 720 * 2 ; Index = 39 Token = 14 + DW 752 * 2 ; Index = 39 Token = 15 + DW 624 * 2 ; Index = 40 Token = 0 + DW 624 * 2 ; Index = 40 Token = 1 + DW 624 * 2 ; Index = 40 Token = 2 + DW 624 * 2 ; Index = 40 Token = 3 + DW 672 * 2 ; Index = 40 Token = 4 + DW 704 * 2 ; Index = 40 Token = 5 + DW 736 * 2 ; Index = 40 Token = 6 + DW 768 * 2 ; Index = 40 Token = 7 + DW 624 * 2 ; Index = 40 Token = 8 + DW 624 * 2 ; Index = 40 Token = 9 + DW 624 * 2 ; Index = 40 Token = 10 + DW 624 * 2 ; Index = 40 Token = 11 + DW 672 * 2 ; Index = 40 Token = 12 + DW 704 * 2 ; Index = 40 Token = 13 + DW 736 * 2 ; Index = 40 Token = 14 + DW 768 * 2 ; Index = 40 Token = 15 + DW 640 * 2 ; Index = 41 Token = 0 + DW 640 * 2 ; Index = 41 Token = 1 + DW 640 * 2 ; Index = 41 Token = 2 + DW 640 * 2 ; Index = 41 Token = 3 + DW 688 * 2 ; Index = 41 Token = 4 + DW 720 * 2 ; Index = 41 Token = 5 + DW 752 * 2 ; Index = 41 Token = 6 + DW 784 * 2 ; Index = 41 Token = 7 + DW 640 * 2 ; Index = 41 Token = 8 + DW 640 * 2 ; Index = 41 Token = 9 + DW 640 * 2 ; Index = 41 Token = 10 + DW 640 * 2 ; Index = 41 Token = 11 + DW 688 * 2 ; Index = 41 Token = 12 + DW 720 * 2 ; Index = 41 Token = 13 + DW 752 * 2 ; Index = 41 Token = 14 + DW 784 * 2 ; Index = 41 Token = 15 + DW 656 * 2 ; Index = 42 Token = 0 + DW 656 * 2 ; Index = 42 Token = 1 + DW 656 * 2 ; Index = 42 Token = 2 + DW 656 * 2 ; Index = 42 Token = 3 + DW 704 * 2 ; Index = 42 Token = 4 + DW 736 * 2 ; Index = 42 Token = 5 + DW 768 * 2 ; Index = 42 Token = 6 + DW 800 * 2 ; Index = 42 Token = 7 + DW 656 * 2 ; Index = 42 Token = 8 + DW 656 * 2 ; Index = 42 Token = 9 + DW 656 * 2 ; Index = 42 Token = 10 + DW 656 * 2 ; Index = 42 Token = 11 + DW 704 * 2 ; Index = 42 Token = 12 + DW 736 * 2 ; Index = 42 Token = 13 + DW 768 * 2 ; Index = 42 Token = 14 + DW 800 * 2 ; Index = 42 Token = 15 + DW 672 * 2 ; Index = 43 Token = 0 + DW 672 * 2 ; Index = 43 Token = 1 + DW 672 * 2 ; Index = 43 Token = 2 + DW 672 * 2 ; Index = 43 Token = 3 + DW 720 * 2 ; Index = 43 Token = 4 + DW 752 * 2 ; Index = 43 Token = 5 + DW 784 * 2 ; Index = 43 Token = 6 + DW 816 * 2 ; Index = 43 Token = 7 + DW 672 * 2 ; Index = 43 Token = 8 + DW 672 * 2 ; Index = 43 Token = 9 + DW 672 * 2 ; Index = 43 Token = 10 + DW 672 * 2 ; Index = 43 Token = 11 + DW 720 * 2 ; Index = 43 Token = 12 + DW 752 * 2 ; Index = 43 Token = 13 + DW 784 * 2 ; Index = 43 Token = 14 + DW 816 * 2 ; Index = 43 Token = 15 + DW 688 * 2 ; Index = 44 Token = 0 + DW 688 * 2 ; Index = 44 Token = 1 + DW 688 * 2 ; Index = 44 Token = 2 + DW 688 * 2 ; Index = 44 Token = 3 + DW 736 * 2 ; Index = 44 Token = 4 + DW 768 * 2 ; Index = 44 Token = 5 + DW 800 * 2 ; Index = 44 Token = 6 + DW 832 * 2 ; Index = 44 Token = 7 + DW 688 * 2 ; Index = 44 Token = 8 + DW 688 * 2 ; Index = 44 Token = 9 + DW 688 * 2 ; Index = 44 Token = 10 + DW 688 * 2 ; Index = 44 Token = 11 + DW 736 * 2 ; Index = 44 Token = 12 + DW 768 * 2 ; Index = 44 Token = 13 + DW 800 * 2 ; Index = 44 Token = 14 + DW 832 * 2 ; Index = 44 Token = 15 + DW 704 * 2 ; Index = 45 Token = 0 + DW 704 * 2 ; Index = 45 Token = 1 + DW 704 * 2 ; Index = 45 Token = 2 + DW 704 * 2 ; Index = 45 Token = 3 + DW 752 * 2 ; Index = 45 Token = 4 + DW 784 * 2 ; Index = 45 Token = 5 + DW 816 * 2 ; Index = 45 Token = 6 + DW 848 * 2 ; Index = 45 Token = 7 + DW 704 * 2 ; Index = 45 Token = 8 + DW 704 * 2 ; Index = 45 Token = 9 + DW 704 * 2 ; Index = 45 Token = 10 + DW 704 * 2 ; Index = 45 Token = 11 + DW 752 * 2 ; Index = 45 Token = 12 + DW 784 * 2 ; Index = 45 Token = 13 + DW 816 * 2 ; Index = 45 Token = 14 + DW 848 * 2 ; Index = 45 Token = 15 + DW 720 * 2 ; Index = 46 Token = 0 + DW 720 * 2 ; Index = 46 Token = 1 + DW 720 * 2 ; Index = 46 Token = 2 + DW 720 * 2 ; Index = 46 Token = 3 + DW 768 * 2 ; Index = 46 Token = 4 + DW 800 * 2 ; Index = 46 Token = 5 + DW 832 * 2 ; Index = 46 Token = 6 + DW 864 * 2 ; Index = 46 Token = 7 + DW 720 * 2 ; Index = 46 Token = 8 + DW 720 * 2 ; Index = 46 Token = 9 + DW 720 * 2 ; Index = 46 Token = 10 + DW 720 * 2 ; Index = 46 Token = 11 + DW 768 * 2 ; Index = 46 Token = 12 + DW 800 * 2 ; Index = 46 Token = 13 + DW 832 * 2 ; Index = 46 Token = 14 + DW 864 * 2 ; Index = 46 Token = 15 + DW 736 * 2 ; Index = 47 Token = 0 + DW 736 * 2 ; Index = 47 Token = 1 + DW 736 * 2 ; Index = 47 Token = 2 + DW 736 * 2 ; Index = 47 Token = 3 + DW 784 * 2 ; Index = 47 Token = 4 + DW 816 * 2 ; Index = 47 Token = 5 + DW 848 * 2 ; Index = 47 Token = 6 + DW 880 * 2 ; Index = 47 Token = 7 + DW 736 * 2 ; Index = 47 Token = 8 + DW 736 * 2 ; Index = 47 Token = 9 + DW 736 * 2 ; Index = 47 Token = 10 + DW 736 * 2 ; Index = 47 Token = 11 + DW 784 * 2 ; Index = 47 Token = 12 + DW 816 * 2 ; Index = 47 Token = 13 + DW 848 * 2 ; Index = 47 Token = 14 + DW 880 * 2 ; Index = 47 Token = 15 + DW 752 * 2 ; Index = 48 Token = 0 + DW 752 * 2 ; Index = 48 Token = 1 + DW 752 * 2 ; Index = 48 Token = 2 + DW 752 * 2 ; Index = 48 Token = 3 + DW 800 * 2 ; Index = 48 Token = 4 + DW 832 * 2 ; Index = 48 Token = 5 + DW 864 * 2 ; Index = 48 Token = 6 + DW 896 * 2 ; Index = 48 Token = 7 + DW 752 * 2 ; Index = 48 Token = 8 + DW 752 * 2 ; Index = 48 Token = 9 + DW 752 * 2 ; Index = 48 Token = 10 + DW 752 * 2 ; Index = 48 Token = 11 + DW 800 * 2 ; Index = 48 Token = 12 + DW 832 * 2 ; Index = 48 Token = 13 + DW 864 * 2 ; Index = 48 Token = 14 + DW 896 * 2 ; Index = 48 Token = 15 + DW 768 * 2 ; Index = 49 Token = 0 + DW 768 * 2 ; Index = 49 Token = 1 + DW 768 * 2 ; Index = 49 Token = 2 + DW 768 * 2 ; Index = 49 Token = 3 + DW 816 * 2 ; Index = 49 Token = 4 + DW 848 * 2 ; Index = 49 Token = 5 + DW 880 * 2 ; Index = 49 Token = 6 + DW 912 * 2 ; Index = 49 Token = 7 + DW 768 * 2 ; Index = 49 Token = 8 + DW 768 * 2 ; Index = 49 Token = 9 + DW 768 * 2 ; Index = 49 Token = 10 + DW 768 * 2 ; Index = 49 Token = 11 + DW 816 * 2 ; Index = 49 Token = 12 + DW 848 * 2 ; Index = 49 Token = 13 + DW 880 * 2 ; Index = 49 Token = 14 + DW 912 * 2 ; Index = 49 Token = 15 + DW 784 * 2 ; Index = 50 Token = 0 + DW 784 * 2 ; Index = 50 Token = 1 + DW 784 * 2 ; Index = 50 Token = 2 + DW 784 * 2 ; Index = 50 Token = 3 + DW 832 * 2 ; Index = 50 Token = 4 + DW 864 * 2 ; Index = 50 Token = 5 + DW 896 * 2 ; Index = 50 Token = 6 + DW 928 * 2 ; Index = 50 Token = 7 + DW 784 * 2 ; Index = 50 Token = 8 + DW 784 * 2 ; Index = 50 Token = 9 + DW 784 * 2 ; Index = 50 Token = 10 + DW 784 * 2 ; Index = 50 Token = 11 + DW 832 * 2 ; Index = 50 Token = 12 + DW 864 * 2 ; Index = 50 Token = 13 + DW 896 * 2 ; Index = 50 Token = 14 + DW 928 * 2 ; Index = 50 Token = 15 + DW 800 * 2 ; Index = 51 Token = 0 + DW 800 * 2 ; Index = 51 Token = 1 + DW 800 * 2 ; Index = 51 Token = 2 + DW 800 * 2 ; Index = 51 Token = 3 + DW 848 * 2 ; Index = 51 Token = 4 + DW 880 * 2 ; Index = 51 Token = 5 + DW 912 * 2 ; Index = 51 Token = 6 + DW 944 * 2 ; Index = 51 Token = 7 + DW 800 * 2 ; Index = 51 Token = 8 + DW 800 * 2 ; Index = 51 Token = 9 + DW 800 * 2 ; Index = 51 Token = 10 + DW 800 * 2 ; Index = 51 Token = 11 + DW 848 * 2 ; Index = 51 Token = 12 + DW 880 * 2 ; Index = 51 Token = 13 + DW 912 * 2 ; Index = 51 Token = 14 + DW 944 * 2 ; Index = 51 Token = 15 + DW 816 * 2 ; Index = 52 Token = 0 + DW 816 * 2 ; Index = 52 Token = 1 + DW 816 * 2 ; Index = 52 Token = 2 + DW 816 * 2 ; Index = 52 Token = 3 + DW 864 * 2 ; Index = 52 Token = 4 + DW 896 * 2 ; Index = 52 Token = 5 + DW 928 * 2 ; Index = 52 Token = 6 + DW 960 * 2 ; Index = 52 Token = 7 + DW 816 * 2 ; Index = 52 Token = 8 + DW 816 * 2 ; Index = 52 Token = 9 + DW 816 * 2 ; Index = 52 Token = 10 + DW 816 * 2 ; Index = 52 Token = 11 + DW 864 * 2 ; Index = 52 Token = 12 + DW 896 * 2 ; Index = 52 Token = 13 + DW 928 * 2 ; Index = 52 Token = 14 + DW 960 * 2 ; Index = 52 Token = 15 + DW 832 * 2 ; Index = 53 Token = 0 + DW 832 * 2 ; Index = 53 Token = 1 + DW 832 * 2 ; Index = 53 Token = 2 + DW 832 * 2 ; Index = 53 Token = 3 + DW 880 * 2 ; Index = 53 Token = 4 + DW 912 * 2 ; Index = 53 Token = 5 + DW 944 * 2 ; Index = 53 Token = 6 + DW 976 * 2 ; Index = 53 Token = 7 + DW 832 * 2 ; Index = 53 Token = 8 + DW 832 * 2 ; Index = 53 Token = 9 + DW 832 * 2 ; Index = 53 Token = 10 + DW 832 * 2 ; Index = 53 Token = 11 + DW 880 * 2 ; Index = 53 Token = 12 + DW 912 * 2 ; Index = 53 Token = 13 + DW 944 * 2 ; Index = 53 Token = 14 + DW 976 * 2 ; Index = 53 Token = 15 + DW 848 * 2 ; Index = 54 Token = 0 + DW 848 * 2 ; Index = 54 Token = 1 + DW 848 * 2 ; Index = 54 Token = 2 + DW 848 * 2 ; Index = 54 Token = 3 + DW 896 * 2 ; Index = 54 Token = 4 + DW 928 * 2 ; Index = 54 Token = 5 + DW 960 * 2 ; Index = 54 Token = 6 + DW 992 * 2 ; Index = 54 Token = 7 + DW 848 * 2 ; Index = 54 Token = 8 + DW 848 * 2 ; Index = 54 Token = 9 + DW 848 * 2 ; Index = 54 Token = 10 + DW 848 * 2 ; Index = 54 Token = 11 + DW 896 * 2 ; Index = 54 Token = 12 + DW 928 * 2 ; Index = 54 Token = 13 + DW 960 * 2 ; Index = 54 Token = 14 + DW 992 * 2 ; Index = 54 Token = 15 + DW 864 * 2 ; Index = 55 Token = 0 + DW 864 * 2 ; Index = 55 Token = 1 + DW 864 * 2 ; Index = 55 Token = 2 + DW 864 * 2 ; Index = 55 Token = 3 + DW 912 * 2 ; Index = 55 Token = 4 + DW 944 * 2 ; Index = 55 Token = 5 + DW 976 * 2 ; Index = 55 Token = 6 + DW 1008 * 2 ; Index = 55 Token = 7 + DW 864 * 2 ; Index = 55 Token = 8 + DW 864 * 2 ; Index = 55 Token = 9 + DW 864 * 2 ; Index = 55 Token = 10 + DW 864 * 2 ; Index = 55 Token = 11 + DW 912 * 2 ; Index = 55 Token = 12 + DW 944 * 2 ; Index = 55 Token = 13 + DW 976 * 2 ; Index = 55 Token = 14 + DW 1008 * 2 ; Index = 55 Token = 15 + DW 880 * 2 ; Index = 56 Token = 0 + DW 880 * 2 ; Index = 56 Token = 1 + DW 880 * 2 ; Index = 56 Token = 2 + DW 880 * 2 ; Index = 56 Token = 3 + DW 928 * 2 ; Index = 56 Token = 4 + DW 960 * 2 ; Index = 56 Token = 5 + DW 992 * 2 ; Index = 56 Token = 6 + DW 1024 * 2 ; Index = 56 Token = 7 + DW 880 * 2 ; Index = 56 Token = 8 + DW 880 * 2 ; Index = 56 Token = 9 + DW 880 * 2 ; Index = 56 Token = 10 + DW 880 * 2 ; Index = 56 Token = 11 + DW 928 * 2 ; Index = 56 Token = 12 + DW 960 * 2 ; Index = 56 Token = 13 + DW 992 * 2 ; Index = 56 Token = 14 + DW 1024 * 2 ; Index = 56 Token = 15 + DW 896 * 2 ; Index = 57 Token = 0 + DW 896 * 2 ; Index = 57 Token = 1 + DW 896 * 2 ; Index = 57 Token = 2 + DW 896 * 2 ; Index = 57 Token = 3 + DW 944 * 2 ; Index = 57 Token = 4 + DW 976 * 2 ; Index = 57 Token = 5 + DW 1008 * 2 ; Index = 57 Token = 6 + DW 1040 * 2 ; Index = 57 Token = 7 + DW 896 * 2 ; Index = 57 Token = 8 + DW 896 * 2 ; Index = 57 Token = 9 + DW 896 * 2 ; Index = 57 Token = 10 + DW 896 * 2 ; Index = 57 Token = 11 + DW 944 * 2 ; Index = 57 Token = 12 + DW 976 * 2 ; Index = 57 Token = 13 + DW 1008 * 2 ; Index = 57 Token = 14 + DW 1040 * 2 ; Index = 57 Token = 15 + DW 912 * 2 ; Index = 58 Token = 0 + DW 912 * 2 ; Index = 58 Token = 1 + DW 912 * 2 ; Index = 58 Token = 2 + DW 912 * 2 ; Index = 58 Token = 3 + DW 960 * 2 ; Index = 58 Token = 4 + DW 992 * 2 ; Index = 58 Token = 5 + DW 1024 * 2 ; Index = 58 Token = 6 + DW 1056 * 2 ; Index = 58 Token = 7 + DW 912 * 2 ; Index = 58 Token = 8 + DW 912 * 2 ; Index = 58 Token = 9 + DW 912 * 2 ; Index = 58 Token = 10 + DW 912 * 2 ; Index = 58 Token = 11 + DW 960 * 2 ; Index = 58 Token = 12 + DW 992 * 2 ; Index = 58 Token = 13 + DW 1024 * 2 ; Index = 58 Token = 14 + DW 1056 * 2 ; Index = 58 Token = 15 + DW 928 * 2 ; Index = 59 Token = 0 + DW 928 * 2 ; Index = 59 Token = 1 + DW 928 * 2 ; Index = 59 Token = 2 + DW 928 * 2 ; Index = 59 Token = 3 + DW 976 * 2 ; Index = 59 Token = 4 + DW 1008 * 2 ; Index = 59 Token = 5 + DW 1040 * 2 ; Index = 59 Token = 6 + DW 1072 * 2 ; Index = 59 Token = 7 + DW 928 * 2 ; Index = 59 Token = 8 + DW 928 * 2 ; Index = 59 Token = 9 + DW 928 * 2 ; Index = 59 Token = 10 + DW 928 * 2 ; Index = 59 Token = 11 + DW 976 * 2 ; Index = 59 Token = 12 + DW 1008 * 2 ; Index = 59 Token = 13 + DW 1040 * 2 ; Index = 59 Token = 14 + DW 1072 * 2 ; Index = 59 Token = 15 + DW 944 * 2 ; Index = 60 Token = 0 + DW 944 * 2 ; Index = 60 Token = 1 + DW 944 * 2 ; Index = 60 Token = 2 + DW 944 * 2 ; Index = 60 Token = 3 + DW 992 * 2 ; Index = 60 Token = 4 + DW 1024 * 2 ; Index = 60 Token = 5 + DW 1056 * 2 ; Index = 60 Token = 6 + DW 1088 * 2 ; Index = 60 Token = 7 + DW 944 * 2 ; Index = 60 Token = 8 + DW 944 * 2 ; Index = 60 Token = 9 + DW 944 * 2 ; Index = 60 Token = 10 + DW 944 * 2 ; Index = 60 Token = 11 + DW 992 * 2 ; Index = 60 Token = 12 + DW 1024 * 2 ; Index = 60 Token = 13 + DW 1056 * 2 ; Index = 60 Token = 14 + DW 1088 * 2 ; Index = 60 Token = 15 + DW 960 * 2 ; Index = 61 Token = 0 + DW 960 * 2 ; Index = 61 Token = 1 + DW 960 * 2 ; Index = 61 Token = 2 + DW 960 * 2 ; Index = 61 Token = 3 + DW 1008 * 2 ; Index = 61 Token = 4 + DW 1040 * 2 ; Index = 61 Token = 5 + DW 1072 * 2 ; Index = 61 Token = 6 + DW 1104 * 2 ; Index = 61 Token = 7 + DW 960 * 2 ; Index = 61 Token = 8 + DW 960 * 2 ; Index = 61 Token = 9 + DW 960 * 2 ; Index = 61 Token = 10 + DW 960 * 2 ; Index = 61 Token = 11 + DW 1008 * 2 ; Index = 61 Token = 12 + DW 1040 * 2 ; Index = 61 Token = 13 + DW 1072 * 2 ; Index = 61 Token = 14 + DW 1104 * 2 ; Index = 61 Token = 15 + DW 976 * 2 ; Index = 62 Token = 0 + DW 976 * 2 ; Index = 62 Token = 1 + DW 976 * 2 ; Index = 62 Token = 2 + DW 976 * 2 ; Index = 62 Token = 3 + DW 1024 * 2 ; Index = 62 Token = 4 + DW 1056 * 2 ; Index = 62 Token = 5 + DW 1088 * 2 ; Index = 62 Token = 6 + DW 1120 * 2 ; Index = 62 Token = 7 + DW 976 * 2 ; Index = 62 Token = 8 + DW 976 * 2 ; Index = 62 Token = 9 + DW 976 * 2 ; Index = 62 Token = 10 + DW 976 * 2 ; Index = 62 Token = 11 + DW 1024 * 2 ; Index = 62 Token = 12 + DW 1056 * 2 ; Index = 62 Token = 13 + DW 1088 * 2 ; Index = 62 Token = 14 + DW 1120 * 2 ; Index = 62 Token = 15 + DW 992 * 2 ; Index = 63 Token = 0 + DW 992 * 2 ; Index = 63 Token = 1 + DW 992 * 2 ; Index = 63 Token = 2 + DW 992 * 2 ; Index = 63 Token = 3 + DW 1040 * 2 ; Index = 63 Token = 4 + DW 1072 * 2 ; Index = 63 Token = 5 + DW 1104 * 2 ; Index = 63 Token = 6 + DW 1136 * 2 ; Index = 63 Token = 7 + DW 992 * 2 ; Index = 63 Token = 8 + DW 992 * 2 ; Index = 63 Token = 9 + DW 992 * 2 ; Index = 63 Token = 10 + DW 992 * 2 ; Index = 63 Token = 11 + DW 1040 * 2 ; Index = 63 Token = 12 + DW 1072 * 2 ; Index = 63 Token = 13 + DW 1104 * 2 ; Index = 63 Token = 14 + DW 1136 * 2 ; Index = 63 Token = 15 + DW 1008 * 2 ; Index = 64 Token = 0 + DW 1008 * 2 ; Index = 64 Token = 1 + DW 1008 * 2 ; Index = 64 Token = 2 + DW 1008 * 2 ; Index = 64 Token = 3 + DW 1056 * 2 ; Index = 64 Token = 4 + DW 1088 * 2 ; Index = 64 Token = 5 + DW 1120 * 2 ; Index = 64 Token = 6 + DW 1152 * 2 ; Index = 64 Token = 7 + DW 1008 * 2 ; Index = 64 Token = 8 + DW 1008 * 2 ; Index = 64 Token = 9 + DW 1008 * 2 ; Index = 64 Token = 10 + DW 1008 * 2 ; Index = 64 Token = 11 + DW 1056 * 2 ; Index = 64 Token = 12 + DW 1088 * 2 ; Index = 64 Token = 13 + DW 1120 * 2 ; Index = 64 Token = 14 + DW 1152 * 2 ; Index = 64 Token = 15 + DW 1024 * 2 ; Index = 65 Token = 0 + DW 1024 * 2 ; Index = 65 Token = 1 + DW 1024 * 2 ; Index = 65 Token = 2 + DW 1024 * 2 ; Index = 65 Token = 3 + DW 1072 * 2 ; Index = 65 Token = 4 + DW 1104 * 2 ; Index = 65 Token = 5 + DW 1136 * 2 ; Index = 65 Token = 6 + DW 1168 * 2 ; Index = 65 Token = 7 + DW 1024 * 2 ; Index = 65 Token = 8 + DW 1024 * 2 ; Index = 65 Token = 9 + DW 1024 * 2 ; Index = 65 Token = 10 + DW 1024 * 2 ; Index = 65 Token = 11 + DW 1072 * 2 ; Index = 65 Token = 12 + DW 1104 * 2 ; Index = 65 Token = 13 + DW 1136 * 2 ; Index = 65 Token = 14 + DW 1168 * 2 ; Index = 65 Token = 15 + DW 1040 * 2 ; Index = 66 Token = 0 + DW 1040 * 2 ; Index = 66 Token = 1 + DW 1040 * 2 ; Index = 66 Token = 2 + DW 1040 * 2 ; Index = 66 Token = 3 + DW 1088 * 2 ; Index = 66 Token = 4 + DW 1120 * 2 ; Index = 66 Token = 5 + DW 1152 * 2 ; Index = 66 Token = 6 + DW 1184 * 2 ; Index = 66 Token = 7 + DW 1040 * 2 ; Index = 66 Token = 8 + DW 1040 * 2 ; Index = 66 Token = 9 + DW 1040 * 2 ; Index = 66 Token = 10 + DW 1040 * 2 ; Index = 66 Token = 11 + DW 1088 * 2 ; Index = 66 Token = 12 + DW 1120 * 2 ; Index = 66 Token = 13 + DW 1152 * 2 ; Index = 66 Token = 14 + DW 1184 * 2 ; Index = 66 Token = 15 + DW 1056 * 2 ; Index = 67 Token = 0 + DW 1056 * 2 ; Index = 67 Token = 1 + DW 1056 * 2 ; Index = 67 Token = 2 + DW 1056 * 2 ; Index = 67 Token = 3 + DW 1104 * 2 ; Index = 67 Token = 4 + DW 1136 * 2 ; Index = 67 Token = 5 + DW 1168 * 2 ; Index = 67 Token = 6 + DW 1200 * 2 ; Index = 67 Token = 7 + DW 1056 * 2 ; Index = 67 Token = 8 + DW 1056 * 2 ; Index = 67 Token = 9 + DW 1056 * 2 ; Index = 67 Token = 10 + DW 1056 * 2 ; Index = 67 Token = 11 + DW 1104 * 2 ; Index = 67 Token = 12 + DW 1136 * 2 ; Index = 67 Token = 13 + DW 1168 * 2 ; Index = 67 Token = 14 + DW 1200 * 2 ; Index = 67 Token = 15 + DW 1072 * 2 ; Index = 68 Token = 0 + DW 1072 * 2 ; Index = 68 Token = 1 + DW 1072 * 2 ; Index = 68 Token = 2 + DW 1072 * 2 ; Index = 68 Token = 3 + DW 1120 * 2 ; Index = 68 Token = 4 + DW 1152 * 2 ; Index = 68 Token = 5 + DW 1184 * 2 ; Index = 68 Token = 6 + DW 1216 * 2 ; Index = 68 Token = 7 + DW 1072 * 2 ; Index = 68 Token = 8 + DW 1072 * 2 ; Index = 68 Token = 9 + DW 1072 * 2 ; Index = 68 Token = 10 + DW 1072 * 2 ; Index = 68 Token = 11 + DW 1120 * 2 ; Index = 68 Token = 12 + DW 1152 * 2 ; Index = 68 Token = 13 + DW 1184 * 2 ; Index = 68 Token = 14 + DW 1216 * 2 ; Index = 68 Token = 15 + DW 1088 * 2 ; Index = 69 Token = 0 + DW 1088 * 2 ; Index = 69 Token = 1 + DW 1088 * 2 ; Index = 69 Token = 2 + DW 1088 * 2 ; Index = 69 Token = 3 + DW 1136 * 2 ; Index = 69 Token = 4 + DW 1168 * 2 ; Index = 69 Token = 5 + DW 1200 * 2 ; Index = 69 Token = 6 + DW 1232 * 2 ; Index = 69 Token = 7 + DW 1088 * 2 ; Index = 69 Token = 8 + DW 1088 * 2 ; Index = 69 Token = 9 + DW 1088 * 2 ; Index = 69 Token = 10 + DW 1088 * 2 ; Index = 69 Token = 11 + DW 1136 * 2 ; Index = 69 Token = 12 + DW 1168 * 2 ; Index = 69 Token = 13 + DW 1200 * 2 ; Index = 69 Token = 14 + DW 1232 * 2 ; Index = 69 Token = 15 + DW 1104 * 2 ; Index = 70 Token = 0 + DW 1104 * 2 ; Index = 70 Token = 1 + DW 1104 * 2 ; Index = 70 Token = 2 + DW 1104 * 2 ; Index = 70 Token = 3 + DW 1152 * 2 ; Index = 70 Token = 4 + DW 1184 * 2 ; Index = 70 Token = 5 + DW 1216 * 2 ; Index = 70 Token = 6 + DW 1248 * 2 ; Index = 70 Token = 7 + DW 1104 * 2 ; Index = 70 Token = 8 + DW 1104 * 2 ; Index = 70 Token = 9 + DW 1104 * 2 ; Index = 70 Token = 10 + DW 1104 * 2 ; Index = 70 Token = 11 + DW 1152 * 2 ; Index = 70 Token = 12 + DW 1184 * 2 ; Index = 70 Token = 13 + DW 1216 * 2 ; Index = 70 Token = 14 + DW 1248 * 2 ; Index = 70 Token = 15 + DW 1120 * 2 ; Index = 71 Token = 0 + DW 1120 * 2 ; Index = 71 Token = 1 + DW 1120 * 2 ; Index = 71 Token = 2 + DW 1120 * 2 ; Index = 71 Token = 3 + DW 1168 * 2 ; Index = 71 Token = 4 + DW 1200 * 2 ; Index = 71 Token = 5 + DW 1232 * 2 ; Index = 71 Token = 6 + DW 1264 * 2 ; Index = 71 Token = 7 + DW 1120 * 2 ; Index = 71 Token = 8 + DW 1120 * 2 ; Index = 71 Token = 9 + DW 1120 * 2 ; Index = 71 Token = 10 + DW 1120 * 2 ; Index = 71 Token = 11 + DW 1168 * 2 ; Index = 71 Token = 12 + DW 1200 * 2 ; Index = 71 Token = 13 + DW 1232 * 2 ; Index = 71 Token = 14 + DW 1264 * 2 ; Index = 71 Token = 15 + DW 1136 * 2 ; Index = 72 Token = 0 + DW 1136 * 2 ; Index = 72 Token = 1 + DW 1136 * 2 ; Index = 72 Token = 2 + DW 1136 * 2 ; Index = 72 Token = 3 + DW 1184 * 2 ; Index = 72 Token = 4 + DW 1216 * 2 ; Index = 72 Token = 5 + DW 1248 * 2 ; Index = 72 Token = 6 + DW 1280 * 2 ; Index = 72 Token = 7 + DW 1136 * 2 ; Index = 72 Token = 8 + DW 1136 * 2 ; Index = 72 Token = 9 + DW 1136 * 2 ; Index = 72 Token = 10 + DW 1136 * 2 ; Index = 72 Token = 11 + DW 1184 * 2 ; Index = 72 Token = 12 + DW 1216 * 2 ; Index = 72 Token = 13 + DW 1248 * 2 ; Index = 72 Token = 14 + DW 1280 * 2 ; Index = 72 Token = 15 + DW 1152 * 2 ; Index = 73 Token = 0 + DW 1152 * 2 ; Index = 73 Token = 1 + DW 1152 * 2 ; Index = 73 Token = 2 + DW 1152 * 2 ; Index = 73 Token = 3 + DW 1200 * 2 ; Index = 73 Token = 4 + DW 1232 * 2 ; Index = 73 Token = 5 + DW 1264 * 2 ; Index = 73 Token = 6 + DW 1296 * 2 ; Index = 73 Token = 7 + DW 1152 * 2 ; Index = 73 Token = 8 + DW 1152 * 2 ; Index = 73 Token = 9 + DW 1152 * 2 ; Index = 73 Token = 10 + DW 1152 * 2 ; Index = 73 Token = 11 + DW 1200 * 2 ; Index = 73 Token = 12 + DW 1232 * 2 ; Index = 73 Token = 13 + DW 1264 * 2 ; Index = 73 Token = 14 + DW 1296 * 2 ; Index = 73 Token = 15 + DW 1168 * 2 ; Index = 74 Token = 0 + DW 1168 * 2 ; Index = 74 Token = 1 + DW 1168 * 2 ; Index = 74 Token = 2 + DW 1168 * 2 ; Index = 74 Token = 3 + DW 1216 * 2 ; Index = 74 Token = 4 + DW 1248 * 2 ; Index = 74 Token = 5 + DW 1280 * 2 ; Index = 74 Token = 6 + DW 1312 * 2 ; Index = 74 Token = 7 + DW 1168 * 2 ; Index = 74 Token = 8 + DW 1168 * 2 ; Index = 74 Token = 9 + DW 1168 * 2 ; Index = 74 Token = 10 + DW 1168 * 2 ; Index = 74 Token = 11 + DW 1216 * 2 ; Index = 74 Token = 12 + DW 1248 * 2 ; Index = 74 Token = 13 + DW 1280 * 2 ; Index = 74 Token = 14 + DW 1312 * 2 ; Index = 74 Token = 15 + DW 1184 * 2 ; Index = 75 Token = 0 + DW 1184 * 2 ; Index = 75 Token = 1 + DW 1184 * 2 ; Index = 75 Token = 2 + DW 1184 * 2 ; Index = 75 Token = 3 + DW 1232 * 2 ; Index = 75 Token = 4 + DW 1264 * 2 ; Index = 75 Token = 5 + DW 1296 * 2 ; Index = 75 Token = 6 + DW 1328 * 2 ; Index = 75 Token = 7 + DW 1184 * 2 ; Index = 75 Token = 8 + DW 1184 * 2 ; Index = 75 Token = 9 + DW 1184 * 2 ; Index = 75 Token = 10 + DW 1184 * 2 ; Index = 75 Token = 11 + DW 1232 * 2 ; Index = 75 Token = 12 + DW 1264 * 2 ; Index = 75 Token = 13 + DW 1296 * 2 ; Index = 75 Token = 14 + DW 1328 * 2 ; Index = 75 Token = 15 + DW 1200 * 2 ; Index = 76 Token = 0 + DW 1200 * 2 ; Index = 76 Token = 1 + DW 1200 * 2 ; Index = 76 Token = 2 + DW 1200 * 2 ; Index = 76 Token = 3 + DW 1248 * 2 ; Index = 76 Token = 4 + DW 1280 * 2 ; Index = 76 Token = 5 + DW 1312 * 2 ; Index = 76 Token = 6 + DW 1344 * 2 ; Index = 76 Token = 7 + DW 1200 * 2 ; Index = 76 Token = 8 + DW 1200 * 2 ; Index = 76 Token = 9 + DW 1200 * 2 ; Index = 76 Token = 10 + DW 1200 * 2 ; Index = 76 Token = 11 + DW 1248 * 2 ; Index = 76 Token = 12 + DW 1280 * 2 ; Index = 76 Token = 13 + DW 1312 * 2 ; Index = 76 Token = 14 + DW 1344 * 2 ; Index = 76 Token = 15 + DW 1216 * 2 ; Index = 77 Token = 0 + DW 1216 * 2 ; Index = 77 Token = 1 + DW 1216 * 2 ; Index = 77 Token = 2 + DW 1216 * 2 ; Index = 77 Token = 3 + DW 1264 * 2 ; Index = 77 Token = 4 + DW 1296 * 2 ; Index = 77 Token = 5 + DW 1328 * 2 ; Index = 77 Token = 6 + DW 1360 * 2 ; Index = 77 Token = 7 + DW 1216 * 2 ; Index = 77 Token = 8 + DW 1216 * 2 ; Index = 77 Token = 9 + DW 1216 * 2 ; Index = 77 Token = 10 + DW 1216 * 2 ; Index = 77 Token = 11 + DW 1264 * 2 ; Index = 77 Token = 12 + DW 1296 * 2 ; Index = 77 Token = 13 + DW 1328 * 2 ; Index = 77 Token = 14 + DW 1360 * 2 ; Index = 77 Token = 15 + DW 1232 * 2 ; Index = 78 Token = 0 + DW 1232 * 2 ; Index = 78 Token = 1 + DW 1232 * 2 ; Index = 78 Token = 2 + DW 1232 * 2 ; Index = 78 Token = 3 + DW 1280 * 2 ; Index = 78 Token = 4 + DW 1312 * 2 ; Index = 78 Token = 5 + DW 1344 * 2 ; Index = 78 Token = 6 + DW 1376 * 2 ; Index = 78 Token = 7 + DW 1232 * 2 ; Index = 78 Token = 8 + DW 1232 * 2 ; Index = 78 Token = 9 + DW 1232 * 2 ; Index = 78 Token = 10 + DW 1232 * 2 ; Index = 78 Token = 11 + DW 1280 * 2 ; Index = 78 Token = 12 + DW 1312 * 2 ; Index = 78 Token = 13 + DW 1344 * 2 ; Index = 78 Token = 14 + DW 1376 * 2 ; Index = 78 Token = 15 + DW 1248 * 2 ; Index = 79 Token = 0 + DW 1248 * 2 ; Index = 79 Token = 1 + DW 1248 * 2 ; Index = 79 Token = 2 + DW 1248 * 2 ; Index = 79 Token = 3 + DW 1296 * 2 ; Index = 79 Token = 4 + DW 1328 * 2 ; Index = 79 Token = 5 + DW 1360 * 2 ; Index = 79 Token = 6 + DW 1392 * 2 ; Index = 79 Token = 7 + DW 1248 * 2 ; Index = 79 Token = 8 + DW 1248 * 2 ; Index = 79 Token = 9 + DW 1248 * 2 ; Index = 79 Token = 10 + DW 1248 * 2 ; Index = 79 Token = 11 + DW 1296 * 2 ; Index = 79 Token = 12 + DW 1328 * 2 ; Index = 79 Token = 13 + DW 1360 * 2 ; Index = 79 Token = 14 + DW 1392 * 2 ; Index = 79 Token = 15 + DW 1264 * 2 ; Index = 80 Token = 0 + DW 1264 * 2 ; Index = 80 Token = 1 + DW 1264 * 2 ; Index = 80 Token = 2 + DW 1264 * 2 ; Index = 80 Token = 3 + DW 1312 * 2 ; Index = 80 Token = 4 + DW 1344 * 2 ; Index = 80 Token = 5 + DW 1376 * 2 ; Index = 80 Token = 6 + DW 1408 * 2 ; Index = 80 Token = 7 + DW 1264 * 2 ; Index = 80 Token = 8 + DW 1264 * 2 ; Index = 80 Token = 9 + DW 1264 * 2 ; Index = 80 Token = 10 + DW 1264 * 2 ; Index = 80 Token = 11 + DW 1312 * 2 ; Index = 80 Token = 12 + DW 1344 * 2 ; Index = 80 Token = 13 + DW 1376 * 2 ; Index = 80 Token = 14 + DW 1408 * 2 ; Index = 80 Token = 15 + DW 1280 * 2 ; Index = 81 Token = 0 + DW 1280 * 2 ; Index = 81 Token = 1 + DW 1280 * 2 ; Index = 81 Token = 2 + DW 1280 * 2 ; Index = 81 Token = 3 + DW 1328 * 2 ; Index = 81 Token = 4 + DW 1360 * 2 ; Index = 81 Token = 5 + DW 1392 * 2 ; Index = 81 Token = 6 + DW 1408 * 2 ; Index = 81 Token = 7 + DW 1280 * 2 ; Index = 81 Token = 8 + DW 1280 * 2 ; Index = 81 Token = 9 + DW 1280 * 2 ; Index = 81 Token = 10 + DW 1280 * 2 ; Index = 81 Token = 11 + DW 1328 * 2 ; Index = 81 Token = 12 + DW 1360 * 2 ; Index = 81 Token = 13 + DW 1392 * 2 ; Index = 81 Token = 14 + DW 1408 * 2 ; Index = 81 Token = 15 + DW 1296 * 2 ; Index = 82 Token = 0 + DW 1296 * 2 ; Index = 82 Token = 1 + DW 1296 * 2 ; Index = 82 Token = 2 + DW 1296 * 2 ; Index = 82 Token = 3 + DW 1344 * 2 ; Index = 82 Token = 4 + DW 1376 * 2 ; Index = 82 Token = 5 + DW 1408 * 2 ; Index = 82 Token = 6 + DW 1408 * 2 ; Index = 82 Token = 7 + DW 1296 * 2 ; Index = 82 Token = 8 + DW 1296 * 2 ; Index = 82 Token = 9 + DW 1296 * 2 ; Index = 82 Token = 10 + DW 1296 * 2 ; Index = 82 Token = 11 + DW 1344 * 2 ; Index = 82 Token = 12 + DW 1376 * 2 ; Index = 82 Token = 13 + DW 1408 * 2 ; Index = 82 Token = 14 + DW 1408 * 2 ; Index = 82 Token = 15 + DW 1312 * 2 ; Index = 83 Token = 0 + DW 1312 * 2 ; Index = 83 Token = 1 + DW 1312 * 2 ; Index = 83 Token = 2 + DW 1312 * 2 ; Index = 83 Token = 3 + DW 1360 * 2 ; Index = 83 Token = 4 + DW 1392 * 2 ; Index = 83 Token = 5 + DW 1408 * 2 ; Index = 83 Token = 6 + DW 1408 * 2 ; Index = 83 Token = 7 + DW 1312 * 2 ; Index = 83 Token = 8 + DW 1312 * 2 ; Index = 83 Token = 9 + DW 1312 * 2 ; Index = 83 Token = 10 + DW 1312 * 2 ; Index = 83 Token = 11 + DW 1360 * 2 ; Index = 83 Token = 12 + DW 1392 * 2 ; Index = 83 Token = 13 + DW 1408 * 2 ; Index = 83 Token = 14 + DW 1408 * 2 ; Index = 83 Token = 15 + DW 1328 * 2 ; Index = 84 Token = 0 + DW 1328 * 2 ; Index = 84 Token = 1 + DW 1328 * 2 ; Index = 84 Token = 2 + DW 1328 * 2 ; Index = 84 Token = 3 + DW 1376 * 2 ; Index = 84 Token = 4 + DW 1408 * 2 ; Index = 84 Token = 5 + DW 1408 * 2 ; Index = 84 Token = 6 + DW 1408 * 2 ; Index = 84 Token = 7 + DW 1328 * 2 ; Index = 84 Token = 8 + DW 1328 * 2 ; Index = 84 Token = 9 + DW 1328 * 2 ; Index = 84 Token = 10 + DW 1328 * 2 ; Index = 84 Token = 11 + DW 1376 * 2 ; Index = 84 Token = 12 + DW 1408 * 2 ; Index = 84 Token = 13 + DW 1408 * 2 ; Index = 84 Token = 14 + DW 1408 * 2 ; Index = 84 Token = 15 + DW 1344 * 2 ; Index = 85 Token = 0 + DW 1344 * 2 ; Index = 85 Token = 1 + DW 1344 * 2 ; Index = 85 Token = 2 + DW 1344 * 2 ; Index = 85 Token = 3 + DW 1392 * 2 ; Index = 85 Token = 4 + DW 1408 * 2 ; Index = 85 Token = 5 + DW 1408 * 2 ; Index = 85 Token = 6 + DW 1408 * 2 ; Index = 85 Token = 7 + DW 1344 * 2 ; Index = 85 Token = 8 + DW 1344 * 2 ; Index = 85 Token = 9 + DW 1344 * 2 ; Index = 85 Token = 10 + DW 1344 * 2 ; Index = 85 Token = 11 + DW 1392 * 2 ; Index = 85 Token = 12 + DW 1408 * 2 ; Index = 85 Token = 13 + DW 1408 * 2 ; Index = 85 Token = 14 + DW 1408 * 2 ; Index = 85 Token = 15 + DW 1360 * 2 ; Index = 86 Token = 0 + DW 1360 * 2 ; Index = 86 Token = 1 + DW 1360 * 2 ; Index = 86 Token = 2 + DW 1360 * 2 ; Index = 86 Token = 3 + DW 1408 * 2 ; Index = 86 Token = 4 + DW 1408 * 2 ; Index = 86 Token = 5 + DW 1408 * 2 ; Index = 86 Token = 6 + DW 1408 * 2 ; Index = 86 Token = 7 + DW 1360 * 2 ; Index = 86 Token = 8 + DW 1360 * 2 ; Index = 86 Token = 9 + DW 1360 * 2 ; Index = 86 Token = 10 + DW 1360 * 2 ; Index = 86 Token = 11 + DW 1408 * 2 ; Index = 86 Token = 12 + DW 1408 * 2 ; Index = 86 Token = 13 + DW 1408 * 2 ; Index = 86 Token = 14 + DW 1408 * 2 ; Index = 86 Token = 15 + DW 1376 * 2 ; Index = 87 Token = 0 + DW 1376 * 2 ; Index = 87 Token = 1 + DW 1376 * 2 ; Index = 87 Token = 2 + DW 1376 * 2 ; Index = 87 Token = 3 + DW 1408 * 2 ; Index = 87 Token = 4 + DW 1408 * 2 ; Index = 87 Token = 5 + DW 1408 * 2 ; Index = 87 Token = 6 + DW 1408 * 2 ; Index = 87 Token = 7 + DW 1376 * 2 ; Index = 87 Token = 8 + DW 1376 * 2 ; Index = 87 Token = 9 + DW 1376 * 2 ; Index = 87 Token = 10 + DW 1376 * 2 ; Index = 87 Token = 11 + DW 1408 * 2 ; Index = 87 Token = 12 + DW 1408 * 2 ; Index = 87 Token = 13 + DW 1408 * 2 ; Index = 87 Token = 14 + DW 1408 * 2 ; Index = 87 Token = 15 + DW 1392 * 2 ; Index = 88 Token = 0 + DW 1392 * 2 ; Index = 88 Token = 1 + DW 1392 * 2 ; Index = 88 Token = 2 + DW 1392 * 2 ; Index = 88 Token = 3 + DW 1408 * 2 ; Index = 88 Token = 4 + DW 1408 * 2 ; Index = 88 Token = 5 + DW 1408 * 2 ; Index = 88 Token = 6 + DW 1408 * 2 ; Index = 88 Token = 7 + DW 1392 * 2 ; Index = 88 Token = 8 + DW 1392 * 2 ; Index = 88 Token = 9 + DW 1392 * 2 ; Index = 88 Token = 10 + DW 1392 * 2 ; Index = 88 Token = 11 + DW 1408 * 2 ; Index = 88 Token = 12 + DW 1408 * 2 ; Index = 88 Token = 13 + DW 1408 * 2 ; Index = 88 Token = 14 + DW 1408 * 2 ; Index = 88 Token = 15 diff --git a/code/redalert/win32lib/irandom.cpp b/code/redalert/win32lib/irandom.cpp new file mode 100644 index 0000000..03b0bd0 --- /dev/null +++ b/code/redalert/win32lib/irandom.cpp @@ -0,0 +1,183 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : IRANDOM.C * + * * + * Programmer : Barry W. Green * + * * + * Last Update : 10 Feb, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "misc.h" + + + +extern "C" unsigned long RandNumb = 0x12349876; + + + +/* IRANDOM ---------------------------------------------------------- + + IRandom returns a random value between min and max inclusive. + + INPUTS: int min and int max + + RETURNS: int random number +*/ + +int IRandom(int minval, int maxval) +{ + int num,mask; + + // Keep minval and maxval straight. + if (minval > maxval) { + minval ^= maxval; + maxval ^= minval; + minval ^= maxval; + } + + mask = Get_Random_Mask(maxval - minval); + + while( (num = (rand() & mask) + minval) > maxval ) ; + return(num); +} + + + +unsigned char Random(void) +{ + __asm { + lea esi, [RandNumb] //; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 //; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 //; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 //; rcl byte 2 of RandNumb + cmc //; complement carry + sbb al,[esi] //; sbb byte 1 of RandNumb + shr al,1 //; sets carry + rcr [BYTE PTR esi],1 //; rcr byte 1 of RandNumb + mov al,[esi] //; reload byte 1 of RandNumb + xor al,[esi+1] //; xor with byte 2 of RandNumb + } +} + + + +int Get_Random_Mask(int maxval) +{ + __asm { + + bsr ecx,[maxval] // ; put bit position of highest bit in ecx + mov eax, 1 // ; set one bit on in eax + jz invalid // ; if BSR shows maxval==0, return eax=1 + inc ecx // ; get one bit higher than count showed + shl eax,cl // ; move our EAX bit into position + dec eax // ; dec it to create the mask. +invalid: + } +} + + + + + + + + + + + +#if (0) + +RandNumb DD 12349876H + +; +; UBYTE Random(VOID); +; int Get_Random_Mask(int maxval); +; +; ---------------------------------------------------------------- + +;----------------------------------------------------------------- +; RANDOM +; +; UBYTE Random(VOID); +; +;* + + PROC Random C near + USES esi + + lea esi, [RandNumb] ; get offset in segment of RandNumb + xor eax,eax + mov al,[esi] + shr al,1 ; shift right 1 bit (bit0 in carry) + shr al,1 + rcl [BYTE PTR esi+2],1 ; rcl byte 3 of RandNumb + rcl [BYTE PTR esi+1],1 ; rcl byte 2 of RandNumb + cmc ; complement carry + sbb al,[esi] ; sbb byte 1 of RandNumb + shr al,1 ; sets carry + rcr [BYTE PTR esi],1 ; rcr byte 1 of RandNumb + mov al,[esi] ; reload byte 1 of RandNumb + xor al,[esi+1] ; xor with byte 2 of RandNumb + + ret + + ENDP Random + + +;----------------------------------------------------------------- +; GET_RANDOM_MASK - returns an AND value that is large enough that it +; encloses the 'maxval' parameter. +; +; int Get_Random_Mask(int maxval); +; +;* + + PROC Get_Random_Mask C near + USES ecx + ARG maxval:DWORD + +; This function takes as a parameter a maximum value, for example, 61. It +; then tries to create an AND mask that is big enough to enclose that number. +; For our example case, that AND mask would be 0x3F. It does this by scanning +; for the highest bit in the number, then making an all-1's mask from that +; bit position down to bit 0. + bsr ecx,[maxval] ; put bit position of highest bit in ecx + mov eax,1 ; set one bit on in eax + jz ??invalid ; if BSR shows maxval==0, return eax=1 + inc ecx ; get one bit higher than count showed + shl eax,cl ; move our EAX bit into position + dec eax ; dec it to create the mask. +??invalid: + ret + ENDP Get_Random_Mask +;---------------------------------------------------------------------------- + + END +#endif \ No newline at end of file diff --git a/code/redalert/win32lib/keyboard.h b/code/redalert/win32lib/keyboard.h new file mode 100644 index 0000000..1be5316 --- /dev/null +++ b/code/redalert/win32lib/keyboard.h @@ -0,0 +1,675 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood Keyboard Library * + * * + * File Name : KEYBOARD.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 10/16/95 * + * * + * Last Update : October 16, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include + +typedef enum { + WWKEY_SHIFT_BIT = 0x100, + WWKEY_CTRL_BIT = 0x200, + WWKEY_ALT_BIT = 0x400, + WWKEY_RLS_BIT = 0x800, + WWKEY_VK_BIT = 0x1000, + WWKEY_DBL_BIT = 0x2000, + WWKEY_BTN_BIT = 0x8000, +} WWKey_Type; + +class WWKeyboardClass +{ + public: + /*===================================================================*/ + /* Define the base constructor and destructors for the class */ + /*===================================================================*/ + WWKeyboardClass(); + + /*===================================================================*/ + /* Define the functions which work with the Keyboard Class */ + /*===================================================================*/ + BOOL Check(void); // checks keybuff for meta key + int Get(void); // gets a meta key from the keybuffer + BOOL Put(int key); // dumps a key into the keybuffer + BOOL Put_Key_Message( UINT vk_key, BOOL release = FALSE, // handles keyboard related message + BOOL dbl = FALSE); // and mouse clicks and dbl clicks + int Check_Num(void); // checks keybuff for a keynum key + int Get_VK(void); // gets keynum key from key buff + int Check_ACII(void); // checks keybuff for an ascii key + int Get_ASCII(void); // gets an ascii key from keybuff + int Check_Bits(void); // checks keybuff for key w/ bits + int Get_Bits(void); // get key from keybuff w/ bits + int To_ASCII(int num); // converts keynum to ascii value + int Option_On(int option); // turns specified option on + int Option_Off(int option); // turns specified option off + void Clear(void); // clears all keys from keybuffer + int Down(int key); // tests to see if a key is down + void AI(void); // messaging logic for key manager + + /*===================================================================*/ + /* Define the main hook for the message processing loop. */ + /*===================================================================*/ + void Message_Handler(HWND hwnd, UINT message, UINT wParam, LONG lParam); + + /*===================================================================*/ + /* Define public routines which can be used on keys in general. */ + /*===================================================================*/ + VOID Split(int &key, int &shift, int &ctrl, int &alt, int &rls, int &dbl); + BOOL Is_Mouse_Key(int key); + + + /*===================================================================*/ + /* Define the public access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + //int MouseQX; + //int MouseQY; + + unsigned char VKRemap[256]; // gives vk for any ascii char + private: + /*===================================================================*/ + /* Define the private access functions which are used by keyboard */ + /*===================================================================*/ + int Buff_Get(void); + + + /*===================================================================*/ + /* Define the private access variables which are used with the */ + /* Keyboard Class. */ + /*===================================================================*/ + unsigned char AsciiRemap[2048]; // remap for shft/ctrl/alt key combos + unsigned short Buffer[256]; // buffer which holds actual keypresses + unsigned char ToggleKeys[256]; // determines toggles which affect key + long Head; // the head position in keyboard buffer + long Tail; // the tail position in keyboard buffer + int MState; + int Conditional; + HANDLE CurrentCursor; +}; + + +#define VK_NONE 0x00 +#define VK_LBUTTON 0x01 +#define VK_RBUTTON 0x02 +#define VK_CANCEL 0x03 +#define VK_MBUTTON 0x04 +#define VK_NONE_05 0x05 +#define VK_NONE_06 0x06 +#define VK_NONE_07 0x07 +#define VK_BACK 0x08 +#define VK_TAB 0x09 +#define VK_NONE_0A 0x0A +#define VK_NONE_0B 0x0B +#define VK_CLEAR 0x0C +#define VK_RETURN 0x0D +#define VK_NONE_0E 0x0E +#define VK_NONE_0F 0x0F +#define VK_SHIFT 0x10 +#define VK_CONTROL 0x11 +#define VK_MENU 0x12 +#define VK_PAUSE 0x13 +#define VK_CAPITAL 0x14 +#define VK_NONE_15 0x15 +#define VK_NONE_16 0x16 +#define VK_NONE_17 0x17 +#define VK_NONE_18 0x18 +#define VK_NONE_19 0x19 +#define VK_NONE_1A 0x1A +#define VK_ESCAPE 0x1B +#define VK_NONE_1C 0x1C +#define VK_NONE_1D 0x1D +#define VK_NONE_1E 0x1E +#define VK_NONE_1F 0x1F +#define VK_SPACE 0x20 +#define VK_PRIOR 0x21 +#define VK_NEXT 0x22 +#define VK_END 0x23 +#define VK_HOME 0x24 +#define VK_LEFT 0x25 +#define VK_UP 0x26 +#define VK_RIGHT 0x27 +#define VK_DOWN 0x28 +#define VK_SELECT 0x29 +#define VK_PRINT 0x2A +#define VK_EXECUTE 0x2B +#define VK_SNAPSHOT 0x2C +#define VK_INSERT 0x2D +#define VK_DELETE 0x2E +#define VK_HELP 0x2F +#define VK_0 0x30 +#define VK_1 0x31 +#define VK_2 0x32 +#define VK_3 0x33 +#define VK_4 0x34 +#define VK_5 0x35 +#define VK_6 0x36 +#define VK_7 0x37 +#define VK_8 0x38 +#define VK_9 0x39 +#define VK_NONE_3B 0x3B +#define VK_NONE_3C 0x3C +#define VK_NONE_3D 0x3D +#define VK_NONE_3E 0x3E +#define VK_NONE_3F 0x3F +#define VK_NONE_40 0x40 +#define VK_A 0x41 +#define VK_B 0x42 +#define VK_C 0x43 +#define VK_D 0x44 +#define VK_E 0x45 +#define VK_F 0x46 +#define VK_G 0x47 +#define VK_H 0x48 +#define VK_I 0x49 +#define VK_J 0x4A +#define VK_K 0x4B +#define VK_L 0x4C +#define VK_M 0x4D +#define VK_N 0x4E +#define VK_O 0x4F +#define VK_P 0x50 +#define VK_Q 0x51 +#define VK_R 0x52 +#define VK_S 0x53 +#define VK_T 0x54 +#define VK_U 0x55 +#define VK_V 0x56 +#define VK_W 0x57 +#define VK_X 0x58 +#define VK_Y 0x59 +#define VK_Z 0x5A +#define VK_NONE_5B 0x5B +#define VK_NONE_5C 0x5C +#define VK_NONE_5D 0x5D +#define VK_NONE_5E 0x5E +#define VK_NONE_5F 0x5F +#define VK_NUMPAD0 0x60 +#define VK_NUMPAD1 0x61 +#define VK_NUMPAD2 0x62 +#define VK_NUMPAD3 0x63 +#define VK_NUMPAD4 0x64 +#define VK_NUMPAD5 0x65 +#define VK_NUMPAD6 0x66 +#define VK_NUMPAD7 0x67 +#define VK_NUMPAD8 0x68 +#define VK_NUMPAD9 0x69 +#define VK_MULTIPLY 0x6A +#define VK_ADD 0x6B +#define VK_SEPARATOR 0x6C +#define VK_SUBTRACT 0x6D +#define VK_DECIMAL 0x6E +#define VK_DIVIDE 0x6F +#define VK_F1 0x70 +#define VK_F2 0x71 +#define VK_F3 0x72 +#define VK_F4 0x73 +#define VK_F5 0x74 +#define VK_F6 0x75 +#define VK_F7 0x76 +#define VK_F8 0x77 +#define VK_F9 0x78 +#define VK_F10 0x79 +#define VK_F11 0x7A +#define VK_F12 0x7B +#define VK_F13 0x7C +#define VK_F14 0x7D +#define VK_F15 0x7E +#define VK_F16 0x7F +#define VK_F17 0x80 +#define VK_F18 0x81 +#define VK_F19 0x82 +#define VK_F20 0x83 +#define VK_F21 0x84 +#define VK_F22 0x85 +#define VK_F23 0x86 +#define VK_F24 0x87 +#define VK_NONE_88 0x88 +#define VK_NONE_89 0x89 +#define VK_NONE_8A 0x8A +#define VK_NONE_8B 0x8B +#define VK_NONE_8C 0x8C +#define VK_NONE_8D 0x8D +#define VK_NONE_8E 0x8E +#define VK_NONE_8F 0x8F +#define VK_NUMLOCK 0x90 +#define VK_SCROLL 0x91 +#define VK_NONE_92 0x92 +#define VK_NONE_93 0x93 +#define VK_NONE_94 0x94 +#define VK_NONE_95 0x95 +#define VK_NONE_96 0x96 +#define VK_NONE_97 0x97 +#define VK_NONE_98 0x98 +#define VK_NONE_99 0x99 +#define VK_NONE_9A 0x9A +#define VK_NONE_9B 0x9B +#define VK_NONE_9C 0x9C +#define VK_NONE_9D 0x9D +#define VK_NONE_9E 0x9E +#define VK_NONE_9F 0x9F +#define VK_NONE_A0 0xA0 +#define VK_NONE_A1 0xA1 +#define VK_NONE_A2 0xA2 +#define VK_NONE_A3 0xA3 +#define VK_NONE_A4 0xA4 +#define VK_NONE_A5 0xA5 +#define VK_NONE_A6 0xA6 +#define VK_NONE_A7 0xA7 +#define VK_NONE_A8 0xA8 +#define VK_NONE_A9 0xA9 +#define VK_NONE_AA 0xAA +#define VK_NONE_AB 0xAB +#define VK_NONE_AC 0xAC +#define VK_NONE_AD 0xAD +#define VK_NONE_AE 0xAE +#define VK_NONE_AF 0xAF +#define VK_NONE_B0 0xB0 +#define VK_NONE_B1 0xB1 +#define VK_NONE_B2 0xB2 +#define VK_NONE_B3 0xB3 +#define VK_NONE_B4 0xB4 +#define VK_NONE_B5 0xB5 +#define VK_NONE_B6 0xB6 +#define VK_NONE_B7 0xB7 +#define VK_NONE_B8 0xB8 +#define VK_NONE_B9 0xB9 +#define VK_NONE_BA 0xBA +#define VK_NONE_BB 0xBB +#define VK_NONE_BC 0xBC +#define VK_NONE_BD 0xBD +#define VK_NONE_BE 0xBE +#define VK_NONE_BF 0xBF +#define VK_NONE_C0 0xC0 +#define VK_NONE_C1 0xC1 +#define VK_NONE_C2 0xC2 +#define VK_NONE_C3 0xC3 +#define VK_NONE_C4 0xC4 +#define VK_NONE_C5 0xC5 +#define VK_NONE_C6 0xC6 +#define VK_NONE_C7 0xC7 +#define VK_NONE_C8 0xC8 +#define VK_NONE_C9 0xC9 +#define VK_NONE_CA 0xCA +#define VK_NONE_CB 0xCB +#define VK_NONE_CC 0xCC +#define VK_NONE_CD 0xCD +#define VK_NONE_CE 0xCE +#define VK_NONE_CF 0xCF +#define VK_NONE_D0 0xD0 +#define VK_NONE_D1 0xD1 +#define VK_NONE_D2 0xD2 +#define VK_NONE_D3 0xD3 +#define VK_NONE_D4 0xD4 +#define VK_NONE_D5 0xD5 +#define VK_NONE_D6 0xD6 +#define VK_NONE_D7 0xD7 +#define VK_NONE_D8 0xD8 +#define VK_NONE_D9 0xD9 +#define VK_NONE_DA 0xDA +#define VK_NONE_DB 0xDB +#define VK_NONE_DC 0xDC +#define VK_NONE_DD 0xDD +#define VK_NONE_DE 0xDE +#define VK_NONE_DF 0xDF +#define VK_NONE_E0 0xE0 +#define VK_NONE_E1 0xE1 +#define VK_NONE_E2 0xE2 +#define VK_NONE_E3 0xE3 +#define VK_NONE_E4 0xE4 +#define VK_NONE_E5 0xE5 +#define VK_NONE_E6 0xE6 +#define VK_NONE_E7 0xE7 +#define VK_NONE_E8 0xE8 +#define VK_NONE_E9 0xE9 +#define VK_NONE_EA 0xEA +#define VK_NONE_EB 0xEB +#define VK_NONE_EC 0xEC +#define VK_NONE_ED 0xED +#define VK_NONE_EE 0xEE +#define VK_NONE_EF 0xEF +#define VK_NONE_F0 0xF0 +#define VK_NONE_F1 0xF1 +#define VK_NONE_F2 0xF2 +#define VK_NONE_F3 0xF3 +#define VK_NONE_F4 0xF4 +#define VK_NONE_F5 0xF5 +#define VK_NONE_F6 0xF6 +#define VK_NONE_F7 0xF7 +#define VK_NONE_F8 0xF8 +#define VK_NONE_F9 0xF9 +#define VK_NONE_FA 0xFA +#define VK_NONE_FB 0xFB +#define VK_NONE_FC 0xFC +#define VK_NONE_FD 0xFD +#define VK_NONE_FE 0xFE +#define VK_NONE_FF 0xFF + +#define VK_UPLEFT VK_HOME +#define VK_UPRIGHT VK_PRIOR +#define VK_DOWNLEFT VK_END +#define VK_DOWNRIGHT VK_NEXT +#define VK_ALT VK_MENU + +enum { + // + // Define all the KA types as variations of the VK types. This is + // so the KA functions will work properly under windows 95. + // + KA_NONE = 0, + KA_MORE = 1, + KA_SETBKGDCOL = 2, + KA_SETFORECOL = 6, + KA_FORMFEED = 12, + KA_SPCTAB = 20, + KA_SETX = 25, + KA_SETY = 26, + + KA_SPACE = 32, /* */ + KA_EXCLAMATION, /* ! */ + KA_DQUOTE, /* " */ + KA_POUND, /* # */ + KA_DOLLAR, /* $ */ + KA_PERCENT, /* % */ + KA_AMPER, /* & */ + KA_SQUOTE, /* ' */ + KA_LPAREN, /* ( */ + KA_RPAREN, /* ) */ + KA_ASTERISK, /* * */ + KA_PLUS, /* + */ + KA_COMMA, /* , */ + KA_MINUS, /* - */ + KA_PERIOD, /* . */ + KA_SLASH, /* / */ + + KA_0, KA_1, KA_2, KA_3, KA_4, KA_5, KA_6, KA_7, KA_8, KA_9, + KA_COLON, /* : */ + KA_SEMICOLON, /* ; */ + KA_LESS_THAN, /* < */ + KA_EQUAL, /* = */ + KA_GREATER_THAN, /* > */ + KA_QUESTION, /* ? */ + + KA_AT, /* @ */ + KA_A, /* A */ + KA_B, /* B */ + KA_C, /* C */ + KA_D, /* D */ + KA_E, /* E */ + KA_F, /* F */ + KA_G, /* G */ + KA_H, /* H */ + KA_I, /* I */ + KA_J, /* J */ + KA_K, /* K */ + KA_L, /* L */ + KA_M, /* M */ + KA_N, /* N */ + KA_O, /* O */ + + KA_P, /* P */ + KA_Q, /* Q */ + KA_R, /* R */ + KA_S, /* S */ + KA_T, /* T */ + KA_U, /* U */ + KA_V, /* V */ + KA_W, /* W */ + KA_X, /* X */ + KA_Y, /* Y */ + KA_Z, /* Z */ + KA_LBRACKET, /* [ */ + KA_BACKSLASH, /* \ */ + KA_RBRACKET, /* ] */ + KA_CARROT, /* ^ */ + KA_UNDERLINE, /* _ */ + + KA_GRAVE, /* ` */ + KA_a, /* a */ + KA_b, /* b */ + KA_c, /* c */ + KA_d, /* d */ + KA_e, /* e */ + KA_f, /* f */ + KA_g, /* g */ + KA_h, /* h */ + KA_i, /* i */ + KA_j, /* j */ + KA_k, /* k */ + KA_l, /* l */ + KA_m, /* m */ + KA_n, /* n */ + KA_o, /* o */ + + KA_p, /* p */ + KA_q, /* q */ + KA_r, /* r */ + KA_s, /* s */ + KA_t, /* t */ + KA_u, /* u */ + KA_v, /* v */ + KA_w, /* w */ + KA_x, /* x */ + KA_y, /* y */ + KA_z, /* z */ + KA_LBRACE, /* { */ + KA_BAR, /* | */ + KA_RBRACE, /* ] */ + KA_TILDA, /* ~ */ + + KA_ESC = VK_ESCAPE | WWKEY_VK_BIT, + KA_EXTEND = VK_ESCAPE | WWKEY_VK_BIT, + KA_RETURN = VK_RETURN | WWKEY_VK_BIT, + KA_BACKSPACE = VK_BACK | WWKEY_VK_BIT, + KA_TAB = VK_TAB | WWKEY_VK_BIT, + KA_DELETE = VK_DELETE | WWKEY_VK_BIT, /* */ + KA_INSERT = VK_INSERT | WWKEY_VK_BIT, /* */ + KA_PGDN = VK_NEXT | WWKEY_VK_BIT, /* */ + KA_DOWNRIGHT = VK_NEXT | WWKEY_VK_BIT, + KA_DOWN = VK_DOWN | WWKEY_VK_BIT, /* */ + KA_END = VK_END | WWKEY_VK_BIT, /* */ + KA_DOWNLEFT = VK_END | WWKEY_VK_BIT, + KA_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* */ + KA_KEYPAD5 = VK_SELECT | WWKEY_VK_BIT, /* NUMERIC KEY PAD <5> */ + KA_LEFT = VK_LEFT | WWKEY_VK_BIT, /* */ + KA_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* */ + KA_UPRIGHT = VK_PRIOR | WWKEY_VK_BIT, + KA_UP = VK_UP | WWKEY_VK_BIT, /* */ + KA_HOME = VK_HOME | WWKEY_VK_BIT, /* */ + KA_UPLEFT = VK_HOME | WWKEY_VK_BIT, + KA_F12 = VK_F12 | WWKEY_VK_BIT, + KA_F11 = VK_F11 | WWKEY_VK_BIT, + KA_F10 = VK_F10 | WWKEY_VK_BIT, + KA_F9 = VK_F9 | WWKEY_VK_BIT, + KA_F8 = VK_F8 | WWKEY_VK_BIT, + KA_F7 = VK_F7 | WWKEY_VK_BIT, + KA_F6 = VK_F6 | WWKEY_VK_BIT, + KA_F5 = VK_F5 | WWKEY_VK_BIT, + KA_F4 = VK_F4 | WWKEY_VK_BIT, + KA_F3 = VK_F3 | WWKEY_VK_BIT, + KA_F2 = VK_F2 | WWKEY_VK_BIT, + KA_F1 = VK_F1 | WWKEY_VK_BIT, + KA_LMOUSE = VK_LBUTTON | WWKEY_VK_BIT, + KA_RMOUSE = VK_RBUTTON | WWKEY_VK_BIT, + + KA_SHIFT_BIT = WWKEY_SHIFT_BIT, + KA_CTRL_BIT = WWKEY_CTRL_BIT, + KA_ALT_BIT = WWKEY_ALT_BIT, + KA_RLSE_BIT = WWKEY_RLS_BIT, + + // + // Define all the KN types as variations of the KA types. This is + // so the KN functions will work properly under windows 95. + // + KN_NONE = 0, + KN_GRAVE = KA_GRAVE, + KN_1 = KA_1, + KN_2 = KA_2, + KN_3 = KA_3, + KN_4 = KA_4, + KN_5 = KA_5, + KN_6 = KA_6, + KN_7 = KA_7, + KN_8 = KA_8, + KN_9 = KA_9, + KN_0 = KA_0, + KN_MINUS = KA_MINUS, /* - */ + KN_EQUAL = KA_EQUAL, /* = */ + + KN_BACKSPACE = KA_BACKSPACE, + + KN_TAB = KA_TAB, /* */ + KN_Q = KA_q, + KN_W = KA_w, + KN_E = KA_e, + KN_R = KA_r, + KN_T = KA_t, + KN_Y = KA_y, + KN_U = KA_u, + KN_I = KA_i, + KN_O = KA_o, + KN_P = KA_p, + KN_LBRACKET = KA_LBRACKET, /* [ */ + KN_RBRACKET = KA_RBRACKET, /* ] */ + KN_BACKSLASH = KA_BACKSLASH, /* \ */ + + + KN_A = KA_a, + KN_S = KA_s, + KN_D = KA_d, + KN_F = KA_f, + KN_G = KA_g, + KN_H = KA_h, + KN_J = KA_j, + KN_K = KA_k, + KN_L = KA_l, + KN_SEMICOLON = KA_SEMICOLON, /* ; */ + KN_SQUOTE = KA_SQUOTE, /* ' */ + KN_BACKSLASH2 = KA_BACKSLASH, + KN_RETURN = KA_RETURN, + KN_Z = KA_z, + KN_X = KA_x, + KN_C = KA_c, + KN_V = KA_v, + KN_B = KA_b, + KN_N = KA_n, + KN_M = KA_m, + KN_COMMA = KA_COMMA, /* , */ + KN_PERIOD = KA_PERIOD, /* . */ + KN_SLASH = KA_SLASH, /* / */ + KN_SPACE = KA_SPACE, + KN_LMOUSE = KA_LMOUSE, + KN_RMOUSE = KA_RMOUSE, + + KN_HOME = KA_HOME, /* num key pad 7 */ + KN_UPLEFT = KA_UPLEFT, + KN_LEFT = KA_LEFT, /* num key pad 4 */ + KN_END = KA_END, /* num key pad 1 */ + KN_DOWNLEFT = KA_DOWNLEFT, + + KN_KEYPAD_SLASH = KA_SLASH, /* num key pad / */ + KN_UP = KA_UP, /* num key pad 8 */ + KN_CENTER = KA_KEYPAD5, /* num key pad 5 */ + KN_DOWN = KA_DOWN, /* num key pad 2 */ + KN_INSERT = KA_INSERT, /* num key pad 0 */ + KN_KEYPAD_ASTERISK= KA_ASTERISK, /* num key pad * */ + KN_PGUP = KA_PGUP, /* num key pad 9 */ + KN_UPRIGHT = KA_UPRIGHT, + KN_RIGHT = KA_RIGHT, /* num key pad 6 */ + KN_PGDN = KA_PGDN, /* num key pad 3 */ + KN_DOWNRIGHT = KA_DOWNRIGHT, + KN_DELETE = KA_DELETE, /* num key pad . */ + + KN_KEYPAD_MINUS = KA_MINUS, /* num key pad - */ + KN_KEYPAD_PLUS = KA_PLUS, /* num key pad + */ + + + KN_KEYPAD_RETURN = KA_RETURN, /* num key pad */ + + KN_ESC = KA_ESC, + KN_F1 = KA_F1, + KN_F2 = KA_F2, + KN_F3 = KA_F3, + KN_F4 = KA_F4, + KN_F5 = KA_F5, + KN_F6 = KA_F6, + KN_F7 = KA_F7, + KN_F8 = KA_F8, + KN_F9 = KA_F9, + KN_F10 = KA_F10, + KN_F11 = KA_F11, + KN_F12 = KA_F12, + + KN_PRNTSCRN = VK_PRINT | WWKEY_VK_BIT, + KN_CAPSLOCK = VK_CAPITAL | WWKEY_VK_BIT, + KN_SCROLLLOCK = VK_SCROLL | WWKEY_VK_BIT, /* */ + KN_PAUSE = VK_PAUSE | WWKEY_VK_BIT, /* */ + KN_LSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_RSHIFT = VK_SHIFT | WWKEY_VK_BIT, + KN_LCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_RCTRL = VK_CONTROL | WWKEY_VK_BIT, + KN_LALT = VK_MENU | WWKEY_VK_BIT, + KN_RALT = VK_MENU | WWKEY_VK_BIT, + KN_E_INSERT = VK_INSERT | WWKEY_VK_BIT, + KN_E_DELETE = VK_DELETE | WWKEY_VK_BIT, + KN_E_LEFT = VK_LEFT | WWKEY_VK_BIT, /* extended */ + KN_E_HOME = VK_HOME | WWKEY_VK_BIT, /* extended */ + KN_E_END = VK_END | WWKEY_VK_BIT, /* extended */ + KN_E_UP = VK_UP | WWKEY_VK_BIT, /* extended */ + KN_E_DOWN = VK_DOWN | WWKEY_VK_BIT, /* extended */ + KN_E_PGUP = VK_PRIOR | WWKEY_VK_BIT, /* extended */ + KN_E_PGDN = VK_NEXT | WWKEY_VK_BIT, /* extended */ + KN_E_RIGHT = VK_RIGHT | WWKEY_VK_BIT, /* extended */ + KN_NUMLOCK = VK_NUMLOCK | WWKEY_VK_BIT, /* */ + + KN_SHIFT_BIT = WWKEY_SHIFT_BIT, + KN_CTRL_BIT = WWKEY_CTRL_BIT | WWKEY_VK_BIT, + KN_ALT_BIT = WWKEY_ALT_BIT | WWKEY_VK_BIT, + KN_RLSE_BIT = WWKEY_RLS_BIT, + KN_BUTTON = WWKEY_BTN_BIT, +}; + + +extern WWKeyboardClass *_Kbd; + + +/* +** The following routines provide some compatability with the old westwood +** library. +*/ +int Check_Key(void); +int Check_Key_Num(void); +int Get_Key_Num(void); +int Get_Key(void); +int KN_To_KA(int key); +void Clear_KeyBuffer(void); +int Key_Down(int key); +int KN_To_VK(int key); + +#endif diff --git a/code/redalert/win32lib/keyboard.inc b/code/redalert/win32lib/keyboard.inc new file mode 100644 index 0000000..58e521f --- /dev/null +++ b/code/redalert/win32lib/keyboard.inc @@ -0,0 +1,126 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYBOARD.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 12, 1994 * +;* * +;* Last Update : July 12, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Contains all the defines used by the keyboard interrupt for assembly * +;* includes. * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +TRUE EQU 1 ; numerical true +FALSE EQU 0 ; numerical false +DEBUG EQU 1 + +MAX_X_PIXEL EQU 319 +MAX_Y_PIXEL EQU 199 + + +KN_RESERVED1 EQU 14 +KN_RESERVED2 EQU 45 +KN_RESERVED3 EQU 56 + + +; these two are reserved for AMIGA open and close. +KN_LCOMM EQU 59 +KN_RCOMM EQU 63 + +KN_LMOUSE EQU 65 +KN_RMOUSE EQU 66 +KN_JBUTTON1 EQU 67 +KN_JBUTTON2 EQU 68 +KN_J_UP EQU 69 +KN_J_RIGHT EQU 70 +KN_J_DOWN EQU 71 +KN_J_LEFT EQU 72 + +KN_LEFT EQU 92 +KN_UPLEFT EQU 91 +KN_UP EQU 96 +KN_UPRIGHT EQU 101 +KN_RIGHT EQU 102 +KN_DOWNRIGHT EQU 103 +KN_DOWN EQU 98 +KN_DOWNLEFT EQU 93 +KN_CENTER EQU 97 + +KN_INSERT EQU 99 +KN_DELETE EQU 104 + +KN_RETURN EQU 43 +KN_SPACE EQU 61 +KN_KEYPAD_RETURN EQU 108 + + +; these two are reserved for AMIGA K left and right paren +KN_KLPAREN EQU 87 +KN_KRPAREN EQU 88 + + +KN_NUMLOCK EQU 90 + +KN_SCROLLOCK EQU 125 ; key ignored by the logging system + +KN_MOUSE_MOVE EQU 45 ; Indicate a mouse move (for playback of logged data) + +; ---------------------------------------------------------------- +; flags used in Keyflags to customize keystroke interrupt. + +REPEATON EQU 0001H ; 1:all makes into buffer, 0:only 1st make +TRACKEXT EQU 0002H ; 1:Home != keypad Home, 0:Home=keypad Home +FILTERONLY EQU 0004H ; 1:Normal BIOS operation with filter +CTRLSON EQU 0008H ; 1:pass scroll lock sequence into BIOS +CTRLALTTURBO EQU 0010H ; 1:Allow turbo up and down in application +CTRLCON EQU 0020H ; 1:pass stop code to BIOS +SCROLLLOCKON EQU 0040H ; 1:pass scroll lock key into BIOS +PAUSEON EQU 0080H ; 1:pass the pause key and seq to BIOS +BREAKON EQU 0100H ; 1:pass the ctrl break seq to BIOS +NONUMLOCK EQU 0200H ; 1:do NOT remap keypad to numbers +TASKSWITCHABLE EQU 0400H ; 1:allows task switching keys thru ALT-TAB, + ; ALT-ESC,CTRL-ESC +PASSBREAKS EQU 0800H ; 1:Pass all break codes to keyboard buffer. +KEYMOUSE EQU 1000H ; 1:Numeric keypad moves mouse +SIMLBUTTON EQU 2000H ; 1:have space and enter keys simulate Left +DEBUGINT EQU 4000H ; mouse button when KEYMOUSE is set + + +SHIFTPRESS EQU 001H ; bit 0 for shift key pressed +CTRLPRESS EQU 002H ; bit 1 for ctrl key pressed +ALTPRESS EQU 004H ; bit 2 for alt key pressed +KEYRELEASE EQU 008H ; bit 3 for key released +NOTKEYRELEASE EQU 0F7H ; not of key released + +CAPSLOCK EQU 00001H ; bit 0 for caps locked +NUMLOCK EQU 00002H ; bit 1 for num locked + + + +CLEARISR EQU 020H ; value to clear In Service Register +DOS EQU 021H +INTCHIP0 EQU 020H ; 8259 interrupt chip controller 0 +KEYCTRL EQU 061H ; control bits for KB sense data +KEYDATA EQU 060H ; keyboard scan code port diff --git a/code/redalert/win32lib/keystruc.inc b/code/redalert/win32lib/keystruc.inc new file mode 100644 index 0000000..f230404 --- /dev/null +++ b/code/redalert/win32lib/keystruc.inc @@ -0,0 +1,159 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : KEYSTRUC.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : July 13, 1994 * +;* * +;* Last Update : July 13, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +STRUC KeyboardType + SoundOn DW ? ; toggled by alt S + MusicOn DW ? ; toggled by alt M + KeyFlags DD ? ; all but repeat for now + Break DW ? + KeyMouseMove DB 6 DUP(?) + ScreenEdge DW 18 DUP (?) + Bits DB 8 DUP (?) + CondPassKey DW 17 DUP (?) + CondPassCond DW 17 DUP (?) + EscRoutine DD ? + ExtCodes DB 16 DUP (?) + ExtNums DB 16 DUP (?) + ExtRemap DB 16 DUP (?) + ExtRemapEnd DB ? + ExtKeyboard DB ? + KeyBuffer DW 128 DUP(?) ; set to empty + KeyBufferHead DD ? ; set to first entry + KeyBufferTail DD ? ; set to head for empty buffer + KeyLock DW ? ; num and caps lock bits + KeyNums DB 89 DUP (?) + KeysCapsLock DB 16 DUP (?) + KeysNumLock DB 16 DUP (?) + KeysUpDown DB 16 DUP (?) + KeyStream DB 16 DUP (?) + PassCount DW ? + KeyStreamIndex DW ? + LastKeyE0 DB ? + LastKeyE1 DB ? + PassAlways DB 10 DUP (?) + PassAlwaysEnd DB ? ; invalid code to END PassAlways + CtrlFlags DB ? + Buffer DW ? + Time DW ? + XYAdjust DB 26 DUP (?) + EdgeConv DW 16 DUP (?) + MouseUpdate DW ? + MouseX DD ? + LocalMouseX DW ? + MouseY DD ? + LocalMouseY DW ? + IsExtKey DB ? + ExtIndex DW ? + + KeyOldRMI DD ? ; The origianl RM interrupt seg:off. + KeyOldPMIOffset DD ? ; The origianl PM interrupt offset + KeyOldPMISelector DD ? ; The original PM interrupt segment. + + KeyCodeOffset DW ? ; Offset of the code in the RM stuff. + CallKeyRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallKeyRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedKeyInt DD ? ; did Protected mode pass this through? + + BrkOldRMI DD ? ; The origianl RM interrupt seg:off. + BrkOldPMIOffset DD ? ; The origianl PM interrupt offset + BrkOldPMISelector DD ? ; The original PM interrupt segment. + + BrkCodeOffset DW ? ; Offset of the code in the RM stuff. + CallBrkRMIntOffset DW ? ; Offset of function to call DOS timer interrupt. + CallBrkRMIntAddr DD ? ; PM address of CallRealIntOffset for speed. + PMIssuedBrkInt DD ? ; did Protected mode pass this through? + KeyIntDisabled DD ? + + DbgOldPMIOffset DD ? ; The origianl PM interrupt offset + DbgOldPMISelector DD ? ; The original PM interrupt segment. + + ;--------------------------------------------------------------------------- + ; Begin definition of Mouse Specific Variables for real mode + ;--------------------------------------------------------------------------- + Button DB ? ; current value of the mouse button + MDisabled DB ? ; Is the mouse driver disabled + MInput DB ? ; Defaults to mouse input allowed. + Adjust DW ? ; flag to adjust coordinates if necessary + MouseStepX DW ? ; step values if the mouse moves at + MouseStepY DW ? ; more than one pixel at a time + MouseOffsetX DW ? ; Fractional step values used if a mouse + MouseOffsetY DW ? ; moves at less than one pixel at a time + MState DD ? ; Tracks if mouse is hidden (TRUE) or not (FALSE) + MouseXOld DW ? ; Holds last MouseX and MouseY to determine if + MouseYOld DW ? ; mouse needs to be redrawn + MCState DW ? ; Tracks if mouse conditional hidden (TRUE) or not + MouseCXLeft DD ? ; Conditional hide mouse left x position + MouseCYUpper DD ? ; Conditional hide mouse top y position + MouseCXRight DD ? ; Conditional hide mouse right x position + MouseCYLower DD ? ; Conditional hide mouse lower y position + MouseCursor DD ? ; Pointer to the mouse cursor to draw + MouseCursorSize DW ? ; Size of mouse cursor draw area + MouseBuffer DD ? ; Pointer to buffer mouse is saved in + MouseXHot DD ? ; Offset to mouse's x hot spot + MouseYHot DD ? ; Offset to mouse's y hot spot + MouseBuffX DD ? ; X position background was saved at + MouseBuffY DD ? ; Y position background was saved at + MouseBuffW DD ? ; Width of the region saved for mouse + MouseBuffH DD ? ; Height of the region saved for mouse + MouseWidth DD ? ; Mouse cursor theoretical width + MouseHeight DD ? ; Mouse cursor theoretical height + MouseCodeOffset DW ? ; Offset to the real mode code offset + MouseRight DD ? ; Right hand side of the screen + MouseBottom DD ? ; Bottom of the screen + + + ShadowPtr dw ? + DrawMousePtr dw ? + + VGAMouseDraw dw ? + VGAMouseShadow dw ? + + VESAMouseDraw dw ? + VESAMouseShadow dw ? + + VesaPtr dd ? + VesaBankTable DD 8 dup (?) + Adjust_XPos dd ? + Adjust_YPos dd ? + + +ENDS + +; InitFlags that are set to have a fully functional interrupt. +IF_ALLOC_RM equ 1 ; Allocation of RM was successful. +IF_SET_VECTORS equ 2 ; Vectors have been set. +IF_LOCKED_PM_CODE equ 4 ; Locked PM code for DPMI. +IF_LOCKED_PM_DATA equ 8 ; Locked PM code for DPMI. +IF_RATE_CHANGE equ 10 ; Timer rate was changed. +IF_FUNCTIONAL equ 20 ; Timer is in and functional. +IF_LOCKED_RM_CODE equ 40 diff --git a/code/redalert/win32lib/load.cpp b/code/redalert/win32lib/load.cpp new file mode 100644 index 0000000..1a10558 --- /dev/null +++ b/code/redalert/win32lib/load.cpp @@ -0,0 +1,375 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/wwlib32/file/rcs/load.cpp 1.4 1994/04/22 12:42:21 scott_bowen Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : LOAD.C * + * * + * Programmer : Christopher Yates * + * * + * Last Update : September 17, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Uncompress -- Load and uncompress the given file. * + * Uncompress_Data -- Uncompress standard CPS buffer. * + * Load_Data -- Loads a data file from disk. * + * Load_Alloc_Data -- Loads and allocates buffer for a file. * + * Write_Data -- Writes a block of data as a file to disk. * + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "iff.h" +#include "file.h" +#include +#include +#include +#include + + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + + + +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +/*************************************************************************** + * LOAD_DATA -- Loads a data file from disk. * + * * + * This routine will load a data file from disk. It does no translation* + * on the data. * + * * + * INPUT: name -- Pointer to ASCII filename of the data file. * + * * + * ptr -- Buffer to load the data file into. * + * * + * size -- Maximum size of the buffer (in bytes). * + * * + * OUTPUT: Returns with the number of bytes read. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/24/1991 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Load_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, READ); + size = Read_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * WRITE_DATA -- Writes a block of data as a file to disk. * + * * + * This routine will write a block of data as a file to the disk. It * + * is the compliment of Load_Data. * + * * + * INPUT: name -- Name of the file to create. * + * * + * ptr -- Pointer to the block of data to write. * + * * + * size -- Size of the data block to be written. * + * * + * OUTPUT: Returns with the number of bytes actually written. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/05/1992 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Write_Data(char const *name, void *ptr, unsigned long size) +{ + int fd; + + fd = Open_File(name, WRITE); + size = Write_File(fd, ptr, size); + Close_File(fd); + return(size); +} + + +/*************************************************************************** + * LOAD_ALLOC_DATA -- Loads and allocates buffer for a file. * + * * + * The routine will allocate a buffer and load the specified file into * + * it. The kind of memory used for the buffer is determined by the * + * memory allocation flags passed in. * + * * + * INPUT: name -- Name of the file to load. * + * * + * flags -- Memory allocation flags to use when allocating. * + * * + * OUTPUT: Returns with a pointer to the buffer that contains the file's * + * data. * + * * + * WARNINGS: A memory error could occur if regular memory flags are * + * specified. If XMS memory is specified, then this routine * + * could likely return NULL. * + * * + * HISTORY: * + * 05/28/1992 JLB : Created. * + *=========================================================================*/ +void * __cdecl Load_Alloc_Data(char const *name, MemoryFlagType flags) +{ + int fd; // Working file handle. + unsigned long size; // Size of the file to load. + void *buffer; // Buffer to hold the file. + + fd = Open_File(name, READ); + size = File_Size(fd); + buffer = Alloc(size, flags); + if (buffer) { + Read_File(fd, buffer, size); + } + Close_File(fd); + return(buffer); +} + + +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char * - file name to uncompress * + * GraphicBufferClass& - to load the source data into * + * GraphicBufferClass& - for the picture * + * void * - ptr for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BufferClass& uncomp_buff, BufferClass& dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize=0; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = uncomp_buff.Get_Buffer(); // get a pointer to buffer + + /*======================================================================*/ + /* Read the file into the uncompression buffer. */ + /*======================================================================*/ + + fd = Open_File(file, READ); // Open up the file to read from + Read_File(fd, (char *) &isize, 2L); // Read the file size + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /*======================================================================*/ + /* Check for and read in the skip data block. */ + /*======================================================================*/ + + skipsize = *(((short *)uncomp_ptr) + 3); + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /*======================================================================*/ + /* If the source and dest buffer are the same, we adjust the pointer so */ + /* that the compressed data is loaded into the end of the buffer. In */ + /* this way the uncompress code can write to the same buffer. */ + /*======================================================================*/ + newuncomp_ptr = (char *)Add_Long_To_Pointer(uncomp_buff.Get_Buffer(), uncomp_buff.Get_Size() - (isize+8L)); + + /*======================================================================*/ + /* Duplicate the header bytes. */ + /*======================================================================*/ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /*======================================================================*/ + /* Read in the main compressed part of the file. */ + /*======================================================================*/ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + /*======================================================================*/ + /* Uncompress the file into the destination buffer (which may very well */ + /* be the source buffer). */ + /*======================================================================*/ + return(Uncompress_Data(newuncomp_ptr, dest_buff.Get_Buffer())); +} +#if(0) +/*************************************************************************** + * LOAD_UNCOMPRESS -- Load and uncompress the given file. * + * * + * INPUT: char *file name to uncompress, BuffType uncomp_buff to load * + * the source data into, BuffType dest_buff for the picture, * + * void *reserved_data pointer for header uncompressed data * + * * + * OUTPUT: unsigned long size of uncompressed data * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/28/1991 CY : Created. * + * 06/26/1991 JLB : Handles load & uncompress to same buffer. * + *=========================================================================*/ +unsigned long __cdecl Load_Uncompress(char const *file, BuffType uncomp_buff, BuffType dest_buff, void *reserved_data) +{ + int fd; // Source file handle. + unsigned int isize; // Size of the file. + unsigned int skipsize; // Size of the skip data bytes. + void *uncomp_ptr; // Source buffer pointer. + char *newuncomp_ptr; // Adjusted source pointer. + + + uncomp_ptr = Get_Buff(uncomp_buff); /* Get pointer to uncomp buffer */ + + /* Read the file into the uncomp_buff */ + + fd = Open_File(file, READ); + Read_File(fd, (char *) &isize, 2L); /* Read the file size */ + #if(AMIGA) + isize = Reverse_Word(isize); + #endif + + Read_File(fd, uncomp_ptr, 8L); // Read the header bytes in. + isize -= 8; // Remaining data in file. + + /* + ** Check for and read in the skip data block. + */ + + skipsize = *(((short*)uncomp_ptr) + 3); + #if(AMIGA) + skipsize = Reverse_Word(skipsize); + #endif + + if (reserved_data && skipsize) { + Read_File(fd, reserved_data, (unsigned long) skipsize); + } else { + Seek_File(fd, skipsize, SEEK_CUR); + } + *( ((short *)uncomp_ptr+3) ) = 0; // K/O any skip value. + isize -= skipsize; + + /* + ** If the source and dest buffer are the same, we + ** adjust the pointer so that the compressed data is + ** loaded into the end of the buffer. In this way the + ** uncompress code can write to the same buffer. + */ + #if(IBM) + newuncomp_ptr = (char *)Add_Long_To_Pointer(Get_Buff(uncomp_buff), PageArraySize[uncomp_buff] - (isize+8L)); + #else + newuncomp_ptr = Get_Buff(uncomp_buff); + newuncomp_ptr += PageArraySize[uncomp_buff] - ((isize+10) & 0xFFFE); + #endif + + /* + ** Duplicate the header bytes. + */ + Mem_Copy(uncomp_ptr,newuncomp_ptr,8); + + /* + ** Read in the main compressed part of the file. + */ + Read_File(fd, newuncomp_ptr + 8, (unsigned long)isize); + Close_File(fd); + + return(Uncompress_Data(newuncomp_ptr, Get_Buff(dest_buff))); +} + +#endif +/*************************************************************************** + * Uncompress_Data -- Uncompresses data from one buffer to another. * + * * + * This routine takes data from a compressed file (sans the first two * + * size bytes) and uncompresses it to a destination buffer. The source * + * data MUST have the CompHeaderType at its start. * + * * + * INPUT: src -- Source compressed data pointer. * + * * + * dst -- Destination (paragraph aligned) pointer. * + * * + * OUTPUT: Returns with the size of the uncompressed data. * + * * + * WARNINGS: If LCW compression is used, the destination buffer must * + * be paragraph aligned. * + * * + * HISTORY: * + * 09/17/1993 JLB : Created. * + *=========================================================================*/ +unsigned long __cdecl Uncompress_Data(void const *src, void *dst) +{ + unsigned int skip; // Number of leading data to skip. + CompressionType method; // Compression method used. + unsigned long uncomp_size=NULL; + + if (!src || !dst) return(NULL); + + /* + ** Interpret the data block header structure to determine + ** compression method, size, and skip data amount. + */ + uncomp_size = ((CompHeaderType*)src)->Size; + #if(AMIGA) + uncomp_size = Reverse_Long(uncomp_size); + #endif + skip = ((CompHeaderType*)src)->Skip; + #if(AMIGA) + skip = Reverse_Word(skip); + #endif + method = (CompressionType) ((CompHeaderType*)src)->Method; + src = Add_Long_To_Pointer((void *)src, (long)sizeof(CompHeaderType) + (long)skip); + + switch (method) { + + default: + case NOCOMPRESS: + Mem_Copy((void *) src, dst, uncomp_size); + break; + + case HORIZONTAL: +#if LIB_EXTERNS_RESOLVED + RLE_Uncompress((void *) src, dst, uncomp_size); +#endif + break; + + case LCW: + LCW_Uncompress((void *) src, (void *) dst, (unsigned long) uncomp_size); + break; + + } + + return(uncomp_size); +} + + diff --git a/code/redalert/win32lib/loadfont.cpp b/code/redalert/win32lib/loadfont.cpp new file mode 100644 index 0000000..87cd7f6 --- /dev/null +++ b/code/redalert/win32lib/loadfont.cpp @@ -0,0 +1,137 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : LOADFONT.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 6, 1991 * + * * + * Last Update : June 27, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Load_Font -- Loads a font from disk. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include "font.h" +#include +#include +#include + +#if(IBM) +#include +#include + +#include + +int FontXSpacing = 0; +int FontYSpacing = 0; +void const *FontPtr = NULL; +char FontWidth = 8; +char FontHeight = 8; + +// only font.c and set_font.c use the following +char *FontWidthBlockPtr = NULL; + + + +/*************************************************************************** + * LOAD_FONT -- Loads a font from disk. * + * * + * This loads a font from disk. This function must be called as a * + * precursor to calling Set_Font(). You need only call this function * + * once per desired font at the beginning of your code, but AFTER * + * Prog_Init() is called. * + * * + * INPUT: name - Pointer to font name to use (eg. "topaz.font") * + * * + * fontsize - Size in points of the font loaded. * + * * + * OUTPUT: Pointer to font data or NULL if unable to load. * + * * + * WARNINGS: Some system memory is grabbed by this routine. * + * * + * HISTORY: * + * 4/10/91 BS : 2.0 compatibily * + * 6/09/91 JLB : IBM and Amiga compatability. * + * 11/27/1991 JLB : Uses file I/O routines for disk access. * + * 01/29/1992 DRD : Modified to use new font format. * + * 02/01/1992 DRD : Added font file verification. * + * 06/29/1994 SKB : modified for 32 bit library * + *=========================================================================*/ +void * __cdecl Load_Font(char const *name) +{ + char valid; + int fh; // DOS file handle for font file. + unsigned short size; // Size of the data in the file (-2); + char *ptr = NULL; // Pointer to newly loaded font. + + + + fh=Open_File(name,READ); + if ( fh>=0 ){ + if ( Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size , MEM_NORMAL ); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return ((void*)errno); + } + + + +#ifdef cuts + if (Find_File(name)) { + fh = Open_File(name, READ); + if (Read_File(fh, (char *) &size, 2) != 2) return(NULL); + + ptr = (char *) Alloc(size, MEM_NORMAL); + *(short *)ptr = size; + Read_File(fh, ptr + 2, size - 2); + Close_File(fh); + } else { + return (NULL); + } +#endif + + // + // verify that the file loaded is a valid font file. + // + + valid = FALSE; + if (*(ptr + 2) == 0) { // no compression + if (*(ptr + 3) == 5) { // currently only 5 data blocks are used. + valid = TRUE; + } + } + + if ( !valid ) { + return (NULL); + } + + return(ptr); +} + +#endif diff --git a/code/redalert/win32lib/mcgaprim.inc b/code/redalert/win32lib/mcgaprim.inc new file mode 100644 index 0000000..a730e41 --- /dev/null +++ b/code/redalert/win32lib/mcgaprim.inc @@ -0,0 +1,119 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MCGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : January 16, 1995 * +;* * +;* Last Update : January 16, 1995 [PWG] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + +; Externs from REGIONSZ.ASM module of the MCGAPRIM library +GLOBAL MCGA_Size_Of_Region :NEAR + +; Externs from GETPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Get_Pixel :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from PUTPIX.ASM module of the MCGAPRIM library +GLOBAL MCGA_Put_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + +; Externs from CLEAR.ASM module of the MCGAPRIM library +GLOBAL MCGA_Clear :NEAR + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR +; Externs from BITBLIT.ASM module of the MCGAPRIM library +GLOBAL Linear_Blit_To_Linear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from TOBUFF.ASM module of the MCGAPRIM library +GLOBAL MCGA_To_Buffer :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from TOPAGE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Buffer_To_Page :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from SCALE.ASM module of the MCGAPRIM library +GLOBAL Linear_Scale_To_Linear :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + +; Externs from TXTPRNT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Print :NEAR + +; Externs from VTXTPRNT.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR + +;*-------------------------------------------------------------------------* +;* Define MCGA only assembly GLOBALS * +;*-------------------------------------------------------------------------* + +; Externs from DRAWLINE.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Line :NEAR + +; Externs from FILLQUAD.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Quad :NEAR + +; Externs from FILLRECT.ASM module of the MCGAPRIM library +GLOBAL MCGA_Fill_Rect :NEAR + +; Externs from REMAP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Remap :NEAR + +; Externs from STAMP.ASM module of the MCGAPRIM library +GLOBAL MCGA_Draw_Stamp :NEAR + +GLOBAL get_clip : NEAR + +struc RECTANGLE + x0 dd ? + y0 dd ? + x1 dd ? + y1 dd ? +ends RECTANGLE + + + + + \ No newline at end of file diff --git a/code/redalert/win32lib/memflag.h b/code/redalert/win32lib/memflag.h new file mode 100644 index 0000000..dbd93ab --- /dev/null +++ b/code/redalert/win32lib/memflag.h @@ -0,0 +1,106 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEMFLAG.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef MEMFLAG_H +#define MEMFLAG_H +// Memory Flags +/* +** Memory allocation flags. These are the flags that are passed into Alloc +** in order to control the type of memory allocated. +*/ +typedef enum { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_NEW = 0x0001, // Called by the operator new and was overloaded. + MEM_CLEAR = 0x0002, // Clear memory before returning. + MEM_REAL = 0x0004, // Clear memory before returning. + MEM_TEMP = 0x0008, // Clear memory before returning. + MEM_LOCK = 0x0010, // Lock the memory that we allocated +} MemoryFlagType; + + +/* +** Prototypes for VMPAGEIN.ASM +*/ +extern "C"{ + void __cdecl Force_VM_Page_In (void *buffer, int length); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: ALLOC.CPP */ +/*=========================================================================*/ + +void * operator new(size_t size, MemoryFlagType flag); +void * operator new[] (size_t size, MemoryFlagType flag); +void *Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void Free(void const *pointer); +void DPMI_Lock(VOID const *ptr, long const size); +void DPMI_Unlock(void const *ptr, long const size); +void *Resize_Alloc(void *original_ptr, unsigned long new_size_in_bytes); +long Ram_Free(MemoryFlagType flag); +long Heap_Size(MemoryFlagType flag); +long Total_Ram_Free(MemoryFlagType flag); + +//#pragma option -Jgd + +inline void * operator new(size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} +inline void * operator new[] (size_t size, MemoryFlagType flag) +{ + return(Alloc(size, flag)); +} + +//#pragma option -Jgd + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM_COPY.ASM */ +/*=========================================================================*/ + +extern "C" { + void __cdecl Mem_Copy(void const *source, void *dest, unsigned long bytes_to_copy); +} + + +inline void *Add_Long_To_Pointer(void const *ptr, long size) +{ + return ((void *) ( (char const *) ptr + size)); +} + +extern void (*Memory_Error)(void); +extern void (*Memory_Error_Exit)(char *string); + +extern unsigned long MinRam; // Record of least memory at worst case. +extern unsigned long MaxRam; // Record of total allocated at worst case. + + +#endif diff --git a/code/redalert/win32lib/misc.h b/code/redalert/win32lib/misc.h new file mode 100644 index 0000000..a9a23aa --- /dev/null +++ b/code/redalert/win32lib/misc.h @@ -0,0 +1,264 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : MISC.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MISC_H +#define MISC_H + +#define WIN32_LEAN_AND_MEAN // eliminates unecessary definitions in windows.h +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#include +#include +//#include + +/*========================= C++ Routines ==================================*/ + + +/*=========================================================================*/ +/* The following prototypes are for the file: DDRAW.CPP */ +/*=========================================================================*/ +void Process_DD_Result(HRESULT result, int display_ok_msg); +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel); +void Reset_Video_Mode(void); +unsigned Get_Free_Video_Memory(void); +void Wait_Blit(void); +unsigned Get_Video_Hardware_Capabilities(void); + +void Wait_Vert_Blank(void); +void Set_DD_Palette (void *palette, bool raShift = true); + +/* +** Pointer to function to call if we detect a focus loss +*/ +extern void (*Misc_Focus_Loss_Function)(void); +/* +** Pointer to function to call if we detect a surface restore +*/ +extern void (*Misc_Focus_Restore_Function)(void); + + +/* + * Flags returned by Get_Video_Hardware_Capabilities + */ +/* Hardware blits supported? */ +#define VIDEO_BLITTER 1 + +/* Hardware blits asyncronous? */ +#define VIDEO_BLITTER_ASYNC 2 + +/* Can palette changes be synced to vertical refresh? */ +#define VIDEO_SYNC_PALETTE 4 + +/* Is the video cards memory bank switched? */ +#define VIDEO_BANK_SWITCHED 8 + +/* Can the blitter do filled rectangles? */ +#define VIDEO_COLOR_FILL 16 + +/* Is there no hardware assistance avaailable at all? */ +#define VIDEO_NO_HARDWARE_ASSIST 32 + + + +/* + * Definition of surface monitor class + * + * This class keeps track of all the graphic buffers we generate in video memory so they + * can be restored after a focus switch. +*/ + +#define MAX_SURFACES 20 + +class SurfaceMonitorClass { + + public: + + SurfaceMonitorClass() { } + + void Add_DD_Surface (void *) { } + void Remove_DD_Surface (void*) { } + BOOL Got_Surface_Already (void*) { } + void Restore_Surfaces (void) { } + void Set_Surface_Focus ( BOOL in_focus ) { } + void Release(void) { } + + BOOL SurfacesRestored; + + private: + + void *Surface[MAX_SURFACES]; + BOOL InFocus; + +}; + +extern SurfaceMonitorClass AllSurfaces; //List of all direct draw surfaces + + +/*=========================================================================*/ +/* The following variables are declared in: DDRAW.CPP */ +/*=========================================================================*/ +extern HWND MainWindow; +extern BOOL SystemToVideoBlits; +extern BOOL VideoToSystemBlits; +extern BOOL SystemToSystemBlits; +extern BOOL OverlappedVideoBlits; // Can video driver blit overlapped regions? + +/*=========================================================================*/ +/* The following prototypes are for the file: EXIT.CPP */ +/* Prog_End Must be supplied by the user program in startup.cpp */ +/*=========================================================================*/ +void __cdecl Prog_End(const char *why, bool fatal); // Added why and fatal. ST - 8/7/2019 10:54AM +VOID __cdecl Exit(INT errorval, const BYTE *message, ...); + +/*=========================================================================*/ +/* The following prototypes are for the file: DELAY.CPP */ +/*=========================================================================*/ +void Delay(int duration); +void Vsync(void); + + +/*=========================================================================*/ +/* The following prototypes are for the file: FINDARGV.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Find_Argv(BYTE const *str); + +/*=========================================================================*/ +/* The following prototypes are for the file: LIB.CPP */ +/*=========================================================================*/ +char *Find_Argv(char const *str); +void Mono_Mem_Dump(void const *databuf, int bytes, int y); +void Convert_RGB_To_HSV(unsigned int r, unsigned int g, unsigned int b, unsigned int *h, unsigned int *s, unsigned int *v); +void Convert_HSV_To_RGB(unsigned int h, unsigned int s, unsigned int v, unsigned int *r, unsigned int *g, unsigned int *b); + + +/*=========================================================================*/ +/* The following prototypes are for the file: VERSION.CPP */ +/*=========================================================================*/ + +BYTE __cdecl Version(VOID); + + +/*=========================================================================*/ +/* The following prototypes are for the file: IRANDOM.CPP */ +/*=========================================================================*/ +int IRandom(int minval, int maxval); + + +/*========================= Assembly Routines ==============================*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/*=========================================================================*/ +/* The following prototypes are for the file: RANDOM.ASM */ +/*=========================================================================*/ + +unsigned char __cdecl Random(void); +int __cdecl Get_Random_Mask(int maxval); + +/*=========================================================================*/ +/* The following prototype is for the file: SHAKESCR.ASM */ +/*=========================================================================*/ + +void __cdecl Shake_Screen(int shakes); + +/*=========================================================================*/ +/* The following prototypes are for the file: REVERSE.ASM */ +/*=========================================================================*/ + +long __cdecl Reverse_Long(long number); +short __cdecl Reverse_Short(short number); +long __cdecl Swap_Long(long number); +#if (0) +/*=========================================================================*/ +/* The following prototype is for the file: FACING8.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing8(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACING16.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing16(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FACINGFF.ASM */ +/*=========================================================================*/ + +int __cdecl Desired_Facing256(int x1, int y1, int x2, int y2); + +/*=========================================================================*/ +/* The following prototype is for the file: FADING.ASM */ +/*=========================================================================*/ +#endif + +void * __cdecl Build_Fading_Table(void const *palette, void const *dest, long int color, long int frac); +/*=========================================================================*/ +/* The following prototype is for the file: CRC.ASM */ +/*=========================================================================*/ + +long __cdecl Calculate_CRC(void *buffer, long length); + +/*=========================================================================*/ +/* The following prototypes are for the file: DETPROC.ASM */ +/*=========================================================================*/ + +extern WORD __cdecl Processor(void); +extern WORD __cdecl Operating_System(void); +extern unsigned long random ( unsigned long mod ) ; +//extern void randomize ( void ) ; + +extern int __cdecl Clip_Rect ( int * x , int * y , int * dw , int * dh , + int width , int height ) ; +extern int __cdecl Confine_Rect ( int * x , int * y , int dw , int dh , + int width , int height ) ; + + + +/*=========================================================================*/ +/* The following prototypes are for the file: OPSYS.ASM */ +/*=========================================================================*/ + +extern WORD OperationgSystem; + +#ifdef __cplusplus +} +#endif + +/*=========================================================================*/ + +#endif // MISC_H diff --git a/code/redalert/win32lib/modemreg.h b/code/redalert/win32lib/modemreg.h new file mode 100644 index 0000000..4e3b81a --- /dev/null +++ b/code/redalert/win32lib/modemreg.h @@ -0,0 +1,69 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifndef WIN32 +#define WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif //WIN32 +#include + + + +class ModemRegistryEntryClass { + + public: + + ModemRegistryEntryClass (int modem_number); + ~ModemRegistryEntryClass (void); + + + char *Get_Modem_Name (void) { return (ModemName); } + + char *Get_Modem_Device_Name (void) { return (ModemDeviceName); } + + char *Get_Modem_Error_Correction_Enable (void) { return (ErrorCorrectionEnable); } + + char *Get_Modem_Error_Correction_Disable (void) { return (ErrorCorrectionDisable); } + + char *Get_Modem_Compression_Enable (void) { return (CompressionEnable); } + + char *Get_Modem_Compression_Disable (void) { return (CompressionDisable); } + + char *Get_Modem_Hardware_Flow_Control (void) { return (HardwareFlowControl); } + + char *Get_Modem_No_Flow_Control (void) { return (HardwareFlowControl); } + + private: + + char *ModemName; + char *ModemDeviceName; + char *ErrorCorrectionEnable; + char *ErrorCorrectionDisable; + char *CompressionEnable; + char *CompressionDisable; + char *HardwareFlowControl; + char *NoFlowControl; + +}; + + + + + + + diff --git a/code/redalert/win32lib/mono.h b/code/redalert/win32lib/mono.h new file mode 100644 index 0000000..3a5baa1 --- /dev/null +++ b/code/redalert/win32lib/mono.h @@ -0,0 +1,182 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\monoc.h_v 2.16 06 Sep 1995 16:29:02 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : MONO.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : July 2, 1994 * + * * + * Last Update : July 2, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef MONOC_H +#define MONOC_H + + +class MonoClass { + /* + ** This is a private structure that is used to control which characters + ** are used when a box is drawn. Line drawing on the monochrome screen is + ** really made up of characters. This specifies which characters to use. + */ + typedef struct { + char UpperLeft; + char TopEdge; + char UpperRight; + char RightEdge; + char BottomRight; + char BottomEdge; + char BottomLeft; + char LeftEdge; + } BoxDataType; + + /* + ** Each cell is constructed of the actual character that is displayed and the + ** attribute to use. This character pair is located at every position on the + ** display (80 x 25). Since this cell pair can be represented by a "short" + ** integer, certain speed optimizations are taken in the monochrome drawing + ** code. + */ + typedef struct { + char Character; // Character to display. + char Attribute; // Attribute. + } CellType; + + /* + ** These private constants are used in the various monochrome operations. + */ + enum MonoClassPortEnums { + CONTROL_PORT=0x03B4, // CRTC control register. + DATA_PORT=0x03B5, // CRTC data register. + COLUMNS=80, // Number of columns. + LINES=25, // Number of lines. + SIZE_OF_PAGE=LINES*COLUMNS*sizeof(CellType), // Entire page size. + DEFAULT_ATTRIBUTE=0x02 // Normal white on black color attribute. + }; + + public: + enum MonoClassPageEnums { + MAX_MONO_PAGES=16, // Maximum RAM pages on mono card. + SEGMENT=0xB000 // Monochrome screen segment. + }; + + /* + ** These are the various box styles that may be used. + */ + typedef enum BoxStyleType { + SINGLE, // Single thickness. + DOUBLE_HORZ, // Double thick on the horizontal axis. + DOUBLE_VERT, // Double thick on the vertical axis. + DOUBLE, // Double thickness. + + COUNT + } BoxStyleType; + + MonoClass(void); + ~MonoClass(void); + + static void Enable(void) {Enabled = 1;}; + static void Disable(void) {Enabled = 0;}; + static int Is_Enabled(void) {return Enabled;}; + static MonoClass * Get_Current(void) {return PageUsage[0];}; + + void Draw_Box(int x, int y, int w, int h, char attrib=DEFAULT_ATTRIBUTE, BoxStyleType thick=SINGLE); + void Set_Default_Attribute(char attrib) {Attrib = attrib;}; + void Clear(void); + void Set_Cursor(int x, int y); + void Print(char const *text); + void Print(int text); + void Printf(char const *text, ...); + void Printf(int text, ...); + void Text_Print(char const *text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void Text_Print(int text, int x, int y, char attrib=DEFAULT_ATTRIBUTE); + void View(void); + int Get_X(void) const {return X;}; + int Get_Y(void) const {return Y;}; + + /* + ** Handles deep copies for the mono class objects. This performs what is essentially + ** a screen copy. + */ + MonoClass & operator = (MonoClass const & ); + + /* + ** This merely makes a duplicate of the mono object into a newly created mono + ** object. + */ + MonoClass (MonoClass const &); + + private: + char X; // Cursor X position. + char Y; // Cursor Y position. + char Attrib; // Normal attribute to use if none specified. + char Page; // The current page to write to. + + /* + ** Helper functions to help with display operations. + */ + int Offset(int x=0, int y=0) const {return (SIZE_OF_PAGE*Page) + sizeof(CellType)*(x + (y*COLUMNS));}; + void Scroll(int lines); + void Store_Cell(CellType &cell, int x, int y) { + *(CellType *)((long)MonoSegment + Offset(x, y)) = cell; + }; + + /* + ** This is the segment/selector of the monochrome screen. + */ + static void * MonoSegment; + + /* + ** This the the arrays of characters used for drawing boxes. + */ + static BoxDataType const CharData[4]; + + /* + ** This array contains pointers to the monochrome objects that are assigned + ** to each of the monochrome pages. As the monochrome pages are made visible, + ** they can be shuffled around between the actual locations. The first entry + ** in this table is the one that is visible. + */ + static MonoClass * PageUsage[MAX_MONO_PAGES]; + + /* + ** If this is true, then monochrome output is allowed. It defaults to false + ** so that monochrome output must be explicitly enabled. + */ + static int Enabled; +}; + +void Mono_Set_Cursor(int x, int y); +int Mono_Printf(char const *string, ...); +void Mono_Clear_Screen(void); +void Mono_Text_Print(void const *text, int x, int y, int attrib); +void Mono_Draw_Rect(int x, int y, int w, int h, int attrib, int thick); +void Mono_Print(void const *text); +int Mono_X(void); +int Mono_Y(void); + +#endif + diff --git a/code/redalert/win32lib/mouse.h b/code/redalert/win32lib/mouse.h new file mode 100644 index 0000000..e84b473 --- /dev/null +++ b/code/redalert/win32lib/mouse.h @@ -0,0 +1,150 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 Bit Library * + * * + * File Name : MOUSE.H * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WW_MOUSE_H +#define WW_MOUSE_H + +#include + +#define MAX_CURSOR_FRAMES 221 + +class WWMouseClass { + public: + WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height); + ~WWMouseClass(); + void *Set_Cursor(int xhotspot, int yhotspot, int cursor); + void Process_Mouse(void); + void Hide_Mouse(void); + void Show_Mouse(void); + void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); + void Conditional_Show_Mouse(void); + int Get_Mouse_State(void); + int Get_Mouse_X(void); + int Get_Mouse_Y(void); + void Get_Mouse_XY(int &x, int &y); + // + // The following two routines can be used to render the mouse onto a graphicbuffer + // other than the hidpage. + // + void Draw_Mouse(GraphicViewPortClass *scr); + void Erase_Mouse(GraphicViewPortClass *scr, int forced = FALSE); + + void Block_Mouse(GraphicBufferClass *buffer); + void Unblock_Mouse(GraphicBufferClass *buffer); + void Set_Cursor_Clip(void); + void Clear_Cursor_Clip(void); + +// jmarshall - accessor functions to hook up mouse code from chronoshift. + int Get_Hotspot_X(void) { return MouseXHot; } + int Get_Hotspot_Y(void) { return MouseYHot; } + int Get_Cursor_Width(void) { return CursorWidth; } + int Get_Cursor_Height(void) { return CursorHeight; } + int Get_Max_Width(void) { return MaxWidth; } + int Get_Max_Height(void) { return MaxHeight; } + void Set_Hotspot_X(int hotspotX) { MouseXHot = hotspotX; } + void Set_Hotspot_Y(int hotspotY) { MouseYHot = hotspotY; } + void Set_Cursor_Width(int width) { CursorWidth = width; } + void Set_Cursor_Height(int height) { CursorHeight = height; } + void Set_Max_Width(int width) { MaxWidth = width; } + void Set_Max_Height(int height) { MaxHeight = height; } + + char* Get_Image_Buff(void) { return MouseCursor; } + void* Get_Frame_Pointer() { return PrevCursor; } + void Set_Frame_Pointer(void* frame) { PrevCursor = (char *)frame; } + + void RenderMouse(void); +// jmarshall end + private: + enum { + CONDHIDE = 1, + CONDHIDDEN = 2, + }; + void Low_Hide_Mouse(void); + void Low_Show_Mouse(int x, int y); + + char *MouseCursor; // pointer to the mouse cursor in memory + int MouseXHot; // X hot spot of the current mouse cursor + int MouseYHot; // Y hot spot of the current mouse cursor + int CursorWidth; // width of the mouse cursor in pixels + int CursorHeight; // height of the mouse cursor in pixels + + char *MouseBuffer; // pointer to background buffer in memory + int MouseBuffX; // pixel x mouse buffer was preserved at + int MouseBuffY; // pixel y mouse buffer was preserved at + int MaxWidth; // maximum width of mouse background buffer + int MaxHeight; // maximum height of mouse background buffer + + int MouseCXLeft; // left x pos if conditional hide mouse in effect + int MouseCYUpper; // upper y pos if conditional hide mouse in effect + int MouseCXRight; // right x pos if conditional hide mouse in effect + int MouseCYLower; // lower y pos if conditional hide mouse in effect + char MCFlags; // conditional hide mouse flags + char MCCount; // nesting count for conditional hide mouse + + GraphicViewPortClass *Screen; // pointer to the surface mouse was init'd with + char * PrevCursor; // pointer to previous cursor shape + int MouseUpdate; + int State; + + char *EraseBuffer; // Buffer which holds background to restore to hidden page + int EraseBuffX; // X position of the hidden page background + int EraseBuffY; // Y position of the hidden page background + int EraseBuffHotX; // X position of the hidden page background + int EraseBuffHotY; // Y position of the hidden page background + + int EraseFlags; // Records whether mutex has been released + + CRITICAL_SECTION MouseCriticalSection; // Control for mouse re-enterancy + unsigned TimerHandle; +// jmarshall +private: + struct Image_t* cursor_frames[MAX_CURSOR_FRAMES]; + struct Image_t* current_cursor; + bool isMouseVisible; +// jmarshall end +}; + +void __cdecl Mouse_Shadow_Buffer(WWMouseClass& mouse, GraphicViewPortClass& viewport, void* buffer, int x_pos, int y_pos, int hspot_x, int hspot_y, BOOL save); +void __cdecl Mouse_Draw(WWMouseClass& mouse, GraphicViewPortClass& viewport, int x_pos, int y_pos); +void* __cdecl Mouse_Set_Cursor(WWMouseClass& mouse, int hspot_x, int hspot_y, void* frame); + +void Hide_Mouse(void); +// jmarshall +void Show_Mouse(bool clearPrevious = true); +// jmarshall end +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2); +void Conditional_Show_Mouse(void); +int Get_Mouse_State(void); +void *Set_Mouse_Cursor(int hotx, int hoty, int cursor); +int Get_Mouse_X(void); +int Get_Mouse_Y(void); + +#endif diff --git a/code/redalert/win32lib/mouse.inc b/code/redalert/win32lib/mouse.inc new file mode 100644 index 0000000..c8031cb --- /dev/null +++ b/code/redalert/win32lib/mouse.inc @@ -0,0 +1,64 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*********************************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : MOUSE.INC * +;* * +;* Programmer : Philip W. Gorrow * +;* * +;* Start Date : 12/12/95 * +;* * +;* Last Update : December 12, 1995 [PWG] * +;* * +;*---------------------------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +STRUC MouseType +MouseCursor DD ? ; pointer to the mouse cursor in memory +MouseXHot DD ? ; X hot spot of the current mouse cursor +MouseYHot DD ? ; Y hot spot of the current mouse cursor +CursorWidth DD ? ; Width of mouse cursor in pixels +CursorHeight DD ? ; Height of the mouse cursor in pixels + +MouseBuffer DD ? ; pointer to background buffer in memory +MouseBuffX DD ? ; pixel x mouse buffer was preserved at +MouseBuffY DD ? ; pixel y mouse buffer was preserved at +MaxWidth DD ? ; Maximum possible width of the background buffer +MaxHeight DD ? ; Maximum possible height of the background buffer + +MouseCXLeft DD ? ; left hand x position if conditional hide mouse in effect +MouseCYUpper DD ? ; upper y position if conditional hide mouse in effect +MouseCXRight DD ? ; right hand x position if conditional hide mouse in effect +MouseCYLower DD ? ; lower y position if conditional hide mouse in effect +MCFlags DB ? ; conditional hide mouse flags +MCCount DB ? ; nesting count for conditional hide mouse + +Screen DD ? ; pointer to the surface mouse was init'd with +PrevCursor DD ? ; pointer to the prev cursor shape +MouseUpdate DD ? ; is the mouse being currently updated +State DD ? + +EraseBuffer DD ? +EraseBuffX DD ? +EraseBuffY DD ? +EraseBuffHotX DD ? +EraseBuffHotY DD ? +EraseFlags DD ? +ENDS diff --git a/code/redalert/win32lib/mouseshape.cpp b/code/redalert/win32lib/mouseshape.cpp new file mode 100644 index 0000000..6808c9f --- /dev/null +++ b/code/redalert/win32lib/mouseshape.cpp @@ -0,0 +1,304 @@ +/** + * @file + * + * @author OmniBlade + * @author CCHyper + * + * @brief Low level sprite drawing functions for Dune2 sprite format (used for mouse in early C&C games). + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +#include "../FUNCTION.H" +#include +#include "mouse.h" +#include "mouseshape.h" +#include "gbuffer.h" +//#include "lcw.h" +#include +#include + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +using std::memcpy; + +uint8_t g_mouseShapeBuffer[65000]; // Original code shares the global ShapeBuffer with other sprite drawing. + +int Get_Shape_Uncomp_Size(void* shape) +{ + return static_cast(shape)->data_length; +} + +int Extract_Shape_Count(void* shape) +{ + return static_cast(shape)->frame_count; +} + +void* Extract_Shape(void* shape, int frame) +{ + char* data = static_cast(shape); + MouseShapeHeader* header = static_cast(shape); + + if (shape && frame >= 0 && header->frame_count > frame) { + return data + (&header->offset)[frame] + 2; + } + + return nullptr; +} + +int Get_Shape_Width(void* shape) +{ + // return static_cast(shape)->Width; + return static_cast(shape)->width; +} + +int Get_Shape_Height(void* shape) +{ + return static_cast(shape)->height; +} + +//int Restore_Shape_Height(void* shape) +//{ +// int oldheight = static_cast(shape)->height; +// static_cast(shape)->height = static_cast(shape)->original_height; +// return oldheight; +//} + +int Get_Shape_Original_Height(void* shape) +{ + return static_cast(shape)->original_height; +} + +void __cdecl Mouse_Shadow_Buffer(WWMouseClass& mouse, GraphicViewPortClass& viewport, void* buffer, int x_pos, int y_pos, int hspot_x, int hspot_y, BOOL save) +{ + int xstart = x_pos - hspot_x; + int ystart = y_pos - hspot_y; + int yend = mouse.Get_Cursor_Height() + ystart - 1; + int xend = mouse.Get_Cursor_Width() + xstart - 1; + int ms_img_offset = 0; + uint8_t* mouse_img = static_cast(buffer); + + //If we aren't drawing within the viewport, return + if (xstart >= viewport.Get_Width() || + ystart >= viewport.Get_Height() || + xend <= 0 || yend <= 0) { + return; + } + + if (xstart < 0) { + ms_img_offset = -xstart; + xstart = 0; + } + + if (ystart < 0) { + mouse_img += mouse.Get_Cursor_Width() * (-ystart); + ystart = 0; + } + + xend = MIN(xend, viewport.Get_Width() - 1); + yend = MIN(yend, viewport.Get_Height() - 1); + + int blit_height = yend - ystart + 1; + int blit_width = xend - xstart + 1; + int src_pitch; + int dst_pitch; + uint8_t* src; + uint8_t* dst; + + // Bool will control if we are copying to or from shadow buffer, set vars here + if (save) { + src_pitch = (viewport.Get_Pitch() + viewport.Get_XAdd() + viewport.Get_Width()); + dst_pitch = mouse.Get_Cursor_Width(); + src = (uint8_t*)(viewport.Get_Offset()) + src_pitch * ystart + xstart; + dst = mouse_img + ms_img_offset; + } + else { + src_pitch = mouse.Get_Cursor_Width(); + dst_pitch = (viewport.Get_Pitch() + viewport.Get_XAdd() + viewport.Get_Width()); + src = mouse_img + ms_img_offset; + dst = (uint8_t*)(viewport.Get_Offset()) + dst_pitch * ystart + xstart; + } + + while (blit_height--) { + memcpy(dst, src, blit_width); + src += src_pitch; + dst += dst_pitch; + } +} + +void __cdecl Mouse_Draw(WWMouseClass& mouse, GraphicViewPortClass& viewport, int x_pos, int y_pos) +{ + int xstart = x_pos - mouse.Get_Hotspot_X(); + int ystart = y_pos - mouse.Get_Hotspot_Y(); + int yend = mouse.Get_Cursor_Height() + ystart - 1; + int xend = mouse.Get_Cursor_Width() + xstart - 1; + int ms_img_offset = 0; + uint8_t* mouse_img = (uint8_t * )mouse.Get_Image_Buff(); + + //If we aren't drawing within the viewport, return + if (xstart >= viewport.Get_Width() || + ystart >= viewport.Get_Height() || + xend <= 0 || yend <= 0) { + return; + } + + if (xstart < 0) { + ms_img_offset = -xstart; + xstart = 0; + } + + if (ystart < 0) { + mouse_img += mouse.Get_Cursor_Width() * (-ystart); + ystart = 0; + } + + xend = MIN(xend, viewport.Get_Width() - 1); + yend = MIN(yend, viewport.Get_Height() - 1); + + int pitch = viewport.Get_Pitch() + viewport.Get_XAdd() + viewport.Get_Width(); + uint8_t* dst = xstart + pitch * ystart + (uint8_t*)(viewport.Get_Offset()); + uint8_t* src = ms_img_offset + mouse_img; + int blit_pitch = xend - xstart + 1; + + if ((xend > xstart) && (yend > ystart)) { + int blit_height = yend - ystart + 1; + int dst_pitch = pitch - blit_pitch; + int src_pitch = mouse.Get_Cursor_Width() - blit_pitch; + + while (blit_height--) { + int blit_width = blit_pitch; + while (blit_width--) { + uint8_t current = *src++; + if (current) { + *dst = current; + } + + ++dst; + //--blit_width; + } + + src += src_pitch; + dst += dst_pitch; + //--blit_height; + } + } +} + +void* __cdecl Mouse_Set_Cursor(WWMouseClass& mouse, int hspot_x, int hspot_y, void* frame) +{ + uint8_t* frame_buff; + uint8_t* data_buff; + uint8_t* decmp_buff; + uint8_t* lcw_buff; + int16_t frame_flags; + + int height; + int width; + int uncompsz; + + MouseShapeFrameHeader* frame_header = static_cast(frame); + + //Get the dimensions of our frame, mouse shp format can have variable + //dimensions for each frame. + uncompsz = Get_Shape_Uncomp_Size(frame); + width = Get_Shape_Width(frame); + height = Get_Shape_Original_Height(frame); + + if (width <= mouse.Get_Max_Width() && height <= mouse.Get_Max_Height()) { + frame_buff = (uint8_t * )mouse.Get_Image_Buff(); + data_buff = static_cast(frame); + frame_flags = frame_header->flags; + + //Flag bit 2 is flag for no compression on frame, decompress to + //intermediate buffer if flag is clear + if (!(frame_header->flags & 2)) { + decmp_buff = g_mouseShapeBuffer; + lcw_buff = reinterpret_cast(frame_header); + frame_flags = frame_header->flags | 2; + + memcpy(decmp_buff, lcw_buff, sizeof(MouseShapeFrameHeader)); + decmp_buff += sizeof(MouseShapeFrameHeader); + lcw_buff += sizeof(MouseShapeFrameHeader); + + //Copies a small lookup table if it exists, probably not in RA. + if (frame_flags & 1) { + memcpy(decmp_buff, lcw_buff, 16); + decmp_buff += 16; + lcw_buff += 16; + } + + LCW_Uncomp(lcw_buff, decmp_buff, uncompsz); + data_buff = g_mouseShapeBuffer; + } + + if (frame_flags & 1) { + uint8_t* data_start = data_buff + sizeof(MouseShapeFrameHeader); + uint8_t* image_start = data_buff + sizeof(MouseShapeFrameHeader) + 16; + int image_size = height * width; + //uint8_t current_byte = 0; + int run_len = 0; + + while (image_size) { + uint8_t current_byte = *image_start++; + if (current_byte) { + *frame_buff++ = data_start[current_byte]; + --image_size; + continue; + } + + if (!image_size) { + break; + } + + run_len = *image_start; + image_size -= run_len; + ++image_start; + + while (run_len--) { + *frame_buff++ = 0; + } + } + } + else { + uint8_t* data_start = data_buff + sizeof(MouseShapeFrameHeader); + int image_size = height * width; + int run_len = 0; + + while (image_size) { + uint8_t current_byte = *data_start++; + if (current_byte) { + *frame_buff++ = current_byte; + --image_size; + continue; + } + + if (!image_size) { + break; + } + + run_len = *data_start; + image_size -= run_len; + ++data_start; + + while (run_len--) { + *frame_buff++ = 0; + } + } + } + + mouse.Set_Hotspot_X(hspot_x); + mouse.Set_Hotspot_Y(hspot_y); + mouse.Set_Cursor_Height(height); + mouse.Set_Cursor_Width(width); + } + + void* result = mouse.Get_Frame_Pointer(); + mouse.Set_Frame_Pointer(frame); + + return result; +} diff --git a/code/redalert/win32lib/mouseshape.h b/code/redalert/win32lib/mouseshape.h new file mode 100644 index 0000000..dbfded0 --- /dev/null +++ b/code/redalert/win32lib/mouseshape.h @@ -0,0 +1,67 @@ +/** + * @file + * + * @author OmniBlade + * @author CCHyper + * + * @brief Low level sprite drawing functions for Dune2 sprite format (used for mouse in early C&C games). + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ +#pragma once + +#ifndef MOUSESHAPE_H +#define MOUSESHAPE_H + +#pragma pack(push, 1) + // Header for each frame in the old SHP format... only used for mouse now. +struct MouseShapeFrameHeader +{ + //"MAKESHAPE_NORMAL" 0 + //"MAKESHAPE_COMPACT" 1 + //"MAKESHAPE_NOCOMP" 2 + //"MAKESHAPE_VARIABLE" 4 + uint16_t flags; // flag 1 specifies that a 16 byte color table follows the header. + // flag 2 specifies that this frame doesn't have LCW compression, + // this frame would only have RLE compression. + // flag 4 specifies a custom length color table follows the header. + uint8_t height; //height after RLE compression? + uint16_t width; + uint8_t original_height; // original frame height without any compression. + uint16_t shape_size; // full frame size including this header. + uint16_t data_length; // uncompressed size, frame size minus header if flag 2 is specified. + // + // Following this is optional data, in Dune 2 this was used to specify remap indexes. + // If flag 1 is set then a uint8_t color_table[16] follows. + // if flag 4 is set then a uint8_t color_table_size and uint8_t color_table[color_table_size] follows. +}; + +// Header for the old SHP format... only used for mouse now. Offset values need +2 to get actual frame pos. +struct MouseShapeHeader +{ + uint16_t frame_count; + uint32_t offset; +}; +#pragma pack(pop) + +class WWMouseClass; +class GraphicViewPortClass; + +int Get_Shape_Uncomp_Size(void* shape); +int Extract_Shape_Count(void* shape); +void* Extract_Shape(void* shape, int frame = 0); +int Get_Shape_Width(void* shape); +int Get_Shape_Height(void* shape); +int Restore_Shape_Height(void* shape); +int Get_Shape_Original_Height(void* shape); + +void __cdecl Mouse_Shadow_Buffer(WWMouseClass& mouse, GraphicViewPortClass& viewport, void* buffer, int x_pos, int y_pos, int hspot_x, int hspot_y, BOOL save); +void __cdecl Mouse_Draw(WWMouseClass& mouse, GraphicViewPortClass& viewport, int x_pos, int y_pos); +void* __cdecl Mouse_Set_Cursor(WWMouseClass& mouse, int hspot_x, int hspot_y, void* shape); + +#endif diff --git a/code/redalert/win32lib/mouseww.cpp b/code/redalert/win32lib/mouseww.cpp new file mode 100644 index 0000000..0d89cd9 --- /dev/null +++ b/code/redalert/win32lib/mouseww.cpp @@ -0,0 +1,587 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : MOUSE.CPP * + * * + * Programmer : Philip W. Gorrow * + * * + * Start Date : 12/12/95 * + * * + * Last Update : December 12, 1995 [PWG] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * WWMouseClass::WWMouseClass -- Constructor for the Mouse Class * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "imgui.h" +#include "../FUNCTION.H" +#include "MOUSE.H" +#include "../image.h" + +static WWMouseClass *_Mouse=NULL; +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +extern bool GameInFocus; +extern UserInputClass UserInput; + +/*********************************************************************************************** + * MOUSECLASS::MOUSECLASS -- Constructor for the Mouse Class * + * * + * INPUT: GraphicViewPortClass * screen - pointer to screen mouse is created for * + * * + * OUTPUT: none * + * * + * HISTORY: * + * 12/12/1995 PWG : Created. * + *=============================================================================================*/ +WWMouseClass::WWMouseClass(GraphicViewPortClass *scr, int mouse_max_width, int mouse_max_height) +{ + MouseCursor = new char[mouse_max_width * mouse_max_height]; + MouseXHot = 0; + MouseYHot = 0; + CursorWidth = 0; + CursorHeight = 0; + + MouseBuffer = new char[mouse_max_width * mouse_max_height]; + MouseBuffX = -1; + MouseBuffY = -1; + MaxWidth = mouse_max_width; + MaxHeight = mouse_max_height; + + MouseCXLeft = 0; + MouseCYUpper = 0; + MouseCXRight = 0; + MouseCYLower = 0; + MCFlags = 0; + MCCount = 0; + + Screen = scr; + PrevCursor = NULL; + MouseUpdate = 0; + State = 1; + timeBeginPeriod ( 1000/ 60); + + InitializeCriticalSection (&MouseCriticalSection); + // + // Install the timer callback event handler + // + + EraseBuffer = new char[mouse_max_width * mouse_max_height]; + EraseBuffX = -1; + EraseBuffY = -1; + EraseBuffHotX = -1; + EraseBuffHotY = -1; + EraseFlags = FALSE; + + _Mouse = this; + + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC); + //TimerHandle = timeSetEvent( 1000/60 , 1 , ::Process_Mouse, 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); // Removed. ST - 2/13/2019 5:12PM + + /* + ** Force the windows mouse pointer to stay withing the graphic view port region + */ + Set_Cursor_Clip(); + + for (int i = 0; i < MAX_CURSOR_FRAMES; i++) { + char mouseFrameName[512]; + sprintf(mouseFrameName, "ui/mouse/mouse-%05d.png", i); + + cursor_frames[i] = Image_LoadImage(mouseFrameName); + } + + isMouseVisible = false; +} + +WWMouseClass::~WWMouseClass() +{ + MouseUpdate++; + + if (MouseCursor) delete[] MouseCursor; + if (MouseBuffer) delete[] MouseBuffer; + if (TimerHandle) { + timeKillEvent(TimerHandle); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + timeEndPeriod (1000/60); + DeleteCriticalSection(&MouseCriticalSection); + + /* + ** Free up the windows mouse pointer movement + */ + Clear_Cursor_Clip(); +} + + +void Block_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Block_Mouse(buffer); + } +} + + +void Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (_Mouse){ + _Mouse->Unblock_Mouse(buffer); + } +} + + + +void WWMouseClass::Block_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + EnterCriticalSection(&MouseCriticalSection); + } +} + + +void WWMouseClass::Unblock_Mouse(GraphicBufferClass *buffer) +{ + if (buffer == Screen->Get_Graphic_Buffer()){ + LeaveCriticalSection(&MouseCriticalSection); + } +} + + + + + +void WWMouseClass::Set_Cursor_Clip(void) +{ +#if (0) // Not needed. ST - 1/3/2019 2:18PM + if (Screen){ + RECT region; + + region.left = 0; + region.top = 0; + region.right = Screen->Get_Width(); + region.bottom = Screen->Get_Height(); + + ClipCursor(®ion); + } +#endif +} + + + +void WWMouseClass::Clear_Cursor_Clip(void) +{ +#if (0) + ClipCursor(NULL); +#endif +} + + + +void WWMouseClass::Process_Mouse(void) +{ + +} + +void *WWMouseClass::Set_Cursor(int xhotspot, int yhotspot, int cursor) +{ + Set_Hotspot_X(xhotspot); + Set_Hotspot_Y(yhotspot); + + if (cursor >= MAX_CURSOR_FRAMES) { + assert(!"Max_Cursor_Frames out of range!"); + } + + current_cursor = cursor_frames[cursor]; + return NULL; +} + +void WWMouseClass::Low_Hide_Mouse() +{ + //if (!State) { + if (MouseBuffX != -1 || MouseBuffY != -1) { + if (Screen->Lock()){ + Mouse_Shadow_Buffer(*this, *Screen, MouseBuffer, MouseBuffX, MouseBuffY, MouseXHot, MouseYHot, 0); + Screen->Unlock(); + } + } + MouseBuffX = -1; + MouseBuffY = -1; + //} + + State++; +} +void WWMouseClass::Hide_Mouse() +{ + MouseUpdate++; + //Low_Hide_Mouse(); + isMouseVisible = false; + MouseUpdate--; +} + +void WWMouseClass::RenderMouse(void) { + if( this == NULL || !isMouseVisible) + return; + + //ImVec2 size(current_cursor->width, current_cursor->height); + // Image((ImTextureID)current_cursor->image, size); + int _my = UserInput.Mouse.Y + (current_cursor->height * 0.25); + int xstart = UserInput.Mouse.X - Get_Hotspot_X(); + int ystart = _my - Get_Hotspot_Y(); + int yend = current_cursor->height + ystart - 1; + int xend = current_cursor->width + xstart - 1; + + ImVec2 mi(xstart, ystart); + ImVec2 ma(xend, yend); + + + ImGui::SetMouseCursor(ImGuiMouseCursor_None); + ImGui::GetForegroundDrawList()->AddImage((ImTextureID)current_cursor->HouseImages[0].image[0][0], mi, ma); +} + +void WWMouseClass::Low_Show_Mouse(int x, int y) +{ + +} + +void WWMouseClass::Show_Mouse() +{ + isMouseVisible = true; +} + +void WWMouseClass::Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + POINT pt; + + MouseUpdate++; + + // + // First of all, adjust all the coordinates so that they handle + // the fact that the hotspot is not necessarily the upper left + // corner of the mouse. + // + x1 -= (CursorWidth - MouseXHot); + x1 = MAX(0, x1); + y1 -= (CursorHeight - MouseYHot); + y1 = MAX(0, y1); + x2 += MouseXHot; + x2 = MIN(x2, Screen->Get_Width()); + y2 += MouseYHot; + y2 = MIN(y2, Screen->Get_Height()); + + // The mouse could be in one of four conditions. + // 1) The mouse is visible and no conditional hide has been specified. + // (perform normal region checking with possible hide) + // 2) The mouse is hidden and no conditional hide as been specified. + // (record region and do nothing) + // 3) The mouse is visible and a conditional region has been specified + // (expand region and perform check with possible hide). + // 4) The mouse is already hidden by a previous conditional. + // (expand region and do nothing) + // + // First: Set or expand the region according to the specified parameters + if (!MCCount) { + MouseCXLeft = x1; + MouseCYUpper = y1; + MouseCXRight = x2; + MouseCYLower = y2; + } else { + MouseCXLeft = MIN(x1, MouseCXLeft); + MouseCYUpper = MIN(y1, MouseCYUpper); + MouseCXRight = MAX(x2, MouseCXRight); + MouseCYLower = MAX(y2, MouseCYLower); + } + // + // If the mouse isn't already hidden, then check its location against + // the hiding region and hide if necessary. + // + if (!(MCFlags & CONDHIDDEN)) { + GetCursorPos(&pt); + if (MouseBuffX >= MouseCXLeft && MouseBuffX <= MouseCXRight && MouseBuffY >= MouseCYUpper && MouseBuffY <= MouseCYLower) { + Low_Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } + } + // + // Record the fact that a conditional hide was called and then exit + // + // + MCFlags |= CONDHIDE; + MCCount++; + MouseUpdate--; + +} +void WWMouseClass::Conditional_Show_Mouse(void) +{ + MouseUpdate++; + + // + // if there are any nested hides then dec the count + // + if (MCCount) { + MCCount--; + // + // If the mouse is now not hidden and it had actually been + // hidden before then display it. + // + if (!MCCount) { + if (MCFlags & CONDHIDDEN) { + Show_Mouse(); + } + MCFlags = 0; + } + } + + MouseUpdate--; +} + + +void WWMouseClass::Draw_Mouse(GraphicViewPortClass *scr) +{ + scr; + return; +//ST - 1/3/2019 10:50AM +#if (0) + + POINT pt; + + if (State != 0) return; + MouseUpdate++; + // + // Get the position that the mouse is currently located at + // + GetCursorPos(&pt); + if (MCFlags & CONDHIDE && pt.x >= MouseCXLeft && pt.x <= MouseCXRight && pt.y >= MouseCYUpper && pt.y <= MouseCYLower) { + Hide_Mouse(); + MCFlags |= CONDHIDDEN; + } else { + // + // If the mouse is already visible then just ignore the problem. + // + EraseFlags = TRUE; + + // + // Try to lock the screen - dont do video stuff if we cant. + // + if (scr->Lock()){ + // + // Save off the area behind the mouse into two different buffers, one + // which will be used to restore the mouse and the other which will + // be used to restore the hidden surface when we get a chance. + // + Mouse_Shadow_Buffer(this, scr, EraseBuffer, pt.x, pt.y, MouseXHot, MouseYHot, 1); + memcpy(MouseBuffer, EraseBuffer, MaxWidth * MaxHeight); + // + // Draw the mouse in its new location + // + ::Draw_Mouse(this, scr, pt.x, pt.y); + // + // Save off the positions that we saved the buffer from + // + EraseBuffX = pt.x; + MouseBuffX = pt.x; + EraseBuffY = pt.y; + MouseBuffY = pt.y; + EraseBuffHotX = MouseXHot; + EraseBuffHotY = MouseYHot; + // + // Unlock the screen and lets get moving. + // + scr->Unlock(); + } + } + + MouseUpdate--; +#endif +} + +void WWMouseClass::Erase_Mouse(GraphicViewPortClass *scr, int forced) +{ +//ST - 1/3/2019 10:50AM + scr; + forced; + return; +#if (0) + // + // If we are forcing the redraw of a mouse we already managed to + // restore then just get outta here. + // + if (forced && EraseBuffX == -1 && EraseBuffY == -1) return; + + MouseUpdate++; + + // + // If this is not a forced call, only update the mouse is we can legally + // lock the buffer. + // + if (!forced) { +#if(0) + if (scr->Lock()) { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, 0); + EraseBuffX = -1; + EraseBuffY = -1; + } + // + // We are done writing to the buffer so unlock it. + // + scr->Unlock(); + } +#endif + } else { + // + // If the surface has not already been restore then restore it and erase the + // restoration coordinates so we don't accidentally do it twice. + // + if (EraseBuffX != -1 || EraseBuffY != -1) { + if (scr->Lock()){ + Mouse_Shadow_Buffer(this, scr, EraseBuffer, EraseBuffX, EraseBuffY, EraseBuffHotX, EraseBuffHotY, 0); + scr->Unlock(); + } + EraseBuffX = -1; + EraseBuffY = -1; + } + } + MouseUpdate--; + EraseFlags = FALSE; +#endif +} + +int WWMouseClass::Get_Mouse_State(void) +{ + return 0;//(State); +} +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_X -- Returns the mouses current x position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current x position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_X(void) +{ + return UserInput.Mouse.X; +} + + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_Y -- returns the mouses current y position in pixels * + * * + * INPUT: none * + * * + * OUTPUT: int - returns the mouses current y position in pixels * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +int WWMouseClass::Get_Mouse_Y(void) +{ + return UserInput.Mouse.Y; +} + +/*********************************************************************************************** + * WWKeyboardClass::Get_Mouse_XY -- Returns the mouses x,y position via reference vars * + * * + * INPUT: int &x - variable to return the mouses x position in pixels * + * int &y - variable to return the mouses y position in pixels * + * * + * OUTPUT: none - output is via reference variables * + * * + * HISTORY: * + * 10/17/1995 PWG : Created. * + *=============================================================================================*/ +void WWMouseClass::Get_Mouse_XY(int &x, int &y) +{ + x = UserInput.Mouse.X; + y = UserInput.Mouse.Y; +} + +//#pragma off(unreferenced) + +void CALLBACK Process_Mouse( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ) +{ + static BOOL in_mouse_callback = false; + + if (_Mouse && !in_mouse_callback) { + in_mouse_callback = TRUE; + _Mouse->Process_Mouse(); + in_mouse_callback = FALSE; + } +} +//#pragma on(unreferenced) + +void Hide_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Hide_Mouse(); +} + +void Show_Mouse(bool clearPrevious) +{ + if (!_Mouse) return; + + if(clearPrevious) + _Mouse->Hide_Mouse(); + + _Mouse->Show_Mouse(); +} + +void Conditional_Hide_Mouse(int x1, int y1, int x2, int y2) +{ + if (!_Mouse) return; + _Mouse->Conditional_Hide_Mouse(x1, y1, x2, y2); +} + +void Conditional_Show_Mouse(void) +{ + if (!_Mouse) return; + _Mouse->Conditional_Show_Mouse(); +} + +int Get_Mouse_State(void) +{ + if (!_Mouse) return(0); + return(_Mouse->Get_Mouse_State()); +} + +void *Set_Mouse_Cursor(int hotx, int hoty, int cursor) +{ + if (!_Mouse) return(0); + return(_Mouse->Set_Cursor(hotx,hoty,cursor)); +} + +extern int DLLForceMouseX; +extern int DLLForceMouseY; + +int Get_Mouse_X(void) +{ + return UserInput.Mouse.X; +} + +int Get_Mouse_Y(void) +{ + return UserInput.Mouse.Y; +} \ No newline at end of file diff --git a/code/redalert/win32lib/nybbtb.inc b/code/redalert/win32lib/nybbtb.inc new file mode 100644 index 0000000..48444c4 --- /dev/null +++ b/code/redalert/win32lib/nybbtb.inc @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;**************************************************************************** +;* bNybbleTablexxxx - ADPCM Lookup table for nybbles +;**************************************************************************** + + align 4 + +bNybbleTableLow DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + DB 00h*2,01h*2,02h*2,03h*2,04h*2,05h*2,06h*2,07h*2,08h*2,09h*2,0Ah*2,0Bh*2,0Ch*2,0Dh*2,0Eh*2,0Fh*2 + + align 4 + +bNybbleTableHigh DB 00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2,00h*2 + DB 01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2,01h*2 + DB 02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2,02h*2 + DB 03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2,03h*2 + DB 04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2,04h*2 + DB 05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2,05h*2 + DB 06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2,06h*2 + DB 07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2,07h*2 + DB 08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2,08h*2 + DB 09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2,09h*2 + DB 0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2,0Ah*2 + DB 0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2,0Bh*2 + DB 0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2,0Ch*2 + DB 0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2,0Dh*2 + DB 0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2,0Eh*2 + DB 0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2,0Fh*2 diff --git a/code/redalert/win32lib/palette.cpp b/code/redalert/win32lib/palette.cpp new file mode 100644 index 0000000..3c8b924 --- /dev/null +++ b/code/redalert/win32lib/palette.cpp @@ -0,0 +1,381 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PALETTE.C * + * * + * Programmer : BILL STOKES * + * * + * Start Date : 6/20/91 * + * * + * Last Update : August 2, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Note: This module contains dependencies upon the video system, * + * specifically Get_Video_Mode(). * + *-------------------------------------------------------------------------* + * Functions: * + * Set_Palette -- sets the current palette * + * Set_Palette_Color -- Set a color number in a palette to the data. * + * Fade_Palette_To -- Fades the current palette into another * + * Determine_Bump_Rate -- determines desired bump rate for fading * + * Bump_Palette -- increments the palette one step, for fading * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* +********************************* Includes ********************************** +*/ +//#include + +#include "palette.h" +#include "timer.h" +#include "wwstd.h" + + +/* +********************************* Constants ********************************* +*/ + +/* +********************************** Globals ********************************** +*/ +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + +/* +******************************** Prototypes ********************************* +*/ + +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, short *rate); +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step); + +/* +******************************** Code ********************************* +*/ + +/*************************************************************************** + * Set_Palette -- sets the current palette * + * * + * INPUT: * + * void *palette - palette to set * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette(void *palette) +{ + + #if(IBM) + Set_Palette_Range(palette); + #else + Copy_Palette(palette,CurrentPalette); + LoadRGB4(&Main_Screen->ViewPort,palette,32L); + LoadRGB4(AltVPort,palette,32L); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Set_Palette_Color -- Set a color number in a palette to the data. * + * * + * * + * INPUT: * + * void *palette - palette to set color in * + * int color - which color index to set * + * void *data - RGB data for color * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/25/1994 SKB : Created. * + * 04/27/1994 BR : Converted to 32-bit * + *=========================================================================*/ +void __cdecl Set_Palette_Color(void *palette, int color, void *data) +{ + /* + ---------------------- Return if 'palette' is NULL ----------------------- + */ + if (!palette) return; + + /* + ------------------- Change the color & set the palette ------------------- + */ + #if(IBM) + memcpy(&((unsigned char *)palette)[color * RGB_BYTES], data, RGB_BYTES); + Set_Palette_Range(palette); + #else + palette[color] = *(unsigned short*)data; + Set_Palette(palette); + #endif + +} /* end of Set_Palette */ + + +/*************************************************************************** + * Fade_Palette_To -- Fades the current palette into another * + * * + * This will allow the palette to fade from current palette into the * + * palette that was passed in. This can be used to fade in and fade out. * + * * + * INPUT: * + * char *palette1 - this is the palette to fade to. * + * unsigned int delay - fade with this timer count down * + * void *callback - user-defined callback function * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 06/20/1991 BS : Created. * + *=========================================================================*/ +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ) +{ + BOOL changed; // Flag that palette has changed this tick. + short jump; // Gun values to jump per palette set. + unsigned long timer; // Tick count timer used for timing. + short ticksper; // The ticks (fixed point) per bit jump. + int tickaccum; + + + extern void (*cb_ptr)(void); // callback function pointer + +// (void *)cb_ptr = callback; + cb_ptr = callback; + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return; + + /* + --------------------------- Get the bump rate ---------------------------- + */ + Determine_Bump_Rate(palette1, delay, &ticksper, &jump); + + tickaccum = 0; // init accumulated elapsed time + timer = TickCount.Time(); // timer = current time + do { + changed = FALSE; + + tickaccum += ticksper; // tickaccum = time of next change * 256 + timer += (tickaccum >> 8); // timer = time of next change (rounded) + tickaccum &= 0x0FF; // shave off high byte, keep roundoff bits + + changed = Bump_Palette(palette1, jump); // increment palette + + /* + .................. Wait for time increment to elapse .................. + */ + if (changed) { + while (TickCount.Time() < (int)timer) { + /* + ................. Update callback while waiting ................. + */ + if (callback) { +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + (*cb_ptr)(); + } + } + } + +#if LIB_EXTERNS_RESOLVED + Sound_Callback(); // should be removed! +#endif + if (callback) { + (*cb_ptr)(); + } + } while (changed); + +} /* end of Fade_Palette_To */ + + +/*************************************************************************** + * Determine_Bump_Rate -- determines desired bump rate for fading * + * * + * INPUT: * + * unsigned char *palette - palette to fade to * + * int delay - desired time delay in 60ths of a second * + * short *ticks - output: loop ticks per color jump * + * short *rate - output: color gun increment rate * + * * + * OUTPUT: * + * none * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Converted to 32-bit * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +PRIVATE void __cdecl Determine_Bump_Rate(void *palette, int delay, short *ticks, + short *rate) +{ + int gun1; // Palette 1 gun value. + int gun2; // Palette 2 gun value. + int diff; // Maximum color gun difference. + int tp; // Temporary tick accumulator. + int index; // Color gun working index. + long t; // Working tick intermediate value. + int adiff; // Absolute difference between guns. + + /* + ------------------------ Find max gun difference ------------------------- + */ + diff = 0; + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette)[index]; + gun2 = CurrentPalette[index]; + adiff = ABS(gun1-gun2); + diff = MAX(diff, adiff); + } + + /*------------------------------------------------------------------------ + ticks = (total time delay ) / (max gun diff) + The value is computed based on (delay * 256), for fixed-point math; + the lower bits represent the leftover from the division; 'ticks' is + returned still shifted, so the low bits can be used to accumulate the + time more accurately; the caller must shift the accumulated value down + 8 bits to determine the actual elapsed time! + ------------------------------------------------------------------------*/ + t = ((long)delay) << 8; + if (diff) { + t /= diff; + t = MIN((long)t, (long)0x7FFF); + } + *ticks = (short)t; + + /*------------------------------------------------------------------------ + Adjust the color gun rate value if the time to fade is faster than can + reasonably be performed given the palette change, ie if (ticks>>8)==0, + and thus less than 1/60 of a second + ------------------------------------------------------------------------*/ + tp = *ticks; + *rate = 1; + while (*rate <= diff && *ticks < 256) { + *ticks += tp; + *rate += 1; + } + +} /* end of Determine_Bump_Rate */ + + +/*************************************************************************** + * Bump_Palette -- increments the palette one step, for fading * + * * + * INPUT: * + * palette1 - palette to fade towards * + * step - max step amount, determined by Determine_Bump_Rate * + * * + * OUTPUT: * + * FALSE = no change, TRUE = changed * + * * + * WARNINGS: * + * * + * HISTORY: * + * 04/27/1994 BR : Created. * + * 08/02/1994 SKB : Made private * + *=========================================================================*/ +#if(IBM) +PRIVATE BOOL __cdecl Bump_Palette(void *palette1, unsigned int step) +{ + BOOL changed=FALSE; // Flag that palette has changed this tick. + int index; // Index to DAC register gun. + int gun1,gun2; // Palette 1 gun value. + unsigned char palette[PALETTE_BYTES]; // copy of current palette + + /* + ---------------------- Return if 'palette1' is NULL ---------------------- + */ + if (!palette1) + return (FALSE); + + + /* + ------------------------ Copy the current palette ------------------------ + */ + memcpy(palette, CurrentPalette, 768); + + /* + ----------------------- Loop through palette bytes ----------------------- + */ + for (index = 0; index < PALETTE_BYTES; index++) { + gun1 = ((unsigned char *)palette1)[index]; + gun2 = palette[index]; + + /* + ............. If the colors match, go on to the next one .............. + */ + if (gun1 == gun2) continue; + + changed = TRUE; + + /* + .................. Increment current palette's color .................. + */ + if (gun2 < gun1) { + gun2 += step; + gun2 = MIN(gun2, gun1); // make sure we didn't overshoot it + } + /* + .................. Decrement current palette's color .................. + */ + else { + gun2 -= step; + gun2 = MAX(gun2, gun1); // make sure we didn't overshoot it + } + + palette[index] = (unsigned char)gun2; + } + + /* + ----------------- Set current palette to the new palette ----------------- + */ + if (changed) { + Set_Palette(&palette[0]); + } + + return (changed); + +} /* end of Bump_Palette */ + +#else + + /* This is already implemented in asm on the Amiga */ + +#endif + +void (*cb_ptr)(void); // callback function pointer + +/**************************** End of palette.cpp ***************************/ + + + \ No newline at end of file diff --git a/code/redalert/win32lib/palette.h b/code/redalert/win32lib/palette.h new file mode 100644 index 0000000..6087d0d --- /dev/null +++ b/code/redalert/win32lib/palette.h @@ -0,0 +1,82 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Palette 32bit Library. * +;* * +;* File Name : PALETTE.H * +;* * +;* Programmer : Scott K. Bowen * +;* * +;* Start Date : April 25, 1994 * +;* * +;* Last Update : April 27, 1994 [BRR] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef PALETTE_H +#define PALETTE_H + +#include +/* +********************************* Constants ********************************* +*/ +#define RGB_BYTES 3 +#define PALETTE_SIZE 256 +#define PALETTE_BYTES 768 + +/* +******************************** Prototypes ********************************* +*/ +/* +-------------------------------- Palette.cpp -------------------------------- +*/ +void __cdecl Set_Palette(void *palette); +void __cdecl Set_Palette_Color(void *palette, int color, void *data); +void Fade_Palette_To(void *palette1, unsigned int delay, void (*callback)() ); + +/* +-------------------------------- loadpal.cpp -------------------------------- +*/ +void __cdecl Load_Palette(char *palette_file_name, void *palette_pointer); + +/* +------------------------------- morphpal.cpp -------------------------------- +*/ +void __cdecl Morph_Palette (void *src_palette, void *dst_palette, unsigned int delay, + void *callback); + +/* +---------------------------------- pal.asm ---------------------------------- +*/ +#ifdef __cplusplus +extern "C" { +#endif + +extern void __cdecl Set_Palette_Range(void *palette); +extern BOOL __cdecl Bump_Color(void *palette, int changable, int target); + +#ifdef __cplusplus +} +#endif +extern "C" extern unsigned char CurrentPalette[]; /* in pal.asm */ + + +#endif // PALETTE_H + +/***************************** End of palette.h ****************************/ diff --git a/code/redalert/win32lib/playcd.h b/code/redalert/win32lib/playcd.h new file mode 100644 index 0000000..0c5a68d --- /dev/null +++ b/code/redalert/win32lib/playcd.h @@ -0,0 +1,306 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WWLIB * + * * + * File Name : PLAYCD.H * + * * + * Programmer : STEVE WETHERILL * + * * + * Start Date : 5/13/94 * + * * + * Last Update : June 4, 1994 [SW] * + * * + *-------------------------------------------------------------------------*/ + +#ifndef PLAYCD_H +#define PLAYCD_H + + +#ifdef NOT_FOR_WIN95 +/* ==================================================================== */ +/* Defines */ +/* ==================================================================== */ + +#define CHLEFT 0 +#define CHRIGHT 1 +#define CHBOTH 2 + +#define AUDIO_START_MIN 1 +#define AUDIO_START_SEC 44 + +typedef struct { + unsigned short seg ; + unsigned short sel ; + } SEGSEL ; + + + + +extern "C" int DPMI_real_alloc ( UINT , SEGSEL * , USHORT * ) ; +extern "C" int DPMI_real_free ( SEGSEL ) ; +extern "C" void DPMI_real_intr ( int , union REGS * , struct SREGS * ); +extern "C" void DPMI_real_call ( void * funct , union REGS * , struct SREGS * ); + + + + +/* ==================================================================== */ +/* Data structures */ +/* ==================================================================== */ + +// Audio Track Info request block + +struct TinfoType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE Track; + ULONG Start; + UBYTE TrCtrl; +}; + +// Audio Track Status Control Block + +struct StatType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE StatInfo; + UWORD Stat; + ULONG Start; + ULONG End; + }; + +// Audio Track Volume control block + +struct VolmType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE MDescr; + + UWORD TrnsAdOff; + UWORD TrnsAdSeg; + + UWORD CntTrns; + UWORD StSect; + + UWORD VolIDOff; + UWORD VolIDSeg; + + UBYTE TrInfo; + UBYTE In0; + UBYTE Vol0; + UBYTE In1; + UBYTE Vol1; + UBYTE In2; + UBYTE Vol2; + UBYTE In3; + UBYTE Vol3; + }; + +// Audio Track Play request block + +struct PlayType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + UBYTE AddrMd; + ULONG Start; + ULONG CntSect; + }; + + +// Audio Track Stop request block + +struct StopType { + UBYTE Length; + UBYTE SubCd; + UBYTE Command; + UWORD Status; + UBYTE Rsvd[8]; + }; + +#endif //NOT_FOR_WIN95 + +/*************************************************************************** + * GetCDClass -- object which will return logical CD drive * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#define MAX_CD_DRIVES 26 +#define NO_CD_DRIVE -1 + +class GetCDClass { + +protected: + + int CDDrives[MAX_CD_DRIVES]; //Array containing CD drive letters + int CDCount; //Number of available CD drives + int CDIndex; + +public: + + + GetCDClass(VOID); // This is the default constructor + ~GetCDClass(VOID); // This is the destructor + + inline int Get_First_CD_Drive(void); + inline int Get_Next_CD_Drive(void); + inline int Get_Number_Of_Drives(void) {return (CDCount);}; + +}; + + + +/*********************************************************************************************** + * GCDC::Get_Next_CD_Drive -- return the logical drive number of the next CD drive * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Logical drive number of a cd drive or -1 if none * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/21/96 3:50PM ST : Created * + *=============================================================================================*/ +inline int GetCDClass::Get_Next_CD_Drive(void) +{ + if (CDCount){ + if (CDIndex == CDCount) CDIndex = 0; + return (CDDrives[CDIndex++]); + }else{ + return (-1); + } +} + + + +/*************************************************************************** + * GCDC::Get_First_CD_Drive -- return the number of the first CD drive * + * * + * * + * * + * INPUT: * + * none * + * OUTPUT: * + * logical drive number * + * WARNINGS: * + * * + * HISTORY: * + * 05/26/1994 SW : Created. * + * 12/4/95 ST : fixed for Win95 * + *=========================================================================*/ +inline int GetCDClass::Get_First_CD_Drive(void) +{ + CDIndex = 0; + return (Get_Next_CD_Drive()); +} + + + + + + +/*************************************************************************** + * RedBookClass -- adds red book functionality * + * * + * this class inherits from GetCDClass and adds red book play functionality* + * * + * * + * HISTORY: * + * 06/04/1994 SW : Created. * + *=========================================================================*/ + +#ifdef NOT_FOR_WIN95 +class RedBookClass : public GetCDClass { + +private: + + SEGSEL Tinfo_addrp; + SEGSEL Stat_addrp; + SEGSEL Stop_addrp; + SEGSEL Volm_addrp; + SEGSEL Play_addrp; + + StopType Stop; + PlayType Play; + VolmType Volm; + StatType Stat; + TinfoType Tinfo; + +public: + + RedBookClass(VOID); // This is the default constructor + ~RedBookClass(VOID); // This is the destructor + + ULONG RedToHS(ULONG i); + ULONG MSFtoRed(UBYTE m, UBYTE s, UBYTE f); + VOID FullCDVolume(UBYTE chan); + VOID PlayTrack(UWORD track); + VOID Play_CD_MSL(UWORD min_sec, UWORD len); + VOID PlayMSF(UBYTE startM, UBYTE startS, UBYTE startF, + UBYTE endM, UBYTE endS, UBYTE endF, UBYTE chan); + UWORD CheckCDMusic(VOID); + VOID StopCDMusic(VOID); + +}; + +#endif //NOT_FOR_WIN95 +/***************************** End of Playcd.h ****************************/ + +#endif // PLAYCD_H + diff --git a/code/redalert/win32lib/profile.h b/code/redalert/win32lib/profile.h new file mode 100644 index 0000000..bc20add --- /dev/null +++ b/code/redalert/win32lib/profile.h @@ -0,0 +1,102 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Library profiler * + * * + * File Name : PROFILE.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 11/17/95 * + * * + * Last Update : November 20th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * New System * + * ~~~~~~~~~~~ * + * * + * The new profiler system creates a seperate thread and then starts a timer off there. The * + * timer in the second thread uses GetThreadContext to sample the IP address of each user * + * thread. This system has the advantage of being able to sample what is happening in all the * + * threads we own not just the main thread. Another advantage is that it doesnt require a * + * major recompilation. * + * The disadvantage is that we dont really know what is going on when the IP is outside the * + * scope of our threads - We could be in direct draw, direct sound or even something like the * + * VMM and there is no way to tell. * + * * + * * + * * + * Old System * + * ~~~~~~~~~~~ * + * * + * The profiler works by using the function prologue and epilogue hooks available in Watcom * + * to register the current functions address in a global variable and then sampling the * + * contents of the variable using a windows timer which runs at up to 1000 samples per second.* + * * + * Compile the code to be sampled with the -ep and -ee flags to enable the prologue (__PRO) * + * and epilogue (__EPI) calls to be generated. * + * At the beginning of the section to be profiled (just before main loop normally) call the * + * Start_Profiler function to start sampling. At the end of the section, call Stop_Profiler * + * which will stop the timer and write the profile data to disk in the PROFILE.BIN file. * + * * + * Use PROFILE.EXE to view the results of the session. * + * * + * The addition of prologue and epilogue code will slow down the product and the profiler * + * allocates a huge buffer for data so it should not be linked in unless it is going to be * + * used. * + * * + * The advantage of the prologue/epilogue approach is that all samples represent valid * + * addresses within our code so we get valid results we can use even when the IP is in system * + * code. * + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#define MAX_PROFILE_TIME 60*1 //1 minute(s) @ 14.4 Mb per hour +#define PROFILE_RATE 1000 //samples per sec (max 1000) + + +/* + * Defines for choosing between the old and new profiler system + * +*/ + +#define OLD_PROFILE_SYSTEM 1 +#define NEW_PROFILE_SYSTEM 2 + +//#define PROFILE_SYSTEM OLD_PROFILE_SYSTEM +#define PROFILE_SYSTEM NEW_PROFILE_SYSTEM + + + +extern "C"{ + void __cdecl Profile_Init(void); + void __cdecl Profile_End(void); + void __cdecl Start_Profiler(void); + void __cdecl Stop_Profiler(void); +} + diff --git a/code/redalert/win32lib/profile.inc b/code/redalert/win32lib/profile.inc new file mode 100644 index 0000000..ec99294 --- /dev/null +++ b/code/redalert/win32lib/profile.inc @@ -0,0 +1,41 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + + +;USE_PROFILER =1 + + +macro prologue +Ifdef USE_PROFILER + global __PRO:near + + call __PRO +endif ;USE_PROFILER +endm + + + +macro epilogue +ifdef USE_PROFILER + global __EPI:near + + push ebp + call __EPI + pop ebp +endif ;USE_PROFILER +endm + + + diff --git a/code/redalert/win32lib/rawfile.h b/code/redalert/win32lib/rawfile.h new file mode 100644 index 0000000..a658390 --- /dev/null +++ b/code/redalert/win32lib/rawfile.h @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\rawfile.h_v 2.15 06 Sep 1995 16:29:30 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : RAWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : October 18, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef RAWFILE_H +#define RAWFILE_H + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include + +//#include +#include +#include +#include +//#include +#include "wwfile.h" + +#ifdef NEVER + /* + ** This is a duplicate of the error numbers. The error handler for the RawFileClass handles + ** these errors. If the error routine is overridden and additional errors are defined, then + ** use numbers starting with 100. Note that these errors here are listed in numerical order. + ** These errors are defined in the standard header file "ERRNO.H". + */ + EZERO, // Non-error. + EINVFNC, // Invalid function number. + ENOFILE, // File not found. + ENOENT=ENOFILE, // No such file or directory. + ENOPATH, // Path not found. + EMFILE, // Too many open files. + EACCES, // Permission denied. + EBADF, // Bad file number. + ECONTR, // Memory blocks destroyed. + ENOMEM, // Not enough core memory. + EINVMEM, // Invalid memory block address. + EINVENV, // Invalid environment. + EINVFMT, // Invalid format. + EINVACC, // Invalid access code. + EINVDAT, // Invalid data. + EFAULT, // Unknown error. + EINVDRV, // Invalid drive specified. + ENODEV=EINVDRV, // No such device. + ECURDIR, // Attempt to remove CurDir. + ENOTSAM, // Not same device. + ENMFILE, // No more files. + EINVAL, // Invalid argument. + E2BIG, // Argument list too long. + ENOEXEC, // exec format error. + EXDEV, // Cross-device link. + ENFILE, // Too many open files. + ECHILD, // No child process. + ENOTTY, // not used + ETXTBSY, // not used + EFBIG, // not used + ENOSPC, // No space left on device. + ESPIPE, // Illegal seek. + EROFS, // Read-only file system. + EMLINK, // not used + EPIPE, // Broken pipe. + EDOM, // Math argument. + ERANGE, // Result too large. + EEXIST, // File already exists. + EDEADLOCK, // Locking violation. + EPERM, // Operation not permitted. + ESRCH, // not used + EINTR, // Interrupted function call. + EIO, // Input/output error. + ENXIO, // No such device or address. + EAGAIN, // Resource temporarily unavailable. + ENOTBLK, // not used + EBUSY, // Resource busy. + ENOTDIR, // not used + EISDIR, // not used + EUCLEAN, // not used +#endif + +/* +** This is the definition of the raw file class. It is derived from the abstract base FileClass +** and handles the interface to the low level DOS routines. This is the first class in the +** chain of derived file classes that actually performs a useful function. With this class, +** I/O is possible. More sophisticated features, such as packed files, CD-ROM support, +** file caching, and XMS/EMS memory support, are handled by derived classes. +** +** Of particular importance is the need to override the error routine if more sophisticated +** error handling is required. This is more than likely if greater functionality is derived +** from this base class. +*/ +class RawFileClass : public FileClass +{ + public: + + /* + ** This is a record of the access rights used to open the file. These rights are + ** used if the file object is duplicated. + */ + int Rights; + + RawFileClass(char const *filename); + RawFileClass(void); + RawFileClass (RawFileClass const & f); + RawFileClass & operator = (RawFileClass const & f); + virtual ~RawFileClass(void) {if (Allocated && Filename) free((char *)Filename);}; + + virtual char const * File_Name(void) const; + virtual char const * Set_Name(char const *filename); + virtual int Create(void); + virtual int Delete(void); + virtual int Is_Available(int forced=false); + virtual int Is_Open(void) const; + virtual int Open(char const *filename, int rights=READ); + virtual int Open(int rights=READ); + virtual long Read(void *buffer, long size); + virtual long Seek(long pos, int dir=SEEK_CUR); + virtual long Size(void); + virtual long Write(void const *buffer, long size); + virtual void Close(void); + virtual void Error(int error, int canretry = false, char const * filename=NULL); + virtual void Set_Buffer_Size(int size); + + + protected: + + /* + ** This function returns the largest size a low level DOS read or write may + ** perform. Larger file transfers are performed in chunks of this size or less. + */ + long Transfer_Block_Size(void) {return (long)((unsigned)UINT_MAX)-16L;}; + + private: + + /* + ** This is the low level DOS handle. A -1 indicates an empty condition. + */ + int Handle; + + /* + ** This points to the filename as a NULL terminated string. It may point to either a + ** constant or an allocated string as indicated by the "Allocated" flag. + */ + char const * const Filename; + + /* + ** Filenames that were assigned as part of the construction process + ** are not allocated. It is assumed that the filename string is a + ** constant in that case and thus making duplication unnecessary. + ** This value will be non-zero if the filename has be allocated + ** (using strdup()). + */ + unsigned Allocated:1; +}; + + +/*********************************************************************************************** + * RawFileClass::File_Name -- Returns with the filename associate with the file object. * + * * + * Use this routine to determine what filename is associated with this file object. If no * + * filename has yet been assigned, then this routing will return NULL. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with a pointer to the file name associated with this file object or NULL * + * if one doesn't exist. * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline char const * RawFileClass::File_Name(void) const +{ + return Filename; +} + +/*********************************************************************************************** + * RawFileClass::RawFileClass -- Default constructor for a file object. * + * * + * This constructs a null file object. A null file object has no file handle or filename * + * associated with it. In order to use a file object created in this fashion it must be * + * assigned a name and then opened. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline RawFileClass::RawFileClass(void) : Filename(0) +{ + Handle = -1; + Allocated = false; +} + +/*********************************************************************************************** + * RawFileClass::Is_Open -- Checks to see if the file is open or not. * + * * + * Use this routine to determine if the file is open. It returns true if it is. * + * * + * INPUT: none * + * * + * OUTPUT: bool; Is the file open? * + * * + * * + * WARNINGS: none * + * * + * HISTORY: * +;* 10/18/1994 JLB : Created. * + *=============================================================================================*/ +inline int RawFileClass::Is_Open(void) const +{ + return (Handle != -1); +} + +#endif diff --git a/code/redalert/win32lib/shape.h b/code/redalert/win32lib/shape.h new file mode 100644 index 0000000..44d8050 --- /dev/null +++ b/code/redalert/win32lib/shape.h @@ -0,0 +1,184 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 * + * * + * File Name : SHAPE.H * + * * + * Programmer : Bill Randolph * + * * + * Start Date : May 25, 1994 * + * * + * Last Update : September 14, 1994 [IML] * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef SHAPE_H +#define SHAPE_H + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif +/* +*********************************** Types *********************************** +*/ +/* +--------------------------- Shape creation flags ---------------------------- +*/ +typedef enum : unsigned short { + MAKESHAPE_NORMAL = 0x0000, // 256-color compressed shape + MAKESHAPE_COMPACT = 0x0001, // 16-color shape (with built-in color table) + MAKESHAPE_NOCOMP = 0x0002, // Uncompressed shape + MAKESHAPE_VARIABLE = 0x0004 // <16-color shape +} MakeShapeFlags_Type; + +inline MakeShapeFlags_Type operator|(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline MakeShapeFlags_Type operator&(MakeShapeFlags_Type a, MakeShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline MakeShapeFlags_Type operator~(MakeShapeFlags_Type a) +{return static_cast(~static_cast(a));} + +/*--------------------------------------------------------------------------- +Shape drawing flags: +- The low byte is for coordinate transformations. +- The high byte is for drawing effects. +---------------------------------------------------------------------------*/ +typedef enum : unsigned short { + SHAPE_NORMAL = 0x0000, // Standard shape + SHAPE_HORZ_REV = 0x0001, // Flipped horizontally + SHAPE_VERT_REV = 0x0002, // Flipped vertically + SHAPE_SCALING = 0x0004, // Scaled (WORD scale_x, WORD scale_y) + SHAPE_VIEWPORT_REL = 0x0010, // Coords are window-relative + SHAPE_WIN_REL = 0x0010, // Coordinates are window relative instead of absolute. + SHAPE_CENTER = 0x0020, // Coords are based on shape's center pt + SHAPE_BOTTOM = 0x0040, // Y coord is based on shape's bottom pt + SHAPE_FADING = 0x0100, // Fading effect (void * fading_table, + // WORD fading_num) + SHAPE_PREDATOR = 0x0200, // Transparent warping effect + SHAPE_COMPACT = 0x0400, // Never use this bit + SHAPE_PRIORITY = 0x0800, // Use priority system when drawing + SHAPE_GHOST = 0x1000, // Shape is drawn ghosted + SHAPE_SHADOW = 0x2000, + SHAPE_PARTIAL = 0x4000, + SHAPE_COLOR = 0x8000 // Remap the shape's colors + // (void * color_table) +} ShapeFlags_Type; + +inline ShapeFlags_Type operator|(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) | static_cast(b));} + +inline ShapeFlags_Type operator&(ShapeFlags_Type a, ShapeFlags_Type b) +{return static_cast(static_cast(a) & static_cast(b));} + +inline ShapeFlags_Type operator~(ShapeFlags_Type a) +{return static_cast(~static_cast(a));} + +/* +------------------------------- Shape header -------------------------------- +*/ +typedef struct { + unsigned short ShapeType; // 0 = normal, 1 = 16 colors, + // 2 = uncompressed, 4 = <16 colors + unsigned char Height; // Height of the shape in scan lines + unsigned short Width; // Width of the shape in bytes + unsigned char OriginalHeight; // Original height of shape in scan lines + unsigned short ShapeSize; // Size of the shape, including header + unsigned short DataLength; // Size of the uncompressed shape (just data) + unsigned char Colortable[16]; // Optional color table for compact shape +} Shape_Type; + +/* +------------------------------- Shape block --------------------------------- +*/ +#pragma warning (disable : 4200) +typedef struct { + unsigned short NumShapes; // number of shapes in the block + long Offsets[]; // array of offsets to shape data + // (offsets within the shape block, with + // 0 being the first offset value, not the + // start of the shape block) +} ShapeBlock_Type; + + +/* +******************************** Prototypes ********************************* +*/ + +/* +-------------------------------- prioinit.c --------------------------------- +*/ + +extern "C" { +extern void *MaskPage; +extern void *BackGroundPage; +extern long _ShapeBufferSize; +extern char *_ShapeBuffer; +} + + +void __cdecl Init_Priority_System (GraphicBufferClass *mask, + GraphicBufferClass *back); + + +/* +-------------------------------- drawshp.asm -------------------------------- +*/ + +extern "C" { +int Draw_Shape(GraphicViewPortClass *gvp, void const *shape, LONG x, LONG y, LONG flags, ...); +} + +/* +---------------------------------- shape.c ---------------------------------- +*/ +short __cdecl Get_Shape_Data(void const *shape, int data); +int __cdecl Extract_Shape_Count(void const *buffer); +void * __cdecl Extract_Shape(void const *buffer, int shape); +int __cdecl Restore_Shape_Height(void *shape); +int __cdecl Set_Shape_Height(void const *shape, int newheight); + +extern "C" { +int __cdecl Get_Shape_Width(void const *shape); +int __cdecl Get_Shape_Height(void const *shape); +int __cdecl Get_Shape_Original_Height(void const *shape); +int __cdecl Get_Shape_Uncomp_Size(void const *shape); +} + + +/* +------------------------------- setshape.asm -------------------------------- +*/ +extern "C" { +void __cdecl Set_Shape_Buffer(void const *buffer, int size); +} +/* +------------------------------- shapeinf.asm -------------------------------- +*/ +int __cdecl Get_Shape_Flags(void const *shape); +int __cdecl Get_Shape_Size(void const *shape); +int __cdecl Get_Shape_Scaled_Width(void const *shape, int scale); +int __cdecl Get_Shape_Scaled_Height(void const *shape, int scale); + +#endif // SHAPE_H + +/****************************** End of shape.h *****************************/ + + \ No newline at end of file diff --git a/code/redalert/win32lib/shape.inc b/code/redalert/win32lib/shape.inc new file mode 100644 index 0000000..f1cb250 --- /dev/null +++ b/code/redalert/win32lib/shape.inc @@ -0,0 +1,212 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : WWLIB32 * +;* * +;* File Name : SHAPE.INC * +;* * +;* Programmer : Scott Bowen * +;* * +;* Start Date : May 25, 1994 * +;* * +;* Last Update : September 14, 1994 [IML] * +;* * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * +;****************************** Equates ************************************ +; + + + + + + +;............................ Shape Types .................................. +; +TRUE equ 1 ; Boolean 'true' value +FALSE equ 0 ; Boolean 'false' value + +MAKESHAPE_NORMAL EQU 0 ; 256-color compressed shape +MAKESHAPE_COMPACT EQU 1 ; 16-color shape (built-in color table) +MAKESHAPE_NOCOMP EQU 2 ; non-wwcomped shape +MAKESHAPE_VARIABLE EQU 4 ; <16-color shape with variable # + ; of colors (ColorTable[0] = # of colors) +; old names: +;COLOR_SHAPE EQU 1 ; flag which determines a color shape +;NORM_SHAPE EQU 2 ; flag that indicates non wwcomped shp +;NORM_SHAPE_16 EQU 4 ; flag that tells us if we have a variable sized table + ; variable sized table +; +;........................................................................... +; Drawing flags: +; The low byte is for coordinate transformations. +; The high byte is for drawing effects. +;........................................................................... +; +SHAPE_NORMAL EQU 0000h ; no options; just a copy +SHAPE_HORZ_REV EQU 0001h ; reverse horizontally +SHAPE_VERT_REV EQU 0002h ; reverse vertically +SHAPE_SCALING EQU 0004h ; scale +SHAPE_VIEWPORT_REL EQU 0010h ; viewport-relative coordinates +SHAPE_CENTER EQU 0020h ; use centered coordinates +SHAPE_BOTTOM EQU 0040h ; Y coord is based on shape bottom pt +SHAPE_FADING EQU 0100h ; fading effect shape +SHAPE_PREDATOR EQU 0200h ; predator effect shape +SHAPE_COMPACT EQU 0400h ; shape is in 16 colors +SHAPE_PRIORITY EQU 0800h ; priority draw shape +SHAPE_GHOST EQU 1000h ; ghosting effect +SHAPE_SHADOW EQU 2000h ; shadow effect +SHAPE_PARTIAL EQU 4000h ; partial predator effect +SHAPE_COLOR EQU 8000h ; use alternative color table effect + +SHAPE_EFFECTS EQU 03F00h ; shape effect flags + +; +;.......................... Shadow Effect .................................. +; +SHADOW_COL EQU 00FFh ; magic number for shadows + +;......................... Priority System ................................. +; +CLEAR_UNUSED_BITS EQU 0007h ; and with 0000-0111 to clear + ; non-walkable high bit and + ; scaling id bits +NON_WALKABLE_BIT EQU 0080h ; and with 1000-0000 to clear all + ; but non-walkable bit +; +;......................... Predator Effect ................................. +; +PRED_MASK EQU 0007h ; mask used for predator pixel puts + +;--------------------------------------------------------------------------- +; This table is a list of the local stack variables in the function +; Draw_Shape. Many other functions in other modules access these variables +; on the stack. Since the BP is not changed when these other functions are +; called by Draw_Shape (possibly indirectly), they can also access these +; stack varibles. When adding or removing from the table, one must be very +; careful to change the offsets. +;--------------------------------------------------------------------------- +;.......................... proc addresses ................................. +LSkipRout EQU DWORD PTR ebp - 04h ;DWORD pointer to the skip routine +RSkipRout EQU DWORD PTR ebp - 08h ;DWORD pointer to the skip routine +DrawRout EQU DWORD PTR ebp - 0Ch ;DWORD pointer to the draw routine +;........................ optional arguments ............................... +ColorTable EQU DWORD PTR ebp - 10h ;DWORD ptr to the shapes color table +FadingTable EQU DWORD PTR ebp - 14h ;DWORD ptr to the fading table + +FadingNum EQU DWORD PTR ebp - 18h ;DWORD number of times to fade +IsTranslucent EQU DWORD PTR ebp - 1Ch ;DWORD ptr to is_translucent table +Translucent EQU DWORD PTR ebp - 20h ;DWORD ptr to actual translucent tbl +PriLevel EQU BYTE PTR ebp - 24h ;BYTE priority level of the object +ScaleX EQU DWORD PTR ebp - 28h ;DWORD the x increment to scale by +ScaleY EQU DWORD PTR ebp - 2Ch ;DWORD the y increment to scale by +ShadowingTable EQU DWORD PTR ebp - 30h ;DWORD ptr to the shadowing table +;........................ Shape header values .............................. +ShapeType EQU DWORD PTR ebp - 34h ;DWORD shape type +ShapeWidth EQU DWORD PTR ebp - 38h ;DWORD shape's unscaled width +ShapeHeight EQU DWORD PTR ebp - 3Ch ;DWORD shape's unscaled height +UncompDataLen EQU DWORD PTR ebp - 40h ;DWORD uncompressed data length +ShapeData EQU DWORD PTR ebp - 44h ;DWORD pointer to shape data +;...................... Scaled shape dimensions ............................ +ScaledWidth EQU DWORD PTR ebp - 48h ;DWORD shape's scaled width +ScaledHeight EQU DWORD PTR ebp - 4Ch ;DWORD shape's scaled height +;...................... Pixel clipping variables ........................... +LeftClipPixels EQU DWORD PTR ebp - 50h ;DWORD # left-clipped pixels +RightClipPixels EQU DWORD PTR ebp - 54h ;DWORD # right-clipped pixels +TopClipPixels EQU DWORD PTR ebp - 58h ;DWORD # top-clipped pixels +BotClipPixels EQU DWORD PTR ebp - 5Ch ;DWORD # bottom-clipped pixels +PixelWidth EQU DWORD PTR ebp - 60h ;DWORD drawable area in pixels +PixelHeight EQU DWORD PTR ebp - 64h ;DWORD drawable area in pixels +;......................... Drawing variables ............................... +NumColors EQU DWORD PTR ebp - 68h ;DWORD # colors for 16-color shapes +StartDraw EQU DWORD PTR ebp - 6Ch ;DWORD offset of drawing start pos +NextLine EQU DWORD PTR ebp - 70h ;DWORD offset of next drawing line +LeftClipBytes EQU DWORD PTR ebp - 74h ;DWORD # left-clipped bytes +XTotal EQU DWORD PTR ebp - 78h ;DWORD accumulated x-pixels +XTotalInit EQU DWORD PTR ebp - 7Ch ;DWORD initial roundoff for XTotal +YTotal EQU DWORD PTR ebp - 80h ;DWORD accumulated y-pixels +HeightCount EQU DWORD PTR ebp - 84h ;DWORD ht counter for drawing lines +LineStart EQU DWORD PTR ebp - 88h ;DWORD address of start of line +WidthCount EQU DWORD PTR ebp - 8Ch ;DWORD counts down # bytes skipped +StashReg EQU DWORD PTR ebp - 90h ;DWORD temp variable for draw routines +MaskAdjust EQU DWORD PTR ebp - 94h ;DWORD priority buffer offset +BackAdjust EQU DWORD PTR ebp - 98h ;DWORD background buffer offset +StashECX EQU DWORD PTR ebp - 9Ch ;DWORD temp variable for ECX register +StashEDX EQU DWORD PTR ebp -0A0h ;DWORD temp variable for EDX register + +Local_Size EQU 00A4h ; Amt of data on stack: 4+last offset + +;****************************** Declarations ******************************* +;--------------------------------------------------------------------------- +; Global variables used by the shape routines, defined in drawshp.asm +;--------------------------------------------------------------------------- +GLOBAL C ShapeBuffer:DWORD +GLOBAL C ShapeBufferSize:DWORD +GLOBAL C _MaskPage:DWORD +GLOBAL C _BackGroundPage:DWORD +GLOBAL C PredCount:DWORD +GLOBAL C PredTable:BYTE +GLOBAL C PredValue:DWORD +GLOBAL C PartialPred:DWORD +GLOBAL C PartialCount:DWORD +GLOBAL C Flags:DWORD + +;--------------------------------------------------------------------------- +; External tables that are defined in ds_table.asm. +;--------------------------------------------------------------------------- +GLOBAL LSkipTable:DWORD +GLOBAL RSkipTable:DWORD +GLOBAL DrawTable:DWORD + +;------------------------------------------------------------------------------ +; Public functions, declared in the order they appear in the function tables. +;-------------------------------------------------------------------------------- +GLOBAL C Not_Supported:NEAR +; LSkipTable: +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Skip:NEAR ; ds_ls +GLOBAL Left_Reverse_Skip:NEAR ; ds_lrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs +GLOBAL Left_Scale_Skip:NEAR ; ds_lss +GLOBAL Left_Scale_Reverse_Skip:NEAR ; ds_lsrs + +; RSkipTable: +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Skip:NEAR ; ds_rs +GLOBAL Right_Reverse_Skip:NEAR ; ds_rrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs +GLOBAL Right_Scale_Skip:NEAR ; ds_rss +GLOBAL Right_Scale_Reverse_Skip:NEAR ; ds_rsrs + +; DrawTable: +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Normal:NEAR ; ds_dn +GLOBAL Draw_Reverse:NEAR ; ds_dr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr +GLOBAL Draw_Scale:NEAR ; ds_ds +GLOBAL Draw_Scale_Reverse:NEAR ; ds_dsr + + +;************************* End of shape.inc ******************************** + diff --git a/code/redalert/win32lib/sos.h b/code/redalert/win32lib/sos.h new file mode 100644 index 0000000..414d0de --- /dev/null +++ b/code/redalert/win32lib/sos.h @@ -0,0 +1,562 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sos.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DEFINED +#define _SOS_DEFINED +#include "sosdefs.h" + +#pragma pack(4) + +// error definition for sound operating system +#define _SOS_ERR -1 + +// number of drivers allowed to be open at one time +#define _SOS_MAX_DRIVERS 5 + +// structure definition for the capabilities +typedef struct _tagCAPABILITIES +{ + BYTE szDeviceName[ 32 ]; // device name + WORD wDeviceVersion; // device version + WORD wBitsPerSample; // bits per sound sample + WORD wChannels; // stereo/mono sound card + WORD wMinRate; // minimum rate + WORD wMaxRate; // maximum rate + WORD wMixerOnBoard; // board contains mixer + WORD wMixerFlags; // mixer capabilities + WORD wFlags; // miscellaneous flags + short far * lpPortList; // list of usable ports + short far * lpDMAList; // list of usable dma channels + short far * lpIRQList; // list of usable irq channels + short far * lpRateList; // list of usable rates, -1 if any in min to max + WORD fBackground; // foreground or background driver + WORD wDeviceID; // ID for the device + WORD wTimerID; // ID for the timer + +} _SOS_CAPABILITIES; + +// far pointer to the device capabilities structure +typedef _SOS_CAPABILITIES far * LPSOSDEVICECAPS; + +// flag types for driver +#define _FLAGS_SIGNED 0x8000 + +// devices that can be loaded +#define _SOUND_BLASTER_8_MONO 0xe000 +#define _SOUND_BLASTER_8_ST 0xe001 +#define _SBPRO_8_ST _SOUND_BLASTER_8_ST +#define _SBPRO_8_MONO 0xe00f +#define _SOUND_MASTER_II_8_MONO 0xe002 +#define _MV_PAS_8_MONO 0xe003 +#define _MV_PAS_16_MONO 0xe004 +#define _MV_PAS_8_ST 0xe005 +#define _MV_PAS_16_ST 0xe006 +#define _ADLIB_GOLD_8_ST 0xe007 +#define _ADLIB_GOLD_16_ST 0xe008 +#define _ADLIB_GOLD_8_MONO 0xe009 +#define _ADLIB_GOLD_16_MONO 0xe00a +#define _MICROSOFT_8_MONO 0xe00b +#define _MICROSOFT_8_ST 0xe00c +#define _MICROSOFT_16_MONO 0xe00d +#define _MICROSOFT_16_ST 0xe00e +#define _SOUND_SOURCE_8_MONO_PC 0xe010 +#define _SOUND_SOURCE_8_MONO_TANDY 0xe011 +#define _GENERAL_PORT_8_MONO 0xe012 +#define _GENERAL_PORT_8_MONO_R 0xe013 +#define _SIERRA_8_MONO 0xe014 +#define _SB16_8_MONO 0xe015 +#define _SB16_8_ST 0xe016 +#define _SB16_16_MONO 0xe017 +#define _SB16_16_ST 0xe018 +#define _ESS_AUDIODRIVE_8_MONO 0xe019 +#define _ESS_AUDIODRIVE_8_ST 0xe01a +#define _ESS_AUDIODRIVE_16_MONO 0xe01b +#define _ESS_AUDIODRIVE_16_ST 0xe01c +#define _SOUNDSCAPE_8_MONO 0xe01d +#define _SOUNDSCAPE_8_ST 0xe01e +#define _SOUNDSCAPE_16_MONO 0xe01f +#define _SOUNDSCAPE_16_ST 0xe020 +#define _RAP10_8_MONO 0xe021 +#define _RAP10_16_MONO 0xe022 +#define _GUS_8_MONO 0xe023 +#define _GUS_8_ST 0xe024 +#define _GUS_16_MONO 0xe025 +#define _GUS_16_ST 0xe026 +#define _GUS_MAX_8_MONO 0xe027 +#define _GUS_MAX_8_ST 0xe028 +#define _GUS_MAX_16_MONO 0xe029 +#define _GUS_MAX_16_ST 0xe02a +#define _WAVEJAMMER_8_MONO 0xe02b +#define _WAVEJAMMER_8_ST 0xe02c +#define _WAVEJAMMER_16_MONO 0xe02d +#define _WAVEJAMMER_16_ST 0xe02e +#define _TEMPOCS_8_MONO 0xe02f +#define _TEMPOCS_8_ST 0xe030 +#define _TEMPOCS_16_MONO 0xe031 +#define _TEMPOCS_16_ST 0xe032 +#define _WAVEJAMMERCD_8_MONO 0xe033 +#define _WAVEJAMMERCD_8_ST 0xe034 +#define _WAVEJAMMERCD_16_MONO 0xe035 +#define _WAVEJAMMERCD_16_ST 0xe036 +#define _SOUND_BLASTER_8_MONO_R 0xe050 +#define _MICROSOFT_8_MONO_R 0xe051 +#define _SOUND_MASTER_II_8_MONO_R 0xe052 +#define _ADLIB_GOLD_8_MONO_R 0xe053 +#define _MV_PAS_8_MONO_R 0xe054 +#define _RAP10_8_MONO_R 0xe058 +#define _RAP10_16_MONO_R 0xe059 +#define _SB16_8_MONO_R 0xe05a +#define _SB16_8_ST_R 0xe05b +#define _SB16_16_MONO_R 0xe05c +#define _SB16_16_ST_R 0xe05d +#define _MV_PAS_16_MONO_R 0xe060 +#define _SOUNDSCAPE_8_MONO_R 0xe061 +#define _SOUNDSCAPE_8_ST_R 0xe062 +#define _SOUNDSCAPE_16_MONO_R 0xe063 +#define _SOUNDSCAPE_16_ST_R 0xe064 +#define _ESS_AUDIODRIVE_8_MONO_R 0xe065 +#define _ESS_AUDIODRIVE_8_ST_R 0xe066 +#define _ESS_AUDIODRIVE_16_MONO_R 0xe067 +#define _ESS_AUDIODRIVE_16_ST_R 0xe068 +#define _SPEECH_THING_8_MONO 0xe090 +#define _YAMAHA_8_MONO 0xe106 +#define _INT_SPEAKER_8_MONO 0xe107 + +// call indexes for the loadable drivers +enum +{ + _DRV_INIT, + _DRV_UNINIT, + _DRV_SETRATE, + _DRV_SETACTION, + _DRV_START, + _DRV_STOP, + _DRV_PAUSE, + _DRV_RESUME, + _DRV_CAPABILITIES, + _DRV_PLAY_FOREGROUND, + _DRV_GET_FILL_INFO, + _DRV_GET_CALL_FUNCTIONS, + _DRV_SET_CALL_FUNCTIONS +}; + +// fill info +typedef struct _tagFillInfo + { + + LPSTR lpFillHandler; // pointer to fill handler + LPWORD lpDMAFillCount; // pointer to dma count + LPSTR lpSampleList; // pointer to sample list + LPWORD lpDMAMasterVolume; // pointer to dma count + + } _SOS_FILL_INFO; + +// caps info structure +typedef struct _tagCapsInfo + { + + LPSTR lpPortList; // pointer to port list + LPSTR lpDMAList; // pointer to DMA list + LPSTR lpIRQList; // pointer to IRQ list + LPSTR lpRateList; // pointer to rate list + + } _SOS_CAPS_INFO; + +// maximum number of available voice +#define _MAX_VOICES 32 + +// structure definition +typedef struct _tagSAMPLE +{ + LPSTR samplePtr; // pointer to data buffer + LPSTR sampleData; // pointer to active data + LPSTR sampleLoopPtr; // pointer for loop back + + WORD sampleLength; // length of sample + WORD sampleIndex; // index into sample + WORD sampleLoopLength; // length of loop + + WORD sampleBytesLeft; // bytes left to play in sample + + WORD sampleLoopPoint; // byte count for loop point + WORD sampleLoopEndLength; // length of remaining chunk + + short sampleFlags; // control sample + short sampleVolume; // volume control + short sampleID; // sample ID + + short sampleChannel; // channel to play sample on + short sampleLoopCount; // loop count + short sampleLastFill; // last fill position + VOID ( far __cdecl * sampleCallback )( WORD, WORD, WORD ); // callback function for sample + + WORD samplePitchAdd; + short samplePitchFraction; + + short samplePort; // port to use for non-dma digitized + + WORD sampleTotalBytes; + WORD sampleByteLength; + + short samplePanLocation; + short samplePanSpeed; + short samplePanDirection; + short samplePanStart; + short samplePanEnd; + + short sampleDelayBytes; + short sampleDelayRepeat; + + WORD sampleADPCMPredicted; + short sampleADPCMIndex; + + short sampleRootNoteMIDI; + + WORD sampleTemp1; + +} _SOS_SAMPLE; + +// enumeration for left or right channel +enum +{ + _LEFT_CHANNEL, + _RIGHT_CHANNEL, + _CENTER_CHANNEL, + _INTERLEAVED +}; + +// enumeration for foreground and background +enum +{ + _FOREGROUND, + _BACKGROUND +}; + +// defines for the sample flags +#define _ACTIVE 0x8000 +#define _LOOPING 0x4000 +#define _FIRST_TIME 0x2000 +#define _PENDING_RELEASE 0x1000 +#define _CONTINUE_BLOCK 0x0800 +#define _PITCH_SHIFT 0x0400 +#define _PANNING 0x0200 +#define _VOLUME 0x0100 +#define _TRANSLATE16TO8 0x0080 +#define _STAGE_LOOP 0x0040 +#define _TRANSLATE8TO16 0x0020 +#define _STEREOTOMONO 0x0010 + +// defines for the wParam flags +#define _SINGLE_SAMPLE 0x01 + +#define _SOS_DCAPS_AUTO_REINIT 0x01 +#define _SOS_DCAPS_MPU_401 0x02 +#define _SOS_DCAPS_OPL2 0x04 +#define _SOS_DCAPS_OPL3 0x08 +#define _SOS_DCAPS_OPL4 0x10 +#define _SOS_DCAPS_WAVETABLE 0x20 +#define _SOS_DCAPS_DL_SAMPLES 0x40 +#define _SOS_DCAPS_FIFO_DEVICE 0x80 +#define _SOS_DCAPS_ENV_NEEDED 0x100 +#define _SOS_DCAPS_PSEUDO_DMA1 0x200 +#define _SOS_DCAPS_SIGNED_DATA 0x8000 + +// file header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // number of drivers in the file + WORD wDrivers; + + // offset of first driver + WORD lOffset; + + // size of the file + WORD lFileSize; + +} _FILEHEADER; + +// driver header structure +typedef struct +{ + // name ID + BYTE szName[ 32 ]; + + // offset of next driver + WORD lNextDriver; + + // size of current driver + WORD wSize; + + // id for the current device + WORD wDeviceID; + + // id for the type of DOS extender + WORD wExtenderType; + +} _DRIVERHEADER; + +// device hardware information +typedef struct +{ + // port to be used + WORD wPort; + + // irq to use + WORD wIRQ; + + // dma channel to se + WORD wDMA; + + // extra parameter + WORD wParam; + +} _SOS_HARDWARE; + +// structure definition for start sample +typedef struct +{ + // pointer to sample + LPSTR lpSamplePtr; + + // size of the sample + WORD dwSampleSize; + + // number of times to loop the sample -1 is infinite + WORD wLoopCount; + + // channel to play sample on + WORD wChannel; + + // volume to play sample at + WORD wVolume; + + // id for the sample + WORD wSampleID; + + // far pointer to the callback function + VOID ( far __cdecl *lpCallback )( WORD, WORD, WORD ); + + // port to use if driver is a non-dma background driver + WORD wSamplePort; + + // flags field + WORD wSampleFlags; + + // total length of sample including loops, etc.. + WORD dwSampleByteLength; + + // loop point for the sample + WORD dwSampleLoopPoint; + WORD dwSampleLoopLength; + + // pitch shifting components + WORD dwSamplePitchAdd; + WORD wSamplePitchFraction; + + // pan components + WORD wSamplePanLocation; + WORD wSamplePanSpeed; + WORD wSamplePanDirection; + WORD wSamplePanStart; + WORD wSamplePanEnd; + + // delay parts + WORD wSampleDelayBytes; + WORD wSampleDelayRepeat; + + // compression components + WORD dwSampleADPCMPredicted; + WORD wSampleADPCMIndex; + + // root note for pitch shifting + WORD wSampleRootNoteMIDI; + + // filler for future upgrades + WORD dwSampleTemp1; + WORD dwSampleTemp2; + WORD dwSampleTemp3; + +} _SOS_START_SAMPLE; + +// structure for initializing a driver +typedef struct +{ + WORD wBufferSize; + LPSTR lpBuffer; + BOOL wAllocateBuffer; + WORD wSampleRate; + WORD wParam; + LONG dwParam; + VOID ( far *lpFillHandler )( VOID ); + LPSTR lpDriverMemory; + LPSTR lpDriverMemoryCS; + LPSTR lpTimerMemory; + LPSTR lpTimerMemoryCS; + WORD wTimerID; + WORD wPhysical; + +} _SOS_INIT_DRIVER; + +// define for the timer types to use +#define _SOS_NORMAL_TIMER 0x00 + +// enumeration for the timer types +enum +{ + _TIMER_8_MONO = 0x1000, + _TIMER_8_ST, + _TIMER_16_MONO, + _TIMER_16_ST, + _TIMER_8_MONO_ULAW, + _TIMER_8_ST_ULAW, + _TIMER_16_MONO_ULAW, + _TIMER_16_ST_ULAW, + _TIMER_8_MONO_REC, + _TIMER_8_MONO_ULAW_REC, + _TIMER_UNDEFINED_1, + _TIMER_UNDEFINED_2, + _TIMER_UNDEFINED_3, + _TIMER_UNDEFINED_4, + _TIMER_UNDEFINED_5, + _TIMER_UNDEFINED_6, + _TIMER_UNDEFINED_7, + _TIMER_UNDEFINED_8, + _TIMER_UNDEFINED_9, + _TIMER_UNDEFINED_A, + _TIMER_UNDEFINED_B, + _TIMER_UNDEFINED_C, + _TIMER_UNDEFINED_D, + _TIMER_UNDEFINED_E, + _TIMER_UNDEFINED_F, + _TIMER_UNDEFINED_10, + _TIMER_UNDEFINED_11, + _TIMER_UNDEFINED_12, + _TIMER_UNDEFINED_13, + _TIMER_UNDEFINED_14, + _TIMER_UNDEFINED_15, + _TIMER_UNDEFINED_16, + _TIMER_8_SOUND_SOURCE, + _TIMER_8_SOUND_SOURCE_TANDY, + _TIMER_8_GENERAL_PORT, + _TIMER_8_GENERAL_PORT_REC +}; + +// define for no slots available +#define _ERR_NO_SLOTS ( WORD )-1 + +// error codes for the system +enum +{ + _ERR_NO_ERROR, + _ERR_DRIVER_NOT_LOADED, + _ERR_INVALID_POINTER, + _ERR_DETECT_INITIALIZED, + _ERR_FAIL_ON_FILE_OPEN, + _ERR_MEMORY_FAIL, + _ERR_INVALID_DRIVER_ID, + _ERR_NO_DRIVER_FOUND, + _ERR_DETECTION_FAILURE, + _ERR_DRIVER_LOADED, + _ERR_INVALID_HANDLE, + _ERR_NO_HANDLES, + _ERR_PAUSED, + _ERR_NOT_PAUSED, + _ERR_INVALID_DATA, + _ERR_DRV_FILE_FAIL, + _ERR_INVALID_PORT, + _ERR_INVALID_IRQ, + _ERR_INVALID_DMA, + _ERR_INVALID_DMA_IRQ +}; + +// maximum number of timer events that can be registered +#define _TIMER_MAX_EVENTS 0x10 + +// flags for the debugging system +#define _SOS_DEBUG_NORMAL 0x0000 +#define _SOS_DEBUG_NO_TIMER 0x0001 +#define _SOS_TIMER_DPMI 0x0002 + +// define for types of DOS extenders +#define _SOS_RATIONAL 0x8000 +#define _SOS_FLASHTECK 0x4000 + +// defines for the types of timers for different +// dos extenders +#define _SOS_TIMER_NEAR 0x8000 +#define _SOS_TIMER_FAR 0x4000 + +// values for callback information +enum +{ + _SAMPLE_PROCESSED, + _SAMPLE_LOOPING, + _SAMPLE_DONE +}; + +// define for special 18.2 callback rate to dos +#define _TIMER_DOS_RATE 0xff00 + +#pragma pack() + +#pragma aux int_3 = "int 3" + +#pragma pack( 1 ) +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned segment; + unsigned short number_available; + unsigned short number_used; + unsigned page0; + +} EVDS_STRUCT; + +typedef struct +{ + unsigned region_size; + unsigned offset; + unsigned short segment; + unsigned short ID; + unsigned physical; + +} VDS_STRUCT; + +#pragma pack() + +#include "sosdata.h" +#include "sosfnct.h" + +#endif \ No newline at end of file diff --git a/code/redalert/win32lib/soscomp.h b/code/redalert/win32lib/soscomp.h new file mode 100644 index 0000000..73ac03a --- /dev/null +++ b/code/redalert/win32lib/soscomp.h @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** +* +* File : soscomp.h +* Date Created : 6/1/94 +* Description : +* +* Programmer(s) : Nick Skrepetos +* Last Modification : 10/1/94 - 11:37:9 AM +* Additional Notes : Modified by Denzil E. Long, Jr. +* +***************************************************************************** +* Copyright (c) 1994, HMI, Inc. All Rights Reserved * +****************************************************************************/ + +#ifndef _SOS_COMPRESS +#define _SOS_COMPRESS + +/* compression types */ +enum { + _ADPCM_TYPE_1, + }; + +/* define compression structure */ +typedef struct _tagCOMPRESS_INFO { + char *lpSource; + char *lpDest; + unsigned long dwCompSize; + unsigned long dwUnCompSize; + unsigned long dwSampleIndex; + long dwPredicted; + long dwDifference; + short wCodeBuf; + short wCode; + short wStep; + short wIndex; + + unsigned long dwSampleIndex2; //added BP for channel 2 + long dwPredicted2; //added BP for channel 2 + long dwDifference2; //added BP for channel 2 + short wCodeBuf2; //added BP for channel 2 + short wCode2; //added BP for channel 2 + short wStep2; //added BP for channel 2 + short wIndex2; //added BP for channel 2 + short wBitSize; + short wChannels; //added BP for # of channels + } _SOS_COMPRESS_INFO; + +/* compressed file type header */ +typedef struct _tagCOMPRESS_HEADER { + unsigned long dwType; // type of compression + unsigned long dwCompressedSize; // compressed file size + unsigned long dwUnCompressedSize; // uncompressed file size + unsigned long dwSourceBitSize; // original bit size + char szName[16]; // file type, for error checking + } _SOS_COMPRESS_HEADER; + +/* Prototypes */ +extern "C" { + void __cdecl sosCODECInitStream(_SOS_COMPRESS_INFO *); + unsigned long __cdecl sosCODECCompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); + unsigned long __cdecl General_sosCODECDecompressData(_SOS_COMPRESS_INFO *, unsigned long); +} + +#endif diff --git a/code/redalert/win32lib/sosdata.h b/code/redalert/win32lib/sosdata.h new file mode 100644 index 0000000..5f8f9ee --- /dev/null +++ b/code/redalert/win32lib/sosdata.h @@ -0,0 +1,125 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosdata.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_DATA +#define _SOS_DATA + +#include + +#pragma pack(4) +extern WORD _sosDIGIData_Start; +extern WORD _sosDIGIData_End; +extern WORD _wSOSDriverLinear[]; +extern WORD _wSOSTimerLinear[]; +extern LPSTR _lpSOSDriver[]; +extern LPSTR _lpSOSTimer[]; +extern LPSTR _lpSOSDriverCS[]; +extern LPSTR _lpSOSTimerCS[]; +extern BOOL _wSOSDriverLoaded[]; +extern BOOL _wSOSTimerLoaded[]; +extern BOOL _wSOSDriverInitialized[]; +extern WORD _wSOSOutputRate[]; +extern WORD _wSOSDMABuffersize[]; +extern LONG _dwSOSDMABufferPhysical[]; +extern LPSTR _lpSOSDMABuffer[]; +extern BOOL _wTimerUsed; +extern VOID ( far *_lpSOSFillHandler[] )( VOID ); +extern WORD _wSOSTimerType[]; +extern WORD _wSOSDriverType[]; +extern _SOS_SAMPLE far * _lpSOSSampleList[][ _MAX_VOICES ]; +extern LPWORD _lpSOSDMAIrqCount[]; +extern LPWORD _lpSOSDMAFillCount[]; +extern WORD _wSOSTmrNextCount; +extern VOID ( interrupt far *_lpSOSOldTimer )( VOID ); +extern WORD _wSOSDriverID[]; +extern _SOS_CAPABILITIES _sSOSDriverCaps[]; +extern WORD _wSOSDMAPortList[]; +extern BYTE _bSOSDMAChannel[]; +extern _SOS_INIT_DRIVER _sSOSDIGIInitDriver[]; +extern BYTE _pSOSDriverPath[]; +extern BYTE _pSOSTempDriverPath[]; +extern BOOL _wTIMERUsed; +extern WORD _wTIMERValue; +extern VOID ( far * _lpTIMEREvents[] )( VOID ); +extern WORD _wTIMEREventRate[]; +extern WORD _dwTIMEREventFraction[]; +extern WORD _dwTIMEREventFractionCurrent[]; +extern BYTE _bSOSMIDITimerSongHandler[]; +extern BYTE _bSOSMIDISongHandle; +extern WORD _wSOSTimerMemHandle[]; +extern WORD _wSOSDriverMemHandle[]; +extern WORD _wSOSRealSeg[]; + +extern _FILEHEADER _sDETFileHeader; +extern _DRIVERHEADER _sDETDriverHeader; +extern _FILEHEADER sLOADFileHeader; +extern _DRIVERHEADER sLOADDriverHeader; +extern BOOL _wDETInitialized; +extern WORD _wDETLinear; +extern LPSTR _lpDETDriverBuffer; +extern LPSTR _lpDETDriverBufferCS; +extern WORD _hDETFile; +extern DWORD _dwDETDriverIndex; +extern WORD _wDETDriverIndexCur; +extern WORD _wDETMemHandle; +extern LPSOSDEVICECAPS _lpDETDeviceCaps; +extern _SOS_CAPABILITIES _sDETCaps; +extern PSTR _pSOSErrorStrings[]; +extern BOOL _wSOSBufferAllocated[]; +extern BOOL _wSOSSystemInitialized; +extern VDS_STRUCT _sSOSVDSInfo; +extern _SOS_FILL_INFO _sSOSFillInfo; +extern WORD _wSOSTimerEventIndex; +extern WORD _wSOSTimerEntered; +extern WORD _wSOSDriverSize[]; +extern WORD _wSOSTimerSize[]; + +#ifdef __cplusplus +extern "C" { +#endif +extern WORD _sosDIGIData1_Start; +extern WORD _sosDIGIData1_End; +extern WORD _sosDIGIData2_Start; +extern WORD _sosDIGIData2_End; +extern BYTE _bTIMERInstalled; +extern BYTE _bTIMERDPMI; +extern WORD wDetectPort; +extern WORD wDetectIRQ; +extern WORD wDetectDMA; +extern WORD wDetectParam; +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif + diff --git a/code/redalert/win32lib/sosdefs.h b/code/redalert/win32lib/sosdefs.h new file mode 100644 index 0000000..a3b592c --- /dev/null +++ b/code/redalert/win32lib/sosdefs.h @@ -0,0 +1,80 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosdefs.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + + +#ifndef _SOSDEFS_DEFINED +#define _SOSDEFS_DEFINED + +#undef _TRUE +#undef _FALSE +#undef _NULL +enum + { + _FALSE, + _TRUE + }; + +#define _NULL 0 + +#ifndef VOID +#define VOID void +#endif +typedef int BOOL; +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned WORD; +#ifndef LONG +typedef signed long LONG; +#endif +typedef unsigned long DWORD; + +typedef BYTE * PBYTE; +typedef char near * PSTR; +typedef WORD * PWORD; +typedef LONG * PLONG; +typedef VOID * PVOID; + +typedef BYTE far * LPBYTE; +typedef BYTE far * LPSTR; +typedef WORD far * LPWORD; +typedef LONG far * LPLONG; +typedef VOID far * LPVOID; + +typedef BYTE huge * HPBYTE; +typedef BYTE huge * HPSTR; +typedef WORD huge * HPWORD; +typedef LONG huge * HPLONG; +typedef VOID huge * HPVOID; + +typedef unsigned HANDLE; + +#endif + diff --git a/code/redalert/win32lib/sosfnct.h b/code/redalert/win32lib/sosfnct.h new file mode 100644 index 0000000..fbfad6f --- /dev/null +++ b/code/redalert/win32lib/sosfnct.h @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosfnct.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#ifndef _SOS_FUNCTIONS +#define _SOS_FUNCTIONS + +#pragma pack(4) + +WORD sosDIGILockMemory ( VOID ); +WORD sosDIGIUnLockMemory ( VOID ); +WORD sosDIGIInitSystem ( LPSTR, WORD ); +WORD sosDIGIUnInitSystem ( VOID ); +WORD sosDIGIInitDriver ( WORD, _SOS_HARDWARE far *, + _SOS_INIT_DRIVER far *, WORD far * ); +WORD sosDIGIUnInitDriver ( WORD, BOOL, BOOL ); +WORD sosDIGILoadDriver ( WORD, WORD, LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadDriver ( WORD ); +WORD sosDIGIGetDeviceCaps ( WORD, LPSOSDEVICECAPS ); + +#ifdef PHARLAP +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#else +LPSTR sosDIGIAllocateBuffer ( WORD , WORD *, WORD * ); +#endif + +WORD sosDIGIStopSample ( WORD, WORD ); +WORD sosDIGISamplesPlaying ( WORD ); +BOOL sosDIGISampleDone ( WORD, WORD ); +BOOL sosDIGISampleFilling ( WORD, WORD ); +WORD sosDIGIStartSample ( WORD, _SOS_START_SAMPLE far * ); +WORD sosDIGIContinueSample ( WORD, WORD, _SOS_START_SAMPLE far * ); + + +WORD sosDIGIDetectInit ( LPSTR ); +WORD sosDIGIDetectUnInit ( VOID ); +WORD sosDIGIDetectFindHardware ( WORD, _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindFirst ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectFindNext ( _SOS_CAPABILITIES far *, WORD far * ); +WORD sosDIGIDetectGetSettings ( _SOS_HARDWARE far * ); +WORD sosDIGIDetectGetCaps ( WORD, _SOS_CAPABILITIES far * ); +WORD sosDIGIDetectVerifySettings( _SOS_HARDWARE far * ); +PSTR sosGetErrorString ( WORD ); + +WORD sosDIGILoadTimer ( WORD , LPSTR far *, LPSTR far *, PSTR, PSTR, WORD * ); +WORD sosDIGIUnLoadTimer ( WORD ); + +WORD sosTIMERRegisterEvent ( WORD wCallRate, VOID ( far * lpTimerEvent )( VOID ), WORD far *lpTimerHandle ); +WORD sosTIMERInitSystem ( WORD, WORD ); +WORD sosTIMERUnInitSystem ( WORD ); +WORD sosTIMERSetRate ( WORD ); +WORD sosTIMERRemoveEvent ( WORD ); +WORD sosTIMERAlterEventRate ( WORD, WORD ); +WORD sosTIMERGetEventRate ( WORD ); +VOID far sosTIMEROldHandler ( VOID ); +VOID far sosTIMERHandler ( VOID ); + +// functions in soscntl.c +WORD sosDIGISetSampleVolume ( WORD, WORD, WORD ); +WORD sosDIGIGetSampleVolume ( WORD, WORD ); +WORD sosDIGISetChannel ( WORD, WORD, WORD ); +WORD sosDIGIGetChannel ( WORD, WORD ); +WORD sosDIGIGetBytesProcessed ( WORD, WORD ); +WORD sosDIGIGetLoopCount ( WORD, WORD ); +WORD sosDIGISetPanLocation ( WORD, WORD, WORD ); +WORD sosDIGIGetPanLocation ( WORD, WORD ); +DWORD sosDIGISetPitch ( WORD, WORD, DWORD ); +DWORD sosDIGIGetPitch ( WORD, WORD ); +WORD sosDIGIGetDMAPosition ( WORD ); +WORD sosDIGISetPanSpeed ( WORD, WORD, WORD ); +WORD sosDIGIGetPanSpeed ( WORD, WORD ); +WORD sosDIGIGetSampleID ( WORD, WORD ); +WORD sosDIGIGetSampleHandle ( WORD, WORD ); +WORD sosDIGISetMasterVolume ( WORD, WORD ); +#ifdef PHARLAP +VOID sosFreeVDSPage ( unsigned short, unsigned short, DWORD ); +WORD sosAllocVDSPage ( unsigned short *, unsigned short *, DWORD * ); +#else +WORD sosAllocVDSPage ( LPSTR *, WORD *, WORD * ); +VOID sosFreeVDSPage ( WORD, WORD, LONG ); +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef PHARLAP +extern int __cdecl sosRealFree ( int ); +extern BOOL __cdecl _sos_read( WORD, LPSTR, WORD, WORD * ); +extern int __cdecl sosRealAlloc( int, int *, int * ); +extern void __cdecl sosDRVFarMemCopy( LPSTR, LPSTR, WORD ); +extern int __cdecl sosGetCS( VOID ); +extern int __cdecl sosGetES( VOID ); +#else +extern int __cdecl sosRealAlloc ( int, int *, int * ); +extern int __cdecl sosRealFree ( int ); +#endif + +// sos driver functions +extern WORD __cdecl sosDRVLockMemory ( DWORD, DWORD ); +extern WORD __cdecl sosDRVUnLockMemory ( DWORD, DWORD ); +extern void __cdecl sosDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDetDRVGetCapsInfo ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVGetCapsPtr ( LPSTR, LPSTR, _SOS_CAPABILITIES far * ); +extern void __cdecl sosDRVInit ( LPSTR, LPSTR, int, int, int, int, int, int ); +extern void __cdecl sosDRVStart ( LPSTR, LPSTR, int, int ); +extern void __cdecl sosDRVSetRate ( LPSTR, LPSTR, int ); +extern void __cdecl sosDRVSetAction ( LPSTR, LPSTR ); +extern void __cdecl sosDRVStop ( LPSTR, LPSTR ); +extern void __cdecl sosDRVUnInit ( LPSTR, LPSTR ); +extern void __cdecl sosDRVGetFillInfo ( LPSTR, LPSTR, LPSTR, int, int, int, _SOS_FILL_INFO * ); +extern void __cdecl sosFillSampleStructs ( PSTR, LPSTR ); +extern WORD __cdecl sosDetDRVExist ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVGetSettings ( LPSTR, LPSTR ); +extern WORD __cdecl sosDetDRVVerifySettings( LPSTR, WORD, WORD, WORD, LPSTR ); +extern WORD __cdecl sosDIGIInitForWindows( WORD ); +extern WORD __cdecl sosDIGIUnInitForWindows( WORD ); +extern LPSTR __cdecl sosAllocateFarMem ( WORD, PSTR, WORD * ); +extern LPSTR __cdecl sosCreateAliasCS ( LPSTR ); +extern VOID __cdecl sosFreeSelector ( LPSTR, DWORD ); +extern LPSTR __cdecl sosMAKEDOSPtr ( PSTR ); +extern VOID __cdecl sosDetDRVSetEnvString ( DWORD, PSTR ); +extern PSTR __cdecl sosDetDRVGetEnvString ( DWORD ); +extern VOID __cdecl sosDetDRVEnvStringInit ( LPSTR, LPSTR ); +extern VOID __cdecl sosDRVSetupCallFunctions( LPSTR, LPSTR, LPSTR, LPSTR ); +extern WORD __cdecl sosDRVGetFreeMemory ( VOID ); +extern WORD __cdecl sosDRVAllocVDSStruct ( WORD, WORD *, WORD * ); +extern WORD __cdecl sosDRVFreeVDSStruct ( WORD, WORD ); +extern WORD __cdecl sosDRVIsWindowsActive ( VOID ); +extern WORD __cdecl sosDRVVDSGetBuffer ( WORD ); +extern WORD __cdecl sosDRVVDSFreeBuffer ( WORD ); +extern WORD __cdecl getDS( VOID ); +extern WORD __cdecl sosDRVMakeDMASelector ( WORD ); +extern WORD __cdecl sosDRVFreeDMASelector ( WORD ); + + +extern void __cdecl sosTIMERDRVInit( int wRate, void ( far * )( void ) ); +extern void __cdecl sosTIMERDRVUnInit( void ); +extern void __cdecl sosTIMERDRVHandler( void ); +extern void __cdecl sosTIMERDRVFHandler( void ); +extern void __cdecl sosTIMERDRVEnable( void ); +extern void __cdecl sosTIMERDRVDisable( void ); +extern void __cdecl sosTIMERDRVCallOld( void ); +extern void __cdecl sosTIMERDRVSetRate( WORD ); +extern void __cdecl sosDIGITimer_Start( void ); +extern void __cdecl sosDIGITimer_End( void ); +extern void __cdecl sosDIGIDrv_Start( void ); +extern void __cdecl sosDIGIDrv_End( void ); +#ifdef __cplusplus +} +#endif + +// external functions for handling system initialization and +// uninitialization +WORD sosEXDIGInitDriver ( WORD, WORD, WORD, LPSTR, + _SOS_HARDWARE far *, WORD * ); +WORD sosEXDIGIUnInitDriver ( VOID ); + +WORD sosEXDETFindDriver ( WORD, LPSTR, _SOS_HARDWARE far *, + _SOS_CAPABILITIES far * ); + +// memory locking prototypes +VOID sosDIGICaps_Start( VOID ); +VOID sosDIGICaps_End( VOID ); +VOID sosDIGIErr_Start( VOID ); +VOID sosDIGIErr_End( VOID ); +VOID sosDIGITmr_Start( VOID ); +VOID sosDIGITmr_End( VOID ); +VOID sosDIGIStart_Start( VOID ); +VOID sosDIGIStart_End( VOID ); +VOID sosDIGIPlyng_Start( VOID ); +VOID sosDIGIPlyng_End( VOID ); +VOID sosDIGIRate_Start( VOID ); +VOID sosDIGIRate_End( VOID ); +VOID sosDIGIDone_Start( VOID ); +VOID sosDIGIDone_End( VOID ); +VOID sosDIGIDetec_Start( VOID ); +VOID sosDIGIDetec_End( VOID ); +VOID sosDIGIInit_Start( VOID ); +VOID sosDIGIInit_End( VOID ); +VOID sosDIGILoad_Start( VOID ); +VOID sosDIGILoad_End( VOID ); +VOID sosDIGICntl_Start( VOID ); +VOID sosDIGICntl_End( VOID ); + +#pragma pack() + +#endif + diff --git a/code/redalert/win32lib/sosres.h b/code/redalert/win32lib/sosres.h new file mode 100644 index 0000000..0584fc9 --- /dev/null +++ b/code/redalert/win32lib/sosres.h @@ -0,0 +1,126 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/**************************************************************************** + + File : sosres.h + + Programmer(s) : Don Fowler, Nick Skrepetos + Date : + + Purpose : Include Files For Zortech C++ Compiler + + Last Updated : + +**************************************************************************** + Copyright(c) 1993,1994 Human Machine Interfaces + All Rights Reserved +****************************************************************************/ + +#define _SOS_RESOURCE +#ifndef _SOS_RESOURCE +#define _SOS_RESOURCE + +// structure for resource file header +typedef struct +{ + // file version + WORD wVersion; + + // file size + LONG dwFileSize; + + // number of resources in file + WORD wResCount; + + // offset of resource data from top of file + LONG dwResOffset; + + // offset of sync track from top of file + LONG dwSyncTrackOffset; + +} _RES_FILE_HEADER; + +// structure for resource block header +typedef struct +{ + // resource id + WORD wID; + + // resource type + WORD wResType; + + // offset of next block + LONG dwNextBlock; + + // size of the current resource information + LONG dwResSize; + + // rate to play block at + WORD wBlockRate; + + // id for the sync track to use + WORD wSyncTrackID; + +} _RES_BLOCK_HEADER; + +// structure for sync mark tag +typedef struct _tagSYNCMARK +{ + // ID of the type of mark being used + WORD wID; + + // location in data of sync mark + LONG dwSyncOffset; + + // length of sync block + LONG dwSyncSize; + + // start sample data + _SOS_START_SAMPLE sampleData; + +} _RES_SYNCMARK; + +typedef union +{ + // structure for sync mark tag + _RES_SYNCMARK syncMark; + +} _RES_TAG; + +// union for filter information for prepareWave +typedef union +{ + // filter type + WORD wFilterID; + + // structure for volume + struct volume + { + WORD wVolume; + }; + + // structure for delay + struct delay + { + WORD wDelaySamples; + }; + +} _SOS_FILTER; + + +#endif + + \ No newline at end of file diff --git a/code/redalert/win32lib/sound.h b/code/redalert/win32lib/sound.h new file mode 100644 index 0000000..ff72582 --- /dev/null +++ b/code/redalert/win32lib/sound.h @@ -0,0 +1,52 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : SOUND.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : September 1, 1993 * + * * + * Last Update : September 1, 1993 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef SOUND_H +#define SOUND_H + +//#define HMI_DRIVER TRUE +//#include "sos.h" +#include "soscomp.h" +#include "dsound.h" + +/* +** Maximum number of sound effects that may run at once. +*/ +#define MAX_SFX 5 + +/* +** Size of temp HMI low memory staging buffer. +*/ +#define SECONDARY_BUFFER_SIZE (1024*32) + +#endif diff --git a/code/redalert/win32lib/soundint.h b/code/redalert/win32lib/soundint.h new file mode 100644 index 0000000..76171df --- /dev/null +++ b/code/redalert/win32lib/soundint.h @@ -0,0 +1,289 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Westwood 32 bit Library * + * * + * File Name : SOUNDINT.H * + * * + * Programmer : Phil W. Gorrow * + * * + * Start Date : June 23, 1995 * + * * + * Last Update : June 23, 1995 [PWG] * + * * + * This file is the include file for the Westwood Sound Sytem defines and * + * routines that are handled in an interrupt. + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "sound.h" + +/* +** Defines for true and false. These are included because we do not allow +** the sound int to include any of the westwood standard headers. If we +** did, there might be too much temptation to call another library function. +** this would be bad, because then that function would not be locked. +*/ +#define FALSE 0 +#define TRUE 1 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum { + SCOMP_NONE=0, // No compression -- raw data. + SCOMP_WESTWOOD=1, // Special sliding window delta compression. + SCOMP_SONARC=33, // Sonarc frame compression. + SCOMP_SOS=99 // SOS frame compression. +} SCompressType; + +/* +** This is the safety overrun margin for the sonarc compressed +** data frames. This value should be equal the maximum 'order' times +** the maximum number of bytes per sample. It should be evenly divisible +** by 16 to aid paragraph alignment. +*/ +#define SONARC_MARGIN 32 + + +/* +** Define the sample control structure which helps us to handle feeding +** data to the sound interrupt. +*/ +#pragma pack(1); +typedef struct { + /* + ** This flags whether this sample structure is active or not. + */ + unsigned Active; + //unsigned Active:1; + + /* + ** This flags whether the sample is loading or has been started. + */ + //unsigned Loading:1; + unsigned Loading; + + /* + ** This semaphore ensures that simultaneous update of this structure won't + ** occur. This is necessary since both interrupt and regular code can modify + ** this structure. + */ + //unsigned DontTouch:1; + unsigned DontTouch; + + /* + ** If this sample is really to be considered a score rather than + ** a sound effect, then special rules apply. These largely fall into + ** the area of volume control. + */ + //unsigned IsScore:1; + unsigned IsScore; + + /* + ** This is the original sample pointer. It is used to control the sample based on + ** pointer rather than handle. The handle method is necessary when more than one + ** sample could be playing simultaneously. The pointer method is necessary when + ** the dealing with a sample that may have stopped behind the programmer's back and + ** this occurance is not otherwise determinable. It is also used in + ** conjunction with original size to unlock a sample which has been DPMI + ** locked. + */ + void const *Original; + long OriginalSize; + + /* + ** These are pointers to the double buffers. + */ + LPDIRECTSOUNDBUFFER PlayBuffer; + + /* + ** Variable to keep track of the playback rate of this buffer + */ + int PlaybackRate; + + /* + ** Variable to keep track of the sample type ( 8 or 16 bit ) of this buffer + */ + int BitSize; + + /* + ** Variable to keep track of the stereo ability of this buffer + */ + int Stereo; + + /* + ** The number of bytes in the buffer that has been filled but is not + ** yet playing. This value is normally the size of the buffer, + ** except for the case of the last bit of the sample. + */ + LONG DataLength; + + /* + ** This is the buffer index for the low buffer that + ** has been filled with data but not yet being + ** played. + */ +// short int Index; + + /* + ** Pointer into the play buffer for writing the next + ** chunk of sample to + ** + */ + VOID *DestPtr; + + /* + ** This flag indicates that there is more source data + ** to copy to the play buffer + ** + */ + BOOL MoreSource; + + /* + ** This flag indicates that the entire sample fitted inside the + ** direct sound secondary buffer + ** + */ + BOOL OneShot; + + /* + ** Pointer to the sound data that has not yet been copied + ** to the playback buffers. + */ + VOID *Source; + + /* + ** This is the number of bytes remaining in the source data as + ** pointed to by the "Source" element. + */ + LONG Remainder; + + /* + ** Object to use with Enter/LeaveCriticalSection + ** + */ + CRITICAL_SECTION AudioCriticalSection; + + /* + ** Samples maintain a priority which is used to determine + ** which sounds live or die when the maximum number of + ** sounds are being played. + */ + int Priority; + + /* + ** This is the handle as returned by sosDIGIStartSample function. + */ + short int Handle; + + /* + ** This is the current volume of the sample as it is being played. + */ + int Volume; + int Reducer; // Amount to reduce volume per tick. + + /* + ** This is the compression that the sound data is using. + */ + SCompressType Compression; + short int TrailerLen; // Number of trailer bytes in buffer. + BYTE Trailer[SONARC_MARGIN]; // Maximum number of 'order' samples needed. + + + DWORD Pitch; + WORD Flags; + + /* + ** This flag indicates whether this sample needs servicing. + ** Servicing entails filling one of the empty low buffers. + */ + short int Service; + + /* + ** This flag is TRUE when the sample has stopped playing, + ** BUT there is more data available. The sample must be + ** restarted upon filling the low buffer. + */ + BOOL Restart; + + /* + ** Streaming control handlers. + */ + BOOL (*Callback)(short int id, short int *odd, VOID **buffer, LONG *size); + VOID *QueueBuffer; // Pointer to continued sample data. + LONG QueueSize; // Size of queue buffer attached. + short int Odd; // Block number tracker (0..StreamBufferCount-1). + int FilePending; // Number of buffers already filled ahead. + long FilePendingSize; // Number of bytes in last filled buffer. + + /* + ** The file variables are used when streaming directly off of the + ** hard drive. + */ + int FileHandle; // Streaming file handle (ERROR = not in use). + VOID *FileBuffer; // Temporary streaming buffer (allowed to be freed). + /* + ** The following structure is used if the sample if compressed using + ** the sos 16 bit compression Codec. + */ + + _SOS_COMPRESS_INFO sosinfo; + + +} SampleTrackerType; + + +typedef struct LockedData { + unsigned int DigiHandle; // = -1; + BOOL ServiceSomething; // = FALSE; + long MagicNumber; // = 0xDEAF; + VOID *UncompBuffer; // = NULL; + long StreamBufferSize; // = (2*SECONDARY_BUFFER_SIZE)+128; + short StreamBufferCount; // = 32; + SampleTrackerType SampleTracker[MAX_SFX]; + unsigned int SoundVolume; + unsigned int ScoreVolume; + BOOL _int; +} LockedDataType; + +extern LockedDataType LockedData; +#pragma pack(4); + +void Init_Locked_Data(void); +long Simple_Copy(void ** source, long * ssize, void ** alternate, long * altsize, void **dest, long size); +long Sample_Copy(SampleTrackerType *st, void ** source, long * ssize, void ** alternate, long * altsize, void * dest, long size, SCompressType scomp, void * trailer, short int *trailersize); +VOID far __cdecl maintenance_callback(VOID); +VOID __cdecl far DigiCallback(unsigned int driverhandle, unsigned int callsource, unsigned int sampleid); +void far HMI_TimerCallback(void); +void *Audio_Add_Long_To_Pointer(void const *ptr, long size); +void DPMI_Unlock(VOID const *ptr, long const size); +extern "C" { + void __cdecl Audio_Mem_Set(void const *ptr, unsigned char value, long size); +// void Mem_Copy(void *source, void *dest, unsigned long bytes_to_copy); + long __cdecl Decompress_Frame(void * source, void * dest, long size); + int __cdecl Decompress_Frame_Lock(void); + int __cdecl Decompress_Frame_Unlock(void); + int __cdecl sosCODEC_Lock(void); + int __cdecl sosCODEC_Unlock(void); + void __GETDS(void); +} diff --git a/code/redalert/win32lib/stamp.inc b/code/redalert/win32lib/stamp.inc new file mode 100644 index 0000000..c629e23 --- /dev/null +++ b/code/redalert/win32lib/stamp.inc @@ -0,0 +1,56 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +; This is the control structure at the start of a loaded icon set. It must match +; the structure in ICONSET.C! This structure MUST be a multiple of 16 bytes long. + +ifdef NO_WAY_THIS_WILL_BE_DEFINED_HAHAHAHA + + STRUC IControl_Type +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +Map DD ? ; Icon map offset. + ENDS + +else + + + STRUC IControl_Type + +Width DW ? ; Width in pixels (per icon). +Height DW ? ; Height in pixels (per icon). +Count DW ? ; Number of icons in this set. +Allocated DW ? ; Was this iconset allocated? +MapWidth DW ? +MapHeight DW ? +Size DD ? ; Size of entire iconset memory block. +Icons DD ? ; Offset from buffer start to icon data. +Palettes DD ? ; Offset from buffer start to palette data. +Remaps DD ? ; Offset from buffer start to remap index data. +TransFlag DD ? ; Offset for transparency flag data. +ColorMap DD ? +Map DD ? ; Icon map offset. + ENDS + +endif + +ICON256 EQU 1 diff --git a/code/redalert/win32lib/structs.h b/code/redalert/win32lib/structs.h new file mode 100644 index 0000000..995ebb5 --- /dev/null +++ b/code/redalert/win32lib/structs.h @@ -0,0 +1,34 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 Examples * + * * + * File Name : STRUCTS.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + diff --git a/code/redalert/win32lib/svgaprim.inc b/code/redalert/win32lib/svgaprim.inc new file mode 100644 index 0000000..33813ca --- /dev/null +++ b/code/redalert/win32lib/svgaprim.inc @@ -0,0 +1,74 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** +;*************************************************************************** +;* * +;* Project Name : Westwood 32 bit Library * +;* * +;* File Name : SVGAPRIM.INC * +;* * +;* Programmer : Phil W. Gorrow * +;* * +;* Start Date : December 8, 1994 * +;* * +;* Last Update : December 8, 1994 [PWG] * +;* * +;*-------------------------------------------------------------------------* + +; Externs from VIDEO.CPP module of the VIDEO library +GLOBAL BankTable :DWORD +GLOBAL VesaFunc :DWORD +GLOBAL XRes :DWORD +GLOBAL YRes :DWORD +GLOBAL CurrentMode :DWORD +global cpu_video_page :dword +global cpu_page_limit :dword + + +; Externs from VESA.ASM module of the SVGAPRIM library +GLOBAL Vesa_Asm_Set_Win :NEAR +GLOBAL Vesa_Asm_Next_Win :NEAR + +; Externs from VGETPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Get_Pixel :NEAR + +; Externs from VPUTTPIX.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Put_Pixel :NEAR + + +; Externs from VCLEAR.ASM module of the MCGA/SVGAPRIM library +GLOBAL Vesa_Clear :NEAR + +; Externs from VBITBLIT.ASM module of the MCGA/SVGAPRIM library +GLOBAL Linear_Blit_To_Vesa :NEAR +GLOBAL Vesa_Blit_To_Linear :NEAR +GLOBAL Vesa_Blit_To_Vesa :NEAR + +; Externs from VTOBUFF.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_To_Buffer :NEAR + +; Externs from VTOPAGE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Buffer_To_Page :NEAR + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Linear_Scale_To_Vesa :NEAR +GLOBAL Vesa_Scale_To_Linear :NEAR +GLOBAL Vesa_Scale_To_Vesa :NEAR + + +; Externs from VSCALE.ASM module of the SVGA/MCGAPRIM library +GLOBAL Vesa_Print :NEAR diff --git a/code/redalert/win32lib/tile.h b/code/redalert/win32lib/tile.h new file mode 100644 index 0000000..30b7f9a --- /dev/null +++ b/code/redalert/win32lib/tile.h @@ -0,0 +1,91 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the TILE Library * + * * + * File Name : TILE.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 2, 1995 * + * * + * Last Update : February 2, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TILE_H +#define TILE_H + +/*=========================================================================*/ +/* The following prototypes are for the file: ICONSET.CPP */ +/*=========================================================================*/ +void * Load_Icon_Set(char const *filename, void *iconsetptr, long buffsize); +void Free_Icon_Set(void const *iconset); +long Get_Icon_Set_Size(void const *iconset); +int Get_Icon_Set_Width(void const *iconset); +int Get_Icon_Set_Height(void const *iconset); +void * Get_Icon_Set_Icondata(void const *iconset); +void * Get_Icon_Set_Trans(void const *iconset); +void * Get_Icon_Set_Remapdata(void const *iconset); +void * Get_Icon_Set_Palettedata(void const *iconset); +int Get_Icon_Set_Count(void const *iconset); +void * Get_Icon_Set_Map(void const *iconset); +#if (0) +/* +** This is the control structure at the start of a loaded icon set. It must match +** the structure in WWLIB.I! This structure MUST be a multiple of 16 bytes long. +*/ +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. +// BOOL Allocated; // Was this iconset allocated? + short Allocated; // Was this iconset allocated? + long Size; // Size of entire iconset memory block. + unsigned char *Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + unsigned char *Map; // Icon map offset (if present). +} IControl_Type; +#endif //(0) + +typedef struct { + short Width; // Width of icons (pixels). + short Height; // Height of icons (pixels). + short Count; // Number of (logical) icons in this set. + short Allocated; // Was this iconset allocated? + short MapWidth; // Width of map (in icons). + short MapHeight; // Height of map (in icons). + long Size; // Size of entire iconset memory block. + long Icons; // Offset from buffer start to icon data. +// unsigned char * Icons; // Offset from buffer start to icon data. + long Palettes; // Offset from buffer start to palette data. + long Remaps; // Offset from buffer start to remap index data. + long TransFlag; // Offset for transparency flag table. + long ColorMap; // Offset for color control value table. + long Map; // Icon map offset (if present). +// unsigned char * Map; // Icon map offset (if present). +} IControl_Type; + + +#endif //TILE_H + diff --git a/code/redalert/win32lib/timer.cpp b/code/redalert/win32lib/timer.cpp new file mode 100644 index 0000000..fb1cc38 --- /dev/null +++ b/code/redalert/win32lib/timer.cpp @@ -0,0 +1,200 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : May 3, 1995 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * TC::Time -- Return the time on the timer. * + * TC::TimerClass -- Construct a timer class object. * + * TC::Stop -- Stop the timer. * + * TC::Start -- Start a timer. * + * TC::Set -- Set the time of a timer. * + * TC::Reset -- Clear the timer. * + * TimerClass::Time -- Get the current time of timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" +#include +#include + + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + +/*************************************************************************** + * TC::TIMERCLASS -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +TimerClass::TimerClass(BaseTimerEnum timer, BOOL on) +{ + Accumulated = 0; + Started = 0; + + TickType=timer; + + if (on && TimerSystemOn) Start(); +} + + + + +/*********************************************************************************************** + * TC:Get_Ticks -- return the number of ticks on the system or user timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:17PM ST : Created * + *=============================================================================================*/ + +long TimerClass::Get_Ticks ( void ) + +{ + if ( WindowsTimer ){ + switch ( TickType ){ + + case BT_SYSTEM : + return ( WindowsTimer->Get_System_Tick_Count() ); + + case BT_USER : + return ( WindowsTimer->Get_User_Tick_Count() ); + + } + } + return 0; +} + + + +/*************************************************************************** + * TIMERCLASS::TIME -- Get the current time of timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/03/1995 SKB : Created. * + *=========================================================================*/ +long TimerClass::Time(void) +{ + if (Started) { + long ticks = Get_Ticks(); + Accumulated += ticks - (Started-1); + Started = ticks+1; + } + return(Accumulated); +} + + +/*************************************************************************** + * TC::STOP -- Stop the timer. * + * * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Stop(void) +{ + long time = Time(); + Started = 0; + return(time); +} + + +/*************************************************************************** + * TC::START -- Start a timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long TimerClass::Start(void) +{ + if (!Started) { + Started = Get_Ticks()+1; + } + return(Time()); +} + + +/*************************************************************************** + * TC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: long value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + * 05/03/1995 SKB : If start return Start since it returns Time * + *=========================================================================*/ +long TimerClass::Set(long value, BOOL start) +{ + Started = 0; + Accumulated = value; + if (start) return (Start()); + return(Time()); +} \ No newline at end of file diff --git a/code/redalert/win32lib/timer.h b/code/redalert/win32lib/timer.h new file mode 100644 index 0000000..fb5d986 --- /dev/null +++ b/code/redalert/win32lib/timer.h @@ -0,0 +1,198 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Timer Class Functions * + * * + * File Name : TIMER.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef TIMER_H +#define TIMER_H + + +#ifndef WIN32 +#define WIN32 1 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#endif +#include +#include + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMERA.ASM */ +/*=========================================================================*/ + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Externs ///////////////////////////////////////////// +extern BOOL TimerSystemOn; +extern HANDLE TimerThreadHandle; //Handle of timer thread +extern int InTimerCallback; //true if we are currently in a callback + + +/*=========================================================================*/ +typedef enum BaseTimerEnum { + BT_SYSTEM, // System timer (60 / second). + BT_USER // User controllable timer (? / second). +} BaseTimerEnum; + +class TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + TimerClass(BaseTimerEnum timer=BT_SYSTEM, BOOL start=FALSE); + + // No destructor. + ~TimerClass(void){} + + // + long Set(long value, BOOL start=TRUE); // Set initial timer value. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Time(void); // Fetch current timer value. + + protected: + long Started; // Time last started (0 == not paused). + long Accumulated; // Total accumulated ticks. + + private: +// long (*Get_Ticks)(void); // System timer fetch. + BaseTimerEnum TickType; + long Get_Ticks (void); +}; + + +inline long TimerClass::Reset(BOOL start) +{ + return(Set(0, start)); +} + + +class CountDownTimerClass : private TimerClass { + public: + // Constructor. Timers set before low level init has been done will not + // be able to be 'Started' or 'on' until timer system is in place. + CountDownTimerClass(BaseTimerEnum timer, long set, int on=FALSE); + CountDownTimerClass(BaseTimerEnum timer=BT_SYSTEM, int on=FALSE); + + // No destructor. + ~CountDownTimerClass(void){} + + // Public functions + long Set(long set, BOOL start=TRUE); // Set count down value. + long Reset(BOOL start=TRUE); // Reset timer to zero. + long Stop(void); // Pause timer. + long Start(void); // Resume timer. + long Time(void); // Fetch current count down value. + + protected: + long DelayTime; // Ticks remaining before countdown timer expires. +}; + +inline long CountDownTimerClass::Stop(void) +{ + TimerClass::Stop(); + return(Time()); +} + +inline long CountDownTimerClass::Start(void) +{ + TimerClass::Start(); + return(Time()); +} + +inline long CountDownTimerClass::Reset(BOOL start) +{ + return (TimerClass::Reset(start)); +} + + + + +class WinTimerClass { + + public: + WinTimerClass ( UINT freq=60 , BOOL partial=0 ); + ~WinTimerClass(); + + void Update_Tick_Count ( void ); + unsigned Get_System_Tick_Count ( void ); + unsigned Get_User_Tick_Count ( void ); + + private: + + unsigned TimerHandle; //Handle for windows timer event + unsigned Frequency; //Frequency of our windows timer in ticks per second + + unsigned TrueRate; //True rate of clock. (only use word) + unsigned SysTicks; //Tick count of timer. + unsigned UserTicks; //Tick count of timer. + unsigned UserRate; //Desired rate of timer. + + +}; + + +extern WinTimerClass *WindowsTimer; + + + + + + + + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// externs ////////////////////////////////////////// +#ifndef FUNCTION_H +extern TimerClass TickCount; +#endif +extern CountDownTimerClass CountDown; + +////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////// Prototypes ////////////////////////////////////////// + +extern "C" { + long __cdecl Get_System_Tick_Count(void); + long __cdecl Get_User_Tick_Count(void); + void far __cdecl Timer_Interrupt_Func(void); +// long Get_Num_Interrupts(unsigned int realmode); + void __cdecl Disable_Timer_Interrupt(void); + void __cdecl Enable_Timer_Interrupt(void); +} + +/*=========================================================================*/ +/* The following prototypes are for the file: TIMER.CPP */ +/*=========================================================================*/ +BOOL __cdecl Init_Timer_System(unsigned int freq, int partial = FALSE); +BOOL __cdecl Remove_Timer_System(VOID); + + +#endif // TIMER_H + diff --git a/code/redalert/win32lib/timerdwn.cpp b/code/redalert/win32lib/timerdwn.cpp new file mode 100644 index 0000000..90fea6e --- /dev/null +++ b/code/redalert/win32lib/timerdwn.cpp @@ -0,0 +1,123 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMER.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 12, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * CDTC::Time -- Return the time on the timer. * + * CDTC::Stop -- Stop the timer. * + * CDTC::Start -- Start a timer. * + * CDTC::DownTimerClass -- Construct a timer class object. * + * CDTC::Set -- Set the time of a timer. * + * CDTC::Reset -- Clear the timer. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include "timer.H" + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + +/*************************************************************************** + * TC::CountDownTimerClass -- Construct a timer class object. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, long set, int on) + :TimerClass(timer, on) +{ + Set(set, on); +} + +CountDownTimerClass::CountDownTimerClass(BaseTimerEnum timer, int on) + :TimerClass(timer, FALSE) +{ + DelayTime = 0; + if (on) Start(); +} + + +/*************************************************************************** + * CDTC::TIME -- Return the time on the timer. * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Time() +{ + long ticks = DelayTime - TimerClass::Time(); + + if (ticks < 0) { + ticks = 0; + } + return(ticks); +} + + +/*************************************************************************** + * CDTC::SET -- Set the time of a timer. * + * * + * * + * * + * INPUT: ULONG value to set timer at. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/12/1994 SKB : Created. * + *=========================================================================*/ +long CountDownTimerClass::Set(long value, BOOL start) +{ + DelayTime = value; + TimerClass::Reset(start); + return(Time()); +} + + + diff --git a/code/redalert/win32lib/timerini.cpp b/code/redalert/win32lib/timerini.cpp new file mode 100644 index 0000000..f453fc8 --- /dev/null +++ b/code/redalert/win32lib/timerini.cpp @@ -0,0 +1,314 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Temp timer for 32bit lib * + * * + * File Name : TIMERINI.CPP * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : July 6, 1994 * + * * + * Last Update : July 6, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Init_Timer_System -- Initialize the WW timer system. * + * Remove_Timer_System -- Removes the timer system. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "timer.H" +#include +#include + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Defines ///////////////////////////////////// + +#define COPY_FROM_MEM TRUE + +///////////////////////////////////////////////////////////////////////////////// +////////////////////////////// timera.asm functions////////////////////////////// + +#ifdef __cplusplus +extern "C" { +#endif + +extern BOOL Install_Timer_Interrupt(VOID *bin_ptr, UINT rm_size, UINT freq, BOOL partial); +extern BOOL Remove_Timer_Interrupt(VOID); + +#ifdef __cplusplus +} +#endif + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////// Global Data ///////////////////////////////////// + +BOOL TimerSystemOn = FALSE; + +// Global timers that the library or user can count on existing. +TimerClass TickCount(BT_SYSTEM); +CountDownTimerClass CountDown(BT_SYSTEM, 0); + + +// Prototype for timer callback +void CALLBACK Timer_Callback ( UINT event_id, UINT res1 , DWORD user, DWORD res2, DWORD res3 ); + +HANDLE TimerThreadHandle = 0; //Handle of timer thread +int InTimerCallback = 0; //Flag to say if we are in a timer callback + +///////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Code //////////////////////////////////////// + + + +#pragma warning (disable : 4996) + + +/*************************************************************************** + * WinTimerClass::WinTimerClass -- Initialize the WW timer system. * + * * + * * + * INPUT: UINT : user timer frequency. * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::WinTimerClass (UINT freq, BOOL partial) +{ + BOOL success; + + // + // Inform windows that we want a higher than normal + // timer resolution + // +#ifdef __SW_EP + timeBeginPeriod(1000/PROFILE_RATE); + Frequency = PROFILE_RATE; +#else + timeBeginPeriod ( 1000/freq ); + Frequency = freq; +#endif + + SysTicks = 0; + UserTicks = 0; + + // + // Install the timer callback event handler + // + //TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC); + // Add TIME_KILL_SYNCHRONOUS flag. ST - 2/13/2019 5:07PM + TimerHandle = timeSetEvent ( 1000/freq , 1 , Timer_Callback , 0 , TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + TimerSystemOn = success = ( TimerHandle !=0 ); + + if (success) { + if (!partial) { + WindowsTimer=this; + TickCount.Start(); + } + }else{ + char error_str [128]; + sprintf (error_str, "Error - timer system failed to start. Error code %d\n", GetLastError()); + OutputDebugString(error_str); + } +} + + + +/*************************************************************************** + * WinTimerClass::~WinTimerClass -- Removes the timer system. * + * * + * * + * INPUT: NONE. * + * * + * OUTPUT: BOOL was it removed successfuly * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/5/95 3:47PM : ST Created. * + *=========================================================================*/ +WinTimerClass::~WinTimerClass( void ) +{ + + if ( TimerHandle ){ + timeKillEvent ( TimerHandle ); + TimerHandle = 0; //ST - 2/13/2019 5:12PM + } + + TimerSystemOn = FALSE; + timeEndPeriod ( 1000/Frequency ); +} + + + + + +/*********************************************************************************************** + * Timer_Callback -- Main timer callback. Equivalent to a timer interrupt handler * + * * + * * + * * + * INPUT: uint timer ID * + * uint reserved * + * long 0 (application defined) * + * long reserved * + * long reserved * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:19PM ST : Created * + *=============================================================================================*/ + + +void CALLBACK Timer_Callback (UINT , UINT , DWORD , DWORD , DWORD) +{ + //CONTEXT context; + + InTimerCallback++; + + // Removed. ST - 2/13/2019 5:11PM + //if (!TimerThreadHandle){ + // DuplicateHandle (GetCurrentProcess(), GetCurrentThread() , GetCurrentProcess() ,&TimerThreadHandle , 0 , TRUE , DUPLICATE_SAME_ACCESS); + //} + + + if (WindowsTimer) { + WindowsTimer->Update_Tick_Count(); + } + InTimerCallback--; +} + + + + + + +/*********************************************************************************************** + * WinTimerClass::Update_Tick_Count -- update westwood timers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 3:58PM ST : Created * + *=============================================================================================*/ + +void WinTimerClass::Update_Tick_Count ( void ) +{ +/* + * + * Increment westwood timers + * + */ + SysTicks++; + UserTicks++; + +} + + + + + + + + +/* +;*************************************************************************** +;* GET_NUM_INTERRUPTS -- Returns the number of interrupts that have occured* +;* * +;* INPUT: TRUE - returns num RM ints. * +;* FALSE - return num PM ints. * +;* * +;* OUTPUT: * +;* * +;* WARNINGS: * +;* * +;* HISTORY: * +;* 07/12/1994 SKB : Created. * +;*=========================================================================* + PROC Get_Num_Interrupts C Near + USES esi + ARG realmode:DWORD + + mov esi,[RealModePtr] + cmp [realmode],0 + je ??prot_mode + mov eax,[(TimerType PTR esi).NumRMInts] + ret +??prot_mode: + mov eax,[(TimerType PTR esi).NumPMInts] + ret + + ENDP + */ + + + + +/*********************************************************************************************** + * WinTimerClass::Get_System_Tick_Count -- returns the system tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_System_Tick_Count ( void ) +{ + return ( SysTicks ); +} + + + +/*********************************************************************************************** + * WinTimerClass::Get_User_Tick_Count -- returns the user tick count * + * * + * INPUT: Nothing * + * * + * OUTPUT: tick count * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/5/95 4:02PM ST : Created * + *=============================================================================================*/ + +unsigned WinTimerClass::Get_User_Tick_Count ( void ) +{ + return ( UserTicks ); +} \ No newline at end of file diff --git a/code/redalert/win32lib/tobuff.cpp b/code/redalert/win32lib/tobuff.cpp new file mode 100644 index 0000000..ff729e8 --- /dev/null +++ b/code/redalert/win32lib/tobuff.cpp @@ -0,0 +1,70 @@ +/** + * @file + * + * @author CCHyper + * @author OmniBlade + * + * @brief Low level software blitters. + * + * @copyright Chronoshift is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version + * 2 of the License, or (at your option) any later version. + * A full copy of the GNU General Public License can be found in + * LICENSE + */ + +#include +#include "gbuffer.h" +#include "misc.h" + +long __cdecl Buffer_To_Buffer(GraphicViewPortClass* vp, int x, int y, int w, int h, void* buffer, int length) +{ + int xstart = x; + int ystart = y; + int xend = x + w - 1; + int yend = y + h - 1; + + int xoffset = 0; + int yoffset = 0; + + // If we aren't drawing within the viewport, return + if (!buffer || length <= 0 || xstart >= vp->Get_Width() || ystart >= vp->Get_Height() || xend < 0 || yend < 0) { + return 0; + } + + // Clipping + if (xstart < 0) { + xoffset = -xstart; + xstart = 0; + } + + if (ystart < 0) { + yoffset += h * (-ystart); + ystart = 0; + } + + xend = min(xend, (int)vp->Get_Width() - 1); + yend = min(yend, (int)vp->Get_Height() - 1); + + // Setup parameters for blit + int pitch = vp->Get_Pitch() + vp->Get_Width() + vp->Get_XAdd(); + uint8_t* src = y * pitch + x + (uint8_t*)(vp->Get_Offset()); + uint8_t* dst = xoffset + w * yoffset + (uint8_t*)(buffer); + // int src_pitch = x_pos + pitch - xend + 1; + // int dst_pitch = x_pos + width - xend + 1; + int lines = yend - ystart + 1; + int blit_width = xend - xstart + 1; + + // Is our buffer large enough? + if (lines * w <= length) { + // blit + while (lines--) { + memcpy(dst, src, blit_width); + src += pitch; + dst += w; + } + } + + return 1; +} diff --git a/code/redalert/win32lib/video.h b/code/redalert/win32lib/video.h new file mode 100644 index 0000000..223d4e6 --- /dev/null +++ b/code/redalert/win32lib/video.h @@ -0,0 +1,216 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : 32 bit library * + * * + * File Name : VIDEO.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef VIDEO_H +#define VIDEO_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +/*=========================================================================*/ +/* The machine can be in one of the following graphic modes. The global */ +/* GraphicMode is set to one of these values. */ +/*=========================================================================*/ +#define CGA_MODE 4 // DOS 320x200 4 color mode +#define TGA_MODE 9 // TANDY 320x200 16 color mode +#define EGA_MODE 13 // DOS 320x200 16 color mode +#define MCGA_MODE 0x13 // DOS 320x200 256 color mode +#define VGA_MODE 0x13 // DOS 320x200 256 color mode +#define EEGA_MODE 14 // DOS 640x400 16 color mode +#define ETGA_MODE 9 // TANDY 640x400 16 color mode +#define HGA_MODE 7 // DOS 768x400 2 color mode +#define TXT_MODE 3 // DOS plain old color text mode +#define VESA_640X400_256 0x100 // VESA 640x400 256 color mode +#define VESA_640X480_256 0x101 // VESA 640x480 256 color mode +#define VESA_800X600_256 0x103 // VESA 800x600 256 color mode +#define VESA_1024X768_256 0x105 // VESA 1024x768 256 color mode +#define VESA_1280X400_256 0x107 // VESA 1280x400 256 color mode +#define VESA_TEXT_80X60 0x108 // VESA 80x60 text mode +#define VESA_TEXT_132X25 0x109 // VESA 132x25 text mode +#define VESA_TEXT_132X60 0x10C // VESA 132x60 text mode +#define RESET_MODE -1 +#define UNINITIALIZED_MODE -1 +#define VESA_MIN VESA_640X400_256 +#define VESA_MAX VESA_TEXT_132X60 + +/*=========================================================================*/ +/* Define the maximum number of bank entries */ +/*=========================================================================*/ +#define MAX_BANK_ENTRIES ((1280L*1024L)/65536L) + + +/*=========================================================================* + * VesaInfoType - General info about this VESA implementation * + * (Filled in by VESA BIOS Function 0) * + * * + * Signature - Will always be 'VESA' * + * Version - Version # * + * OEMString - OEM ID string * + * Capabilities - Not defined by VESA yet * + * AvailModes - List of available modes; terminated with -1 (0xffff) * + * TotalMemory - ??? * + * Reserved - Pads structure to 256 bytes total * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + char Signature[4]; + short Version; + REALPTR OEMString; + long Capabilities; + REALPTR AvailModes; + short TotalMemory; + char Reserved[236]; +} VesaInfoType; + +#endif //NOT_FOR_WIN95 + +/*=========================================================================* + * VesaModeInfoType - Info about this VESA mode * + * (Filled in by VESA BIOS Function 1) * + * * + * Attributes - bit 0: 1 = mode is supported * + * bit 1: 1 = optional info available * + * bit 2: 1 = std BIOS output funcs valid in this mode * + * bit 3: 0 = monochrome, 1 = color * + * bit 4: 0 = text mode, 1 = graphics * + * WinA_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinB_Attributes - bit 0 = win exists, bit 1=readable, bit 2= writable * + * WinGranularity - smallest address boundary window can be placed upon; * + * size is in KB (ie 64, 32, 4) * + * WinSize - size of windows in KB (ie 64, 32) * + * WinA_Segment - location of Window A in CPU space (usually 0xa000) * + * WinB_Segment - location of Window B in CPU space (usually 0xb000) * + * WinFunc - address of window-setting function (This is provided * + * as an alternative to Int 10 for speed.) * + * BytesPerScanline - # bytes per scan line * + * * + * Optional info (available if bit 1 of Attributes is set): * + * * + * XRes - X-resolution * + * YRes - Y-resolution * + * XCharSize - Horizontal size of char cell * + * YCharSize - Vertical size of char cell * + * NumPlanes - # of memory planes (???) * + * BitsPerPixel - # bites per pixel * + * NumBanks - # of banks (ie planes) * + * MemoryModel - 00h = Text mode * + * 01h = CGA mode * + * 02h = Hercules * + * 03h = 4 plane planar mode * + * 04h = packed pixel mode (1 byte/pixel) * + * 05h = non-chain 4, 256-color mode * + * 06-0Fh = * + * 10-FFh = OEM-specific * + * BankSize - Bank size in KB * + *=========================================================================*/ +#ifdef NOT_FOR_WIN95 +typedef struct { + short Attributes; + char WinA_Attributes; + char WinB_Attributes; + short WinGranularity; + short WinSize; + short WinA_Segment; + short WinB_Segment; + REALPTR WinFunc; + short BytesPerScanline; + short XRes; + short YRes; + char XCharSize; + char YCharSize; + char NumPlanes; + char BitsPerPixel; + char NumBanks; + char MemoryModel; + char BankSize; + char NumInputPages; + char Reserved; + char RedMaskSize; + char RedFieldPosition; + char GreenMaskSize; + char GreenFieldPosition; + char BlueMaskSize; + char BlueFieldPosition; + char RsvdMaskSize; + char RsvdFieldPosition; + char DirectColorModeInfo; + char pad[216]; +} VesaModeInfoType; + +extern REALPTR VesaFunc; + +#endif //NOT_FOR_WIN95 + + +extern "C" { +extern int GraphicMode; +extern long XRes; +extern long YRes; + +extern long BankTable []; +extern unsigned long RMVesaVector ; +extern unsigned long RMVesaRegs ; +} + +/*=========================================================================*/ +/* The following prototypes are for the file: VIDEO.CPP */ +/*=========================================================================*/ + +extern "C" int Set_Video_Mode(int mode); +int Get_Video_Mode(void); +void Update_Video_Mode (void) ; +void Vesa_Info(void); +void Vesa_Set_Window(long grain_num); +int Get_Original_Video_Mode(void); +void Set_Original_Video_Mode(int mode); + +/*=========================================================================*/ +/* The following prototypes are for the file: INITDLAY.CPP */ +/*=========================================================================*/ + +extern VOID Init_Delay(VOID); +extern BOOL VertBlank; + +/*=========================================================================*/ +/* The following prototypes are for the file: VERTBLNK.ASM */ +/*=========================================================================*/ + +extern "C" { + extern WORD Get_Vert_Blank(VOID); + extern VOID Wait_Vert_Blank(BOOL blank); +} + +/*=========================================================================*/ + +#endif // VIDEO_H \ No newline at end of file diff --git a/code/redalert/win32lib/wincomm.h b/code/redalert/win32lib/wincomm.h new file mode 100644 index 0000000..5d26049 --- /dev/null +++ b/code/redalert/win32lib/wincomm.h @@ -0,0 +1,454 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer/ WW Library * + * * + * File Name : WINCOMM.H * + * * + * Programmer : Steve Tall * + * * + * Start Date : 1/10/96 * + * * + * Last Update : January 10th 1996 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * * + * These classes was created to replace the greenleaf comms functions used in C&C DOS with * + * WIN32 API calls. * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef WIN32 +#define WIN32 +#define _WIN32 +#endif //WIN32 +#include + +typedef enum WinCommDialMethodType { + WC_TOUCH_TONE = 0, + WC_PULSE +} WinCommDialMethodType; + + + +#define COMMSUCCESS 0 +#define ASTIMEOUT -10 +#define COMMUSERABORT -16 + + +/* +** The size of our serial buffer within the class. +** +** !!!!!! THIS MUST BE A POWER OF 2 !!!!!! +** +*/ +#define SIZE_OF_WINDOWS_SERIAL_BUFFER 2048 + + + +/* +** WinModemClass. +** +** This class provides access to modems under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +*/ + +class WinModemClass +{ + + public: + + WinModemClass (void); //WinModemClass Contructor + virtual ~WinModemClass (void); //WinModemClass Destructor + + + /* + ** Serial port open should be called to get a handle to the COM port + ** This needs to be called first as other class members rely on the handle + ** + ** Replacement for Greenleaf function: PortOpenGreenleafFast + */ + //virtual HANDLE Serial_Port_Open (int port, int baud, int parity, int wordlen, int stopbits); + virtual HANDLE Serial_Port_Open (char *device_name, int baud, int parity, int wordlen, int stopbits, int flowcontrol); + + /* + ** This function releases the COM port handle and should be called after + ** communications have finished + ** + ** Replacement for Greenleaf function: PortClose + */ + void Serial_Port_Close (void); + + /* + ** This member copies any bytes from the internal class serial buffer + ** into your user buffer. + ** + ** Replacement for Greenleaf function: ReadBuffer + */ + int Read_From_Serial_Port (unsigned char *dest_ptr, int buffer_len); + + /* + ** Write chars to the serial port + ** + ** Replacement for Greenleaf function: WriteBuffer + */ + void Write_To_Serial_Port (unsigned char *buffer, int length); + + /* + ** Wait for the outgoing buffer to empty + */ + void Wait_For_Serial_Write (void); + + /* + ** Set the dial type to DIAL_TOUCH_TONE or DIAL_PULSE + ** + ** Replacement for Greenleaf function: HMSetDiallingMethod + */ + virtual void Set_Modem_Dial_Type (WinCommDialMethodType method); + + /* + ** Get the status of the modem control lines + ** Possible flags are: CTS_SET DSR_SET RI_SET & CD_SET + ** + ** Replacement for Greenleaf function: GetModemStatus + */ + virtual unsigned Get_Modem_Status (void); + + /* + ** Set the DTR line to the given state + ** + ** Replacement for Greenleaf function: SetDtr + */ + virtual void Set_Serial_DTR (BOOL state); + + /* + ** Get the result code from the modem after issuing an 'AT' command + ** + ** Replacement for Greenleaf function: HMInputLine + */ + virtual int Get_Modem_Result (int delay, char *buffer, int buffer_len); + + /* + ** Issue a dial command to the modem. + ** Use Set_Modem_Dial_Type to select pulse or tone dial + ** + ** Replacement for Greenleaf function: HMDial + */ + virtual void Dial_Modem (char *dial_number); + + /* + ** Send a command to the modem. This is usually an 'AT' command. + ** Function will optionally retry until 'OK' is received. + */ + virtual int Send_Command_To_Modem (char *command, char terminator, char *buffer, int buflen, int delay, int retries); + + /* + ** Sets a pointer to a function that will be called for each incoming serial char + ** + ** Replacement for Greenleaf function: HMSetUpEchoRoutine + */ + virtual void Set_Echo_Function (void(*func)(char c)); + + /* + ** Sets a pointer to a function that will be called if ESC is pressed during a dial + ** + ** Replacement for Greenleaf function: HMSetUpAbortKey + */ + virtual void Set_Abort_Function (int (*func)(void)); + + /* + ** Member to allow access to the serial port handle + */ + HANDLE Get_Port_Handle(void); + + /* + ** Status vars for debugging purposes + */ + int FramingErrors; + int IOErrors; + int BufferOverruns; + int InBufferOverflows; + int ParityErrors; + int OutBufferOverflows; + int InQueue; + int OutQueue; + + /* + ** Modem send result codes + */ + enum SendModemEnum { + MODEM_CMD_TIMEOUT = 0, + MODEM_CMD_OK, + MODEM_CMD_0, + MODEM_CMD_ERROR + }; + + + /* + ** Enums for modem status flags + */ + enum { + CTS_SET = 0x10, + DSR_SET = 0x20, + RI_SET = 0x40, + CD_SET = 0x80 + }; + + + protected: + + + /* + ** Copy incoming data from the windows file buffer into the internal class buffer + */ + BOOL Read_Serial_Chars(void); + + /* + ** Pointer to the internal class circular buffer for incoming data + */ + unsigned char *SerialBuffer; + + /* + ** Overlap object for asyncronous reads from the serial port + */ + OVERLAPPED ReadOverlap; + + /* + ** Overlap object for asyncronous writes to the serial port + */ + OVERLAPPED WriteOverlap; + + /* + ** Flag that there is no outstanding incoming data in the windows buffer + */ + BOOL WaitingForSerialCharRead; + + /* + ** Flag that we are waiting for the last write to port operation to complete + */ + BOOL WaitingForSerialCharWrite; + + /* + ** Head and Tail pointers for our internal serial buffer + */ + int SerialBufferReadPtr; + int SerialBufferWritePtr; + + /* + ** Windows handle to the COM port device + */ + HANDLE PortHandle; + + /* + ** Dialing method - DIAL_TOUCH_TONE or DIAL_PULSE + */ + WinCommDialMethodType DialingMethod; + + /* + ** Pointer to function for echoing incoming data - can be NULL + */ + void (*EchoFunction)(char c); + + /* + ** Pointer to function for aborting when ESC pressed - can be NULL + */ + int (*AbortFunction)(void); + + /* + ** Serial buffer for asyncronous reads + */ + char TempSerialBuffer[SIZE_OF_WINDOWS_SERIAL_BUFFER]; +}; + + + + + + + + + + +/* +** WinNullModemClass. +** +** This class provides access to serial ports under Win95. The functions are designed to be more or less +** drop in replacements for the Grenleaf comms functions. +** +** This class just overloads the WinModemClass members that arent required for direct serial communications +** via a 'null modem' cable. +*/ +class WinNullModemClass : public WinModemClass +{ + + public: + + virtual inline void Set_Modem_Dial_Type (int){}; + virtual inline unsigned Get_Modem_Status (void){return (0);}; + virtual inline void Set_Serial_DTR (BOOL){}; + virtual inline int Get_Modem_Result (int, char*, int){return(0);}; + virtual inline void Dial_Modem (char*){}; + virtual inline int Send_Command_To_Modem (char*, char, char*, int, int, int){return (0);}; + virtual inline void Set_Echo_Function (void(*)(char)){}; + virtual inline void Set_Abort_Function (int(*)(void)){}; + +}; + + +extern WinModemClass *SerialPort; + + + + + + + + + +// +// +// This bit swiped from the SDK because its not in the Watcom headers yet +// +// + +/************************************************************************ +* * +* mcx.h -- This module defines the 32-Bit Windows MCX APIs * +* * +* Copyright (c) 1990-1995, Microsoft Corp. All rights reserved. * +* * +************************************************************************/ + +#ifndef _MCX_H_ +#define _MCX_H_ + +typedef struct _MODEMDEVCAPS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // product and version identification + DWORD dwModemProviderVersion; + DWORD dwModemManufacturerOffset; + DWORD dwModemManufacturerSize; + DWORD dwModemModelOffset; + DWORD dwModemModelSize; + DWORD dwModemVersionOffset; + DWORD dwModemVersionSize; + + // local option capabilities + DWORD dwDialOptions; // bitmap of supported values + DWORD dwCallSetupFailTimer; // maximum in seconds + DWORD dwInactivityTimeout; // maximum in seconds + DWORD dwSpeakerVolume; // bitmap of supported values + DWORD dwSpeakerMode; // bitmap of supported values + DWORD dwModemOptions; // bitmap of supported values + DWORD dwMaxDTERate; // maximum value in bit/s + DWORD dwMaxDCERate; // maximum value in bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS; + +typedef struct _MODEMSETTINGS { + DWORD dwActualSize; + DWORD dwRequiredSize; + DWORD dwDevSpecificOffset; + DWORD dwDevSpecificSize; + + // static local options (read/write) + DWORD dwCallSetupFailTimer; // seconds + DWORD dwInactivityTimeout; // seconds + DWORD dwSpeakerVolume; // level + DWORD dwSpeakerMode; // mode + DWORD dwPreferredModemOptions; // bitmap + + // negotiated options (read only) for current or last call + DWORD dwNegotiatedModemOptions; // bitmap + DWORD dwNegotiatedDCERate; // bit/s + + // Variable portion for proprietary expansion + BYTE abVariablePortion [1]; +} MODEMSETTINGS, *PMODEMSETTINGS, *LPMODEMSETTINGS; + +// Dial Options +#define DIALOPTION_BILLING 0x00000040 // Supports wait for bong "$" +#define DIALOPTION_QUIET 0x00000080 // Supports wait for quiet "@" +#define DIALOPTION_DIALTONE 0x00000100 // Supports wait for dial tone "W" + +// SpeakerVolume for MODEMDEVCAPS +#define MDMVOLFLAG_LOW 0x00000001 +#define MDMVOLFLAG_MEDIUM 0x00000002 +#define MDMVOLFLAG_HIGH 0x00000004 + +// SpeakerVolume for MODEMSETTINGS +#define MDMVOL_LOW 0x00000000 +#define MDMVOL_MEDIUM 0x00000001 +#define MDMVOL_HIGH 0x00000002 + +// SpeakerMode for MODEMDEVCAPS +#define MDMSPKRFLAG_OFF 0x00000001 +#define MDMSPKRFLAG_DIAL 0x00000002 +#define MDMSPKRFLAG_ON 0x00000004 +#define MDMSPKRFLAG_CALLSETUP 0x00000008 + +// SpeakerMode for MODEMSETTINGS +#define MDMSPKR_OFF 0x00000000 +#define MDMSPKR_DIAL 0x00000001 +#define MDMSPKR_ON 0x00000002 +#define MDMSPKR_CALLSETUP 0x00000003 + +// Modem Options +#define MDM_COMPRESSION 0x00000001 +#define MDM_ERROR_CONTROL 0x00000002 +#define MDM_FORCED_EC 0x00000004 +#define MDM_CELLULAR 0x00000008 +#define MDM_FLOWCONTROL_HARD 0x00000010 +#define MDM_FLOWCONTROL_SOFT 0x00000020 +#define MDM_CCITT_OVERRIDE 0x00000040 +#define MDM_SPEED_ADJUST 0x00000080 +#define MDM_TONE_DIAL 0x00000100 +#define MDM_BLIND_DIAL 0x00000200 +#define MDM_V23_OVERRIDE 0x00000400 + +#endif /* _MCX_H_ */ + + + + + + + + + + + + + + + + diff --git a/code/redalert/win32lib/windows.cpp b/code/redalert/win32lib/windows.cpp new file mode 100644 index 0000000..b31d434 --- /dev/null +++ b/code/redalert/win32lib/windows.cpp @@ -0,0 +1,973 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./windows.c 1.12 1994/05/20 15:35:25 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : LIBRARY * + * * + * File Name : WINDOWS.C * + * * + * Programmer : Everyone * + * * + * Last Update : February 3, 1992 [DRD] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Change_New_Window -- Combined Change_Window and New_Window. * + * Change_Window -- Changes the 'current' window in the system. * + * Fetch_Char -- Gets one undipthonged char from input. * + * Flush_Line -- Outputs the accumulate text line to screen. * + * In_Char -- Stores (un-dipped) character(s) from input to buffer. * + * New_Window -- Clears the current window to the background color. * + * Set_More_Off -- Turns the 'more' prompting off. * + * Set_More_On -- Turns the 'more' prompting on. * + * Set_More_Prompt -- Adjusts the more prompt text for default routine * + * Standard_More_Prompt -- Default more prompt code for Window_Print * + * Window_Int_Print -- Prints an integer to the window. * + * Window_Print -- Displays and wraps text into a window. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include +#include +#include +#include +#include "ww_win.h" +#include +#include +#include + +PRIVATE void Scroll_Window(void); +PRIVATE void Flush_Line(void); +PRIVATE void In_Char(char *str); +PRIVATE char Fetch_Char(void); + + +PRIVATE int ScrollCounter = 0; // Count of the lines displayed before a pause. +PRIVATE char Line[84]; // Staging line buffer. +PRIVATE int Pos; // Char Position of next free character. +PRIVATE int PPos; // Pixel position of next free character. +PRIVATE int WPos; // Char position in window. +PRIVATE char *MainSource; +PRIVATE char *AltSource; +PRIVATE char Char[2]; +PRIVATE char Stack; +PRIVATE char WordWrapFlag = FALSE; // flag for a word wrap. + +PRIVATE int MoreSpace = 7; +PRIVATE int MoreFColor = 0; +PRIVATE int MoreBColor = 0; + + +int WindowColumns=40; +int WindowLines=25; +int WindowWidth=40; +unsigned int WinB=0; +unsigned int WinC=1; +unsigned int WinX=0; +unsigned int WinY=0; +unsigned int WinCx=0; +unsigned int WinCy=0; +unsigned int WinH=25; +unsigned int WinW=40; +unsigned int Window=0; + +int MoreOn = TRUE; +char *TXT_MoreText = "--More--"; +void (*Window_More_Ptr)(char const *,int,int,int) = Standard_More_Prompt; + +extern GraphicViewPortClass *LogicPage; +/*************************************************************************** + * STANDARD_MORE_PROMPT -- Default more prompt code for Window_Print * + * * + * This is the standard "" prompting code that is used by * + * Window_Print when a page is full of text and a pause is desired * + * before the next page of text is printed. This function is called * + * through the Window_More_Ptr global. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background oclor to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + int x, y, moresize; + + moresize = (space - 1) * (FontWidth+FontXSpacing); + x = ((WinX+WinW) << 3) - moresize; + //y = WinY + ((WinH/FontHeight)-1)*FontHeight; + y = WinY + (WinCy-1) * (FontHeight+FontYSpacing); + + // Default "more" prompter. + LogicPage->Print(prompt, x, y, fcolor ? fcolor : WindowList[Window][WINDOWBCOL], bcolor ? bcolor : WindowList[Window][WINDOWFCOL]); +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + // PWG - have to figure out how to do this in windows library + +// Clear_KeyBuffer(); +// Get_Key(); + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + // Erase the more prompt prompt. + // Text_Print(prompt, x, y, WinB, WinB); + LogicPage->Fill_Rect(x, y, x + moresize - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); +} + + +/*************************************************************************** + * SET_MORE_PROMPT -- Adjusts the more prompt text for default routine * + * * + * Use this routine to control the text of the "" prompt that * + * the default more prompt routine uses. This can be useful for * + * foreign language translations. * + * * + * INPUT: prompt -- Pointer to ASCII text that will be window printed * + * at the right margin. * + * * + * space -- The number of spaces to allow for the 'more' text. * + * * + * fcolor -- The foreground color to use for the 'more' text. * + * * + * bcolor -- The background color to use for the 'more' text. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/29/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor) +{ + if (prompt) { + TXT_MoreText = (char*)prompt; + MoreSpace = space; + MoreFColor = fcolor; + MoreBColor = bcolor; + } + else { + TXT_MoreText = ""; + MoreSpace = 7; + MoreFColor = MoreBColor = 0; + } +} + + +/*************************************************************************** + * SET_MORE_ON -- Turns the 'more' prompting on. * + * * + * Use this routine to turn on the 'more' prompting that Window_Print * + * does. If you have a custom more function pointer, then that * + * routine will be called, otherwise the library default 'more' prompt * + * will be utilized. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_On(void) +{ + MoreOn = TRUE; + ScrollCounter = 0; +} + + +/*************************************************************************** + * SET_MORE_OFF -- Turns the 'more' prompting off. * + * * + * This routine will turn the 'more' prompting that Window_Print does * + * off. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Set_More_Off(void) +{ + MoreOn = FALSE; +} + + +/*************************************************************************** + * CHANGE_WINDOW -- Changes the 'current' window in the system. * + * * + * Use this routine to change the 'current' window. The current window * + * is used in Window_Print and some other graphic output routines. * + * * + * INPUT: windnum -- The window number to change to. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_Window(int windnum) +{ + int oldwindow; + int *data; + + oldwindow = Window; + Window = windnum; + data = &WindowList[windnum][0]; + + WinX = *data++; + WinY = *data++; + WinW = *data++; + WinH = *data++; + WinC = *data++; + WinB = *data++; + WinCx = *data++; + WinCy = *data++; + ScrollCounter = 0; + WPos = WinCx / (FontWidth+FontXSpacing); + WindowLines = (WinH-FontYSpacing) / (FontHeight+FontYSpacing); + WindowWidth = WinW << 3; + WindowColumns = WindowWidth / (FontWidth+FontXSpacing); + return (oldwindow); +} + + +/*************************************************************************** + * CHANGE_NEW_WINDOW -- Combined Change_Window and New_Window. * + * * + * This is a combo-routine. It merely combines the Change_Window * + * with the New_Window routines. It will save some (small) code if * + * you use this routine instead of the two function calls. * + * * + * INPUT: window -- Window number to change to and clear. * + * * + * OUTPUT: Returns with the previously current window. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +int Change_New_Window(int windnum) +{ + int oldwindow; + + oldwindow = Change_Window(windnum); + New_Window(); + return(oldwindow); +} + + +/*************************************************************************** + * NEW_WINDOW -- Clears the current window to the background color. * + * * + * This routine clears the current window to the background color. It * + * is used in preparation to Window_Print because it ensures a clean * + * 'slate' for the text. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void New_Window(void) +{ + int x,y,w,h; + + x = WinX << 3; + y = WinY; + w = (WinX + WinW) << 3; + h = WinY + WinH; + + LogicPage->Fill_Rect(x, y, w - 1, h - 1, (unsigned char)WinB); + + WinCx = WPos = 0; + WinCy = 0; + ScrollCounter = 0; +} + + +/*************************************************************************** + * WINDOW_INT_PRINT -- Prints an integer to the window. * + * * + * Use this routine to print an integer to the window. This routine * + * as all other Window printing routines will handle word wrap. * + * * + * INPUT: num -- The integer to convert to ASCII and print. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +void Window_Int_Print(int num) +{ + Window_Print("%d", num); +} + + +/*************************************************************************** + * WINDOW_PRINT -- Displays and wraps text into a window. * + * * + * This is the general purpos text output routine that will handle * + * word wrap within a window. It is useful for displaying arbitrary * + * text. This routine will handle dipthonged text and as such it * + * can be quite useful in saving memory. * + * * + * INPUT: string -- String to print. This can be of ANY length and * + * can even contain some formatting codes. The * + * codes supported are: * + * * + * KA_SETX Forces the cursor X position to the value * + * specified. * + * * + * KA_SETY Forces the cursor Y position to the value * + * specified. * + * * + * KA_MORE Causes an immediate "" prompt * + * regardless of the scroll situation. * + * * + * KA_RETURN Breaks line and continues output at the * + * left edge of following line. * + * * + * * + * KA_FORMFEED Clears the window and continues printing at * + * the upper left corner. * + * * + * KA_SETBKGDCOL Set the background color with the color * + * specified by the following byte. * + * * + * * + * KA_SETFORECOL Set the foreground color with the color * + * specified by the following byte. * + * * + * KA_TAB Move the cursor over to the next tabstop. * + * Tabstops are set every 8 columns. * + * * + * KA_SPCTAB Insert spaces until the cursor is positioned * + * at the next tabstop. * + * * + * %s Replace the "%s" with the text pointed to * + * by the pointer argument passed to the * + * routine (follows same method a printf). * + * * + * %d Replace the "%d" with an integer ASCII * + * number of the int passed to the routine. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + * 07/29/1991 JLB : Added MORE, SETX, and SETY * + *=========================================================================*/ +void Window_Print(char const string[], ...) +{ + int oldcx, x, y; // Scratch variables. + char c; // Current character. + char buffer[10]; // Working %d buffer. + int old_c, old_b; // Original window colors. + va_list arg; // Argument list var. + + + va_start(arg, string); + + WordWrapFlag = FALSE; // initialize word wrap flag. + Pos = PPos = 0; + Line[0] = '\0'; + Char[0] = Char[1] = 0; + MainSource = (char*)&string[0]; + AltSource = NULL; + old_c = WinC; + old_b = WinB; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Hide_Mouse(Window); +//BG } + + while (TRUE) { + + c = Fetch_Char(); + + if (!c) break; // Exit on NULL character. + + /* + ** Substitution commands only work if not already expanding a + ** string. + */ + if (!AltSource) { + if (c == '%') { + switch(tolower(Char[0])) { + case 's': + AltSource = va_arg(arg, char*); + if (AltSource) { + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + } + break; + + case 'd': + AltSource = buffer; + sprintf(buffer, "%d", va_arg(arg, int)); + Stack = Char[1]; + Char[0] = Char[1] = '\0'; + c = Fetch_Char(); + break; + + default: + break; + } + } + } + + switch(c) { + +#if(FALSE) + // these are the positions of foreign language characters + /* + ** These are characters that shouldn't be window printed because + ** they are currently reserved. + */ + case KA_CTRL_C: + case KA_CTRL_D: + case KA_CTRL_E: + case KA_CTRL_G: + case KA_CTRL_J: + case KA_CTRL_K: + case KA_CTRL_N: + case KA_CTRL_O: + case KA_CTRL_P: + case KA_CTRL_Q: + case KA_CTRL_R: + case KA_CTRL_T: + case KA_CTRL_U: + case KA_CTRL_V: + case KA_CTRL_W: + case KA_CTRL_Z: + case KA_CTRL_BACKSLASH: + case KA_CTRL_CARROT: + case KA_CTRL_UNDERLINE: + break; +#endif + /* + ** Force cursor column to specified X value. + */ + case KA_SETX: + Flush_Line(); + WPos = Fetch_Char(); + WPos = MAX(0, WPos); + + // WPos is max width char position + + WPos = MIN(WindowColumns-1, WPos); + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Force the cursor to specified Y value. + */ + case KA_SETY: + Flush_Line(); + WinCy = Fetch_Char(); + //WinCy = MAX(0, WinCy); + WinCy = MIN((long)WindowLines-1, (long)WinCy); + break; + + /* + ** Force a "" prompt. + */ + case KA_MORE: + Flush_Line(); + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + break; + + /* + ** Clear and home the window cursor. This is the same + ** as New_Window(). + */ + case KA_FORMFEED: + New_Window(); + break; + + /* + ** Move cursor to start of next line. + */ + case KA_RETURN: + Flush_Line(); + ScrollCounter++; + WinCx = 0; + +#if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } + else { + WinCy++; + } +#else + WinCy++; +#endif + + break; + + /* + ** Set the background color. + */ + case KA_SETBKGDCOL: + Flush_Line(); + WinB = Fetch_Char(); + break; + + /* + ** Set the foreground color. + */ + case KA_SETFORECOL: + Flush_Line(); + WinC = Fetch_Char(); + break; + + /* + ** Move cursor to next column. + */ + case KA_TAB: + Flush_Line(); + WPos = ((WPos + 8) & 0xFFF8) - 1; + if (WPos >= WindowColumns) { + WPos = 0; + } + WinCx = WPos * (FontWidth+FontXSpacing); + break; + + /* + ** Tab to specified column but add spaces. + */ + case KA_SPCTAB: + Flush_Line(); + oldcx = WinCx; + x = WinX << 3; + y = WinY + (WinCy * (FontHeight+FontYSpacing)); + WPos = ((WPos + 8) & 0xFFF8) - 1; + + if (WPos >= WindowColumns) { + WinCx = WPos = 0; + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WindowWidth - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + + ScrollCounter++; + WinCy++; + } + else { + WinCx = WPos * (FontWidth+FontXSpacing); + + // Fill_Rect instead of printing spaces + + LogicPage->Fill_Rect(x + oldcx, y, + x + WinCx - 1, y + (FontHeight+FontYSpacing) - 1, (unsigned char)WinB); + } + break; + + /* + ** next character is a extended value 1-127, but 128 is added + ** for a value 129-255 + */ + case KA_EXTEND: + c = 127; + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** next character is a literal value 1-127, except 13 + */ +// case KA_LITERAL: +// if (c != (char) 127) { // check if fell thru from extend case +// c = 0; // set to zero for literal case +// } +// c += Fetch_Char(); + + // NOTE: this falls thru to the default case DO NOT MOVE!!!!! + + + /* + ** Normal character output. + */ + default: + PPos += Char_Pixel_Width(c); // get pixel location of next char + Line[Pos++] = c; + Line[Pos] = '\0'; + + if (WinCx + PPos > (unsigned)WindowWidth) { + Flush_Line(); + } + break; + } + } + + /* + ** If there is text still pending, then display it before exiting. + */ + if (Pos) Flush_Line(); + + /* + ** Record changes in the cursor position. + */ + WindowList[Window][WINDOWCURSORX] = WinCx; + WindowList[Window][WINDOWCURSORY] = WinCy; + + /* + ** Restore the window colors to their original values. + */ + WindowList[Window][WINDOWFCOL] = WinC = old_c; + WindowList[Window][WINDOWBCOL] = WinB = old_b; + +//BG if (LogicPage == SEENPAGE) { +//BG Window_Show_Mouse(); +//BG } + + va_end(arg); +} + + +/*************************************************************************** + * SCROLL_WINDOW -- Scrolls the text window up one line. * + * * + * This will scroll the text window up one line. It will handle any * + * pausing for "more" if the MoreOn flag is set. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: This routine assumes that the LogicPage is the SEENPAGE. * + * If this is not the case, the program may appear to hang * + * if a "more" prompt is generated. * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Scroll_Window(void) +{ + int y; // Top pixel row of bottom line of window. + + /* + ** Possibly prompt for more text. + */ + if (ScrollCounter >= WindowLines-1 && MoreOn) { + ScrollCounter = 0; + + if (Window_More_Ptr) { +//BG if (LogicPage == SEENPAGE) Window_Show_Mouse(); + Window_More_Ptr(TXT_MoreText, MoreSpace, MoreFColor, MoreBColor); +//BG if (LogicPage == SEENPAGE) Window_Hide_Mouse(Window); + } + } + + /* + ** Scroll the window up one line. + */ + y = ((WinH / (FontHeight+FontYSpacing)) - 1) * (FontHeight+FontYSpacing); + LogicPage->Blit(*LogicPage,WinX<<3, WinY + (FontHeight+FontYSpacing), WinX<<3, WinY, WinW<<3, WinH - (FontHeight+FontYSpacing) ); + LogicPage->Fill_Rect(WinX<<3, + WinY + y, + ((WinX+WinW)<<3) - 1, + WinY + WinH - 1, + (unsigned char)WinB); +} + + +/*************************************************************************** + * FLUSH_LINE -- Outputs the accumulate text line to screen. * + * * + * This will display the accumlated text line to the screen. It will * + * handle breaking the text line at an appropriate position. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void Flush_Line(void) +{ + int breakit, breaksize, breakwidth; + int x, y; // Coordinates of text print. + int breakpoint; // Point to break the line (if possible). + char breakchar; // Break replace character. + int index; // Backward moving index var. + + /* + ** There could be a held and this is implied by the cursor Y position + ** beyond the bottom of the window. If this is the case, then scroll the + ** window and proceed with the line flush. + */ + while (WinCy >= (unsigned)WindowLines /*&& Pos > 0*/) { + Scroll_Window(); + if (WinCy >= (unsigned)WindowLines) WinCy--; + } + //if (WinCy >= WindowLines) WinCy = WindowLines-1; + + x = (WinX<<3) + WinCx; + y = WinY + (WinCy*(FontHeight+FontYSpacing)); + + breakwidth = WindowWidth; +// if (ScrollCounter >= WindowLines - 1 && MoreOn) { +// breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width +// } + + /* + ** Try to break the line at the last space IF the line has reached the edge + ** of the window. + */ + breakpoint = Pos; + breaksize = PPos; + if (WinCx + breaksize > (unsigned)breakwidth) { + + /* + ** Since the text WILL spill past the edge of the window, determine the + ** point where the break should occur. If this line is ready for the + ** prompt, then breaking must account for the text. + */ + if (ScrollCounter >= WindowLines - 1 && MoreOn) { + breakwidth -= (MoreSpace * (FontWidth+FontXSpacing)); // use maximum font width + } + breakwidth -= WinCx; + + breakit = 0; + for (index = breakpoint - 1; index > 0; index--) { + breakchar = Line[index]; + breaksize -= Char_Pixel_Width(breakchar); + + // only once, find largest text that can fit on the line + if (!breakit) { + // was this the char that went past the right edge + if (breaksize <= breakwidth) { + breakit = index; // save this position if there is no spaces + } + } + + // after largest text is found then look for a space to break on + if (breakit && breakchar == KA_SPACE) { + breakpoint = index; + WordWrapFlag = FALSE; // word will start at beginning of next line + break; + } + } + + /* + ** Exception: When the current text buffer cannot be broken at a logical + ** place AND the text is starting past the left margin, THEN there is + ** an implied break between the previous text output and this one. + ** Output the current text on the next line left margin. + */ + if (!index) { + if (WinCx && !WordWrapFlag) { + breakpoint = breaksize = 0; // Continue text on next line. + WordWrapFlag = TRUE; // indicate a word continuation. + } + else { + breakpoint = breakit; // Just print as much as possible. + } + } + } + + breakchar = Line[breakpoint]; + Line[breakpoint] = '\0'; + + LogicPage->Print(Line, x, y, WinC, WinB); + WinCx += breaksize; // add size of text string printed. + + Line[breakpoint] = breakchar; + if (breakchar == KA_SPACE) { // take out a space between words. + breakpoint++; + } + + // take out another space for double spacing after end of sentence. + if (Line[breakpoint] == KA_SPACE) { + breakpoint++; + } + + strcpy(Line, &Line[breakpoint]); + Pos = strlen(Line); + PPos = String_Pixel_Width(Line); + + /* + ** If at this point there is still text in the buffer, then flushing has + ** not been completed. Scroll to next line and repeat the text flushing + ** process. + */ + if (Pos || WinCx >= (unsigned)WindowWidth) { + WinCx = WPos = 0; + #if(FALSE) + if (WinCy >= WindowLines-1) { + Scroll_Window(); + } else { + WinCy++; + } + #else + WinCy++; + #endif + Flush_Line(); + ScrollCounter++; // must be done after flush line for correct counting + } +} + + +/*************************************************************************** + * IN_CHAR -- Stores (un-dipped) character(s) from input to buffer. * + * * + * Use this routine to fetch the next character from the input stream. * + * If the character was dipthonged, then it will be broken into its * + * component ASCII characters and stored in the specified location. * + * This is the core character stream reading code. * + * * + * INPUT: str -- char pointer to the position to store the character(s)* + * fetched from the input stream. * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE void In_Char(char *str) +{ + char c; // Character to return. + char next; // Following character (if any). + + c = next = '\0'; + + /* + ** Fetch a raw byte from the input stream. + */ + if (AltSource) { + if (*AltSource == '\0') { + AltSource = NULL; + c = Stack; + } else { + c = *AltSource++; + } + } + + if (!c && MainSource) { + if (*MainSource == '\0') { + MainSource = NULL; + } else { + c = *MainSource++; + } + } + + /* + ** Convert a dipthong character into it's component + ** ASCII characters. + */ + if (c & 0x80) { + c &= 0x7F; + next = c & (char)0x07; + c = (char)((c & (char)0x78) >> 3); + + next = Dipthong[c][next]; // Dipthong character. + c = Common[c]; // Common character. + } + + *str++ = c; + *str = next; +} + + +/*************************************************************************** + * FETCH_CHAR -- Gets one undipthonged char from input. * + * * + * This routine will fetch one character from the input stream. The * + * character has already been un-dipthonged. It is a straight ASCII * + * character. This routine ensures that if the next character in the * + * input stream needs to be examined, it is available in Char[0]. * + * * + * INPUT: none * + * * + * OUTPUT: Returns next character in the input stream (ASCII). If NULL * + * is returned, then this indicates the end of the input stream. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/25/1991 JLB : Created. * + *=========================================================================*/ +PRIVATE char Fetch_Char(void) +{ + char c; // Character to return. + + if (!Char[0]) { + In_Char(&Char[0]); + } + + c = Char[0]; + Char[0] = Char[1]; + Char[1] = '\0'; + + if (!Char[0]) { + In_Char(&Char[0]); + } + + return (c); +} + + \ No newline at end of file diff --git a/code/redalert/win32lib/winhide.cpp b/code/redalert/win32lib/winhide.cpp new file mode 100644 index 0000000..93680de --- /dev/null +++ b/code/redalert/win32lib/winhide.cpp @@ -0,0 +1,94 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./winhide.c 1.10 1994/05/20 15:35:50 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Dune * + * * + * File Name : WINHIDE.C * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : May 30, 1991 * + * * + * Last Update : August 16, 1991 [JLB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Window_Hide_Mouse -- Hides the mouse when it enters a window. * + * Window_Show_Mouse -- Shows the mouse after Window_Hide_Mouse hides it.* + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include +#include +#include "ww_win.h" + + +#if(IBM) +/*************************************************************************** + * WINDOW_HIDE_MOUSE -- Hides the mouse when it enters a window. * + * * + * This is an intelligent form of Conditional_Hide_Mouse(). It will * + * hide the mouse if it enters the specified window (see the * + * WindowList global). * + * * + * INPUT: window - Window number. * + * * + * OUTPUT: none * + * * + * WARNINGS: Just like Conditional_Hide_Mouse(), this function is NOT * + * nestable. * + * * + * HISTORY: * + * 04/26/1991 JLB : Created. * + *=========================================================================*/ +void Window_Hide_Mouse(int window) +{ + int x,y,w,h; + + x = WindowList[window][WINDOWX]<<3; + y = WindowList[window][WINDOWY]; + w = WindowList[window][WINDOWWIDTH]<<3; + h = WindowList[window][WINDOWHEIGHT]; +// Conditional_Hide_Mouse(x,y,x+w-1,y+h-1); +} + + +/*************************************************************************** + * WINDOW_SHOW_MOUSE -- Shows the mouse after Window_Hide_Mouse hides it. * + * * + * This routines will show the mouse after Window_Hide_Mouse has hidden * + * it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 05/22/1991 JLB : Created. * + *=========================================================================*/ +void Window_Show_Mouse(void) +{ +// Conditional_Show_Mouse(); +} +#endif + + + diff --git a/code/redalert/win32lib/writepcx.cpp b/code/redalert/win32lib/writepcx.cpp new file mode 100644 index 0000000..d3681b4 --- /dev/null +++ b/code/redalert/win32lib/writepcx.cpp @@ -0,0 +1,177 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : iff * + * * + * File Name : WRITEPCX.CPP * + * * + * Programmer : Julio R. Jerez * + * * + * Start Date : May 2, 1995 * + * * + * Last Update : May 2, 1995 [JRJ] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * int Save_PCX_File (char* name, GraphicViewPortClass& pic, char* palette)* + *= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + +#include +#include "filepcx.h" +#include +static void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ); + + +/*************************************************************************** + * WRITE_PCX_FILE -- Write the data in ViewPort to a pcx file * + * * + * * + * * + * INPUT: name is a NULL terminated string of the fromat [xxxx.pcx] * + * pic is a pointer to a GraphicViewPortClass or to a * + * GraphicBufferClass holding the picture. * + * palette is a pointer the the memry block holding the color * * + * palette of the picture. * + * * + * OUTPUT: FALSE if the function fails zero otherwise * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + * 08/01/1995 SKB : Copy the palette so it is not modified. * + *=========================================================================*/ +int Write_PCX_File (char* name, GraphicViewPortClass& pic, unsigned char* palette ) +{ + unsigned char palcopy[256 * 3]; + unsigned i ; + //unsigned width ; + int file_handle ; + int VP_Scan_Line ; + char * ptr ; + RGB * pal ; + GraphicBufferClass * Graphic_Buffer ; + PCX_HEADER header = { 10 , 5 , 1 , 8 , 0 , 0 , 319 , 199 , + 320 , 200 , { 0 } , 0 , 1 , 320 , 1 , {0} } ; + + // Open file name + file_handle = Open_File ( name , WRITE ) ; + if ( file_handle == WW_ERROR ) return FALSE ; + + + header.width = pic.Get_Width() - 1 ; + header.height = pic.Get_Height() - 1 ; + header.byte_per_line = pic.Get_Width() ; + Write_File ( file_handle, & header , sizeof (PCX_HEADER)) ; + + VP_Scan_Line = pic.Get_Width() + pic.Get_XAdd(); + Graphic_Buffer = pic.Get_Graphic_Buffer() ; + ptr = ( char * ) Graphic_Buffer->Get_Buffer() ; + ptr += ( (pic.Get_YPos() * VP_Scan_Line) + pic.Get_XPos() ); + + for ( i = 0 ; i < (unsigned)header.height + 1 ; i ++ ) + Write_Pcx_ScanLine ( file_handle , header.byte_per_line, (unsigned char*)ptr + i * VP_Scan_Line ) ; + + Mem_Copy(palette, palcopy, 256 * 3); + pal = ( RGB * ) palcopy ; + for ( i = 0 ; i < 256 ; i ++ ) { + pal -> red <<= 2 ; + pal -> green <<= 2 ; + pal -> blue <<= 2 ; + pal ++ ; + } + i = 0x0c ; + Write_File ( file_handle, & i , 1 ) ; + Write_File ( file_handle, palcopy , 256 * sizeof (RGB) ) ; + Close_File (file_handle) ; + return 0 ; +} + + + + +/*************************************************************************** + * WRITE_PCX_SCANLINE -- function to write a single pcx scanline to a file * + * * + * * + * INPUT: * + * * + * OUTPUT: * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/04/1995 JRJ : Created. * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define WRITE_CHAR(x) { \ + * file_ptr ++ = x ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + Write_File ( file_handle, pool , POOL_SIZE ) ; \ + file_ptr = pool ; \ + } } + + +void Write_Pcx_ScanLine ( int file_handle , int scansize , unsigned char * ptr ) +{ + unsigned i ; + unsigned rle ; + unsigned color ; + unsigned last ; + unsigned char * file_ptr ; + unsigned char pool [ POOL_SIZE ] ; + + file_ptr = pool ; + last = * ptr ; + rle = 1 ; + + for ( i = 1 ; i < (unsigned)scansize ; i ++ ) { + color = 0xff & * ++ ptr ; + if ( color == last ) { + rle ++ ; + if ( rle == 63 ) { + WRITE_CHAR ( 255 ) ; + WRITE_CHAR ( color ) ; + rle = 0 ; + } + } else { + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last ) ; + } + } + last = color ; + rle = 1 ; + } + } + if ( rle ) { + if ( rle == 1 && ( 192 != ( 192 & last ))) { + WRITE_CHAR ( last ) ; + } else { + WRITE_CHAR ( rle | 192 ) ; + WRITE_CHAR ( last) ; + } + } + + Write_File ( file_handle, pool , ( int ) file_ptr - ( int ) pool ) ; +} diff --git a/code/redalert/win32lib/wsa.cpp b/code/redalert/win32lib/wsa.cpp new file mode 100644 index 0000000..3dcc2f9 --- /dev/null +++ b/code/redalert/win32lib/wsa.cpp @@ -0,0 +1,1153 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: g:/library/source/rcs/./wsa.c 1.16 1994/05/20 15:35:27 joe_bostic Exp $ */ +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Library Animation System * + * * + * File Name : WSA.C * + * * + * Programmer : Michael Legg * + * * + * Start Date : November 20, 1991 * + * * + *-------------------------------------------------------------------------* + * There are many different ways that the user can use the WSA library * + * module. The options are as follows : * + * * + * System Allocation vs User Buffer - The user may request that the * + * system allocate the needed buffer from the heap or the user may * + * pass his own buffer in for the animator to use. * + * * + * Resident vs File based - If there is enough RAM, the user may put the * + * entire animation into RAM for fastest animations. If there is * + * not enouph RAM, the system will automatically make it so each * + * frame will be read off disk when needed. * + * * + * Direct to Page vs Use of a user buffer -- Noramally Direct to page * + * is the best method both in speed and in RAM need to hold anim. * + * One may want to use the write to user buffer method if they * + * are using the animation in a non sequencial order. * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Animate_Frame -- Displays a frame of a given animation * + * Get_Animation_Frame_Count -- Return Number of frames in an animation. * + * Get_Animation_X -- Gets the x from an animation * + * Get_Animation_Y -- Gets the y from an animation * + * Get_Animation_Width -- Gets the width from an animation * + * Get_Animation_Height -- The height of the animation we are processing * + * Apply_Delta -- Copies frame into delta buffer, then applies to target * + * Close_Animation -- Close the animation, freeing the space if necessary* + * Get_File_Frame_Offset -- Get offset of a delta frame from animate file* + * Get_Resident_Frame_Offset -- Gets frame offset of animate file in RAM * + * Open_Animation -- Opens an animation file and reads into buffer * + *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#include +#include "wsa.h" +#include +#include +#include +#include +//#include +#include + +// +// WSA animation header allocation type. +// If we need more then 8 flags for the flags variable, we can combine +// USER_ALLOCATED with SYS_ALLOCATED and combine FILE with RESIDENT. +// +#define WSA_USER_ALLOCATED 0x01 +#define WSA_SYS_ALLOCATED 0x02 +#define WSA_FILE 0x04 +#define WSA_RESIDENT 0x08 +#define WSA_TARGET_IN_BUFFER 0x10 +#define WSA_LINEAR_ONLY 0x20 +#define WSA_FRAME_0_ON_PAGE 0x40 +#define WSA_AMIGA_ANIMATION 0x80 +#define WSA_PALETTE_PRESENT 0x100 +#define WSA_FRAME_0_IS_DELTA 0x200 + +// These are used to call Apply_XOR_Delta_To_Page_Or_Viewport() to setup flags parameter. If +// These change, make sure and change their values in lp_asm.asm. +#define DO_XOR 0x0 +#define DO_COPY 0x01 +#define TO_VIEWPORT 0x0 +#define TO_PAGE 0x02 + + + + +typedef struct { + unsigned short current_frame; + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + char *delta_buffer; + char *file_buffer; + char file_name[ 13 ]; + short flags; + // New fields that animate does not know about below this point. SEE EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT + short file_handle; + unsigned long anim_mem_size; +} SysAnimHeaderType; + +// NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE +// UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING +// IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. +#define EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT (sizeof(short) + sizeof(unsigned long)) + + +// +// Header structure for the file. +// NOTE: The 'total_frames' field is used to differentiate between Amiga and IBM +// animations. Amiga animations have the HIGH bit set. +// +typedef struct { + unsigned short total_frames; + unsigned short pixel_x; + unsigned short pixel_y; + unsigned short pixel_width; + unsigned short pixel_height; + unsigned short largest_frame_size; + short flags; + unsigned long frame0_offset; + unsigned long frame0_end; + /* unsigned long data_seek_offset, unsigned short frame_size ... */ +} WSA_FileHeaderType; + +#define WSA_FILE_HEADER_SIZE ( sizeof(WSA_FileHeaderType) - (2 * sizeof(unsigned long)) ) + +/*=========================================================================*/ +/* The following PRIVATE functions are in this file: */ +/*=========================================================================*/ + +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ); +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust); +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w); +/*= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/ + + +/*************************************************************************** + * OPEN_ANIMATION -- Opens an animation file and reads into buffer * + * * + * INPUT: char *file_name of animation sequence file. * + * char *user_buffer pointer if one exists (NULL ok) * + * unsigned long user_buffer_size if known (NULL ok) * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to palette space for return (NULL ok) * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: May return NULL, please check. * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette) +{ + int fh, anim_flags; + int palette_adjust; + unsigned int offsets_size; + unsigned int frame0_size; + long target_buffer_size, delta_buffer_size, file_buffer_size; + long max_buffer_size, min_buffer_size; + char *sys_anim_header_buffer; + char *target_buffer; + char *delta_buffer, *delta_back; + SysAnimHeaderType *sys_header; + WSA_FileHeaderType file_header; + + /*======================================================================*/ + /* Open the file to get the header information */ + /*======================================================================*/ + + anim_flags = 0; + fh = Open_File(file_name, READ); + Read_File(fh, (char *) &file_header, sizeof(WSA_FileHeaderType)); + + /*======================================================================*/ + /* If the file has an attached palette then if we have a valid palette */ + /* pointer we need to read it in. */ + /*======================================================================*/ + + if (file_header.flags & 1) { + anim_flags |= WSA_PALETTE_PRESENT; + palette_adjust = 768; + + if (palette != NULL) { + Seek_File(fh, sizeof(unsigned long) * (file_header.total_frames), SEEK_CUR); + Read_File(fh, palette, 768L); + } + + } else { + palette_adjust = 0; + } + + // Check for flag from ANIMATE indicating that this animation was + // created from a .LBM and a .ANM. These means that the first + // frame is a XOR Delta from a picture, not black. + if (file_header.flags & 2) { + anim_flags |= WSA_FRAME_0_IS_DELTA; + } + + + // Get the total file size minus the size of the first frame and the size + // of the file header. These will not be read in to save even more space. + file_buffer_size = Seek_File(fh, 0L, SEEK_END); + + if (file_header.frame0_offset) { + long tlong; + + tlong = file_header.frame0_end - file_header.frame0_offset; + frame0_size = (unsigned short) tlong; + } + else { + anim_flags |= WSA_FRAME_0_ON_PAGE; + frame0_size = 0; + } + + file_buffer_size -= palette_adjust + frame0_size + WSA_FILE_HEADER_SIZE; + + // We need to determine the buffer sizes required for the animation. At a + // minimum, we need a target buffer for the uncompressed frame and a delta + // buffer for the delta data. We may be able to make the file resident, + // so we will determine the file size. + // + // If the target buffer is in the user buffer + // Then figure its size + // and set the allocation flag + // Else size is zero. + // + if (user_flags & WSA_OPEN_DIRECT) { + target_buffer_size = 0L; + } + else { + anim_flags |= WSA_TARGET_IN_BUFFER; + target_buffer_size = (unsigned long) file_header.pixel_width * file_header.pixel_height; + } + + // NOTE:"THIS IS A BAD THING. SINCE sizeof(SysAnimHeaderType) CHANGED, THE ANIMATE.EXE + // UTILITY DID NOT KNOW I UPDATED IT, IT ADDS IT TO largest_frame_size BEFORE SAVING + // IT TO THE FILE. THIS MEANS I HAVE TO ADD THESE charS ON NOW FOR IT TO WORK. + delta_buffer_size = (unsigned long) file_header.largest_frame_size + EXTRA_charS_ANIMATE_NOT_KNOW_ABOUT; + min_buffer_size = target_buffer_size + delta_buffer_size; + max_buffer_size = min_buffer_size + file_buffer_size; + + // check to see if buffer size is big enough for at least min required + if (user_buffer && (user_buffer_size < min_buffer_size)) { + Close_File(fh); + return(NULL); + } + + // A buffer was not passed in, so do allocations + if (user_buffer == NULL) { + + // If the user wants it from the disk, then let us give it to him, + // otherwise, try to give a max allocation he can have. + if (user_flags & WSA_OPEN_FROM_DISK) { + user_buffer_size = min_buffer_size; + } + // else no buffer size, then try max configuration. + else if (!user_buffer_size) { + user_buffer_size = max_buffer_size; + } + // else if buffer specified is less then max needed, give min. + else if (user_buffer_size < max_buffer_size) { + user_buffer_size = min_buffer_size; + } + // otherwise we only want to alloc what we need. + else { + user_buffer_size = max_buffer_size; + } + + + // Check to see if enough RAM available for buffer_size. + if (user_buffer_size > Ram_Free(MEM_NORMAL)) { + + // If not enough room for even the min, return no buffer. + + if (min_buffer_size > Ram_Free(MEM_NORMAL)) { + Close_File(fh); + return(NULL); + } + + // Else make buffer size the min and allocate it. + user_buffer_size = min_buffer_size; + } + + // allocate buffer needed + user_buffer = (char *) Alloc(user_buffer_size, MEM_CLEAR); + + anim_flags |= WSA_SYS_ALLOCATED; + } + else { + // Check to see if the user_buffer_size should be min or max. + if ((user_flags & WSA_OPEN_FROM_DISK) || (user_buffer_size < max_buffer_size)) { + user_buffer_size = min_buffer_size; + } + else { + user_buffer_size = max_buffer_size; + } + anim_flags |= WSA_USER_ALLOCATED; + } + + + // Set the pointers to the RAM buffers + sys_anim_header_buffer = user_buffer; + target_buffer = (char *) Add_Long_To_Pointer(sys_anim_header_buffer, sizeof(SysAnimHeaderType)); + delta_buffer = (char *) Add_Long_To_Pointer(target_buffer, target_buffer_size); + + // Clear target buffer if it is in the user buffer. + if (target_buffer_size) { + memset( target_buffer, 0, (unsigned short) target_buffer_size ); + } + + // Poke data into the system animation header (start of user_buffer) + // current_frame is set to total_frames so that Animate_Frame() knows that + // it needs to clear the target buffer. + + sys_header = ( SysAnimHeaderType * ) sys_anim_header_buffer; + sys_header -> current_frame = + sys_header -> total_frames = file_header.total_frames; + sys_header -> pixel_x = file_header.pixel_x; + sys_header -> pixel_y = file_header.pixel_y; + sys_header -> pixel_width = file_header.pixel_width; + sys_header -> pixel_height = file_header.pixel_height; + sys_header -> anim_mem_size = user_buffer_size; + sys_header -> delta_buffer = delta_buffer; + sys_header -> largest_frame_size = + (unsigned short) (delta_buffer_size - sizeof(SysAnimHeaderType)); + + strcpy(sys_header->file_name, file_name); + + // Figure how much room the frame offsets take up in the file. + // Add 2 - one for the wrap around and one for the final end offset. + offsets_size = (file_header.total_frames + 2) << 2; + + // Can the user_buffer_size handle the maximum case buffer? + if ( user_buffer_size == max_buffer_size) { + + // + // set the file buffer pointer, + // Skip over the header information. + // Read in the offsets. + // Skip over the first frame. + // Read in remaining frames. + // + + sys_header->file_buffer = (char *)Add_Long_To_Pointer(delta_buffer,sys_header->largest_frame_size); + Seek_File( fh, WSA_FILE_HEADER_SIZE, SEEK_SET); + Read_File( fh, sys_header->file_buffer, offsets_size); + Seek_File( fh, frame0_size + palette_adjust, SEEK_CUR); + Read_File( fh, sys_header->file_buffer + offsets_size, + file_buffer_size - offsets_size); + + // + // Find out if there is an ending value for the last frame. + // If there is not, then this animation will not be able to + // loop back to the beginning. + // + if (Get_Resident_Frame_Offset( sys_header->file_buffer, sys_header->total_frames + 1)) + anim_flags |= WSA_RESIDENT; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_RESIDENT; + } + else { // buffer cannot handle max_size of buffer + + if(Get_File_Frame_Offset( fh, sys_header->total_frames + 1, palette_adjust)) + anim_flags |= WSA_FILE; + else + anim_flags |= WSA_LINEAR_ONLY | WSA_FILE; +//// + sys_header->file_buffer = NULL; + } + + // Figure where to back load frame 0 into the delta buffer. + delta_back = (char *)Add_Long_To_Pointer(delta_buffer, + sys_header->largest_frame_size - frame0_size); + + // Read the first frame into the delta buffer and uncompress it. + // Then close it. + Seek_File( fh, WSA_FILE_HEADER_SIZE + offsets_size + palette_adjust, SEEK_SET); + Read_File( fh, delta_back, frame0_size); + + // We do not use the file handle when it is in RAM. + if (anim_flags & WSA_RESIDENT) { + sys_header -> file_handle = (short) -1; + Close_File(fh); + } + else { + sys_header -> file_handle = (short)fh; + } + + LCW_Uncompress(delta_back, delta_buffer, sys_header->largest_frame_size); + + // Finally set the flags, + sys_header->flags = (short)anim_flags; + + // return valid handle + return( user_buffer ); +} + + +/*************************************************************************** + * CLOSE_ANIMATION -- Close the animation, freeing the space if necessary. * + * * + * INPUT: void *handle to the animation data buffer * + * * + * OUTPUT: none * + * * + * WARNINGS: handle MUST have been returned by Open_Animation * + * * + * HISTORY: * + * 11/23/1991 ML : Created. * + *=========================================================================*/ +void __cdecl Close_Animation( void *handle ) +{ + SysAnimHeaderType *sys_header; + + // Assign our local system header pointer to the beginning of the handle space + sys_header = (SysAnimHeaderType *) handle; + + // Close the WSA file in it was disk based. + if (sys_header->flags & WSA_FILE) { + Close_File(sys_header->file_handle); + } + + // Check to see if the buffer was allocated OR the programmer provided the buffer + if (handle && sys_header->flags & WSA_SYS_ALLOCATED) { + Free(handle); + } +} + +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +//#pragma off (unreferenced) +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + if (view.Lock()!=TRUE) return (FALSE); + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd() + view.Get_Pitch(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + view.Unlock(); + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = (short)frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + view.Unlock(); + return TRUE; +} +/*************************************************************************** + * ANIMATE_FRAME -- Displays a frame of a given animation * + * * + * INPUT: void *handle to the animation. * + * int frame_number wanted to be displayed * + * int x_pixel position of left side of animation on page * + * int y_pixel position of top of animation on page * + * * + * OUTPUT: BOOL if successfull or not. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/27/1991 SB : Created. * + *=========================================================================*/ +//#pragma argsused +#ifdef cuts +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel, int y_pixel, + WSAType flags_and_prio, void *magic_cols, void *magic) +{ + SysAnimHeaderType *sys_header; // fix up the void pointer past in. + int curr_frame; // current frame we are on. + int total_frames; // number of frames in anim. + int distance; // distance to desired frame. + int search_dir; // direcion to search for desired frame. + int search_frames; // How many frames to search. + int loop; // Just a loop varible. + char *frame_buffer; // our destination. + BOOL direct_to_dest; // are we going directly to the destination? + int dest_width; // the width of the destination buffer or page. + + // Assign local pointer to the beginning of the buffer where the system information + // resides + sys_header = (SysAnimHeaderType *)handle; + + // Get the total number of frames + total_frames = sys_header->total_frames; + + // Are the animation handle and the frame number valid? + if (!handle || (total_frames <= frame_number)) { + return FALSE; + } + + + // Decide if we are going to a page or a viewport (part of a buffer). + dest_width = view.Get_Width() + view.Get_XAdd(); + + // + // adjust x_pixel and y_pixel by system pixel_x and pixel_y respectively. + // + x_pixel += (short)sys_header->pixel_x; + y_pixel += (short)sys_header->pixel_y; + + // + // Check to see if we are using a buffer inside of the animation buffer or if + // it is being drawn directly to the destination page or buffer. + // + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + // Get a pointer to the frame in animation buffer. + frame_buffer = (char *)Add_Long_To_Pointer(sys_header, sizeof(SysAnimHeaderType)); + direct_to_dest = FALSE; + } + else { + frame_buffer = (char *)view.Get_Offset(); + frame_buffer += (y_pixel * dest_width) + x_pixel; + direct_to_dest = TRUE; + } + // + // If current_frame is equal to tatal_frames, then no animations have taken place + // so must uncompress frame 0 in delta buffer to the frame_buffer/page if it + // exists. + // + if (sys_header->current_frame == total_frames) { + + // Call apply delta telling it wether to copy or to xor depending on if the + // target is a page or a buffer. + + if (!(sys_header->flags & WSA_FRAME_0_ON_PAGE)) { + if (direct_to_dest) { + + // The last parameter says weather to copy or to XOR. If the + // first frame is a DELTA, then it must be XOR'd. A TRUE is + // copy while FALSE is XOR. + Apply_XOR_Delta_To_Page_Or_Viewport(frame_buffer, sys_header->delta_buffer, + sys_header->pixel_width, dest_width, //dest_width - sys_header->pixel_width, + (sys_header->flags & WSA_FRAME_0_IS_DELTA)? DO_XOR : DO_COPY); + } + else { + Apply_XOR_Delta(frame_buffer, sys_header->delta_buffer); + } + } + sys_header->current_frame = 0; + } + + // + // Get the current frame + // If no looping aloud, are the trying to do it anyways? + // + curr_frame = sys_header->current_frame; +#if (FALSE) + // This is commented out since we will let them loop even though they should + // not - it will be slower. + if ( (sys_header->flags & WSA_LINEAR_ONLY) && (frame_number < cur_frame) ) { + return FALSE; + } +#endif + + // Get absoulte distance from our current frame to the target frame + distance = ABS(curr_frame - frame_number); + + // Assume we are searching right + search_dir = 1; + + // Calculate the number of frames to search if we go right and wrap + + if (frame_number > curr_frame) { + search_frames = total_frames - frame_number + curr_frame; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames < distance) && !(sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + } + else { + search_frames = distance; + } + } + else { + search_frames = total_frames - curr_frame + frame_number; + + // Is going right faster than going backwards? + // Or are they trying to loop when the should not? + if ((search_frames >= distance) || (sys_header->flags & WSA_LINEAR_ONLY)) { + search_dir = -1; // No, so go left + search_frames = distance; + } + } + + // Take care of the case when we are searching right (possibly right) + + if (search_dir > 0) { + for (loop = 0; loop < search_frames; loop++) { + + // Move the logical frame number ordinally right + curr_frame += search_dir; + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + // Adjust the current frame number, taking into consideration that we could + // have wrapped + + if (curr_frame == total_frames) { + curr_frame = 0; + } + } + } + else { + for (loop = 0; loop < search_frames; loop++) { + + // If we are going backwards and we are on frame 0, the delta to get + // to the last frame is the n + 1 delta (wrap delta) + + if (curr_frame == 0) { + curr_frame = total_frames; + } + + Apply_Delta(sys_header, curr_frame, frame_buffer, dest_width); + + curr_frame += search_dir; + } + } + + sys_header->current_frame = frame_number; + + // If we did this all in a hidden buffer, then copy it to the desired page or viewport. + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { +#if TRUE + + Buffer_To_Page(x_pixel, y_pixel, sys_header->pixel_width, sys_header->pixel_height, frame_buffer, view); + +#else + int flags = ((unsigned short)flags_and_prio & 0xFF00u) >> 12u; + int pri = flags_and_prio & 0x00FF; + + Buffer_Bitblit_To_LogicPage(x_pixel, y_pixel, sys_header->pixel_width, + sys_header->pixel_height, 0, flags, frame_buffer, pri, + magic_cols, magic); +#endif + } + + + return TRUE; +} +#endif + + +/*************************************************************************** + * ANIMATE_FRAME_COUNT -- Return Number of frames in an animation. * + * * + * INPUT: void *handle to the animation. * + * * + * OUTPUT: int number of frames in animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 12/05/1991 SB : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Frame_Count(void *handle) +{ + SysAnimHeaderType *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return((short)sys_header->total_frames); +} + + +/*************************************************************************** + * GET_ANIM_X -- Gets the x from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the x of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 07/03/1992 DRD : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_X(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_x); +} + +/*************************************************************************** + * GET_ANIM_Y -- Gets the y from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the y of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Y(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_y); +} + +/*************************************************************************** + * GET_ANIM_WIDTH -- Gets the width from an animation * + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the width of the animation we are processing * + * * + * WARNINGS: * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Width(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_width); +} + +/*************************************************************************** + * GET_ANIM_HEIGHT -- The height of the animation we are processing * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int the height of the animation we are processing * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Height(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->pixel_height); +} + + +/*************************************************************************** + * GET_ANIM_PALETTE -- Returns true if the anim had an attached palette * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: int True if the animation has a set palette. False if the * + * animation does not. * + * * + * HISTORY: * + * 10/14/1992 PWG : Created. * + *=========================================================================*/ +int __cdecl Get_Animation_Palette(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->flags & WSA_PALETTE_PRESENT); +} + + +/*************************************************************************** + * GET_ANIMATION_SIZE -- Return the amount of memory the animation is using* + * * + * * + * INPUT: void * to the animation that we are processing * + * * + * OUTPUT: unsigned long number of byte used by animation. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/23/1994 SKB : Created. * + *=========================================================================*/ +unsigned long __cdecl Get_Animation_Size(void const *handle) +{ + SysAnimHeaderType const *sys_header; + + if (!handle) { + return FALSE; + } + sys_header = (SysAnimHeaderType *) handle; + return(sys_header->anim_mem_size); +} + +/* :::::::::::::::::::::::::::: PRIVATE FUNCTIONS :::::::::::::::::::::::::::::: */ + + +/*************************************************************************** + * GET_RESIDENT_FRAME_OFFSET -- Gets frame offset of animate file in RAM * + * * + * INPUT: char *file_buffer in RAM of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_Resident_Frame_Offset( char *file_buffer, int frame ) +{ + unsigned long frame0_size; + unsigned long *lptr; + + // If there is a frame 0, the calculate its size. + lptr = (unsigned long *) file_buffer; + + if (*lptr) { + frame0_size = lptr[1] - *lptr; + } else { + frame0_size = 0; + } + + // Return the offset into RAM for the frame. + lptr += frame; + if (*lptr) + return (*lptr - (frame0_size + WSA_FILE_HEADER_SIZE)); + else + return (0L); +} + + +/*************************************************************************** + * GET_FILE_FRAME_OFFSET -- Get offset of a delta frame from animate file. * + * * + * INPUT: int file_handle of animation file. * + * int frame number that we need the offset of. * + * * + * OUTPUT: int offset of frame requested. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE unsigned long Get_File_Frame_Offset( int file_handle, int frame, int palette_adjust) +{ + unsigned long offset; + + Seek_File(file_handle, (frame << 2) + WSA_FILE_HEADER_SIZE, SEEK_SET); + + if (Read_File(file_handle, (char *) &offset, sizeof(unsigned long)) != sizeof(unsigned long)) { + offset = 0L; + } + offset += palette_adjust; + return( offset ); +} + + +/*************************************************************************** + * APPLY_DELTA -- Copies frame into delta buffer, then applies to target * + * * + * INPUT: SysAnimHeaderType *sys_header - pointer to animation buffer.* + * int curr_frame - frame to put into target buffer. * + * * + * OUTPUT: BOOL - Return wether or not it worked. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 11/26/1991 SB : Created. * + *=========================================================================*/ +PRIVATE BOOL Apply_Delta(SysAnimHeaderType *sys_header, int curr_frame, char *dest_ptr, int dest_w) +{ + char *data_ptr, *delta_back; + int file_handle, palette_adjust; + unsigned long frame_data_size, frame_offset; + + + palette_adjust = ((sys_header->flags & WSA_PALETTE_PRESENT) ? 768 : 0); + delta_back = sys_header->delta_buffer; + + if (sys_header->flags & WSA_RESIDENT) { + // Get offset of the given frame in the resident file + // Get the size of the frame <- (frame+1 offset) - (offset) + // Point at the delta data + // figure offset to load data into end of delta buffer + // copy it into buffer + + frame_offset = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame); + frame_data_size = Get_Resident_Frame_Offset(sys_header->file_buffer, curr_frame + 1) - frame_offset; + + data_ptr = (char *)Add_Long_To_Pointer(sys_header->file_buffer, frame_offset); + delta_back = (char *)Add_Long_To_Pointer(delta_back, + sys_header->largest_frame_size - frame_data_size); + + Mem_Copy( data_ptr, delta_back, frame_data_size ); + + } else if (sys_header -> flags & WSA_FILE) { + + // Open up file because not file not in RAM. + // Get offset of the given frame in the file on disk + // Get the size of the frame <- (frame+1 offset) - (offset) + // Return if Get_.._offset() failed. -- need error handling???? + // Seek to delta data. + // figure offset to load data into end of delta buffer + // Read it into buffer -- Return if correct amount not read.-- errors?? + + file_handle = sys_header->file_handle; + Seek_File(file_handle, 0L, SEEK_SET); + + frame_offset = Get_File_Frame_Offset(file_handle, curr_frame, palette_adjust); + frame_data_size = Get_File_Frame_Offset(file_handle, curr_frame + 1, palette_adjust) - frame_offset; + + if (!frame_offset || !frame_data_size) { + return(FALSE); + } + + Seek_File(file_handle, frame_offset, SEEK_SET); + delta_back = (char *)Add_Long_To_Pointer(delta_back, sys_header->largest_frame_size - frame_data_size); + + if (Read_File(file_handle, delta_back, frame_data_size) != frame_data_size) { + return(FALSE); + } + } + + // Uncompress data at end of delta buffer to the beginning of delta buffer. + // Find start of target buffer. + // Apply the XOR delta. + + LCW_Uncompress(delta_back, sys_header->delta_buffer, sys_header->largest_frame_size); + + if (sys_header->flags & WSA_TARGET_IN_BUFFER) { + Apply_XOR_Delta(dest_ptr, sys_header->delta_buffer); + } + else { + Apply_XOR_Delta_To_Page_Or_Viewport(dest_ptr, sys_header->delta_buffer, + sys_header->pixel_width, dest_w, DO_XOR); + } + + return(TRUE); +} + diff --git a/code/redalert/win32lib/wsa.h b/code/redalert/win32lib/wsa.h new file mode 100644 index 0000000..8cb0fd6 --- /dev/null +++ b/code/redalert/win32lib/wsa.h @@ -0,0 +1,160 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : WSA 32bit LIbrary * + * * + * File Name : WSA.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : May 23, 1994 * + * * + * Last Update : May 25, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * Open_Animation -- file name and flags, system allocates buffer. * + * Open_Animation -- file name, flags, palette, system allocates buffer. * + * Open_Animation -- file_name, graphic buffer, flags. * + * Open_Animation -- file name, bufferclass, flags, palette. * + * Open_Animation -- filename, ptr, size, flags, no palette. * + * Animate_Frame -- Animate a frame to a page with magic colors. * + * Animate_Frame -- Animate a frame to a viewport with magic colors. * + * Animate_Frame -- Animate a frame to a page. * + * Animate_Frame -- Animate a frame to a viewport. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSA_H +#define WSA_H + +#ifndef WWSTD_H +#include "wwstd.h" +#endif + +#ifndef GBUFFER_H +#include "gbuffer.h" +#endif + +//lint -strong(AJX,WSAType) +typedef enum { + WSA_NORMAL, // Normal WSA animation + WSA_GHOST = 0x1000, // Or'd with the above flags to get ghosting + WSA_PRIORITY2 = 0x2000, // Copy using a priority (or in the priority) + WSA_TRANS = 0x4000, // Copy frame, ignoring transparent colors + WSA_PRIORITY = 0x8000 // Copy using a priority (or in the priority) +} WSAType; + + +//lint -strong(AJX,WSAOpenType) +typedef enum { + WSA_OPEN_FROM_MEM = 0x0000, // Try to load entire anim into memory. + WSA_OPEN_INDIRECT = 0x0000, // First animate to internal buffer, then copy to page/viewport. + WSA_OPEN_FROM_DISK = 0x0001, // Force the animation to be disk based. + WSA_OPEN_DIRECT = 0x0002, // Animate directly to page or viewport. + + // These next two have been added for the 32 bit library to give a better idea of what is + // happening. You may want to animate directly to the destination or indirectly to the + // destination by using the animations buffer. Indirecly is best if the dest is a seenpage + // and the animation is not linear or if the destination is modified between frames. + WSA_OPEN_TO_PAGE = WSA_OPEN_DIRECT , + WSA_OPEN_TO_BUFFER= WSA_OPEN_INDIRECT , + +} WSAOpenType; + +/*=========================================================================*/ +/* The following prototypes are for the file: WSA.CPP */ +/*=========================================================================*/ + +void * __cdecl Open_Animation(char const *file_name, char *user_buffer, long user_buffer_size, WSAOpenType user_flags, unsigned char *palette=NULL); +void __cdecl Close_Animation( void *handle ); +BOOL __cdecl Animate_Frame(void *handle, GraphicViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +BOOL __cdecl Animate_Frame(void *handle, VideoViewPortClass& view, + int frame_number, int x_pixel=0, int y_pixel=0, + WSAType flags_and_prio = WSA_NORMAL, void *magic_cols=NULL, void *magic=NULL); +int __cdecl Get_Animation_Frame_Count(void *handle); +int __cdecl Get_Animation_X(void const *handle); +int __cdecl Get_Animation_Y(void const *handle); +int __cdecl Get_Animation_Width(void const *handle); +int __cdecl Get_Animation_Height(void const *handle); +int __cdecl Get_Animation_Palette(void const *handle); +unsigned long __cdecl Get_Animation_Size(void const *handle); + + +/*************************************************************************** + * OPEN_ANIMATION -- file name, flags, palette, system allocates buffer. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, NULL, 0L, user_flags, palette)); +} + + +/*************************************************************************** + * OPEN_ANIMATION -- file_name, bufferclass, flags. * + * * + * * + * INPUT: char *file_name - name of file to open. * + * GraphicBufferClass - pointer to a buffer. * + * WSAOpenType user_flags - flags on how to open. * + * unsigned char *palette - pointer to a palette buffer to fill. * + * * + * OUTPUT: void *pointer to animation data. Must be used for all * + * other WSA calls. * + * * + * WARNINGS: * + * * + * HISTORY: * + * 05/24/1994 SKB : Created. * + *=========================================================================*/ +inline void * __cdecl Open_Animation(char *file_name, BufferClass& buffer, WSAOpenType user_flags, unsigned char *palette=NULL) +{ + return (Open_Animation(file_name, (char *)buffer.Get_Buffer(), buffer.Get_Size(), user_flags, palette)); +} + + +/*=========================================================================*/ +/* The following prototypes are for the file: LP_ASM.ASM */ +/*=========================================================================*/ + + +extern "C" { +unsigned int __cdecl Apply_XOR_Delta(char *source_ptr, char *delta_ptr); +void __cdecl Apply_XOR_Delta_To_Page_Or_Viewport(void *target, void *delta, int width, int nextrow, int copy); +} + + + +#endif // WSA_H + diff --git a/code/redalert/win32lib/ww_win.h b/code/redalert/win32lib/ww_win.h new file mode 100644 index 0000000..cdebfbb --- /dev/null +++ b/code/redalert/win32lib/ww_win.h @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** + *************************************************************************** + * * + * Project Name : Part of the WINDOWS Library * + * * + * File Name : WINDOWS.H * + * * + * Programmer : Barry W. Green * + * * + * Start Date : February 16, 1995 * + * * + * Last Update : February 16, 1995 [BWG] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WINDOWS_H +#define WINDOWS_H + +/*=========================================================================*/ +/* The following prototypes are for the file: WINHIDE.CPP */ +/*=========================================================================*/ +void Window_Hide_Mouse(int window); +void Window_Show_Mouse(void); + +/*=========================================================================*/ +/* The following prototypes are for the file: WINDOWS.CPP */ +/*=========================================================================*/ +void Standard_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_Prompt(char const *prompt, int space, int fcolor, int bcolor); +void Set_More_On(void); +void Set_More_Off(void); +int Change_Window(int windnum); +int Change_New_Window(int windnum); +void New_Window(void); +void Window_Int_Print(int num); +void Window_Print(char const string[], ...); + +/* +** The WindowList[][8] array contains the following elements. Use these +** defines when accessing the WindowList. +*/ +typedef enum { + WINDOWX, // X byte position of left edge. + WINDOWY, // Y pixel position of top edge. + WINDOWWIDTH, // Width in bytes of the window. + WINDOWHEIGHT, // Height in pixels of the window. + WINDOWFCOL, // Default foreground color. + WINDOWBCOL, // Default background color. + WINDOWCURSORX, // Current cursor X position (in rows). + WINDOWCURSORY, // Current cursor Y position (in lines). + WINDOWPADDING=0x1000 +} WindowIndexType; + +extern int WindowList[][9]; +extern int WindowColumns; +extern int WindowLines; +extern int WindowWidth; +extern unsigned int WinB; +extern unsigned int WinC; +extern unsigned int WinX; +extern unsigned int WinY; +extern unsigned int WinCx; +extern unsigned int WinCy; +extern unsigned int WinH; +extern unsigned int WinW; +extern unsigned int Window; + +extern int MoreOn; +extern char *TXT_MoreText; + +extern void (*Window_More_Ptr)(char const *, int, int, int); + +#endif //WINDOWS_H + + \ No newline at end of file diff --git a/code/redalert/win32lib/wwfile.h b/code/redalert/win32lib/wwfile.h new file mode 100644 index 0000000..d7dd406 --- /dev/null +++ b/code/redalert/win32lib/wwfile.h @@ -0,0 +1,68 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c\vcs\code\wwfile.h_v 2.14 06 Sep 1995 16:30:00 JOE_BOSTIC $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_H +#define WWFILE_H + +#include + +#ifndef READ +#define READ _READ +#endif +#ifndef WRITE +#define WRITE _WRITE +#endif + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/code/redalert/win32lib/wwlib32.h b/code/redalert/win32lib/wwlib32.h new file mode 100644 index 0000000..88fe24e --- /dev/null +++ b/code/redalert/win32lib/wwlib32.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : WWLIB32 User include file * + * * + * File Name : WWLIB32.H * + * * + * Programmer : Scott K. Bowen * + * * + * Start Date : August 3, 1994 * + * * + * Last Update : August 3, 1994 [SKB] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWLIB32_H +#define WWLIB32_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#endif // WWLIB32_H + + diff --git a/code/redalert/win32lib/wwmem.h b/code/redalert/win32lib/wwmem.h new file mode 100644 index 0000000..20bb06a --- /dev/null +++ b/code/redalert/win32lib/wwmem.h @@ -0,0 +1,67 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : Memory System * + * * + * File Name : MEM.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : April 4, 1994 * + * * + * Last Update : September 8, 1994 [IML] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#ifndef WWMEM_H +#define WWMEM_H + +#include "wwstd.h" +#include "new.h" +#include "memflag.h" + +// Defines +//============== + + + + +/*=========================================================================*/ +/* The following prototypes are for the file: MEM.CPP */ +/*=========================================================================*/ + +int Mem_Init(void *buffer, long size); +void *Mem_Alloc(void *poolptr, long lsize, unsigned long id); +int Mem_Free(void *poolptr, void *buffer); +void Mem_Reference(void *node); +void Mem_Lock_Block(void *node); +void Mem_In_Use(void *node); +void *Mem_Find(void *poolptr, unsigned long id); +unsigned long Mem_Get_ID(void *node); +void *Mem_Find_Oldest(void *poolptr); +void *Mem_Free_Oldest(void *poolptr); +long Mem_Pool_Size(void *poolptr); +long Mem_Avail(void *poolptr); +long Mem_Largest_Avail(void *poolptr); +void Mem_Cleanup(void *poolptr); + + +#endif + diff --git a/code/redalert/win32lib/wwmem.inc b/code/redalert/win32lib/wwmem.inc new file mode 100644 index 0000000..954b433 --- /dev/null +++ b/code/redalert/win32lib/wwmem.inc @@ -0,0 +1,40 @@ +; +; Copyright 2020 Electronic Arts Inc. +; +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +; software: you can redistribute it and/or modify it under the terms of +; the GNU General Public License as published by the Free Software Foundation, +; either version 3 of the License, or (at your option) any later version. + +; TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +; in the hope that it will be useful, but with permitted additional restrictions +; under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +; distributed with this program. You should have received a copy of the +; GNU General Public License along with permitted additional restrictions +; with this program. If not, see [https://github.com/electronicarts/CnC_Remastered_Collection]>. + +;*************************************************************************** +;** C O N F I D E N T I A L --- W E S T W O O D A S S O C I A T E S ** +;*************************************************************************** +;* * +;* Project Name : Memory System * +;* * +;* File Name : WWMEM.INC * +;* * +;* Programmer : Ian M. Leslie * +;* * +;* Start Date : August 11, 1994 * +;* * +;* Last Update : August 17, 1994 [IML] * +;* * +;*-------------------------------------------------------------------------* +;* Functions: * +;* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * + + +MEM_NORMAL EQU 0000h ; Default memory (normal). +MEM_NEW EQU 0001h ; Called by the operator new and was overloaded. +MEM_CLEAR EQU 0002h ; + +GLOBAL @Alloc$qul14MemoryFlagType:PROC +GLOBAL @Free$qpv:PROC diff --git a/code/redalert/win32lib/wwstd.h b/code/redalert/win32lib/wwstd.h new file mode 100644 index 0000000..00b7674 --- /dev/null +++ b/code/redalert/win32lib/wwstd.h @@ -0,0 +1,342 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*************************************************************************** + ** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ** + *************************************************************************** + * * + * Project Name : wwstd.h * + * * + * File Name : WWLIB.H * + * * + * Programmer : Jeff Wilson * + * * + * Start Date : March 1, 1994 * + * * + * Last Update : March 1, 1994 [] * + * * + *-------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWSTD_H +#define WWSTD_H + + +// +// Win 95 includes +// + +#ifndef WIN32 +#ifndef _WIN32 // Denzil 6/2/98 Watcom 11.0 complains without this check +#define _WIN32 +#endif // _WIN32 +#define WIN32 1 +#define WIN32_LEAN_AND_MEAN +#include +#include +#else +#include +#include +#endif + +// Note: SKB 4/11/94 +// Before this library is done, this needs to be able to be set to TRUE. +// Once it is, the FALSE parts should be removed from the source code. +#define LIB_EXTERNS_RESOLVED FALSE + + +#include +#include +#include +//#include + +//================================ + +// TRUE and FALSE are defined in pltypes.h + +#ifndef IBM +#define IBM TRUE +#endif + +#ifndef AMIGA +#define AMIGA FALSE +#endif + +#ifndef SEGA +#define SEGA FALSE +#endif + +/* +** Common constants used in normal code. +*/ +#define WW_ERROR -1 + +#if (0) //PG +#ifdef NULL +#undef NULL +#endif + +#ifndef NULL +#define NULL 0L +#endif + +#ifdef VOID +#undef VOID +#endif +#endif + +#define PRIVATE static +#define PUBLIC /* Routines & data don't have a specifier */ + +#ifdef __cplusplus +#define __CPPARGS ... +#else +#define __CPPARGS +#endif + +// This macro will get the size (in elements) of the specified array. +#ifdef GET_SIZE +#undef GET_SIZE +#endif +#define GET_SIZE(a) ((sizeof(a) / sizeof(*a))) + +//#pragma option -Jg +// Returns the absolute value of the number. +#ifdef ABS +#undef ABS +#endif +template T ABS(T a) +{ + return (a < 0) ? -a : a; +} +//int ABS(int); +//long ABS(long); + +// Returns the minimum of the two numbers. +#ifdef MIN +#undef MIN +#endif +template T MIN(T a, T b) +{ + return (b < a) ? b : a; +}; +//PG +//short MIN(short, short); +//int MIN(int, int); +//long MIN(long, long); + +// Returns the maximum of the two numbers. +#ifdef MAX +#undef MAX +#endif +template T MAX(T a, T b) +{ + return (b > a) ? b : a; +}; +//short MAX(short, short); +//int MAX(int, int); +//long MAX(long, long); +//#pragma option -Jgd + +// Returns the low word of a long +#define LOW_WORD(a) ((unsigned short)((long)(a) & 0x0000FFFFL)) + +// Returns the high word of a long +#define HIGH_WORD(a) ((unsigned long)(a) >> 16) + +// Merges to shorts to become a long +#define MAKE_LONG(a,b) (((long)(a) << 16) | (long)((b)&0x0000FFFFL)) + +/* +** Macro allows our routines to act like +** sprintf etc.. +*/ +#ifdef AssembleTo +#undef AssembleTo +#endif + +#define AssembleTo(dest,fmt)\ +{\ + va_list argptr;\ + if (fmt != (dest))\ + {\ + va_start (argptr, fmt);\ + vsprintf ((dest), fmt, argptr);\ + va_end (argptr);\ + }\ +} + +#if (0)//PG +// type definitions +//======================================= +typedef void VOID; + +//================================================== +// Pharlape defines these for use so we use their +// typedefs! +// typedef unsigned char BOOL; +// typedef signed long LONG; +// typedef unsigned long ULONG; +//================================================== +#ifndef PRIVATE +#define PRIVATE static +#endif + +// The purpose of the INT and UINT is for efficiency. It says that while a short int (16 bit) +// has enough precision, it is more efficient to pass in an int (32 bits). For efficiency, most +// WORD and UWORD should be an INT or UINT, especially on the stack and structures that will +// not be in large arrays. When size efficiency is more important then speed, use WORD UWORD. + +#define VOID void + +//#pragma warn -eas +#define TRUE 1 +#define FALSE 0 + +/* +** The "bool" integral type was defined by the C++ comittee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif +//#define true 1 +//#define false 0 + +#define BOOL int // 32 bits for speed. use CHAR for size optimizations. +#if(0) +#ifndef HMI_DRIVER +#define INT int +#define UINT unsigned int +#define BYTE char +#define UBYTE unsigned char +#define UCHAR unsigned char +#define WORD signed short +#define UWORD unsigned short +#define USHORT unsigned short + +#define LONG signed long +#define ULONG unsigned long +#define REALPTR unsigned long + +#define FARPTR char far * + +#endif +#endif +#endif //PG + +/* +** The type of processor running on this system as +** returned by Processor(). +*/ +#define PROC_80386 0 +#define PROC_80486 1 +#define PROC_PENTIUM 2 + + +// Inline Routines +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ +// +// These Template functions are generally used +// by classes when they havce over loaded > and <. +// +#ifdef __cplusplus +template T Min(T a, T b) +{ + return (a inline T Max(T a, T b) +{ + return (a>b ? a : b); +} + +template T Abs(T a) +{ + return ((a<0) ? -(a) : a); +} + +template VOID minimize(T &a, T b) +{ + if( b VOID maximize(T &a, T b) +{ + if( b>a ) + a=b; +} +#endif + +/* +** Macros that control bit settings in a variable. +*/ +#define Bit_Flags_On(a,b) a |= (b) +#define Bit_Flags_Off(a,b) a &= (~(b)) +#define Bit_Flags_Value(a,b) (a & (b)) +#define Bit_Flags_Flip(a,b) a ^= (b) + +// Template replacements for the user defines above +#ifdef __cplusplus +template VOID BitFlagsOn(T &a, T b) +{ + a |= (b); +} + +template VOID BitFlagsOff(T &a, T b) +{ + a &= (~(b)); +} + +template T BitFlagsValue(T a, T b) +{ + return (a & (b)); +} + +template VOID BitFlagsFlip(T &a, T b) +{ + a ^= (b); +} +#endif + +typedef enum : unsigned short { + TBLACK, + PURPLE, + CYAN, + GREEN, + LTGREEN, + YELLOW, + PINK, + BROWN, + RED, + LTCYAN, + LTBLUE, + BLUE, + BLACK, + GREY, + LTGREY, + WHITE, + COLOR_PADDING=0x1000 +} ColorType; + + +#endif diff --git a/code/redalert/winstub.cpp b/code/redalert/winstub.cpp new file mode 100644 index 0000000..1a77d5c --- /dev/null +++ b/code/redalert/winstub.cpp @@ -0,0 +1,1231 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WINSTUB.CPP 3 3/13/97 2:06p Steve_tall $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : WINSTUB.CPP * + * * + * Programmer : Steve Tall * + * * + * Start Date : 10/04/95 * + * * + * Last Update : October 4th 1995 [ST] * + * * + *---------------------------------------------------------------------------------------------* + * Overview: * + * This file contains stubs for undefined externals when linked under Watcom for Win 95 * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * Assert_Failure -- display the line and source file where a failed assert occurred * + * Check_For_Focus_Loss -- check for the end of the focus loss * + * Create_Main_Window -- opens the MainWindow for C&C * + * Focus_Loss -- this function is called when a library function detects focus loss * + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "imgui.h" +#include "examples/imgui_impl_sdl.h" +#include "function.h" +#include "il/il.h" +#include "il/ilu.h" +#include "image.h" + +#ifdef WINSOCK_IPX +#include "WSProto.h" +#else //WINSOCK_IPX +#include "tcpip.h" +#include "ipx95.h" +#endif //WINSOCK_IPX + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "HOUSECOLOR.H" +#include "IMGUTIL.H" + +GLuint backbuffer_texture = -1; +//byte* backbuffer_data; +unsigned char* backbuffer_data_raw; +byte backbuffer_palette[768]; +HWND MainWindow; +SurfaceMonitorClass AllSurfaces; +SDL_Window* game_window; +SDL_GLContext game_context; +int OverlappedVideoBlits = 0; +float animFrameNum = 0; + +extern std::vector renderedFrameObjects; + +bool renderConsole = false; + +void (*Misc_Focus_Loss_Function)(void) = nullptr; +void (*Misc_Focus_Restore_Function)(void) = nullptr; + +void (*Imgui_Dialog_Function)(void) = nullptr; + +bool show_oldframebuffer = true; +void Show_OldFrameBuffer(bool show) { + show_oldframebuffer = show; + memset(backbuffer_data_raw, 0, ScreenWidth * ScreenHeight * 4); +} + +std::vector loaded_images; + +unsigned char* Draw_Dropsample(const unsigned char* in, int inwidth, int inheight, int outwidth, int outheight); + +/* +================ +return a hash value for the filename +================ +*/ +int64_t generateHashValue(const char* fname, const int size) { + uint32_t hash = 0x811c9dc5; + uint32_t prime = 0x1000193; + + for (int i = 0; i < size; ++i) { + uint8_t value = fname[i]; + hash = hash ^ value; + hash *= prime; + } + + return hash; +} + +/* +================ +Image_WriteTGA +================ +*/ +void Image_WriteTGA(const char* filename, const byte* data, int width, int height, bool flipVertical) { + byte* buffer; + int i; + int bufferSize = width * height * 4 + 18; + int imgStart = 18; + + buffer = (byte*)malloc(bufferSize); + memset(buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width & 255; + buffer[13] = width >> 8; + buffer[14] = height & 255; + buffer[15] = height >> 8; + buffer[16] = 32; // pixel size + + if (!flipVertical) { + buffer[17] = (1 << 5); // flip bit, for normal top to bottom raster order + } + + // swap rgba to bgra + for (i = imgStart; i < bufferSize; i += 4) { + buffer[i] = data[i - imgStart + 2]; // blue + buffer[i + 1] = data[i - imgStart + 1]; // green + buffer[i + 2] = data[i - imgStart + 0]; // red + buffer[i + 3] = data[i - imgStart + 3]; // alpha + } + char temp[512]; + strcpy(temp, filename); + COM_SetExtension(temp, strlen(temp), ".tga"); + FILE* f = fopen(temp, "wb"); + fwrite(buffer, 1, bufferSize, f); + fclose(f); + + free(buffer); +} + + +static char* Image_GetGeneratedName(Image_t* image, const char* name, int houseid, int animid, int frameId, int64_t hash) { + static char generatedFileName[1024]; + sprintf(generatedFileName, "cache/v4.%d.%d.%d.%d.tga", frameId, houseid, animid, hash); + return &generatedFileName[0]; +} + +static bool Image_loadHDImage(Image_t *image, const char* name, int houseid, int animid, int frameId, int64_t hash, bool writeGenerated) { + // Check to see if we can load a generated image first. + // We only generate images that need a houseid + if (!writeGenerated && houseid >= 0) { + char* generatedImageName = Image_GetGeneratedName(image, name, houseid, animid, frameId, hash); + if (Image_loadHDImage(image, generatedImageName, houseid, animid, frameId, hash, true)) { + return true; + } + } + + ILuint ImageName; + ilGenImages(1, &ImageName); + ilBindImage(ImageName); + if (!ilLoadImage(name)) { + ilDeleteImages(1, &ImageName); + return false; + } + + bool swapBGR = false; + if (strstr(name, ".TGA") || strstr(name, ".tga")) { + if (!writeGenerated) + { + iluFlipImage(); + swapBGR = true; + } + } + + ILuint Width, Height, Bpp; + Width = ilGetInteger(IL_IMAGE_WIDTH); + Height = ilGetInteger(IL_IMAGE_HEIGHT); + Bpp = ilGetInteger(IL_IMAGE_BPP); + ILubyte* Data = ilGetData(); + + if (Bpp == 4 && houseid != -1 && !writeGenerated) { + //Buffer_Enable_HD_Texture(true); + //Data = Draw_Dropsample(Data, Width, Height, Width / 2, Height / 2); + //Buffer_Enable_HD_Texture(false); + //Width = Width / 2; + //Height = Height / 2; + for (int i = 0; i < Width * Height; i++) { + RenderTeamPalette(&Data[i * 4], *houseColors[houseid]); + } + } + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + if (Bpp == 4) { + if (swapBGR) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_BGRA, GL_UNSIGNED_BYTE, Data); + } + else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data); + } + } + else if (Bpp == 3) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, Data); + } + else if (Bpp == 1) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_R, GL_UNSIGNED_BYTE, Data); + } + else { + assert(!"Unknown BPP in image!"); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + + strcpy(image->name, name); + + if(houseid == -1) + image->HouseImages[0].image[animid][frameId] = texture; + else + image->HouseImages[houseid].image[animid][frameId] = texture; + image->namehash = hash; + image->renderwidth[animid] = image->width = Width; + image->renderheight[animid] = image->height = Height; + //image->buffer[houseid][animid] = new unsigned char[Width * Height * Bpp]; + //memcpy(image->buffer[houseid][animid], Data, Width * Height * Bpp); + + if (!writeGenerated && Bpp == 4 && houseid >= 0) { + char* generatedImageName = Image_GetGeneratedName(image, name, houseid, animid, frameId, hash); + Image_WriteTGA(generatedImageName, Data, Width, Height, false); + + Console_Printf("Writing %s to cache %s\n", image->name, name); + Sleep(0); + } + + ilDeleteImages(1, &ImageName); + + return true; +} + +Image_t* Image_LoadImage(const char* name, bool loadAnims, bool loadHouseColor) { + int64_t hash = generateHashValue(name, strlen(name)); + + int image_table_size = loaded_images.size(); + if (image_table_size > 0) + { + Image_t** image_table = &loaded_images[0]; + + // Check to see if the image is already loaded. + for (int i = 0; i < image_table_size; i++) { + if (image_table[i]->namehash == hash) { + return image_table[i]; + } + } + } + Image_t* image = new Image_t(); + + if (!loadAnims) { + unsigned char remap[3] = { 0, 0, 0 }; + if (!Image_loadHDImage(image, name, -1, 0, 0, hash, false)) { + delete image; + return NULL; + } + } + else { + byte* palette = (byte *)CCPalette.Get_Data(); + for (int i = 0; i < MAX_MPLAYER_COLORS; i++) { + if (!Image_loadHDImage(image, name, i, 0, 0, hash, false)) { + delete image; + return NULL; + } + } + } + + loaded_images.push_back(image); + return image; +} + +Image_t *Find_Image(const char* name) { + int64_t hash = generateHashValue(name, strlen(name)); + + int image_table_size = loaded_images.size(); + if (image_table_size > 0) + { + Image_t** image_table = &loaded_images[0]; + + // Check to see if the image is already loaded. + for (int i = 0; i < image_table_size; i++) { + if (image_table[i]->namehash == hash) { + return image_table[i]; + } + } + } + + return NULL; +} + +Image_t* Image_CreateImageFrom8Bit(const char* name, int Width, int Height, unsigned char *data, unsigned char* remap) { + int64_t hash = generateHashValue(name, strlen(name)); + + int image_table_size = loaded_images.size(); + if (image_table_size > 0) + { + Image_t** image_table = &loaded_images[0]; + + // Check to see if the image is already loaded. + for (int i = 0; i < image_table_size; i++) { + if (image_table[i]->namehash == hash) { + return image_table[i]; + } + } + } + + unsigned char* ccpalete = (unsigned char*)CCPalette.Get_Data(); + + Image_t* image = new Image_t(); + unsigned char *buffer = new unsigned char[Width * Height * 4]; + + for (int i = 0; i < Width * Height; i++) { + unsigned char c = data[i]; + if (remap) { + c = remap[c]; + } + + unsigned char r = ccpalete[(c * 3) + 0] << 2; + unsigned char g = ccpalete[(c * 3) + 1] << 2; + unsigned char b = ccpalete[(c * 3) + 2] << 2; + unsigned char a = 0; + + if ((r == 84 && g == 252 && b == 84) || (r == 0 && g == 168 && b == 0)) { + r = 0; + g = 0; + b = 0; + a = 128; + } + else if (c != 0) { + a = 255; + } + + + buffer[(i * 4) + 0] = r; + buffer[(i * 4) + 1] = g; + buffer[(i * 4) + 2] = b; + buffer[(i * 4) + 3] = a; + } + + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + + strcpy(image->name, name); + image->HouseImages[0].image[0][0] = texture; + image->numAnimFrames = -1; + image->namehash = hash; + image->renderwidth[0] = image->width = Width; + image->renderheight[0] = image->height = Height; + loaded_images.push_back(image); + delete buffer; + return image; +} + +void Image_Add8BitImage(Image_t *image, int HouseId, int ShapeID, int Width, int Height, unsigned char* data, unsigned char* remap) { + unsigned char* buffer = new unsigned char[Width * Height * 4]; + unsigned char* ccpalete = (unsigned char*)CCPalette.Get_Data(); + + if (image->width != Width || image->height != Height) { + assert(!"Image_Add8BitImage: Invalid new dimensions!"); + } + + for (int i = 0; i < Width * Height; i++) { + unsigned char c = data[i]; + if (remap) { + c = remap[c]; + } + + unsigned char r = ccpalete[(c * 3) + 0] << 2; + unsigned char g = ccpalete[(c * 3) + 1] << 2; + unsigned char b = ccpalete[(c * 3) + 2] << 2; + unsigned char a = 0; + + if ((r == 84 && g == 252 && b == 84) || (r == 0 && g == 168 && b == 0)) { + r = 0; + g = 0; + b = 0; + a = 128; + } + else if (c != 0) { + a = 255; + } + + + buffer[(i * 4) + 0] = r; + buffer[(i * 4) + 1] = g; + buffer[(i * 4) + 2] = b; + buffer[(i * 4) + 3] = a; + } + + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + + image->HouseImages[HouseId].image[ShapeID][0] = texture; + image->numAnimFrames = -1; + image->renderwidth[ShapeID] = image->width = Width; + image->renderheight[ShapeID] = image->height = Height; + + delete buffer; +} + +bool Image_Add32BitImage(const char *name, Image_t* image, int HouseId, int ShapeID, int frameId) { + return Image_loadHDImage(image, name, HouseId, ShapeID, frameId, image->namehash, false); +} + +BOOL Set_Video_Mode(HWND hwnd, int w, int h, int bits_per_pixel) { + // Todo implement resoluton scaling. + return true; // Always return true; +} + +unsigned int Get_Free_Video_Memory(void) { + return 64 * 1024 * 1024; +} + +unsigned int Get_Video_Hardware_Capabilities() { + return 0; +} + +void Reset_Video_Mode(void) { + +} + +void Set_DD_Palette(void* palette, bool raShift) +{ + char* palette_get = (char*)palette; //CCPalette.Get_Data(); + for (int j = 0; j < 768; j++) { + if (raShift) { + backbuffer_palette[j] = palette_get[j] << 2; + } + else { + backbuffer_palette[j] = palette_get[j]; + } + } +} + +void ImGui_NewFrame(void) { + ImGuiIO& io = ImGui::GetIO(); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + io.DisplaySize = ImVec2(ScreenWidth, ScreenHeight); + ImGui_ImplOpenGL3_NewFrame(); + ImGui::NewFrame(show_oldframebuffer); + + ImGui_ImplSDL2_NewFrame(game_window); + + renderedFrameObjects.clear(); +} + +void Device_Present(void) { + + // Rasterize the palette. + //for (int i = 0; i < ScreenWidth * ScreenHeight; i++) { + // backbuffer_data[(i * 4) + 0] = backbuffer_palette[(backbuffer_data_raw[i] * 3) + 0]; + // backbuffer_data[(i * 4) + 1] = backbuffer_palette[(backbuffer_data_raw[i] * 3) + 1]; + // backbuffer_data[(i * 4) + 2] = backbuffer_palette[(backbuffer_data_raw[i] * 3) + 2]; + // backbuffer_data[(i * 4) + 3] = 255; + //} + glBindTexture(GL_TEXTURE_2D, backbuffer_texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ScreenWidth, ScreenHeight, GL_RGBA, GL_UNSIGNED_BYTE, backbuffer_data_raw); + + bool ScreenActive; + ImGuiStyle& style = ImGui::GetStyle(); + style.FramePadding = ImVec2(0, 0); + ImGui::SetNextWindowSize(ImVec2(ScreenWidth, ScreenHeight)); + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::Begin("RenderWindow", &ScreenActive, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMouseInputs); + if (show_oldframebuffer) + { + ImVec2 desktopSize(ScreenWidth, ScreenHeight); + ImGui::Image((ImTextureID)backbuffer_texture, desktopSize); + } + + ImGui::End(); + + + if (Imgui_Dialog_Function) { + Imgui_Dialog_Function(); + } + + if (renderConsole) { + Console_Render(); + } + + WWMouse->RenderMouse(); + + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + SDL_GL_SwapWindow(game_window); + + // Last thing we do is execute any console commands + Console_Tick(); + + ImGui_NewFrame(); +} + + +void output(short,short) +{} + +unsigned long CCFocusMessage = WM_USER+50; //Private message for receiving application focus +extern void VQA_PauseAudio(void); +extern void VQA_ResumeAudio(void); + +//#include "WolDebug.h" + + +/*********************************************************************************************** + * Focus_Loss -- this function is called when a library function detects focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/1/96 2:10PM ST : Created * + *=============================================================================================*/ + +void Focus_Loss(void) +{ + Theme.Suspend(); + Stop_Primary_Sound_Buffer(); + if (WWMouse) WWMouse->Clear_Cursor_Clip(); +} + +void Focus_Restore(void) +{ + Restore_Cached_Icons(); + Map.Flag_To_Redraw(true); + Start_Primary_Sound_Buffer(TRUE); + if (WWMouse) WWMouse->Set_Cursor_Clip(); + VisiblePage.Clear(); + HiddenPage.Clear(); +} + + + +/*********************************************************************************************** + * Check_For_Focus_Loss -- check for the end of the focus loss * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 2/2/96 10:49AM ST : Created * + *=============================================================================================*/ + +void Check_For_Focus_Loss(void) +{ +#if (0)//PG + static BOOL focus_last_time = 1; + MSG msg; + + + if ( !GameInFocus ) { + Focus_Loss(); + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD ) ) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + if (!focus_last_time && GameInFocus) { + + VQA_PauseAudio(); + CountDownTimerClass cd; + cd.Set(60*1); + + do { + while (PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE )) { + if (!GetMessage( &msg, NULL, 0, 0 ) ) { + return; + } + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + } while (cd.Time()); + VQA_ResumeAudio(); + PostMessage (MainWindow, CCFocusMessage, 0, 0); +// AllSurfaces.Restore_Surfaces(); +// VisiblePage.Clear(); +// HiddenPage.Clear(); +// Map.Flag_To_Redraw(true); + } + + focus_last_time = GameInFocus; +#endif +} + +extern bool InMovie; + +HANDLE DebugFile = INVALID_HANDLE_VALUE; + +/*********************************************************************************************** + * WWDebugString -- sends a string to the debugger and echos it to disk * + * * + * * + * * + * INPUT: string * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/28/96 12:48PM ST : Created * + *=============================================================================================*/ +void WWDebugString (char *string) +{ + Console_Printf(string); +} + + + + + + + + + +/*********************************************************************************************** + * Create_Main_Window -- opens the MainWindow for C&C * + * * + * * + * * + * INPUT: instance -- handle to program instance * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/10/95 4:08PM ST : Created * + *=============================================================================================*/ + +#define CC_ICON 1 + +#define WINDOW_NAME "Red Alert" + +void Create_Main_Window ( HANDLE instance , int command_show , int width , int height ) + +{ + HWND hwnd ; + + ilInit(); + + SDL_Init(SDL_INIT_VIDEO); + game_window = SDL_CreateWindow(WINDOW_NAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL); + game_context = SDL_GL_CreateContext(game_window); + SDL_GL_SetSwapInterval(0); + + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(game_window, &wmInfo); + + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + + hwnd = wmInfo.info.win.window; + GameInFocus = TRUE; + + ShowWindow(hwnd, command_show ); + ShowCommand = command_show; + UpdateWindow(hwnd); + SetFocus(hwnd); + MainWindow = hwnd; + hInstance = instance; + + CCFocusMessage = RegisterWindowMessage("CC_GOT_FOCUS"); + + //Audio_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Loss_Function = &Focus_Loss; + Misc_Focus_Restore_Function = &Focus_Restore; + Gbuffer_Focus_Loss_Function = &Focus_Loss; + + glGenTextures(1, &backbuffer_texture); + glBindTexture(GL_TEXTURE_2D, backbuffer_texture); + // Give the image to OpenGL + backbuffer_data_raw = new byte[width * height * 4]; + memset(backbuffer_data_raw, 0, width * height * 4); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, backbuffer_data_raw); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glewInit(); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + //ImGui::StyleColorsClassic(); + + // Setup Platform/Renderer bindings + ImGui_ImplSDL2_InitForD3D(game_window); + ImGui_ImplOpenGL3_Init(); + + io.Fonts->AddFontFromFileTTF("fonts/Arial.ttf", 16.0f); + + ImGui_NewFrame(); +} + + +void Window_Dialog_Box(HANDLE hinst, LPCTSTR lpszTemplate, HWND hwndOwner, DLGPROC dlgprc) +{ +#if (0)//PG + MSG msg; + /* + ** Get rid of the Westwood mouse cursor because we are showing a + ** dialog box and we want to have the right windows cursor showing + ** for it. + */ + Hide_Mouse(); + ShowCursor(TRUE); + + /* + ** Pop up the dialog box and then run a standard message handler + ** until the dialog box is closed. + */ + + DialogBox(hinst, lpszTemplate, hwndOwner, dlgprc); + while (GetMessage(&msg, NULL, 0, 0) && !AllDone) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + /* + ** Restore the westwood mouse cursor and get rid of the windows one + ** because it is now time to restore back to the westwood way of + ** doing things. + */ + ShowCursor(FALSE); + Show_Mouse(); +#endif +} + + + +typedef struct tColourList { + + char Red; + char Green; + char Blue; +} ColourList; + +ColourList ColourLookup[9]={ + 0,0,0, + 63,0,0, + 0,63,0, + 0,0,63, + 63,0,63, + 63,63,0, + 0,63,63, + 32,32,32, + 63,63,63 +}; + + + + +int DebugColour=1; + + +extern "C" void Set_Palette_Register(int number, int red, int green, int blue); +//#pragma off (unreferenced) +void Colour_Debug (int call_number) +{ +#if (0)//PG + //#if 0 + //if (DebugColour==call_number || !call_number) { + + //if (call_number) { + // Wait_Vert_Blank(); + //} + + Set_Palette_Register (0,ColourLookup[call_number].Red , + ColourLookup[call_number].Green, + ColourLookup[call_number].Blue); + //} + //#endif +#endif +} + +//#pragma on (unreferenced) + + + + + +BOOL Any_Locked (void) +{ + if (SeenBuff.Get_LockCount() || + HidPage.Get_LockCount()) { + return (TRUE); + } else { + return(FALSE); + } +} + + + + + + + + +// +// Miscellaneous stubs. Mainly for multi player stuff +// +// +// + +//IPXAddressClass::IPXAddressClass(void) { +// int i; +// i++; +//} +//int IPXManagerClass::Num_Connections(void) { return (0); } +//int IPXManagerClass::Connection_ID( int ) { return (0); } +//IPXAddressClass * IPXManagerClass::Connection_Address( int ) { return ((IPXAddressClass*)0); } +//char * IPXManagerClass::Connection_Name( int ) { return ((char*)0); } +//int IPXAddressClass::Is_Broadcast() { return (0); } +//int IPXManagerClass::Send_Global_Message( void *, int, int, IPXAddressClass * ) { return (0); } +//int IPXManagerClass::Service() { return (0); } +//int IPXManagerClass::Get_Global_Message( void *, int *, IPXAddressClass *, short unsigned * ) { return (0); } +//int IPXAddressClass::operator ==( IPXAddressClass & ) { return (0); } +//IPXManagerClass::IPXManagerClass( int, int, int, int, short unsigned, short unsigned ) {} +//IPXManagerClass::~IPXManagerClass() { +// int i; +// i++; +// } +//int IPXManagerClass::Delete_Connection( int ) { return (0); } +//IPXAddressClass::IPXAddressClass( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Socket( short unsigned ) {} +//int IPXManagerClass::Is_IPX() { return (0); } +//int IPXManagerClass::Init() { return (0); } +//void IPXAddressClass::Get_Address( char unsigned *, char unsigned * ) {} +//void IPXManagerClass::Set_Bridge( char unsigned * ) {} +//int IPXManagerClass::Global_Num_Send() { return (0); } +//void IPXManagerClass::Set_Timing( long unsigned, long unsigned, long unsigned ) {} +//unsigned long IPXManagerClass::Global_Response_Time() { return (0); } +//int IPXManagerClass::Create_Connection( int, char *, IPXAddressClass * ) { return (0); } +//int IPXAddressClass::operator !=( IPXAddressClass & ) { return (0); } +//int IPXManagerClass::Send_Private_Message( void *, int, int, int ) { return (0); } +//int IPXManagerClass::Get_Private_Message( void *, int *, int * ) { return (0); } +//int IPXManagerClass::Connection_Index( int ) { return (0); } +//void IPXManagerClass::Reset_Response_Time() {} +//long unsigned IPXManagerClass::Response_Time() { return (0); } +//int IPXManagerClass::Private_Num_Send( int ) { return (0); } + +//void VQA_Init(VQAHandle *, long(*)(VQAHandle *vqa, long action, void *buffer, long nbytes)) {} + +//long VQA_Open(VQAHandle *, char const *, VQAConfig *) +//{ +// return (0); +//} + +//void VQA_Close(VQAHandle *) {} + +//long VQA_Play(VQAHandle *, long) +//{ +// return (0); +//} + + +unsigned char *VQPalette; +long VQNumBytes; +unsigned long VQSlowpal; +bool VQPaletteChange = false; + + +extern "C"{ + void __cdecl SetPalette(unsigned char *palette, long numbytes, unsigned long slowpal); +} + + + +void Flag_To_Set_Palette(unsigned char *palette, long numbytes, unsigned long slowpal) +{ + VQPalette = palette; + VQNumBytes = numbytes; + VQSlowpal = slowpal; + VQPaletteChange = true; +} + + + +void Check_VQ_Palette_Set(void) +{ + if (VQPaletteChange) { + SetPalette(VQPalette, VQNumBytes, VQSlowpal); + VQPaletteChange = false; + } +} + + + + + +void __cdecl SetPalette(unsigned char *palette, long, unsigned long) +{ + for (int i=0 ; i<256*3 ; i++) { + *(palette+i)&=63; + } + Increase_Palette_Luminance(palette , 15 , 15 , 15 ,63); + + if (PalettesRead) { + memcpy (&PaletteInterpolationTable[0][0] , InterpolatedPalettes[PaletteCounter++] , 65536); + } + Set_Palette(palette); +} + + +void WWSDL_ProcessEvents(KeyNumType& key, int& flags, bool presentBuffer) { + //UserInput.Process_Input(key, flags); +} + +#ifndef NDEBUG +/*********************************************************************************************** + * Assert_Failure -- display the line and source file where a failed assert occurred * + * * + * * + * INPUT: line number in source file * + * name of source file * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 4/17/96 9:58AM ST : Created * + *=============================================================================================*/ + +void Assert_Failure (char *expression, int line, char *file) +{ + char assertbuf[256]; + char timebuff[512]; + SYSTEMTIME time; + + sprintf (assertbuf, "assert '%s' failed at line %d in module %s.\n", expression, line, file); + + if (!MonoClass::Is_Enabled()) MonoClass::Enable(); + + Mono_Clear_Screen(); + Mono_Printf("%s", assertbuf); + + WWDebugString(assertbuf); + + GetLocalTime(&time); + + sprintf (timebuff, "%02d/%02d/%04d %02d:%02d:%02d - %s", time.wMonth, time.wDay, time.wYear, + time.wHour, time.wMinute, time.wSecond, + assertbuf); + + + HMMIO handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + if (!handle) { + handle = mmioOpen("ASSERT.TXT", NULL, MMIO_CREATE | MMIO_WRITE); + //mmioClose(handle, 0); + //handle = mmioOpen("ASSERT.TXT", NULL, MMIO_WRITE); + } + + if (handle) { + + mmioWrite(handle, timebuff, strlen(timebuff)); + mmioClose(handle, 0); + } + + WWMessageBox().Process(assertbuf); + + Prog_End(assertbuf, false); + Invalidate_Cached_Icons(); +} +#endif + + + + + + + +/*********************************************************************************************** + * Memory_Error_Handler -- Handle a possibly fatal failure to allocate memory * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 5/22/96 3:57PM ST : Created * + *=============================================================================================*/ +void Memory_Error_Handler(void) +{ + VisiblePage.Clear(); + CCPalette.Set(); + while (Get_Mouse_State()) {Show_Mouse();}; + WWMessageBox().Process(TEXT_MEMORY_ERROR, TEXT_ABORT, false); + + ReadyToQuit = 1; + + PostMessage(MainWindow, WM_DESTROY, 0, 0); + do + { + Keyboard->Check(); + }while (ReadyToQuit == 1); + + ExitProcess(0); +} + + + + + + + + +GraphicBufferClass* Read_PCX_File(char* name, char* Palette, void *Buff, long Size); +void Load_Title_Screen(char *name, GraphicViewPortClass *video_page, unsigned char *palette) { + char filename[512]; + sprintf(filename, "ui/%s", name); + COM_SetExtension(filename, sizeof(filename), ".png"); + Image_t* image = Image_LoadImage(filename); + + GL_RenderImage(image, 0, 0, ScreenWidth, ScreenHeight); + + // We still have to do this for palette reasons. + { + GraphicBufferClass* load_buffer; + load_buffer = Read_PCX_File(name, (char*)palette, NULL, 0); + delete load_buffer; + //if (load_buffer) { + // load_buffer->Scale(*video_page, 0, 0, 0, 0, load_buffer->Get_Width(), load_buffer->Get_Height(), ScreenWidth, ScreenHeight, NULL); + // delete load_buffer; + //} + } +} + + + +#include "filepcx.h" + +/*************************************************************************** + * READ_PCX_FILE -- read a pcx file into a Graphic Buffer * + * * + * GraphicBufferClass* Read_PCX_File (char* name, char* palette ,void *Buff, long size ); * + * * + * * + * INPUT: name is a NULL terminated string of the format [xxxx.pcx] * + * palette is optional, if palette != NULL the the color palette of * + * the pcx file will be place in the memory block pointed * + * by palette. * + * Buff is optional, if Buff == NULL a new memory Buffer * + * will be allocated, otherwise the file will be placed * + * at location pointed by Buffer; * + * Size is the size in bytes of the memory block pointed by Buff * + * is also optional; * * + * OUTPUT: on success a pointer to a GraphicBufferClass containing the * + * pcx file, NULL otherwise. * + * * + * WARNINGS: * + * Appears to be a comment-free zone * + * * + * HISTORY: * + * 05/03/1995 JRJ : Created. * + * 04/30/1996 ST : Tidied up and modified to use CCFileClass * + *=========================================================================*/ + +#define POOL_SIZE 2048 +#define READ_CHAR() *file_ptr++ ; \ + if ( file_ptr >= & pool [ POOL_SIZE ] ) { \ + file_handle.Read (pool , POOL_SIZE ); \ + file_ptr = pool ; \ + } + + +GraphicBufferClass* Read_PCX_File(char* name, char* palette, void *Buff, long Size) +{ + unsigned i, j; + unsigned rle; + unsigned color; + unsigned scan_pos; + char *file_ptr; + int width; + int height; + char *buffer; + PCX_HEADER header; + RGB *pal; + char pool [POOL_SIZE]; + GraphicBufferClass *pic; + + CCFileClass file_handle(name); + + if (!file_handle.Is_Available()) return (NULL); + + file_handle.Open(READ); + + file_handle.Read (&header, sizeof (PCX_HEADER)); + + if (header.id != 10 && header.version != 5 && header.pixelsize != 8 ) return NULL ; + + width = header.width - header.x + 1; + height = header.height - header.y + 1; + + if (Buff) { + buffer = (char *)Buff; + i = Size / width; + height = MIN ((int)i - 1, height); + pic = new GraphicBufferClass(width, height, buffer, Size); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } else { + pic = new GraphicBufferClass(width, height, NULL, width*(height+4)); + if ( !(pic && pic->Get_Buffer()) ) return NULL ; + } + + buffer = (char *) pic->Get_Buffer(); + file_ptr = pool ; + file_handle.Read (pool , POOL_SIZE); + + if ( header.byte_per_line != width ) { + + for ( scan_pos = j = 0 ; j < (unsigned) height ; j ++, scan_pos += width ) { + for ( i = 0 ; i < (unsigned)width ; ) { + rle = READ_CHAR (); + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); ; + memset ( buffer + scan_pos + i , color , rle ); + i += rle; + } else { + *(buffer+scan_pos + i++ ) = (char)rle; + } + } + } + + if ( i == width ) rle = READ_CHAR (); + if ( rle > 192 ) rle = READ_CHAR (); + + } else { + + for ( i = 0 ; i < (unsigned)width * height ; ) { + rle = READ_CHAR (); + rle &= 0xff; + if ( rle > 192 ) { + rle -= 192 ; + color = READ_CHAR (); + memset ( buffer + i , color , rle ); + i += rle ; + } else { + *(buffer + i++) = (char)rle; + } + } + } + + if ( palette ) { + file_handle.Seek (- (256 * (int)sizeof ( RGB )) , SEEK_END ); + file_handle.Read (palette , 256L * sizeof ( RGB )); + + pal = ( RGB * ) palette; + for (i = 0 ; i < 256 ; i ++) { + pal ->red >>= 2; + pal ->green >>= 2; + pal ->blue >>= 2; + pal ++ ; + } + } + + file_handle.Close(); + return pic; +} \ No newline at end of file diff --git a/code/redalert/wol_cgam.cpp b/code/redalert/wol_cgam.cpp new file mode 100644 index 0000000..72bc6e6 --- /dev/null +++ b/code/redalert/wol_cgam.cpp @@ -0,0 +1,386 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION // Implies FIXIT_CSII. + +// Wol_CGam.cpp - Create game dialog. +// ajw 09/9/98 + +#include "function.h" + +#ifndef FIXIT_CSII +#error FIXIT_CSII must be defined. +#endif + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "BigCheck.h" + +//extern char* LoadShpFile( const char* szShpFile ); +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ); + +//*********************************************************************************************** +CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ) +{ + CREATEGAMEINFO cgiReturn; + cgiReturn.bCreateGame = false; + cgiReturn.iPlayerMax = 2; + cgiReturn.bTournament = false; + cgiReturn.bPrivate = false; + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + *cgiReturn.szPassword = 0; + + bool bEscapeDown = false; + bool bReturnDown = false; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 150 * RESFACTOR; // dialog width + int d_dialog_h = 135 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_gaugeplayers_w = 70 * RESFACTOR; + int d_gaugeplayers_h = 9 * RESFACTOR; + int d_gaugeplayers_x = d_dialog_cx - d_gaugeplayers_w / 2; + int d_gaugeplayers_y = d_dialog_y + d_margin + 42; + + int d_checktourn_w = 75 * RESFACTOR; + int d_checktourn_h = 9 * RESFACTOR; + int d_checktourn_x = d_dialog_cx - d_checktourn_w / 2; + int d_checktourn_y = d_gaugeplayers_y + d_gaugeplayers_h + 10; + + int d_checkpriv_w = d_checktourn_w; + int d_checkpriv_h = 9 * RESFACTOR; + int d_checkpriv_x = d_checktourn_x; + int d_checkpriv_y = d_checktourn_y + d_checktourn_h + 10; + + int d_checkra_w = d_checktourn_w; + int d_checkra_h = 9 * RESFACTOR; + int d_checkra_x = d_checktourn_x; + int d_checkra_y = d_checkpriv_y + d_checkpriv_h + 20; + + int d_checkcs_w = d_checktourn_w; + int d_checkcs_h = 9 * RESFACTOR; + int d_checkcs_x = d_checktourn_x; + int d_checkcs_y = d_checkra_y + d_checkra_h + 5; + + int d_checkam_w = d_checktourn_w; + int d_checkam_h = 9 * RESFACTOR; + int d_checkam_x = d_checktourn_x; + int d_checkam_y = d_checkcs_y + d_checkcs_h + 5; + +#if (GERMAN | FRENCH) + int d_ok_w = 30 * RESFACTOR; +#else + int d_ok_w = 30 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_x + ( d_dialog_w / 3 ) - ( d_ok_w / 2 ); + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + int d_cancel_w = 40 * RESFACTOR; + int d_cancel_h = 9 * RESFACTOR; + int d_cancel_x = d_dialog_x + ( ( d_dialog_w * 2 ) / 3 ) - ( d_cancel_w / 2 ); + int d_cancel_y = d_ok_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + BUTTON_CANCEL, + GAUGE_PLAYERCOUNT, + CHECK_TOURNAMENT, + CHECK_PRIVACY, + CHECK_RA, + CHECK_CS, + CHECK_AM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + StaticButtonClass PlayerCountStatic( 0, " ", TPF_TEXT, d_gaugeplayers_x, d_gaugeplayers_y - 16 ); + GaugeClass PlayerCountGauge( GAUGE_PLAYERCOUNT, d_gaugeplayers_x, d_gaugeplayers_y, d_gaugeplayers_w, d_gaugeplayers_h ); + + if( pWO->bEgg8Player ) + PlayerCountGauge.Set_Maximum( 6 ); + else + PlayerCountGauge.Set_Maximum( 2 ); + PlayerCountGauge.Set_Value( cgiReturn.iPlayerMax - 2 ); + + BigCheckBoxClass TournamentCheck( CHECK_TOURNAMENT, d_checktourn_x, d_checktourn_y, d_checktourn_w, d_checktourn_h, + TXT_WOL_CG_TOURNAMENT, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bTournament ); + + BigCheckBoxClass PrivacyCheck( CHECK_PRIVACY, d_checkpriv_x, d_checkpriv_y, d_checkpriv_w, d_checkpriv_h, + TXT_WOL_CG_PRIVACY, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.bPrivate ); + + BigCheckBoxClass RA_Check( CHECK_RA, d_checkra_x, d_checkra_y, d_checkra_w, d_checkra_h, + TXT_WOL_CG_RAGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::RAGAME ); + BigCheckBoxClass CS_Check( CHECK_CS, d_checkcs_x, d_checkcs_y, d_checkcs_w, d_checkcs_h, + TXT_WOL_CG_CSGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::CSGAME ); + BigCheckBoxClass AM_Check( CHECK_AM, d_checkam_x, d_checkam_y, d_checkam_w, d_checkam_h, + TXT_WOL_CG_AMGAME, TPF_6PT_GRAD | TPF_NOSHADOW, cgiReturn.GameKind == CREATEGAMEINFO::AMGAME ); + + if( !Is_Counterstrike_Installed() ) + CS_Check.Disable(); + + if( !Is_Aftermath_Installed() ) + AM_Check.Disable(); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + CancelBtn.Add_Tail(*commands); + PlayerCountStatic.Add_Tail(*commands); + PlayerCountGauge.Add_Tail(*commands); + TournamentCheck.Add_Tail(*commands); + PrivacyCheck.Add_Tail(*commands); + RA_Check.Add_Tail(*commands); + CS_Check.Add_Tail(*commands); + AM_Check.Add_Tail(*commands); + + char szPlayerCount[ 100 ]; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_CG_TITLE, d_dialog_x, d_dialog_y, d_dialog_w ); +// Fancy_Text_Print( TXT_WOL_CG_PLAYERS, d_gaugeplayers_x - 2*RESFACTOR, d_gaugeplayers_y, +// GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_RIGHT ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_CANCEL | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + bReturnDown = true; + } + else if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + + /* + ** Process input. + */ + + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + cgiReturn.bCreateGame = true; + process = false; + break; + + case ( BUTTON_CANCEL | KN_BUTTON ): + process = false; + break; + + case ( GAUGE_PLAYERCOUNT | KN_BUTTON ): + if( PlayerCountGauge.Get_Value() != 0 && cgiReturn.bTournament ) + { + WWMessageBox().Process( TXT_WOL_TOURNAMENTPLAYERLIMIT ); + PlayerCountGauge.Set_Value( 0 ); + display = true; + } + cgiReturn.iPlayerMax = PlayerCountGauge.Get_Value() + 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + break; + + case ( CHECK_TOURNAMENT | KN_BUTTON ): + cgiReturn.bTournament = TournamentCheck.IsOn; + if( cgiReturn.bTournament ) + { + PlayerCountGauge.Set_Value( 0 ); +// PlayerCountGauge.Disable(); + cgiReturn.iPlayerMax = 2; + sprintf( szPlayerCount, TXT_WOL_CG_PLAYERS, cgiReturn.iPlayerMax ); + PlayerCountStatic.Set_Text( szPlayerCount ); + PlayerCountStatic.Draw_Me(); + } +// else +// PlayerCountGauge.Enable(); + break; + + case ( CHECK_PRIVACY | KN_BUTTON ): + cgiReturn.bPrivate = PrivacyCheck.IsOn; + break; + + case ( CHECK_RA | KN_BUTTON ): + if( RA_Check.IsOn ) + { + // Box was checked. + CS_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::RAGAME; + } + else + // Box was unchecked. Has no effect. + RA_Check.Turn_On(); + break; + case ( CHECK_CS | KN_BUTTON ): + if( CS_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + AM_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::CSGAME; + } + else + // Box was unchecked. Has no effect. + CS_Check.Turn_On(); + break; + case ( CHECK_AM | KN_BUTTON ): + if( AM_Check.IsOn ) + { + // Box was checked. + RA_Check.Turn_Off(); + CS_Check.Turn_Off(); + cgiReturn.GameKind = CREATEGAMEINFO::AMGAME; + } + else + // Box was unchecked. Has no effect. + AM_Check.Turn_On(); + break; + + default: + break; + } + } + + if( cgiReturn.bCreateGame && cgiReturn.bPrivate ) + { + // Get a password for the channel. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATEPRIVGAMETITLE, + TXT_WOL_PASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + strcpy( cgiReturn.szPassword, pEditDlg->szEdit ); + else + cgiReturn.bCreateGame = false; // Cancel creation. + pWO->bPump_In_Call_Back = false; + } + + return cgiReturn; +} + +//*********************************************************************************************** +void SetPlayerCountList( IconListClass& PlayerCountList, int iPlayerMax, char* pShpBoxCheck, char* pShpBoxEmpty ) +{ + // Checks appropriate list item based on iPlayerMax. + switch( iPlayerMax ) + { + case 2: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 3: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + break; + case 4: + PlayerCountList.Set_Icon( 0, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 1, 0, (void*)pShpBoxEmpty, ICON_SHAPE ); + PlayerCountList.Set_Icon( 2, 0, (void*)pShpBoxCheck, ICON_SHAPE ); + break; + } +} + +#endif diff --git a/code/redalert/wol_chat.cpp b/code/redalert/wol_chat.cpp new file mode 100644 index 0000000..be36a01 --- /dev/null +++ b/code/redalert/wol_chat.cpp @@ -0,0 +1,1578 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// wol_chat.cpp +// ajw 7/8/98 + +#include "function.h" +#include "iconlist.h" +#include "WolapiOb.h" +#include "SEditDlg.h" +#include "WolStrng.h" +#include "ToolTip.h" + +//#include "WolDebug.h" + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ); +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ); +bool ExitChatChannel( WolapiObject* pWO ); +void CreateChatChannel( WolapiObject* pWO ); +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ); +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ); + +enum LIST_EXPAND_STATE +{ + LES_NORMAL, + LES_CHANNELS_EXPANDED, + LES_USERS_EXPANDED, +}; +static LIST_EXPAND_STATE lesCurrent = LES_NORMAL; + +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ); +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ); +void ResizeChannelList( IconListClass& chanlist, bool bExpand ); +void ResizeUserList( IconListClass& userlist, bool bExpand ); + +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ); + +extern CREATEGAMEINFO WOL_CreateGame_Dialog( WolapiObject* pWO ); + +static int d_chanlist_w; +static int d_chanlist_h; +static int d_chanlist_x; +static int d_chanlist_y; + +static int d_userlist_w; +static int d_userlist_h; +static int d_userlist_x; +static int d_userlist_y; + +#define DRAWTOGDOWN Turn_Off() +#define DRAWTOGUP Turn_On() + +//*********************************************************************************************** +int WOL_Chat_Dialog( WolapiObject* pWO ) +{ + int rc; + bool bFirsttime = true; + bool bHackFocus = true; + + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + int d_dialog_w = 320 *RESFACTOR; // dialog width + int d_dialog_h = 200 *RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_text_h = 12; + int d_margin1 = 34; // large margin + int d_margin2 = 14; // small margin + + int d_chatlist_w = 340; + int d_chatlist_x = d_dialog_x + d_margin1; + int d_chatlist_y = d_dialog_y + d_margin2 + d_margin1 + 27; + int d_chatlist_h = 337 - d_chatlist_y; + + d_chanlist_w = 227; + d_chanlist_h = 50 * RESFACTOR; + d_chanlist_x = d_dialog_x + d_dialog_w - (d_margin1 + d_chanlist_w); + d_chanlist_y = d_chatlist_y; + + d_userlist_w = d_chanlist_w; +// int d_userlist_h = ((10 * 6) + 3) *RESFACTOR; + d_userlist_x = d_chanlist_x; + d_userlist_y = d_chanlist_y + d_chanlist_h + 14 + 5; + + d_userlist_h = d_chatlist_y + d_chatlist_h - d_userlist_y; + + int d_action_w = 100; + int d_action_h = 9 *RESFACTOR; + int d_action_x = d_dialog_x + 500; + int d_action_y = 365; + +// int d_chanpriv_w = 60; +// int d_chanpriv_h = 9 *RESFACTOR; +// int d_chanpriv_x = d_dialog_x + 150; +// int d_chanpriv_y = d_action_y; + +// int d_cgame_w = 60; +// int d_cgame_h = 9 *RESFACTOR; +// int d_cgame_x = d_dialog_x + 390; //d_dialog_cx - d_cgame_w / 2; +// int d_cgame_y = d_action_y; + + int d_back_w = 100; + int d_back_h = 9 *RESFACTOR; + int d_back_x = d_dialog_x + 100; + int d_back_y = d_action_y; + + int d_join_w = 100; + int d_join_h = 9 *RESFACTOR; + int d_join_x = d_dialog_x + 210; + int d_join_y = d_action_y; + + int d_create_w = 100; + int d_create_h = 9 *RESFACTOR; + int d_create_x = d_dialog_x + 320; //((d_dialog_w * 5) / 6) - (d_create_w / 2); + int d_create_y = d_action_y; + + int d_send_w = d_chanlist_x + d_chanlist_w - d_chatlist_x; + int d_send_h = 9 *RESFACTOR; + int d_send_x = d_chatlist_x; + int d_send_y = d_chatlist_y + d_chatlist_h + 5; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_CHATLIST, + BUTTON_CHANLIST, + BUTTON_USERLIST, + BUTTON_SENDEDIT, + BUTTON_ACTION, +// BUTTON_CGAME, + BUTTON_CREATE, + BUTTON_JOIN, + BUTTON_BACK, + BUTTON_EXPANDCHANNELS, + BUTTON_EXPANDUSERS, + BUTTON_RANKRA, + BUTTON_RANKAM + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + RedrawType display = REDRAW_ALL; // redraw level + bool process = true; // process while true + KeyNumType input; + + TTimerClass lastclick_timer; + int lastclick_idx = 0; // index of item last clicked on + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + //------------------------------------------------------------------------ + // Buttons + //------------------------------------------------------------------------ + GadgetClass *commands; // button list + + char* pShpExpand = (char*)MFCD::Retrieve( "exp.shp" ); + char* pShpUnexpand = (char*)MFCD::Retrieve( "unexp.shp" ); + + IconListClass chatlist( BUTTON_CHATLIST, d_chatlist_x, d_chatlist_y, d_chatlist_w, d_chatlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 500 ); + ShapeButtonClass ExpandChanBtn( BUTTON_EXPANDCHANNELS, pShpExpand, d_chanlist_x + d_chanlist_w - 17, d_chanlist_y - 14 ); + IconListClass chanlist( BUTTON_CHANLIST, d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + ShapeButtonClass ExpandUserBtn( BUTTON_EXPANDUSERS, pShpExpand, d_userlist_x + d_userlist_w - 17, d_userlist_y - 14 ); + IconListClass userlist( BUTTON_USERLIST, d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); + TextButtonClass ActionBtn( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + TextButtonClass CreateBtn( BUTTON_CREATE, TXT_WOL_NEWSOMETHING, TPF_BUTTON, d_create_x, d_create_y, d_create_w ); + TextButtonClass JoinBtn( BUTTON_JOIN, TXT_WOL_JOIN, TPF_BUTTON, d_join_x, d_join_y, d_join_w ); + TextButtonClass BackBtn( BUTTON_BACK, TXT_WOL_BACK, TPF_BUTTON, d_back_x, d_back_y, d_back_w ); + char* szRecordToStartWith; + if( pWO->bShowRankRA ) + szRecordToStartWith = pWO->szMyRecord; + else + szRecordToStartWith = pWO->szMyRecordAM; + StaticButtonClass chatlistTitle( 0, szRecordToStartWith, TPF_TYPE, d_chatlist_x + 2, d_chatlist_y - 13, d_chatlist_w - 4, 12 ); + StaticButtonClass chanlistTitle( 0, "", TPF_TYPE, d_chanlist_x + 2, d_chanlist_y - 16 + 4, d_chanlist_w - 4 - 16, 12 ); + StaticButtonClass userlistTitle( 0, TXT_WOL_NOUSERLIST, TPF_TYPE, d_userlist_x + 2, d_userlist_y - 16 + 4, d_userlist_w - 4 - 16 * 4, 12 ); + + char szSendBuffer[MAXCHATSENDLENGTH] = ""; + EditClass sendedit( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + + char* pShpRankRA = (char*)MFCD::Retrieve( "rank_ra.shp" ); + char* pShpRankAM = (char*)MFCD::Retrieve( "rank_am.shp" ); + ShapeButtonClass RankRABtn( BUTTON_RANKRA, pShpRankRA, d_userlist_x + d_userlist_w - ( 16 * 4 + 1 ), d_userlist_y - 14 ); + ShapeButtonClass RankAMBtn( BUTTON_RANKAM, pShpRankAM, d_userlist_x + d_userlist_w - ( 16 * 3 + 1 ), d_userlist_y - 14 ); + // Change draw behavior of toggle buttons. + RankRABtn.ReflectButtonState = true; + RankAMBtn.ReflectButtonState = true; + + // Build the button list. + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + chatlist.Add_Tail(*commands); + ExpandChanBtn.Add_Tail(*commands); + chanlist.Add_Tail(*commands); + ExpandUserBtn.Add_Tail(*commands); + userlist.Add_Tail(*commands); + ActionBtn.Add_Tail(*commands); + CreateBtn.Add_Tail(*commands); +// CreatePrivBtn.Add_Tail(*commands); + JoinBtn.Add_Tail(*commands); + BackBtn.Add_Tail(*commands); +// CGameBtn.Add_Tail(*commands); + chatlistTitle.Add_Tail(*commands); + chanlistTitle.Add_Tail(*commands); + userlistTitle.Add_Tail(*commands); + sendedit.Add_Tail(*commands); + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead = NULL; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast = NULL; // ToolTip the mouse was last over, or null. + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + pToolTip = pToolTip->next; + ToolTipClass TTipChanExpand( &ExpandChanBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandChanBtn.X + 8, ExpandChanBtn.Y - 9, true ); + pToolTip->next = &TTipChanExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipUserExpand( &ExpandUserBtn, TXT_WOL_TTIP_EXPANDLIST, ExpandUserBtn.X + 8, ExpandUserBtn.Y - 9, true ); + pToolTip->next = &TTipUserExpand; + pToolTip = pToolTip->next; + ToolTipClass TTipChanList( &chanlist, 0, chanlist.X + 1, chanlist.Y + 1, true, true ); + pToolTip->next = &TTipChanList; + pToolTip = pToolTip->next; + ToolTipClass TTipJoin( &JoinBtn, TXT_WOL_TTIP_JOIN, d_join_x + d_join_w/2, d_join_y - 6 ); + pToolTip->next = &TTipJoin; + pToolTip = pToolTip->next; + ToolTipClass TTipBack( &BackBtn, TXT_WOL_TTIP_BACK, d_back_x + d_back_w/2, d_back_y - 6 ); + pToolTip->next = &TTipBack; + pToolTip = pToolTip->next; + ToolTipClass TTipCreate( &CreateBtn, TXT_WOL_TTIP_CREATE, d_create_x + d_create_w/2, d_create_y - 6 ); + pToolTip->next = &TTipCreate; + pToolTip = pToolTip->next; + ToolTipClass TTipAction( &ActionBtn, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + pToolTip->next = &TTipAction; + pToolTip = pToolTip->next; + ToolTipClass TTipRankRA( &RankRABtn, TXT_WOL_TTIP_RANKRA, RankRABtn.X + 8, RankRABtn.Y - 9, true ); + pToolTip->next = &TTipRankRA; + pToolTip = pToolTip->next; + ToolTipClass TTipRankAM( &RankAMBtn, TXT_WOL_TTIP_RANKAM, RankAMBtn.X + 8, RankAMBtn.Y - 9, true ); + pToolTip->next = &TTipRankAM; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + //........................................................................ + // List boxes + //........................................................................ + int tabs[] = { 150 }; // tabs for channel list + chanlist.Set_Tabs( tabs ); + +// Fancy_Text_Print("", 0, 0, scheme, TBLACK, TPF_TEXT); + + lastclick_timer = 0; + + // Tell WolapiObject about lists to use for output. + // (Sure wish I'd gone against the grain and made this dialog a class...) + pWO->LinkToChatDlg( &chatlist, &chanlist, &userlist, &userlistTitle ); + + if( !pWO->bChatShownBefore ) + { + // Print message of the day. + chatlist.Add_Item( pWO->pChatSink->szMotd, NULL, NULL, ICON_SHAPE ); + } + else + { + // We have returned to the chat dialog after being in either game setup or an actual game. + pWO->RestoreChat(); + pWO->DeleteSavedChat(); + + if( pWO->bReturningAfterGame ) + pWO->RejoinLobbyAfterGame(); + else + { + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of a game setup. + WOL_PrintMessage( chatlist, TXT_WOL_YOUWEREKICKEDFROMGAME, WOLCOLORREMAP_KICKORBAN ); + pWO->pChatSink->bGotKickedTrigger = false; + } + } + + if( pWO->iLobbyReturnAfterGame != -1 ) + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, pWO->iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, pWO->iLobbyReturnAfterGame ); + pWO->OnEnteringChatChannel( szChannelToJoin, false, iChannelLobbyNumber( (unsigned char*)szChannelToJoin ) ); + } + else + // Will never happen presumably, if games are always entered via a lobby chat channel. + pWO->EnterLevel_Top(); + + pWO->iLobbyReturnAfterGame = -1; + + if( pWO->bReturningAfterGame ) + { + Sound_Effect( WOLSOUND_LOGIN ); + pWO->bReturningAfterGame = false; + } + else + Sound_Effect( WOLSOUND_EXITGAME ); + } + + // Cause a refresh of szMyRecord, the string showing my win/loss record. + pWO->RequestLadders( pWO->szMyName ); + + //------------------------------------------------------------------------ + // Init Mono Output + //------------------------------------------------------------------------ + #if(SHOW_MONO) + Ipx.Configure_Debug(-1, sizeof (GlobalHeaderType), sizeof(NetCommandType), GlobalPacketNames, 0, 13); + Ipx.Mono_Debug_Print(-1,1); + #endif + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (process) + { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { +/* + if( pToolTipHitLast && pToolTipHitLast->bShowing ) // Lame hack. Problem is draws that occur in callbacks. + { + pToolTipHitLast->Unshow(); + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + pToolTipHitLast->Show(); + } + else + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + } +*/ + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + + // Special post-callback processing... + if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + rc = -1; // As if the user logged himself out. + process = false; + break; + } + if( pWO->pChatSink->bGotKickedTrigger ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_UserChat(); + } + else if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_Lobbies(); + } + else // Must be WOL_LEVEL_INOFFICIALCHATCHANNEL. + { + pWO->OnExitingChatChannel(); + pWO->EnterLevel_OfficialChat(); + } + pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + if( pWO->bMyRecordUpdated ) + { + if( pWO->bShowRankRA ) + chatlistTitle.Set_Text( pWO->szMyRecord ); + else + chatlistTitle.Set_Text( pWO->szMyRecordAM ); + pWO->bMyRecordUpdated = false; + } + if( pWO->bChannelListTitleUpdated ) + { + chanlistTitle.Set_Text( pWO->szChannelListTitle ); + pWO->bChannelListTitleUpdated = false; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Synch rank toggle buttons state. + if( BackBtn.Get_Prev() == &RankAMBtn ) + { + if( pWO->CurrentLevel != WOL_LEVEL_INLOBBY ) + { + // Rank buttons are there and shouldn't be. + RankRABtn.Remove(); + RankAMBtn.Remove(); + display = REDRAW_ALL; + } + else + { + if( pWO->bShowRankUpdated ) + { + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + // Cause my own record to get refreshed. + pWO->bMyRecordUpdated = true; + // Refresh list to show different rankings type. + pWO->ListChannelUsers(); + } + } + } + else + { + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Rank buttons aren't there and should be. + RankRABtn.Add( JoinBtn ); + RankAMBtn.Add( RankRABtn ); + RankRABtn.Flag_To_Redraw(); + RankAMBtn.Flag_To_Redraw(); + if( pWO->bShowRankRA ) + { + RankRABtn.DRAWTOGDOWN; + RankAMBtn.DRAWTOGUP; + } + else + { + RankRABtn.DRAWTOGUP; + RankAMBtn.DRAWTOGDOWN; + } + // Buttons have been refreshed. + pWO->bShowRankUpdated = false; + } + } + + // Regularly update the channels list in certain cases. + if( ( pWO->CurrentLevel == WOL_LEVEL_OFFICIALCHAT || pWO->CurrentLevel == WOL_LEVEL_USERCHAT || + pWO->CurrentLevel == WOL_LEVEL_LOBBIES || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) && + ::timeGetTime() > pWO->dwTimeNextChannelUpdate ) + { + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ); + break; + case WOL_LEVEL_USERCHAT: + pWO->UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ); + break; + case WOL_LEVEL_LOBBIES: // Overkill in this case to update so often... + pWO->UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ); + break; + case WOL_LEVEL_INLOBBY: + pWO->UpdateChannels( GAME_TYPE, CHANNELFILTER_LOCALLOBBYGAMES, true ); + break; + } + pWO->dwTimeNextChannelUpdate = ::timeGetTime() + CHANNELUPDATEWAIT; + } + +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if( bFirsttime && !pWO->bChatShownBefore ) + { + WWMessageBox().Process( TXT_WOL_FINDINGLOBBY, TXT_NONE ); + char szLobbyName[ WOL_CHANNAME_LEN_MAX ]; + if( pWO->GetNameOfBeginningLobby( szLobbyName ) ) + { +// debugprint( "Found lobby to go into: '%s'\n", szLobbyName ); + if( !EnterChannel( pWO, chatlist, NULL, szLobbyName, false ) ) + { + // Could not enter channel for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + } + else + { + // Could not find name of a lobby for some reason. Go to top instead. + pWO->EnterLevel_Top(); + } + pWO->bChatShownBefore = true; + display = REDRAW_ALL; + // Play login sound. + Sound_Effect( WOLSOUND_LOGIN ); + } + bFirsttime = false; + + //..................................................................... + // Refresh display if needed + //..................................................................... + if (display) + { + Hide_Mouse(); + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + + commands->Draw_All(); + + // Draw title bar above channel list. + Draw_Box( d_chanlist_x, d_chanlist_y - 15, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + switch( lesCurrent ) + { + case LES_CHANNELS_EXPANDED: + // Draw users title bar at bottom. + Draw_Box( d_userlist_x, d_userlist_y + d_userlist_h - 16, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + case LES_USERS_EXPANDED: + // Draw users title bar at top. + Draw_Box( d_chanlist_x, d_chanlist_y, d_chanlist_w, 16, BOXSTYLE_BOX, false ); + break; + default: + // Draw users title bar in middle. + Draw_Box( d_userlist_x, d_userlist_y - 15, d_userlist_w, 16, BOXSTYLE_BOX, false ); + break; + } + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + // Mouse button is down. + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + { + pToolTipHitLast->Unshow(); + } + } + + // If anything currently on the controls list is set to redraw, hide tooltip. + if( pToolTipHitLast && pToolTipHitLast->bShowing && commands->Is_List_To_Redraw() ) + { + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + // This hack, used elsewhere in this form, appears to be the standard dodge around GadgetClass::Input's + // tendency to remove any focus the first time it runs for a 'commands' list. + + // ajw - Perhaps I could try doing this every cycle regardless - would avoid stupid non-focused editbox key reactions bug. + if( bHackFocus ) + { + sendedit.Set_Focus(); + sendedit.Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && bLinkInList( commands, pToolTipHit->pGadget ) ) // (Gadget must be in controls list.) + { + if( !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && + !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + else if( pToolTipHit->bIconList && pToolTipHit->bOverDifferentLine() ) + { + pToolTipHit->Unshow(); + pToolTipHit->Show(); + } + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + //..................................................................... + // Process input + //..................................................................... + switch (input) + { + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by sendedit control. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, false ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case KN_LMOUSE: + break; + + case ( BUTTON_EXPANDCHANNELS | KN_BUTTON ): + if( OnExpandChannelList( chanlist, userlist ) ) + { + // Hide userlist. + if( ExpandUserBtn.Get_Next() == &userlist ) + userlist.Remove(); + // Ensure chanlist is visible. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, chanlist.Y + chanlist.Height ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, chanlist.Y + chanlist.Height + 2 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpUnexpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + else + { + // Show userlist. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_EXPANDUSERS | KN_BUTTON ): + if( OnExpandUserList( chanlist, userlist ) ) + { + // Hide chanlist controls. + if( ExpandChanBtn.Get_Next() == &chanlist ) + chanlist.Remove(); + // Ensure userlist is visible. + if( ExpandUserBtn.Get_Next() != &userlist ) + userlist.Add( ExpandUserBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpUnexpand ); + } + else + { + // Show chanlist. + if( ExpandChanBtn.Get_Next() != &chanlist ) + chanlist.Add( ExpandChanBtn ); + // Set buttons. + ExpandChanBtn.Set_Shape( pShpExpand ); + ExpandUserBtn.Set_Shape( pShpExpand ); + } + // Move userlist expand button. + ExpandUserBtn.Set_Position( ExpandUserBtn.X, userlist.Y - 14 ); + TTipUserExpand.Move( ExpandUserBtn.X + 8, ExpandUserBtn.Y - 16 ); + userlistTitle.Set_Position( userlistTitle.X, userlist.Y - 16 + 4 ); + // Move rank buttons. + RankRABtn.Set_Position( RankRABtn.X, ExpandUserBtn.Y ); + RankAMBtn.Set_Position( RankAMBtn.X, ExpandUserBtn.Y ); + TTipRankRA.Move( RankRABtn.X + 8, RankRABtn.Y - 16 ); + TTipRankAM.Move( RankAMBtn.X + 8, RankAMBtn.Y - 16 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_CHANLIST | KN_BUTTON ): + // User clicks on the game list. + //............................................................... + // Handle a double-click + //............................................................... + if( lastclick_timer < 30 && chanlist.Current_Index() == lastclick_idx ) + { + // Doubleclick on channel list. + if( ProcessChannelListSelection( pWO, chatlist, chanlist, lastclick_idx ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + else + { + //............................................................... + // Handle a single-click + //............................................................... + //............................................................ + // If no double-click occurred, set the last-clicked index + // & double-click timer. + //............................................................ + lastclick_timer = 0; + lastclick_idx = chanlist.Current_Index(); + + } + break; + + case ( BUTTON_JOIN | KN_BUTTON ): + // Pressing the join button is exactly like doubleclicking on the selected index in chanlist, except: + // if the first item is selected, ignore, unless we are at the top level + if( pWO->CurrentLevel == WOL_LEVEL_TOP || chanlist.Current_Index() != 0 ) + { + if( ProcessChannelListSelection( pWO, chatlist, chanlist, chanlist.Current_Index() ) ) + { + // Exit the chat dialog, go to game dialog. + rc = 2; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + } + break; + + case ( BUTTON_USERLIST | KN_BUTTON ): + // User clicks on user list. + break; + + case ( BUTTON_CREATE | KN_BUTTON ): + switch( pWO->CurrentLevel ) + { + case WOL_LEVEL_INCHATCHANNEL: +// debugprint( "%s\n", TXT_WOL_CANTCREATEINCHANNEL ); + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + case WOL_LEVEL_INLOBBY: + { + pWO->bPump_In_Call_Back = true; + CREATEGAMEINFO CreateGameInfo = WOL_CreateGame_Dialog( pWO ); + pWO->bPump_In_Call_Back = false; + if( CreateGameInfo.bCreateGame ) + { + if( CreateGameChannel( pWO, CreateGameInfo ) ) + { + rc = 1; + process = false; + } + } + break; + } + case WOL_LEVEL_GAMES: + case WOL_LEVEL_GAMESOFTYPE: + case WOL_LEVEL_LOBBIES: + WOL_PrintMessage( chatlist, TXT_WOL_CANTCREATEHERE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + break; + default: + CreateChatChannel( pWO ); + } + + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + // Because of the way things are set up, this is exactly like selecting the first item in chanlist. + // (Button is disabled when this is not appropriate.) + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): + pWO->dwTimeNextChannelUpdate = ::timeGetTime(); + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( &userlist ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( &userlist, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( &userlist, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( KN_ESC ): +// break; ajw Put back in? + + case ( BUTTON_BACK | KN_BUTTON ): + // Pressing the back button is exactly like doubleclicking on the top item in chanlist, except + // when we're at the top level. + if( pWO->CurrentLevel != WOL_LEVEL_TOP ) + { + ProcessChannelListSelection( pWO, chatlist, chanlist, 0 ); + display = REDRAW_ALL; + break; + } + // Note no break; here. Fall through if at top level. + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + ExitChatChannel( pWO ); + pWO->Logout(); + rc = -1; + process = false; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + pWO->SendMessage( sendedit.Get_Text(), userlist, true ); + // Clear sendedit, reset focus. + szSendBuffer[0] = 0; + sendedit.Set_Focus(); + // Mark for redraw. + //chatlist.Flag_To_Redraw(); + sendedit.Flag_To_Redraw(); + } + else + { + WOL_PrintMessage( chatlist, TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + sendedit.Set_Focus(); + // Mark for redraw. + sendedit.Flag_To_Redraw(); + } + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_RANKRA | KN_BUTTON ): + pWO->bShowRankRA = true; + pWO->bShowRankUpdated = true; + break; + + case ( BUTTON_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = false; + pWO->bShowRankUpdated = true; + break; + + default: + + break; + } + + //..................................................................... + // Service the sounds & score; GameActive must be false at this point, + // so Call_Back() doesn't intercept global messages from me! + //..................................................................... + Call_Back(); + + } // end of while + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + +/* + //------------------------------------------------------------------------ + // Restore screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); + Show_Mouse(); +*/ + + pWO->SaveChat(); + + pWO->ClearListPtrs(); + + return rc; +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + RemapControlType* pColorRemap = ( iColorRemap == PCOLOR_NONE ? NULL : &ColorRemaps[ iColorRemap ] ); + WOL_PrintMessage( ILTarget, szText, pColorRemap ); +} + +//*********************************************************************************************** +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ) +{ + ILTarget.Add_Item( szText, NULL, NULL, ICON_SHAPE, NULL, NULL, pColorRemap ); + if( !ILTarget.bScrollBeingDragged() ) + ILTarget.Show_Last_Item(); +} + +//*********************************************************************************************** +bool OnExpandChannelList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand channel list button was pressed. + // Returns true if userlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + ResizeChannelList( chanlist, true ); + lesCurrent = LES_CHANNELS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +bool OnExpandUserList( IconListClass& chanlist, IconListClass& userlist ) +{ + // Expand user list button was pressed. + // Returns true if chanlist controls are to be hidden, false if they are to be shown. + switch( lesCurrent ) + { + case LES_NORMAL: + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_CHANNELS_EXPANDED: + ResizeChannelList( chanlist, false ); + ResizeUserList( userlist, true ); + lesCurrent = LES_USERS_EXPANDED; + break; + case LES_USERS_EXPANDED: + ResizeUserList( userlist, false ); + lesCurrent = LES_NORMAL; + return false; + } + return true; +} + +//*********************************************************************************************** +void ResizeChannelList( IconListClass& chanlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_userlist_y + d_userlist_h - 15 - d_chanlist_y ); + else + chanlist.Resize( d_chanlist_x, d_chanlist_y, d_chanlist_w, d_chanlist_h ); +} + +//*********************************************************************************************** +void ResizeUserList( IconListClass& userlist, bool bExpand ) +{ + // If bExpand, makes list big, else normal size. + if( bExpand ) + userlist.Resize( d_userlist_x, d_chanlist_y + 15, d_userlist_w, d_userlist_y + d_userlist_h - ( d_chanlist_y + 15 ) ); + else + userlist.Resize( d_userlist_x, d_userlist_y, d_userlist_w, d_userlist_h ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex, bool bGame ) +{ + // Enter the channel specified in chanlist at iIndex. + // Called to enter chat channels, "lobbies", and game channels. + + // We've stored the channel pointer in the hidden extra data field. + // ( Be careful about calling RAChatEventSink::DeleteChannelList()! ) + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + return EnterChannel( pWO, chatlist, pChannel, NULL, bGame ); +} + +//*********************************************************************************************** +bool EnterChannel( WolapiObject* pWO, IconListClass& chatlist, Channel* pChannel, char* szChannelName, bool bGame ) +{ + // Called to cause the user to enter a channel (chat, lobby, or game). + // If pChannel is NULL, szChannelName will be used. + + Channel ChannelWhenNameOnly; + if( !pChannel ) + { + if( !szChannelName ) + { +// debugprint( "pChannel and szChannelName null in EnterChannel" ); + pWO->bSelfDestruct = true; + return false; + } + pChannel = &ChannelWhenNameOnly; + strcpy( (char*)pChannel->name, szChannelName ); + } + + if( bGame && pChannel->currentUsers >= pChannel->maxUsers ) // Pre-emptive fullness check. + { + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + + if( bGame ) + { + // It is possible to enter a game channel while currently in a chat channel. (A lobby, presumably.) + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel on join failed" ); + pWO->bSelfDestruct = true; + return false; + } + } + +/* The following doesn't work because the needpw field is not currently being set in wolapi for chat channels. + Instead, we wait for a fail on join, then present this dialog and try again. + if( pChannel->needpw ) + { + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); + if( !pEditDlg->Show() || !*pEditDlg->szEdit ) + return false; + + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + } +*/ + bool bKeepTrying = true; + + // Set password automatically for our lobbies, if trying to join one. + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby != -1 ) + strcpy( (char*)pChannel->key, LOBBYPASSWORD ); + + char szSuccessfulPassword[ WOL_PASSWORD_LEN + 5 ]; + *szSuccessfulPassword = 0; + + HRESULT hRes; + while( bKeepTrying ) + { + hRes = pWO->ChannelJoin( pChannel ); + switch( hRes ) + { + case CHAT_E_BADCHANNELPASSWORD: + { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. +#ifdef ENGLISH + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else +#ifdef GERMAN + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 400, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#else + SimpleEditDlgClass* pEditDlg = new SimpleEditDlgClass( 500, TXT_WOL_JOINPRIVATETITLE, TXT_WOL_JOINPRIVATEPROMPT, + WOL_CHANKEY_LEN_MAX ); +#endif +#endif + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) != 0 || !*pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + delete pEditDlg; + bKeepTrying = false; + break; + } + pWO->bPump_In_Call_Back = false; + strcpy( (char*)pChannel->key, pEditDlg->szEdit ); + strcpy( szSuccessfulPassword, pEditDlg->szEdit ); + delete pEditDlg; + break; + } + case CHAT_E_TIMEOUT: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_CHANNELDOESNOTEXIST: + pWO->bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_CHANNELGONE ); + pWO->bPump_In_Call_Back = false; + bKeepTrying = false; + break; + case CHAT_E_BANNED: + WOL_PrintMessage( chatlist, TXT_WOL_YOUREBANNED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case CHAT_E_CHANNELFULL: + WOL_PrintMessage( chatlist, TXT_WOL_CHANNELFULL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + bKeepTrying = false; + break; + case E_FAIL: + pWO->GenericErrorMessage(); + bKeepTrying = false; + break; + case S_OK: + bKeepTrying = false; + break; + } + } + + if( !bGame ) + { + if( hRes == S_OK ) + return pWO->OnEnteringChatChannel( (char*)pChannel->name, false, iLobby ); + else + return false; + } + else + { + if( hRes == S_OK ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; + // Return later to the lobby of the channel creator - which was saved in the channel itself. + pWO->iLobbyReturnAfterGame = pChannel->reserved & 0x00FFFFFF; + if( pWO->iLobbyReturnAfterGame == 0x00FFFFFF ) + pWO->iLobbyReturnAfterGame = -1; + CREATEGAMEINFO CreateGameInfo; + // Not all of these values are currently used during setup. + CreateGameInfo.bCreateGame = false; + CreateGameInfo.iPlayerMax = pChannel->maxUsers; + CreateGameInfo.bTournament = pChannel->tournament; + if( *szSuccessfulPassword ) + { + CreateGameInfo.bPrivate = true; + strcpy( CreateGameInfo.szPassword, szSuccessfulPassword ); + } + else + { + CreateGameInfo.bPrivate = false; + *CreateGameInfo.szPassword = 0; + } + CreateGameInfo.GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + return pWO->OnEnteringGameChannel( (char*)pChannel->name, false, CreateGameInfo ); + } + else + { + pWO->OnFailedToEnterGameChannel(); + *pWO->szChannelReturnOnGameEnterFail = 0; + return false; + } + } +} + +//*********************************************************************************************** +bool ExitChatChannel( WolapiObject* pWO ) +{ + // Called to cause user to leave current chat or lobby channel. + +// debugprint( "ExitChatChannel\n" ); + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingChatChannel(); + + return true; +} + +//*********************************************************************************************** +//void CreateChatChannel( WolapiObject* pWO, bool bPrivate ) +void CreateChatChannel( WolapiObject* pWO ) +{ + SimpleEditDlgClass* pEditDlg; +/* if( !bPrivate ) + { + pEditDlg = new SimpleEditDlgClass( 300, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX ); + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + else +*/ { + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pEditDlg = new SimpleEditDlgClass( 350, TXT_WOL_CREATECHANNELTITLE, TXT_WOL_CREATECHANNELPROMPT, + WOL_CHANNAME_LEN_MAX, TXT_WOL_OPTIONALPASSPROMPT, WOL_CHANKEY_LEN_MAX ); + pWO->bPump_In_Call_Back = true; + if( strcmp( pEditDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pEditDlg->szEdit ) + { + pWO->bPump_In_Call_Back = false; + if( *pEditDlg->szEdit2 ) + { + if( pWO->ChannelCreate( pEditDlg->szEdit, pEditDlg->szEdit2 ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + else + { + // Create public channel. + if( pWO->ChannelCreate( pEditDlg->szEdit, NULL ) ) + pWO->OnEnteringChatChannel( pEditDlg->szEdit, true, -1 ); + } + } + pWO->bPump_In_Call_Back = false; + } + + delete pEditDlg; +} + +//*********************************************************************************************** +bool CreateGameChannel( WolapiObject* pWO, const CREATEGAMEINFO& cgi ) +{ + char szNewChannelName[ WOL_CHANNAME_LEN_MAX ]; + sprintf( szNewChannelName, "%s's_game", pWO->szMyName ); + + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + if( !pWO->ExitChatChannelForGameChannel() ) + { + *pWO->szChannelReturnOnGameEnterFail = 0; +// debugprint( "ExitChatChannelForGameChannel in CreateGameChannel() error" ); + pWO->bSelfDestruct = true; + return false; + } + + const char* szKey; + if( *cgi.szPassword ) + szKey = cgi.szPassword; + else + szKey = NULL; + + if( pWO->ChannelCreate( szNewChannelName, szKey, true, cgi.iPlayerMax, cgi.bTournament, pWO->iLobbyLast, cgi.GameKind ) ) + pWO->OnEnteringGameChannel( szNewChannelName, true, cgi ); + else + { + pWO->OnFailedToEnterGameChannel(); +// debugprint( "CreateGameChannel fail" ); + return false; + } + *pWO->szChannelReturnOnGameEnterFail = 0; + + return true; +} + +//*********************************************************************************************** +bool ProcessChannelListSelection( WolapiObject* pWO, IconListClass& chatlist, IconListClass& chanlist, int iIndex ) +{ + // Takes whatever action necessary due to user selecting iIndex from chanlist. + // Returns true if user selected to enter a game channel, else false. + if( iIndex < 0 ) + return false; + +// debugprint( "iIndex %i\n", iIndex ); + + const char* szChannelType = chanlist.Get_Item_ExtraDataString( iIndex ); + if( szChannelType ) + { +//debugprint( "szChannelType %s\n", szChannelType ); + if( strcmp( szChannelType, CHANNELTYPE_OFFICIALCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_OfficialChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_USERCHAT ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_UserChat(); + } + else if( strcmp( szChannelType, CHANNELTYPE_TOP ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Top(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Games(); + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMESOFTYPE ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Now not possible. + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + void* pExtraData = chanlist.Get_Item_ExtraDataPtr( iIndex ); + pWO->EnterLevel_GamesOfType( (WOL_GAMETYPEINFO*)pExtraData ); + } + else if( strcmp( szChannelType, CHANNELTYPE_CHATCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_GAMECHANNEL ) == 0 ) + { + // User attempting to join game channel. + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Trying to jump from channel to channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Check if local user is allowed to join GameKind. + Channel* pChannel = (Channel*)chanlist.Get_Item_ExtraDataPtr( iIndex ); + if( pChannel->type == GAME_TYPE ) + { + // It is a game of our type, at least. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + break; + case CREATEGAMEINFO::CSGAME: + if( !Is_Counterstrike_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDCOUNTERSTRIKE, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + case CREATEGAMEINFO::AMGAME: + if( !Is_Aftermath_Installed() ) + { + WOL_PrintMessage( chatlist, TXT_WOL_NEEDAFTERMATH, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + Sound_Effect( WOLSOUND_ERROR ); + return false; + } + // Join the game channel. + if( EnterChannel( pWO, chatlist, chanlist, iIndex, true ) ) + { + // Exit the chat dialog, go to game dialog. + return true; + } + } + else + { + // User doubleclicked on a game that is of a different type. + // Offer to take them to a web page regarding the game type. + pWO->DoGameAdvertising( pChannel ); + } + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBIES ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL ) + { + // Not currently possible. +// debugprint( "Chat channel to lobbies level?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + if( pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + if( !ExitChatChannel( pWO ) ) + { + pWO->bSelfDestruct = true; + return false; + } + } + pWO->EnterLevel_Lobbies(); + } + else if( strcmp( szChannelType, CHANNELTYPE_LOBBYCHANNEL ) == 0 ) + { + if( pWO->CurrentLevel == WOL_LEVEL_INCHATCHANNEL || + pWO->CurrentLevel == WOL_LEVEL_INLOBBY ) + { + // Not currently possible. +// debugprint( "Chat or lobby channel to lobby channel?!\n" ); + pWO->bSelfDestruct = true; + return false; + } + // Join the lobby chat channel. + EnterChannel( pWO, chatlist, chanlist, iIndex, false ); // Can fail. + } + else if( strcmp( szChannelType, CHANNELTYPE_LOADING ) == 0 ) + { + // User clicked on the channel list loading notification - do nothing. + } + else + { +// debugprint( "Item selected in channel list unidentifiable from extradata field\n" ); + pWO->bSelfDestruct = true; + return false; + } + + } + return false; +} + +//*********************************************************************************************** +bool bLinkInList( const LinkClass* pListHead, const LinkClass* pLinkToFind ) +{ + const LinkClass* pLink = pListHead; + while( pLink ) + { + if( pLink == pLinkToFind ) + return true; + pLink = pLink->Get_Next(); + } + return false; +} + +#endif diff --git a/code/redalert/wol_dnld.cpp b/code/redalert/wol_dnld.cpp new file mode 100644 index 0000000..8f53f8f --- /dev/null +++ b/code/redalert/wol_dnld.cpp @@ -0,0 +1,260 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Dnld.cpp - WW online patch download dialog. +// ajw 10/12/98 + +#include "function.h" +#include "WolapiOb.h" +#include "WolStrng.h" + +//*********************************************************************************************** +bool WOL_Download_Dialog( IDownload* pDownload, RADownloadEventSink* pDownloadSink, const char* szTitle ) +{ + // This dialog is presented for each file that is to be downloaded during a WOLAPI patch. + + bool bReturn = true; + DWORD dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + + /* + ** Dialog & button dimensions + */ + int d_dialog_w = 200*RESFACTOR; // dialog width + int d_dialog_h = 90*RESFACTOR; // dialog height + int d_dialog_x = ((320*RESFACTOR - d_dialog_w) / 2); // dialog x-coord + int d_dialog_y = ((200*RESFACTOR - d_dialog_h) / 2); // centered y-coord + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + int d_margin = 34; + int d_txt6_h = 15; + +#if (GERMAN | FRENCH) + int d_cancel_w = 50*RESFACTOR; +#else + int d_cancel_w = 40*RESFACTOR; +#endif + int d_cancel_h = 9*RESFACTOR; + int d_cancel_x = d_dialog_cx - d_cancel_w / 2; + int d_cancel_y = d_dialog_y + d_dialog_h - 20*RESFACTOR; + + int d_progress_w = 100*RESFACTOR; + int d_progress_h = 10*RESFACTOR; + int d_progress_x = (SeenBuff.Get_Width()/2) - d_progress_w/2; + int d_progress_y = d_dialog_y + 45*RESFACTOR; + +// int width; +// int height; +// char* info_string = (char*)szTitle; + + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), + TBLACK, TPF_CENTER|TPF_6PT_GRAD|TPF_USE_GRAD_PAL|TPF_NOSHADOW ); + +// Format_Window_String( info_string, SeenBuff.Get_Height(), width, height ); + + + /* + ** Button Enumerations + */ + enum { + BUTTON_CANCEL = 100, + BUTTON_PROGRESS + }; + + /* + ** Buttons + */ + TextButtonClass cancelbtn( BUTTON_CANCEL, TXT_CANCEL, TPF_CENTER | TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW, +#if (GERMAN | FRENCH) + d_cancel_x, d_cancel_y ); +#else + d_cancel_x, d_cancel_y, d_cancel_w, d_cancel_h ); +#endif + + GaugeClass progress_meter( BUTTON_PROGRESS, d_progress_x, d_progress_y, d_progress_w, d_progress_h ); + progress_meter.Use_Thumb( 0 ); + + StaticButtonClass StatTitle( 0, szTitle, TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 28, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatStatus( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 49, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatBytes( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 71, d_dialog_w - 2 * d_margin, d_txt6_h ); + StaticButtonClass StatTime( 0, "", TPF_CENTER|TPF_TEXT, d_dialog_x + d_margin, d_dialog_y + 117, d_dialog_w - 2 * d_margin, d_txt6_h ); + + typedef enum { + REDRAW_NONE = 0, + REDRAW_PROGRESS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + + bool process = true; + RedrawType display = REDRAW_ALL; // redraw level + KeyNumType input; + GadgetClass* commands; // button list + + commands = &cancelbtn; + progress_meter.Add_Tail(*commands); + StatTitle.Add_Tail(*commands); + StatBytes.Add_Tail(*commands); + StatTime.Add_Tail(*commands); + StatStatus.Add_Tail(*commands); + + progress_meter.Set_Maximum(100); // Max is 100% + progress_meter.Set_Value(0); // Current is 0% + + do { +#ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } +#endif + + if (display){ + + if (display >= REDRAW_BACKGROUND){ + + Hide_Mouse(); + /* + ** Redraw backgound & dialog box + */ + Load_Title_Page(true); + Set_Palette(CCPalette); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + /* + ** Dialog & Field labels + */ + Draw_Caption (TXT_NONE, d_dialog_x, d_dialog_y, d_dialog_w); + +// Fancy_Text_Print(info_string, d_dialog_cx-width/2, d_dialog_y + 25*RESFACTOR, +// GadgetClass::Get_Color_Scheme(), TBLACK, +// TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_NOSHADOW); + + Show_Mouse(); + + } + + if (display >= REDRAW_BUTTONS){ + + commands->Draw_All(); + + } + + if (display >= REDRAW_PROGRESS){ + progress_meter.Draw_Me(true); + } + + display = REDRAW_NONE; + } + + if (process){ + input = cancelbtn.Input(); + switch (input) { + + /* + ** Cancel. Just return to the main menu + */ + case (KN_ESC): + case (BUTTON_CANCEL | KN_BUTTON): + pDownload->Abort(); + process = false; + bReturn = false; + break; + } + } + + if( ::timeGetTime() > dwTimeNextPump ) + { + pDownload->PumpMessages(); + if( pDownloadSink->bFlagEnd ) + { + pDownloadSink->bFlagEnd = false; + process = false; + break; + } + if( pDownloadSink->bFlagError ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADERROR ); + pDownloadSink->bFlagError = false; + process = false; + bReturn = false; + break; + } + if( pDownloadSink->bFlagProgressUpdate ) + { + pDownloadSink->bFlagProgressUpdate = false; + progress_meter.Set_Value( ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + char szText[200]; + sprintf( szText, TXT_WOL_DOWNLOADBYTES, pDownloadSink->iBytesRead, pDownloadSink->iTotalSize, + ( pDownloadSink->iBytesRead * 100 ) / pDownloadSink->iTotalSize ); + StatBytes.Set_Text( szText ); + sprintf( szText, TXT_WOL_DOWNLOADTIME, pDownloadSink->iTimeLeft / 60, pDownloadSink->iTimeLeft % 60 ); + StatTime.Set_Text( szText ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagStatusUpdate ) + { + pDownloadSink->bFlagStatusUpdate = false; + switch( pDownloadSink->iStatus ) + { + case DOWNLOADSTATUS_CONNECTING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADCONNECTING ); + break; + + case DOWNLOADSTATUS_FINDINGFILE: + StatStatus.Set_Text( TXT_WOL_DOWNLOADLOCATING ); + break; + + case DOWNLOADSTATUS_DOWNLOADING: + StatStatus.Set_Text( TXT_WOL_DOWNLOADDOWNLOADING ); + break; + + default: +// debugprint( "Unknown status update!\n" ); + break; + } + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + if( pDownloadSink->bFlagQueryResume ) + { + if( pDownloadSink->bResumed ) + { + char szTitleNew[200]; + sprintf( szTitleNew, TXT_WOL_DOWNLOADRESUMED, szTitle ); + StatTitle.Set_Text( szTitleNew ); + if( display < REDRAW_BUTTONS ) display = REDRAW_BUTTONS; + } + } + + dwTimeNextPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + // Invoke game callback + Call_Back(); + + } while ( process ); + + return bReturn; +} + +#endif diff --git a/code/redalert/wol_gsup.cpp b/code/redalert/wol_gsup.cpp new file mode 100644 index 0000000..8b655c4 --- /dev/null +++ b/code/redalert/wol_gsup.cpp @@ -0,0 +1,3500 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#ifdef WOLAPI_INTEGRATION // Now implies also WINSOCK_IPX, WIN32, and FIXIT_CSII must be true + +#include "wol_gsup.h" +#include "function.h" +#include "IconList.h" +#include +#include "WolStrng.h" +#include "wsproto.h" +#include "BigCheck.h" +#include "ToolTip.h" + +extern char const* EngMisStr[]; + +bool Is_Mission_126x126 (char *file_name); +bool Is_Mission_Aftermath (char *file_name); +bool Is_Mission_Counterstrike (char *file_name); + +bool Force_Scenario_Available( const char* szName ); + +int ScenarioIndex_From_Filename( const char* szScenarioFilename ); + +bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +#include "WolDebug.h" + +#define PARAMREFRESHWAIT 2000 + +#define PING_AND_DISPLAY_WAIT 5000 + +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void CC_Draw_DIB( const char* pDIB, int xDest, int yDest, int iWidth, WindowNumberType window ); + +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ); +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ); +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ); + +extern unsigned long PlanetWestwoodStartTime; //Time that game was started + +extern bool cancel_current_msgbox; +extern bool disable_current_msgbox; + +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ); + +//*********************************************************************************************** +/* Game startup logic: +When a guest joins the channel, the host sends all current setup info. +When a guest changes house, he tells the host, who informs everyone. A house change can't be denied. +When a guest changes color, he asks the host for the new color, and assumes the request will go through. If it doesn't, the +host sends the guest a messages setting him back to the old color, otherwise, everyone gets the new color for the guest. +When the host changes a game param, the change will be noticed within PARAMREFRESHWAIT milliseconds and then transmitted to +all guests. +All player specific information is stored within the players list (IconListClass), and nowhere else. Though this storage +method got a bit inelegant, it gets the job done. +All "informs" (messages sent from host to guest) except color changes (WOL_GAMEOPT_INFCOLOR) and player "accept" status +changes (WOL_GAMEOPT_INFACCEPT) are assigned a ID, which +increments on each send. Color informs are: a) not a condition for removing a guest's "accept" status, and b) sent out to +individual guests in certain cases, and would thus complicate the ID tracking scheme. +When a guest "accepts" the game setup (with a WOL_GAMEOPT_REQACCEPT), the latest received ID is included in the message. +If the host receives a WOL_GAMEOPT_REQACCEPT with an out-of-date ID, the accept is ignored. Though the guest sets himself to +"accepted" when he sends the WOL_GAMEOPT_REQACCEPT, if it gets ignored by the host it is because a new inform is already on its +way, which will reset the guest's own status to "not accepted" when it arrives. +Host clears all accepteds when params are changed, player joins or leaves, or house change message arrives. Though a +WOL_GAMEOPT_INFACCEPT is sent out when a guest accepts, there is no corresponding "unaccept all" message sent out at this point, +as all guests will naturally clear their recorded accept status for everyone when the change arrives there. +Guest clears own accepted when an inform from the server other than color change arrives, player joins or leaves, or own +house is directly changed. +When all players are "accepted", and the host says "start", a WOL_GAMEOPT_INFSTART is sent to all guests and the host goes into +"waiting to start" mode. If a house change or other arrives in this phase, host clears waiting mode and (naturally) marks guests +unaccepted and sends out the house change to the guests. (The same applies to player joins/leaves.) +While in this mode, the host user is locked out of making any changes to game params. +If a change does come in during this phase, a WOL_GAMEOPT_INFCANCELSTART is sent to the guests, and the waiting mode cancelled. +When a guest receives WOL_GAMEOPT_INFSTART: If he does not see himself as accepted, it is because a change has just been sent +to the host, or some other event occurred that the host will learn about, so the guest can simply ignore the WOL_GAMEOPT_INFSTART. +If the guest does see himself as accepted when WOL_GAMEOPT_INFSTART arrives, he responds with WOL_GAMEOPT_REQSTART and goes into +"waiting to start" mode. When in this mode, the user is locked out of making any changes. +Note that the host is not really asking for any sort of "confirmation that it's ok" to start from the guest here. (Hence no +"cancel" response is possible.) It's really just a way of telling the guests not to make any more changes to the setup; it's +like we're making sure we've "flushed the queue" of game changes. +If a guest receives a WOL_GAMEOPT_INFCANCELSTART, he cancels out of "waiting to start" mode. Note that while in the waiting +mode, normal processing of messages is allowed to take place - the user is simply blocked from doing anything. +Presumably (hopefully) this phase will last less than a second. +When the host receives a WOL_GAMEOPT_REQSTART from everyone, he tells everyone to start with a WOL_GAMEOPT_INFGO. +Because there is a chance of color changes being out of sync, all player colors are sent to all guests at this point. +There is no chance of other data being out of sync. +*/ + +//-------------------------------------------------------------------------- +// Pieced together from Net_New_Dialog() and Net_Join_Dialog(). +//-------------------------------------------------------------------------- +//*********************************************************************************************** +WOL_GameSetupDialog::WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ) : pWO( pWO ), bHost( bHost ), + HousePrevious( HOUSE_NONE ), + pILPlayers( NULL ), + pILScens( NULL ), + pILDisc( NULL ), + pEditSend( NULL ), + pGaugeCount( NULL ), + pGaugeLevel( NULL ), + pGaugeCredits( NULL ), + pGaugeAIPlayers( NULL ), + pCheckListOptions( NULL ), +// pTextBtnOk( NULL ), + pTextBtnCancel( NULL ), + pTextBtnAcceptStart( NULL ), + pTextBtnAction( NULL ), + pStaticDescrip( NULL ), + pStaticUnit( NULL ), + pStaticLevel( NULL ), + pStaticCredits( NULL ), + pStaticAIPlayers( NULL ), + pDropListHouse( NULL ), + pCheckAftermathUnits( NULL ), + nHostLastParamID( 0 ), + nGuestLastParamID( 0 ), + bWaitingToStart( false ), + bParamsReceived( false ), + bHostSayGo( false ), + bExitForGameTrigger( false ), + pToolTipHead( NULL ), + pToolTipHitLast( NULL ), + pTTipAcceptStart( NULL ), + pTTipCancel( NULL ), + pTTipAction( NULL ), + ScenKindCurrent( SCENARIO_UNINITIALIZED ), + pShpBtnScenarioRA( NULL ), + pShpBtnScenarioCS( NULL ), + pShpBtnScenarioAM( NULL ), + pShpBtnScenarioUser( NULL ), + bLeaveDueToRulesMismatchTrigger( false ), + bHostWaitingForGoTrigger( false ) +{ + *szSendBuffer = 0; + *szHouseBuffer = 0; + memset( &GParamsLastSent, 0, sizeof( GAMEPARAMS ) ); + *szNameOfHostWhoJustBailedOnUs = 0; + *szTriggerGameStartInfo = 0; +} + +//*********************************************************************************************** +WOL_GameSetupDialog::~WOL_GameSetupDialog() +{ + delete pILPlayers; + delete pILScens; + delete pILDisc; + delete pEditSend; + delete pGaugeCount; + delete pGaugeLevel; + delete pGaugeCredits; + delete pGaugeAIPlayers; + delete pCheckListOptions; +// delete pTextBtnOk; + delete pTextBtnCancel; + delete pTextBtnAcceptStart; + delete pTextBtnAction; +// delete pStaticDescrip; + delete pStaticUnit; + delete pStaticLevel; + delete pStaticCredits; + delete pStaticAIPlayers; + delete pDropListHouse; + delete pCheckAftermathUnits; + delete pTTipAcceptStart; + delete pTTipCancel; + delete pTTipAction; + delete pShpBtnScenarioRA; + delete pShpBtnScenarioCS; + delete pShpBtnScenarioAM; + delete pShpBtnScenarioUser; +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Run() +{ + Initialize(); + return Show(); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::Initialize() +{ + //------------------------------------------------------------------------ + // Dialog & button dimensions + //------------------------------------------------------------------------ + d_dialog_w = 640; // dialog width + d_dialog_h = 400; // dialog height + d_dialog_x = 0; + d_dialog_y = 0; + d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // center x-coord + + d_txt6_h = 6*RESFACTOR+1; // ht of 6-pt text + int d_text_h = 12; + d_margin1 = 34; // large margin + + d_color_w = 10 *RESFACTOR; + d_color_h = 9 *RESFACTOR; + d_color_x = 294; //54; //d_dialog_x + ((d_dialog_w / 4) * 3) - (d_color_w * 3); + d_color_y = 89; //142; //d_house_y; + + d_house_w = 60 *RESFACTOR; + d_house_h = (8 * 5 *RESFACTOR); + d_house_x = 466; //65; //d_color_x; //d_dialog_cx - (d_house_w / 2); + d_house_y = d_color_y; // + 36; //d_dialog_y + ( 7 * RESFACTOR ) + d_txt6_h + (2*RESFACTOR); + + if( bHost ) + d_disc_w = 365; //d_dialog_w - (d_margin1 * 2) - 20*RESFACTOR; + else + d_disc_w = d_dialog_w - ( d_margin1 * 2 ); + d_disc_x = d_dialog_x + d_margin1; + d_disc_y = 205; //d_dialog_y + d_dialog_h - ( 65 + d_disc_h ); + d_disc_h = 337 - d_disc_y; + + d_playerlist_w = 124*RESFACTOR; + d_playerlist_h = d_text_h * 4 + 4; + d_playerlist_x = d_dialog_x + d_margin1; + d_playerlist_y = 75; //d_dialog_y + d_margin1 + d_txt6_h + 3*RESFACTOR + 18 * RESFACTOR; + + int d_tab_h = 19; + + d_scenariolist_w = 200; +// d_scenariolist_h = (4 * d_txt6_h) + 3*RESFACTOR; // 4 rows high + d_scenariolist_x = d_dialog_x + d_dialog_w - d_margin1 - d_scenariolist_w; + d_scenariolist_y = d_disc_y + d_tab_h; + d_scenariolist_h = d_disc_y + d_disc_h - d_scenariolist_y; + + d_gamekind_w = 182; + d_gamekind_h = 30; + d_gamekind_x = 52; // 370; + d_gamekind_y = 153; // d_playerlist_y + d_text_h + 3; // + d_playerlist_h - d_text_h; + + d_count_w = 25*RESFACTOR; + d_count_h = d_txt6_h; + d_count_x = 310; + d_count_y = 138; //d_playerlist_y + d_playerlist_h + d_margin2 + 9*RESFACTOR; + + d_level_w = 25*RESFACTOR; + d_level_h = d_txt6_h; + d_level_x = d_count_x; + d_level_y = d_count_y + d_count_h; + + d_credits_w = 25*RESFACTOR; + d_credits_h = d_txt6_h; + d_credits_x = d_count_x; + d_credits_y = d_level_y + d_level_h; + + d_aiplayers_w = 25*RESFACTOR; + d_aiplayers_h = d_txt6_h; + d_aiplayers_x = d_count_x; + d_aiplayers_y = d_credits_y + d_credits_h; + +#ifdef GERMAN + d_options_w = 186; +#else + d_options_w = 180; +#endif + d_options_h = ((6 * 6) + 4)*RESFACTOR + 1; + d_options_x = d_dialog_x + d_dialog_w - d_options_w - d_margin1; + d_options_y = 127 - d_txt6_h + 2; + + d_send_w = d_dialog_w - ( d_margin1 * 2 ); + d_send_h = 9*RESFACTOR; + d_send_x = d_dialog_x + d_margin1; + d_send_y = d_disc_y + d_disc_h + 5; + +// d_ok_w = 50*RESFACTOR; +// d_ok_h = 9*RESFACTOR; +// d_ok_x = d_dialog_x + (d_dialog_w / 6) - (d_ok_w / 2); +// d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin1 - 3*RESFACTOR; + + d_cancel_w = 100; + d_cancel_h = 9*RESFACTOR; + d_cancel_x = d_dialog_x + 100; //d_dialog_cx - (d_cancel_w / 2); + d_cancel_y = 365; //d_dialog_y + d_dialog_h - d_cancel_h - d_margin1 - 3*RESFACTOR; + + d_accept_w = 100; + d_accept_h = 9 *RESFACTOR; + d_accept_x = d_dialog_x + 210; + d_accept_y = d_cancel_y; //d_dialog_y + d_dialog_h - 17*RESFACTOR; + + d_action_w = 100; + d_action_h = 9 *RESFACTOR; + d_action_x = d_dialog_x + 500; + d_action_y = d_cancel_y; + +/* int d_load_w = 50*RESFACTOR; + int d_load_h = 9*RESFACTOR; + int d_load_x = d_dialog_x + ((d_dialog_w * 5) / 6) - (d_load_w / 2); + int d_load_y = d_dialog_y + d_dialog_h - d_load_h - d_margin1 - 3*RESFACTOR; +*/ + d_amunits_w = 160; + d_amunits_h = 18; + d_amunits_x = d_dialog_x + (d_dialog_w / 6) - (d_amunits_w / 2); + d_amunits_y = 365; + + ToolTipClass* pToolTip = pToolTipHead = pWO->pTTipDiscon; + pToolTip->next = pWO->pTTipLeave; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipRefresh; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipSquelch; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipBan; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipKick; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipFindpage; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipOptions; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipLadder; + pToolTip = pToolTip->next; + pToolTip->next = pWO->pTTipHelp; + + pILPlayers = new IconListClass( BUTTON_PLAYERLIST, d_playerlist_x, d_playerlist_y, d_playerlist_w, d_playerlist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 2 ); +// ListClass scenariolist(BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP")); + pILScens = new IconListClass( BUTTON_SCENARIOLIST, d_scenariolist_x, d_scenariolist_y, d_scenariolist_w, d_scenariolist_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1 ); + pILDisc = new IconListClass( BUTTON_DISCLIST, d_disc_x, d_disc_y, d_disc_w, d_disc_h, TPF_TYPE, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 0, 300 ); + + pEditSend = new EditClass( BUTTON_SENDEDIT, szSendBuffer, MAXCHATSENDLENGTH, TPF_TEXT, d_send_x, d_send_y, d_send_w, d_send_h ); + +// TextButtonClass rejectbtn( BUTTON_REJECT, TXT_REJECT, TPF_BUTTON, d_reject_x, d_reject_y ); + pGaugeCount = new GaugeClass( BUTTON_COUNT, d_count_x, d_count_y, d_count_w, d_count_h ); + pGaugeLevel = new GaugeClass( BUTTON_LEVEL, d_level_x, d_level_y, d_level_w, d_level_h ); + pGaugeCredits = new GaugeClass( BUTTON_CREDITS, d_credits_x, d_credits_y, d_credits_w, d_credits_h ); + pGaugeAIPlayers = new GaugeClass( BUTTON_AIPLAYERS, d_aiplayers_x, d_aiplayers_y, d_aiplayers_w, d_aiplayers_h ); + pCheckListOptions = new CheckListClass( BUTTON_PARAMS, d_options_x, d_options_y, d_options_w, d_options_h, TPF_TEXT, MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP") ); +// pTextBtnOk = new TextButtonClass( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, 60*RESFACTOR ); +// TextButtonClass loadbtn(BUTTON_LOAD, TXT_LOAD_BUTTON, TPF_BUTTON, d_load_x, d_load_y, 60*RESFACTOR); + pTextBtnCancel = new TextButtonClass( BUTTON_CANCEL, TXT_WOL_CANCELGAME, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + pTTipCancel = new ToolTipClass( pTextBtnCancel, TXT_WOL_TTIP_CANCELGAME, d_cancel_x + d_cancel_w/2, d_cancel_y - 6 ); + + if( bHost ) + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_STARTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_START, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + else + { + pTextBtnAcceptStart = new TextButtonClass( BUTTON_ACCEPTSTART, TXT_WOL_ACCEPTBUTTON, TPF_BUTTON, d_accept_x, d_accept_y, d_accept_w ); + pTTipAcceptStart = new ToolTipClass( pTextBtnAcceptStart, TXT_WOL_TTIP_ACCEPT, d_accept_x + d_accept_w/2, d_accept_y - 6 ); + } + + pTextBtnAction = new TextButtonClass( BUTTON_ACTION, TXT_WOL_ACTION, TPF_BUTTON, d_action_x, d_action_y, d_action_w ); + pTTipAction = new ToolTipClass( pTextBtnAction, TXT_WOL_TTIP_ACTION, d_action_x + d_action_w/2, d_action_y - 6, true ); + + pToolTip = pToolTip->next; + pToolTip->next = pTTipCancel; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAcceptStart; + pToolTip = pToolTip->next; + pToolTip->next = pTTipAction; + pToolTip = pToolTip->next; + pToolTip->next = NULL; + + if( bHost ) + pTextBtnAcceptStart->Disable(); + + // pStaticDescrip is no longer used - can't get the bloody thing to clip text. You'd think a StaticButton control would. +// pStaticDescrip = new StaticButtonClass( 0, "", TPF_TYPE, d_gamekind_x, d_gamekind_y, d_gamekind_w, d_gamekind_h ); + pStaticUnit = new StaticButtonClass( 0, " ", TPF_TEXT, d_count_x + d_count_w + 2*RESFACTOR, d_count_y ); + pStaticLevel = new StaticButtonClass( 0, " ", TPF_TEXT, d_level_x + d_level_w + 2*RESFACTOR, d_level_y ); + pStaticCredits = new StaticButtonClass( 0, " ", TPF_TEXT, d_credits_x + d_credits_w + 2*RESFACTOR, d_credits_y ); + pStaticAIPlayers = new StaticButtonClass( 0, " ", TPF_TEXT, d_aiplayers_x + d_aiplayers_w + 2*RESFACTOR, d_aiplayers_y ); + + Fancy_Text_Print("", 0, 0, 0, 0, TPF_TEXT); + pDropListHouse = new DropListClass( BUTTON_HOUSE, szHouseBuffer, sizeof( szHouseBuffer ), + TPF_TEXT, + d_house_x, d_house_y, d_house_w, d_house_h, + MFCD::Retrieve("BTN-UP.SHP"), + MFCD::Retrieve("BTN-DN.SHP") ); + + // ajw - This checkbox is not used. Could be turned on, though. + pCheckAftermathUnits = new BigCheckBoxClass( BUTTON_AFTERMATHUNITS, d_amunits_x, d_amunits_y, d_amunits_w, d_amunits_h, + "Aftermath units enabled", TPF_TEXT, false ); + + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME ) + { + bAftermathUnits = true; + int current_drive = CCFileClass::Get_CD_Drive(); + int cd_index = Get_CD_Index(current_drive, 1*60); + if( cd_index != 3 && cd_index != 5 ) + { + WOL_PrintMessage( *pILDisc, TXT_WOL_AMDISCNEEDED, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + else + { + bAftermathUnits = false; + pCheckAftermathUnits->Disable(); + } + + bSlowUnitBuildRate = true; + +#define TABSPACING 38 + pShpBtnScenarioRA = new ShapeButtonClass( BUTTON_SCENARIO_RA, MFCD::Retrieve( "tabra.shp" ), d_scenariolist_x, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioCS = new ShapeButtonClass( BUTTON_SCENARIO_CS, MFCD::Retrieve( "tabcs.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + pShpBtnScenarioAM = new ShapeButtonClass( BUTTON_SCENARIO_AM, MFCD::Retrieve( "tabam.shp" ), d_scenariolist_x + TABSPACING, d_scenariolist_y - d_tab_h ); + + int iScenarioUserTabPos; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME || pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + // Place user tab in the third tab position. (It may still not be present.) + iScenarioUserTabPos = d_scenariolist_x + TABSPACING * 2; + else + iScenarioUserTabPos = d_scenariolist_x + TABSPACING; + + pShpBtnScenarioUser = new ShapeButtonClass( BUTTON_SCENARIO_USER, MFCD::Retrieve( "tabus.shp" ), iScenarioUserTabPos, d_scenariolist_y - d_tab_h ); + + // Change draw behavior of tab buttons. + pShpBtnScenarioRA->ReflectButtonState = true; + pShpBtnScenarioCS->ReflectButtonState = true; + pShpBtnScenarioAM->ReflectButtonState = true; + pShpBtnScenarioUser->ReflectButtonState = true; + + if( !bHost ) + { + pILScens->Disable(); + pGaugeCount->Disable(); + pGaugeLevel->Disable(); + pGaugeCredits->Disable(); + pGaugeAIPlayers->Disable(); + pCheckListOptions->Disable(); + pCheckAftermathUnits->Disable(); + } + + pWO->LinkToGameDlg( pILDisc, pILPlayers ); + pWO->pGSupDlg = this; + + dwTimeNextParamRefresh = ::timeGetTime(); +} + +//*********************************************************************************************** +RESULT_WOLGSUP WOL_GameSetupDialog::Show() +{ + // Returns: -1 == return to chat dialog. + + //------------------------------------------------------------------------ + // Dialog variables + //------------------------------------------------------------------------ + bool bHackFocus = true; + + display = REDRAW_ALL; // redraw level + bProcess = true; // process while true + KeyNumType input; + + DWORD timeWaitingToStartTimeout; + + char szScenarioNameDisplay[ 300 ]; + + bool bInformParamChange; + + long ok_timer = 0; // for timing OK button + int i; + int tabs[] = {77*RESFACTOR}; // tabs for player list box + int optiontabs[] = {8*RESFACTOR}; // tabs for option list box + + CCFileClass loadfile ("SAVEGAME.NET"); + int load_game = 0; // 1 = load a saved game + RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + + int cbox_x[] = { + d_color_x, + d_color_x + d_color_w, + d_color_x + (d_color_w * 2), + d_color_x + (d_color_w * 3), + d_color_x + (d_color_w * 4), + d_color_x + (d_color_w * 5), + d_color_x + (d_color_w * 6), + d_color_x + (d_color_w * 7), + }; + + bool bRetractHouseDropDown = false; + + if( !pWO->OnEnteringGameSetup() ) // Gets a userlist setup, among other things. + strcpy( szNameOfHostWhoJustBailedOnUs, TXT_WOL_THEGAMEHOST ); // Will cause immediate exit. + + // If I'm not already listed with a color, give myself a color. + // (I may have already received my assigned color from the game host.) + int iItem = pILPlayers->Find( pWO->szMyName ); // (I must be in the list I just got.) + _ASSERTE( iItem != -1 ); + RemapControlType* pColorRemap = pILPlayers->Get_Item_Color( iItem ); + PlayerColorType Color = PlayerColorTypeOf( pColorRemap ); + +// debugprint( "Starting up, I see myself as color %i\n", Color ); + if( Color == PCOLOR_NONE ) + SetPlayerColor( pWO->szMyName, ColorNextAvailable() ); // Unless I'm host, this will be changed quite immediately, + // but nice to have it be a valid value until then. + + DWORD dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + DWORD dwTimeNextPingDisplay = dwTimeNextPlayerPing + 1500; // Stagger ping and ping display periods. + + //------------------------------------------------------------------------ + // Build the button list + //------------------------------------------------------------------------ + BindControls( true ); + + pILPlayers->Set_Tabs(tabs); + + // If we have not already received a param update from a host, set default param values. + if( !bParamsReceived ) + { + if( !bHost ) + // Accept button disabled until first params arrive. + pTextBtnAcceptStart->Disable(); + + Special.IsCaptureTheFlag = Rule.IsMPCaptureTheFlag; // Ugh. Use of "Special" global. + if( bHost ) { + Session.Options.Credits = Rule.MPDefaultMoney; // init credits & credit buffer + Session.Options.Bases = Rule.IsMPBasesOn; // init scenario parameters + Session.Options.Tiberium = Rule.IsMPTiberiumGrow; + Session.Options.Goodies = Rule.IsMPCrates; + Session.Options.AIPlayers = 0; + Session.Options.UnitCount = (SessionClass::CountMax[Session.Options.Bases] + SessionClass::CountMin[Session.Options.Bases]) / 2; + //first_time = 0; + } + //------------------------------------------------------------------------ + // Init other scenario parameters + //------------------------------------------------------------------------ + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + if( bHost ) + { + //------------------------------------------------------------------------ + // Set up array of lists of available scenarios. + //------------------------------------------------------------------------ + for( i = 0; i < Session.Scenarios.Count(); i++ ) + { + // Reworking of the loop previously used for language translation. (What a hack I have inherited...) + MultiMission* pMMission = Session.Scenarios[ i ]; + const char* szScenarioNameShow = pMMission->Description(); + #if defined( GERMAN ) || defined( FRENCH ) + for( int j = 0; EngMisStr[j] != NULL; j++ ) + { + if( !strcmp( szScenarioNameShow, EngMisStr[j] ) ) + { + // Found description in translation array that matches mission. + szScenarioNameShow = EngMisStr[ j + 1 ]; + break; + } + } + // (If no match found, defaults to English description.) + #endif + // Place scenario name in a specific scenario list. + if( pMMission->Get_Official() ) + { + if( Is_Mission_Counterstrike( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as CS\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_CS ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_CS ].Add( (void*)i ); + } + else if( Is_Mission_Aftermath( (char*)( Session.Scenarios[i]->Get_Filename() ) ) ) + { + // debugprint( " ---------------- Adding scenario %s as AM\n", szScenarioNameShow ); + // If this is not an Aftermath game channel, we must filter out any AM maps that have + // special AM units on them. + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + !bSpecialAftermathScenario( pMMission->Description() ) ) + { + ar_szScenarios[ SCENARIO_AM ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_AM ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as RA\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_RA ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_RA ].Add( (void*)i ); + } + } + else + { + // debugprint( " ---------------- Adding scenario %s as User\n", szScenarioNameShow ); + ar_szScenarios[ SCENARIO_USER ].Add( szScenarioNameShow ); + ar_szScenIndexes[ SCENARIO_USER ].Add( (void*)i ); + } + } + + // Set default scenario list viewing mode and prepare tab buttons. + /* switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + ScenarioDisplayMode( SCENARIO_RA ); + break; + case CREATEGAMEINFO::CSGAME: + ScenarioDisplayMode( SCENARIO_CS ); + break; + case CREATEGAMEINFO::AMGAME: + ScenarioDisplayMode( SCENARIO_AM ); + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + Fatal( "Illegal GameInfoCurrent value." ); + } + */ + Session.Options.ScenarioIndex = 0; // 1st scenario is selected + + ScenarioDisplayMode( SCENARIO_RA ); // Always start on RedAlert tab. Next line depends on selected item in + // list matching selected scenario. +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + } + + Seed = rand(); + } + + //------------------------------------------------------------------------ + // Init button states + //------------------------------------------------------------------------ + pCheckListOptions->Set_Tabs(optiontabs); + pCheckListOptions->Set_Read_Only(0); + + pCheckListOptions->Add_Item(Text_String(TXT_BASES)); + pCheckListOptions->Add_Item(Text_String(TXT_ORE_SPREADS)); + pCheckListOptions->Add_Item(Text_String(TXT_CRATES)); + pCheckListOptions->Add_Item(Text_String(TXT_CAPTURE_THE_FLAG)); + pCheckListOptions->Add_Item(Text_String(TXT_SHADOW_REGROWS)); + pCheckListOptions->Add_Item(TXT_WOL_SLOWUNITBUILD); + + SetSpecialControlStates(); + + //........................................................................ + // House buttons + //........................................................................ + for (HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++) { + pDropListHouse->Add_Item(Text_String(HouseTypeClass::As_Reference(house).Full_Name())); + } + pDropListHouse->Set_Selected_Index(Session.House - HOUSE_USSR); + pDropListHouse->Set_Read_Only (true); + + + bInformParamChange = false; + +// PlayingAgainstVersion = VerNum.Version_Number(); + + //------------------------------------------------------------------------ + // Init random-number generator, & create a seed to be used for all random + // numbers from here on out + //------------------------------------------------------------------------ + srand(time(NULL)); + + //------------------------------------------------------------------------ + // Init the version-clipping system + //------------------------------------------------------------------------ + VerNum.Init_Clipping(); + + +// Load_Title_Page(true); +// CCPalette.Set(); //GamePalette.Set(); + + // + // Now init the max range of the AI players slider. + // +// pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); +// pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + Sound_Effect( WOLSOUND_ENTERGAME ); + + //------------------------------------------------------------------------ + // Processing loop + //------------------------------------------------------------------------ + while (bProcess) { + #if(SHOW_MONO) + Ipx.Mono_Debug_Print(-1,0); + #endif + + // Check for change of house. Occurs on first loop and when user changes house. + if( HousePrevious != Session.House ) + { + if( bHost ) + { + // Host changed house. + // Do processing as if we'd received a message from a guest. + + // Set house in our own list. + SetPlayerHouse( pWO->szMyName, Session.House ); + // Tell guests. + nHostLastParamID++; + InformAboutPlayerHouse( pWO->szMyName, Session.House, NULL ); + HousePrevious = Session.House; + ClearAllAccepts(); + } + else + { + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else we have not received the user list yet and don't know who the host is. + // We'll keep trying this until we get a host - HousePrevious keeps us triggering until then. + { +// debugprint( "Session.House changed.\n" ); + // Tell host we changed our house. + char szSend[ 20 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQHOUSE, Session.House ); + pWO->SendGameOpt( szSend, pUserHost ); + // Set house in our own list. This is fine because we know that the change must be affirmed by the host. + SetPlayerHouse( pWO->szMyName, Session.House ); + HousePrevious = Session.House; + } + } + } + + // Regularly ping other players to assess latencies. + // Display of ping results is on a parallel timer, slightly behind the ping so that we'll be likely to have + // a fresh average to show. Not too big a deal if OnPings haven't arrived by then (though they should have). + if( ::timeGetTime() > dwTimeNextPlayerPing ) + { + pWO->RequestPlayerPings(); + dwTimeNextPlayerPing = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + if( ::timeGetTime() > dwTimeNextPingDisplay ) + { + pWO->ListChannelUsers(); + dwTimeNextPingDisplay = ::timeGetTime() + PING_AND_DISPLAY_WAIT; + } + + if( pWO->bShowRankUpdated ) + { + pWO->bShowRankUpdated = false; + pWO->ListChannelUsers(); + } + + // Regularly check for incoming messages from wolapi. + if( ::timeGetTime() > pWO->dwTimeNextWolapiPump ) + { + pWO->pChat->PumpMessages(); + pWO->pNetUtil->PumpMessages(); + // Special post-callback processing... + if( pWO->pChatSink->bGotKickedTrigger ) + { + // We got kicked out of the channel. + pWO->OnExitingGameChannel(); + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + // Leave the bGotKickedTrigger flag so we can react to it upon reentering the chat dialog. + //pWO->pChatSink->bGotKickedTrigger = false; + display = REDRAW_ALL; + } + pWO->dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + if( bHostSayGo ) + { +// debugprint( "bHostSayGo trigger\n" ); + // We are the host, now ready to tell everyone to GO and start our game. + HostSaysGo(); +/* Part of old method of game start. + bProcess = false; + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; +// debugprint( "Host about to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; +*/ + } + else if( *szNameOfHostWhoJustBailedOnUs ) // Host left channel - cancel setup. + { +// debugprint( "Guest about to exit game setup dialog because host bailed on us.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_HOSTLEFT; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddHostLeftMessageToSavedChat( szNameOfHostWhoJustBailedOnUs ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( bLeaveDueToRulesMismatchTrigger ) + { +// debugprint( "Guest about to exit game setup dialog because of rules.ini mismatch.\n" ); + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_RULESMISMATCH; // Return to chat. + // Add a message explaining what happened to the saved chat that will be restored in the chat dialog. + pWO->AddMessageToSavedChat( TXT_WOL_RULESMISMATCH ); + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + else if( *szTriggerGameStartInfo ) + { +// debugprint( "About to trigger game start.\n" ); + TriggerGameStart( szTriggerGameStartInfo ); + } + else if( pWO->bSelfDestruct ) + { + if( pWO->pChatSink->bConnected ) + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; // As if the user logged himself out. + } + + if( bExitForGameTrigger ) + { + // We are now exiting to go into a game. + bProcess = false; + if( bHost ) + ResultReturn = RESULT_WOLGSUP_STARTGAMEHOST; + else + ResultReturn = RESULT_WOLGSUP_STARTGAME; +// debugprint( "About to exit game setup dialog for game.\n" ); + if( !ExitGameChannel() ) + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + break; + } + + if( bHost && bWaitingToStart && !bExitForGameTrigger && timeGetTime() > timeWaitingToStartTimeout ) + { + ClearAllAccepts(); // Results in all required steps to cancel game start. + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTTIMEOUT, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + + // Regularly send game param changes if I'm the host. + if( bHost && !bHostSayGo && !bExitForGameTrigger && ::timeGetTime() > dwTimeNextParamRefresh ) + { + if( bParamsUnfresh() ) + { + nHostLastParamID++; + SendParams(); + ClearAllAccepts(); + } + dwTimeNextParamRefresh = ::timeGetTime() + PARAMREFRESHWAIT; + } + + if( !bProcess ) // Avoid redrawing if we're about to bail. + break; + + // + // If we have just received input focus again after running in the background then + // we need to redraw. + // + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = REDRAW_ALL; + } + + //..................................................................... + // Refresh display if needed + //..................................................................... + + if( bRetractHouseDropDown ) //|| pCheckListOptions->Is_To_Redraw() ) + { + bRetractHouseDropDown = false; + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + } +// if( pILDisc->Is_To_Redraw() ) +// { +// pDropListHouse->Flag_To_Redraw(); +// } + + if( display ) + { + Hide_Mouse(); + + /* + ** Collapse the country list if we are going to redraw the game list + */ +// if (pILScens->Is_To_Redraw() && pDropListHouse->IsDropped) { +// pDropListHouse->Collapse(); +// if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; +// } + + //.................................................................. + // Redraw backgound & dialog box + //.................................................................. + if (display >= REDRAW_BACKGROUND) { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( pDropListHouse->IsDropped ) + pDropListHouse->Collapse(); + + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + + //............................................................... + // Dialog & Field labels + //............................................................... + Fancy_Text_Print(TXT_PLAYERS, d_playerlist_x + (d_playerlist_w / 2), d_playerlist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + if( bHost ) + Fancy_Text_Print( TXT_SCENARIOS, d_scenariolist_x + d_scenariolist_w, d_scenariolist_y - 12, scheme, TBLACK, TPF_TYPE | TPF_RIGHT ); +// else +// Fancy_Text_Print( TXT_SCENARIO_COLON, d_scenariolist_x + (d_scenariolist_w / 2), d_scenariolist_y - d_txt6_h, scheme, TBLACK, TPF_TEXT | TPF_CENTER); + Fancy_Text_Print(TXT_COUNT, d_count_x - 2*RESFACTOR, d_count_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_LEVEL, d_level_x - 2*RESFACTOR, d_level_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_CREDITS_COLON, d_credits_x - 2*RESFACTOR, d_credits_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_AI_PLAYERS_COLON, d_aiplayers_x - 2*RESFACTOR, d_aiplayers_y, scheme, TBLACK, TPF_TEXT | TPF_RIGHT); + Fancy_Text_Print(TXT_SIDE_COLON, +// d_house_x + (d_house_w / 2), + d_house_x + ( ( d_house_w + 16 ) / 2 ), + d_house_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + Fancy_Text_Print(TXT_COLOR_COLON, + d_color_x + d_color_w * 4, + d_color_y - d_txt6_h, + scheme, TBLACK, + TPF_CENTER | TPF_TEXT); + + const char* szGameKind; + const char* pDIB; + switch( pWO->GameInfoCurrent.GameKind ) + { + case CREATEGAMEINFO::RAGAME: + szGameKind = TXT_WOL_CG_RAGAME; + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + break; + case CREATEGAMEINFO::CSGAME: + szGameKind = TXT_WOL_CG_CSGAME; + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + break; + case CREATEGAMEINFO::AMGAME: + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + break; + default: +// debugprint( "Illegal GameInfoCurrent value." ); + //Fatal( "Illegal GameInfoCurrent value." ); + szGameKind = TXT_WOL_CG_AMGAME; + pDIB = NULL; + pWO->bSelfDestruct = true; + break; + } + int iGameInfoSpacingY = 14; + int iGameInfoSecondColumnX = 0; //170; + // Game kind. + Fancy_Text_Print( szGameKind, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + // Game kind icon. + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + // "Tournament." + if( pWO->GameInfoCurrent.bTournament ) + { + Fancy_Text_Print( TXT_WOL_CG_TOURNAMENT, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 1, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_TOURNAMENT ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 1 - 2, 100, WINDOW_MAIN ); + } + // "Password: ..." + if( pWO->GameInfoCurrent.bPrivate ) + { + char szPrivatePassword[ 100 ]; + sprintf( szPrivatePassword, TXT_WOL_PRIVATEPASSWORD, pWO->GameInfoCurrent.szPassword ); + Fancy_Text_Print( szPrivatePassword, d_gamekind_x + iGameInfoSecondColumnX, + d_gamekind_y + iGameInfoSpacingY * 2, scheme, TBLACK, TPF_TYPE ); + CC_Draw_DIB( pWO->DibIconInfos[ DIBICON_PRIVATE ].pDIB, d_gamekind_x + iGameInfoSecondColumnX - 16, + d_gamekind_y + iGameInfoSpacingY * 2 - 2, 100, WINDOW_MAIN ); + } + // "Scenario:" - scenario name is drawn separately. + //Fancy_Text_Print( TXT_SCENARIO_COLON, d_gamekind_x, d_gamekind_y - iGameInfoSpacingY, scheme, TBLACK, TPF_TYPE ); + } + + //.................................................................. + // Redraw buttons + //.................................................................. + if (display >= REDRAW_BUTTONS) { + + commands->Draw_All(); + } + + //.................................................................. + // Draw the color boxes + //.................................................................. + if (display >= REDRAW_COLORS ) { + for (i = 0; i < MAX_MPLAYER_COLORS; i++) { + LogicPage->Fill_Rect (cbox_x[i] + 1, d_color_y + 1, + cbox_x[i] + 1 + d_color_w - 2 *RESFACTOR, d_color_y + 1 + d_color_h - 2, + ColorRemaps[i].Box); + + if (i == Session.ColorIdx) { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_DOWN, false); + } else { + Draw_Box(cbox_x[i], d_color_y, d_color_w, d_color_h, BOXSTYLE_RAISED, false); + } + } + } + + //.................................................................. + // Draw the messages: + // - Erase an old message first + // - If we're in a game, print the game options (if they've been + // received) + // - If we've been rejected from a game, print that message + //.................................................................. + if (display >= REDRAW_MESSAGE) { +// Draw_Box(d_disc_x, d_disc_y, d_disc_w, d_disc_h, BOXSTYLE_BOX, true); +// Draw_Box(d_send_x, d_send_y, d_send_w, d_send_h, BOXSTYLE_BOX, true); +// Session.Messages.Draw(); + } + + //.................................................................. + // Update game parameter labels + //.................................................................. + if (display >= REDRAW_PARMS) + { + char txt[80]; + + char* szScenarioDesc; + bool bOfficial; + char* szScenarioFileName; + if( !bHost ) + { + szScenarioDesc = Session.Options.ScenarioDescription; + bOfficial = Session.ScenarioIsOfficial; + szScenarioFileName = Session.ScenarioFileName; + } + else + { + szScenarioDesc = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description(); + bOfficial = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + szScenarioFileName = (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename(); + } + + if( *szScenarioDesc ) + { + // Language translation. + for (int ii = 0; EngMisStr[ii] != NULL; ii++) { + if (!strcmp( szScenarioDesc, EngMisStr[ii]) ) { + #if defined(GERMAN) || defined(FRENCH) + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), EngMisStr[ii+1]); + sprintf(txt, "%s", EngMisStr[ii+1]); + #else + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Session.Options.ScenarioDescription); + sprintf(txt, "%s", szScenarioDesc); + #endif + break; + } + } + if (EngMisStr[ii] == NULL) { + sprintf(txt, "%s", szScenarioDesc); + } +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + + // Show icon for gamekind of scenario. + const char* pDIB; + + if( bOfficial ) + { + if( Is_Mission_Counterstrike( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 1 ].pDIB; + else if( Is_Mission_Aftermath( szScenarioFileName ) ) + pDIB = pWO->OldRAGameTypeInfos[ 2 ].pDIB; + else + pDIB = pWO->OldRAGameTypeInfos[ 0 ].pDIB; + } + else + pDIB = pWO->DibIconInfos[ DIBICON_USER ].pDIB; + + DrawScenarioDescripIcon( pDIB ); + } + else + { + //sprintf(txt, "%s %s", Text_String(TXT_SCENARIO_COLON), Text_String(TXT_NOT_FOUND)); + sprintf(txt, "%s", TXT_WOL_SCENARIONAMEWAIT ); +// pStaticDescrip->Set_Text( txt, false ); + strcpy( szScenarioNameDisplay, txt ); + } + + // Print scenario name. + Conquer_Clip_Text_Print( szScenarioNameDisplay, d_gamekind_x, d_gamekind_y, scheme, TBLACK, TPF_TYPE, d_gamekind_w ); +// pStaticDescrip->Draw_Me(); + + sprintf(txt,"%d",Session.Options.UnitCount); + pStaticUnit->Set_Text(txt); + pStaticUnit->Draw_Me(); + + if (BuildLevel <= MPLAYER_BUILD_LEVEL_MAX) { + sprintf(txt,"%d",BuildLevel); + } else { + sprintf(txt, "**"); + } + pStaticLevel->Set_Text(txt); + pStaticLevel->Draw_Me(); + + sprintf(txt,"%d",Session.Options.Credits); + pStaticCredits->Set_Text(txt); + pStaticCredits->Draw_Me(); + + sprintf(txt,"%d",Session.Options.AIPlayers); + pStaticAIPlayers->Set_Text(txt); + pStaticAIPlayers->Draw_Me(); + } + + Show_Mouse(); + display = REDRAW_NONE; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + //..................................................................... + // Get user input + //..................................................................... + if( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) + { + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + } + + input = commands->Input(); + + if( bHackFocus ) + { + pEditSend->Set_Focus(); + pEditSend->Flag_To_Redraw(); + input = commands->Input(); + bHackFocus = false; + } + + // Tooltips... + if( pToolTipHead ) + { + ToolTipClass* pToolTipHit = pToolTipHead->GetToolTipHit(); + if( pToolTipHit == pToolTipHitLast ) + { + if( pToolTipHit && !pToolTipHit->bShowing && ::timeGetTime() > timeToolTipAppear && !( ( ::GetAsyncKeyState( KN_LMOUSE ) & 0x8000 ) || ( ::GetAsyncKeyState( KN_RMOUSE ) & 0x8000 ) ) ) + { + pToolTipHit->Show(); + } + } + else + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pToolTipHitLast = pToolTipHit; + timeToolTipAppear = ::timeGetTime() + TOOLTIPDELAY; + } + } + + // Yummy special hack. Luv' that UI system. + if( input == 2049 ) // Left mouse released, not on control that captured it. + { + // Redraw the tabs in case they were what were pressed down on. + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); + } + + //..................................................................... + // Process input + //..................................................................... + switch( input ) + { + case KN_LMOUSE: + if( !bWaitingToStart ) + { + // Check for mouse down on a control when player is not host. + if( !bHost ) + { + if ( ( + Get_Mouse_X() >= d_count_x && + Get_Mouse_X() <= d_count_x + d_count_w && + Get_Mouse_Y() >= d_count_y && + Get_Mouse_Y() <= d_aiplayers_y + d_aiplayers_h + ) || ( + Get_Mouse_X() >= d_options_x && + Get_Mouse_X() <= d_options_x + d_options_w && + Get_Mouse_Y() >= d_options_y && + Get_Mouse_Y() <= d_options_y + d_options_h + ) || ( + Get_Mouse_X() >= d_scenariolist_x && + Get_Mouse_X() <= d_scenariolist_x + d_scenariolist_w && + Get_Mouse_Y() >= d_scenariolist_y && + Get_Mouse_Y() <= d_scenariolist_y + d_scenariolist_h + ) ) + { + //Session.Messages.Add_Message(NULL, 0, (char *)Text_String(TXT_ONLY_HOST_CAN_MODIFY), PCOLOR_BROWN, TPF_TEXT, 1200); + WOL_PrintMessage( *pILDisc, Text_String( TXT_ONLY_HOST_CAN_MODIFY ), WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + if (display < REDRAW_MESSAGE) display = REDRAW_MESSAGE; + break; + } + } + + if (Keyboard->MouseQX > cbox_x[0] && + Keyboard->MouseQX < (cbox_x[MAX_MPLAYER_COLORS - 1] + d_color_w) && + Keyboard->MouseQY > d_color_y && + Keyboard->MouseQY < (d_color_y + d_color_h)) + { + Session.PrefColor = (PlayerColorType) + ((Keyboard->MouseQX - cbox_x[0]) / d_color_w); + + // Ensure that no one is using this color (to our knowledge). + if( pILPlayers->FindColor( &ColorRemaps[ Session.PrefColor == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Session.PrefColor ] ) == -1 ) + { + // Show me as the new color. +// debugprint( "Color box pressed - " ); + SetPlayerColor( pWO->szMyName, Session.PrefColor ); + if( bHost ) + { + // Tell all guests about the color change. + InformAboutPlayerColor( pWO->szMyName, Session.PrefColor, NULL ); + } + else + { + RequestPlayerColor( Session.PrefColor ); + } + } + + } + } + break; + + case ( BUTTON_DISCONNECT | KN_BUTTON ): + if( WWMessageBox().Process( TXT_WOL_CONFIRMLOGOUT, TXT_YES, TXT_NO ) == 0 ) + { +// debugprint( "Logging out from gsup.\n" ); + ExitGameChannel(); + pWO->Logout(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_LOGOUT; + } + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_REFRESH | KN_BUTTON ): // Always disabled. + break; + + case ( BUTTON_SQUELCH | KN_BUTTON ): + pWO->DoSquelch( pILPlayers ); + break; + + case ( BUTTON_BAN | KN_BUTTON ): + pWO->DoKick( pILPlayers, true ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_KICK | KN_BUTTON ): + pWO->DoKick( pILPlayers, false ); +// display = REDRAW_ALL; + break; + + case ( BUTTON_FINDPAGE | KN_BUTTON ): + pWO->DoFindPage(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_OPTIONS | KN_BUTTON ): + pWO->DoOptions(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_LADDER | KN_BUTTON ): + pWO->DoLadder(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HELP | KN_BUTTON ): + pWO->DoHelp(); + display = REDRAW_ALL; + bHackFocus = true; + break; + + case ( BUTTON_HOUSE | KN_BUTTON ): + Session.House = (HousesType)( pDropListHouse->Current_Index() + HOUSE_USSR ); +/* + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( pDropListHouse->Current_Item(), "Russia" ) == 0 ) + Session.House = HOUSE_USSR; + else + { + Session.House = HouseTypeClass::From_Name( pDropListHouse->Current_Item() ); // Fails on "Russia". (Thinks "USSR".) + if( Session.House == HOUSE_NONE ) + { +// debugprint( "Couldn't find house from selected '%s'.\n", pDropListHouse->Current_Item() ); + } + } +*/ + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + else + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; // Droplist already got contracted. + + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_AFTERMATHUNITS | KN_BUTTON ): + bAftermathUnits = pCheckAftermathUnits->IsOn; + break; + + case ( BUTTON_SENDEDIT | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, false ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + case ( BUTTON_ACTION | KN_BUTTON ): + // Enter has been pressed - was caught by pEditSend control. + pWO->SendMessage( pEditSend->Get_Text(), *pILPlayers, true ); + // Clear pEditSend, reset focus. + szSendBuffer[0] = 0; + pEditSend->Set_Focus(); + // Mark for redraw. + pEditSend->Flag_To_Redraw(); + break; + + //.................................................................. + // New Scenario selected. + //.................................................................. + case (BUTTON_SCENARIOLIST | KN_BUTTON): + { + if( pILScens->Count() ) + { + int iSelectedScenIndex = (int)pILScens->Get_Item_ExtraDataPtr( pILScens->Current_Index() ); + if( iSelectedScenIndex != Session.Options.ScenarioIndex ) + { + Session.Options.ScenarioIndex = iSelectedScenIndex; + bInformParamChange = true; + if( !pILScens->SetSelectType( 1 ) ) // Hack to deal with ListClass "highlighting nothing" problem. + // SelectType was 0 before call. + pILScens->Flag_To_Redraw(); +// pStaticDescrip->Set_Text( pILScens->Get_Item( pILScens->Current_Index() ), false ); + strcpy( szScenarioNameDisplay, pILScens->Get_Item( pILScens->Current_Index() ) ); + //if (display < REDRAW_PARMS) display = REDRAW_PARMS; + display = REDRAW_ALL; + Sound_Effect(VOC_OPTIONS_CHANGED); + } + } + break; + } + //.................................................................. + // User adjusts max # units + //.................................................................. + case (BUTTON_COUNT | KN_BUTTON): + Session.Options.UnitCount = pGaugeCount->Get_Value() + + SessionClass::CountMin[Session.Options.Bases]; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts build level + //.................................................................. + case (BUTTON_LEVEL | KN_BUTTON): + BuildLevel = pGaugeLevel->Get_Value() + 1; + if (BuildLevel > MPLAYER_BUILD_LEVEL_MAX) // if it's pegged, max it out + BuildLevel = MPLAYER_BUILD_LEVEL_MAX; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User edits the credits value; retransmit new game options + // Round the credits to the nearest 500. + //.................................................................. + case (BUTTON_CREDITS | KN_BUTTON): + Session.Options.Credits = pGaugeCredits->Get_Value(); + Session.Options.Credits = + ((Session.Options.Credits + 250) / 500) * 500; + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // User adjusts # of AI players + //.................................................................. + case (BUTTON_AIPLAYERS | KN_BUTTON): + Session.Options.AIPlayers = pGaugeAIPlayers->Get_Value(); +// if (Session.Options.AIPlayers+Session.Players.Count() > Rule.MaxPlayers) { // if it's pegged, max it out + if (Session.Options.AIPlayers + pWO->GameInfoCurrent.iPlayerMax > Rule.MaxPlayers) { // if it's pegged, max it out + Session.Options.AIPlayers = Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax; + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + } + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + //.................................................................. + // Toggle-able options: + // If 'Bases' gets toggled, we have to change the range of the + // UnitCount slider. + // Also, if Tiberium gets toggled, we have to set the flags + // in SpecialClass. + //.................................................................. + case ( BUTTON_PARAMS | KN_BUTTON ): + bRetractHouseDropDown = true; + if (Special.IsCaptureTheFlag != pCheckListOptions->Is_Checked(3) && !Special.IsCaptureTheFlag) { + pCheckListOptions->Check_Item(0, true); + } + if (Session.Options.Bases != pCheckListOptions->Is_Checked(0)) { + Session.Options.Bases = pCheckListOptions->Is_Checked(0); + if (Session.Options.Bases) { + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[1] - + SessionClass::CountMin[1], + Cardinal_To_Fixed( + SessionClass::CountMax[0]-SessionClass::CountMin[0], + Session.Options.UnitCount-SessionClass::CountMin[0])) + + SessionClass::CountMin[1]; + } else { + pCheckListOptions->Check_Item(3, false); + Session.Options.UnitCount = Fixed_To_Cardinal ( + SessionClass::CountMax[0] - + SessionClass::CountMin[0], + Cardinal_To_Fixed( + SessionClass::CountMax[1]-SessionClass::CountMin[1], + Session.Options.UnitCount - SessionClass::CountMin[1])) + + SessionClass::CountMin[0]; + } + pGaugeCount->Set_Maximum( + SessionClass::CountMax[Session.Options.Bases] - + SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - + SessionClass::CountMin[Session.Options.Bases]); + } + Session.Options.Tiberium = pCheckListOptions->Is_Checked(1); + Special.IsTGrowth = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTGrowth = Session.Options.Tiberium; + Special.IsTSpread = Session.Options.Tiberium; // Ugh. Use of "Special" global. + Rule.IsTSpread = Session.Options.Tiberium; + + Session.Options.Goodies = pCheckListOptions->Is_Checked(2); + Special.IsCaptureTheFlag = pCheckListOptions->Is_Checked(3); // Ugh. Use of "Special" global. + Special.IsShadowGrow = pCheckListOptions->Is_Checked(4); // Ugh. Use of "Special" global. + + bSlowUnitBuildRate = pCheckListOptions->Is_Checked(5); + + bInformParamChange = true; + if (display < REDRAW_PARMS) display = REDRAW_PARMS; + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + + case ( BUTTON_ACCEPTSTART | KN_BUTTON ): // 'Accept' or 'Start Game' button. + if( !bHost ) + { + // Guest wishes to accept game params. + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // Else it's too early to even be thinking about accepting, anyway. + { + // Set ourself as accepted. We want to do this immediately so the user has feedback for pressing button. + // Besides, the host is guaranteed to allow this and set us to "accepted", unless it is simultaneously + // sending us a new update we haven't received yet. If this is the case, we will get that update in a + // second and will set ourselves to unaccepted, which will match the unaccepted state on the server. + // Upshot - We can ignore messages from the server telling us that we, ourself, accepted. + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Disable(); + if( SetPlayerAccepted( pWO->szMyName, true ) ) // Else we didn't find ourself in list! + { +// debugprint( "Sending accept.\n" ); + // Tell host we accept. + char szSend[ 20 ]; + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQACCEPT, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + } + } + } + else + { + // Host says start the game. + // If we have changes just made and not yet sent, don't say start, because we're about to send changes + // that will unaccept everyone. + if( !bParamsUnfresh() ) + { + // Force user to put the correct disk in before proceeding. (Not crucial, but can lead to ugly + // timeouts if the scenario has to be downloaded before game start.) + if( !Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() || + Force_Scenario_Available( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ) ) + { + // Go into "waiting to start" mode, tell guests to, and wait for responses. + bWaitingToStart = true; + timeWaitingToStartTimeout = ::timeGetTime() + 30000; + nHostLastParamID++; + InformAboutStart(); + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + SetPlayerReadyToGo( pWO->szMyName, "ready" ); + Sound_Effect( VOC_GAME_CLOSED ); + } + } + } + break; + + case ( BUTTON_SCENARIO_RA | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_RA ); + break; + case ( BUTTON_SCENARIO_CS | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_CS ); + break; + case ( BUTTON_SCENARIO_AM | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_AM ); + break; + case ( BUTTON_SCENARIO_USER | KN_BUTTON ): + ScenarioDisplayMode( SCENARIO_USER ); + break; + +// case (BUTTON_LOAD | KN_BUTTON): +// case (BUTTON_OK | KN_BUTTON): +// break; + + case (KN_ESC): + if( pDropListHouse->IsDropped ) + bRetractHouseDropDown = true; + break; + + case ( BUTTON_LEAVE | KN_BUTTON ): + case ( BUTTON_CANCEL | KN_BUTTON ): + if( ExitGameChannel() ) + { + pWO->RejoinLobbyAfterGame(); + bProcess = false; + ResultReturn = RESULT_WOLGSUP_BACKTOCHAT; // Return to chat. + } + else + { + bProcess = false; + ResultReturn = RESULT_WOLGSUP_FATALERROR; // Return with an error value. + } + break; + } + + Call_Back(); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + pWO->ClearListPtrs(); + pWO->pGSupDlg = NULL; + + return ResultReturn; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetSpecialControlStates() +{ + // Set gauges and checklist. + + pCheckListOptions->Check_Item(0, Session.Options.Bases); + pCheckListOptions->Check_Item(1, Session.Options.Tiberium); + pCheckListOptions->Check_Item(2, Session.Options.Goodies); + pCheckListOptions->Check_Item(3, Special.IsCaptureTheFlag); // Ugh. Use of "Special" global. + pCheckListOptions->Check_Item(4, Special.IsShadowGrow); // Ugh. Use of "Special" global. + + pCheckListOptions->Check_Item(5, bSlowUnitBuildRate); // Ugh. Use of "Special" global. + + pGaugeCount->Set_Maximum(SessionClass::CountMax[Session.Options.Bases] - SessionClass::CountMin[Session.Options.Bases]); + pGaugeCount->Set_Value(Session.Options.UnitCount - SessionClass::CountMin[Session.Options.Bases]); + + pGaugeLevel->Set_Maximum(MPLAYER_BUILD_LEVEL_MAX - 1); + pGaugeLevel->Set_Value(BuildLevel - 1); + + pGaugeCredits->Set_Maximum(Rule.MPMaxMoney); + pGaugeCredits->Set_Value(Session.Options.Credits); + + if( pWO->GameInfoCurrent.bTournament ) + { + pGaugeAIPlayers->Set_Maximum( 0 ); + } + else + { + // Note dependency of AIPlayers on number of human players. + // pGaugeAIPlayers->Set_Maximum(Rule.MaxPlayers-Session.Players.Count()); + pGaugeAIPlayers->Set_Maximum( Rule.MaxPlayers - pWO->GameInfoCurrent.iPlayerMax ); + } + pGaugeAIPlayers->Set_Value(Session.Options.AIPlayers); + + if( bAftermathUnits ) + pCheckAftermathUnits->Turn_On(); + else + pCheckAftermathUnits->Turn_Off(); +} + +#define DRAWTABDOWN Turn_On() +#define DRAWTABUP Turn_Off() + +//*********************************************************************************************** +void WOL_GameSetupDialog::BindControls( bool bBind ) +{ + if( bBind ) + { + commands = pWO->pShpBtnDiscon; + pWO->pShpBtnLeave->Add_Tail(*commands); + pWO->pShpBtnRefresh->Add_Tail(*commands); + pWO->pShpBtnSquelch->Add_Tail(*commands); + pWO->pShpBtnBan->Add_Tail(*commands); + pWO->pShpBtnKick->Add_Tail(*commands); + pWO->pShpBtnFindpage->Add_Tail(*commands); + pWO->pShpBtnOptions->Add_Tail(*commands); + pWO->pShpBtnLadder->Add_Tail(*commands); + pWO->pShpBtnHelp->Add_Tail(*commands); + pILPlayers->Add_Tail(*commands); + if( bHost ) + { + // Draw order of tabs depends on which one is selected, as we want them to overlap appropriately. + // Also, the selected tab must appear over the scenario list, while the unselected tabs are below it. + // This assures that that bottom of the tab overlaps the scenario list border, making it look connected. + + // ajw I could make all maps always available now that they're downloadable. Playing CS map with AM rules + // would mean switching CDs, though. + // Would mean no difference between RA and CS games. + + switch( ScenKindCurrent ) + { + case SCENARIO_RA: + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABUP; + break; + case SCENARIO_CS: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::CSGAME. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABUP; + break; + case SCENARIO_AM: // pWO->GameInfoCurrent.GameKind must be CREATEGAMEINFO::AMGAME, or RAGAME with AM installed. + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABDOWN; + } + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + pILScens->Add_Tail(*commands); + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABUP; + break; + case SCENARIO_USER: // pWO->GameInfoCurrent.bTournament must be false. + pShpBtnScenarioRA->Add_Tail(*commands); + pShpBtnScenarioRA->DRAWTABDOWN; + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + { + pShpBtnScenarioCS->Add_Tail(*commands); + pShpBtnScenarioCS->DRAWTABDOWN; + } + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + { + pShpBtnScenarioAM->Add_Tail(*commands); + pShpBtnScenarioAM->DRAWTABDOWN; + } + pILScens->Add_Tail(*commands); + pShpBtnScenarioUser->Add_Tail(*commands); + pShpBtnScenarioUser->DRAWTABUP; + break; + } + } +// pStaticDescrip->Add_Tail(*commands); + pEditSend->Add_Tail(*commands); + pStaticUnit->Add_Tail(*commands); + pStaticLevel->Add_Tail(*commands); + pStaticCredits->Add_Tail(*commands); + pStaticAIPlayers->Add_Tail(*commands); + pGaugeCount->Add_Tail(*commands); + pGaugeCredits->Add_Tail(*commands); + pGaugeAIPlayers->Add_Tail(*commands); + pGaugeLevel->Add_Tail(*commands); + pCheckListOptions->Add_Tail(*commands); + pTextBtnCancel->Add_Tail(*commands); + pTextBtnAcceptStart->Add_Tail(*commands); + pTextBtnAction->Add_Tail(*commands); + pILDisc->Add_Tail(*commands); + pDropListHouse->Add_Tail(*commands); +// pCheckAftermathUnits->Add_Tail(*commands); + } + else + { + pWO->pShpBtnDiscon->Zap(); + pWO->pShpBtnLeave->Zap(); + pWO->pShpBtnRefresh->Zap(); + pWO->pShpBtnSquelch->Zap(); + pWO->pShpBtnBan->Zap(); + pWO->pShpBtnKick->Zap(); + pWO->pShpBtnFindpage->Zap(); + pWO->pShpBtnOptions->Zap(); + pWO->pShpBtnLadder->Zap(); + pWO->pShpBtnHelp->Zap(); + pILPlayers->Zap(); + if( bHost ) + { + pShpBtnScenarioRA->Zap(); + if( !pWO->GameInfoCurrent.bTournament ) + { + pShpBtnScenarioUser->Zap(); + } + if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::CSGAME ) + pShpBtnScenarioCS->Zap(); + else if( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME || + ( pWO->GameInfoCurrent.GameKind == CREATEGAMEINFO::RAGAME && Is_Aftermath_Installed() && !pWO->GameInfoCurrent.bTournament ) ) + pShpBtnScenarioAM->Zap(); + pILScens->Zap(); + } +// pStaticDescrip->Zap(); + pEditSend->Zap(); + pStaticUnit->Zap(); + pStaticLevel->Zap(); + pStaticCredits->Zap(); + pStaticAIPlayers->Zap(); + pGaugeCount->Zap(); + pGaugeCredits->Zap(); + pGaugeAIPlayers->Zap(); + pGaugeLevel->Zap(); + pCheckListOptions->Zap(); + pTextBtnCancel->Zap(); + pTextBtnAcceptStart->Zap(); + pTextBtnAction->Zap(); + pILDisc->Zap(); + pDropListHouse->Zap(); +// pCheckAftermathUnits->Zap(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ) +{ +// debugprint( "ScenarioDisplayMode, from %i going into %i mode.\n", ScenKindCurrent, ScenKind ); + + // Puts us into mode where we are viewing a particular gamekind of scenario list. + if( ScenKindCurrent == ScenKind ) + return; + + // Reorder tab buttons. + BindControls( false ); + ScenKindCurrent = ScenKind; + BindControls( true ); + + // Reset list items. + pILScens->Clear(); +// debugprint( " * * * * * * ScenKind %i has %i items.\n", ScenKind, ar_szScenarios[ ScenKind ].Count() ); + // Check for the currently selected scenario. + bool bFoundCurrentSelection = false; + int iSelect; + for( int i = 0; i != ar_szScenarios[ ScenKind ].Count(); i++ ) + { + // Put ScenarioIndex in as extradata to list item. + int iScenIndex = (int)ar_szScenIndexes[ ScenKind ][ i ]; + int iIndexNew = pILScens->Add_Item( ar_szScenarios[ ScenKind ][ i ], NULL, NULL, ICON_DIB, NULL, (void*)iScenIndex ); + if( iScenIndex == Session.Options.ScenarioIndex && !bFoundCurrentSelection ) + { + // (Choose first line of what can be multiline description of currently selected scenario.) + bFoundCurrentSelection = true; + iSelect = i; + } + } + // If the current scenario selection is in this list, enable the list to show the selection, otherwise, + // make the listclass selection invisible, because it doesn't indicate the real scenario selection. + // Basically, problem is that I can't have no selection in ListClass, and I don't want to risk changing + // it as I'll affect the rest of the code. So I do this horrible hack. + if( bFoundCurrentSelection ) + { + pILScens->SetSelectType( 1 ); // Regular selection. + pILScens->Set_Selected_Index( iSelect ); + } + else + pILScens->SetSelectType( 0 ); // Invisible selection. + +// display = REDRAW_ALL; + pILScens->Flag_To_Redraw(); + pShpBtnScenarioRA->Flag_To_Redraw(); + pShpBtnScenarioCS->Flag_To_Redraw(); + pShpBtnScenarioAM->Flag_To_Redraw(); + pShpBtnScenarioUser->Flag_To_Redraw(); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::ExitGameChannel() +{ +// debugprint( "ExitGameChannel \n" ); + if( pDropListHouse->IsDropped ) + { + pDropListHouse->Collapse(); + if (display < REDRAW_BACKGROUND) display = REDRAW_BACKGROUND; + } + if( !pWO->ChannelLeave() ) + { + pWO->GenericErrorMessage(); + return false; + } + + pWO->OnExitingGameChannel(); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::DrawScenarioDescripIcon( const char* pDIB ) const +{ + CC_Draw_DIB( pDIB, d_gamekind_x - 16, d_gamekind_y - 2, 100, WINDOW_MAIN ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerColor( const char* szName, PlayerColorType Color ) +{ + // Sets player color - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerColor %s to %i\n", szName, Color ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player color Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - color will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerColor could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + if( strcmp( pWO->szMyName, szName ) == 0 ) + { + // I am the player involved. + Session.ColorIdx = Color; + if( display < REDRAW_COLORS ) display = REDRAW_COLORS; + } + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ Color == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : Color ] ); + pILPlayers->Flag_To_Redraw(); + +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::GetPlayerColor( const char* szName ) +{ + // Returns player color, if player is found in list, else PCOLOR_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return PCOLOR_NONE; + } + RemapControlType* pRemap = pILPlayers->Get_Item_Color( iItem ); + return PlayerColorTypeOf( pRemap ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetPlayerHouse( const char* szName, HousesType House ) +{ + // Sets player house - does not verify if it is "okay" to do so. +// debugprint( "SetPlayerHouse %s to %i\n", szName, House ); + + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + // This can happen when player house Informs arrive from the host before I have gotten a userlist. + // Insert an entry for user - house will be maintained when the userlist arrives. + // "early insertion" +// debugprint( "SetPlayerHouse could not find name '%s'! Inserting...\n", szName ); + iItem = pILPlayers->Add_Item( szName ); + } + + // Reset item text. + char szItem[ 100 ]; + pWO->WritePlayerListItem( szItem, szName, House ); +// debugprint ( "%i, %s\n", iItem, szItem ); + pILPlayers->Set_Item( iItem, szItem ); + pILPlayers->Flag_To_Redraw(); +} + +//*********************************************************************************************** +HousesType WOL_GameSetupDialog::GetPlayerHouse( const char* szName ) +{ + // Returns player house for user if found in list, else HOUSE_NONE. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. + return HOUSE_NONE; + } + + return pWO->PullPlayerHouse_From( pILPlayers->Get_Item( iItem ) ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerAccepted( const char* szName, bool bAccepted ) +{ + // Sets player's 'accepted' state to true or false. + // Value is stored in the player list: if there is an accepted icon, player has accepted. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerAccepted() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerAccepted() - set '%s' to %s.\n", szName, bAccepted ? "accepted" : "NOT accepted" ); + return pWO->MarkItemAccepted( iItem, bAccepted ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::IveAccepted() +{ + // Returns true if I am marked as "accepted". + int iItem = pILPlayers->Find( pWO->szMyName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedAccepted( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::SetPlayerReadyToGo( const char* szName, const char* szReadyState ) +{ + // Sets player's 'ready to go' state. + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + // Value is stored in the player list in the hidden string field. + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + { + // Player name was not found in list. +// debugprint( "SetPlayerReadyToGo() - could not find '%s'.\n", szName ); + return false; + } +// debugprint( "SetPlayerReadyToGo() - set '%s' to %s.\n", szName, szReadyState ); + pWO->MarkItemReadyToGo( iItem, szReadyState ); + + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ResetReadyToGo() +{ + // Resets all users to "not ready to go". + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + pWO->MarkItemReadyToGo( i, NULL ); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bPlayerReadyToGo( const char* szName ) +{ + // Returns true if player is marked as "ready to go". + int iItem = pILPlayers->Find( szName ); + if( iItem == -1 ) + return false; + return pWO->bItemMarkedReadyToGo( iItem ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllPlayersReadyToGo() +{ + // Returns true if all players are marked as "ready to go". +// debugprint( "Checking for all players ready - there are %i\n", pILPlayers->Count() ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedReadyToGo( i ) ) + { +// debugprint( "Item %i NOT ready\n", i ); + return false; + } +// debugprint( "Item %i is ready\n", i ); + } +// debugprint( "Return true\n" ); + return true; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessGuestRequest( User* pUser, const char* szRequest ) +{ + // Game host processes a request that arrived as a privategameopt from one of the guests. + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 null-terminator +// debugprint( "ProcessGuestRequest. szRequest is '%s', len %i.\n", szRequest, strlen( szRequest ) ); + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szRequest ); + szRequest += 3; + + switch( opt ) + { + case WOL_GAMEOPT_REQCOLOR: + { + PlayerColorType ColorDesired = (PlayerColorType)( atoi( szRequest ) ); + if( pILPlayers->FindColor( &ColorRemaps[ ColorDesired == PCOLOR_DIALOG_BLUE ? PCOLOR_REALLY_BLUE : ColorDesired ] ) == -1 ) + { + // Color is available. + SetPlayerColor( (char*)pUser->name, ColorDesired ); + // Tell all guests about the color change. + InformAboutPlayerColor( (char*)pUser->name, ColorDesired, NULL ); + } + else + { + // Color is not available. +// debugprint( "Color %i denied to %s\n", ColorDesired, (char*)pUser->name ); + // Tell requestor that his color is still the same. + RemapControlType* pColorRemapCurrent = pILPlayers->Get_Item_Color( pILPlayers->Find( (char*)pUser->name ) ); + InformAboutPlayerColor( (char*)pUser->name, PlayerColorTypeOf( pColorRemapCurrent ), pUser ); + } + break; + } + case WOL_GAMEOPT_REQHOUSE: + { + HousesType HouseChoice = (HousesType)( atoi( szRequest ) ); +// debugprint( "Host received: '%s' changed house to %u.\n", (char*)pUser->name, HouseChoice ); + SetPlayerHouse( (char*)pUser->name, HouseChoice ); + nHostLastParamID++; + InformAboutPlayerHouse( (char*)pUser->name, HouseChoice, NULL ); + ClearAllAccepts(); + break; + } + case WOL_GAMEOPT_REQACCEPT: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) + { +// debugprint( "Host received valid accept from '%s'.\n", (char*)pUser->name ); + SetPlayerAccepted( (char*)pUser->name, true ); + InformAboutPlayerAccept( (char*)pUser->name, NULL ); + // We may be ready to start a game now. + if( bAllGuestsAccept() ) + { + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + pTextBtnAcceptStart->Enable(); + } + } + else + { +// debugprint( "______________Host received invalid accept from '%s'. ID = %i when it should be %i.\n", (char*)pUser->name, +// atoi( szRequest ), nHostLastParamID ); + } + break; + case WOL_GAMEOPT_REQSTART: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART from '%s'.\n", (char*)pUser->name ); +// WOL_PrintMessage( *pILDisc, "WOL_GAMEOPT_REQSTART response", WOLCOLORREMAP_LOCALMACHINEMESS ); +// WOL_PrintMessage( *pILDisc, (char*)pUser->name, WOLCOLORREMAP_LOCALMACHINEMESS ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "ready" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO: + // Does Param ID of accept request match the last param change ID sent? See notes at top. + if( atoi( szRequest ) == nHostLastParamID ) // Otherwise ignore - it's old and we don't care. (Incredibly unlikely to happen, actually.) + { +// debugprint( "Host received valid WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO from '%s'.\n", (char*)pUser->name ); + if( bWaitingToStart ) + // If all responses are in, start the game! + GuestIsReadyToPlay( (char*)pUser->name, "need scenario" ); +// else +// debugprint( "Ignoring - I am no longer waiting to start a game.\n" ); + } + break; + case WOL_GAMEOPT_INFGO: + // I have told myself to start game right now. + // This is the new method. Avoids apparent "private messages/gameopts are getting delayed" problem in chatserver... + // (I don't want to end up leaving the channel before guests get my go message.) + strcpy( szTriggerGameStartInfo, szRequest ); + break; + default: +// debugprint( "Unhandled value of %i in ProcessGuestRequest!!!\n", opt ); + break; + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ProcessInform( char* szInform ) +{ + // Process inform message arriving from game host. +// debugprint( "ProcessInform: '%s'\n", szInform ); + if( !bHost ) + { + WOL_GAMEOPT opt = (WOL_GAMEOPT)atoi( szInform ); + szInform += 3; + switch( opt ) + { + case WOL_GAMEOPT_INFCOLOR: + { + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + PlayerColorType Color = (PlayerColorType)( atoi( szInform ) ); + szInform += 3; + SetPlayerColor( szInform, Color ); // (szInform is now sitting at the start of the name string.) + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFHOUSE: // Note: In theory, I could ignore this if it refers to me. I've already set my own house. + { + nGuestLastParamID = atoi( szInform ); + szInform += 7; + HousesType House = (HousesType)( atoi( szInform ) ); + szInform += 3; + SetPlayerHouse( szInform, House ); // (szInform is now sitting at the start of the name string.) + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + } + case WOL_GAMEOPT_INFACCEPT: // Note: In theory, I could ignore this if it refers to me. + // A guest has accepted. + SetPlayerAccepted( szInform, true ); // (szInform is now sitting at the start of the name string.) + break; + case WOL_GAMEOPT_INFPARAMS: + // Game params have changed. + bParamsReceived = true; + if( !AcceptParams( szInform ) ) + bLeaveDueToRulesMismatchTrigger = true; + SetSpecialControlStates(); + //pILScens->Set_Selected_Index( Session.Options.ScenarioIndex ); + display = REDRAW_ALL; + ClearAllAccepts(); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFNEWGUESTPLAYERINFO: + // I have just joined and have received a message with info on all players in game. + AcceptNewGuestPlayerInfo( szInform ); + Sound_Effect(VOC_OPTIONS_CHANGED); + break; + case WOL_GAMEOPT_INFSTART: + { + // Host tells us to wait for start of game. +// debugprint( "Guest received WOL_GAMEOPT_INFSTART.\n" ); + nGuestLastParamID = atoi( szInform ); +// The following check is not necessary. Rules.ini, if manually replaced by a cheater, is not reloaded. +// So prior checks (that occur on game params receives) are sufficient. +// szInform += 7; +// // Check rules.ini compatibility. +// int iRulesID = atoi( szInform ); +// if( RuleINI.Get_Unique_ID() != iRulesID ) +// { +// // Rules.ini incompatible. Don't respond to call for start. +// bLeaveDueToRulesMismatchTrigger = true; +// break; +// } + User* pUserHost = pWO->pGameHost(); + if( pUserHost ) // This better'd be true. + { + // Send ack back to host. + char szSend[ 20 ]; + // Make sure we have the scenario. + if( !bNeedScenarioDownload() ) + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART, nGuestLastParamID ); + else + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, nGuestLastParamID ); + pWO->SendGameOpt( szSend, pUserHost ); + // Enter waiting mode. + bWaitingToStart = true; + WWMessageBox().Process( TXT_WOL_WAITINGTOSTART, TXT_NONE ); + BindControls( false ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. + // Set global that will force edit dialogs to stop accepting characters. + // This is to fix a minor glitch: guests can keep typing into a "page" dialog editbox after the + // "Launching game..." message has appeared on top of it. + if( pWO->bPump_In_Call_Back ) + disable_current_msgbox = true; + } + else + { +// debugprint( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + Fatal( "Impossible arose on WOL_GAMEOPT_INFSTART.\n" ); + } + Sound_Effect( VOC_GAME_CLOSED ); + break; + } + case WOL_GAMEOPT_INFCANCELSTART: + // Host tells us to cancel waiting for start of game. + bWaitingToStart = false; + ClearAllAccepts(); + BindControls( true ); + WOL_PrintMessage( *pILDisc, TXT_WOL_STARTCANCELLED, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( VOC_SYS_ERROR ); + display = REDRAW_ALL; + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + case WOL_GAMEOPT_INFGO: + // Host says start game right now. + strcpy( szTriggerGameStartInfo, szInform ); + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + break; + default: +// debugprint( "Unhandled value of %i in ProcessInform!!!\n", opt ); + //WOL_PrintMessage( *pILDisc, "Error - Unhandled value in ProcessInform!!!", WOLCOLORREMAP_LOCALMACHINEMESS ); + Fatal( "Error - Unhandled value in ProcessInform!" ); + break; + } + } +// debugprint( "* END of ProcessInform: '%s'\n", szInform ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bParamsUnfresh() +{ + // Returns true if game setup parameters do not match what they were last time they were sent. + GAMEPARAMS GParamsNow; + SetGParamsToCurrent( GParamsNow ); + +// if( !( GParamsNow == GParamsLastSent ) ) +// { +// debugprint( "---- Params Unfresh: OLD...\n" ); +// Debug_GlobalPacketType( GParamsLastSent.GPacket ); +// debugprint( "------------------- NEW...\n" ); +// Debug_GlobalPacketType( GParamsNow.GPacket ); +// debugprint( "old bAftermathUnits = %i\n", GParamsLastSent.bAftermathUnits ); +// debugprint( "new bAftermathUnits = %i\n", GParamsNow.bAftermathUnits ); +// } + + return !( GParamsNow == GParamsLastSent ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SendParams() +{ + // Host only. + + SetGParamsToCurrent( GParamsLastSent ); + + char szSend[ 130 + DESCRIP_MAX + 12 + 32 + 100 ]; + sprintf( szSend, + "%01u " + "%06u " + "%03u " + "%s " + "%u " + "%s " + "%01u " + "%.32s " // No null-terminator on digest. There may be nothing at all inserted here. + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + "%u " + , + WOL_GAMEOPT_INFPARAMS, + nHostLastParamID, + strlen( GParamsLastSent.GPacket.ScenarioInfo.Scenario ), + GParamsLastSent.GPacket.ScenarioInfo.Scenario, + GParamsLastSent.GPacket.ScenarioInfo.FileLength, + GParamsLastSent.GPacket.ScenarioInfo.ShortFileName, +// strlen( (char*)GParamsLastSent.GPacket.ScenarioInfo.FileDigest ), not null-terminated! + GParamsLastSent.GPacket.ScenarioInfo.FileDigest[0] ? 1 : 0, + GParamsLastSent.GPacket.ScenarioInfo.FileDigest, + GParamsLastSent.GPacket.ScenarioInfo.OfficialScenario, + GParamsLastSent.GPacket.ScenarioInfo.Credits, + GParamsLastSent.GPacket.ScenarioInfo.IsBases, + GParamsLastSent.GPacket.ScenarioInfo.IsTiberium, + GParamsLastSent.GPacket.ScenarioInfo.IsGoodies, + GParamsLastSent.GPacket.ScenarioInfo.BuildLevel, + GParamsLastSent.GPacket.ScenarioInfo.UnitCount, + GParamsLastSent.GPacket.ScenarioInfo.AIPlayers, + GParamsLastSent.GPacket.ScenarioInfo.Seed, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsShadowGrow, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsSpeedBuild, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsFromInstall, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsCaptureTheFlag, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsInert, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsThreePoint, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTGrowth, + GParamsLastSent.GPacket.ScenarioInfo.Special.IsTSpread, + GParamsLastSent.GPacket.ScenarioInfo.GameSpeed, + GParamsLastSent.GPacket.ScenarioInfo.Version, + GParamsLastSent.bAftermathUnits, // Not currently used. + GParamsLastSent.bSlowUnitBuildRate, + RuleINI.Get_Unique_ID() // Used to verify rules.ini files match. + ); + + pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::AcceptParams( char* szParams ) +{ + // Reverse of SendParams() process. szParams has already been stripped of 2 bytes header. Guest only. + + // Returns false if rules.ini doesn't match that of the host. + // (Or if an error occurs due to the packet being incorrect - which happened once in test...) + + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szParams, szDelimiter ); + nGuestLastParamID = atoi( szToken ); + + // Read in length of following string. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iLen = atoi( szToken ); + // Set string pointer to start of string (length is 3 digits). + szRemaining = szToken + 4; + // Read in string. + memcpy( Session.Options.ScenarioDescription, szRemaining, iLen ); + // Null-terminate. + Session.Options.ScenarioDescription[ iLen ] = 0; + // Advance string pointer to next param. + szRemaining += iLen + 1; + +//debugprint( "scenario description is '%s'\n", Session.Options.ScenarioDescription ); +//debugprint( "remaining: '%s'\n", szRemaining ); + + szToken = strtok( szRemaining, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioFileLength = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + strcpy( Session.ScenarioFileName, szToken ); + +// // Read in length of following string. +// szToken = strtok( NULL, szDelimiter ); +// iLen = atoi( szToken ); +// // Set string pointer to start of string (length is 3 digits). +// szRemaining = szToken + 4; + // Method changed. + // Check if there is a digest. + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + iLen = atoi( szToken ); // 1 or 0, indicating if there is a digest following. + if( iLen ) + { +// // Set string pointer to start of string (previous field is 1 digit). +// szRemaining = szToken + 2; +// iLen = sizeof( Session.ScenarioDigest ); +// // Read in string. +// memcpy( Session.ScenarioDigest, szRemaining, iLen ); +// // // Null-terminate. +// // Session.ScenarioDigest[ iLen ] = 0; Digest has no null-terminator! +// // Advance string pointer to next param. +// szRemaining += iLen + 1; + // There is a digest. + szToken = strtok( NULL, szDelimiter ); // (Digests can't have spaces in the them.) +//debugprint( "digest: '%s'\n", szToken ); + if( !szToken ) return false; + strncpy( Session.ScenarioDigest, szToken, sizeof( Session.ScenarioDigest ) ); + } + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.ScenarioIsOfficial = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Credits = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Bases = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Tiberium = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.Goodies = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + BuildLevel = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.UnitCount = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Session.Options.AIPlayers = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Seed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsShadowGrow = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsSpeedBuild = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsFromInstall = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsCaptureTheFlag = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsInert = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsThreePoint = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTGrowth = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Special.IsTSpread = ( atoi( szToken ) == 0 ) ? 0 : 1; + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + Options.GameSpeed = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; +// "Version" = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bAftermathUnits = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + bSlowUnitBuildRate = (bool)atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); + if( !szToken ) return false; + int iRulesID = atoi( szToken ); + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + return ( RuleINI.Get_Unique_ID() == iRulesID ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::SetGParamsToCurrent( GAMEPARAMS& GParams ) +{ + // Sets values in a GAMEPARAMS to the current game settings. + + strcpy( GParams.GPacket.ScenarioInfo.Scenario, Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + CCFileClass file( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + GParams.GPacket.ScenarioInfo.FileLength = file.Size(); + strcpy( GParams.GPacket.ScenarioInfo.ShortFileName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); + // Digest is not null-terminated. + strncpy( (char*)GParams.GPacket.ScenarioInfo.FileDigest, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Digest(), sizeof( GParams.GPacket.ScenarioInfo.FileDigest ) ); + GParams.GPacket.ScenarioInfo.OfficialScenario = Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official(); + GParams.GPacket.ScenarioInfo.Credits = Session.Options.Credits; + GParams.GPacket.ScenarioInfo.IsBases = Session.Options.Bases; + GParams.GPacket.ScenarioInfo.IsTiberium = Session.Options.Tiberium; + GParams.GPacket.ScenarioInfo.IsGoodies = Session.Options.Goodies; + GParams.GPacket.ScenarioInfo.BuildLevel = BuildLevel; + GParams.GPacket.ScenarioInfo.UnitCount = Session.Options.UnitCount; + GParams.GPacket.ScenarioInfo.AIPlayers = Session.Options.AIPlayers; + GParams.GPacket.ScenarioInfo.Seed = Seed; + GParams.GPacket.ScenarioInfo.Special = Special; + GParams.GPacket.ScenarioInfo.GameSpeed = Options.GameSpeed; + GParams.GPacket.ScenarioInfo.Version = VerNum.Get_Clipped_Version(); + + GParams.bAftermathUnits = bAftermathUnits; + GParams.bSlowUnitBuildRate = bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GAMEPARAMS& gp1, const GAMEPARAMS& gp2 ) +{ + return gp1.GPacket == gp2.GPacket && + gp1.bAftermathUnits == gp2.bAftermathUnits && gp1.bSlowUnitBuildRate == gp2.bSlowUnitBuildRate; +} + +//*********************************************************************************************** +bool operator==( const GlobalPacketType& gp1, const GlobalPacketType& gp2 ) +{ + if( strcmp( gp1.ScenarioInfo.Scenario, gp2.ScenarioInfo.Scenario ) != 0 ) return false; + if( strcmp( gp1.ScenarioInfo.ShortFileName, gp2.ScenarioInfo.ShortFileName ) != 0 ) return false; + // Digest is not null-terminated... + if( strncmp( (const char*)gp1.ScenarioInfo.FileDigest, (const char*)gp2.ScenarioInfo.FileDigest, sizeof( gp1.ScenarioInfo.FileDigest ) ) != 0 ) return false; + + if( gp1.ScenarioInfo.FileLength == gp2.ScenarioInfo.FileLength && + gp1.ScenarioInfo.OfficialScenario == gp2.ScenarioInfo.OfficialScenario && + gp1.ScenarioInfo.Credits == gp2.ScenarioInfo.Credits && + gp1.ScenarioInfo.IsBases == gp2.ScenarioInfo.IsBases && + gp1.ScenarioInfo.IsTiberium == gp2.ScenarioInfo.IsTiberium && + gp1.ScenarioInfo.IsGoodies == gp2.ScenarioInfo.IsGoodies && + gp1.ScenarioInfo.BuildLevel == gp2.ScenarioInfo.BuildLevel && + gp1.ScenarioInfo.UnitCount == gp2.ScenarioInfo.UnitCount && + gp1.ScenarioInfo.AIPlayers == gp2.ScenarioInfo.AIPlayers && + gp1.ScenarioInfo.Seed == gp2.ScenarioInfo.Seed && + gp1.ScenarioInfo.Special.IsShadowGrow == gp2.ScenarioInfo.Special.IsShadowGrow && + gp1.ScenarioInfo.Special.IsSpeedBuild == gp2.ScenarioInfo.Special.IsSpeedBuild && + gp1.ScenarioInfo.Special.IsFromInstall == gp2.ScenarioInfo.Special.IsFromInstall && + gp1.ScenarioInfo.Special.IsCaptureTheFlag == gp2.ScenarioInfo.Special.IsCaptureTheFlag && + gp1.ScenarioInfo.Special.IsInert == gp2.ScenarioInfo.Special.IsInert && + gp1.ScenarioInfo.Special.IsThreePoint == gp2.ScenarioInfo.Special.IsThreePoint && + gp1.ScenarioInfo.Special.IsTGrowth == gp2.ScenarioInfo.Special.IsTGrowth && + gp1.ScenarioInfo.Special.IsTSpread == gp2.ScenarioInfo.Special.IsTSpread && + gp1.ScenarioInfo.GameSpeed == gp2.ScenarioInfo.GameSpeed && + gp1.ScenarioInfo.Version == gp2.ScenarioInfo.Version ) + return true; + + return false; +} + +//*********************************************************************************************** +/* +void Debug_GlobalPacketType( const GlobalPacketType& gp1 ) // ajw debugging +{ + if( *gp1.ScenarioInfo.Scenario ) +// debugprint( "Scenario = %s\n", (char*)gp1.ScenarioInfo.Scenario ); + else +// debugprint( "!Scenario string is null\n" ); + if( *gp1.ScenarioInfo.ShortFileName ) +// debugprint( "ShortFileName = %s\n", (char*)gp1.ScenarioInfo.ShortFileName ); + else +// debugprint( "!ShortFileName string is null\n" ); + // Remember ShortFileName is not null-terminated... + if( *gp1.ScenarioInfo.FileDigest ) +// debugprint( "FileDigest = %.32s\n", (char*)gp1.ScenarioInfo.FileDigest ); + else +// debugprint( "!FileDigest string is null\n" ); + +// debugprint( "FileLength = %i\n" + "OfficialScenario = %i\n" + "Credits = %i\n" + "IsBases = %i\n" + "IsTiberium = %i\n" + "IsGoodies = %i\n" + "BuildLevel = %i\n" + "UnitCount = %i\n" + "AIPlayers = %i\n" + "Seed = %i\n" + "Special.IsShadowGrow = %i\n" + "Special.IsSpeedBuild = %i\n" + "Special.IsFromInstall = %i\n" + "Special.IsCaptureTheFlag = %i\n" + "Special.IsInert = %i\n" + "Special.IsThreePoint = %i\n" + "Special.IsTGrowth = %i\n" + "Special.IsTSpread = %i\n" + "GameSpeed = %i\n" + "Version = %i\n", + gp1.ScenarioInfo.FileLength, + gp1.ScenarioInfo.OfficialScenario, + gp1.ScenarioInfo.Credits, + gp1.ScenarioInfo.IsBases, + gp1.ScenarioInfo.IsTiberium, + gp1.ScenarioInfo.IsGoodies, + gp1.ScenarioInfo.BuildLevel, + gp1.ScenarioInfo.UnitCount, + gp1.ScenarioInfo.AIPlayers, + gp1.ScenarioInfo.Seed, + gp1.ScenarioInfo.Special.IsShadowGrow, + gp1.ScenarioInfo.Special.IsSpeedBuild, + gp1.ScenarioInfo.Special.IsFromInstall, + gp1.ScenarioInfo.Special.IsCaptureTheFlag, + gp1.ScenarioInfo.Special.IsInert, + gp1.ScenarioInfo.Special.IsThreePoint, + gp1.ScenarioInfo.Special.IsTGrowth, + gp1.ScenarioInfo.Special.IsTSpread, + gp1.ScenarioInfo.GameSpeed, + gp1.ScenarioInfo.Version ); +} +*/ + +//*********************************************************************************************** +PlayerColorType PlayerColorTypeOf( RemapControlType* pColorRemap ) +{ + for( PlayerColorType pcolor = PCOLOR_FIRST; pcolor < PCOLOR_COUNT; pcolor++ ) + { + if( &ColorRemaps[ pcolor ] == pColorRemap ) + return pcolor; + } + return PCOLOR_NONE; +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::RequestPlayerColor( PlayerColorType Color ) +{ + // Local player sends a request to the game host asking for a particular color. + char szSend[ 20 ]; + + // WOL_GAMEOPT_REQCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_REQCOLOR, Color ); + + _ASSERTE( pWO->pGameHost() ); + + return pWO->SendGameOpt( szSend, pWO->pGameHost() ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ) +{ + // Game host tells guests about a player color assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 20 + WOL_NAME_LEN_MAX ]; + + // WOL_GAMEOPT_INFCOLOR format: + // 2 WOL_GAMEOPT + // 1 space + // 2 color + // 1 space + // string name of player + + if( Color == PCOLOR_NONE ) + { +// debugprint( "Bad Color for %s in InformAboutPlayerColor.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %02u %s", WOL_GAMEOPT_INFCOLOR, Color, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ) +{ + // Game host tells guests about a player house assignment. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 28 + WOL_NAME_LEN_MAX ]; + // WOL_GAMEOPT_INFHOUSE format: + // 2 WOL_GAMEOPT + // 1 space + // 6 param ID + // 1 space + // 2 house + // 1 space + // string name of player + + if( House == HOUSE_NONE ) + { +// debugprint( "Bad House for %s in InformAboutPlayerHouse.\n", szName ); + *szSend = 0; + } + else + sprintf( szSend, "%02u %06u %02u %s", WOL_GAMEOPT_INFHOUSE, nHostLastParamID, (short)House, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutPlayerAccept( const char* szName, User* pUserPriv ) +{ + // Game host tells guests about player accepting game params. + // If pUserPriv is not null, indicates user to send message as private to. + char szSend[ 6 + WOL_NAME_LEN_MAX ]; + sprintf( szSend, "%02u %s", WOL_GAMEOPT_INFACCEPT, szName ); + + return pWO->SendGameOpt( szSend, pUserPriv ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. + char szSend[ 10 ]; + + sprintf( szSend, "%02u %06u", WOL_GAMEOPT_INFSTART, nHostLastParamID ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::InformAboutCancelStart() +{ + // Game host tells all guests that he wants to start the game. + // Note that nHostLastParamID is involved here. We want to make sure that guest responses apply + // to the latest WOL_GAMEOPT_INFSTART, and not to an earlier one we canceled out of. +// debugprint( "InformAboutCancelStart!\n" ); + + char szSend[ 10 ]; + + sprintf( szSend, "%02u", WOL_GAMEOPT_INFCANCELSTART ); + + return pWO->SendGameOpt( szSend, NULL ); +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestJoin( User* pUser ) +{ + // A guest (not myself) has entered the game channel. +// debugprint( "OnGuestJoin()\n" ); + char* szPrint = new char[ strlen( TXT_WOL_PLAYERJOINEDGAME ) + strlen( (char*)pUser->name ) + 5 ]; + sprintf( szPrint, TXT_WOL_PLAYERJOINEDGAME, (char*)pUser->name ); + WOL_PrintMessage( *pILDisc, szPrint, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szPrint; + + ClearAllAccepts(); + + if( bHost ) + { + // Send the new guest the current setup. Note that nHostLastParamID doesn't change here. + + // Assign color to new guest. + PlayerColorType Color = ColorNextAvailable(); + SetPlayerColor( (char*)pUser->name, Color ); + + // Previously, I was sending an individual color, house, and acceptedstate message for each other guest. + // Though convenient code-wise, this causes the initial info to arrive at the new guest in a very slow + // manner. This is because of the wonderful "anti-flood" feature of the chat server, which prevents a + // series of messages from a client from being passed on faster than a certain rate. + // For this reason, a new message that contains all of the info about all of the other guests has been + // created (WOL_GAMEOPT_INFNEWGUESTPLAYERINFO). + + // WOL_GAMEOPT_INFNEWGUESTPLAYERINFO format (items separated by spaces): + // WOL_GAMEOPT + // number of players + // for each player { + // length of player name string + // player name + // color + // bool - is there a house field following? + // (house) + // // (removed) acceptedness - true for set player accepted, false for do nothing + // } + + // Build up a big WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + char szSend[ 500 ]; + sprintf( szSend, "%02u %02u", WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, pILPlayers->Count() ); + // Send color and house of all players (including himself) to the new guest. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + char szSendPiece[ 100 ]; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( i ) ); +// InformAboutPlayerColor( szPlayerName, PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ), pUser ); + sprintf( szSendPiece, " %02u %s %02u", strlen( szPlayerName ), szPlayerName, + PlayerColorTypeOf( pILPlayers->Get_Item_Color( i ) ) ); + + if( strcmp( szPlayerName, (char*)pUser->name ) != 0 ) + { + HousesType House = pWO->PullPlayerHouse_From( pILPlayers->Get_Item( i ) ); + if( House != HOUSE_NONE ) + { + // InformAboutPlayerHouse( szPlayerName, House, pUser ); + char szSendHouse[ 50 ]; + sprintf( szSendHouse, " 1 %02u", (short)House ); + strcat( szSendPiece, szSendHouse ); + } + else + { + // Player must not have told me what house he is yet. Don't send house value. + strcat( szSendPiece, " 0" ); + } + } + else + { + // Player is the new guest himself. Don't send house value. + strcat( szSendPiece, " 0" ); + } + +// Acceptedness must be false! No need to send. +// // Send "accepted" status of player. Ignore myself, as I'm the host. +// if( strcmp( szPlayerName, pWO->szMyName ) != 0 && pWO->bItemMarkedAccepted( i ) ) +// strcat( szSendPiece, " 1" ); +// else +// strcat( szSendPiece, " 0" ); + + strcat( szSend, szSendPiece ); + } + pWO->SendGameOpt( szSend, pUser ); + + // Send everyone the color of the new guest. + InformAboutPlayerColor( (char*)pUser->name, Color, NULL ); + + // Send game params. + // This is done last because it contains a param ID value, and we need to ensure that the new guest has + // received everything we are sending him here before he tries to send me an accept. + // If game params were sent first, he could theoretically receive them, then send an accept, even though he + // has not received the WOL_GAMEOPT_INFNEWGUESTPLAYERINFO. + // By doing this I avoid having to have a param ID in WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, which would be hard, + // since it is a private message. + // For simplicity, send public. + SendParams(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::AcceptNewGuestPlayerInfo( char* szMsg ) +{ + // Process a received WOL_GAMEOPT_INFNEWGUESTPLAYERINFO message. + // szMsg has already been stripped of 2 bytes header. + char szDelimiter[] = " "; + char* szToken; + char* szRemaining; + + szToken = strtok( szMsg, szDelimiter ); + unsigned int nPlayers = atoi( szToken ); + + // We have to assist strtok a bit because of calls below that may also call strtok()... + szRemaining = szMsg + 3; + + for( unsigned int nPlayer = 0; nPlayer != nPlayers; ++nPlayer ) + { + // Read in length of following string. + szToken = strtok( szRemaining, szDelimiter ); + int iLen = atoi( szToken ); + + // Set string pointer to start of string (length is 2 digits). + szRemaining = szToken + 3; + // Read in string. + char szPlayerName[ 50 ]; + memcpy( szPlayerName, szRemaining, iLen ); + // Null-terminate. + szPlayerName[ iLen ] = 0; + + // Advance string pointer to next param. + szRemaining += iLen + 1; + + // Read color. + szToken = strtok( szRemaining, szDelimiter ); + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); + + // SetPlayerColor may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 3; + + // Read whether there is a house field. + szToken = strtok( szRemaining, szDelimiter ); + bool bHouseField = (bool)atoi( szToken ); + + if( bHouseField ) + { + // Read house. + szToken = strtok( NULL, szDelimiter ); + HousesType House = (HousesType)atoi( szToken ); + SetPlayerHouse( szPlayerName, House ); + // SetPlayerHouse may call strtok, so we can't use the strtok( NULL, option... in the next call. + szRemaining += 5; + } + else + szRemaining += 2; // Advance past "0 ". + +// Acceptedness must be false. No need for it in message. +// // Read acceptedness. +// szToken = strtok( NULL, szDelimiter ); +// bool bAccepted = (bool)atoi( szToken ); +// if( bAccepted ) +// SetPlayerAccepted( szPlayerName, true ); + } + + szToken = strtok( NULL, szDelimiter ); +// if( szToken ) +// debugprint( "szToken should be NULL!!!!!!!!\n" ); + + ClearAllAccepts(); // Most likely a pointless precaution. +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::OnGuestLeave( User* pUser ) +{ + // pUser is about to leave but is still in our player list. + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + // Host is leaving the channel. We must be a guest, and so must leave also. This will trigger exit. + strcpy( szNameOfHostWhoJustBailedOnUs, (char*)pUser->name ); + } + else + { + ClearAllAccepts(); + } +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::ClearAllAccepts() +{ + // Clears all "player has accepted" marks. +//debugprint( "ClearAllAccepts()\n" ); + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_CHANNELOWNER ) ) // pUser null if this is an "early insertion" entry on startup + pWO->MarkItemAccepted( i, false ); + } + + if( pToolTipHitLast && pToolTipHitLast->bShowing ) + pToolTipHitLast->Unshow(); + + if( bHost ) + { + pTextBtnAcceptStart->Disable(); + if( bWaitingToStart ) + { + // Something has happened that makes starting a game not possible now. + // Cancel out of waiting mode and tell guests to do the same. + bWaitingToStart = false; + InformAboutCancelStart(); + BindControls( true ); + ResetReadyToGo(); + display = REDRAW_ALL; + } + else + pTextBtnAcceptStart->Flag_To_Redraw(); + } + else + { + pTextBtnAcceptStart->Enable(); + pTextBtnAcceptStart->Flag_To_Redraw(); + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bAllGuestsAccept() +{ + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + if( !pWO->bItemMarkedAccepted( i ) ) + return false; + } + return true; +} + +//*********************************************************************************************** +PlayerColorType WOL_GameSetupDialog::ColorNextAvailable() +{ + // Returns the first free player color available. + + // (Totally unoptimized, but hardly ever called.) + + for( int i = 0; i < MAX_MPLAYER_COLORS; i++ ) + { + if( pILPlayers->FindColor( &ColorRemaps[ i ] ) == -1 ) + { + return (PlayerColorType)i; + } + } +// debugprint( "ColorNextAvailable is NONE!\n" ); + return PCOLOR_NONE; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::GuestIsReadyToPlay( const char* szName, const char* szReadyState ) +{ + // A guest has responded to a game start request. + SetPlayerReadyToGo( szName, szReadyState ); + + if( bAllPlayersReadyToGo() ) + { +// debugprint( "All players ready to go.\n" ); + // We can start the game. + bHostSayGo = true; // Set trigger to fire function after we're out of callback. + } +} + +//*********************************************************************************************** +bool WOL_GameSetupDialog::bNeedScenarioDownload() +{ + // Returns true if we don't have the scenario and it is allowable as a download. + if( !bHost ) + { + if( Find_Local_Scenario( Session.Options.ScenarioDescription, + Session.ScenarioFileName, + Session.ScenarioFileLength, + Session.ScenarioDigest, + Session.ScenarioIsOfficial ) ) + { +// debugprint( "bNeedScenarioDownload() returning false.\n" ); + bRequestedScenarioDownload = false; + return false; + } + else + { +/* if( !Session.ScenarioIsOfficial ) + { + bRequestedScenarioDownload = true; + return true; + } + else + { +// debugprint( "bNeedScenarioDownload fatal\n" ); + Fatal( "" ); + } +*/ +// debugprint( "Requesting download\n" ); // ajw Shouldn't be happening with am maps when i have am...? + bRequestedScenarioDownload = true; // All maps are downloadable. + return true; + } + } + + bRequestedScenarioDownload = false; + return false; +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::HostSaysGo() +{ + bHostSayGo = false; + bHostWaitingForGoTrigger = true; + +// debugprint( "HostSaysGo()\n" ); + + if( !pWO->RequestGameStart() ) + { +// debugprint( "Call to RequestGameStart() failed.\n" ); + //Fatal( "Call to RequestGameStart() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + + // Tell guests to start game. +// debugprint( "Telling guests to start game.\n" ); + + // Create WOL_GAMEOPT_INFGO message. + // This contains the color for each player, which can change about haphazardly at the end of setup, + // without causing "unacceptedness". This means that the colors everyone thinks everyone else is might not + // be sync'ed. Host sets everyone straight here. + char szSend[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 50 ] = ""; + sprintf( szSend, "%02u", WOL_GAMEOPT_INFGO ); + + User* pUser = pWO->pChatSink->pGameUserList; + while( pUser ) + { + char szUser[ WOL_NAME_LEN_MAX + 10 ]; + PlayerColorType Color = GetPlayerColor( (char*)pUser->name ); + sprintf( szUser, " %s %02u", (char*)pUser->name, Color ); // What if player left just now, and got removed from list. Ok to continue and fail on game start? + strcat( szSend, szUser ); + pUser = pUser->next; + } + if( !pWO->SendGo( szSend ) ) + { +// debugprint( "Call to SendGo() failed.\n" ); + //Fatal( "Call to SendGo() failed.\n" ); + pWO->bSelfDestruct = true; + return; + } + +/* ...Method changed. It appears that my channelleave may appear to guests before the "go" privategameopt. + For this reason, I'll wait until I receive a copy of the "go" message sent to myself, before proceeding. + + pWO->pChat->PumpMessages(); // Flush the send out. + // (ajw - Note: An apparent bug in wolapi means that this pump does not necessarily flush the go gameopts. + // This is ok in practice, because the host hits pumps later that will flush them.) + + // Pretend we just received szSend, and processed it like a guest would. + char* szFakeGoMessage = szSend + 3; + + TriggerGameStart( szFakeGoMessage ); +*/ +} + +//*********************************************************************************************** +void WOL_GameSetupDialog::TriggerGameStart( char* szGoMessage ) +{ + // Last function before dialog is exited for game start. (Which must occur now.) + // Host or guest is about to start a game using final data in szGoMessage. + +// debugprint( "TriggerGameStart( %s )\n", szGoMessage ); + + // If we are in a modal dialog, we must have arrived here through Call_Back()'s PumpMessages. Set global that will + // force a cancel out of the dialog. + if( pWO->bPump_In_Call_Back ) + cancel_current_msgbox = true; + + bHostWaitingForGoTrigger = false; + bExitForGameTrigger = true; + + // The following is based on Read_Game_Options()... + +// WWGetPrivateProfileString("Options", "Handle", "Noname", Session.Handle, sizeof(Session.Handle), buffer); + + strcpy( Session.Handle, pWO->szMyName ); + + // GameName will be the host's name... + strcpy( Session.GameName, pWO->pGameHostName() ); +// debugprint( "Session.GameName is %s\n", Session.GameName ); + +// gotit Session.ColorIdx = (PlayerColorType) WWGetPrivateProfileInt("Options", "Color", 0, buffer); +// gotit Session.PrefColor = Session.ColorIdx; +// gotit int temp = WWGetPrivateProfileInt("Options", "Side", 0, buffer); +// gotit Session.House = (HousesType) ((int)HOUSE_USSR + temp); + +// gotit Session.Options.Credits = WWGetPrivateProfileInt("Options", "Credits", 0, buffer); +// gotit Session.Options.Bases = WWGetPrivateProfileInt("Options", "Bases", 0, buffer); +// gotit Session.Options.Tiberium = WWGetPrivateProfileInt("Options", "Tiberium", 0, buffer); +// gotit Session.Options.Goodies = WWGetPrivateProfileInt("Options", "Crates", 0, buffer); +// gotit Special.IsShadowGrow = WWGetPrivateProfileInt ("Options", "Shadow", 0, buffer); +// gotit BuildLevel = WWGetPrivateProfileInt("Options", "BuildLevel", 0, buffer); +// gotit Session.Options.UnitCount = WWGetPrivateProfileInt("Options", "UnitCount", 0, buffer); +// gotit Seed = WWGetPrivateProfileInt("Options", "Seed", 0, buffer); +// gotit Special.IsCaptureTheFlag = WWGetPrivateProfileInt("Options", "CapFlag", 0, buffer); + +// UnitBuildPenalty = WWGetPrivateProfileInt ("Options", "BuildRate", 100, buffer); + if( bSlowUnitBuildRate ) + UnitBuildPenalty = 250; + else + UnitBuildPenalty = 100; + + //PlanetWestwoodGameID = WWGetPrivateProfileInt("Internet", "GameID", 0, buffer); + PlanetWestwoodGameID = pWO->pChatSink->iGameID; + + // Reset ChatSink's iGameID. + pWO->pChatSink->iGameID = 0; + + //PlanetWestwoodStartTime = WWGetPrivateProfileInt ("Internet", "StartTime", 0, buffer); + PlanetWestwoodStartTime = time( NULL ); + //WChatHWND = (HWND) WWGetPrivateProfileInt("Internet", "HWND", (int)FindWindow("OWL_Window", "Westwood Chat"), buffer); + +// gotit Session.Options.AIPlayers = WWGetPrivateProfileInt("Options", "AI", 0, buffer); //Number of AI players + if (Session.Options.AIPlayers){ + Session.Options.Ghosts = 1; + } + + if (Session.Options.Tiberium) { + Special.IsTGrowth = 1; + Special.IsTSpread = 1; + } else { + Special.IsTGrowth = 0; + Special.IsTSpread = 0; + } + + // The preceding was based on Read_Game_Options()... + + + // Now do whatever we've left out that the horrific Net_Fake_New_Dialog() and Net_Fake_Join_Dialog() used to do for us... + + // Set up the Session.Players list. + // I think there is dependence on the local player being first, so put him there. + // Else put them in order listed in the szGoMessage. + // I will set "ID" based on a player's color, though it seems unclear if this is even used in the game, or what it should be. + + Clear_Vector( &Session.Players ); + + // Make the pILPlayers a valid list of players in the game. + // Players might (incredibly rarely) have joined in the last split-second, and we only want the players listed in + // the szGoMessage. To test for whether they're in this list, first wipe the colors from all list items. + // Then we fill them in from info in szGoMessage. + // We can ignore any list items then that have no color assigned. + // Also, we'll know that the colors assigned to valid players indeed match up with what every other client has. + // Remember, all other data should already be sync'ed because it has been implemented in such a way that changes would + // cause "unacceptedness" of guests to occur. + + // Clear colors in list. + for( int iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + pILPlayers->Set_Item_Color( iItem, &ColorRemaps[ PCOLOR_NONE ] ); + + // Parse szGoMessage to iterate through players. + char szDelimiter[] = " "; + char* szToken; + char szPlayerName[ WOL_NAME_LEN_MAX ]; + + szToken = strtok( szGoMessage, szDelimiter ); + + while( szToken ) + { + strcpy( szPlayerName, szToken ); + szToken = strtok( NULL, szDelimiter ); + + PlayerColorType Color = (PlayerColorType)atoi( szToken ); + SetPlayerColor( szPlayerName, Color ); // ajw note: inserts if not found. + szToken = strtok( NULL, szDelimiter ); + } + + // Add myself to Session.Players list. + _ASSERTE( pILPlayers->Find( pWO->szMyName ) != -1 ); + + NodeNameType* pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, pWO->szMyName ); // "Name" is 12 chars max. + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( pWO->szMyName ); +//debugprint( "ME: pPlayerNew->Player.House = %i\n", pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( pWO->szMyName ); +// This gets done later. +// pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + char szHostName[ WOL_NAME_LEN_MAX ] = "Game host"; + + // Add all other players to Session.Players list (if they have a valid color - see just above). + // Also in this step - build the scenario download requests array (used by hosts only). + memset( Session.ScenarioRequests, 0, sizeof( Session.ScenarioRequests ) ); + Session.RequestCount = 0; + for( iItem = 0; iItem < pILPlayers->Count(); iItem++ ) + { + // The following is not very efficient, but doesn't have to be. Better in this case to keep it clear and simple. + pWO->PullPlayerName_Into_From( szPlayerName, pILPlayers->Get_Item( iItem ) ); + if( strcmp( szPlayerName, pWO->szMyName ) != 0 && GetPlayerColor( szPlayerName ) != PCOLOR_NONE ) + { +// debugprint( "Creating player node '%s'\n", szPlayerName ); + pPlayerNew = new NodeNameType; + strcpy( pPlayerNew->Name, szPlayerName ); + // Get player's IP address from pChatSink... + unsigned long lAddress = ( pWO->pChatSink->GetPlayerGameIP( szPlayerName ) ); //ntohl( +// debugprint( "IP address is %i, or 0x%x\n", lAddress, lAddress ); + if( pWO->GameInfoCurrent.bTournament ) + { + // This is a tournament game, and I therefore have only one opponent: this one. + // for convenience, save a copy of his IP address in case I need it later for disconnect pinging. + pWO->TournamentOpponentIP = lAddress; + pWO->bDisconnectPingingCompleted = false; + } + NetNumType net; + NetNodeType node; + memset( net, 0, 4 ); + memset( node, 0, 6 ); + memcpy( node, &lAddress, 4 ); + //memcpy( node + 2, &lAddress, 4 ); + pPlayerNew->Address.Set_Address( net, node ); + //pPlayerNew->Address = Session.GAddress; + pPlayerNew->Player.House = GetPlayerHouse( szPlayerName ); +//debugprint( "Player %i: pPlayerNew->Player.House = %i\n", iItem, pPlayerNew->Player.House ); + pPlayerNew->Player.Color = GetPlayerColor( szPlayerName ); + // This gets done later. + //pPlayerNew->Player.ID = (HousesType)( pPlayerNew->Player.Color + HOUSE_MULTI1 ); + + Session.Players.Add( pPlayerNew ); + + // If player is the game host, set HostAddress. This global is used when downloading scenarios; who knows where else. + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( iItem ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + { + Session.HostAddress = pPlayerNew->Address; + strcpy( szHostName, (char*)pUser->name ); +/* + // debugging + NetNumType netxxx; + NetNodeType nodexxx; + Session.HostAddress.Get_Address( netxxx, nodexxx ); +// debugprint( "Host, ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); +*/ + } +/* + else + { + NetNumType netxxx; + NetNodeType nodexxx; + pPlayerNew->Address.Get_Address( netxxx, nodexxx ); +// debugprint( "Player ip %i.%i.%i.%i.%i.%i\n", nodexxx[0], nodexxx[1], nodexxx[2], nodexxx[3], nodexxx[4], nodexxx[5] ); + } +*/ + + if( bHost && pWO->bItemMarkedNeedScenario( iItem ) ) + { +// debugprint( "%s has requested scenario download.\n", szPlayerName ); + Session.ScenarioRequests[ Session.RequestCount++ ] = Session.Players.Count() - 1; + } + } +// else +// debugprint( "%s excluded from Session.Players\n", szPlayerName ); + } + + + // From Init... +// debugprint( "About to call Open_Socket().\n"); + PacketTransport->Open_Socket( 0 ); + +// debugprint( "RA95 - About to call Start_Listening.\n" ); + PacketTransport->Start_Listening(); + + /* + ** Flush out any pending packets from a previous game. + */ + PacketTransport->Discard_In_Buffers(); + PacketTransport->Discard_Out_Buffers(); + + WWDebugString ("RA95 - About to call Init_Network.\n"); + Init_Network(); + + Ipx.Set_Timing ( 30, // retry 2 times per second + -1, // ignore max retries + 600); // give up after 10 seconds + +// debugprint( "Session.ScenarioFileName is %s.\n", Session.ScenarioFileName ); + + + /* + ** Read the scenario name from the .INI and try to match it with a scenario file in our list. + */ +// gotit WWGetPrivateProfileString("Options", "Scenario", "SCM01EA.INI", +// Session.Options.ScenarioDescription, +// sizeof (Session.Options.ScenarioDescription), +// buffer); + //WWDebugString ("RA95I - Scenario is "); + //WWDebugString (Session.Options.ScenarioDescription); + //WWDebugString ("\n"); + + if( !bHost ) // Else ScenarioIndex is already set. + { + if( bRequestedScenarioDownload ) + { + Session.Options.ScenarioIndex = 1; + if( bSpecialAftermathScenario( Session.Options.ScenarioDescription ) ) + { + // Shouldn't ever happen. We should never have the opportunity to ask for one of these maps to be downloaded. + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + // Wait for download from game host. +//debugprint( "Wait for download from game host.\n" ); + if( !Get_Scenario_File_From_Host( Session.ScenarioFileName, 1 ) ) + { +// debugprint( "Get_Scenario_File_From_Host failed!\n" ); + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + // Trigger the "our host just left the channel" code... + strcpy( szNameOfHostWhoJustBailedOnUs, szHostName ); + return; + } + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.ScenarioFileName ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + else + { + // Match ScenarioDescription to a ScenarioIndex. +/* This is how the same code existed previously. Insufficient because Description may match on many scenarios. + Session.Options.ScenarioIndex = -1; + for (int i = 0; i < Session.Scenarios.Count(); i++) { + if (!strcmp (Session.Scenarios[i]->Description(), Session.Options.ScenarioDescription) ){ + Session.Options.ScenarioIndex = i; + break; + } + } +*/ + // (We have already done the lookup, in Find_Local_Scenario, above.) + Session.Options.ScenarioIndex = ScenarioIndex_From_Filename( Session.ScenarioFileName ); + _ASSERTE( Session.Options.ScenarioIndex != -1 ); + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + } + } + else // bHost + { + Scen.Scenario = Session.Options.ScenarioIndex; +// debugprint( "Scen.Scenario = %i\n", Scen.Scenario ); + strcpy( Scen.ScenarioName, Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Filename() ); +// debugprint( "Scen.ScenarioName = %s\n", Scen.ScenarioName ); + strcpy( Session.Options.ScenarioDescription, (char*)Session.Scenarios[ Session.Options.ScenarioIndex ]->Description() ); + } + + Options.GameSpeed = 0; + + //Session.MaxAhead = WChatMaxAhead = WWGetPrivateProfileInt("Timing", "MaxAhead", 9, buffer); + //Session.FrameSendRate = WChatSendRate = WWGetPrivateProfileInt("Timing", "SendRate", 3, buffer); + Session.MaxAhead = 15; //9; + //Session.FrameSendRate = 5; //3; + Session.FrameSendRate = 3; //3; + + // This is from NETDLG processing... + Session.NumPlayers = Session.Players.Count(); + + pWO->GameInfoCurrent.iPlayerCount = Session.Players.Count(); + + Ipx.Set_Timing (25, (unsigned long) -1, 1000); + + + if( bHost ) + { + if( Session.Scenarios[ Session.Options.ScenarioIndex ]->Get_Official() ) + { + if( !Force_Scenario_Available( Scen.ScenarioName ) ) + { + bExitForGameTrigger = false; + *szTriggerGameStartInfo = 0; + pWO->bSelfDestruct = true; + return; + } + } + if( Session.RequestCount ) + { + // Send the scenario to any guests that requested a download. +//debugprint( "Send the scenario to any guests that requested a download.\n" ); + Send_Remote_File( Scen.ScenarioName, 1 ); + } + } + + Session.CommProtocol = COMM_PROTOCOL_MULTI_E_COMP; + Ipx.Set_Timing (30, (unsigned long) -1, 600); + + pWO->bEnableNewAftermathUnits = bAftermathUnits; + bAftermathMultiplayer = bAftermathUnits; + + *pWO->szExternalPager = 0; + + pWO->bDoingDisconnectPinging = false; // Pointlessly making sure. + + *szTriggerGameStartInfo = 0; // This was set in order to trigger my coming here. +} + +#endif + +#include +//*********************************************************************************************** +bool bSpecialAftermathScenario( const char* szScenarioDescription ) +{ + // Returns true if szScenarioDescription matches one of the descriptions for Aftermath multiplayer + // scenarios that have special Aftermath-only units *embedded* within them. + if( strcmp( szScenarioDescription, "Booby Traps (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Central Conflict Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Circles of Death (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Holy Grounds (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "Island Wars Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "King of the Hills Extreme (Mega 8 players)" ) == 0 || + strcmp( szScenarioDescription, "The Hills Have Eyes (Mega 8 players)" ) == 0 ) + return true; + return false; +} + +//*********************************************************************************************** +int ScenarioIndex_From_Filename( const char* szScenarioFilename ) +{ +#if (0)//PG + // Returns the scenario index that matches the scenario filename, or -1 if no match found. + for( int index = 0; index < Session.Scenarios.Count(); index++ ) + { + if( _stricmp( szScenarioFilename, Session.Scenarios[index]->Get_Filename() ) == 0 ) + return index; + } +#endif + return -1; +} diff --git a/code/redalert/wol_gsup.h b/code/redalert/wol_gsup.h new file mode 100644 index 0000000..633c804 --- /dev/null +++ b/code/redalert/wol_gsup.h @@ -0,0 +1,361 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// wol_gsup.h "WOL Game Setup Dialog" +// ajw 08/06/98 + +// Class WOL_GameSetupDialog is a move away from doing dialogs purely in C, a format that I've maintained in the login and +// chat dialogs, mimicking how dialogs are done elsewhere in the code, but became frustrated with. +// Though I'll follow the same pattern as before, things will be centralized a little cleaner through the use of this object. +// Why a standard dialog class that would handle common input behavior and so forth (one that this class could derive from) +// wasn't written 5 years ago, I don't know... + +#include "WolapiOb.h" +//class WolapiObject; +class IconListClass; +class EditClass; +class GaugeClass; +class CheckListClass; +class TextButtonClass; +class StaticButtonClass; +class DropListClass; +class ShapeButtonClass; +class BigCheckBoxClass; +class ToolTipClass; + +//*********************************************************************************************** +enum RESULT_WOLGSUP +{ + RESULT_WOLGSUP_BACKTOCHAT, + RESULT_WOLGSUP_FATALERROR, + RESULT_WOLGSUP_HOSTLEFT, + RESULT_WOLGSUP_STARTGAMEHOST, + RESULT_WOLGSUP_STARTGAME, + RESULT_WOLGSUP_RULESMISMATCH, + RESULT_WOLGSUP_LOGOUT, +}; + +struct GAMEPARAMS +{ + GlobalPacketType GPacket; + // My additions to game params. - ajw + bool bAftermathUnits; + bool bSlowUnitBuildRate; +}; + +//*********************************************************************************************** +class WOL_GameSetupDialog +{ +public: + WOL_GameSetupDialog( WolapiObject* pWO, bool bHost ); + ~WOL_GameSetupDialog(); + + RESULT_WOLGSUP Run(); + +public: + bool bHost; // True when I created the game channel and am the host. + + bool bHostSayGo; // Trigger host instructing all to start game immediately. + bool bHostWaitingForGoTrigger; // True while host is waiting for go message to bounce back to him and trigger start. + bool bExitForGameTrigger; // Trigger exiting dialog for game. + + + void ProcessGuestRequest( User* pUser, const char* szRequest ); + void ProcessInform( char* szRequest ); + void OnGuestJoin( User* pUser ); + void OnGuestLeave( User* pUser ); + +protected: + void Initialize(); + RESULT_WOLGSUP Show(); + void SetSpecialControlStates(); + void BindControls( bool bBind ); + bool ExitGameChannel(); + void DrawScenarioDescripIcon( const char* pDIB ) const; + void SetPlayerColor( const char* szName, PlayerColorType Color ); + PlayerColorType GetPlayerColor( const char* szName ); + void SetPlayerHouse( const char* szName, HousesType House ); + HousesType GetPlayerHouse( const char* szName ); + bool SetPlayerAccepted( const char* szName, bool bAccepted ); + bool IveAccepted(); + bool SetPlayerReadyToGo( const char* szName, const char* szReadyState ); + void ResetReadyToGo(); + bool bPlayerReadyToGo( const char* szName ); + bool bAllPlayersReadyToGo(); + + bool bParamsUnfresh(); + void SendParams(); + bool AcceptParams( char* szParams ); + void SetGParamsToCurrent( GAMEPARAMS& GParams ); + + void AcceptNewGuestPlayerInfo( char* szMsg ); + + bool RequestPlayerColor( PlayerColorType Color ); + bool InformAboutPlayerColor( const char* szName, PlayerColorType Color, User* pUserPriv ); + bool InformAboutPlayerHouse( const char* szName, HousesType House, User* pUserPriv ); + bool InformAboutPlayerAccept( const char* szName, User* pUserPriv ); + bool InformAboutStart(); + bool InformAboutCancelStart(); + + void ClearAllAccepts(); + bool bAllGuestsAccept(); + + PlayerColorType ColorNextAvailable(); + + void GuestIsReadyToPlay( const char* szName, const char* szReadyState ); + bool bNeedScenarioDownload(); + void HostSaysGo(); + void TriggerGameStart( char* szGoMessage ); + + enum SCENARIO_GAMEKIND + { + SCENARIO_RA = 0, + SCENARIO_CS, + SCENARIO_AM, + SCENARIO_USER, + SCENARIO_UNINITIALIZED, + }; + void ScenarioDisplayMode( SCENARIO_GAMEKIND ScenKind ); + +// bool bSpecialAftermathScenario( const char* szScenarioDescription ); + +public: + int d_dialog_w; + int d_dialog_h; + int d_dialog_x; + int d_dialog_y; + int d_dialog_cx; + + int d_txt6_h; + int d_margin1; + + int d_house_w; + int d_house_h; + int d_house_x; + int d_house_y; + + int d_color_w; + int d_color_h; + int d_color_x; + int d_color_y; + + int d_playerlist_w; + int d_playerlist_h; + int d_playerlist_x; + int d_playerlist_y; + + int d_scenariolist_w; + int d_scenariolist_h; + int d_scenariolist_x; + int d_scenariolist_y; + + int d_gamekind_w; + int d_gamekind_h; + int d_gamekind_x; + int d_gamekind_y; + + int d_count_w; + int d_count_h; + int d_count_x; + int d_count_y; + + int d_level_w; + int d_level_h; + int d_level_x; + int d_level_y; + + int d_credits_w; + int d_credits_h; + int d_credits_x; + int d_credits_y; + + int d_aiplayers_w; + int d_aiplayers_h; + int d_aiplayers_x; + int d_aiplayers_y; + + int d_options_w; + int d_options_h; + int d_options_x; + int d_options_y; + + int d_disc_w; + int d_disc_h; + int d_disc_x; + int d_disc_y; + + int d_send_w; + int d_send_h; + int d_send_x; + int d_send_y; + + int d_ok_w; + int d_ok_h; + int d_ok_x; + int d_ok_y; + + int d_cancel_w; + int d_cancel_h; + int d_cancel_x; + int d_cancel_y; + + int d_accept_w; + int d_accept_h; + int d_accept_x; + int d_accept_y; + + int d_amunits_w; + int d_amunits_h; + int d_amunits_x; + int d_amunits_y; + + int d_action_w; + int d_action_h; + int d_action_x; + int d_action_y; + +protected: + GadgetClass* commands; // The controls list. + + IconListClass* pILPlayers; + IconListClass* pILScens; + IconListClass* pILDisc; + char szSendBuffer[ MAXCHATSENDLENGTH ]; + EditClass* pEditSend; + GaugeClass* pGaugeCount; + GaugeClass* pGaugeLevel; + GaugeClass* pGaugeCredits; + GaugeClass* pGaugeAIPlayers; + CheckListClass* pCheckListOptions; + TextButtonClass* pTextBtnOk; + TextButtonClass* pTextBtnCancel; + TextButtonClass* pTextBtnAcceptStart; + TextButtonClass* pTextBtnAction; + StaticButtonClass* pStaticDescrip; + StaticButtonClass* pStaticUnit; + StaticButtonClass* pStaticLevel; + StaticButtonClass* pStaticCredits; + StaticButtonClass* pStaticAIPlayers; + char szHouseBuffer[25]; // buffer for house droplist + DropListClass* pDropListHouse; + BigCheckBoxClass* pCheckAftermathUnits; + ShapeButtonClass* pShpBtnScenarioRA; + ShapeButtonClass* pShpBtnScenarioCS; + ShapeButtonClass* pShpBtnScenarioAM; + ShapeButtonClass* pShpBtnScenarioUser; + + ToolTipClass* pTTipAcceptStart; + ToolTipClass* pTTipCancel; + ToolTipClass* pTTipAction; + + WolapiObject* pWO; + + + GAMEPARAMS GParamsLastSent; // Used merely as a handy container for the vars I need to set. + + DWORD dwTimeNextParamRefresh; // Param changes are sent by host at certain interval. + + HousesType HousePrevious; + + unsigned int nHostLastParamID; // Host's send update tracking packet ID. + unsigned int nGuestLastParamID; // Guest's record of last ID received from host. + + bool bWaitingToStart; + + bool bProcess; // True means continue doing input loop. + RESULT_WOLGSUP ResultReturn; // Value that will be returned from Show(). + char szNameOfHostWhoJustBailedOnUs[ WOL_NAME_LEN_MAX ]; // If set, triggers setup cancellation. + + bool bParamsReceived; // True after any WOL_GAMEOPT_INFPARAMS messages have been received from a host. + + bool bLeaveDueToRulesMismatchTrigger; + + bool bRequestedScenarioDownload; + + char szTriggerGameStartInfo[ ( WOL_NAME_LEN_MAX + 10 ) * 4 + 60 ]; + + // Tooltips... + DWORD timeToolTipAppear; + ToolTipClass* pToolTipHead; // Head of list of ToolTips that parallels gadget list. + ToolTipClass* pToolTipHitLast; // ToolTip the mouse was last over, or null. + + // Extra game params... + bool bAftermathUnits; // True if aftermath units are to be used in the game. + bool bSlowUnitBuildRate; + + SCENARIO_GAMEKIND ScenKindCurrent; // Describes what gamekind of scenarios we are viewing, if host. + DynamicVectorClass< const char* > ar_szScenarios[ 4 ]; // Lists of scenarios, by SCENARIO_GAMEKIND. + // ar_szScenIndexes parallels ar_szScenarios, holds ScenarioIndex. It's actually an int, but I'm using void* to avoid + // template instantiation problems and the need to change defines.h. + DynamicVectorClass< void* > ar_szScenIndexes[ 4 ]; + + //------------------------------------------------------------------------ + // Button Enumerations + //------------------------------------------------------------------------ + enum + { + BUTTON_DISCONNECT = 100, // Note: standard WOL button IDs must match values in WolapiObject::PrepareButtonsAndIcons(). + BUTTON_LEAVE, + BUTTON_REFRESH, + BUTTON_SQUELCH, + BUTTON_BAN, + BUTTON_KICK, + BUTTON_FINDPAGE, + BUTTON_OPTIONS, + BUTTON_LADDER, + BUTTON_HELP, + + BUTTON_PLAYERLIST, + BUTTON_HOUSE, + BUTTON_SCENARIOLIST, + BUTTON_DISCLIST, + BUTTON_SENDEDIT, + BUTTON_COUNT, + BUTTON_LEVEL, + BUTTON_CREDITS, + BUTTON_AIPLAYERS, + BUTTON_PARAMS, +// BUTTON_OK, + BUTTON_CANCEL, + BUTTON_ACCEPTSTART, + BUTTON_ACTION, + BUTTON_AFTERMATHUNITS, + BUTTON_SCENARIO_RA, + BUTTON_SCENARIO_CS, + BUTTON_SCENARIO_AM, + BUTTON_SCENARIO_USER, + }; + + //------------------------------------------------------------------------ + // Redraw values: in order from "top" to "bottom" layer of the dialog + //------------------------------------------------------------------------ + typedef enum { + REDRAW_NONE = 0, + REDRAW_PARMS, + REDRAW_MESSAGE, + REDRAW_COLORS, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + RedrawType display; + +}; + +#endif + diff --git a/code/redalert/wol_logn.cpp b/code/redalert/wol_logn.cpp new file mode 100644 index 0000000..f53f049 --- /dev/null +++ b/code/redalert/wol_logn.cpp @@ -0,0 +1,662 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Logn.cpp - WW online name/password dialog. +// ajw 07/16/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "PassEdit.h" +#include "WolStrng.h" +#include "BigCheck.h" + +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ); +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ); +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ); +//char* LoadShpFile( const char* szShpFile ); + +void DebugChatDef( HRESULT hRes ); + +extern bool bTabKeyPressedHack; + +//#include "WolDebug.h" + +//*********************************************************************************************** +int WOL_Login_Dialog( WolapiObject* pWO ) +{ + // Return values: 0 = user cancels, 1 = success, -1 = force game exit + + if( pWO->bLoggedIn() ) + { + pWO->bReturningAfterGame = true; // Set trigger for chat dialog. + return 1; // We are already logged in, and have just come back from a game. + } + + /* + ** Dialog & button dimensions + */ +#ifdef FRENCH + int d_dialog_w = 160 * RESFACTOR; // dialog width +#else + int d_dialog_w = 150 * RESFACTOR; // dialog width +#endif + int d_dialog_h = 85 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((255 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + + int d_name_w = 66 * RESFACTOR; + int d_name_h = 10 * RESFACTOR; +#ifdef FRENCH + int d_name_x = d_dialog_x + 25 * RESFACTOR; +#else + int d_name_x = d_dialog_x + 20 * RESFACTOR; +#endif + int d_name_y = d_dialog_y + top_margin + 25 * RESFACTOR; + + int d_pass_w = 36 * RESFACTOR; + int d_pass_h = d_name_h; + int d_pass_x = d_name_x + d_name_w + 6 * RESFACTOR; + int d_pass_y = d_name_y; + + int d_list_w = d_name_w; + int d_list_h = 20 * RESFACTOR; + int d_list_x = d_name_x; + int d_list_y = d_dialog_y + top_margin + 40 * RESFACTOR; + +// int d_save_w = d_pass_w; + int d_save_h = 9 * RESFACTOR; + int d_save_x = d_pass_x + ( d_pass_w / 2 ) - ( d_pass_w / 2 ); + int d_save_y = d_list_y; // + ( d_list_h / 2 ) - ( d_save_h / 2 ); + + int d_delete_w = d_pass_w; + int d_delete_h = 10 * RESFACTOR; + int d_delete_x = d_save_x; + int d_delete_y = d_list_y + d_list_h - d_delete_h; + +#ifdef FRENCH + int d_connect_w = 45 * RESFACTOR; +#else + int d_connect_w = 40 * RESFACTOR; +#endif + int d_connect_h = 13 * RESFACTOR; + int d_connect_x = d_name_x + d_name_w/2 - d_connect_w/2; + int d_connect_y = d_dialog_y + top_margin + 65 * RESFACTOR; //d_dialog_y + d_dialog_h - d_connect_h - d_margin; + +#if defined(GERMAN) || defined(FRENCH) + int d_cancel_w = 40 * RESFACTOR;//BG:40 +#else + int d_cancel_w = 40 * RESFACTOR; +#endif + int d_cancel_h = 13 * RESFACTOR; + int d_cancel_x = d_pass_x + d_pass_w/2 - d_cancel_w/2; //d_dialog_cx + d_margin; + int d_cancel_y = d_connect_y; + + /* + ** Button enumerations + */ + enum { + BUTTON_CONNECT = 100, + BUTTON_CANCEL, + LISTBOX_NICKS, + EDITBOX_NAME, + EDITBOX_PASS, + BUTTON_SAVECHECK, + BUTTON_DELETE, + }; + + /* + ** Redraw values: in order from "top" to "bottom" layer of the dialog + */ + typedef enum { + REDRAW_NONE = 0, + REDRAW_BUTTONS, + REDRAW_BACKGROUND, + REDRAW_ALL = REDRAW_BACKGROUND + } RedrawType; + + /* + ** Dialog variables + */ + int iReturn = 1; // 0 = user cancels, 1 = success, -1 = force game exit + + /* + ** Other Variables + */ + char szNameBuffer[ WOL_NAME_LEN_MAX ] = {0}; // User name. + char szPassBuffer[ WOL_PASSWORD_LEN ] = {0}; // User password. + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass ConnectBtn( BUTTON_CONNECT, TXT_WOL_CONNECT, TPF_BUTTON, d_connect_x, d_connect_y, d_connect_w ); + TextButtonClass CancelBtn( BUTTON_CANCEL, TXT_CANCEL, TPF_BUTTON, d_cancel_x, d_cancel_y, d_cancel_w ); + + IconListClass NickList( LISTBOX_NICKS, d_list_x, d_list_y, d_list_w, d_list_h, TPF_6PT_GRAD | TPF_NOSHADOW, + MFCD::Retrieve("BTN-UP.SHP"), MFCD::Retrieve("BTN-DN.SHP"), true, 1, 0 ); + + WOLEditClass NameEdit( EDITBOX_NAME, szNameBuffer, sizeof(szNameBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_name_x, d_name_y, d_name_w, -1, EditClass::ALPHANUMERIC ); + + PassEditClass PassEdit( EDITBOX_PASS, szPassBuffer, sizeof(szPassBuffer), TPF_6PT_GRAD|TPF_NOSHADOW, + d_pass_x, d_pass_y, d_pass_w, -1, EditClass::ALPHANUMERIC ); + + // Just making sure globals are set right before String_Pixel_Width() call... sigh + Fancy_Text_Print( TXT_NONE, 0, 0, GadgetClass::Get_Color_Scheme(), TBLACK, TPF_6PT_GRAD | TPF_NOSHADOW ); + int iSaveTextWidth = String_Pixel_Width( TXT_WOL_SAVELOGIN ) + BIGCHECK_OFFSETX; + BigCheckBoxClass SaveCheckBox( BUTTON_SAVECHECK, d_save_x, d_save_y, iSaveTextWidth, d_save_h, + TXT_WOL_SAVELOGIN, TPF_6PT_GRAD | TPF_NOSHADOW, true ); + + TextButtonClass DeleteBtn( BUTTON_DELETE, TXT_DELETE_BUTTON, TPF_BUTTON, d_delete_x, d_delete_y, d_delete_w ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + // Get saved nickname/passwords from the registry. + if( ReadSavedNicks( pWO, NickList, szNameBuffer, szPassBuffer ) ) + { + PassEdit.bClearOnNextSetFocus = true; + } + else + { + // Offer user the chance to go to web site now to get a nick. + if( pWO->DoWebRegistration() ) + { + // User chose to go to web page. Leave function so that we'll re-read nicks when they return. + return 0; + } + } + + /* + ** Create the button list. + */ + commands = &ConnectBtn; + CancelBtn.Add_Tail(*commands); + NickList.Add_Tail(*commands); + NameEdit.Add_Tail(*commands); + PassEdit.Add_Tail(*commands); + SaveCheckBox.Add_Tail(*commands); + DeleteBtn.Add_Tail(*commands); + NameEdit.Set_Focus(); + + if( NickList.Count() == 0 ) + DeleteBtn.Disable(); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool firsttime = true; + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + Call_Back(); + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + //------------------------------------------------------------------------ + // Clear screen + //------------------------------------------------------------------------ + Hide_Mouse(); + Load_Title_Page(true); +// Show_Mouse(); + + /* + ** Display the dialog box. + */ +// Hide_Mouse(); + if (display) { + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption(TXT_WOL_LOGINDIALOG, d_dialog_x, d_dialog_y, d_dialog_w); + } + + /* + ** Redraw the buttons. + */ + if (display) { + Fancy_Text_Print( TXT_WOL_NAME, d_name_x + ( d_name_w / 2 ), d_name_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + Fancy_Text_Print( TXT_WOL_PASSWORD, d_pass_x + ( d_pass_w / 2 ), d_pass_y - 14, + GadgetClass::Get_Color_Scheme(), TBLACK, TPF_TEXT | TPF_CENTER ); + commands->Flag_List_To_Redraw(); + } + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + bTabKeyPressedHack = false; + KeyNumType input = commands->Input(); + + /* + ** The first time through the processing loop, set the edit + ** gadget to have the focus. The + ** focus must be set here since the gadget list has changed + ** and this change will cause any previous focus setting to be + ** cleared by the input processing routine. + */ + if (firsttime ) { + firsttime = false; + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + } + +// /* +// ** If the key was pressed, then default to the appropriate +// ** action button according to the style of this dialog box. +// */ +/* if (input == KN_RETURN || input == (BUTTON_CONNECT|KN_BUTTON)) { + ToggleClass * toggle = NULL; + input = (KeyNumType)(BUTTON_CONNECT|KN_BUTTON); + CancelBtn.Turn_Off(); + toggle = (ToggleClass*)commands->Extract_Gadget(BUTTON_CONNECT); + if (toggle != NULL) { + toggle->Turn_On(); + toggle->IsPressed = true; + } + + Hide_Mouse(); + commands->Draw_All(true); + Show_Mouse(); + } +*/ + /* + ** Process input. + */ +// if( input ) +// debugprint( "input: %i\n", input ); + + if( bTabKeyPressedHack ) + { + if( NameEdit.Has_Focus() ) + PassEdit.Set_Focus(); + else + NameEdit.Set_Focus(); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + } + + switch( input ) + { + /* + ** ESC/Cancel: break + */ + case ( KN_ESC ): + case ( BUTTON_CANCEL | KN_BUTTON ): + iReturn = 0; + process = false; + break; + + case KN_RETURN: + case ( EDITBOX_NAME | KN_BUTTON ): + case ( EDITBOX_PASS | KN_BUTTON ): + case ( BUTTON_CONNECT | KN_BUTTON ): + { + if( !strlen( szNameBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGNAME ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + if( !strlen( szPassBuffer ) ) + { + WWMessageBox().Process( TXT_WOL_MISSINGPASSWORD ); + firsttime = true; // Bloody hack. + PassEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + } + + // If we have not done RequestServerList() yet, do it now. + if( !pWO->pChatSink->pServer ) + { + bool bBreak = false; + HRESULT hRes = pWO->GetChatServer(); + switch( hRes ) + { + case E_FAIL: + bBreak = true; + WWMessageBox().Process( TXT_WOL_CANTCONNECT ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case USERCANCELLED: + bBreak = true; + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHAVOIDED: + bBreak = true; + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + break; + case PATCHDOWNLOADED: + bBreak = true; + process = false; + iReturn = -1; + break; + } + if( bBreak ) + break; + } + + // RequestConnection()... + HRESULT hRes = pWO->AttemptLogin( szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ); + if( hRes == S_OK ) + { + if( SaveCheckBox.IsOn && !bSaveNick( pWO, szNameBuffer, szPassBuffer, PassEdit.bClearOnNextSetFocus ) ) + { + // Nick/pass save failed. + WWMessageBox().Process( TXT_WOL_CANTSAVENICK ); + } + process = false; + } + else + { + switch( hRes ) + { + case USERCANCELLED: + WWMessageBox().Process( TXT_WOL_LOGINCANCEL ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case CHAT_E_BADPASS: + WWMessageBox().Process( TXT_WOL_BADPASS ); + break; + case CHAT_E_NICKINUSE: + WWMessageBox().Process( TXT_WOL_NICKINUSE ); + break; + case CHAT_E_CON_ERROR: + // This error value I pass back myself, when the emergency timeout is hit. + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + } + firsttime = true; // Bloody hack. + NameEdit.Set_Focus(); + Keyboard->Clear(); + display = true; + } + break; + } + +/* + case( EDITBOX_PASS | KN_BUTTON ): + { + // Message with delay so that user has time to read it... + CDTimerClass timer; + timer = TICKS_PER_SECOND*4; + WWMessageBox().Process(TXT_WOL_DEBUG2, TXT_NONE); + while (timer > 0) { + Call_Back(); + } + Keyboard->Clear(); + + display = true; + break; + } +*/ + case ( LISTBOX_NICKS | KN_BUTTON ): + strcpy( szNameBuffer, NickList.Get_Item( NickList.Current_Index() ) ); + strcpy( szPassBuffer, NickList.Get_Item_ExtraDataString( NickList.Current_Index() ) ); + NameEdit.Flag_To_Redraw(); + PassEdit.Flag_To_Redraw(); + // Because the password is mangled, if the user begins to edit it now, we clear it. + // Otherwise we could get a half-mangled, half-unmangled password field. + // PassEdit.bClearOnNextSetFocus also acts as a flag telling us whether or not the + // password field is mangled or not. + PassEdit.bClearOnNextSetFocus = true; +// display = true; + break; + + case ( BUTTON_SAVECHECK | KN_BUTTON ): + break; + + case ( BUTTON_DELETE | KN_BUTTON ): + if( NickList.Count() > 0 ) + { + DeleteNick( pWO, NickList.Current_Index() + 1 ); + NickList.Remove_Item( NickList.Current_Index() ); + NickList.Flag_To_Redraw(); + if( NickList.Count() == 0 ) + { + DeleteBtn.Disable(); + DeleteBtn.Flag_To_Redraw(); + } + } + break; + + default: + break; + } + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReadSavedNicks( WolapiObject* pWO, IconListClass& NickList, char* szNameBuffer, char* szPassBuffer ) +{ + // Read saved nickname/passwords from the registry. + // Set up the list of nick/passwords. + // Copy the first nick into the nick/password edits. + + // Returns true if edits are set with a default nick/pass because a nick was found. + + LPCSTR szNick; + LPCSTR szPass; + bool bReturn = false; + + for( int i = 1; i != 3; i++ ) + { + if( pWO->pChat->GetNick( i, &szNick, &szPass ) == S_OK ) + { + if( *szNick ) + { + NickList.Add_Item( szNick, NULL, NULL, ICON_SHAPE, szPass ); + if( i == 1 ) + { + strcpy( szNameBuffer, szNick ); + strcpy( szPassBuffer, szPass ); + bReturn = true; + } + } + } + } + return bReturn; +} + +//*********************************************************************************************** +bool bSaveNick( WolapiObject* pWO, const char* szNickToSave, const char* szPassToSave, bool bPassIsMangled ) +{ + // Saves specified nick and password in the registry, using SetNick. + // Returns false if nick can't be saved. + + // If slot 1 empty, use slot 1. + // Else push nick 1 down to second slot and save new nick in slot 1, unless + // nick 1 name matches new entry. + + LPCSTR szNick; + LPCSTR szPass; + bool bPushSlot1 = true; + + switch( pWO->pChat->GetNick( 1, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bPushSlot1 = false; + break; + case S_OK: + if( *szNick == 0 ) + bPushSlot1 = false; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bPushSlot1 = false; // We can use this slot as the name is the same. + break; + } + + if( bPushSlot1 ) + { + // Move nick in slot 1 to slot 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + } + + // Save new nick in slot 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + +/* + int iSlot; + bool bStop = false; + for( iSlot = 1; iSlot != 3; iSlot++ ) + { + switch( pWO->pChat->GetNick( iSlot, &szNick, &szPass ) ) + { + case E_FAIL: + // Assume that this is because there is no registry entry. We can use this slot. + bStop = true; + break; + case S_OK: + if( *szNick == 0 ) + bStop = true; // We can use this blank slot. + else + if( strcmp( szNick, szNickToSave ) == 0 ) + bStop = true; // We can use this slot as the name is the same. + break; + } + if( bStop ) + break; + } + if( iSlot == 3 ) + { + // No open slots were found. + // Get nick 1. + pWO->pChat->GetNick( 1, &szNick, &szPass ); + // Save as nick 2. + pWO->pChat->SetNick( 2, szNick, szPass, false ); // (Already mangled.) + // Save new nick 1. + return ( pWO->pChat->SetNick( 1, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } + else + { + // iSlot points to an open slot. + return ( pWO->pChat->SetNick( iSlot, szNickToSave, szPassToSave, !bPassIsMangled ) == S_OK ); + } +*/ +} + +//*********************************************************************************************** +void DeleteNick( WolapiObject* pWO, int iOneBasedEntryToDelete ) +{ + // Delete a nick from the registry via wolapi SetNick. + // If nick to delete is in position one, and there is a second nick, move the second nick into position one. + if( iOneBasedEntryToDelete == 1 ) + { + // Check for nick 2. + LPCSTR szNick; + LPCSTR szPass; + if( pWO->pChat->GetNick( 2, &szNick, &szPass ) == S_OK && *szNick != 0 ) + { + // Copy nick in slot 2 to slot 1. + pWO->pChat->SetNick( 1, szNick, szPass, false ); // (Already mangled.) + // Delete slot 2. + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } + else + { + // No second nick. + HRESULT hRes = pWO->pChat->SetNick( 1, "", "", false ); + DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pWO->pChat->SetNick( 2, "", "", false ); + DebugChatDef( hRes ); + } +} + +/* +//*********************************************************************************************** +char* LoadShpFile( const char* szShpFile ) +{ + // Returns pointer to shp data that has been new'ed (and must be delete[]d), or NULL if failure. + // ajw: No longer needed - I used this before putting new resources into a mix file. + HANDLE hFile; + hFile = CreateFile( szShpFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + DWORD dwFileSize = GetFileSize( hFile, NULL ); + char* pShp = new char[ dwFileSize ]; + DWORD dwBytesRead; + ReadFile( hFile, pShp, dwFileSize, &dwBytesRead, NULL ); +// debugprint( "~~ LoadShpFile() - Read %i bytes out of %i from shp file.\n", dwBytesRead, dwFileSize ); + CloseHandle( hFile ); + return pShp; +} +*/ + +#endif diff --git a/code/redalert/wol_main.cpp b/code/redalert/wol_main.cpp new file mode 100644 index 0000000..9557a77 --- /dev/null +++ b/code/redalert/wol_main.cpp @@ -0,0 +1,284 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Main.cpp - Bottom level wolapi-stuff function. +// ajw 07/16/98 + +#include "function.h" +#include "WolapiOb.h" +#include "wol_gsup.h" +#include "WolStrng.h" + +int WOL_Login_Dialog( WolapiObject* pWolapi ); +int WOL_Chat_Dialog( WolapiObject* pWolapi ); +const char* Game_Registry_Key(); + +bool ReregisterWolapiDLL(); +void HandleDLLFail(); + +WolapiObject* pWolapi = NULL; + +#include "WolDebug.h" + +//*********************************************************************************************** +// The first time through, pWolapi is NULL thus wolapi gets set up. WOL_Login_Dialog presents the user +// with the login dialog and attempts to log us on to the server. If the user continues on all the +// way to a game start, we will drop out of here with pWolapi still pointing to a valid WolapiObject, +// and with pWolapi's iLobbyReturnAfterGame set to the number of the lobby to return to automatically +// after the game ends. +// Init() automatically brings us here if pWolapi is non-null. +//*********************************************************************************************** +int WOL_Main() +{ + // Return values: + // 0 = cancel + // 1 = start game + // -1 = patch downloaded, shut down app + int iReturn = 0; + + if( pWolapi ) + { + // We have returned from a game started through ww online. + + // Start theme up again. + Theme.Play_Song( THEME_INTRO ); + + // Verify that we are still connected. If we aren't, kill WolapiObject and start over. + // (This will likely occur during the game, if connection is lost. Ensure that it is done here.) + pWolapi->pChat->PumpMessages(); // Causes OnNetStatus() call if no longer connected. + if( pWolapi->bConnectionDown ) + { +//debugprint( "Re-entering WOL_Main(), pWolapi->bConnectionDown is true. Deleting old WolapiObject...\n" ); + WWMessageBox().Process( TXT_WOL_WOLAPIREINIT ); + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + } + + if( !pWolapi ) + { + // Start up wolapi. + pWolapi = new WolapiObject; + if( !pWolapi->bSetupCOMStuff() ) + { + // Things are really bad if this happens. A COM call failed. + + // We first assume that their wolapi.dll failed to register during wolsetup.exe, part of the patch process. + // This happens if they have an outdated oleaut32.dll, such as the one that comes with original + // version of Windows 95. + +// debugprint( "bSetupCOMStuff failed. Attemping to reregister wolapi.dll...\n" ); + // Attempt to re-register wolapi.dll... + if( ReregisterWolapiDLL() ) + { + if( !pWolapi->bSetupCOMStuff() ) + { + // Still failed after reregistering seemed to work. + HandleDLLFail(); + return 0; + } + } + else + { + HandleDLLFail(); + return 0; + } + } + pWolapi->PrepareButtonsAndIcons(); + // Undocumented hack needed for patch downloading, per Neal. + pWolapi->pChat->SetAttributeValue( "RegPath", Game_Registry_Key() ); + // (Not that anything's really "documented".) + } + + pWolapi->bInGame = false; + + int iLoginResult = WOL_Login_Dialog( pWolapi ); + if( iLoginResult == 1 ) + { + pWolapi->SetOptionDefaults(); + bool bKeepGoing = true; + while( bKeepGoing ) + { + bool bCreator; + switch( WOL_Chat_Dialog( pWolapi ) ) + { + case -1: + bKeepGoing = false; + break; + case 1: + // User created game channel. + bCreator = true; + break; + case 2: + // User joined game channel. + bCreator = false; + break; + } + if( bKeepGoing ) + { + WOL_GameSetupDialog GSupDlg( pWolapi, bCreator ); + switch( GSupDlg.Run() ) + { + case RESULT_WOLGSUP_LOGOUT: + // User logged out. + bKeepGoing = false; + break; + case RESULT_WOLGSUP_BACKTOCHAT: + case RESULT_WOLGSUP_HOSTLEFT: + case RESULT_WOLGSUP_RULESMISMATCH: + // Return to chat. + break; + case RESULT_WOLGSUP_STARTGAMEHOST: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = true; + break; + case RESULT_WOLGSUP_STARTGAME: + // Proceed with game. + bKeepGoing = false; + iReturn = 1; + pWolapi->bGameServer = false; + break; + case RESULT_WOLGSUP_FATALERROR: +// debugprint( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); +// Fatal( "RESULT_WOLGSUP_FATALERROR from game setup dialog.\n" ); + if( pWolapi->pChatSink->bConnected ) + pWolapi->Logout(); + bKeepGoing = false; + break; + } + } + } + } + + if( iReturn != 1 ) + { + // Kill wolapi. + pWolapi->UnsetupCOMStuff(); + delete pWolapi; + pWolapi = NULL; + } + else + { + pWolapi->bInGame = true; + pWolapi->bConnectionDown = false; + } + + if( iLoginResult == -1 ) + { + WWMessageBox().Process( TXT_WOL_DOWNLOADEXITWARNING ); + iReturn = -1; + } + + return iReturn; +} + +//*********************************************************************************************** +bool ReregisterWolapiDLL() +{ + // Attempt to reregister wolapi.dll. + // Returns true if we think we succeeded. + HKEY hKey; + char szInstallPath[ _MAX_PATH ]; + if( ::RegOpenKeyEx( HKEY_LOCAL_MACHINE, "Software\\Westwood\\WOLAPI", 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwBufSize = _MAX_PATH; + if( ::RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szInstallPath, &dwBufSize ) == ERROR_SUCCESS ) + { + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szInstallPath, &wfd ); + if( handle == INVALID_HANDLE_VALUE ) + { + // File is not there. + FindClose( handle ); + ::RegCloseKey( hKey ); + return false; + } +// debugprint( "Found dll -> %s\n", szInstallPath ); + // Get the DLL to register itself. + HINSTANCE hLib = LoadLibrary( szInstallPath ); + if( !hLib ) + { +// debugprint( "LoadLibrary failed, GetLastError is %i\n", GetLastError() ); + ::RegCloseKey( hKey ); + return false; + } + FARPROC lpDllRegisterFunction = GetProcAddress( hLib, "DllRegisterServer" ); + if( !lpDllRegisterFunction ) + { + ::RegCloseKey( hKey ); + return false; + } + if( lpDllRegisterFunction() != S_OK ) + { + ::RegCloseKey( hKey ); + return false; + } + // There is a bug in wolapi.dll that makes the following delay necessary. + // Something about Neal's extra threads only getting half-way set up before they get deleted. + // (The extra threads shouldn't really be created in this case, anyway...) + ::Sleep( 1000 ); + FreeLibrary( hLib ); + FindClose( handle ); + } + else + { + ::RegCloseKey( hKey ); + return false; + } + ::RegCloseKey( hKey ); + } + else + { + return false; + } + return true; +} + +//*********************************************************************************************** +void HandleDLLFail() +{ + // The DLL failed to load. Either we failed to reregister it, or we think we succeeded at this but it + // still is not working. Show an error message and delete pWolapi. + // We show either "call tech support" or "download IE3", depending on whether oleaut32.dll looks out of date. + + char szPath[ _MAX_PATH + 1 ]; + ::GetSystemDirectory( szPath, _MAX_PATH ); + if( *szPath && szPath[ strlen( szPath ) - 1 ] != '\\' ) + strcat( szPath, "\\" ); + + strcat( szPath, "oleaut32.dll" ); + + WIN32_FIND_DATA wfd; + HANDLE handle = FindFirstFile( szPath, &wfd ); + +// debugprint( "HandleDLLFail(): filesize of oleaut32 is %i\n", wfd.nFileSizeLow ); + if( handle != INVALID_HANDLE_VALUE && wfd.nFileSizeLow <= 232720 ) + WWMessageBox().Process( TXT_WOL_DLLERROR_GETIE3 ); + else + WWMessageBox().Process( TXT_WOL_DLLERROR_CALLUS ); + + FindClose( handle ); + + delete pWolapi; + pWolapi = NULL; +} + +#endif diff --git a/code/redalert/wol_opt.cpp b/code/redalert/wol_opt.cpp new file mode 100644 index 0000000..04bf76c --- /dev/null +++ b/code/redalert/wol_opt.cpp @@ -0,0 +1,254 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// Wol_Opt.cpp - WW online options dialog. +// ajw 09/1/98 + +#include "function.h" + +#include "IconList.h" +#include "WolapiOb.h" +#include "WolStrng.h" +#include "BigCheck.h" +//#include "WolDebug.h" + +extern bool cancel_current_msgbox; + +//*********************************************************************************************** +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ) +{ + // Returns true only if called from inside game, and the game ended on us unexpectedly. + bool bReturn = false; + + bool bEscapeDown = false; + bool bReturnDown = false; + + bool bIgnoreReturnDown = false; + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + // The return key is already down, as we enter the dialog. + // Until it comes up again, ignore this fact, so that we don't act on a return press that's not valid. + bIgnoreReturnDown = true; + } + + /* + ** Dialog & button dimensions + */ +#ifdef GERMAN + int d_list_w = 180 * RESFACTOR; +#else +#ifdef FRENCH + int d_list_w = 165 * RESFACTOR; +#else + int d_list_w = 165 * RESFACTOR; +#endif +#endif + + int d_dialog_w = d_list_w + 40 * RESFACTOR; // dialog width + int d_dialog_h = 90 * RESFACTOR; // dialog height + int d_dialog_x = (((320 * RESFACTOR) - d_dialog_w) / 2); + int d_dialog_y = (((200 * RESFACTOR) - d_dialog_h) / 2); + int d_dialog_cx = d_dialog_x + (d_dialog_w / 2); // coord of x-center + + int d_txt8_h = 11 * RESFACTOR; // ht of 8-pt text + int d_margin = 7 * RESFACTOR; // margin width/height + int x_margin = 16 * RESFACTOR; // margin width/height + + int top_margin = 0; + +// int d_list_w = 100 * RESFACTOR; + int d_list_h = 7 * RESFACTOR; + int d_list_x = d_dialog_cx - d_list_w / 2; + int d_list_y = d_dialog_y + d_margin + 24; + +#if (GERMAN | FRENCH) + int d_ok_w = 40 * RESFACTOR; +#else + int d_ok_w = 40 * RESFACTOR; +#endif + int d_ok_h = 13 * RESFACTOR; + int d_ok_x = d_dialog_cx - d_ok_w / 2; + int d_ok_y = d_dialog_y + d_dialog_h - d_ok_h - d_margin; + + /* + ** Button enumerations + */ + enum { + BUTTON_OK = 100, + CHECK_FIND, + CHECK_PAGE, + CHECK_LANGUAGE, + CHECK_ALLGAMES, + CHECK_RANKAM, + }; + + /* + ** Buttons + */ + ControlClass* commands = NULL; // the button list + + TextButtonClass OkBtn( BUTTON_OK, TXT_OK, TPF_BUTTON, d_ok_x, d_ok_y, d_ok_w ); + + BigCheckBoxClass FindCheck( CHECK_FIND, d_list_x, d_list_y, d_list_w, d_list_h, + TXT_WOL_OPTFIND, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bFindEnabled ); + BigCheckBoxClass PageCheck( CHECK_PAGE, d_list_x, d_list_y + d_list_h + 2, d_list_w, d_list_h, + TXT_WOL_OPTPAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bPageEnabled ); + BigCheckBoxClass LanguageCheck( CHECK_LANGUAGE, d_list_x, d_list_y + 2 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTLANGUAGE, TPF_6PT_GRAD | TPF_NOSHADOW, pWO->bLangFilter ); + BigCheckBoxClass GamescopeCheck( CHECK_ALLGAMES, d_list_x, d_list_y + 3 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTGAMESCOPE, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bAllGamesShown ); + BigCheckBoxClass RankAMCheck( CHECK_RANKAM, d_list_x, d_list_y + 4 * ( d_list_h + 2 ), d_list_w, d_list_h, + TXT_WOL_OPTRANKAM, TPF_6PT_GRAD | TPF_NOSHADOW, !pWO->bShowRankRA ); + + /* + ** Initialize. + */ + Set_Logic_Page(SeenBuff); + + /* + ** Create the button list. + */ + commands = &OkBtn; + FindCheck.Add_Tail(*commands); + PageCheck.Add_Tail(*commands); + LanguageCheck.Add_Tail(*commands); + GamescopeCheck.Add_Tail(*commands); + RankAMCheck.Add_Tail(*commands); + + /* + ** Main Processing Loop. + */ + Keyboard->Clear(); + bool display = true; + bool process = true; + while (process) { + + /* + ** Invoke game callback. + */ + if( !bCalledFromGame ) + Call_Back(); + else + { + if( Main_Loop() ) // Game ended on us in the background. + { + process = false; + bReturn = true; + } + } + + #ifdef WIN32 + /* + ** If we have just received input focus again after running in the background then + ** we need to redraw. + */ + if (AllSurfaces.SurfacesRestored) { + AllSurfaces.SurfacesRestored=FALSE; + display = true; + } + #endif + + /* + ** Refresh display if needed. + */ + if (display) { + + /* + ** Display the dialog box. + */ + Hide_Mouse(); + Dialog_Box(d_dialog_x, d_dialog_y, d_dialog_w, d_dialog_h); + Draw_Caption( TXT_WOL_OPTTITLE, d_dialog_x, d_dialog_y, d_dialog_w ); + commands->Flag_List_To_Redraw(); + Show_Mouse(); + display = false; + } + + // Force mouse visible, as some beta testers report unexplicable disappearing cursors. + while( Get_Mouse_State() ) + Show_Mouse(); + // Be nice to other apps. + Sleep( 50 ); + + /* + ** Get user input. + */ + KeyNumType input = commands->Input(); + + // My hack for triggering escape and return on key up instead of down... + // The problem that was occurring was that the calling dialog would act on the key up, + // though this dialog handled the key down. ajw + if( ( ::GetAsyncKeyState( VK_ESCAPE ) & 0x8000 ) ) + { + bEscapeDown = true; + } + else if( bEscapeDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bEscapeDown = false; + } + if( ( ::GetAsyncKeyState( VK_RETURN ) & 0x8000 ) ) + { + if( !bIgnoreReturnDown ) + bReturnDown = true; + } + else + { + bIgnoreReturnDown = false; + if( bReturnDown ) + { + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + bReturnDown = false; + } + } + + /* + ** Process input. + */ + + if( cancel_current_msgbox ) + { + cancel_current_msgbox = false; + input = (KeyNumType)( BUTTON_OK | KN_BUTTON ); + } + switch( input ) + { + case ( BUTTON_OK | KN_BUTTON ): + process = false; + break; + + case ( CHECK_FIND | KN_BUTTON ): + case ( CHECK_PAGE | KN_BUTTON ): + case ( CHECK_LANGUAGE | KN_BUTTON ): + case ( CHECK_ALLGAMES | KN_BUTTON ): + pWO->SetOptions( FindCheck.IsOn, PageCheck.IsOn, LanguageCheck.IsOn, !GamescopeCheck.IsOn ); + break; + + case ( CHECK_RANKAM | KN_BUTTON ): + pWO->bShowRankRA = !RankAMCheck.IsOn; + pWO->bMyRecordUpdated = true; + pWO->bShowRankUpdated = true; + break; + + default: + break; + } + } + return bReturn; +} + +#endif diff --git a/code/redalert/wolapiob.cpp b/code/redalert/wolapiob.cpp new file mode 100644 index 0000000..a1d72a7 --- /dev/null +++ b/code/redalert/wolapiob.cpp @@ -0,0 +1,3432 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.cpp - Implementation of class WolapiObject. +// ajw 07/10/98 + +#include "WolapiOb.h" +#include "RAWolapi.h" +#include "WolStrng.h" +#include "SEditDlg.h" +#include "ToolTip.h" +#include "Wol_GSup.h" + +#include "WolDebug.h" + +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); +extern void WOL_PrintMessage( IconListClass& ILTarget, const char* szText, RemapControlType* pColorRemap ); +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ); + +bool WOL_Options_Dialog( WolapiObject* pWO, bool bCalledFromGame ); + +extern bool cancel_current_msgbox; + +const char* Game_Registry_Key(); + +//*********************************************************************************************** +WolapiObject::WolapiObject() : pChat( NULL ), pDownload( NULL ), pChatSink( NULL ), pDownloadSink( NULL ), + dwChatAdvise( 0 ), dwDownloadAdvise( 0 ), pILChat( NULL ), pILUsers( NULL ), + CurrentLevel( WOL_LEVEL_TOP ), bChannelOwner( false ), GameTypeInfos( NULL ), + nGameTypeInfos( 0 ), bChatShownBefore( false ), + pILPlayers( NULL ), pChatSaveList( NULL ), iLobbyReturnAfterGame( -1 ), iLobbyLast( -1 ), + bFindEnabled( true ), + bPageEnabled( true ), + bLangFilter( true ), + bAllGamesShown( true ), + pGSupDlg( NULL ), + pShpDiscon( NULL ), + pShpLeave( NULL ), + pShpRefresh( NULL ), + pShpSquelch( NULL ), + pShpBan( NULL ), + pShpKick( NULL ), + pShpFindpage( NULL ), + pShpOptions( NULL ), + pShpLadder( NULL ), + pShpHelp( NULL ), + pShpBtnDiscon( NULL ), + pShpBtnLeave( NULL ), + pShpBtnRefresh( NULL ), + pShpBtnSquelch( NULL ), + pShpBtnBan( NULL ), + pShpBtnKick( NULL ), + pShpBtnFindpage( NULL ), + pShpBtnOptions( NULL ), + pShpBtnLadder( NULL ), + pShpBtnHelp( NULL ), + bReturningAfterGame( false ), + pTTipDiscon( NULL ), + pTTipLeave( NULL ), + pTTipRefresh( NULL ), + pTTipSquelch( NULL ), + pTTipBan( NULL ), + pTTipKick( NULL ), + pTTipFindpage( NULL ), + pTTipOptions( NULL ), + pTTipLadder( NULL ), + pTTipHelp( NULL ), + bMyRecordUpdated( false ), + bChannelListTitleUpdated( false ), + bInGame( false ), + pStaticUsers( NULL ), + bPump_In_Call_Back( false ), + bFreezeExternalPager( false ), + bDoingDisconnectPinging( false ), + bSelfDestruct( false ), + bEggSounds( true ), + bEgg8Player( false ), + bShowRankRA( true ), + bShowRankUpdated( false ) +{ + *szMyName = 0; + *szMyRecord = 0; + *szMyRecordAM = 0; + *szChannelListTitle = 0; + *szChannelNameCurrent = 0; + *szChannelReturnOnGameEnterFail = 0; + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + dwTimeNextChannelUpdate = 0; + *szLadderServerHost = 0; + *szGameResServerHost1 = 0; + *szGameResServerHost2 = 0; + + strcpy( DibIconInfos[ DIBICON_OWNER ].szFile, "dib_own.bmp" ); + strcpy( DibIconInfos[ DIBICON_SQUELCH ].szFile, "dib_sqel.bmp" ); + strcpy( DibIconInfos[ DIBICON_LATENCY ].szFile, "latency.bmp" ); + strcpy( DibIconInfos[ DIBICON_ACCEPT ].szFile, "dib_acpt.bmp" ); + strcpy( DibIconInfos[ DIBICON_NOTACCEPT ].szFile, "dib_acp2.bmp" ); + strcpy( DibIconInfos[ DIBICON_USER ].szFile, "dib_user.bmp" ); + strcpy( DibIconInfos[ DIBICON_PRIVATE ].szFile, "privgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_TOURNAMENT ].szFile, "tourgame.bmp" ); + strcpy( DibIconInfos[ DIBICON_VOICE ].szFile, "voice.bmp" ); + for( int i = 0; i != NUMDIBICONS; i++ ) + { + DibIconInfos[ i ].hDIB = 0; + DibIconInfos[ i ].pDIB = NULL; + } + + // Determine name of executable of user's web browser. + // This seems to be the "correct" way to do this, but it's bloody stupid. + *szWebBrowser = 0; + char szFile[] = "\\name_unlikely_to_conflict_77.html"; //"\\it is really dumb for me to have to create this file.html"; + HANDLE hFile = ::CreateFile( szFile, GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile != INVALID_HANDLE_VALUE ) + { + ::CloseHandle( hFile ); + HINSTANCE hExecutable = ::FindExecutable( szFile, "", szWebBrowser ); +// if( (int)hExecutable <= 32 ) +// { +// debugprint( "error %i getting browser\n", hExecutable ); +// } +// else +// debugprint( "szWebBrowser is %s\n", szWebBrowser ); + ::DeleteFile( szFile ); + } +} + +//*********************************************************************************************** +WolapiObject::~WolapiObject() +{ + DeleteSavedChat(); + + if( nGameTypeInfos ) + { + // Delete DIBs that were created, and the wol_gametypeinfos themselves + for( unsigned int n = 0; n != nGameTypeInfos; n++ ) + { + if( GameTypeInfos[ n ].hDIB ) + { + GlobalUnlock( GameTypeInfos[ n ].hDIB ); // Release pDIB. + DestroyDIB( GameTypeInfos[ n ].hDIB ); // Destroy mem alloc'ed for dib bits data. + } + } + delete [] GameTypeInfos; + GameTypeInfos = NULL; + } + + for( int i = 0; i != NUMDIBICONS; i++ ) + { + if( DibIconInfos[ i ].pDIB ) + { + GlobalUnlock( DibIconInfos[ i ].hDIB ); + DestroyDIB( DibIconInfos[ i ].hDIB ); + } + } + + if( pChatSink ) + UnsetupCOMStuff(); + + // Delete buttons, etc., shared by dialogs. + delete pShpBtnDiscon; + delete pShpBtnLeave; + delete pShpBtnRefresh; + delete pShpBtnSquelch; + delete pShpBtnBan; + delete pShpBtnKick; + delete pShpBtnFindpage; + delete pShpBtnOptions; + delete pShpBtnLadder; + delete pShpBtnHelp; + // Delete shared tooltips. + delete pTTipDiscon; + delete pTTipLeave; + delete pTTipRefresh; + delete pTTipSquelch; + delete pTTipBan; + delete pTTipKick; + delete pTTipFindpage; + delete pTTipOptions; + delete pTTipLadder; + delete pTTipHelp; +} + +//*********************************************************************************************** +bool WolapiObject::bSetupCOMStuff() +{ +// debugprint( "++++Begin WolapiObject::bSetupCOMStuff\n" ); + + HRESULT hRes; + + // Grab IChat, INetUtil, set up "sinks". +// debugprint( "CoCreateInstance\n" ); + CoCreateInstance( CLSID_Chat, NULL, CLSCTX_INPROC_SERVER, IID_IChat, (void**)&pChat ); + if( !pChat ) + return false; // Severe, essentially fatal. + CoCreateInstance( CLSID_NetUtil, NULL, CLSCTX_INPROC_SERVER, IID_INetUtil, (void**)&pNetUtil ); + if( !pNetUtil ) + return false; // Severe, essentially fatal. + + // Set up RAChatEventSink. + pChatSink = new RAChatEventSink( this ); + pChatSink->AddRef(); + // Set up RANetUtilEventSink. + pNetUtilSink = new RANetUtilEventSink( this ); + pNetUtilSink->AddRef(); + + // If we could use ATL stuff, this would be different. (We'd use AtlAdvise.) + + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + + // Get a connection point from the chat class for the chatsink. +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect chat to chatsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (IChatEvent*)pChatSink, &dwChatAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; + // Get a connection point from the netutil class for the netutilsink. +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + // Connect netutil to netutilsink. +// debugprint( "Advise. pChatSink = %i, pConnectionPoint = %i\n", pChatSink, pConnectionPoint ); + hRes = pConnectionPoint->Advise( (INetUtilEvent*)pNetUtilSink, &dwNetUtilAdvise ); + if( !SUCCEEDED(hRes) ) + return false; // Severe, essentially fatal. + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "++++End WolapiObject::bSetupCOMStuff\n" ); + return true; +} + +//*********************************************************************************************** +void WolapiObject::UnsetupCOMStuff() +{ +// debugprint( "----Begin WolapiObject::UnsetupCOMStuff\n" ); + + HRESULT hRes; + + // If we could use ATL stuff, this would be different. (We'd use AtlUnadvise.) + + // Unsetup RAChatEventSink and RANetUtilEventSink, release IChat. + IConnectionPoint* pConnectionPoint = NULL; + IConnectionPointContainer* pContainer = NULL; + +// debugprint( "QueryInterface\n" ); + hRes = pChat->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_IChatEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwChatAdvise ); + pConnectionPoint->Unadvise( dwChatAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + + pConnectionPoint = NULL; + pContainer = NULL; +// debugprint( "QueryInterface\n" ); + hRes = pNetUtil->QueryInterface( IID_IConnectionPointContainer, (void**)&pContainer ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "FindConnectionPoint\n" ); + hRes = pContainer->FindConnectionPoint( IID_INetUtilEvent, &pConnectionPoint ); + _ASSERTE(SUCCEEDED(hRes)); +// debugprint( "Unadvise: %i\n", dwNetUtilAdvise ); + pConnectionPoint->Unadvise( dwNetUtilAdvise ); + + pContainer->Release(); + pConnectionPoint->Release(); + +// debugprint( "pChat->Release\n" ); + pChat->Release(); + +// debugprint( "pChatSink->Release\n" ); + pChatSink->Release(); // This results in pChatSink deleting itself for us. + pChatSink = NULL; + +// debugprint( "pNetUtil->Release\n" ); + pNetUtil->Release(); + +// debugprint( "pNetUtilSink->Release\n" ); + pNetUtilSink->Release(); // This results in pChatSink deleting itself for us. + pNetUtilSink = NULL; + +// debugprint( "----End WolapiObject::UnsetupCOMStuff\n" ); +} + +//*********************************************************************************************** +void WolapiObject::LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ) +{ + // Called to initialize this before the chat dialog is shown. + + // Set pointers to lists in dialog. + this->pILChat = pILChat; + this->pILChannels = pILChannels; + this->pILUsers = pILUsers; + + this->pStaticUsers = pStaticUsers; +} + +//*********************************************************************************************** +void WolapiObject::ClearListPtrs() +{ + // Called to clear list pointers when chat or gamesetup dialog goes away, for safety. + pILChat = NULL; + pILChannels = NULL; + pILUsers = NULL; + + pILPlayers = NULL; + + pStaticUsers = NULL; +} + +//*********************************************************************************************** +void WolapiObject::LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ) +{ + // Called to initialize this before the gamesetup dialog is shown. + + // Set pointers to lists in dialog. + pILChat = pILDisc; + this->pILPlayers = pILPlayers; +} + +//*********************************************************************************************** +void WolapiObject::PrepareButtonsAndIcons() +{ + // Load shapes for buttons. Store images in this order: up, down, disabled. + //pShpDiscon = LoadShpFile( "discon.shp" ); etc + pShpDiscon = (char*)MFCD::Retrieve( "discon.shp" ); + pShpLeave = (char*)MFCD::Retrieve( "leave.shp" ); + pShpRefresh = (char*)MFCD::Retrieve( "refresh.shp" ); + pShpSquelch = (char*)MFCD::Retrieve( "squelch.shp" ); + pShpBan = (char*)MFCD::Retrieve( "ban.shp" ); + pShpKick = (char*)MFCD::Retrieve( "kick.shp" ); + pShpFindpage = (char*)MFCD::Retrieve( "findpage.shp" ); + pShpOptions = (char*)MFCD::Retrieve( "ops.shp" ); + pShpLadder = (char*)MFCD::Retrieve( "ladder.shp" ); + pShpHelp = (char*)MFCD::Retrieve( "help.shp" ); + + // Set up standard wol buttons, used by both main dialogs. Note hardcoded ID values: must match values in dialog. + int iWolButtons_x = 34; + int iWolButtons_y = 20; + int iWolButtons_dx = 53; + int xWolButton = iWolButtons_x; + int xTTip = 10; // Offset for tooltip. + int yTTip = - 5; // Offset for tooltip. + pShpBtnDiscon = new ShapeButtonClass( 100, pShpDiscon, xWolButton, iWolButtons_y ); + pTTipDiscon = new ToolTipClass( pShpBtnDiscon, TXT_WOL_TTIP_DISCON, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnLeave = new ShapeButtonClass( 101, pShpLeave, xWolButton, iWolButtons_y ); + pTTipLeave = new ToolTipClass( pShpBtnLeave, TXT_WOL_TTIP_LEAVE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnRefresh = new ShapeButtonClass( 102, pShpRefresh, xWolButton, iWolButtons_y ); + pTTipRefresh = new ToolTipClass( pShpBtnRefresh, TXT_WOL_TTIP_REFRESH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnSquelch = new ShapeButtonClass( 103, pShpSquelch, xWolButton, iWolButtons_y ); + pTTipSquelch = new ToolTipClass( pShpBtnSquelch, TXT_WOL_TTIP_SQUELCH, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnBan = new ShapeButtonClass( 104, pShpBan, xWolButton, iWolButtons_y ); + pTTipBan = new ToolTipClass( pShpBtnBan, TXT_WOL_TTIP_BAN, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnKick = new ShapeButtonClass( 105, pShpKick, xWolButton, iWolButtons_y ); + pTTipKick = new ToolTipClass( pShpBtnKick, TXT_WOL_TTIP_KICK, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton += iWolButtons_dx; + pShpBtnFindpage = new ShapeButtonClass( 106, pShpFindpage, xWolButton, iWolButtons_y ); + pTTipFindpage = new ToolTipClass( pShpBtnFindpage, TXT_WOL_TTIP_FINDPAGE, xWolButton + xTTip, iWolButtons_y + yTTip ); + xWolButton = 452; + pShpBtnOptions = new ShapeButtonClass( 107, pShpOptions, xWolButton, iWolButtons_y ); + pTTipOptions = new ToolTipClass( pShpBtnOptions, TXT_WOL_TTIP_OPTIONS, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnLadder = new ShapeButtonClass( 108, pShpLadder, xWolButton, iWolButtons_y ); + pTTipLadder = new ToolTipClass( pShpBtnLadder, TXT_WOL_TTIP_LADDER, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + xWolButton += iWolButtons_dx; + pShpBtnHelp = new ShapeButtonClass( 109, pShpHelp, xWolButton, iWolButtons_y ); + pTTipHelp = new ToolTipClass( pShpBtnHelp, TXT_WOL_TTIP_HELP, xWolButton + xTTip, iWolButtons_y + yTTip, true ); + + // Load standard hard-coded icons. + HPALETTE hPal = GetCurrentScreenPalette(); + + int iFileLength; + const char* pFileData; + for( int iDibIcon = 0; iDibIcon != NUMDIBICONS; iDibIcon++ ) + { + //pFileData = LoadFileIntoMemory( DibIconInfos[ iDibIcon ].szFile, iFileLength ); + pFileData = (char*)MFCD::Retrieve( DibIconInfos[ iDibIcon ].szFile ); + if( pFileData ) + { + CCFileClass ccfileDib( DibIconInfos[ iDibIcon ].szFile ); + iFileLength = ccfileDib.Size(); + //debugprint( "Loaded %s, size is %i.\n", DibIconInfos[ iDibIcon ].szFile, iFileLength ); + DibIconInfos[ iDibIcon ].hDIB = LoadDIB_FromMemory( (unsigned char*)pFileData, iFileLength ); + if( DibIconInfos[ iDibIcon ].hDIB ) + { + DibIconInfos[ iDibIcon ].pDIB = (char*)GlobalLock( DibIconInfos[ iDibIcon ].hDIB ); + RemapDIBToPalette( hPal, DibIconInfos[ iDibIcon ].pDIB ); + } +// else +// debugprint( "LoadDIB_FromMemory failed!\n" ); + } +// else +// debugprint( "Couldn't find %s in mix.\n", DibIconInfos[ iDibIcon ].szFile ); + } + + if( DibIconInfos[ DIBICON_LATENCY ].pDIB ) + fLatencyToIconWidth = (float)DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) / 1000; + else + fLatencyToIconWidth = 0; + + // All of the following is for the list of game icons... + + // Load game icons from the wol api. + LPCSTR szSkus; + if( pChat->GetGametypeList( &szSkus ) == S_OK ) + { + // Make two copies of szSkus because strtok insists on messing with them. + char* szSkus1 = new char[ strlen( szSkus ) + 1 ]; + char* szSkus2 = new char[ strlen( szSkus ) + 1 ]; + strcpy( szSkus1, szSkus ); + strcpy( szSkus2, szSkus ); + // Count commas. + char seps[] = ","; + char* token; + nGameTypeInfos = 0; + token = strtok( szSkus1, seps ); + while( token != NULL ) + { + nGameTypeInfos++; + token = strtok( NULL, seps ); + } + // There are actually 2 additional game types available in wolapi - 0 (ws icon) and -1 (wwonline icon). + nGameTypeInfos += 2; + // Create structs to hold infos. +// debugprint( "Creating %i gametypeinfos\n", nGameTypeInfos ); + GameTypeInfos = new WOL_GAMETYPEINFO[ nGameTypeInfos ]; + int iMyIndex = 0; + token = strtok( szSkus2, seps ); + while( token != NULL ) + { + GetGameTypeInfo( atoi( token ), GameTypeInfos[ iMyIndex ], hPal ); + token = strtok( NULL, seps ); + iMyIndex++; + } + // Get the two extra game type infos... + GetGameTypeInfo( -1, GameTypeInfos[ iMyIndex++ ], hPal ); + GetGameTypeInfo( 0, GameTypeInfos[ iMyIndex++ ], hPal ); + } +// else +// debugprint( "GetGametypeList() failed.\n" ); + + // Load icons that we'll need to represent Red Alert GameKinds. + // These are available in wolapi at their old sku number locations. + GetGameTypeInfo( 2, OldRAGameTypeInfos[ 0 ], hPal ); // RA + GetGameTypeInfo( 3, OldRAGameTypeInfos[ 1 ], hPal ); // CS + GetGameTypeInfo( 4, OldRAGameTypeInfos[ 2 ], hPal ); // AM + + if( hPal ) + DeleteObject( hPal ); +} + +//*********************************************************************************************** +void WolapiObject::GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ) +{ + unsigned char* pVirtualFile; + int iFileLength; +// debugprint( "GetGametypeInfo, type %i\n", iGameType ); + LPCSTR szName; + LPCSTR szURL; + pChat->GetGametypeInfo( iGameType, 12, &pVirtualFile, &iFileLength, &szName, &szURL ); + GameTypeInfo.iGameType = iGameType; + if( szName ) + strcpy( GameTypeInfo.szName, szName ); + else + *GameTypeInfo.szName = 0; + if( szURL ) + strcpy( GameTypeInfo.szURL, szURL ); + else + *GameTypeInfo.szURL = 0; + +// debugprint( "LoadDIB_FromMemory( %i, %i )\n", pVirtualFile, iFileLength ); + // Create a DIB by "loading" (as if it was a file) the bitmap data. + GameTypeInfo.hDIB = LoadDIB_FromMemory( pVirtualFile, iFileLength ); +// debugprint( "hDIB is %i\n", GameTypeInfo.hDIB ); + if( !GameTypeInfo.hDIB ) + { + GameTypeInfo.pDIB = NULL; + return; // Load failed. Should not ever happen. + } + GameTypeInfo.pDIB = (const char*)GlobalLock( GameTypeInfo.hDIB ); + +// debugprint( "@@@@@ Created gametypeinfo #%i: name %s, pDIB = %i\n", iIndex, GameTypeInfo.szName, GameTypeInfo.pDIB ); + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)GameTypeInfo.pDIB; + if( lpbi->biBitCount != 8 ) + { + // Do not use this loaded bmp, as it's not 256 color. + GlobalUnlock( GameTypeInfo.hDIB ); // Release pDIB. + GameTypeInfo.pDIB = NULL; + DestroyDIB( GameTypeInfo.hDIB ); // Destroy mem alloc'ed for dib bits data. + GameTypeInfo.hDIB = 0; + return; + } + + // Remap colors... + RemapDIBToPalette( hPal, GameTypeInfo.pDIB ); + +} + +//*********************************************************************************************** +void* WolapiObject::IconForGameType( int iGameType ) +{ + // Returns a GameTypeInfos entry by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return (void*)GameTypeInfos[i].pDIB; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::NameOfGameType( int iGameType ) const +{ + // Returns the name of a sku by gametype, instead of our (potentially arbitrary) index. + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szName; + } + return NULL; +} + +//*********************************************************************************************** +const char* WolapiObject::URLForGameType( int iGameType ) const +{ + // Returns NULL if type not found in list, which will of course never happen... + for( int i = 0; i != nGameTypeInfos; i++ ) + { + if( GameTypeInfos[i].iGameType == iGameType ) + return GameTypeInfos[i].szURL; + } + return NULL; +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, PlayerColorType iColorRemap /* = PCOLOR_NONE */ ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, iColorRemap ); +} + +//*********************************************************************************************** +void WolapiObject::PrintMessage( const char* szText, RemapControlType* pColorRemap ) +{ + if( pILChat ) + WOL_PrintMessage( *pILChat, szText, pColorRemap ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::GetChatServer() +{ + // Calls RequestServerList() to get a chat server ready for us to login to. + // Returns S_OK and sets pChatSink->pServer if successful. + WWMessageBox().Process( TXT_WOL_CONNECTING, TXT_NONE ); + +//if( !( ::GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) // ajw - allow use of test servers +//{ + + // Request chat server address from server server. + pChatSink->bRequestServerListWait = true; +// debugprint( "Calling RequestServerList...\n" ); + if( !SUCCEEDED( pChat->RequestServerList( GAME_SKU, GAME_VERSION, "unused", "unused", 5 ) ) ) + { +// debugprint( "RequestServerList call failed\n" ); + return E_FAIL; + } + DWORD dwTimeLimit = timeGetTime(); // ajw My own extra timeout at one minute, in case wolapi chokes. +// debugprint( "Called RequestServerList...\n" ); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + hresPatchResults = 0; + while( pChatSink->bRequestServerListWait && timeGetTime() - dwTimeLimit < 60000 ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } +// debugprint( "PumpMessages after RequestServerList...\n" ); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + // If an "update list" of patches has been received, instead of a server list, this flag will have been set + // for us describing the results. We'll either cancel log in or trigger game exit. + if( hresPatchResults ) + { + pChatSink->bRequestServerListWait = false; + return hresPatchResults; + } + } +// debugprint( "RequestServerList wait finished\n" ); + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->pServer ) + return S_OK; + else + return E_FAIL; + +/* +} +else +{ + // Test using local server on LAN. + + // Bypass RequestServerList, as it is unnecessary and may not be possible if serverserver can't be reached. + // Set SKU manually because normally RequestServerList does this for you. + pChat->SetProductSKU( GAME_SKU ); + if( pChatSink->pServer ) + delete pChatSink->pServer; + pChatSink->pServer = new Server; + if( !( ::GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) + strcpy( (char*)pChatSink->pServer->conndata, "TCP;irc.westwood.com;9000" ); + else + // Control key down as well. + strcpy( (char*)pChatSink->pServer->conndata, "TCP;10.2.20.28;4000" ); + strcpy( (char*)pChatSink->pServer->connlabel, "IRC" ); + strcpy( (char*)pChatSink->pServer->name, "Chat"); + return S_OK; +} +*/ + +} + +//*********************************************************************************************** +HRESULT WolapiObject::AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ) +{ + // If RequestConnection() succeeds, sets pChatSink->bConnected true and returns S_OK. + // Else returns RequestConnection() error result. + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGIN, TXT_NONE ); + +// debugprint( "~1\n" ); + strcpy( (char*)pChatSink->pServer->login, szName ); + strcpy( (char*)pChatSink->pServer->password, szPass ); + +/* +// debugprint( "RequestConnection with:\n%s,%s,%s,%s,%s - %s\n", + pChatSink->pServer->name, + pChatSink->pServer->connlabel, + pChatSink->pServer->conndata, + pChatSink->pServer->login, + pChatSink->pServer->password, + bPassIsMangled ? "(mangled)" : "(unmangled)" ); +*/ + pChatSink->bRequestConnectionWait = true; + pChatSink->hresRequestConnectionError = 0; + + if( !SUCCEEDED( pChat->RequestConnection( pChatSink->pServer, 15, !bPassIsMangled ) ) ) + { +// debugprint( "RequestConnection call failed\n" ); + return CHAT_E_CON_ERROR; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + ::GetAsyncKeyState( VK_ESCAPE ); // Set up for escape key checking. + bool bCancel = false; + while( pChatSink->bRequestConnectionWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + { + Call_Back(); + if( ::GetAsyncKeyState( VK_ESCAPE ) & 1 ) + { + bCancel = true; + break; + } + } + if( bCancel ) + break; + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; +// Sleep( PUMPSLEEPDURATION ); // Can't do because we want to Call_Back() + } + if( bCancel ) + { + Keyboard->Clear(); + return USERCANCELLED; + } + if( pChatSink->bRequestConnectionWait ) + return CHAT_E_CON_ERROR; + + if( pChatSink->bConnected ) + { + strcpy( szMyName, szName ); + strcpy( szMyRecord, szName ); + strcpy( szMyRecordAM, szName ); + return S_OK; + } + else + return pChatSink->hresRequestConnectionError; +} + +//*********************************************************************************************** +bool WolapiObject::bLoggedIn() +{ + return pChatSink->bConnected; +} + +//*********************************************************************************************** +void WolapiObject::Logout() +{ + // Requests logout from wolapi. Doesn't return any error values, as what we would do if it + // failed - force the user to stay connected? + + if( bSelfDestruct ) + WWMessageBox().Process( TXT_WOL_ERRORLOGOUT, TXT_NONE ); + else + WWMessageBox().Process( TXT_WOL_ATTEMPTLOGOUT, TXT_NONE ); + +// debugprint( "RequestLogout()\n" ); + + pChatSink->bRequestLogoutWait = true; + + if( !SUCCEEDED( pChat->RequestLogout() ) ) + { +// debugprint( "RequestLogout() call failed\n" ); + } + + DWORD dwTimePatience = timeGetTime(); // After 5 seconds we run out of patience and bail. + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestLogoutWait && timeGetTime() - dwTimePatience < 5000 ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + pChatSink->bConnected = false; + *szMyName = 0; + + Sound_Effect( WOLSOUND_LOGOUT ); +} + +//*********************************************************************************************** +bool WolapiObject::UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ) +{ + // This is now non-modal. + // Sends off a request for a new channels list. + +// // Returns false upon total failure. +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + +// pChatSink->bRequestChannelListWait = true; + pChatSink->ChannelFilter = ChannelFilter; + +// debugprint( "RequestChannelList(), iChannelType = %i, filter = %i\n", iChannelType, ChannelFilter ); + if( !SUCCEEDED( pChat->RequestChannelList( iChannelType, bAutoping ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } +/* + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListWait ) + return false; +*/ + LastUpdateChannelCallLevel = CurrentLevel; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnChannelList() +{ + // The chatsink calls this when its OnChannelList() is called, and it has remade its internal channel list. + // The question here is: should we display the values now in the chatsink? + // As UpdateChannels() is now non-modal, there is the danger that we have moved away from the place in + // the channel heirarchy where we originally called RequestChannelList(). + // To help ensure we're getting this where we expect to get it, we check the value of CurrentLevel against + // what it was when we called UpdateChannels(). + if( CurrentLevel == LastUpdateChannelCallLevel ) + ListChannels(); +} + +//*********************************************************************************************** +void WolapiObject::ListChannels() +{ + // Show pChatSink's pChannelList in pILChannels. + // The extra data ptr hidden in each list item will hold a void pointer to the channel described. +// debugprint( "ListChannels(), pChannelList = %i\n", pChatSink->pChannelList ); + + static WOL_LEVEL LevelLastListed = WOL_LEVEL_INVALID; + + int iListViewIndex = 0; + + // If redrawing the same list as before, preserve the view position. + if( LevelLastListed == CurrentLevel ) + iListViewIndex = pILChannels->Get_View_Index(); + else + LevelLastListed = CurrentLevel; + + pILChannels->Clear(); + switch( CurrentLevel ) + { + case WOL_LEVEL_GAMESOFTYPE: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_LOBBIES: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_GAMES, NULL, ICON_SHAPE, CHANNELTYPE_GAMES ); + break; + case WOL_LEVEL_INLOBBY: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + break; + default: + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + break; + } + + Channel* pChannel = pChatSink->pChannelList; + while( pChannel ) + { + if( pChannel->type == 0 ) + { + // Show chat channel. + char* pShow; + int iLobby = iChannelLobbyNumber( pChannel->name ); + if( iLobby == - 1 ) + { + // Regular chat channel. + pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + sprintf( pShow, "%s\t%-3u", (char*)pChannel->name, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_CHAT, (char*)pChannel->name, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, (void*)DibIconInfos[ DIBICON_USER ].pDIB, ICON_DIB, CHANNELTYPE_CHATCHANNEL, (void*)pChannel ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + pShow = new char[ REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( pShow, "%s\t%-3u", szLobbyName, pChannel->currentUsers ); + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_LOBBY, szLobbyName, pChannel->currentUsers ); + pILChannels->Add_Item( pShow, szHelp, IconForGameType( -1 ), ICON_DIB, CHANNELTYPE_LOBBYCHANNEL, (void*)pChannel ); +// debugprint( ":::::added pChannel %i, name %s, as %s\n", pChannel, pChannel->name, pShow ); + } + delete [] pShow; + } + else + { + // Show game channel. + char* pShow = new char[ strlen( (char*)pChannel->name ) + 10 ]; + char szHelp[ 200 ]; + void* pGameKindIcon; + if( pChannel->type == GAME_TYPE ) + { + // Get RedAlert GameKind. + CREATEGAMEINFO::GAMEKIND GameKind = (CREATEGAMEINFO::GAMEKIND)( pChannel->reserved & 0xFF000000 ); + switch( GameKind ) + { + case CREATEGAMEINFO::RAGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 0 ].pDIB; // Red Alert icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_REDALERT, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::CSGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 1 ].pDIB; // CS icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_COUNTERSTRIKE, + pChannel->currentUsers, pChannel->maxUsers ); + break; + case CREATEGAMEINFO::AMGAME: + pGameKindIcon = (void*)OldRAGameTypeInfos[ 2 ].pDIB; // AM icon + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_RAGAME, TXT_WOL_TTIP_AFTERMATH, + pChannel->currentUsers, pChannel->maxUsers ); + break; + default: +// debugprint( "Illegal value for GameKind channel reserved field: %s\n", (char*)pChannel->name ); + pGameKindIcon = NULL; + break; + } + sprintf( pShow, "%s\t%u/%u", (char*)pChannel->name, pChannel->currentUsers, pChannel->maxUsers ); + } + else + { + pGameKindIcon = IconForGameType( pChannel->type ); + sprintf( pShow, "%s\t%-2u", (char*)pChannel->name, pChannel->currentUsers ); + sprintf( szHelp, TXT_WOL_TTIP_CHANLIST_GAME, NameOfGameType( pChannel->type ), + pChannel->currentUsers ); + } + void* pPrivateIcon = NULL; + if( pChannel->flags & CHAN_MODE_KEY ) + { + // Game is private. + pPrivateIcon = (void*)DibIconInfos[ DIBICON_PRIVATE ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_PRIVATEGAME ); + } + + void* pTournamentIcon = NULL; + if( pChannel->tournament ) + { + // Game is tournament. + pTournamentIcon = (void*)DibIconInfos[ DIBICON_TOURNAMENT ].pDIB; + strcat( szHelp, TXT_WOL_TTIP_TOURNAMENTGAME ); + } + + int iLatencyUse = pChannel->latency; + if( iLatencyUse == -1 ) + iLatencyUse = 0; + + static int iLatencyBarX = 227 - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 19; + + pILChannels->Add_Item( pShow, szHelp, pGameKindIcon, ICON_DIB, + CHANNELTYPE_GAMECHANNEL, (void*)pChannel, NULL, + pPrivateIcon, ICON_DIB, pTournamentIcon, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, + iLatencyBarX, 0, iLatencyUse * fLatencyToIconWidth ); + delete [] pShow; + } + pChannel = pChannel->next; + } + if( iListViewIndex ) + pILChannels->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( const char* szChannelName, const char* szKey ) +{ + // Used for CHAT channels (or lobbies) only. Channel type is set to 0. + Channel ChannelTemp; + memset( &ChannelTemp, 0, sizeof( ChannelTemp ) ); + strcpy( (char*)ChannelTemp.name, szChannelName ); + strcpy( (char*)ChannelTemp.key, szKey ); + return ChannelJoin( &ChannelTemp ); +} + +//*********************************************************************************************** +HRESULT WolapiObject::ChannelJoin( Channel* pChannelToJoin ) +{ + // Returns an HRESULT, the meaning of which is totally customized for my own uses. + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelJoinWait = true; + pChatSink->hresRequestJoinResult = 0; + +// debugprint( "RequestChannelJoin(), %s\n", pChannelToJoin->name ); + HRESULT hRes = pChat->RequestChannelJoin( pChannelToJoin ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelJoin() call failed, result %i ", hRes ); + DebugChatDef( hRes ); + return E_FAIL; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelJoinWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; // Turn on response to channel lists. + + if( pChatSink->bRequestChannelJoinWait ) + return CHAT_E_TIMEOUT; + + switch( pChatSink->hresRequestJoinResult ) + { + case CHAT_E_CHANNELDOESNOTEXIST: + case CHAT_E_BADCHANNELPASSWORD: + case CHAT_E_BANNED: + case CHAT_E_CHANNELFULL: + return pChatSink->hresRequestJoinResult; + } + + if( !pChatSink->bJoined ) + return E_FAIL; + + return S_OK; +} + +//*********************************************************************************************** +bool WolapiObject::ChannelLeave() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelLeaveWait = true; + pChatSink->DeleteUserList(); + +// debugprint( "RequestChannelLeave()\n" ); + if( !SUCCEEDED( pChat->RequestChannelLeave() ) ) + { +// debugprint( "RequestChannelLeave() call failed\n" ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelLeaveWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelLeaveWait || pChatSink->bJoined ) + return false; + + return true; +} + +/* +//*********************************************************************************************** +bool WolapiObject::UserList() +{ + // Returns false upon total failure. + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestUserListWait = true; + + // I no longer request a user list, as this was being done only when entering a channel, and it turns out wolapi gives + // us a user list automatically when joining. This function is used as a blocker that waits until the user list has + // definitely arrived. +// debugprint( "RequestUserList()\n" ); +// if( !SUCCEEDED( pChat->RequestUserList() ) ) +// { +// debugprint( "RequestUserList() call failed\n" ); +// return false; +// } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestUserListWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestUserListWait ) + { + pChatSink->bRequestUserListWait = false; + return false; + } + + return true; +} +*/ + +//*********************************************************************************************** +//typedef char CHANNELUSERNAME[ WOL_NAME_LEN_MAX ]; +struct CHANNELUSERINFO +{ + char szName[ WOL_NAME_LEN_MAX ]; + bool bFlagged; + RemapControlType* pColorRemap; + HousesType House; // Only used if game channel. + bool bAccept; // Only used if game channel. + char szExtra[ 50 ]; // Only used if game channel. +}; + +//*********************************************************************************************** +bool WolapiObject::ListChannelUsers() +{ + // Show pChatSink's pUserList in pILUsers or pILPlayers (depending on CurrentLevel), after clearing it. + // The extra data ptr hidden in each list item will hold a void pointer to the user described. + // For simplicity, I destroy the old list and write a new one, even though possibly only one item + // may have changed. + // In order for the multiselect flags in the list to persist, I record the names that are flagged + // before clearing the list, then reset them (if found) in the new list. + // This is inefficient, but should be fine in this non-time-critical situation. + // (I also save item color here, and save all items. Now it's really inefficient.) + // (Oh, boy. Now I've added the persistence of house info when this is a game channel...) + // The idea was to avoid duplication of player data, and not have any dependence on the integrity of the chatsink's + // players list. Now it is a case of it working "well enough" to not require time to elegantize it. + + // Extra bonus, if useful: returns true if an operator (channel owner) is found in the channel, false otherwise. + + bool bChannelOwnerFound = false; + + bool bInLobby; + IconListClass* pListToUse; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + bInLobby = false; + pListToUse = pILPlayers; + } + else + { + bInLobby = ( iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ) != -1 ); + pListToUse = pILUsers; + } + + if( pListToUse && // Fails in rare cases when list draw is triggered before it is fully set up. + *szChannelNameCurrent ) // No users to list if not in a channel. + { + // If redrawing the same list as before, preserve the view position. + static char szChannelLastListed[ WOL_CHANNAME_LEN_MAX ] = { 0 }; +//debugprint( "szChannelLastListed '%s', szChannelNameCurrent '%s'\n", szChannelLastListed, szChannelNameCurrent ); + int iListViewIndex = 0; + if( strcmp( szChannelLastListed, szChannelNameCurrent ) == 0 ) + iListViewIndex = pListToUse->Get_View_Index(); + else + strcpy( szChannelLastListed, szChannelNameCurrent ); + +//debugprint( "ListChannelUsers(), pUserList = %i\n", pChatSink->pUserList ); + // Save users in current list. + int iCount = pListToUse->Count(); + CHANNELUSERINFO* pUsersSaved = NULL; + int iUsersSaved = 0; + if( iCount ) + { + pUsersSaved = new CHANNELUSERINFO[ iCount ]; + for( int i = 0; i != iCount; i++ ) + { + PullPlayerName_Into_From( pUsersSaved[ iUsersSaved ].szName, pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bFlagged = pListToUse->bItemIsMultiSelected( i ); + pUsersSaved[ iUsersSaved ].pColorRemap = pListToUse->Get_Item_Color( i ); +// debugprint( " Saving color of %s as %i.\n", pUsersSaved[ iUsersSaved ].szName, pUsersSaved[ iUsersSaved ].pColorRemap ); + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + pUsersSaved[ iUsersSaved ].House = PullPlayerHouse_From( pListToUse->Get_Item( i ) ); + pUsersSaved[ iUsersSaved ].bAccept = bItemMarkedAccepted( i ); + const char* szExtra = pListToUse->Get_Item_ExtraDataString( i ); + if( szExtra ) + strcpy( pUsersSaved[ iUsersSaved ].szExtra, szExtra ); + else + *pUsersSaved[ iUsersSaved ].szExtra = 0; + } + iUsersSaved++; + } + } + // Clear list and recreate with new users list. + pListToUse->Clear(); + User* pUser = pChatSink->pUserList; + int iUserCount = 0; + while( pUser ) + { + ++iUserCount; + void* pIcon1 = NULL; + void* pIcon2 = NULL; + if( pUser->flags & CHAT_USER_CHANNELOWNER ) + { + pIcon1 = (void*)DibIconInfos[ DIBICON_OWNER ].pDIB; + bChannelOwnerFound = true; + } + else + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + pIcon1 = (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB; + else + { + if( pUser->flags & CHAT_USER_VOICE ) + pIcon1 = (void*)DibIconInfos[ DIBICON_VOICE ].pDIB; + else + pIcon1 = (void*)DibIconInfos[ DIBICON_USER ].pDIB; + } + } + if( pUser->flags & CHAT_USER_SQUELCHED ) + pIcon2 = (void*)DibIconInfos[ DIBICON_SQUELCH ].pDIB; + + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL || bInLobby ) + { + int iRank = pNetUtilSink->GetUserRank( (char*)pUser->name, bShowRankRA ); + char szNameToShow[ WOL_NAME_LEN_MAX + 40 ]; + if( iRank ) + { +// debugprint(" Found %s has rank %u\n", (char*)pUser->name, iRank ); + sprintf( szNameToShow, TXT_WOL_USERRANK, (char*)pUser->name, iRank ); + } + else + strcpy( szNameToShow, (char*)pUser->name ); + + static int iLatencyBarX = 124*RESFACTOR - DIBWidth( DibIconInfos[ DIBICON_LATENCY ].pDIB ) - 5 - 16; + + // If we have had a chance to request pings to the player, there'll be some avg. results waiting for us. + int iLatencyBarWidth = 0; + int iLatency; + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); +// debugprint( "player %s ip address %i\n", szNameToShow, UserIP ); + if( UserIP && pNetUtil->GetAvgPing( UserIP, &iLatency ) == S_OK ) + { +// debugprint( "player %s latency %i\n", szNameToShow, iLatency ); + if( iLatency == -1 ) + iLatency = 0; + iLatencyBarWidth = iLatency * fLatencyToIconWidth; + } + } + pListToUse->Add_Item( szNameToShow, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB, NULL, ICON_DIB, + (void*)DibIconInfos[ DIBICON_LATENCY ].pDIB, ICON_DIB, iLatencyBarX, 2, iLatencyBarWidth ); + } + else + pListToUse->Add_Item( (char*)pUser->name, NULL, pIcon1, ICON_DIB, NULL, (void*)pUser, NULL, pIcon2, ICON_DIB ); + + pUser = pUser->next; + } + if( pStaticUsers ) + { + // Display number of users in channel. + char szCount[100]; + sprintf( szCount, TXT_WOL_USERLIST, iUserCount ); + pStaticUsers->Set_Text( szCount ); + } + // Reset multiselectedness, color, and item text for a user. Slow. + // (What a bloody, bloody hack.) + for( int iUser = 0; iUser != iUsersSaved; iUser++ ) + { + int iFind = pListToUse->Find( pUsersSaved[ iUser ].szName ); // Finds any item beginning with szName... + if( iFind != -1 ) + { + if( CurrentLevel == WOL_LEVEL_INGAMECHANNEL ) + { + if( pUsersSaved[ iUser ].House != HOUSE_NONE ) + { + // Append house text to item string, as we found a valid house name after the name, above. + char szItem[ 120 ]; + WritePlayerListItem( szItem, pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].House ); + pListToUse->Set_Item( iFind, szItem ); + } + if( pUsersSaved[ iUser ].bAccept ) + { + // Player was marked "accepted" before. If he has one now, it's because he is the host. + // Else it was an accepted icon before, so put one in again now. (a-hacking-we-will-go) + if( !bItemMarkedAccepted( iFind ) ) + MarkItemAccepted( iFind, true ); + } + if( *pUsersSaved[ iUser ].szExtra ) + pListToUse->Set_Item_ExtraDataString( iFind, pUsersSaved[ iUser ].szExtra ); + } + if( pUsersSaved[ iUser ].bFlagged ) + pListToUse->MultiSelect( iFind, true ); +// debugprint( " Restoring color of %s as %i.\n", pUsersSaved[ iUser ].szName, pUsersSaved[ iUser ].pColorRemap ); + pListToUse->Set_Item_Color( iFind, pUsersSaved[ iUser ].pColorRemap ); + } +// else +// debugprint( "ListChannelUsers() - Couldn't find %s!\n", pUsersSaved[ iUser ].szName ); + } + delete [] pUsersSaved; + if( iListViewIndex ) + pListToUse->Set_View_Index( iListViewIndex ); // Not perfect but should keep list pretty stable on updates. + } + return bChannelOwnerFound; +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedAccepted( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers has an icon pointer in position 0 that + // is either the host icon or the accepted icon. + const IconList_ItemExtras* pItemExtras = pILPlayers->Get_ItemExtras( iIndex ); + return ( pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_OWNER ].pDIB || + pItemExtras->pIcon[0] == (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB ); +} + +//*********************************************************************************************** +bool WolapiObject::MarkItemAccepted( int iIndex, bool bAccept ) +{ + pILPlayers->Flag_To_Redraw(); + if( bAccept ) + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB, ICON_DIB ); + else + //return pILPlayers->Set_Icon( iIndex, 0, NULL, ICON_DIB ); + return pILPlayers->Set_Icon( iIndex, 0, (void*)DibIconInfos[ DIBICON_NOTACCEPT ].pDIB, ICON_DIB ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedReadyToGo( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as "ready to go". + // This is true if the player is marked as "ready" or "need scenario". + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; +// debugprint( "szItem is %s\n", szItem ); + return ( strcmp( szItem, "ready" ) == 0 || strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::MarkItemReadyToGo( int iIndex, const char* szReadyState ) +{ + // Set szReadyState to "ready", "need scenario", or NULL. + // First two cases are regarded as player being ready to go. + pILPlayers->Flag_To_Redraw(); + pILPlayers->Set_Item_ExtraDataString( iIndex, szReadyState ); +} + +//*********************************************************************************************** +bool WolapiObject::bItemMarkedNeedScenario( int iIndex ) +{ + // Returns true if the iIndex'th entry in pILPlayers marks the player as ready to go, but needing scenario download. + const char* szItem = pILPlayers->Get_Item_ExtraDataString( iIndex ); + if( !szItem ) + return false; + return ( strcmp( szItem, "need scenario" ) == 0 ); +} + +//*********************************************************************************************** +void WolapiObject::PullPlayerName_Into_From( char* szDest, const char* szSource ) +{ + // Sets szDest to the "player name" found in szSource. + // Called "player" name because this is mainly designed for use in game channels. + // Player name appears first in item, separated by a space from anything later. + + char* pSpace = strstr( szSource, " " ); + if( !pSpace ) + { + // No space character. Use entire item. + strcpy( szDest, szSource ); + } + else + { + int iSpacePosition = pSpace - szSource; + strncpy( szDest, szSource, iSpacePosition ); + szDest[ iSpacePosition ] = 0; // terminate + } +// debugprint( "PullPlayerName_Into_From: '%s' from '%s', ok?\n", szDest, szSource ); +} + +//*********************************************************************************************** +HousesType WolapiObject::PullPlayerHouse_From( const char* szSource ) +{ + // Pulls the house value out of a player list item in a game channel. + // House appears as the last word, and it's in <>. +// char* pChar = strrchr( szSource, ' ' ); // Last space character. was failing on roy. uni cause of space +// if( !pChar ) +// return HOUSE_NONE; +// ++pChar; +// if( *pChar++ != '<' ) // We know house has to be last, so if not the case, no house in item. +// return HOUSE_NONE; + char* pChar = strrchr( szSource, '<' ); // Last < character. + if( !pChar ) + return HOUSE_NONE; + ++pChar; + int iLen = strlen( pChar ); // Remaining: "housename>" + // Copy remaining string. + char szHouse[ 30 ]; + strcpy( szHouse, pChar ); + *( szHouse + iLen - 1 ) = 0; // Terminate to remove ">" +// debugprint( "PullPlayerHouse_From: '%s' from '%s', ok?\n", szHouse, szSource ); + // pChar is now a valid house name. + +// return HouseTypeClass::From_Name( szHouse ); +#ifdef ENGLISH + // Bloody bloody hell I can't believe there are bugs in RA like the one I deal with here... + if( strcmp( szHouse, "Russia" ) == 0 ) + return HOUSE_USSR; + else + return HouseTypeClass::From_Name( szHouse ); // Fails on "Russia". (Thinks "USSR".) +#else + for( HousesType house = HOUSE_USSR; house <= HOUSE_FRANCE; house++ ) + if( strcmp( Text_String(HouseTypeClass::As_Reference(house).Full_Name()), szHouse ) == 0 ) + return house; +// debugprint( "dohfus" ); // should never happen +// Fatal( "" ); + return HOUSE_USSR; +#endif +} + +//*********************************************************************************************** +void WolapiObject::WritePlayerListItem( char* szDest, const char* szName, HousesType House ) +{ + // Sets szDest to the way a player list item appears in a game channel. + char szHouse[ 50 ]; + strcpy( szHouse, Text_String( HouseTypeClass::As_Reference( House ).Full_Name() ) ); + + int iRank = pNetUtilSink->GetUserRank( szName, bShowRankRA ); // Horrendous inefficiency here, when called for relisting players... + if( iRank ) + sprintf( szDest, TXT_WOL_USERRANKHOUSE, szName, iRank, szHouse ); + else + sprintf( szDest, TXT_WOL_USERHOUSE, szName, szHouse ); +// debugprint( "WritePlayerListItem: '%s', ok?\n", szDest ); +} + +//*********************************************************************************************** +void WolapiObject::RequestPlayerPings() +{ + // Does a RequestPing for every other player listed in pILPlayers. + for( int i = 0; i < pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && !( pUser->flags & CHAT_USER_MYSELF ) ) + { + unsigned long UserIP = pChatSink->GetUserIP( (char*)pUser->name ); + if( UserIP ) + { + int iUnused; + in_addr inaddrUser; + inaddrUser.s_addr = UserIP; + char* szIP = inet_ntoa( inaddrUser ); +// debugprint( "RequestPing of %s, ipaddr of %i, aka %s\n", (char*)pUser->name, UserIP, szIP ); + pNetUtil->RequestPing( szIP, 1000, &iUnused ); + } + } + } +} + +//*********************************************************************************************** +void WolapiObject::SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ) +{ + // Send regular chat message. + + if( *szMessage == 0 ) + return; + + if( strlen( szMessage ) > 4 && szMessage[0] == 63 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + return; + } + if( strlen( szMessage ) > 4 && szMessage[0] == 35 && szMessage[1] == 97 && szMessage[2] == 106 && szMessage[3] == 119 ) + { + int i = atoi( szMessage + 4 ); + if( i >= VOX_ACCOMPLISHED && i <= VOX_LOAD1 ) + Speak( (VoxType)i ); + } + + // Iterate through ILUsers looking for selected entries. Build up a users list of selected + // items. If the list turns out to be blank, send message publicly. + User* pUserListSend = NULL; + User* pUserNew; + User* pUserTail = NULL; + int iCount = ILUsers.Count(); + int iPrivatePrintLen = 1; + for( int i = 0; i != iCount; i++ ) + { + if( ILUsers.bItemIsMultiSelected( i ) ) + { + pUserNew = new User; + *pUserNew = *( (User*)ILUsers.Get_Item_ExtraDataPtr( i ) ); +// debugprint( "Copied %s for sendmessage.\n", pUserNew->name ); + pUserNew->next = NULL; // (We don't want the value that was just copied!) + if( !pUserTail ) + { + // First User in list. + pUserListSend = pUserNew; + } + else + { + pUserTail->next = pUserNew; + } + pUserTail = pUserNew; + iPrivatePrintLen += ( strlen( (char*)pUserNew->name ) + 2 ); // Extra space and comma. + } + } + if( pUserListSend ) + { + // Send private message. + if( !bAction ) + pChat->RequestPrivateMessage( pUserListSend, szMessage ); + else + pChat->RequestPrivateAction( pUserListSend, szMessage ); + char* szPrint = 0; + if( iPrivatePrintLen > 50 ) + { + // Too many users specified to print out. Just say "multiple users". + if( !bAction ) + { + szPrint = new char[ strlen( szMessage ) + 135 ]; + sprintf( szPrint, "%s %s", TXT_WOL_PRIVATETOMULTIPLE, szMessage ); + } + else + { + szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 138 ]; + sprintf( szPrint, "%s %s %s", TXT_WOL_PRIVATETOMULTIPLE, szMyName, szMessage ); + } + } + else + { + if( !bAction ) + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 120 ]; + else + szPrint = new char[ strlen( szMessage ) + iPrivatePrintLen + 125 + strlen( szMyName ) ]; + //strcpy( szPrint, "name ); + if( pUserPrint->next ) + strcat( szPrint, ", " ); + else + strcat( szPrint, ">: " ); + pUserPrint = pUserPrint->next; + } + if( bAction ) + { + strcat( szPrint, szMyName ); + strcat( szPrint, " " ); + } + strcat( szPrint, szMessage ); + } + if( !bAction ) + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + else + { + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } + else + { + // Send public message. + if( !bAction ) + { + // Easter egg related. + if( _stricmp( szMessage, "/nousersounds" ) == 0 ) + { + bEggSounds = false; + return; + } + else if( _stricmp( szMessage, "/usersounds" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEggSounds = true; + return; + } + else if( _stricmp( szMessage, "/8playergames" ) == 0 ) // Left as obvious text in the exe, for someone to find... :-) + { + bEgg8Player = true; + return; + } + HRESULT hRes = pChat->RequestPublicMessage( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicMessage() failed with: " ); +// DebugChatDef( hRes ); + } + } + else + { + HRESULT hRes = pChat->RequestPublicAction( szMessage ); + if( hRes != S_OK ) + { +// debugprint( " RequestPublicAction() failed with: " ); +// DebugChatDef( hRes ); + } + } + char* szPrint = new char[ strlen( szMessage ) + strlen( szMyName ) + 10 ]; + if( !bAction ) + { + sprintf( szPrint, "%s: %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_SELFSPEAKING ); + } + else + { + sprintf( szPrint, "%s %s", szMyName, szMessage ); + PrintMessage( szPrint, WOLCOLORREMAP_ACTION ); + pChatSink->ActionEggSound( szMessage ); + } + delete [] szPrint; + } +} + +//*********************************************************************************************** +bool WolapiObject::ChannelCreate( const char* szChannelName, const char* szKey, bool bGame /* = false */, + int iMaxPlayers /* = 0 */, bool bTournament /* = false */, int iLobby /* = 0 */, + CREATEGAMEINFO::GAMEKIND GameKind /* = red alert */ ) +{ + // Create a channel. + // szKey is NULL if a public channel is to be created, else channel password. + + // Returns true if everything goes okay. + + if( pChatSink->bJoined ) + { + // This never happens. Here just in case. +// debugprint( "WolapiObject::ChannelCreate called when bJoined is true!\n" ); + return false; +// Fatal( "WolapiObject::ChannelCreate called when bJoined is true!" ); + } + + Channel ChannelNew; + + // Prepare the struct. + memset( &ChannelNew, 0, sizeof( ChannelNew ) ); + + if( !bGame ) + { + // ChannelNew.type = 0; 0 for chat channel. + strcpy( (char*)ChannelNew.name, szChannelName ); + } + else + { + ChannelNew.type = GAME_TYPE; + ChannelNew.maxUsers = iMaxPlayers; + ChannelNew.tournament = bTournament; + // Channel 'reserved' stores GameKind in the highest byte, and + // lobby number to return to in the lower three bytes. + // Note: If lobby number is -1 (no lobby to return to), it's encoded as 0x00FFFFFF + ChannelNew.reserved = ( iLobby & 0x00FFFFFF ) | GameKind; + strcpy( (char*)ChannelNew.name, szChannelName ); + } + +// debugprint( "RequestChannelCreate(), channel name: '%s'\n", szChannelName ); + + if( szKey ) + strcpy( (char*)ChannelNew.key, szKey ); + + WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelCreateWait = true; + + HRESULT hRes = pChat->RequestChannelCreate( &ChannelNew ); + if( !SUCCEEDED( hRes ) ) + { +// debugprint( "RequestChannelCreate() call failed:" ); + DebugChatDef( hRes ); + return false; + } + pChatSink->bIgnoreChannelLists = true; // Turn off response to channel lists. + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelCreateWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + pChatSink->bIgnoreChannelLists = false; + + if( pChatSink->bRequestChannelCreateWait || !pChatSink->bJoined ) + return false; // Timed out or callback got fail value. + + if( bGame ) + iLobbyReturnAfterGame = iLobby; + + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoFindPage() +{ + // User presses find/page button. + SimpleEditDlgClass* pFindPageDlg; + + // Ask user for user desired. + Fancy_Text_Print( TXT_NONE, 0, 0, TBLACK, TBLACK, TPF_TEXT ); // Required before String_Pixel_Width() call, for god's sake. + pFindPageDlg = new SimpleEditDlgClass( 400, TXT_WOL_PAGELOCATE, TXT_WOL_USERNAMEPROMPT, WOL_NAME_LEN_MAX ); + pFindPageDlg->SetButtons( TXT_WOL_LOCATE, Text_String( TXT_CANCEL ), TXT_WOL_PAGE ); + bPump_In_Call_Back = true; + const char* szNameDlgResult = pFindPageDlg->Show(); + bPump_In_Call_Back = false; + + if( strcmp( szNameDlgResult, Text_String( TXT_CANCEL ) ) == 0 || !*pFindPageDlg->szEdit ) + { + delete pFindPageDlg; + return; + } + + if( strcmp( szNameDlgResult, TXT_WOL_LOCATE ) == 0 ) + { + // Locate user. + HRESULT hRes = Locate( pFindPageDlg->szEdit ); + switch( hRes ) + { + case CHAT_S_FIND_NOTHERE: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOTHERE ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_NOCHAN: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_NOCHAN ); + bPump_In_Call_Back = false; + break; + case CHAT_S_FIND_OFF: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_FIND_OFF ); + bPump_In_Call_Back = false; + break; + case CHAT_E_TIMEOUT: + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + bPump_In_Call_Back = false; + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + { + char* szChannel = (char*)pChatSink->OnFindChannel.name; + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannel ); + char* szFound; + if( iLobby != -1 ) + { + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szLobbyName ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szLobbyName ); + } + else + { + szFound = new char[ strlen( TXT_WOL_FOUNDIN ) + strlen( szChannel ) + 5 ]; + sprintf( szFound, TXT_WOL_FOUNDIN, szChannel ); + } + bPump_In_Call_Back = true; + WWMessageBox().Process( szFound ); + bPump_In_Call_Back = false; + delete [] szFound; + break; + } + } + } + else + { + // Page user. + // Ask user for text to send. + SimpleEditDlgClass* pMessDlg = new SimpleEditDlgClass( 600, TXT_WOL_PAGEMESSAGETITLE, + TXT_WOL_PAGEMESSAGEPROMPT, MAXCHATSENDLENGTH ); + bPump_In_Call_Back = true; + if( strcmp( pMessDlg->Show(), Text_String( TXT_OK ) ) == 0 && *pMessDlg->szEdit ) + { + switch( Page( pFindPageDlg->szEdit, pMessDlg->szEdit, true ) ) + { + case CHAT_S_PAGE_NOTHERE: + WWMessageBox().Process( TXT_WOL_PAGE_NOTHERE ); + break; + case CHAT_S_PAGE_OFF: + WWMessageBox().Process( TXT_WOL_PAGE_OFF ); + break; + case CHAT_E_TIMEOUT: + WWMessageBox().Process( TXT_WOL_TIMEOUT ); + break; + case E_FAIL: + GenericErrorMessage(); + break; + case S_OK: + char szMessage[ WOL_NAME_LEN_MAX + 30 ]; + sprintf( szMessage, TXT_WOL_WASPAGED, pFindPageDlg->szEdit ); + PrintMessage( szMessage, WOLCOLORREMAP_LOCALMACHINEMESS ); + break; + } + } + bPump_In_Call_Back = false; + } + + delete pFindPageDlg; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Locate( const char* szUser ) +{ + // Returns HRESULT with possibly customized meanings. + + char* szMessage = new char[ strlen( TXT_WOL_LOCATING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_LOCATING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + + pChatSink->bRequestFindWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestFind()\n" ); + if( !SUCCEEDED( pChat->RequestFind( &userFind ) ) ) + { +// debugprint( "RequestFind() call failed\n" ); + return 0; + } + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestFindWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Find pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestFindWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestFindResult; +} + +//*********************************************************************************************** +HRESULT WolapiObject::Page( const char* szUser, const char* szSend, bool bWaitForResult ) +{ + // Returns HRESULT with possibly customized meanings. + + if( bWaitForResult ) + { + char* szMessage = new char[ strlen( TXT_WOL_PAGING ) + strlen( szUser ) + 5 ]; + sprintf( szMessage, TXT_WOL_PAGING, szUser ); + WWMessageBox().Process( szMessage, TXT_NONE ); + delete [] szMessage; + } + + pChatSink->bRequestPageWait = true; + + User userFind; + strcpy( (char*)userFind.name, szUser ); + +// debugprint( "RequestPage()\n" ); + if( !SUCCEEDED( pChat->RequestPage( &userFind, szSend ) ) ) + { +// debugprint( "RequestPage() call failed\n" ); + return 0; + } + if( !bWaitForResult ) + return 0; + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestPageWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); +// debugprint( ">Page pump\n" ); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestPageWait ) + return CHAT_E_TIMEOUT; + + return pChatSink->hresRequestPageResult; +} + +//*********************************************************************************************** +void WolapiObject::DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ) +{ + // Kick selected users. + + if( CurrentLevel != WOL_LEVEL_INCHATCHANNEL && CurrentLevel != WOL_LEVEL_INLOBBY && CurrentLevel != WOL_LEVEL_INGAMECHANNEL ) + { + PrintMessage( TXT_WOL_YOURENOTINCHANNEL, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else if( !bChannelOwner ) + { + PrintMessage( TXT_WOL_ONLYOWNERCANKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + else + { + int iFound = 0; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't kick yourself. + { + Kick( pUser ); + if( bAndBan ) + Ban( pUser ); + iFound++; + if( iFound < 5 ) + Sound_Effect( (VocType)( VOC_SCREAM1 + ( rand() % 9 ) ) ); + } + } + } + if( !iFound ) + { + PrintMessage( TXT_WOL_NOONETOKICK, WOLCOLORREMAP_LOCALMACHINEMESS ); + Sound_Effect( WOLSOUND_ERROR ); + } + } +} + +//*********************************************************************************************** +bool WolapiObject::Kick( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestUserKick()\n" ); + if( !SUCCEEDED( pChat->RequestUserKick( pUserToKick ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::Ban( User* pUserToKick ) +{ + // Returns false if something terrible happens. +// debugprint( "RequestChannelBan()\n" ); + if( !SUCCEEDED( pChat->RequestChannelBan( (char*)pUserToKick->name, true ) ) ) + { +// debugprint( "RequestUserKick() call failed\n" ); + return false; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoSquelch( IconListClass* pILUsersOrPlayers ) +{ + // Squelch/unsquelch selected users. + bool bFound = false; + for( int i = 0; i < pILUsersOrPlayers->Count(); i++ ) + { + if( pILUsersOrPlayers->bItemIsMultiSelected( i ) ) + { + User* pUser = (User*)pILUsersOrPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser ) + { + if( strcmp( (char*)pUser->name, szMyName ) != 0 ) // Don't squelch yourself. + { + Squelch( pUser ); +// char szMess[ 150 ]; +// if( Squelch( pUser ) ) +// sprintf( szMess, TXT_WOL_USERISSQUELCHED, (char*)pUser->name ); +// else +// sprintf( szMess, TXT_WOL_USERISNOTSQUELCHED, (char*)pUser->name ); +// WOL_PrintMessage( chatlist, szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + + bFound = true; + pILUsersOrPlayers->Flag_To_Redraw(); + } + else + PrintMessage( TXT_WOL_CANTSQUELCHSELF, WOLCOLORREMAP_LOCALMACHINEMESS ); + } + } + } + if( bFound ) + { + Sound_Effect( VOC_SQUISH ); + ListChannelUsers(); // Refresh displayed user list. + } +} + +//*********************************************************************************************** +bool WolapiObject::Squelch( User* pUserToSquelch ) +{ + // Returns true if user is now squelched, false if not squelched. + // Sets User pointer flags value. +// debugprint( "Squelch:: pUser is %i, flags is %i\n", pUserToSquelch, pUserToSquelch->flags ); + + if( pUserToSquelch->flags & CHAT_USER_SQUELCHED ) + { + pChat->SetSquelch( pUserToSquelch, false ); + pUserToSquelch->flags &= ~CHAT_USER_SQUELCHED; + return false; + } + pChat->SetSquelch( pUserToSquelch, true ); + pUserToSquelch->flags |= CHAT_USER_SQUELCHED; + return true; +} + +//*********************************************************************************************** +void WolapiObject::DoOptions() +{ + // Show options dialog. + bPump_In_Call_Back = true; + WOL_Options_Dialog( this, false ); + bPump_In_Call_Back = false; + // Set trigger for an immediate channel list update, in case local lobby games filter was changed. + dwTimeNextChannelUpdate = ::timeGetTime(); +} + +//*********************************************************************************************** +bool WolapiObject::DoLadder() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_LADDERSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; +#ifdef ENGLISH + //return SpawnBrowser( "http://www.westwood.com/ra_ladders.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +#ifdef GERMAN +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_german.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#else +// return SpawnBrowser( "http://www.westwood.com/ra_ladders_french.html" ); + return SpawnBrowser( "http://www.westwood.com/westwoodonline/tournaments/redalert/index.html" ); +#endif +#endif + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoHelp() +{ + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_HELPSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + const char* szURL; + if( pChat->GetHelpURL( &szURL ) == S_OK ) + return SpawnBrowser( szURL ); + GenericErrorMessage(); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoWebRegistration() +{ + // Get the executable name from the registry. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWARE\\Westwood\\Register", 0, KEY_READ, &hKey ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + char szPath[ _MAX_PATH + 1 ]; + DWORD dwBufSize = _MAX_PATH; + if( RegQueryValueEx( hKey, "InstallPath", 0, NULL, (LPBYTE)szPath, &dwBufSize ) != ERROR_SUCCESS ) + { + GenericErrorMessage(); + return false; + } + RegCloseKey( hKey ); +// debugprint( "Registration app is '%s'\n", szPath ); + + bPump_In_Call_Back = true; + if( WWMessageBox().Process( TXT_WOL_WEBREGISTRATIONSHELL, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + ::ShellExecute( NULL, "open", szPath, NULL, ".", SW_SHOW ); + return true; + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::DoGameAdvertising( const Channel* pChannel ) +{ + const char* szURL = URLForGameType( pChannel->type ); + if( !szURL ) + { + GenericErrorMessage(); + return false; + } + + char szQuestion[512]; + sprintf( szQuestion, TXT_WOL_GAMEADVERTSHELL, NameOfGameType( pChannel->type ) ); + bPump_In_Call_Back = true; + if( WWMessageBox().Process( szQuestion, TXT_YES, TXT_NO ) == 0 ) + { + bPump_In_Call_Back = false; + return SpawnBrowser( szURL ); + } + + bPump_In_Call_Back = false; + return false; +} + +//*********************************************************************************************** +bool WolapiObject::SpawnBrowser( const char* szURL ) +{ + // Attempts to launch user's web browser, and monitors it, waiting for user to close it, at which + // point we bring focus back to the game. + + // Loosely based on Dune2000 example. + + bool bSuccess = false; + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + + if( *szWebBrowser ) + { + char szCommandLine[ _MAX_PATH + 300 ]; + sprintf( szCommandLine, "\"%s\" %s", szWebBrowser, szURL ); +// debugprint( "About to CreateProcess: '%s'\n", szCommandLine ); + Hide_Mouse(); + BlackPalette.Set( FADE_PALETTE_FAST, Call_Back ); +// ::ShowWindow( MainWindow, SW_SHOWMINIMIZED ); + SeenPage.Clear(); + if( ::CreateProcess( NULL, + szCommandLine, // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + FALSE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent’s environment block. + NULL, // Use parent’s starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi ) ) // Pointer to PROCESS_INFORMATION structure. + { + if( pi.hProcess ) + { +// debugprint( "CreateProcess: '%s'\n", szCommandLine ); + bSuccess = true; + ::WaitForInputIdle( pi.hProcess, 5000 ); + bPump_In_Call_Back = true; + for( ; ; ) + { + DWORD dwActive; + Call_Back(); + Sleep( 200 ); + ::GetExitCodeProcess( pi.hProcess, &dwActive ); + if( dwActive != STILL_ACTIVE || cancel_current_msgbox ) + { + // Either user closed the browser app, or game is starting and we should return focus to game. + cancel_current_msgbox = false; + ::SetForegroundWindow( MainWindow ); + ::ShowWindow( MainWindow, SW_RESTORE ); + break; + } + if( ::GetTopWindow( NULL ) == MainWindow ) + { + ::ShowWindow( MainWindow, SW_RESTORE ); // In case it was topmost but minimized. + break; + } + } + bPump_In_Call_Back = false; + GamePalette.Set( FADE_PALETTE_FAST, Call_Back ); + Show_Mouse(); + } + } + } + + if( !bSuccess ) + { + // This was the old way - does not pop you back into game when finished... + if( (int)::ShellExecute( NULL, NULL, szURL, NULL, ".", SW_SHOW ) <= 32 ) + { +// debugprint( "ShellExecute\n" ); + // ShellExecute failed as well. Just print a message instead. + GamePalette.Set(); + ::ShowWindow( MainWindow, SW_RESTORE ); + char szError[ 300 ]; + sprintf( szError, TXT_WOL_CANTLAUNCHBROWSER, szURL ); + Show_Mouse(); + WWMessageBox().Process( szError ); + return false; + } + // (We return immediately after launching in this case.) + GamePalette.Set(); + Show_Mouse(); + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::ChannelListTitle( const char* szTitle ) +{ + strcpy( szChannelListTitle, szTitle ); + bChannelListTitleUpdated = true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Top() +{ + // + +// debugprint( "*** EnterLevel_Top\n" ); + // (Might as well hardcode the channels tree.) + + ChannelListTitle( TXT_WOL_TOPLEVELTITLE ); + pILChannels->Clear(); + //void* pTopIcon = (void*)DibIconInfos[ DIBICON_ACCEPT ].pDIB; + void* pTopIcon = IconForGameType( 0 ); + pILChannels->Add_Item( TXT_WOL_OFFICIALCHAT, CHANNELTYPE_OFFICIALCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_OFFICIALCHAT ); + pILChannels->Add_Item( TXT_WOL_USERCHAT, CHANNELTYPE_USERCHAT, pTopIcon, ICON_DIB, CHANNELTYPE_USERCHAT ); + pILChannels->Add_Item( TXT_WOL_GAMECHANNELS, CHANNELTYPE_GAMES, pTopIcon, ICON_DIB, CHANNELTYPE_GAMES ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + CurrentLevel = WOL_LEVEL_TOP; + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_OfficialChat() +{ + // + +// debugprint( "*** EnterLevel_OfficialChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_OFFICIALCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_OFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_OFFICIALCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_UserChat() +{ + // + +// debugprint( "*** EnterLevel_UserChat\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_USERCHAT; + if( !UpdateChannels( 0, CHANNELFILTER_UNOFFICIAL, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_USERCHAT ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Games() +{ + // + +// debugprint( "*** EnterLevel_Games\n" ); + // (Might as well hardcode the channels tree.) + + CurrentLevel = WOL_LEVEL_GAMES; + + ChannelListTitle( TXT_WOL_GAMECHANNELS ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNEL_TOP, CHANNELTYPE_TOP, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + + // Create entry for our lobbies at the top. + bool bFound = false; + // (There are actually 2 additional game types at the end of GameTypeInfos - for ws icon and wwonline icon.) + for( int i = 0; i < nGameTypeInfos - 2; i++ ) + { + if( GameTypeInfos[ i ].iGameType == GAME_TYPE ) + { + //pILChannels->Add_Item( GameTypeInfos[ i ].szName, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + bFound = true; + break; + } + } + if( !bFound ) + { + // In the production version, this should never happen, as there should always be a gametypeinfo created that matches + // our game type. It depends on the recentness of the WOR file accompanying the wolapi.dll. + pILChannels->Add_Item( TXT_WOL_REDALERTLOBBIES, CHANNELTYPE_LOBBIES, (void*)OldRAGameTypeInfos[ 0 ].pDIB, ICON_DIB, CHANNELTYPE_LOBBIES ); + } + + // A pointer to the GameTypeInfos entry is stored in the item for convenience later. + for( i = 0; i < nGameTypeInfos - 2; i++ ) + { + int iType = GameTypeInfos[ i ].iGameType; + if( iType != GAME_TYPE ) // Else it is our game - skip it here since we put it at the top. + { + if( iType != 2 && iType != 3 && iType != 4 ) // Hack needed for the time being, to prevent the old ra games from being seen. + { + char szHelp[ 200 ]; + sprintf( szHelp, TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE, GameTypeInfos[ i ].szName ); + pILChannels->Add_Item( GameTypeInfos[ i ].szName, szHelp, (void*)GameTypeInfos[ i ].pDIB, ICON_DIB, CHANNELTYPE_GAMESOFTYPE, (void*)&GameTypeInfos[ i ] ); + } + } + } + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ) +{ + // + +// debugprint( "*** EnterLevel_GamesOfType: pGameTypeInfo->szName %s, iGameType %i, URL %s\n", pGameTypeInfo->szName, pGameTypeInfo->iGameType, pGameTypeInfo->szURL ); + + CurrentLevel = WOL_LEVEL_GAMESOFTYPE; + if( !UpdateChannels( pGameTypeInfo->iGameType, CHANNELFILTER_NO, true ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( pGameTypeInfo->szName ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::EnterLevel_Lobbies() +{ + // + +// debugprint( "*** EnterLevel_Lobbies\n" ); + + CurrentLevel = WOL_LEVEL_LOBBIES; + if( !UpdateChannels( 0, CHANNELFILTER_LOBBIES, false ) ) + { + GenericErrorMessage(); + return false; + } + + ChannelListTitle( TXT_WOL_REDALERTLOBBIES ); + pILChannels->Clear(); + pILChannels->Add_Item( TXT_WOL_CHANNELLISTLOADING, CHANNELTYPE_LOADING, NULL, ICON_SHAPE, CHANNELTYPE_LOADING ); + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Disable(); + pShpBtnRefresh->Enable(); + pShpBtnSquelch->Disable(); + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ) +{ + // Called when a chat channel (or lobby) has been successfully joined. +// debugprint( "*** OnEnteringChatChannel '%s'\n", szChannelName ); + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders if this is a lobby. + if( iLobby != -1 ) + RequestLadders( NULL ); + + // Set channels list. + pILChannels->Clear(); + // Add a "return" choice at the top of the channel list, based on where we want to go 'back' to... + if( iLobby == -1 ) + { + switch( CurrentLevel ) + { + case WOL_LEVEL_OFFICIALCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_OFFICIALCHAT, NULL, ICON_SHAPE, CHANNELTYPE_OFFICIALCHAT ); + break; + case WOL_LEVEL_USERCHAT: + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); + break; + default: + // If entering a channel from anywhere else, user must have created the channel. + // Make "back" take them to user channels list. +// if( bICreatedChannel ) // ajw just verifying + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_USERCHAT, NULL, ICON_SHAPE, CHANNELTYPE_USERCHAT ); +/* else + { +// debugprint( "Case that should not occur in OnEnteringChatChannel. CurrentLevel %i\n", CurrentLevel ); + pILChannels->Add_Item( "ERROR in OnEnteringChatChannel", NULL, NULL, ICON_SHAPE, CHANNELTYPE_TOP ); + } +*/ + break; + } + } + else + { + pILChannels->Add_Item( TXT_WOL_CHANNEL_BACK, CHANNELTYPE_LOBBIES, NULL, ICON_SHAPE, CHANNELTYPE_LOBBIES ); + } + + char* szMess; + if( iLobby == -1 ) + { + CurrentLevel = WOL_LEVEL_INCHATCHANNEL; + szMess = new char[ strlen( TXT_WOL_YOUJOINED ) + strlen( szChannelName ) + 5 ]; + sprintf( szMess, TXT_WOL_YOUJOINED, szChannelName ); + ChannelListTitle( szChannelName ); + } + else + { + CurrentLevel = WOL_LEVEL_INLOBBY; + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOUJOINEDLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOUJOINEDLOBBY, szLobbyName ); + ChannelListTitle( szLobbyName ); + iLobbyLast = iLobby; + dwTimeNextChannelUpdate = ::timeGetTime(); // Set trigger for an immediate channel list update. + } + + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; + + // Set users list. + ListChannelUsers(); + + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + Sound_Effect( WOLSOUND_ENTERCHAN ); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + if( CurrentLevel == WOL_LEVEL_INLOBBY ) + pShpBtnRefresh->Enable(); + else + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnExitingChatChannel() +{ + // Called when we successfully ExitChannel, or we get kicked out. (Lobbies included.) + + // Clear users list. + pILUsers->Clear(); + if( pStaticUsers ) + pStaticUsers->Set_Text( TXT_WOL_NOUSERLIST ); + +// debugprint( "*** OnExitingChatChannel() - szChannelNameCurrent '%s', CurrentLevel %i\n", szChannelNameCurrent, CurrentLevel ); + int iLobby = iChannelLobbyNumber( (unsigned char*)szChannelNameCurrent ); + char* szMess; + if( iLobby == -1 ) + { + szMess = new char[ strlen( TXT_WOL_YOULEFT ) + strlen( szChannelNameCurrent ) + 5 ]; + sprintf( szMess, TXT_WOL_YOULEFT, szChannelNameCurrent ); + } + else + { + // Channel is a lobby. + char szLobbyName[ REASONABLELOBBYINTERPRETEDNAMELEN ]; + InterpretLobbyNumber( szLobbyName, iLobby ); + szMess = new char[ strlen( TXT_WOL_YOULEFTLOBBY ) + REASONABLELOBBYINTERPRETEDNAMELEN + 10 ]; + sprintf( szMess, TXT_WOL_YOULEFTLOBBY, szLobbyName ); + } + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + + *szChannelNameCurrent = 0; + CurrentLevel = WOL_LEVEL_INVALID; + + Sound_Effect( WOLSOUND_EXITCHAN ); +} + +//*********************************************************************************************** +bool WolapiObject::ExitChatChannelForGameChannel() +{ + // We are about to try and join/create a game channel, and are currently in a chat channel. + + // Save this channel name, so we can come back to it if game channel join/create fails. + strcpy( szChannelReturnOnGameEnterFail, szChannelNameCurrent ); + + if( !ChannelLeave() ) + { + GenericErrorMessage(); + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, + const CREATEGAMEINFO& CreateGameInfo ) +{ + // Called when a game channel has been successfully joined, while still in chat dialog, + // before game dialog has been created. + // CreateGameInfo is copied to GameInfoCurrent, so that we know what kind of a game we're in during setup. + +// debugprint( "*** OnEnteringGameChannel() - %s\n", szChannelName ); + + CurrentLevel = WOL_LEVEL_INGAMECHANNEL; + strcpy( szChannelNameCurrent, szChannelName ); + + bChannelOwner = bICreatedChannel; +// GameKindCurrent = GameKind; + GameInfoCurrent = CreateGameInfo; + strcpy( GameInfoCurrent.szPassword, CreateGameInfo.szPassword ); + + // Remove shared buttons from wolchat's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + // Set wol buttons enabled/disabled. + pShpBtnLeave->Enable(); + pShpBtnRefresh->Disable(); + pShpBtnSquelch->Enable(); + if( bChannelOwner ) + { + pShpBtnBan->Enable(); + pShpBtnKick->Enable(); + } + else + { + pShpBtnBan->Disable(); + pShpBtnKick->Disable(); + } + + if( CreateGameInfo.GameKind == CREATEGAMEINFO::AMGAME ) + { + if( bShowRankRA ) + { + // Switch to "show AM rankings" mode. + bShowRankRA = false; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + else + { + if( !bShowRankRA ) + { + // Switch to "show RA rankings" mode. + bShowRankRA = true; + bMyRecordUpdated = true; + bShowRankUpdated = true; + } + } + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::OnEnteringGameSetup() +{ + // Called when entering the game setup screen. Controls are initialized. OnEnteringGameChannel + // has just been called earlier. + + // Returns false only if we find there is not host - he must have simultaneously left. + +// // Block until we have a userlist. - Not necessary - always comes immediately following OnJoin. +// if( !UserList() ) +// return false; + + // Request ladders. + RequestLadders( NULL ); + + // Request IP addresses. + RequestIPs( NULL ); + + // Set users list. + if( !ListChannelUsers() ) + { + // No host was found currently in channel! + return false; + } + + if( !pGSupDlg->bHost ) + { + char* szMess = new char[ strlen( TXT_WOL_YOUJOINEDGAME ) + WOL_NAME_LEN_MAX + 5 ]; + char szHostName[ WOL_NAME_LEN_MAX ]; + HostNameFromGameChannelName( szHostName, szChannelNameCurrent ); + sprintf( szMess, TXT_WOL_YOUJOINEDGAME, szHostName ); + PrintMessage( szMess, WOLCOLORREMAP_LOCALMACHINEMESS ); + delete [] szMess; + } + else + PrintMessage( TXT_WOL_YOUCREATEDGAME, WOLCOLORREMAP_LOCALMACHINEMESS ); + + return true; +} + +//*********************************************************************************************** +void WolapiObject::OnFailedToEnterGameChannel() +{ + if( *szChannelReturnOnGameEnterFail == 0 ) + return; + + // This is called when we fail to join/create a game channel. + *szChannelNameCurrent = 0; + + // Because we don't save the channel key as well, assume the usual lobby password. If we fail, we'll return to top level. + HRESULT hRes = ChannelJoin( szChannelReturnOnGameEnterFail, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + OnEnteringChatChannel( szChannelReturnOnGameEnterFail, false, iChannelLobbyNumber( (unsigned char*)szChannelReturnOnGameEnterFail ) ); + break; + default: + // ChannelJoin returned fail value. + // (Now only applies if you could ever enter a game channel from a non-lobby.) + // There is the possibility that the channel we were in disappeared in the instant between leaving it and + // failing to join the game channel. Or, the channel has a password, that we didn't record. In either + // case, go back to the top level. + GenericErrorMessage(); + EnterLevel_Top(); + } +} + +//*********************************************************************************************** +void WolapiObject::OnExitingGameChannel() +{ + // This is called after we leave a game channel, while still in the game setup dialog. + + // Remove shared buttons from wolgsup's command list. + pShpBtnDiscon->Zap(); + pShpBtnLeave->Zap(); + pShpBtnRefresh->Zap(); + pShpBtnSquelch->Zap(); + pShpBtnBan->Zap(); + pShpBtnKick->Zap(); + pShpBtnFindpage->Zap(); + pShpBtnOptions->Zap(); + pShpBtnLadder->Zap(); + pShpBtnHelp->Zap(); + + CurrentLevel = WOL_LEVEL_INVALID; + *szChannelNameCurrent = 0; +} + +//*********************************************************************************************** +void WolapiObject::RejoinLobbyAfterGame() +{ + // Called to rejoin lobby after EITHER a game, or the game setup dialog. +//debugprint( "RejoinLobbyAfterGame, iLobbyReturnAfterGame is %i\n", iLobbyReturnAfterGame ); + + if( iLobbyReturnAfterGame == -1 ) + { + // Will never happen presumably, if games are always entered via a lobby chat channel. + // We will naturally reenter the top level. + } + else + { + char szChannelToJoin[ WOL_CHANNAME_LEN_MAX ]; + //sprintf( szChannelToJoin, "Lob_%i_%i", GAME_TYPE, iLobbyReturnAfterGame ); + sprintf( szChannelToJoin, "%s%i", LOB_PREFIX, iLobbyReturnAfterGame ); +//debugprint( "RejoinLobbyAfterGame, channel is %s\n", szChannelToJoin ); + + HRESULT hRes = ChannelJoin( szChannelToJoin, LOBBYPASSWORD ); + switch( hRes ) + { + case S_OK: + //OnEnteringChatChannel( szChannelToJoin, false ); Done automatically now in wol_chat. + break; + default: + // Something went wrong when trying to rejoin the lobby we were in. + // We'll go back to the top level instead, which happens automatically if we do this... + iLobbyReturnAfterGame = -1; + break; + } + } +} + +//*********************************************************************************************** +bool WolapiObject::RequestLadders( const char* szName ) +{ + // If szName is NULL, calls RequestLadderList() until all ladder structs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current ladder list. + // This function does not block. + + if( szName && *szName ) + { +// debugprint( "RequestLadderList( %s )\n", szName ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szName, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + return true; + } + + char szNames[ ( WOL_NAME_LEN_MAX + 1 ) * 30 ]; // Neal says max is actually 25 names requested at once. Do 24... + + pNetUtilSink->DeleteLadderList(); + + // Do not request more than this number of times, to prevent overloads to ladder server. + // If we have that many people in the channel, forget about doing ladders for all of them. + // Probably this will never come into play (except while testing), because lobbies will be limited in # of users. + int iCallLimit = 4; + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + // Reset names string. + *szNames = 0; + // Get 24 users from list and add names to string. + for( int i = 0; i != 24; ++i ) + { + strcat( szNames, (char*)pUser->name ); + strcat( szNames, ":" ); + pUser = pUser->next; + if( !pUser ) + break; + } + // Remove last colon. + szNames[ strlen( szNames ) - 1 ] = 0; + +// debugprint( "RequestLadderList( %s )\n", szNames ); + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_RA, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( !SUCCEEDED( pNetUtil->RequestLadderList( szLadderServerHost, iLadderServerPort, szNames, LADDER_CODE_AM, -1, 0, 0 ) ) ) + { +// debugprint( "RequestLadderList() call failed\n" ); + return false; + } + if( --iCallLimit == 0 ) + return false; + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestIPs( const char* szName ) +{ + // If szName is NULL, calls RequestUserIP() until IPs for all users in pChatSink's current + // list have been asked for. Does not wait for results - these come in asynchronously. The previous list is + // erased before new results come in. + // If szName is valid, asks for specific name only. Result is appended to current IP list. + // This function does not block. + + if( szName && *szName ) + { + User user; + strcpy( (char*)user.name, szName ); +// debugprint( "RequestUserIP( %s )\n", szName ); + if( !SUCCEEDED( pChat->RequestUserIP( &user ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + return true; + } + + // Do all users in current chatsink list. + + pChatSink->DeleteUserIPList(); // Clear old user IPs. (To keep searches fast, if we go in and out of game channels a lot.) + + User* pUser = pChatSink->pUserList; + while( pUser ) + { + if( !( pUser->flags & CHAT_USER_MYSELF ) ) + { + if( !SUCCEEDED( pChat->RequestUserIP( pUser ) ) ) + { +// debugprint( "RequestUserIP() call failed\n" ); + return false; + } + } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::SaveChat() +{ + // Basically, a big hack to avoiding restructuring things so that the dialogs are persistent + // objects. Save the contents of the chat list in the chat dialog so that we can refresh it + // after returning from the game setup dialog (if necessary). + // This turns out to be the easiest and most straightforward way to implement this. + pChatSaveLast = pChatSaveList = NULL; + CHATSAVE* pChatSaveNew; + + for( int i = 0; i != pILChat->Count(); i++ ) + { + pChatSaveNew = new CHATSAVE; + const char* szItem = pILChat->Get_Item( i ); + if( strlen( szItem ) < SAVECHATWIDTH ) + strcpy( pChatSaveNew->szText, szItem ); + const IconList_ItemExtras* pItemExtras = pILChat->Get_ItemExtras( i ); + pChatSaveNew->ItemExtras.pColorRemap = pItemExtras->pColorRemap; + pChatSaveNew->next = NULL; + + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; + } +} + +//*********************************************************************************************** +void WolapiObject::RestoreChat() +{ + // See SaveChat()... + CHATSAVE* pChatSave = pChatSaveList; + while( pChatSave ) + { + PrintMessage( pChatSave->szText, pChatSave->ItemExtras.pColorRemap ); + pChatSave = pChatSave->next; + } +} + +//*********************************************************************************************** +void WolapiObject::AddHostLeftMessageToSavedChat( const char* szName ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + sprintf( pChatSaveNew->szText, TXT_WOL_HOSTLEFTGAME, szName ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::AddMessageToSavedChat( const char* szMessage ) +{ + CHATSAVE* pChatSaveNew; + pChatSaveNew = new CHATSAVE; + strcpy( pChatSaveNew->szText, szMessage ); + pChatSaveNew->ItemExtras.pColorRemap = &ColorRemaps[ WOLCOLORREMAP_LOCALMACHINEMESS ]; + pChatSaveNew->next = NULL; + if( pChatSaveLast ) + pChatSaveLast->next = pChatSaveNew; + else + pChatSaveList = pChatSaveNew; + pChatSaveLast = pChatSaveNew; +} + +//*********************************************************************************************** +void WolapiObject::DeleteSavedChat() +{ + // See SaveChat()... + CHATSAVE* pChatSaveNext; + while( pChatSaveList ) + { + pChatSaveNext = pChatSaveList->next; + delete pChatSaveList; + pChatSaveList = pChatSaveNext; + } +} + +//*********************************************************************************************** +void WolapiObject::GenericErrorMessage() +{ + // Displays generic "something bad happened" error message. + bPump_In_Call_Back = true; + WWMessageBox().Process( TXT_WOL_ERRORMESSAGE ); + bPump_In_Call_Back = false; +} + +//*********************************************************************************************** +bool WolapiObject::GetNameOfBeginningLobby( char* szNameToSet ) +{ + // Checks for game lobbies, sets szNameToSet to the channel name that the new user should enter and returns true if succeeds. + if( !GetLobbyChannels() ) + return false; + + // Chatsink should now have a list of lobbies. + int iCount = 0; + Channel* pChannel = pChatSink->pChannelList; + if( !pChannel ) + // List is empty. + return false; + + // Return the name of the first lobby with less than 50 users. + while( pChannel ) + { + if( pChannel->currentUsers < 50 ) + { + strcpy( szNameToSet, (char*)pChannel->name ); + return true; + } + ++iCount; + pChannel = pChannel->next; + } + + // All lobbies have 50 or more users. So just choose a random one. + int iChoice = ( rand() % iCount ); + pChannel = pChatSink->pChannelList; + for( int i = 0; i != iChoice; i++ ) + pChannel = pChannel->next; + + strcpy( szNameToSet, (char*)pChannel->name ); + + return true; +} + +//*********************************************************************************************** +bool WolapiObject::GetLobbyChannels() +{ + // Modal version of UpdateChannels, for fetching lobby names. + +// // Returns false upon total failure. ajxxx do same for other calls +// WWMessageBox().Process( TXT_WOL_WAIT, TXT_NONE ); + + pChatSink->bRequestChannelListForLobbiesWait = true; + pChatSink->ChannelFilter = CHANNELFILTER_LOBBIES; + +// debugprint( "RequestChannelList() for lobbies\n" ); + if( !SUCCEEDED( pChat->RequestChannelList( 0, false ) ) ) + { +// debugprint( "RequestChannelList() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestChannelListForLobbiesWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestChannelListForLobbiesWait ) + return false; + + return true; +} + +//*********************************************************************************************** +const char* WolapiObject::pGameHostName() +{ + // Returns a POINTER (careful - temporary!) to the name of the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return (char*)pUser->name; + } + } + return NULL; +} + +//*********************************************************************************************** +User* WolapiObject::pGameHost() +{ + // Returns a POINTER (careful - temporary!) to the creator of the game channel we're in, or null. + // Uses players' list as its means of reference. + if( pILPlayers ) + { + for( int i = 0; i != pILPlayers->Count(); i++ ) + { + User* pUser = (User*)pILPlayers->Get_Item_ExtraDataPtr( i ); + if( pUser && pUser->flags & CHAT_USER_CHANNELOWNER ) + return pUser; + } + } + return NULL; +} + +//*********************************************************************************************** +bool WolapiObject::SendGameOpt( const char* szSend, User* pUserPriv ) +{ + // Used during game setup to send public or private game options string. + // If pUserPriv is NULL, message is public, else private to pUserPriv. + if( !pUserPriv ) + { +// debugprint( "Send public game opt: '%s'\n", szSend ); + if( !SUCCEEDED( pChat->RequestPublicGameOptions( szSend ) ) ) + { +// debugprint( "RequestPublicGameOptions() call failed\n" ); + return false; + } + } + else + { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUserPriv->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUserPriv, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } + } + return true; +} + +//*********************************************************************************************** +bool WolapiObject::RequestGameStart() +{ + // Host is starting a game. + +/* + // Block any users that join the channel in the next microsecond from becoming involved, and + // block any users that leave from being recognized as having left. + //what if someone leaves? + // This is done to preserve the integrity of the ChatSink's user list + pWO->pChatSink->bIgnoreJoin = true; +*/ + pChatSink->bRequestGameStartWait = true; + +// debugprint( "RequestGameStart()\n" ); + if( !SUCCEEDED( pChat->RequestGameStart( pChatSink->pUserList ) ) ) + { +// debugprint( "RequestGameStart() call failed\n" ); + return false; + } + + DWORD dwTimeStart = timeGetTime(); + DWORD dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + while( pChatSink->bRequestGameStartWait && timeGetTime() - dwTimeStart < EMERGENCY_TIMEOUT ) + { + while( timeGetTime() < dwTimeNextPump ) + Call_Back(); + pChat->PumpMessages(); + dwTimeNextPump = timeGetTime() + PUMPSLEEPDURATION; + } + + if( pChatSink->bRequestGameStartWait ) + { +// debugprint( "WolapiObject::RequestGameStart returning false\n" ); + pChatSink->bRequestGameStartWait = false; + return false; + } + +// debugprint( "WolapiObject::RequestGameStart returning true\n" ); + return true; +} + +//*********************************************************************************************** +bool WolapiObject::SendGo( const char* szSend ) +{ + // Send a "GO" message to all players included in the list that came back from OnGameStart. + // (Don't just broadcast it. We don't want to include any users that may have joined the channel + // in the last microsecond.) +// debugprint( "SendGo()\n" ); + + User* pUser = pChatSink->pGameUserList; + + while( pUser ) + { +// if( !( pUser->flags & CHAT_USER_MYSELF ) ) Method changed. I now wait for go message to bounce back to me. +// { +// debugprint( "Send private game opt to %s: '%s'\n", (char*)pUser->name, szSend ); + if( !SUCCEEDED( pChat->RequestPrivateGameOptions( pUser, szSend ) ) ) + { +// debugprint( "RequestPrivateGameOptions() call failed\n" ); + return false; + } +// } + pUser = pUser->next; + } + return true; +} + +//*********************************************************************************************** +void WolapiObject::Init_DisconnectPinging() +{ + // Sets us up to begin "disconnect pinging" - the pinging that occurs when connection is broken + // during a tournament game. The idea is to try and figure out who is responsible for the connection + // going down. We do this by repeatedly pinging the opponent and the game results server. The number + // of successful pings is sent in the game results package. + iDisconnectPingCurrent = 0; + for( int i = 0; i != DISCONNECT_PING_COUNT; ++i ) + { + DisconnectPingResult_Server[ i ] = PING_UNSTARTED; + DisconnectPingResult_Opponent[ i ] = PING_UNSTARTED; + } + bDisconnectPingingCompleted = false; + bDoingDisconnectPinging = true; +} + +//*********************************************************************************************** +bool WolapiObject::Pump_DisconnectPinging() +{ + // Called repeatedly and continuously when it seems a tournament game connection with the opponent + // has been broken. Does PumpMessages() and requests new pings when previous results have been received. + // Returns true when the required number of pings have been completed. + + if( ::timeGetTime() > dwTimeNextWolapiPump ) + { + pChat->PumpMessages(); + pNetUtil->PumpMessages(); + dwTimeNextWolapiPump = ::timeGetTime() + WOLAPIPUMPWAIT; + } + + switch( DisconnectPingResult_Server[ iDisconnectPingCurrent ] ) + { + case PING_UNSTARTED: + // Pings have yet to be requested. + // Ping game results server. + int iUnused; + if( *szGameResServerHost1 ) + { +// debugprint( "RequestPing ( gameres server )\n" ); + if( pNetUtil->RequestPing( szGameResServerHost1, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( gameres server ) failed\n" ); + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + } + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_WAITING; + } + else + // We never got an address for the gameresults server. Fake fail result. + DisconnectPingResult_Server[ iDisconnectPingCurrent ] = PING_BAD; + + // Ping opponent. + in_addr inaddr; + char* szIP; + inaddr.s_addr = TournamentOpponentIP; + szIP = inet_ntoa( inaddr ); +// debugprint( "RequestPing ( opponent )\n" ); + if( pNetUtil->RequestPing( szIP, 1000, &iUnused ) != S_OK ) + { +// debugprint( "RequestPing() ( opponent ) failed\n" ); + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_BAD; + } + else + DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] = PING_WAITING; + break; + case PING_WAITING: + // Ping results still pending. (Callback will set vars when results arrive.) + break; + default: + // Ping result for server is in. + if( DisconnectPingResult_Opponent[ iDisconnectPingCurrent ] == PING_WAITING ) + break; + // Both results are in. Begin new ping, or end disconnect pinging. + iDisconnectPingCurrent++; + if( iDisconnectPingCurrent == DISCONNECT_PING_COUNT ) + { + bDisconnectPingingCompleted = true; + bDoingDisconnectPinging = false; + return true; + } + break; + } + return false; +} + +//*********************************************************************************************** +void WolapiObject::DisconnectPingResultsString( char* szStringToSet ) +{ + int iGoodServerPings = 0; + int iGoodPlayerPings = 0; + for( int i = 0; i < DISCONNECT_PING_COUNT; ++i ) + { + if( DisconnectPingResult_Server[ i ] == PING_GOOD ) ++iGoodServerPings; + if( DisconnectPingResult_Opponent[ i ] == PING_GOOD ) ++iGoodPlayerPings; + } + + sprintf( szStringToSet, "%1u/%1u %1u/%1u", iGoodServerPings, DISCONNECT_PING_COUNT, iGoodPlayerPings, DISCONNECT_PING_COUNT ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptionDefaults() +{ + // Get stored defaults for options. + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_READ, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue; + DWORD dwBufSize = sizeof( DWORD ); + if( RegQueryValueEx( hKey, "WOLAPI Find Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bFindEnabled = true; + else + bFindEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Page Enabled", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bPageEnabled = true; + else + bPageEnabled = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Lang Filter", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bLangFilter = true; + else + bLangFilter = (bool)dwValue; + if( RegQueryValueEx( hKey, "WOLAPI Show All Games", 0, NULL, (LPBYTE)&dwValue, &dwBufSize ) != ERROR_SUCCESS ) + bAllGamesShown = true; + else + bAllGamesShown = (bool)dwValue; + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +void WolapiObject::SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ) +{ + // Set options and remember them in registry. + + bFindEnabled = bEnableFind; + bPageEnabled = bEnablePage; + bLangFilter = bLangFilterOn; + bAllGamesShown = bShowAllGames; + + HKEY hKey; + if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, Game_Registry_Key(), 0, KEY_WRITE, &hKey ) == ERROR_SUCCESS ) + { + DWORD dwValue = bFindEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Find Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bPageEnabled ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Page Enabled", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bLangFilter ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Lang Filter", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + dwValue = bAllGamesShown ? 1 : 0; + RegSetValueEx( hKey, "WOLAPI Show All Games", 0, REG_DWORD, (LPBYTE)&dwValue, sizeof( dwValue ) ); + + RegCloseKey( hKey ); + } + pChat->SetFindPage( bFindEnabled, bPageEnabled ); + pChat->SetLangFilter( bLangFilter ); +} + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette() +{ + // Get the palette of the current screen. + // Returns 0 if can't get palette. + // Remember to DeleteObject the HPALETTE if non-zero. + GraphicViewPortClass draw_window( LogicPage->Get_Graphic_Buffer(), + WindowList[WINDOW_MAIN][WINDOWX] + LogicPage->Get_XPos(), + WindowList[WINDOW_MAIN][WINDOWY] + LogicPage->Get_YPos(), + WindowList[WINDOW_MAIN][WINDOWWIDTH], + WindowList[WINDOW_MAIN][WINDOWHEIGHT] ); + LPDIRECTDRAWSURFACE lpDDS = draw_window.Get_Graphic_Buffer()->Get_DD_Surface(); + LPDIRECTDRAWPALETTE lpDDP; + HPALETTE hPal = 0; + if( lpDDS->GetPalette( &lpDDP ) == DD_OK ) + { + PALETTEENTRY pe[256]; + if( lpDDP->GetEntries( 0, 0, 256, pe ) == DD_OK ) + { + LOGPALETTE* pLogPal = (LOGPALETTE*)new char[ sizeof( LOGPALETTE ) + sizeof( PALETTEENTRY ) * 255 ]; + pLogPal->palVersion = 0x300; + pLogPal->palNumEntries = 256; + for( int i = 0; i != 256; i++ ) + { + pLogPal->palPalEntry[i].peRed = pe[i].peRed; + pLogPal->palPalEntry[i].peGreen = pe[i].peGreen; + pLogPal->palPalEntry[i].peBlue = pe[i].peBlue; + pLogPal->palPalEntry[i].peFlags = 0; +// debugprint( "DD Palette %03u: %03u, %03u, %03u\n", i, pe[i].peRed, pe[i].peGreen, pe[i].peBlue ); + } + hPal = CreatePalette( pLogPal ); + delete [] pLogPal; + } +// else +// debugprint( "DD GetEntries failed.\n" ); + } +// else +// debugprint( "DD GetPalette failed.\n" ); + + return hPal; +} + +//*********************************************************************************************** +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ) // Note: pDIB is treated as non-const. +{ + // Converts pDIB's actual pixel data to proper values for a different palette (hPal). + // Obeys convention that index 0 in the DIB's palette should map to transparent. For our purposes, make it black. + + // Set the values of the qNewPalette array to hold the new destination palette index we want + // a bmp palette entry to map to. + unsigned char qNewPalette[ 256 ]; + + LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)pDIB; + + RGBQUAD* pRGBEntry = (RGBQUAD*)( (char*)lpbi + lpbi->biSize ); // This now points to the first entry in the bmp's palette table. + +// debugprint( "Starting rgbquads at %i\n", pRGBEntry ); + + // Index zero is supposed to be transparent. In our case, that means make it black. + qNewPalette[0] = GetNearestPaletteIndex( hPal, RGB( 0, 0, 0 ) ); + pRGBEntry++; + + for( int i = 1; i != 256; i++ ) + { + qNewPalette[i] = GetNearestPaletteIndex( hPal, RGB( pRGBEntry->rgbRed, pRGBEntry->rgbGreen, pRGBEntry->rgbBlue ) ); +// if( iIndex == 1 ) +// debugprint( "Remapping bmp %03u to new %03u\n", i, qNewPalette[i] ); + pRGBEntry++; + } + + // Convert the data to values that match game palette. + int iWidth = DIBWidth( pDIB ); + int iHeight = DIBHeight( pDIB ); + int iSrcPitch = ( iWidth + 3 ) & ~3; + int iLength = iSrcPitch * iHeight; + unsigned char* pBits = (unsigned char*)FindDIBBits( pDIB ); +// debugprint( "First Byte value %03u will become %03u\n", *pBits, qNewPalette[ *pBits ] ); + for( i = 0; i != iLength; i++ ) + { + *pBits++ = qNewPalette[ *pBits ]; + } +} + +/* +//*********************************************************************************************** +char* LoadFileIntoMemory( const char* szFileName, int& iLength ) +{ + // Loads a file into a buffer. + // Delete[] the pointer when you're done with the buffer. + HANDLE hFile; + hFile = CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); + if( hFile == INVALID_HANDLE_VALUE ) + return NULL; + iLength = GetFileSize( hFile, NULL ); + char* pData = new char[ iLength ]; + DWORD dwBytesRead; + ReadFile( hFile, pData, iLength, &dwBytesRead, NULL ); + if( dwBytesRead != iLength ) +// debugprint( "######LoadFileIntoMemory expected %i bytes and got %i\n", iLength, dwBytesRead ); + CloseHandle( hFile ); + return pData; +} +*/ + +//*********************************************************************************************** +void HostNameFromGameChannelName( char* szNameToSet, const char* szChannelName ) +{ + int iApostrophe = strcspn( szChannelName, "'" ); + memcpy( szNameToSet, szChannelName, iApostrophe ); + szNameToSet[ iApostrophe ] = 0; +} + +#endif diff --git a/code/redalert/wolapiob.h b/code/redalert/wolapiob.h new file mode 100644 index 0000000..ba72c13 --- /dev/null +++ b/code/redalert/wolapiob.h @@ -0,0 +1,417 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +// WolapiOb.h +// ajw 07/10/98 + +// Class WolapiObject is mainly a container so I can avoid globals and keep things clean. +// All WOLAPI interfacing will be done through this object. It's lifetime will begin when +// API functions are first needed and end when we are finished with the API - this will +// presumably parallel the duration of the user's connection to WOL. + +#ifndef WOLAPI_H +#define WOLAPI_H + +#include "RAWolapi.h" +#include "dibapi.h" +#include "IconList.h" + +//*********************************************************************************************** +class IconListClass; +class WOL_GameSetupDialog; +class ToolTipClass; + +#define PUMPSLEEPDURATION 300 // Milliseconds between PumpMessages() calls. +#define EMERGENCY_TIMEOUT 40000 // Longest we wait for a wolapi response before terminating everything. + +// Milliseconds between automatic behaviors. +#define WOLAPIPUMPWAIT 300 +#define CHANNELUPDATEWAIT 45000 + +#define WOL_NAME_LEN_MAX 10 // Includes null-terminator. +#define WOL_PASSWORD_LEN 9 // Includes null-terminator. +#define WOL_CHANNAME_LEN_MAX 17 // Includes null-terminator. +#define WOL_CHANKEY_LEN_MAX 9 // Includes null-terminator. + +#define CHAT_USER_SQUELCHED 0x0004 // Will theoretically be added to the api and implemented. + + +#define LOBBYPASSWORD "not_a_valid_password" // password removed per Security requirements - 8/27/2018 + +#define USERCANCELLED 1 +#define PATCHDOWNLOADED 2 +#define PATCHAVOIDED 3 + +// Special hidden descriptors added to channel list items. +// These serve double-duty as tooltip help text. +#define CHANNELTYPE_TOP TXT_WOL_CHANNELTYPE_TOP +#define CHANNELTYPE_OFFICIALCHAT TXT_WOL_CHANNELTYPE_OFFICIALCHAT +#define CHANNELTYPE_USERCHAT TXT_WOL_CHANNELTYPE_USERCHAT +#define CHANNELTYPE_GAMES TXT_WOL_CHANNELTYPE_GAMES +#define CHANNELTYPE_GAMESOFTYPE "GamesOfType" // Not seen. +#define CHANNELTYPE_CHATCHANNEL "ChatChannel" // Not seen. +#define CHANNELTYPE_GAMECHANNEL "GameChannel" // Not seen. +#define CHANNELTYPE_LOADING TXT_WOL_CHANNELTYPE_LOADING +#define CHANNELTYPE_LOBBIES TXT_WOL_CHANNELTYPE_LOBBIES +#define CHANNELTYPE_LOBBYCHANNEL "LobbyChannel" // Not seen. + +enum WOL_LEVEL +{ + WOL_LEVEL_TOP, // Viewing top level menu choices. + WOL_LEVEL_OFFICIALCHAT, // Viewing official chat channels. + WOL_LEVEL_USERCHAT, // Viewing user chat channels. + WOL_LEVEL_INCHATCHANNEL, // In a chat channel. + WOL_LEVEL_GAMES, // Viewing types (skus) of games. + WOL_LEVEL_GAMESOFTYPE, // Viewing game channels of a type. + WOL_LEVEL_INGAMECHANNEL, // In a game channel. + WOL_LEVEL_LOBBIES, // Viewing the game lobbies. + WOL_LEVEL_INLOBBY, // In a "lobby" chat channel. + WOL_LEVEL_INVALID +}; + +struct WOL_GAMETYPEINFO +{ + int iGameType; + char szName[128]; + char szURL[256]; + HDIB hDIB; // DIB handle. + const char* pDIB; // What you get when you GlobalLock hDIB. +}; + +// Header values for game options messages. Note that 0 is not used! +enum WOL_GAMEOPT +{ + WOL_GAMEOPT_REQCOLOR = 1, // REQuest = guest asks game host for a color + WOL_GAMEOPT_INFCOLOR, // INForm = game host tells guests color of a single player (not "accept-canceling") + WOL_GAMEOPT_INFPARAMS, // host tells guests all common game params + WOL_GAMEOPT_REQHOUSE, // guest tells host he's changed house (REQ because it's guest->host) + WOL_GAMEOPT_INFHOUSE, // host tells guests about new house of a single player + WOL_GAMEOPT_REQACCEPT, // guest tells host he accepts current params + WOL_GAMEOPT_INFACCEPT, // host tells guests that a player accepted + WOL_GAMEOPT_INFSTART, // host tell guests to go into wait for start mode + WOL_GAMEOPT_REQSTART, // guest acknowledges WOL_GAMEOPT_INFSTART + WOL_GAMEOPT_REQSTART_BUTNEEDSCENARIO, // guests acks WOL_GAMEOPT_INFSTART and asks for scenario download + WOL_GAMEOPT_INFCANCELSTART, // host tells guests to cancel game start, as a change arrived or player joined/left + WOL_GAMEOPT_INFGO, // host tells everyone to start + WOL_GAMEOPT_INFNEWGUESTPLAYERINFO, // host tells new guest a lot of stuff about everyone that's in the game +}; +enum DIBICON +{ + DIBICON_OWNER, + DIBICON_SQUELCH, + DIBICON_LATENCY, + DIBICON_ACCEPT, + DIBICON_NOTACCEPT, + DIBICON_USER, + DIBICON_PRIVATE, + DIBICON_TOURNAMENT, + DIBICON_VOICE, +}; +#define NUMDIBICONS 9 + +struct DIBICONINFO +{ + char szFile[50]; + HDIB hDIB; + const char* pDIB; +}; + +// See SaveChat()... +#define SAVECHATWIDTH 150 // Wider than text that will fit in the chat list window. +struct CHATSAVE // What we save about each individual list item. +{ + char szText[ SAVECHATWIDTH + 1 ]; + IconList_ItemExtras ItemExtras; // Only color is used. + CHATSAVE* next; +}; + +struct CREATEGAMEINFO +{ + enum GAMEKIND // Gets or'ed with lobby number in channel 'reserved' field. + { + RAGAME = 0x01000000, + CSGAME = 0x02000000, + AMGAME = 0x04000000, + }; + + bool bCreateGame; // True if user confirms game creation. + int iPlayerMax; // NOT number of players, but maximum number allowed into game channel. + int iPlayerCount; // Number of initial human players in game. Set at game launch, used for stats. + bool bTournament; + bool bPrivate; + GAMEKIND GameKind; + char szPassword[ WOL_CHANKEY_LEN_MAX ]; // If not blank, key for private game. +}; + +//*********************************************************************************************** +HPALETTE GetCurrentScreenPalette(); +void RemapDIBToPalette( HPALETTE hPal, const char* pDIB ); // Note: pDIB is treated as non-const. +//char* LoadFileIntoMemory( const char* szFileName, int& iLength ); + +//*********************************************************************************************** +class WolapiObject +{ +public: + WolapiObject(); + virtual ~WolapiObject(); + + IChat* pChat; + IDownload* pDownload; + INetUtil* pNetUtil; + DWORD dwChatAdvise; // Value that identifies the "connection" from chat to chatsink. + DWORD dwDownloadAdvise; + DWORD dwNetUtilAdvise; + + RAChatEventSink* pChatSink; + RADownloadEventSink* pDownloadSink; + RANetUtilEventSink* pNetUtilSink; + + bool bChatShownBefore; + + char szLadderServerHost[ 150 ]; + int iLadderServerPort; + char szGameResServerHost1[ 150 ]; + int iGameResServerPort1; + char szGameResServerHost2[ 150 ]; + int iGameResServerPort2; + + bool bFindEnabled; // I have to maintain these, though wolapi should do it for me... + bool bPageEnabled; // Note they are initialized true, as is currently the case in wol. + bool bLangFilter; // + bool bAllGamesShown; + + bool bEggSounds; // Easter egg related. True = user actions trigger sounds. + bool bEgg8Player; // True = 8 player games can be created. This is hidden so that we don't really have to support the feature... + + WOL_LEVEL CurrentLevel; + WOL_LEVEL LastUpdateChannelCallLevel; + char szMyName[WOL_NAME_LEN_MAX]; // Local user's name, valid while connected. + char szMyRecord[ WOL_NAME_LEN_MAX + 80 ]; + char szMyRecordAM[ WOL_NAME_LEN_MAX + 80 ]; + bool bMyRecordUpdated; // True when szMyRecord has changed and not yet recognized by chat dialog. + char szChannelListTitle[ 100 ]; + bool bChannelListTitleUpdated; + char szChannelNameCurrent[WOL_CHANNAME_LEN_MAX]; + bool bChannelOwner; + char szChannelReturnOnGameEnterFail[WOL_CHANNAME_LEN_MAX]; + + int iLobbyReturnAfterGame; // When in game channel, part of the value of the channel's 'reserved' field. + + bool bReturningAfterGame; + + int iLobbyLast; // Number of last lobby we personally were in. + +// CREATEGAMEINFO::GAMEKIND GameKindCurrent; // Kind of game (Red Alert, CS, AM) we are in game setup for. + CREATEGAMEINFO GameInfoCurrent; // Kind of game (Red Alert, CS, AM, tournament, private) we are in game setup for. + bool bEnableNewAftermathUnits; // Used to pass game parameter back to init only. + + DWORD dwTimeNextWolapiPump; + DWORD dwTimeNextChannelUpdate; + + DIBICONINFO DibIconInfos[ NUMDIBICONS ]; + + HRESULT hresPatchResults; // Used when a patch has been downloaded or cancelled. + + WOL_GameSetupDialog* pGSupDlg; // When in a game channel, setting up a game; ptr to the dialog. + + bool bInGame; // True while playing a game. + bool bConnectionDown; // Flag used while in a game, set to true if connection goes down. + bool bGameServer; // Flag used while in a game, true if game server (host). + + unsigned long TournamentOpponentIP; // Valid while playing a tournament game. IP address of opponent. + + bool bPump_In_Call_Back; // Used to enable PumpMessages during Call_Back(), for when we're in a modal dialog. + + bool bSelfDestruct; // If set true, causes logout and deletion of wolapi object. + + char szWebBrowser[ _MAX_PATH + 1 ]; + + // For "disconnect pinging". + bool bDoingDisconnectPinging; + bool bDisconnectPingingCompleted; + int iDisconnectPingCurrent; + DISCONNECT_PING_STATUS DisconnectPingResult_Server[ DISCONNECT_PING_COUNT ]; + DISCONNECT_PING_STATUS DisconnectPingResult_Opponent[ DISCONNECT_PING_COUNT ]; + + // Used for in-game paging and responding. + char szExternalPager[ WOL_NAME_LEN_MAX ]; // Last person to page me from outside the game, or blank for none. + bool bFreezeExternalPager; + + bool bShowRankRA; // true = view RA rankings, false = view AM rankings + bool bShowRankUpdated; // set true when bShowRankRA value changes + + // Standard wol buttons. + char* pShpDiscon; + char* pShpLeave; + char* pShpRefresh; + char* pShpSquelch; + char* pShpBan; + char* pShpKick; + char* pShpFindpage; + char* pShpOptions; + char* pShpLadder; + char* pShpHelp; + ShapeButtonClass* pShpBtnDiscon; + ShapeButtonClass* pShpBtnLeave; + ShapeButtonClass* pShpBtnRefresh; + ShapeButtonClass* pShpBtnSquelch; + ShapeButtonClass* pShpBtnBan; + ShapeButtonClass* pShpBtnKick; + ShapeButtonClass* pShpBtnFindpage; + ShapeButtonClass* pShpBtnOptions; + ShapeButtonClass* pShpBtnLadder; + ShapeButtonClass* pShpBtnHelp; + ToolTipClass* pTTipDiscon; + ToolTipClass* pTTipLeave; + ToolTipClass* pTTipRefresh; + ToolTipClass* pTTipSquelch; + ToolTipClass* pTTipBan; + ToolTipClass* pTTipKick; + ToolTipClass* pTTipFindpage; + ToolTipClass* pTTipOptions; + ToolTipClass* pTTipLadder; + ToolTipClass* pTTipHelp; + + WOL_GAMETYPEINFO OldRAGameTypeInfos[ 3 ]; // Used for storing old red alert icons only. + +public: + bool bLoggedIn(); + + void LinkToChatDlg( IconListClass* pILChat, IconListClass* pILChannels, IconListClass* pILUsers, StaticButtonClass* pStaticUsers ); + void ClearListPtrs(); + void LinkToGameDlg( IconListClass* pILDisc, IconListClass* pILPlayers ); + + void PrepareButtonsAndIcons(); + + bool bSetupCOMStuff(); + void UnsetupCOMStuff(); + + void PrintMessage( const char* szText, PlayerColorType iColorRemap = PCOLOR_NONE ); + void PrintMessage( const char* szText, RemapControlType* pColorRemap ); + + HRESULT GetChatServer(); + HRESULT AttemptLogin( const char* szName, const char* szPass, bool bPassIsMangled ); + void Logout(); + bool UpdateChannels( int iChannelType, CHANNELFILTER ChannelFilter, bool bAutoping ); + void OnChannelList(); + void ListChannels(); + HRESULT ChannelJoin( const char* szChannelName, const char* szKey ); + HRESULT ChannelJoin( Channel* pChannelToJoin ); + bool ChannelLeave(); +// bool UserList(); + bool ListChannelUsers(); + + bool bItemMarkedAccepted( int iIndex ); + bool MarkItemAccepted( int iIndex, bool bAccept ); + bool bItemMarkedReadyToGo( int iIndex ); + void MarkItemReadyToGo( int iIndex, const char* szReadyState ); + bool bItemMarkedNeedScenario( int iIndex ); + void PullPlayerName_Into_From( char* szDest, const char* szSource ); + HousesType PullPlayerHouse_From( const char* szSource ); + void WritePlayerListItem( char* szDest, const char* szName, HousesType House ); + + void RequestPlayerPings(); + + void SendMessage( const char* szMessage, IconListClass& ILUsers, bool bAction ); + bool ChannelCreate( const char* szChannelName, const char* szKey, bool bGame = false, int iMaxPlayers = 0, + bool bTournament = false, int iLobby = 0, CREATEGAMEINFO::GAMEKIND GameKind = CREATEGAMEINFO::RAGAME ); + void DoFindPage(); + HRESULT Locate( const char* szUser ); + HRESULT Page( const char* szUser, const char* szSend, bool bWaitForResult ); + void DoKick( IconListClass* pILUsersOrPlayers, bool bAndBan ); + bool Kick( User* pUserToKick ); + bool Ban( User* pUserToKick ); + void DoSquelch( IconListClass* pILUsersOrPlayers ); + bool Squelch( User* pUserToSquelch ); + void DoOptions(); + bool DoLadder(); + bool DoHelp(); + bool DoWebRegistration(); + bool DoGameAdvertising( const Channel* pChannel ); + bool SpawnBrowser( const char* szURL ); + + void ChannelListTitle( const char* szTitle ); + bool EnterLevel_Top(); + bool EnterLevel_OfficialChat(); + bool EnterLevel_UserChat(); + bool EnterLevel_Games(); + bool EnterLevel_GamesOfType( WOL_GAMETYPEINFO* pGameTypeInfo ); + bool EnterLevel_Lobbies(); + bool OnEnteringChatChannel( const char* szChannelName, bool bICreatedChannel, int iLobby ); + void OnExitingChatChannel(); + bool ExitChatChannelForGameChannel(); + bool OnEnteringGameChannel( const char* szChannelName, bool bICreatedChannel, const CREATEGAMEINFO& CreateGameInfo ); + bool OnEnteringGameSetup(); + void OnFailedToEnterGameChannel(); + void OnExitingGameChannel(); + void RejoinLobbyAfterGame(); + + bool RequestLadders( const char* szName ); + bool RequestIPs( const char* szName ); + + void SaveChat(); + void RestoreChat(); + void AddHostLeftMessageToSavedChat( const char* szName ); + void AddMessageToSavedChat( const char* szMessage ); + void DeleteSavedChat(); + void GenericErrorMessage(); + + bool GetNameOfBeginningLobby( char* szNameToSet ); + bool GetLobbyChannels(); + + const char* pGameHostName(); + User* pGameHost(); + bool SendGameOpt( const char* szSend, User* pUserPriv ); + bool RequestGameStart(); + bool SendGo( const char* szSend ); + + void Init_DisconnectPinging(); + bool Pump_DisconnectPinging(); + void DisconnectPingResultsString( char* szStringToSet ); + + void SetOptionDefaults(); + void SetOptions( bool bEnableFind, bool bEnablePage, bool bLangFilterOn, bool bShowAllGames ); + +protected: + void GetGameTypeInfo( int iGameType, WOL_GAMETYPEINFO& GameTypeInfo, HPALETTE hPal ); + void* IconForGameType( int iGameType ); + const char* NameOfGameType( int iGameType ) const; + const char* URLForGameType( int iGameType ) const; + +protected: + // Used by the general chat dialog. + IconListClass* pILChat; // Main messages list. + IconListClass* pILChannels; // Channels list. + IconListClass* pILUsers; // Users list. + +// IconListClass* pILDisc; // Main messages list. (pILChat is used.) + IconListClass* pILPlayers; // Players list. + + StaticButtonClass* pStaticUsers; // Title for a users list. Used by main chat dialog only, not by game setup. + + WOL_GAMETYPEINFO* GameTypeInfos; + unsigned int nGameTypeInfos; + + float fLatencyToIconWidth; + + CHATSAVE* pChatSaveList; + CHATSAVE* pChatSaveLast; +}; + +#endif + +#endif diff --git a/code/redalert/woldebug.h b/code/redalert/woldebug.h new file mode 100644 index 0000000..d3aa164 --- /dev/null +++ b/code/redalert/woldebug.h @@ -0,0 +1,32 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + + +#if 0 + +#define debugprogress +#define _ASSERTE( x ) + +#else + +#define debugprint OutputDebugString + +#define _DEBUG +#include "w95trace.h" + +#define debugprogress debugprint( "...%s: %i\n", __FILE__, __LINE__ ) +#define _ASSERTE( x ) if( !(x) ) debugprint( "ASSERT FALSE!\n" ); + +#endif diff --git a/code/redalert/woledit.cpp b/code/redalert/woledit.cpp new file mode 100644 index 0000000..05b6375 --- /dev/null +++ b/code/redalert/woledit.cpp @@ -0,0 +1,164 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * Note: An editbox of this class cannot be made read-only. See comment below. + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "WOLEdit.h" + +//#include "WolDebug.h" + +bool bTabKeyPressedHack = false; + +//*********************************************************************************************** +void WOLEditClass::Draw_Text( char const * text ) +{ + // Only difference between this and EditClass: cursor shows up when + // string is at MaxLength. + + TextPrintType flags; + + if (Has_Focus()) { + flags = TPF_BRIGHT_COLOR; + } else { + flags = (TextPrintType)0; + } + + Conquer_Clip_Text_Print(text, X+1, Y+1, Color, TBLACK, TextFlags | flags, Width-2); + + if (Has_Focus() && // strlen(text) < MaxLength && + (String_Pixel_Width(text) + String_Pixel_Width ("_") < Width-2) ) { + Conquer_Clip_Text_Print( "_", X+1+String_Pixel_Width(text), Y+1, Color, TBLACK, TextFlags | flags); + } +} + +//*********************************************************************************************** +// Override of EditClass::Action, because the base class does not behave correctly in certain circumstances. +// (Escape key is being processed as enter key.) +// Again, I'm not about to change the base class directly, as I'm trying to have as minimal an affect as possible on +// the current game code. -ajw +int WOLEditClass::Action(unsigned flags, KeyNumType & key) +{ + // (Mostly duplicated from base class ::Action) +/* For some painful reason, IsReadOnly is private in the base class, so I can't do the following. + For this reason, don't make a WOLEditClass edit box read-only. + + // + // If this is a read-only edit box, it's a display-only device + // + if (IsReadOnly) { + return(false); + } +*/ + + //debugprint( "WOLEditClass::Action this=%i, flags=0x%x, key=0x%x\n", this, flags, key ); + // + // If the left mouse button is pressed over this gadget, then set the focus to + // this gadget. The event flag is cleared so that no button ID number is returned. + // + if ((flags & LEFTPRESS)) { + flags &= ~LEFTPRESS; + Set_Focus(); + Flag_To_Redraw(); // force to draw cursor + } + + // + // Handle keyboard events here. Normally, the key is added to the string, but if the + // RETURN key is pressed, then the button ID number is returned from the Input() + // function. + // + if ((flags & KEYBOARD) && Has_Focus()) { + + // + // Process the keyboard character. If indicated, consume this keyboard event + // so that the edit gadget ID number is not returned. + // + if (key == KN_ESC) { + + Clear_Focus(); + flags = 0; + + } else { +#ifdef WIN32 + + KeyASCIIType ascii = (KeyASCIIType)(Keyboard->To_ASCII(key) & 0xff); + + // + // Allow numeric keypad presses to map to ascii numbers + // + if ((key & WWKEY_VK_BIT) && ascii >='0' && ascii <= '9') { + + key = (KeyNumType)(key & ~WWKEY_VK_BIT); + if ( (!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key (ascii) ) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + // + // Filter out all special keys except return and backspace + // + if ((!(key & WWKEY_VK_BIT) && ascii >= ' ' && ascii <= 255) + || key == KN_RETURN || key == KN_BACKSPACE) { + + + + if ((!(flags & LEFTRELEASE)) && (!(flags & RIGHTRELEASE))) { + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } else { + if( key == KN_TAB ) + { + bTabKeyPressedHack = true; + } + flags &= ~KEYBOARD; + key = KN_NONE; + } + } + } + +#else //WIN32 + if (Handle_Key(Keyboard->To_ASCII(key))) { + flags &= ~KEYBOARD; + key = KN_NONE; + } + } +#endif //WIN32 + } + else + { + // ajw added +// if( key == ( KN_ESC | WWKEY_RLS_BIT ) && ( key & WWKEY_ALT_BIT ) ) +// { + //Clear_Focus(); + flags = 0; + key = KN_NONE; +// } + } + + return(ControlClass::Action(flags, key)); +} + +#endif diff --git a/code/redalert/woledit.h b/code/redalert/woledit.h new file mode 100644 index 0000000..5f46dea --- /dev/null +++ b/code/redalert/woledit.h @@ -0,0 +1,40 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +#ifdef WOLAPI_INTEGRATION + +/*************************************************************************** + * WOLEditClass -- Derived from EditClass, includes changes I wanted for + * wolapi integration stuff. + * + * HISTORY: 07/17/1998 ajw : Created. + *=========================================================================*/ + +#include "Function.h" + +class WOLEditClass : public EditClass +{ +public: + WOLEditClass(int id, char * text, int max_len, TextPrintType flags, int x, int y, int w, int h, EditStyle style) : + EditClass( id, text, max_len, flags, x, y, w, h, style ) {} + + virtual int Action (unsigned flags, KeyNumType &key); // Override of base + +protected: + virtual void Draw_Text( char const * text ); // Override of base + +}; + +#endif diff --git a/code/redalert/wolstrng.cpp b/code/redalert/wolstrng.cpp new file mode 100644 index 0000000..9ab304e --- /dev/null +++ b/code/redalert/wolstrng.cpp @@ -0,0 +1,804 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// New character strings for wolapi integration. + +//#ifdef WOLAPI_INTEGRATION +#include "function.h" +#include "WolStrng.h" + +//#undef ENGLISH +//#define GERMAN + +#ifdef ENGLISH +#pragma message( "...Building English version..." ) + +// Menu choice for Internet game. +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +// Generic error message, though implies that blame lies with Westwood Online. +const char TXT_WOL_ERRORMESSAGE[] = "Unexpected error occurred communicating with Westwood Online."; +// Connect button on login dialog. +const char TXT_WOL_CONNECT[] = "Connect"; +// Title for login dialog. +const char TXT_WOL_LOGINDIALOG[] = "Westwood Online Login"; +// Appears on login dialog - user login name field. +const char TXT_WOL_NAME[] = "Nickname"; +// Appears on login dialog - user password field. +const char TXT_WOL_PASSWORD[] = "Password"; +// Appears on login dialog - checkbox specifying whether nickname/password should be saved to disk. +const char TXT_WOL_SAVELOGIN[] = "Save"; +// User hit the Escape button to cancel the logging in process. +const char TXT_WOL_LOGINCANCEL[] = "Login cancelled."; + +const char TXT_WOL_MISSINGNAME[] = "Please enter your login nickname."; + +const char TXT_WOL_MISSINGPASSWORD[] = "Please enter your login password."; + +const char TXT_WOL_CANTSAVENICK[] = "Error saving nickname/password."; + +const char TXT_WOL_NICKINUSE[] = "That nickname is in use. Please select another."; + +const char TXT_WOL_BADPASS[] = "Invalid password for this nickname."; + +const char TXT_WOL_TIMEOUT[] = "Connection to Westwood Online timed out."; + +const char TXT_WOL_CONNECTING[] = "Connecting to Westwood Online..."; + +const char TXT_WOL_CANTCONNECT[] = "Could not establish connection to Westwood Online."; +// Appears while connecting and logging in to Westwood Online. +const char TXT_WOL_ATTEMPTLOGIN[] = "Logging in..."; +// Appears while logging out and disconnecting from Westwood Online. +const char TXT_WOL_ATTEMPTLOGOUT[] = "Logging out..."; +// Appears while logging out and disconnecting from Westwood Online after an error has occurred. +const char TXT_WOL_ERRORLOGOUT[] = "Terminating connection with Westwood Online..."; +// Common "please wait" message. +const char TXT_WOL_WAIT[] = "Please wait... communicating with Westwood Online..."; +// Title for the top WW Online level. +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +// Title for the WW Online level where "official" chat channels are listed. +const char TXT_WOL_OFFICIALCHAT[] = "Official Chat"; +// Title for the WW Online level where "user" (in other words, unofficial) chat channels are listed. +const char TXT_WOL_USERCHAT[] = "User Chat"; +// Title for the WW Online level where game channels are listed. +const char TXT_WOL_GAMECHANNELS[] = "Game Channels"; +// Title for the WW Online level where Red Alert game lobbies are listed. +const char TXT_WOL_REDALERTLOBBIES[] = "Red Alert Lobbies"; +// Appears briefly while a list of channels is being downloaded. +const char TXT_WOL_CHANNELLISTLOADING[] = "...downloading..."; + +const char TXT_WOL_YOURENOTINCHANNEL[] = "You are not currently in a chat channel."; +// "Action" button. Causes text entered by user to show up as if they were performing an action, as opposed to speaking. +const char TXT_WOL_ACTION[] = "Action"; +// "Join" button. Allows user to join a channel, game, or WW Online level. +const char TXT_WOL_JOIN[] = "Join"; + +const char TXT_WOL_CANTCREATEINCHANNEL[] = "You can't create a new channel until you exit this channel."; +// "New" button. Allows user to create a new chat channel or game. +const char TXT_WOL_NEWSOMETHING[] = "New"; +// Title for chat channel creation dialog. +const char TXT_WOL_CREATECHANNELTITLE[] = "Create Channel"; + +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel Name: "; +// Prompt for fields where the user must enter a password. +const char TXT_WOL_PASSPROMPT[] = "Password: "; +// Prompt for fields where the user may enter a password, but it is not required. +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Password (optional): "; +// Appears in channel list, as top choice, which the user can use to go back to the top WW Online level. +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +// Appears in channel list, as top choice, which the user can use to go back up one WW Online level. +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOUJOINED[] = "You have joined the %s channel."; +// %s is replaced by the name of a user. +const char TXT_WOL_YOUJOINEDGAME[] = "You have joined %s's game."; +// Message confirming that user created a new game. +const char TXT_WOL_YOUCREATEDGAME[] = "New game created."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOUJOINEDLOBBY[] = "You have entered the %s lobby."; +// %s is replaced by the name of a channel. +const char TXT_WOL_YOULEFT[] = "You have left the %s channel."; +// %s is replaced by the name of a lobby. +const char TXT_WOL_YOULEFTLOBBY[] = "You have left the %s lobby."; +// Title for dialog that prompts user for the password needed to enter a private channel. +const char TXT_WOL_JOINPRIVATETITLE[] = "Join Private Channel"; + +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Enter Channel Password: "; + +const char TXT_WOL_BADCHANKEY[] = "Incorrect channel password."; +// Title for the Page/Locate dialog. Page = send a user a message. Locate = find out where a user is. +const char TXT_WOL_PAGELOCATE[] = "Page/Locate"; +// Appears on Page/Locate dialog. +const char TXT_WOL_USERNAMEPROMPT[] = "User Name: "; +// Text for Page button on dialog. +const char TXT_WOL_PAGE[] = "Page"; +// Text for Locate button on dialog. +const char TXT_WOL_LOCATE[] = "Locate"; +// %s is replaced with name of user being located. +const char TXT_WOL_LOCATING[] = "Locating %s..."; + +const char TXT_WOL_FIND_NOTHERE[] = "The specified user name does not exist."; + +const char TXT_WOL_FIND_NOCHAN[] = "The specified user is currently not in a channel."; + +const char TXT_WOL_FIND_OFF[] = "The specified user has disabled find capability."; +// %s is replaced with name of user being located. +const char TXT_WOL_FOUNDIN[] = "User found in the %s channel."; +// Title for Page dialog. +const char TXT_WOL_PAGEMESSAGETITLE[] = "Page User"; +// Prompt for field in which user enters the message that is to be sent to user. +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message to Send: "; +// %s is replaced with name of user being paged. +const char TXT_WOL_PAGING[] = "Paging %s..."; + +const char TXT_WOL_PAGE_NOTHERE[] = "The specified user is not logged in."; + +const char TXT_WOL_PAGE_OFF[] = "The specified user has disabled page capability."; +// First %s is replaced with user name, second %s with a text message. +const char TXT_WOL_ONPAGE[] = "Page from %s: %s"; +// %s is replaced with name of user being paged. +const char TXT_WOL_WASPAGED[] = "%s was successfully paged."; +// %s is replaced with the name of a user that has just been squelched. (Currently unused.) +//const char TXT_WOL_USERISSQUELCHED[] = "%s has been squelched."; +// %s is replaced with the name of a user that has had squelch removed. (Currently unused.) +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s is no longer squelched."; + +const char TXT_WOL_ONLYOWNERCANKICK[] = "Only the channel owner can kick users out."; +// Both %s replaced with user names. +const char TXT_WOL_USERKICKEDUSER[] = "%s kicked %s out of the channel."; +// %s replaced with user name. +const char TXT_WOL_USERKICKEDYOU[] = "You were kicked out of the channel by %s."; + +const char TXT_WOL_NOONETOKICK[] = "Select the user(s) you wish to kick out."; +// %s replaced with user name. +const char TXT_WOL_USERWASBANNED[] = "%s has been banned from the channel."; +// Title for dialog in which user enters password for new game they are creating. +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Create Private Game"; + +const char TXT_WOL_YOUREBANNED[] = "You've been banned from entering this channel."; +// %s replaced with user name. +const char TXT_WOL_PLAYERLEFTGAME[] = "%s has left the game."; +// %s replaced with user name. +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s has joined the game."; + +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "You've been kicked out of the game."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Red Alert: Ranked %u. Won %u. Lost %u. Points %u."; +// Shows user's ladder ranking and win/loss record. Appears above main chat area. Appended Aftermath ranking. +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Aftermath: Ranked %u. Won %u. Lost %u. Points %u."; +// Used to show brief user ladder ranking in user lists. Example: FredX (Rank 134) +const char TXT_WOL_USERRANK[] = "%s (Rank %u)"; +// No need to translate. +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +// "Rank" translates the same here as above. +const char TXT_WOL_USERRANKHOUSE[] = "%s (Rank %u) <%s>"; +// Button host user presses to start a game they have created. +const char TXT_WOL_STARTBUTTON[] = "Start"; +// Button that guests joining a game press to indicate that they agree to the game rules set up by the host. +const char TXT_WOL_ACCEPTBUTTON[] = "Accept"; +// %s replaced with user name. +const char TXT_WOL_HOSTLEFTGAME[] = "%s has cancelled the game."; +// Appears when game is actually being started. +const char TXT_WOL_WAITINGTOSTART[] = "Launching game..."; +// Tooltip help for WW Online button: disconnect. +const char TXT_WOL_TTIP_DISCON[] = " Leave Westwood Online "; +// Tooltip help for WW Online button: leave current channel. +const char TXT_WOL_TTIP_LEAVE[] = " Leave the channel you are in "; +// Tooltip help for WW Online button: refresh current list. +const char TXT_WOL_TTIP_REFRESH[] = " Refresh current channel list "; +// Tooltip help for WW Online button: squelch user(s). +const char TXT_WOL_TTIP_SQUELCH[] = " Enable/disable incoming message from user(s) "; +// Tooltip help for WW Online button: ban (and kick) user(s). +const char TXT_WOL_TTIP_BAN[] = " Ban user(s) from channel "; +// Tooltip help for WW Online button: kick user(s). +const char TXT_WOL_TTIP_KICK[] = " Kick user(s) out of channel "; +// Tooltip help for WW Online button: find/page. +const char TXT_WOL_TTIP_FINDPAGE[] = " Find or page a user "; +// Tooltip help for WW Online button: show options dialog. +const char TXT_WOL_TTIP_OPTIONS[] = " Set Westwood Online options "; +// Tooltip help for WW Online button: browse game ladder. +const char TXT_WOL_TTIP_LADDER[] = " Browse Red Alert ladders "; +// Tooltip help for WW Online button: show help. +const char TXT_WOL_TTIP_HELP[] = " Show Westwood Online help "; +// Tooltip help. Appears for button host presses to start a game. +const char TXT_WOL_TTIP_START[] = " Start the game "; +// Tooltip help. Appears for button guests press in order to agree to (accept) game rules set up by the host. +const char TXT_WOL_TTIP_ACCEPT[] = " Accept the current game settings "; +// Tooltip help. Appears for the small buttons that allow users to enlarge or diminish the size of channel/user lists. +const char TXT_WOL_TTIP_EXPANDLIST[] = " Expand/contract list "; +// Tooltip for Cancel button during game setup. +const char TXT_WOL_TTIP_CANCELGAME[] = " Go back a level "; +// Tooltip for Join button during chat. +const char TXT_WOL_TTIP_JOIN[] = " Join a chat or game channel "; +// Tooltip for Back button during chat. +const char TXT_WOL_TTIP_BACK[] = " Go back a level "; +// Tooltip for New button during chat. +const char TXT_WOL_TTIP_CREATE[] = " Create a new chat/game channel "; +// Tooltip for Action button. +const char TXT_WOL_TTIP_ACTION[] = " Action message "; + +const char TXT_WOL_OPTFIND[] = "Let others FIND you."; + +const char TXT_WOL_OPTPAGE[] = "Let others PAGE you."; + +const char TXT_WOL_OPTLANGUAGE[] = "Filter out bad language."; +// "Display just the games that were created by someone in the lobby you are currently in." +const char TXT_WOL_OPTGAMESCOPE[] = "Show local lobby games only."; + +const char TXT_WOL_CHANNELGONE[] = "Channel no longer exists."; +// Title for create new game dialog. +const char TXT_WOL_CG_TITLE[] = "Create Game"; +// %i replaced by number of players allowed into game channel. +const char TXT_WOL_CG_PLAYERS[] = "Players: %i"; +// Marks field indicating whether or not this is a tournament game. +const char TXT_WOL_CG_TOURNAMENT[] = "Tournament"; +// Marks field indicating whether or not this is a private game. +const char TXT_WOL_CG_PRIVACY[] = "Private"; + +const char TXT_WOL_CG_RAGAME[] = "Red Alert game"; + +const char TXT_WOL_CG_CSGAME[] = "Counterstrike game"; + +const char TXT_WOL_CG_AMGAME[] = "Aftermath game"; + +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sorry, you must have Counterstrike installed to play this game."; + +const char TXT_WOL_NEEDAFTERMATH[] = "Sorry, you must have Aftermath installed to play this game."; +// %s = name of channel, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doubleclick to join the '%s' channel (%i current users). "; +// %s = name of lobby, %i = number of people in channel. +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doubleclick to join the '%s' lobby (%i current users). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_REDALERT[] = "Red Alert"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Counterstrike"; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_AFTERMATH[] = "Aftermath"; +// %s = name of user, first %i = number of players in channel, second %i = maximum number of players allowed. +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s game (%i players of a maximum %i). "; +// %s = name of user, %i = number of players in channel. +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s game (%i players). "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Private) "; +// Appears in tooltip help for a channel list item. +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournament) "; +// %s is a kind of game, for example, "Dune 2000". +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doubleclick to view %s games. "; + +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Tournament games must be two player games."; +// Shows on game setup screen for private games. %s = password for game. +const char TXT_WOL_PRIVATEPASSWORD[] = "Password: %s"; +// User cannot join game because either he or the game host has hacked the game. +const char TXT_WOL_RULESMISMATCH[] = "Your game is incompatible with the host's!"; +// Message appears when game host presses start button but slow responses cause an automatic cancellation of game start. +const char TXT_WOL_STARTTIMEOUT[] = "Timed out waiting for guest responses! Game start cancelled."; +// Message appears for guests when automatic cancellation occurs. +const char TXT_WOL_STARTCANCELLED[] = "Game start cancelled."; +// Text of button on game setup screen that takes user out of the game channel. +const char TXT_WOL_CANCELGAME[] = "Back"; + +const char TXT_WOL_PATCHQUESTION[] = "An update patch is required for Internet play. Do you want to download it now?"; +// Title of patch download dialog. First %i = current file being downloaded, second %i = total # of files to download. +const char TXT_WOL_DOWNLOADING[] = "Download file %i of %i"; + +const char TXT_WOL_DOWNLOADERROR[] = "An error occurred during file download."; +// Appears on patch download dialog. First %i = current # of bytes downloaded, second %i = total # of bytes to download. +const char TXT_WOL_DOWNLOADBYTES[] = "Received %i bytes out of %i. (%i%%%%)"; +// Appears on patch download dialog. First %i = number of minutes left, second %i = number of seconds left. +const char TXT_WOL_DOWNLOADTIME[] = "Time Remaining: %i min. %i secs."; +// Appended to title of patch download dialog when resuming an interrupted download. %s is the regular title, as above. +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Resumed after interruption.)"; + +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Connecting..."; + +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Locating file..."; + +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Downloading..."; + +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Download complete! Red Alert will now restart in order to apply the update patch."; + +const char TXT_WOL_HELPSHELL[] = "Are you sure you want to launch the Internet browser for Westwood Online help?"; + +const char TXT_WOL_LADDERSHELL[] = "Are you sure you want to launch the Internet browser for the Red Alert ladders?"; + +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "No saved usernames found. Would you like to register a new username for Westwood Online?"; + +const char TXT_WOL_GAMEADVERTSHELL[] = "Are you sure you want to launch the Internet browser for information about %s?"; +// Appears above user list. %i = number of users in the current channel. +const char TXT_WOL_USERLIST[] = "Users %i"; +// Appears above user list to explain why no users are being listed: because the user is not currently in a chat channel. +const char TXT_WOL_NOUSERLIST[] = "(not in a channel)"; + +const char TXT_WOL_CANTCREATEHERE[] = "To start a game, you have to be in a Red Alert lobby."; +// Appears inside game, when connection to WW Online is lost. +const char TXT_WOL_WOLAPIGONE[] = "Connection to Westwood Online has been lost!"; +// Appears after game, when attempting to get back into WW Online. +const char TXT_WOL_WOLAPIREINIT[] = "Connection to Westwood Online was lost. Reinitializing..."; + +const char TXT_WOL_NOTPAGED[] = "Can't respond to page; no one has paged you."; +// Appears briefly in the space for scenario name, in game setup dialog. +const char TXT_WOL_SCENARIONAMEWAIT[] = "waiting for scenario..."; +// Text of button on chat screen that takes user out of a chat channel, or up one WW Online level. +const char TXT_WOL_BACK[] = "Back"; + +const char TXT_WOL_AMDISCNEEDED[] = "The Aftermath disk will be required for this game; please insert it now."; + +const char TXT_WOL_CONFIRMLOGOUT[] = "Are you sure you want to leave Westwood Online?"; +// "Propose a stalemate" button. +const char TXT_WOL_PROPOSE_DRAW[] = "Propose a Draw"; +// Withdraw proposed stalemate button. +const char TXT_WOL_RETRACT_DRAW[] = "Retract Draw Proposal"; +// Accept offered stalemate button. +const char TXT_WOL_ACCEPT_DRAW[] = "Accept Proposed Draw"; +// User proposes that the game be declared a stalemate. +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Are you sure you want to propose a draw?"; +// User accepts the other's offer that the game be a tie. +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Are you sure you want to accept a draw?"; + +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "You have proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s has proposed that the game be declared a draw."; + +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "You have retracted your offer of a draw."; + +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s has retracted the offer of a draw."; +// Message that appears in place of "Mission Accomplished" or "Mission Failed", when game is a draw. +const char TXT_WOL_DRAW[] = "The Game is a Draw"; +// Error message that appears when user's web browser can't be automatically launched. %s is a web site URL. +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Can't launch web browser to open %s!"; + +const char TXT_WOL_CHANNELFULL[] = "That chat/game channel is full."; + +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doubleclick to go to the top level. "; + +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doubleclick to go to the official chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doubleclick to go to the user chat channels level. "; + +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doubleclick to go to the game channels level. "; + +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Loading list from Westwood Online, please wait... "; + +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doubleclick to go to the lobbies level. "; + +const char TXT_WOL_FINDINGLOBBY[] = "Connected - finding available lobby to enter..."; + +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; + +const char TXT_WOL_PRIVATETO[] = "Private to"; + +const char TXT_WOL_CS_MISSIONS[] = "Counterstrike Missions"; + +const char TXT_WOL_AM_MISSIONS[] = "Aftermath Missions"; + +const char TXT_WOL_CANTSQUELCHSELF[] = "You cannot disable viewing of your own messages!"; +// Title of the WW Online options dialog. +const char TXT_WOL_OPTTITLE[] = "Westwood Online Options"; + +const char TXT_WOL_SLOWUNITBUILD[] = "Slow Unit Build"; + +const char TXT_WOL_THEGAMEHOST[] = "The game host"; + +const char TXT_WOL_TTIP_RANKRA[] = " Show Red Alert ladder rankings "; + +const char TXT_WOL_TTIP_RANKAM[] = " Show Aftermath ladder rankings "; + +const char TXT_WOL_OPTRANKAM[] = "Show Aftermath rankings (instead of Red Alert)"; + +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (AND FORFEIT THE GAME)"; + +const char TXT_WOL_DLLERROR_GETIE3[] = "Your version of Windows is out of date. Please upgrade to Windows SP1, or install Internet Explorer 3.0 or higher."; + +const char TXT_WOL_DLLERROR_CALLUS[] = "An unexpected error has occurred. Please contact Westwood technical support."; + +const char TXT_WOL_PRIVATE[] = ""; + +#else + +#ifdef GERMAN +#pragma message( "...Building German version..." ) + +// Replaced "á" with ascii 169 (in octal, 251) in cases where 8 point font is used (see 8point.lbm for why). +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Unerwarteter Fehler trat bei der Kommunikation mit Westwood Online auf."; +const char TXT_WOL_CONNECT[] = "Verbinden"; +const char TXT_WOL_LOGINDIALOG[] = "Westwood-Online-Login"; +const char TXT_WOL_NAME[] = "Spitzname"; +const char TXT_WOL_PASSWORD[] = "Pa\251wort"; +const char TXT_WOL_SAVELOGIN[] = "Speichern"; +const char TXT_WOL_LOGINCANCEL[] = "Login abgebrochen."; +const char TXT_WOL_MISSINGNAME[] = "Bitte geben Sie Ihren Login-Spitznamen ein."; +const char TXT_WOL_MISSINGPASSWORD[] = "Bitte geben Sie Ihr Login-Pa\251wort ein."; +const char TXT_WOL_CANTSAVENICK[] = "Fehler beim Speichern des Spitznamens/Pa\251worts"; +const char TXT_WOL_NICKINUSE[] = "Dieser Spitzname wird bereits verwendet. Bitte w„hlen Sie einen anderen."; +const char TXT_WOL_BADPASS[] = "Ungltiges Pa\251wort fr diesen Spitznamen"; +const char TXT_WOL_TIMEOUT[] = "Verbindung zu Westwood Online unterbrochen"; +const char TXT_WOL_CONNECTING[] = "Verbinde zu Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Verbindung zu Westwood Online konnte nicht hergestellt werden."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Einloggen ... "; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Ausloggen ..."; +const char TXT_WOL_ERRORLOGOUT[] = "Verbindung zu Westwood Online beenden ..."; +const char TXT_WOL_WAIT[] = "Bitte warten ... Verbindung zu Westwood Online wird hergestellt ..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Offizieller Chat"; +const char TXT_WOL_USERCHAT[] = "User-Chat"; +const char TXT_WOL_GAMECHANNELS[] = "Game-Channels"; +const char TXT_WOL_REDALERTLOBBIES[] = "Alarmstufe-Rot-Lobbies"; +const char TXT_WOL_CHANNELLISTLOADING[] = "... Daten werden heruntergeladen ..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Sie befinden sich zur Zeit nicht in einem Chat-Channel."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Teilnehmen"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Sie k”nnen keinen neuen Channel erstellen, bevor Sie diesen Channel verlassen."; +const char TXT_WOL_NEWSOMETHING[] = "Neu"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Channel erstellen"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Channel-Name: "; +const char TXT_WOL_PASSPROMPT[] = "Pa\251wort: "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Pa\251wort (optional): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Sie nehmen am %s-Channel teil."; +const char TXT_WOL_YOUJOINEDGAME[] = "Sie nehmen an %ss Spiel teil."; +const char TXT_WOL_YOUCREATEDGAME[] = "Neues Spiel erstellt."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Sie haben die %s-Lobby betreten."; +const char TXT_WOL_YOULEFT[] = "Sie haben den %s-Channel verlassen."; +const char TXT_WOL_YOULEFTLOBBY[] = "Sie haben die %s-Lobby verlassen."; +const char TXT_WOL_JOINPRIVATETITLE[] = "An privatem Channel teilnehmen"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Channel-Pa\251wort eingeben: "; +const char TXT_WOL_BADCHANKEY[] = "Falsches Channel-Pa\251wort."; +const char TXT_WOL_PAGELOCATE[] = "Senden/Suchen"; +const char TXT_WOL_USERNAMEPROMPT[] = "User-Name: "; +const char TXT_WOL_PAGE[] = "Senden"; +const char TXT_WOL_LOCATE[] = "Suchen"; +const char TXT_WOL_LOCATING[] = "Suche %s..."; +const char TXT_WOL_FIND_NOTHERE[] = "Der gesuchte User-Name existiert nicht."; +const char TXT_WOL_FIND_NOCHAN[] = "Der genannte User befindet sich zur Zeit nicht in einem Channel."; +const char TXT_WOL_FIND_OFF[] = "Der genannte User hat die Suchfunktion ausgeschaltet."; +const char TXT_WOL_FOUNDIN[] = "User wurde im %s-Channel gefunden."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Sender"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Zu sendende Nachricht: "; +const char TXT_WOL_PAGING[] = "Sende an %s ..."; +const char TXT_WOL_PAGE_NOTHERE[] = "Der genannte User ist nicht eingeloggt."; +const char TXT_WOL_PAGE_OFF[] = "Der genannte User hat die Empfangsfunktion ausgeschaltet."; +const char TXT_WOL_ONPAGE[] = "Sende von %s: %s"; +const char TXT_WOL_WASPAGED[] = "Die Nachricht wurde %s erfolgreich zugestellt."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s ist zur Zeit nicht erreichbar."; +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s ist jetzt wieder erreichbar."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Nur der Channel-Besitzer kann andere User hinauswerfen."; +const char TXT_WOL_USERKICKEDUSER[] = "%s hat %s aus dem Channel geworfen."; +const char TXT_WOL_USERKICKEDYOU[] = "Sie wurden von %s aus dem Channel geworfen."; +const char TXT_WOL_NOONETOKICK[] = "W„hlen Sie den/die User, die Sie hinauswerfen m”chten."; +const char TXT_WOL_USERWASBANNED[] = "%s hat keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Privates Spiel erstellen"; +const char TXT_WOL_YOUREBANNED[] = "Sie haben keinen Zutritt mehr zu diesem Channel."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s hat das Spiel verlassen."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s hat an dem Spiel teilgenommen."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Sie wurden aus dem Spiel geworfen."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alarmstufe Rot: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Vergeltungsschlag: Pl %u. Siege %u. Niederl %u. Pkte %u."; +const char TXT_WOL_USERRANK[] = "%s (Platz %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Platz %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "Start"; +const char TXT_WOL_ACCEPTBUTTON[] = "Best„tigen"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s hat das Spiel abgebrochen."; +const char TXT_WOL_WAITINGTOSTART[] = "Spiel wird gestartet ..."; +const char TXT_WOL_TTIP_DISCON[] = " Westwood Online verlassen"; +const char TXT_WOL_TTIP_LEAVE[] = " Derzeitigen Channel verlassen "; +const char TXT_WOL_TTIP_REFRESH[] = " Channel-Liste aktualisieren "; +const char TXT_WOL_TTIP_SQUELCH[] = " Nachrichteneingang von User(n) ein/ausschalten"; +const char TXT_WOL_TTIP_BAN[] = " User(n) Zutritt zum Channel verwehren "; +const char TXT_WOL_TTIP_KICK[] = " User aus dem Channel werfen "; +const char TXT_WOL_TTIP_FINDPAGE[] = " User suchen oder an User senden "; +const char TXT_WOL_TTIP_OPTIONS[] = " Westwood-Online-Optionen einstellen "; +const char TXT_WOL_TTIP_LADDER[] = " Alarmstufe-Rot-Tabelle durchsuchen "; +const char TXT_WOL_TTIP_HELP[] = " Westwood-Online-Hilfe anzeigen "; +const char TXT_WOL_TTIP_START[] = " Spiel starten "; +const char TXT_WOL_TTIP_ACCEPT[] = " Aktuelle Spieleinstellungen best„tigen "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Listen erweitern/verkleinern "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_JOIN[] = " An Chat- oder Game-Channel teilnehmen "; +const char TXT_WOL_TTIP_BACK[] = " Einen Level zurck "; +const char TXT_WOL_TTIP_CREATE[] = " Neuen Chat- oder Game-Level erstellen "; +const char TXT_WOL_TTIP_ACTION[] = " Action-Nachricht "; +const char TXT_WOL_OPTFIND[] = "Lassen Sie zu, da\251 andere Sie FINDEN."; +const char TXT_WOL_OPTPAGE[] = "Lassen Sie zu, da\251 andere Ihnen Nachrichten SENDEN."; +const char TXT_WOL_OPTLANGUAGE[] = "Unangemessene Sprache herausfiltern."; +const char TXT_WOL_OPTGAMESCOPE[] = "Nur lokale Spiel-Lobby anzeigen."; +//const char TXT_WOL_OPTTITLE[] = "Optionen"; +const char TXT_WOL_CHANNELGONE[] = "Channel existiert nicht mehr."; +const char TXT_WOL_CG_TITLE[] = "Spiel erstellen"; +const char TXT_WOL_CG_PLAYERS[] = "Spieler: %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Turnier"; +const char TXT_WOL_CG_PRIVACY[] = "Privat"; +const char TXT_WOL_CG_RAGAME[] = "Alarmstufe-Rot-Spiel"; +const char TXT_WOL_CG_CSGAME[] = "Gegenangriff-Spiel"; +const char TXT_WOL_CG_AMGAME[] = "Vergeltungsschlag-Spiel"; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "Sie mssen 'Gegenangriff' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_NEEDAFTERMATH[] = "Sie mssen 'Vergeltungsschlag' installiert haben, um dieses Spiel spielen zu k”nnen."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Doppelklick zur Teilnahme am '%s'-Channel (z.Z. %i User). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Doppelklick zur Teilnahme an der '%s'-Lobby (z.Z. %i User). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alarmstufe Rot"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Gegenangriff"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Vergeltungsschlag"; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " %s-Spiel (%i Spieler von maximal %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " %s-Spiel (%i Spieler). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Privat) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Turnier) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Doppelklicken Sie, um %s-Spiele anzusehen. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Turnierspiele mssen von zwei Spieler gespielt werden."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Pa\251wort: %s"; +const char TXT_WOL_RULESMISMATCH[] = "Ihr Spiel ist mit dem des Host nicht kompatibel!"; +const char TXT_WOL_STARTTIMEOUT[] = "Keine Antworten von G„sten! Spielstart abgebrochen."; +const char TXT_WOL_STARTCANCELLED[] = "Spielstart abgebrochen."; +const char TXT_WOL_CANCELGAME[] = "Zurck"; +const char TXT_WOL_PATCHQUESTION[] = "Ein Update-Patch wird fr Internet-Spiele ben”tigt. M”chten Sie es jetzt herunterladen?"; +const char TXT_WOL_DOWNLOADING[] = "Datei %i von %i herunterladen"; +const char TXT_WOL_DOWNLOADERROR[] = "Ein Fehler trat beim Herunterladen der Dateien auf."; +const char TXT_WOL_DOWNLOADBYTES[] = "%i Bytes von %i erhalten. (%i%%%%)"; +const char TXT_WOL_DOWNLOADTIME[] = "Verbleibende Zeit: %i Min. %i Sek."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (Nach Unterbrechung wiederaufgenommen.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Status: Verbinde ..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Status: Suche Datei ..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Status: Lade herunter ..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "Herunterladen abgeschlossen! Alarmstufe Rot wird jetzt neugestartet, damit das Update-Patch angewendet werden kann."; +const char TXT_WOL_HELPSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Westwood-Online-Hilfe starten m”chten?"; +const char TXT_WOL_LADDERSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr die Alarmstufe-Rot-Tabellen starten m”chten?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Keine gespeicherten User-Namen gefunden. M”chten Sie einen neuen User-Namen fr Westwood Online eintragen?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Sind Sie sicher, da\251 Sie den Internet-Browser fr Informationen ber %s starten m”chten?"; +const char TXT_WOL_USERLIST[] = "User: %i"; +const char TXT_WOL_NOUSERLIST[] = "(nicht in einem Channel)"; +const char TXT_WOL_CANTCREATEHERE[] = "Um ein Spiel zu starten, mssen Sie in der Lobby Alarmstufe Rot sein."; +const char TXT_WOL_WOLAPIGONE[] = "Verbindung zu Westwood Online verloren!"; +const char TXT_WOL_WOLAPIREINIT[] = "Verbindung zu Westwood Online verloren. Verbinde erneut ..."; +const char TXT_WOL_NOTPAGED[] = "Kann keine Antwort senden, niemand hat Ihnen geschrieben."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "Warte auf Szenario ..."; +const char TXT_WOL_BACK[] = "Zurck"; +const char TXT_WOL_AMDISCNEEDED[] = "Die CD 'Vergeltungsschlag' wird fr dieses Spiel ben”tigt, bitte legen Sie sie jetzt ein."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Sind Sie sicher, da\251 Sie Westwood Online verlassen m”chten?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Unentschieden vorschlagen"; +const char TXT_WOL_RETRACT_DRAW[] = "Unentschieden-Vorschlag zurckziehen"; +const char TXT_WOL_ACCEPT_DRAW[] = "Unentschieden-Vorschlag akzeptieren"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden vorschlagen m”chten?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Sind Sie sicher, da\251 Sie ein Unentschieden akzeptieren m”chten?"; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Sie haben vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s hat vorgeschlagen, da\251 das Spiel fr unentschieden erkl„rt wird."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Sie haben Ihr Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s hat das Unentschieden-Angebot zurckgezogen."; +const char TXT_WOL_DRAW[] = "Das Spiel ist unentschieden"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Web-Browser kann %s nicht ”ffnen!"; +const char TXT_WOL_CHANNELFULL[] = "Dieser Chat-/Game-Channel ist voll."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Doppelklicken Sie, um zum obersten Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Doppelklicken Sie, um zum offiziellen Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Doppelklicken Sie, um zum User-Chat-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Doppelklicken Sie, um zum Game-Channel-Level zu gelangen. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Liste von Westwood Online wird geladen, bitte warten... "; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Doppelklicken Sie, um zum Lobby-Level zu gelangen. "; +const char TXT_WOL_FINDINGLOBBY[] = "Verbunden - suche verfgbare Lobby..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = ":"; +const char TXT_WOL_PRIVATETO[] = "Privat an"; +const char TXT_WOL_CS_MISSIONS[] = "Gegenangriff-Missionen"; +const char TXT_WOL_AM_MISSIONS[] = "Vergeltungsschlag-Missionen"; +const char TXT_WOL_CANTSQUELCHSELF[] = "Sie k”nnen die Option zum Lesen Ihrer eigenen Nachrichten nicht ausschalten!"; +const char TXT_WOL_OPTTITLE[] = "Westwood Online-Optionen"; +const char TXT_WOL_SLOWUNITBUILD[] = "Einheitenbau verlangsamen"; +const char TXT_WOL_THEGAMEHOST[] = "Der Spiel-Host"; +const char TXT_WOL_TTIP_RANKRA[] = " Alarmstufe-Rot-Platz anzeigen "; +const char TXT_WOL_TTIP_RANKAM[] = " Vergeltungsschlag-Platz anzeigen "; +const char TXT_WOL_OPTRANKAM[] = "Vergeltungsschlag-Platz anzeigen."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (UND BSSEN SIE DAS SPIEL EIN)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Ihre Version der Windows ist veraltet. Bauen Sie bitte zu den Windows SP1, aus oder installieren Sie Internet Explorer 3,0 oder h”heres."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Ein unerwarteter Fehler ist aufgetreten. Bitte wenden Sie sich an den Technischen Kundendienst."; +const char TXT_WOL_PRIVATE[] = ""; + +#else // FRENCH + +#pragma message( "...Building French version..." ) + +const char TXT_WOL_INTERNETBUTTON[] = "Internet"; +const char TXT_WOL_ERRORMESSAGE[] = "Erreur inattendue lors de la connexion … Westwood Online."; +const char TXT_WOL_CONNECT[] = "Se connecter"; +const char TXT_WOL_LOGINDIALOG[] = "Identifiant … Westwood Online"; +const char TXT_WOL_NAME[] = "Pseudo"; +const char TXT_WOL_PASSWORD[] = "Mot de passe"; +const char TXT_WOL_SAVELOGIN[] = "Sauvegarder"; +const char TXT_WOL_LOGINCANCEL[] = "Ouverture de session annul‚e."; +const char TXT_WOL_MISSINGNAME[] = "Veuillez entrer l'identifiant pour votre pseudo."; +const char TXT_WOL_MISSINGPASSWORD[] = "Veuillez entrer l'identifiant pour votre mot de passe."; +const char TXT_WOL_CANTSAVENICK[] = "Erreur lors de la sauvegarde du pseudo/mot de passe."; +const char TXT_WOL_NICKINUSE[] = "Ce pseudo est d‚j… utilis‚. S‚lectionnez-en un autre."; +const char TXT_WOL_BADPASS[] = "Mot de passe invalide pour ce pseudo."; +const char TXT_WOL_TIMEOUT[] = "Expiration du temps de connexion … Westwood Online."; +const char TXT_WOL_CONNECTING[] = "Connexion … Westwood Online..."; +const char TXT_WOL_CANTCONNECT[] = "Impossible d'‚tablir la connexion … Westwood Online."; +const char TXT_WOL_ATTEMPTLOGIN[] = "Ouverture de la session en cours..."; +const char TXT_WOL_ATTEMPTLOGOUT[] = "Fermeture de la session en cours..."; +const char TXT_WOL_ERRORLOGOUT[] = "Fin de connexion avec Westwood Online..."; +const char TXT_WOL_WAIT[] = "Attendez svp, en communication … Westwood Online..."; +const char TXT_WOL_TOPLEVELTITLE[] = "Westwood Online"; +const char TXT_WOL_OFFICIALCHAT[] = "Conversation officielle"; +const char TXT_WOL_USERCHAT[] = "Conversation utilisateur"; +const char TXT_WOL_GAMECHANNELS[] = "Canaux de jeu"; +const char TXT_WOL_REDALERTLOBBIES[] = "Salons d'Alerte Rouge"; +const char TXT_WOL_CHANNELLISTLOADING[] = "...En cours de t‚l‚chargement..."; +const char TXT_WOL_YOURENOTINCHANNEL[] = "Vous n'ˆtes pas dans un canal de conversation."; +const char TXT_WOL_ACTION[] = "Action"; +const char TXT_WOL_JOIN[] = "Rejoindre"; +const char TXT_WOL_CANTCREATEINCHANNEL[] = "Cr‚ation d'un nouveau canal impossible tant que vous ne quittez pas ce canal."; +const char TXT_WOL_NEWSOMETHING[] = "Nouveau"; +const char TXT_WOL_CREATECHANNELTITLE[] = "Cr‚er un canal"; +const char TXT_WOL_CREATECHANNELPROMPT[] = "Nom du canal : "; +const char TXT_WOL_PASSPROMPT[] = "Mot de passe : "; +const char TXT_WOL_OPTIONALPASSPROMPT[] = "Mot de passe (en option): "; +const char TXT_WOL_CHANNEL_TOP[] = ".. "; +const char TXT_WOL_CHANNEL_BACK[] = ".. "; +const char TXT_WOL_YOUJOINED[] = "Vous avez rejoint le canal %s."; +const char TXT_WOL_YOUJOINEDGAME[] = "Vous rejoignez la partie de %s."; +const char TXT_WOL_YOUCREATEDGAME[] = "Cr‚ation d'une nouvelle partie."; +const char TXT_WOL_YOUJOINEDLOBBY[] = "Vous ˆtes entr‚ dans le salon %s."; +const char TXT_WOL_YOULEFT[] = "Vous avez quitt‚ le canal %s."; +const char TXT_WOL_YOULEFTLOBBY[] = "Vous avez quitt‚ le salon %s."; +const char TXT_WOL_JOINPRIVATETITLE[] = "Rejoindre un canal priv‚"; +const char TXT_WOL_JOINPRIVATEPROMPT[] = "Entrer le mot de passe du canal : "; +const char TXT_WOL_BADCHANKEY[] = "Mot de passe du canal incorrect."; +const char TXT_WOL_PAGELOCATE[] = "Envoyer/Rechercher"; +const char TXT_WOL_USERNAMEPROMPT[] = "Nom de l'utilisateur : "; +const char TXT_WOL_PAGE[] = "Envoyer"; +const char TXT_WOL_LOCATE[] = "Rechercher"; +const char TXT_WOL_LOCATING[] = "Recherche de %s en cours ..."; +const char TXT_WOL_FIND_NOTHERE[] = "Le nom de l'utilisateur sp‚cifi‚ n'existe pas."; +const char TXT_WOL_FIND_NOCHAN[] = "L'utilisateur sp‚cifi‚ n'est pas sur le canal pour le moment."; +const char TXT_WOL_FIND_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction de recherche."; +const char TXT_WOL_FOUNDIN[] = "Utilisateur trouv‚ dans le canal %s."; +const char TXT_WOL_PAGEMESSAGETITLE[] = "Envoyer … l'utilisateur"; +const char TXT_WOL_PAGEMESSAGEPROMPT[] = "Message … envoyer : "; +const char TXT_WOL_PAGING[] = "Envoi … %s en cours..."; +const char TXT_WOL_PAGE_NOTHERE[] = "L'utilisateur sp‚cifi‚ n'a pas ouvert la session."; +const char TXT_WOL_PAGE_OFF[] = "L'utilisateur sp‚cifi‚ a d‚sactiv‚ la fonction d'envoi de messages."; +const char TXT_WOL_ONPAGE[] = "Envoi de %s : %s"; +const char TXT_WOL_WASPAGED[] = "Envoi … %s r‚ussi."; +//const char TXT_WOL_USERISSQUELCHED[] = "%s a ‚t‚ rejet‚."; // ajw rejete really means squelched? +//const char TXT_WOL_USERISNOTSQUELCHED[] = "%s n'est plus rejet‚."; +const char TXT_WOL_ONLYOWNERCANKICK[] = "Seul le responsable du canal peut expulser des utilisateurs."; +const char TXT_WOL_USERKICKEDUSER[] = "%s expulse %s du canal."; +const char TXT_WOL_USERKICKEDYOU[] = "Vous ˆtes expuls‚ du canal par %s."; +const char TXT_WOL_NOONETOKICK[] = "S‚lectionnez l'(les) utilisateur(s) que vous voulez expulser."; +const char TXT_WOL_USERWASBANNED[] = "%s est exclu du canal."; +const char TXT_WOL_CREATEPRIVGAMETITLE[] = "Cr‚er une partie priv‚e"; +const char TXT_WOL_YOUREBANNED[] = "Vous n'ˆtes pas autoris‚ … entrer dans ce canal."; +const char TXT_WOL_PLAYERLEFTGAME[] = "%s a quitt‚ la partie."; +const char TXT_WOL_PLAYERJOINEDGAME[] = "%s a rejoint la partie."; +const char TXT_WOL_YOUWEREKICKEDFROMGAME[] = "Vous avez ‚t‚ expuls‚ de la partie."; +const char TXT_WOL_PERSONALWINLOSSRECORD[] = "%s. Alerte Rouge: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_PERSONALWINLOSSRECORDAM[]= "%s. Missions M.A.D.: position %u. Vict. %u. D‚f. %u. Pts. %u."; +const char TXT_WOL_USERRANK[] = "%s (Position %u)"; +const char TXT_WOL_USERHOUSE[] = "%s <%s>"; +const char TXT_WOL_USERRANKHOUSE[] = "%s (Position %u) <%s>"; +const char TXT_WOL_STARTBUTTON[] = "D‚marrer"; +const char TXT_WOL_ACCEPTBUTTON[] = "Accepter"; +const char TXT_WOL_HOSTLEFTGAME[] = "%s a annul‚ la partie."; +const char TXT_WOL_WAITINGTOSTART[] = "Lancement de la partie..."; +const char TXT_WOL_TTIP_DISCON[] = " Quitter Westwood Online "; +const char TXT_WOL_TTIP_LEAVE[] = " Quitter le canal o— vous vous trouvez "; +const char TXT_WOL_TTIP_REFRESH[] = " RafraŒchir la liste du canal "; +const char TXT_WOL_TTIP_SQUELCH[] = " Activer/d‚sactiver les messages en provenance de(s) l'utilisateur(s) "; +const char TXT_WOL_TTIP_BAN[] = " Exclure l'/les utilisateur(s)du canal "; +const char TXT_WOL_TTIP_KICK[] = " Expulser l'/les utilisateurs du canal "; +const char TXT_WOL_TTIP_FINDPAGE[] = " Rechercher ou envoyer un message … un utilisateur "; +const char TXT_WOL_TTIP_OPTIONS[] = " R‚gler les options de Westwood Online "; +const char TXT_WOL_TTIP_LADDER[] = " Parcourir les hi‚rarchies d'Alerte Rouge "; +const char TXT_WOL_TTIP_HELP[] = " Afficher l'aide de Westwood Online "; +const char TXT_WOL_TTIP_START[] = " D‚marrer le jeu "; +const char TXT_WOL_TTIP_ACCEPT[] = " Valider les paramŠtres actuels du jeu "; +const char TXT_WOL_TTIP_EXPANDLIST[] = " Compl‚ter/r‚duire la liste "; +const char TXT_WOL_TTIP_CANCELGAME[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_JOIN[] = " Rejoindre un canal de conversation/jeu "; +const char TXT_WOL_TTIP_BACK[] = " Retour au niveau pr‚c‚dent "; +const char TXT_WOL_TTIP_CREATE[] = " Cr‚er un nouveau canal de conversation/jeu "; +const char TXT_WOL_TTIP_ACTION[] = " Message d'action "; +const char TXT_WOL_OPTFIND[] = "Laisser les autres vous RECHERCHER."; +const char TXT_WOL_OPTPAGE[] = "Laisser les autres vous ENVOYER des messages."; +const char TXT_WOL_OPTLANGUAGE[] = "Filtrer les vulgarit‚s."; +const char TXT_WOL_OPTGAMESCOPE[] = "Afficher seulement les parties en salons locaux."; +//const char TXT_WOL_OPTTITLE[] = "Options"; +const char TXT_WOL_CHANNELGONE[] = "Ce canal n'existe plus."; +const char TXT_WOL_CG_TITLE[] = "Cr‚er une partie"; +const char TXT_WOL_CG_PLAYERS[] = "Joueurs : %i"; +const char TXT_WOL_CG_TOURNAMENT[] = "Tournoi"; +const char TXT_WOL_CG_PRIVACY[] = "Priv‚e"; +const char TXT_WOL_CG_RAGAME[] = "Partie Alerte Rouge"; +const char TXT_WOL_CG_CSGAME[] = "Partie Missions Ta‹ga"; +const char TXT_WOL_CG_AMGAME[] = "Partie Missions M.A.D."; +const char TXT_WOL_NEEDCOUNTERSTRIKE[] = "D‚sol‚, vous devez installer Missions Ta‹ga pour jouer cette partie."; +const char TXT_WOL_NEEDAFTERMATH[] = "D‚sol‚, vous devez installer Missions M.A.D. pour jouer cette partie."; +const char TXT_WOL_TTIP_CHANLIST_CHAT[] = " Double-clic pour rejoindre canal %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_CHANLIST_LOBBY[] = " Double-clic pour rejoindre salon %s (%i utilisateurs). "; +const char TXT_WOL_TTIP_REDALERT[] = "Alerte Rouge"; +const char TXT_WOL_TTIP_COUNTERSTRIKE[] = "Missions Ta‹ga"; +const char TXT_WOL_TTIP_AFTERMATH[] = "Missions M.A.D."; +const char TXT_WOL_TTIP_CHANLIST_RAGAME[] = " Partie de %s (%i joueurs pour un max. de %i). "; +const char TXT_WOL_TTIP_CHANLIST_GAME[] = " Partie de %s (%i joueurs). "; +const char TXT_WOL_TTIP_PRIVATEGAME[] = "(Priv‚e) "; +const char TXT_WOL_TTIP_TOURNAMENTGAME[] = "(Tournoi) "; +const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[] = " Double-clic pour afficher les parties %s. "; +const char TXT_WOL_TOURNAMENTPLAYERLIMIT[] = "Les parties en tournoi doivent rassembler deux joueurs."; +const char TXT_WOL_PRIVATEPASSWORD[] = "Mot de passe : %s"; +const char TXT_WOL_RULESMISMATCH[] = "Votre partie n'est pas compatible avec celle du serveur !"; +const char TXT_WOL_STARTTIMEOUT[] = "Expiration du temps de r‚ponse des clients ! D‚marrage du jeu annul‚."; +const char TXT_WOL_STARTCANCELLED[] = "D‚marrage du jeu annul‚."; +const char TXT_WOL_CANCELGAME[] = "Retour"; +const char TXT_WOL_PATCHQUESTION[] = "Un patch mis … jour est n‚cessaire pour le jeu sur Internet. Voulez-vous le t‚l‚charger maintenant ?"; +const char TXT_WOL_DOWNLOADING[] = "T‚l‚charger %i fichier(s) sur %i."; +const char TXT_WOL_DOWNLOADERROR[] = "Erreur lors du t‚l‚chargement du fichier."; +const char TXT_WOL_DOWNLOADBYTES[] = "R‚ception de %i octets sur %i. (%i%%%%)."; +const char TXT_WOL_DOWNLOADTIME[] = "Temps restant : %i min. %i secs."; +const char TXT_WOL_DOWNLOADRESUMED[] = "%s (reprise aprŠs interruption.)"; +const char TXT_WOL_DOWNLOADCONNECTING[] = "Etat : en cours de connexion..."; +const char TXT_WOL_DOWNLOADLOCATING[] = "Etat : recherche du fichier..."; +const char TXT_WOL_DOWNLOADDOWNLOADING[] = "Etat : en cours de t‚l‚chargement..."; +const char TXT_WOL_DOWNLOADEXITWARNING[] = "T‚l‚chargement termin‚ ! Alerte Rouge est relanc‚ pour que le nouveau patch soit pris en compte."; +const char TXT_WOL_HELPSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir l'aide Westwood Online ?"; +const char TXT_WOL_LADDERSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour les hi‚rarchies d'Alerte Rouge ?"; +const char TXT_WOL_WEBREGISTRATIONSHELL[] = "Aucun nom d'utilisateur sauvegard‚. Voulez-vous enregistrer un nouveau nom d'utilisateur pour Westwood Online ?"; +const char TXT_WOL_GAMEADVERTSHELL[] = "Voulez-vous vraiment lancer le navigateur Internet pour obtenir des informations sur %s ?"; +const char TXT_WOL_USERLIST[] = "Utilisateurs %i"; +const char TXT_WOL_NOUSERLIST[] = "(absent du canal)"; +const char TXT_WOL_CANTCREATEHERE[] = "Pour commencer une partie, vous devez ˆtre dans un salon d'Alerte Rouge."; +const char TXT_WOL_WOLAPIGONE[] = "Perte de connexion avec Westwood Online !"; +const char TXT_WOL_WOLAPIREINIT[] = "Perte de connexion avec Westwood Online. R‚initialisation en cours..."; +const char TXT_WOL_NOTPAGED[] = "Impossible de r‚pondre au message ; personne ne vous en a envoy‚."; +const char TXT_WOL_SCENARIONAMEWAIT[] = "En attente du sc‚nario..."; +const char TXT_WOL_BACK[] = "Retour"; +const char TXT_WOL_AMDISCNEEDED[] = "Le CD de Missions M.A.D. est n‚cessaire pour cette partie ; ins‚rez-le maintenant."; +const char TXT_WOL_CONFIRMLOGOUT[] = "Voulez-vous vraiment quitter Westwood Online ?"; +const char TXT_WOL_PROPOSE_DRAW[] = "Proposer une fin avec ‚galit‚"; +const char TXT_WOL_RETRACT_DRAW[] = "Annuler la proposition de fin avec ‚galit‚"; +const char TXT_WOL_ACCEPT_DRAW[] = "Accepter la proposition de fin avec ‚galit‚"; +const char TXT_WOL_PROPOSE_DRAW_CONFIRM[] = "Voulez-vous vraiment proposer une fin avec ‚galit‚ ?"; +const char TXT_WOL_ACCEPT_DRAW_CONFIRM[] = "Voulez-vous vraiment accepter une fin avec ‚galit‚ ?" ; +const char TXT_WOL_DRAW_PROPOSED_LOCAL[] = "Vous proposez de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_PROPOSED_OTHER[] = "%s a propos‚ de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_LOCAL[] = "Vous avez annul‚ votre proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW_RETRACTED_OTHER[] = "%s a annul‚ sa proposition de terminer la partie sans vainqueur ni perdant."; +const char TXT_WOL_DRAW[] = "Match nul"; +const char TXT_WOL_CANTLAUNCHBROWSER[] = "Impossible de lancer le navigateur web pour ouvrir %s !"; +const char TXT_WOL_CHANNELFULL[] = "Ce canal de jeu/conversation est satur‚."; +const char TXT_WOL_CHANNELTYPE_TOP[] = " Double-clic pour retourner au premier niveau. "; +const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[] = " Double-clic pour les canaux de conversation officiels. "; +const char TXT_WOL_CHANNELTYPE_USERCHAT[] = " Double-clic pour les canaux d' utilisateur. "; +const char TXT_WOL_CHANNELTYPE_GAMES[] = " Double-clic pour acc‚der au niveau des canaux de jeu. "; +const char TXT_WOL_CHANNELTYPE_LOADING[] = " Chargement de la liste depuis Westwood Online, veuillez patienter..."; +const char TXT_WOL_CHANNELTYPE_LOBBIES[] = " Double-clic pour acc‚der au niveau des salons. "; +const char TXT_WOL_FINDINGLOBBY[] = "Connection : recherche de salons disponibles..."; +const char TXT_WOL_PRIVATETOMULTIPLE[] = " :"; +const char TXT_WOL_PRIVATETO[] = "Message personnel …"; +const char TXT_WOL_CS_MISSIONS[] = "Missions extraites de Missions Ta‹ga"; +const char TXT_WOL_AM_MISSIONS[] = "Missions extraites de Missions M.A.D."; +const char TXT_WOL_CANTSQUELCHSELF[] = "Vous ne pouvez pas d‚sactiver vos propres messages!"; +const char TXT_WOL_OPTTITLE[] = "Options de Westwood Online"; +const char TXT_WOL_SLOWUNITBUILD[] = "Ralentir la Construction"; +const char TXT_WOL_THEGAMEHOST[] = "Le serveur"; +const char TXT_WOL_TTIP_RANKRA[] = " Afficher les positions d'Alerte Rouge "; +const char TXT_WOL_TTIP_RANKAM[] = " Afficher les positions de Missions M.A.D. "; +const char TXT_WOL_OPTRANKAM[] = "Afficher les positions de Missions M.A.D."; +const char TXT_WOL_CANCELMEANSFORFEIT[] = " (ET RENONCER AU JEU)"; +const char TXT_WOL_DLLERROR_GETIE3[] = "Votre version des Windows est d‚mod‚e. Am‚liorez s'il vous plait aux Windows SP1, ou installez l'Internet Explorer 3,0 ou plus haut."; +const char TXT_WOL_DLLERROR_CALLUS[] = "Une erreur inattendue s'est produite. Veuillez contacter l'assistance technique de Electronic Arts."; +const char TXT_WOL_PRIVATE[] = ""; + +#endif + +#endif + + +//#endif diff --git a/code/redalert/wolstrng.h b/code/redalert/wolstrng.h new file mode 100644 index 0000000..286899a --- /dev/null +++ b/code/redalert/wolstrng.h @@ -0,0 +1,209 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +// Wolstrng.h - this will presumably go away eventually. + +// New character strings. + +extern const char TXT_WOL_INTERNETBUTTON[]; +extern const char TXT_WOL_ERRORMESSAGE[]; +extern const char TXT_WOL_CONNECT[]; +extern const char TXT_WOL_LOGINDIALOG[]; +extern const char TXT_WOL_NAME[]; +extern const char TXT_WOL_PASSWORD[]; +extern const char TXT_WOL_SAVELOGIN[]; +extern const char TXT_WOL_LOGINCANCEL[]; +extern const char TXT_WOL_MISSINGNAME[]; +extern const char TXT_WOL_MISSINGPASSWORD[]; +extern const char TXT_WOL_CANTSAVENICK[]; +extern const char TXT_WOL_NICKINUSE[]; +extern const char TXT_WOL_BADPASS[]; +extern const char TXT_WOL_TIMEOUT[]; +extern const char TXT_WOL_CONNECTING[]; +extern const char TXT_WOL_CANTCONNECT[]; +extern const char TXT_WOL_ATTEMPTLOGIN[]; +extern const char TXT_WOL_ATTEMPTLOGOUT[]; +extern const char TXT_WOL_ERRORLOGOUT[]; +extern const char TXT_WOL_WAIT[]; +extern const char TXT_WOL_OFFICIALCHAT[]; +extern const char TXT_WOL_USERCHAT[]; +extern const char TXT_WOL_YOURENOTINCHANNEL[]; +extern const char TXT_WOL_ACTION[]; +extern const char TXT_WOL_JOIN[]; +extern const char TXT_WOL_CANTCREATEINCHANNEL[]; +extern const char TXT_WOL_NEWSOMETHING[]; +extern const char TXT_WOL_CREATECHANNELTITLE[]; +extern const char TXT_WOL_CREATECHANNELPROMPT[]; +extern const char TXT_WOL_PASSPROMPT[]; +extern const char TXT_WOL_OPTIONALPASSPROMPT[]; +extern const char TXT_WOL_CHANNEL_TOP[]; +extern const char TXT_WOL_CHANNEL_BACK[]; +extern const char TXT_WOL_YOUJOINED[]; +extern const char TXT_WOL_YOUJOINEDGAME[]; +extern const char TXT_WOL_YOUCREATEDGAME[]; +extern const char TXT_WOL_YOUJOINEDLOBBY[]; +extern const char TXT_WOL_YOULEFT[]; +extern const char TXT_WOL_YOULEFTLOBBY[]; +extern const char TXT_WOL_JOINPRIVATETITLE[]; +extern const char TXT_WOL_JOINPRIVATEPROMPT[]; +extern const char TXT_WOL_BADCHANKEY[]; +extern const char TXT_WOL_PAGELOCATE[]; +extern const char TXT_WOL_USERNAMEPROMPT[]; +extern const char TXT_WOL_PAGE[]; +extern const char TXT_WOL_LOCATE[]; +extern const char TXT_WOL_LOCATING[]; +extern const char TXT_WOL_FIND_NOTHERE[]; +extern const char TXT_WOL_FIND_NOCHAN[]; +extern const char TXT_WOL_FIND_OFF[]; +extern const char TXT_WOL_FOUNDIN[]; +extern const char TXT_WOL_PAGEMESSAGETITLE[]; +extern const char TXT_WOL_PAGEMESSAGEPROMPT[]; +extern const char TXT_WOL_PAGING[]; +extern const char TXT_WOL_PAGE_NOTHERE[]; +extern const char TXT_WOL_PAGE_OFF[]; +extern const char TXT_WOL_ONPAGE[]; +extern const char TXT_WOL_WASPAGED[]; +//extern const char TXT_WOL_USERISSQUELCHED[]; +//extern const char TXT_WOL_USERISNOTSQUELCHED[]; +extern const char TXT_WOL_ONLYOWNERCANKICK[]; +extern const char TXT_WOL_USERKICKEDUSER[]; +extern const char TXT_WOL_USERKICKEDYOU[]; +extern const char TXT_WOL_NOONETOKICK[]; +extern const char TXT_WOL_USERWASBANNED[]; +extern const char TXT_WOL_GAMECHANNELS[]; +extern const char TXT_WOL_CHANNELLISTLOADING[]; +extern const char TXT_WOL_CREATEPRIVGAMETITLE[]; +extern const char TXT_WOL_YOUREBANNED[]; +extern const char TXT_WOL_PLAYERLEFTGAME[]; +extern const char TXT_WOL_PLAYERJOINEDGAME[]; +extern const char TXT_WOL_YOUWEREKICKEDFROMGAME[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORD[]; +extern const char TXT_WOL_PERSONALWINLOSSRECORDAM[]; +extern const char TXT_WOL_USERRANK[]; +extern const char TXT_WOL_USERHOUSE[]; +extern const char TXT_WOL_USERRANKHOUSE[]; +extern const char TXT_WOL_STARTBUTTON[]; +extern const char TXT_WOL_ACCEPTBUTTON[]; +extern const char TXT_WOL_HOSTLEFTGAME[]; +extern const char TXT_WOL_WAITINGTOSTART[]; +extern const char TXT_WOL_TTIP_DISCON[]; +extern const char TXT_WOL_TTIP_LEAVE[]; +extern const char TXT_WOL_TTIP_REFRESH[]; +extern const char TXT_WOL_TTIP_SQUELCH[]; +extern const char TXT_WOL_TTIP_BAN[]; +extern const char TXT_WOL_TTIP_KICK[]; +extern const char TXT_WOL_TTIP_FINDPAGE[]; +extern const char TXT_WOL_TTIP_OPTIONS[]; +extern const char TXT_WOL_TTIP_LADDER[]; +extern const char TXT_WOL_TTIP_HELP[]; +extern const char TXT_WOL_TTIP_START[]; +extern const char TXT_WOL_TTIP_ACCEPT[]; +extern const char TXT_WOL_TTIP_EXPANDLIST[]; +extern const char TXT_WOL_TTIP_CANCELGAME[]; +extern const char TXT_WOL_TTIP_JOIN[]; +extern const char TXT_WOL_TTIP_BACK[]; +extern const char TXT_WOL_TTIP_CREATE[]; +extern const char TXT_WOL_TTIP_ACTION[]; +extern const char TXT_WOL_OPTFIND[]; +extern const char TXT_WOL_OPTPAGE[]; +extern const char TXT_WOL_OPTLANGUAGE[]; +extern const char TXT_WOL_OPTGAMESCOPE[]; +extern const char TXT_WOL_OPTTITLE[]; +extern const char TXT_WOL_REDALERTLOBBIES[]; +extern const char TXT_WOL_TOPLEVELTITLE[]; +extern const char TXT_WOL_CHANNELGONE[]; +extern const char TXT_WOL_CG_TITLE[]; +extern const char TXT_WOL_CG_PLAYERS[]; +extern const char TXT_WOL_CG_TOURNAMENT[]; +extern const char TXT_WOL_CG_PRIVACY[]; +extern const char TXT_WOL_CG_RAGAME[]; +extern const char TXT_WOL_CG_CSGAME[]; +extern const char TXT_WOL_CG_AMGAME[]; +extern const char TXT_WOL_NEEDCOUNTERSTRIKE[]; +extern const char TXT_WOL_NEEDAFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_CHAT[]; +extern const char TXT_WOL_TTIP_CHANLIST_LOBBY[]; +extern const char TXT_WOL_TTIP_REDALERT[]; +extern const char TXT_WOL_TTIP_COUNTERSTRIKE[]; +extern const char TXT_WOL_TTIP_AFTERMATH[]; +extern const char TXT_WOL_TTIP_CHANLIST_RAGAME[]; +extern const char TXT_WOL_TTIP_CHANLIST_GAME[]; +extern const char TXT_WOL_TTIP_PRIVATEGAME[]; +extern const char TXT_WOL_TTIP_TOURNAMENTGAME[]; +extern const char TXT_WOL_TTIP_CHANNELTYPE_GAMESOFTYPE[]; +extern const char TXT_WOL_TOURNAMENTPLAYERLIMIT[]; +extern const char TXT_WOL_PRIVATEPASSWORD[]; +extern const char TXT_WOL_RULESMISMATCH[]; +extern const char TXT_WOL_STARTTIMEOUT[]; +extern const char TXT_WOL_CANCELGAME[]; +extern const char TXT_WOL_PATCHQUESTION[]; +extern const char TXT_WOL_DOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADERROR[]; +extern const char TXT_WOL_DOWNLOADBYTES[]; +extern const char TXT_WOL_DOWNLOADTIME[]; +extern const char TXT_WOL_DOWNLOADRESUMED[]; +extern const char TXT_WOL_DOWNLOADCONNECTING[]; +extern const char TXT_WOL_DOWNLOADLOCATING[]; +extern const char TXT_WOL_DOWNLOADDOWNLOADING[]; +extern const char TXT_WOL_DOWNLOADEXITWARNING[]; +extern const char TXT_WOL_HELPSHELL[]; +extern const char TXT_WOL_LADDERSHELL[]; +extern const char TXT_WOL_WEBREGISTRATIONSHELL[]; +extern const char TXT_WOL_GAMEADVERTSHELL[]; +extern const char TXT_WOL_USERLIST[]; +extern const char TXT_WOL_NOUSERLIST[]; +extern const char TXT_WOL_STARTCANCELLED[]; +extern const char TXT_WOL_CANTCREATEHERE[]; +extern const char TXT_WOL_WOLAPIGONE[]; +extern const char TXT_WOL_WOLAPIREINIT[]; +extern const char TXT_WOL_NOTPAGED[]; +extern const char TXT_WOL_SCENARIONAMEWAIT[]; +extern const char TXT_WOL_BACK[]; +extern const char TXT_WOL_AMDISCNEEDED[]; +extern const char TXT_WOL_CONFIRMLOGOUT[]; +extern const char TXT_WOL_PROPOSE_DRAW[]; +extern const char TXT_WOL_RETRACT_DRAW[]; +extern const char TXT_WOL_ACCEPT_DRAW[]; +extern const char TXT_WOL_PROPOSE_DRAW_CONFIRM[]; +extern const char TXT_WOL_ACCEPT_DRAW_CONFIRM[]; +extern const char TXT_WOL_DRAW_PROPOSED_LOCAL[]; +extern const char TXT_WOL_DRAW_PROPOSED_OTHER[]; +extern const char TXT_WOL_DRAW_RETRACTED_LOCAL[]; +extern const char TXT_WOL_DRAW_RETRACTED_OTHER[]; +extern const char TXT_WOL_DRAW[]; +extern const char TXT_WOL_CANTLAUNCHBROWSER[]; +extern const char TXT_WOL_CHANNELFULL[]; +extern const char TXT_WOL_CHANNELTYPE_TOP[]; +extern const char TXT_WOL_CHANNELTYPE_OFFICIALCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_USERCHAT[]; +extern const char TXT_WOL_CHANNELTYPE_GAMES[]; +extern const char TXT_WOL_CHANNELTYPE_LOADING[]; +extern const char TXT_WOL_CHANNELTYPE_LOBBIES[]; +extern const char TXT_WOL_FINDINGLOBBY[]; +extern const char TXT_WOL_PRIVATETOMULTIPLE[]; +extern const char TXT_WOL_PRIVATETO[]; +extern const char TXT_WOL_CS_MISSIONS[]; +extern const char TXT_WOL_AM_MISSIONS[]; +extern const char TXT_WOL_CANTSQUELCHSELF[]; +extern const char TXT_WOL_SLOWUNITBUILD[]; +extern const char TXT_WOL_THEGAMEHOST[]; +extern const char TXT_WOL_TTIP_RANKRA[]; +extern const char TXT_WOL_TTIP_RANKAM[]; +extern const char TXT_WOL_OPTRANKAM[]; +extern const char TXT_WOL_CANCELMEANSFORFEIT[]; +extern const char TXT_WOL_DLLERROR_GETIE3[]; +extern const char TXT_WOL_DLLERROR_CALLUS[]; +extern const char TXT_WOL_PRIVATE[]; + diff --git a/code/redalert/wsnwlink.h b/code/redalert/wsnwlink.h new file mode 100644 index 0000000..86d80c1 --- /dev/null +++ b/code/redalert/wsnwlink.h @@ -0,0 +1,294 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* + * wsnwlink.h + * + * + * Microsoft Windows + * Copyright (C) Microsoft Corporation, 1992-1996. + * Microsoft-specific extensions to the Windows NT IPX/SPX Windows + * Sockets interface. These extensions are provided for use as + * necessary for compatibility with existing applications. They are + * otherwise not recommended for use, as they are only guaranteed to + * work * over the Microsoft IPX/SPX stack. An application which + * uses these * extensions may not work over other IPX/SPX + * implementations. Include this header file after winsock.h and + * wsipx.h. + * + * To open an IPX socket where a particular packet type is sent in + * the IPX header, specify NSPROTO_IPX + n as the protocol parameter + * of the socket() API. For example, to open an IPX socket that + * sets the packet type to 34, use the following socket() call: + * + * s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX + 34); + * + * Below are socket option that may be set or retrieved by specifying + * the appropriate manifest in the "optname" parameter of getsockopt() + * or setsockopt(). Use NSPROTO_IPX as the "level" argument for the + * call. + * + */ + +#ifndef _WSNWLINK_ +#define _WSNWLINK_ + + +/* + * Set/get the IPX packet type. The value specified in the + * optval argument will be set as the packet type on every IPX + * packet sent from this socket. The optval parameter of + * getsockopt()/setsockopt() points to an int. + * + */ + +#define IPX_PTYPE 0x4000 + + +/* + * Set/get the receive filter packet type. Only IPX packets with + * a packet type equal to the value specified in the optval + * argument will be returned; packets with a packet type that + * does not match are discarded. optval points to an int. + * + */ + +#define IPX_FILTERPTYPE 0x4001 + + +/* + * Stop filtering on packet type set with IPX_FILTERPTYPE. + * + */ + +#define IPX_STOPFILTERPTYPE 0x4003 + + +/* + * Set/get the value of the datastream field in the SPX header on + * every packet sent. optval points to an int. + * + */ + +#define IPX_DSTYPE 0x4002 + + +/* + * Enable extended addressing. On sends, adds the element + * "unsigned char sa_ptype" to the SOCKADDR_IPX structure, + * making the total length 15 bytes. On receives, add both + * the sa_ptype and "unsigned char sa_flags" to the SOCKADDR_IPX + * structure, making the total length 16 bytes. The current + * bits defined in sa_flags are: + * + * 0x01 - the received frame was sent as a broadcast + * 0x02 - the received frame was sent from this machine + * + * optval points to a BOOL. + * + */ + +#define IPX_EXTENDED_ADDRESS 0x4004 + + +/* + * Send protocol header up on all receive packets. optval points + * to a BOOL. + * + */ + +#define IPX_RECVHDR 0x4005 + + +/* + * Get the maximum data size that can be sent. Not valid with + * setsockopt(). optval points to an int where the value is + * returned. + * + */ + +#define IPX_MAXSIZE 0x4006 + + +/* + * Query information about a specific adapter that IPX is bound + * to. In a system with n adapters they are numbered 0 through n-1. + * Callers can issue the IPX_MAX_ADAPTER_NUM getsockopt() to find + * out the number of adapters present, or call IPX_ADDRESS with + * increasing values of adapternum until it fails. Not valid + * with setsockopt(). optval points to an instance of the + * IPX_ADDRESS_DATA structure with the adapternum filled in. + * + */ + +#define IPX_ADDRESS 0x4007 + +typedef struct _IPX_ADDRESS_DATA { + INT adapternum; /* input: 0-based adapter number */ + UCHAR netnum[4]; /* output: IPX network number */ + UCHAR nodenum[6]; /* output: IPX node address */ + BOOLEAN wan; /* output: TRUE = adapter is on a wan link */ + BOOLEAN status; /* output: TRUE = wan link is up (or adapter is not wan) */ + INT maxpkt; /* output: max packet size, not including IPX header */ + ULONG linkspeed; /* output: link speed in 100 bytes/sec (i.e. 96 == 9600 bps) */ +} IPX_ADDRESS_DATA, *PIPX_ADDRESS_DATA; + + +/* + * Query information about a specific IPX network number. If the + * network is in IPX's cache it will return the information directly, + * otherwise it will issue RIP requests to find it. Not valid with + * setsockopt(). optval points to an instance of the IPX_NETNUM_DATA + * structure with the netnum filled in. + * + */ + +#define IPX_GETNETINFO 0x4008 + +typedef struct _IPX_NETNUM_DATA { + UCHAR netnum[4]; /* input: IPX network number */ + USHORT hopcount; /* output: hop count to this network, in machine order */ + USHORT netdelay; /* output: tick count to this network, in machine order */ + INT cardnum; /* output: 0-based adapter number used to route to this net */ + /* can be used as adapternum input to IPX_ADDRESS */ + UCHAR router[6]; /* output: MAC address of the next hop router, zeroed if */ + /* the network is directly attached */ +} IPX_NETNUM_DATA, *PIPX_NETNUM_DATA; + + +/* + * Like IPX_GETNETINFO except it *does not* issue RIP requests. If the + * network is in IPX's cache it will return the information, otherwise + * it will fail (see also IPX_RERIPNETNUMBER which *always* forces a + * re-RIP). Not valid with setsockopt(). optval points to an instance of + * the IPX_NETNUM_DATA structure with the netnum filled in. + * + */ + +#define IPX_GETNETINFO_NORIP 0x4009 + + +/* + * Get information on a connected SPX socket. optval points + * to an instance of the IPX_SPXCONNSTATUS_DATA structure. + * + * All numbers are in Novell (high-low) order. + * + */ + +#define IPX_SPXGETCONNECTIONSTATUS 0x400B + +typedef struct _IPX_SPXCONNSTATUS_DATA { + UCHAR ConnectionState; + UCHAR WatchDogActive; + USHORT LocalConnectionId; + USHORT RemoteConnectionId; + USHORT LocalSequenceNumber; + USHORT LocalAckNumber; + USHORT LocalAllocNumber; + USHORT RemoteAckNumber; + USHORT RemoteAllocNumber; + USHORT LocalSocket; + UCHAR ImmediateAddress[6]; + UCHAR RemoteNetwork[4]; + UCHAR RemoteNode[6]; + USHORT RemoteSocket; + USHORT RetransmissionCount; + USHORT EstimatedRoundTripDelay; /* In milliseconds */ + USHORT RetransmittedPackets; + USHORT SuppressedPacket; +} IPX_SPXCONNSTATUS_DATA, *PIPX_SPXCONNSTATUS_DATA; + + +/* + * Get notification when the status of an adapter that IPX is + * bound to changes. Typically this will happen when a wan line + * goes up or down. Not valid with setsockopt(). optval points + * to a buffer which contains an IPX_ADDRESS_DATA structure + * followed immediately by a HANDLE to an unsignaled event. + * + * When the getsockopt() query is submitted, it will complete + * successfully. However, the IPX_ADDRESS_DATA pointed to by + * optval will not be updated at that point. Instead the + * request is queued internally inside the transport. + * + * When the status of an adapter changes, IPX will locate a + * queued getsockopt() query and fill in all the fields in the + * IPX_ADDRESS_DATA structure. It will then signal the event + * pointed to by the HANDLE in the optval buffer. This handle + * should be obtained before calling getsockopt() by calling + * CreateEvent(). If multiple getsockopts() are submitted at + * once, different events must be used. + * + * The event is used because the call needs to be asynchronous + * but currently getsockopt() does not support this. + * + * WARNING: In the current implementation, the transport will + * only signal one queued query for each status change. Therefore + * only one service which uses this query should be running at + * once. + * + */ + +#define IPX_ADDRESS_NOTIFY 0x400C + + +/* + * Get the maximum number of adapters present. If this call returns + * n then the adapters are numbered 0 through n-1. Not valid + * with setsockopt(). optval points to an int where the value + * is returned. + * + */ + +#define IPX_MAX_ADAPTER_NUM 0x400D + + +/* + * Like IPX_GETNETINFO except it forces IPX to re-RIP even if the + * network is in its cache (but not if it is directly attached to). + * Not valid with setsockopt(). optval points to an instance of + * the IPX_NETNUM_DATA structure with the netnum filled in. + * + */ + +#define IPX_RERIPNETNUMBER 0x400E + + +/* + * A hint that broadcast packets may be received. The default is + * TRUE. Applications that do not need to receive broadcast packets + * should set this sockopt to FALSE which may cause better system + * performance (note that it does not necessarily cause broadcasts + * to be filtered for the application). Not valid with getsockopt(). + * optval points to a BOOL. + * + */ + +#define IPX_RECEIVE_BROADCAST 0x400F + + +/* + * On SPX connections, don't delay before sending ack. Applications + * that do not tend to have back-and-forth traffic over SPX should + * set this; it will increase the number of acks sent but will remove + * delays in sending acks. optval points to a BOOL. + * + */ + +#define IPX_IMMEDIATESPXACK 0x4010 + +#endif + diff --git a/code/redalert/wspipx.cpp b/code/redalert/wspipx.cpp new file mode 100644 index 0000000..2d5107d --- /dev/null +++ b/code/redalert/wspipx.cpp @@ -0,0 +1,446 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPIPX.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 6 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * * + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "wspipx.h" +#include "ipxaddr.h" + +#include +#include + + +/*********************************************************************************************** + * IPXInterfaceClass::IPXInterfaceClass -- Class constructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 11:41AM ST : Created * + *=============================================================================================*/ +IPXInterfaceClass::IPXInterfaceClass (void) : WinsockInterfaceClass() +{ + /* + ** Set the net and node addressed to their default values. + */ + memset ( BroadcastNet, 0xff, sizeof (BroadcastNet) ); + memset ( BroadcastNode, 0xff, sizeof (BroadcastNode) ); + memset ( MyNode, 0xff, sizeof (MyNode) ); +} + + +/*********************************************************************************************** + * IPXInterfaceClass::Get_Network_Card_Address -- Get the ID of the installed net card * + * * + * * + * * + * INPUT: card number to retrieve ID for * + * ptr to addr to return ID in * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/1/97 3:04PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr) +{ + int cbOpt; + int cbAddr = sizeof( SOCKADDR_IPX ); + SOCKET s; + SOCKADDR_IPX Addr; + IPX_ADDRESS_DATA IpxData; + + /* + ** Create a temporary IPX socket. + */ + s = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX ); + if ( s == SOCKET_ERROR ) { + assert ( s != SOCKET_ERROR ); + return (false); + } + + /* + ** Socket must be bound prior to calling IPX_MAX_ADAPTER_NUM + */ + memset( &Addr, 0, sizeof( Addr )); + Addr.sa_family = AF_IPX; + int err = bind( s, (SOCKADDR*) &Addr, cbAddr); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + memset( &IpxData, 0, sizeof(IpxData)); + + /* + ** Specify which adapter to check. + */ + IpxData.adapternum = card_number; + cbOpt = sizeof( IpxData ); + + /* + ** Get information for the current adapter. + */ + err = getsockopt( s, NSPROTO_IPX, IPX_ADDRESS, (char*) &IpxData, &cbOpt ); + if ( err == SOCKET_ERROR ) { + assert ( err != SOCKET_ERROR ); + closesocket (s); + return (false); + } + + /* + ** IpxData contains the address for the current adapter. + ** The network number will be needed later for broadcasts as the net number ff,ff,ff,ff + ** doesn't work under NT. + ** + ** Note: Due to a bug in Win95s implementation of Winsock, only the netnum & nodenum + ** values are correctly returned. NT returns all expected values. ST - 7/31/97 0:57AM + */ + memcpy (addr->sa_netnum, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (BroadcastNet, IpxData.netnum, sizeof (addr->sa_netnum)); + memcpy (addr->sa_nodenum, IpxData.nodenum, sizeof (addr->sa_nodenum)); + + closesocket (s); + return (true); +} + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Open_Socket -- Opens an IPX socket for reading & writing * + * * + * * + * * + * INPUT: SOCKET number to open. This is usually VIRGIN_SOCKET * + * * + * OUTPUT: true if socket was opened without error * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:54PM ST : Created * + *=============================================================================================*/ +bool IPXInterfaceClass::Open_Socket( SOCKET socketnum ) +{ + SOCKADDR_IPX addr; + bool delay = true; + int err; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + IPXSocketNumber = socketnum; + + /* + ** Set up the addr structure for the IPX socket + */ + addr.sa_family = AF_IPX; + memset (addr.sa_netnum, 0, sizeof (addr.sa_netnum)); + memset (addr.sa_nodenum, -1, sizeof (addr.sa_nodenum)); + addr.sa_socket = htons ( socketnum ); + + /* + ** Create the socket. + */ + Socket = socket (AF_NS, SOCK_DGRAM, NSPROTO_IPX); + if (Socket == INVALID_SOCKET) { + char out[128]; + sprintf (out, "TS: Failed to create IPX socket - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( Socket != INVALID_SOCKET ); + closesocket(Socket); + return ( false ); + } + + /* + ** Get the network card address. This is needed so we can bind the socket to the net card. + */ + if ( !Get_Network_Card_Address (0, &addr)){ + closesocket ( Socket ); + return ( false ); + } + + /* + ** Bind the IPX socket to the network card. + */ + if (bind ( Socket, (const struct sockaddr *) &addr, 16) == SOCKET_ERROR ){ + char out[128]; + sprintf (out, "TS: IPX socket bind failed with error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + closesocket(Socket); + return ( false );; + } + + + /* + ** Set the various options for this IPX socket + */ + unsigned long optval = true; + int packet_type = 4; + + /* + ** The SO_BROADCAST option allows broadcasting on this socket. This shouldn't be needed + ** except for the bug in the Win95 implementation of Winsock which causes broadcasts to + ** fail if it isn't set. + */ + if ( setsockopt ( Socket, SOL_SOCKET, SO_BROADCAST, (char*)&optval, sizeof(optval) ) == SOCKET_ERROR ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_BROADCAST - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( false ); + } + + /* + ** Set the value in the packet type field for outgoing packets. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_PTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_PTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Ignore all incoming packets not of this type. + */ + err = setsockopt ( Socket, NSPROTO_IPX, IPX_FILTERPTYPE, (char*)&packet_type, sizeof(packet_type)); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX protocol option IPX_FILTERTYPE - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Set the the base class socket options for buffer sizes. + */ + WinsockInterfaceClass::Set_Socket_Options(); + + /* + ** Woohoo! + */ + return ( true ); +} + + + + + + + + + +/*********************************************************************************************** + * IPXInterfaceClass::Message_Handler -- Handler for windows messages relating to IPX * + * * + * * + * * + * INPUT: Usual windoze message handler stuff * + * * + * OUTPUT: 0 if message was handled * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/4/97 5:55PM ST : Created * + *=============================================================================================*/ +long IPXInterfaceClass::Message_Handler(HWND , UINT message, UINT , LONG lParam) +{ + + int addr_len; // Length of address structure + int rc; // Result code + SOCKADDR_IPX addr; // Winsock IPX addressing structure + WinsockBufferType *packet; // Ptr to packet + NetNumType netnum; + NetNodeType nodenum; + + + /* + ** We only handle IPX events. + */ + if ( message != WM_IPXASYNCEVENT ) return ( 1 ); + + + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return(0); + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*) ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len ); + if (rc == SOCKET_ERROR) { + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + } + return(0); + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make a copy of the address that this packet came from. + */ + memcpy ( netnum, addr.sa_netnum, sizeof (netnum) ); + memcpy ( nodenum, addr.sa_nodenum, sizeof (nodenum) ); + + /* + ** If this packet was from me then ignore it. + */ + if ( !memcmp (netnum, BroadcastNet, sizeof (BroadcastNet)) && !memcmp(nodenum, MyNode, sizeof (MyNode)) ) { + return (0); + } + + /* + ** Create a new buffer and store this packet in it. + */ + packet = new WinsockBufferType; + packet->BufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Set_Address ( netnum, nodenum ); + InBuffers.Add ( packet ); + } + return(0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error ( Socket ); + return(0); + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + while ( OutBuffers.Count() != 0 ) { + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sa_family = AF_IPX; + addr.sa_socket = htons ( IPXSocketNumber ); + + /* + ** Set up the address as either a broadcast address or the given address + */ + if ( packet->IsBroadcast ) { + memcpy ( addr.sa_netnum, BroadcastNet, sizeof (BroadcastNet) ); + memcpy ( addr.sa_nodenum, BroadcastNode, sizeof (BroadcastNode) ); + }else{ + IPXAddressClass *paddress = (IPXAddressClass*) (&packet->Address[0]); + paddress->Get_Address ( netnum, nodenum ); + memcpy ( addr.sa_netnum, netnum, sizeof (netnum) ); + memcpy ( addr.sa_nodenum, nodenum, sizeof (nodenum) ); + } + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + break; + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + } + + return(0); + } + + return (0); +} + + diff --git a/code/redalert/wspipx.h b/code/redalert/wspipx.h new file mode 100644 index 0000000..2ee300e --- /dev/null +++ b/code/redalert/wspipx.h @@ -0,0 +1,103 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPIPX.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPIPX_H +#define WSPIPX_H + +#include "WSProto.h" + +/* +** Include Windows specific extensions for Winsock that allow IPX over winsock 1.1 +*/ +#include + +/* +** This file normally resides with the SDK. However, since it needs fixing up before watcom will +** compile it, it has been incorporated into the project. +*/ +#include "wsnwlink.h" + +/* +** IPX interface class. This handles access to the IPX specific portions of the +** Winsock interface. +** +*/ +class IPXInterfaceClass : public WinsockInterfaceClass { + + public: + + IPXInterfaceClass (void); + //virtual ~IPXInterfaceClass(void){Close();}; + bool Get_Network_Card_Address (int card_number, SOCKADDR_IPX *addr); + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_IPX); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_IPXASYNCEVENT); + }; + + + private: + /* + ** The address of the network we will send broadcasts to. Normally you would expect + ** this to be ff,ff,ff,ff but this fails under NT 4.0. Instead, we can use the network + ** number of the net that this PC is attached to. This limits broadcasts to the current + ** network. + */ + unsigned char BroadcastNet[4]; + + /* + ** The node to use as a broadcast address. Normally ff,ff,ff,ff,ff,ff. + */ + unsigned char BroadcastNode[6]; + + /* + ** The id of the network cars in this machine. + */ + unsigned char MyNode[6]; + + /* + ** The socket number to connect with. Normally this will be virgins reserved socket + ** number - VIRGIN_SOCKET (0x8813). + */ + SOCKET IPXSocketNumber; + +}; + + + +#endif \ No newline at end of file diff --git a/code/redalert/wsproto.cpp b/code/redalert/wsproto.cpp new file mode 100644 index 0000000..e84f411 --- /dev/null +++ b/code/redalert/wsproto.cpp @@ -0,0 +1,591 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSProto.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/20/97 10:54a $* + * * + * $Revision:: 5 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * * + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * WIC::Close -- Releases any currently in use Winsock resources. * + * WIC::Close_Socket -- Close the communication socket if its open * + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * WIC::Stop_Listening -- Disable the winsock event callback * + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * WIC::Init -- Initialised Winsock and this class for use. * + * WIC::Read -- read any pending input from the communications socket * + * WIC::WriteTo -- Send data via the Winsock socket * + * WIC::Broadcast -- Send data via the Winsock socket * + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "WSProto.h" + +#include + + +/*********************************************************************************************** + * WIC::WinsockInterfaceClass -- constructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:51PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::WinsockInterfaceClass(void) +{ + WinsockInitialised = false; + ASync = INVALID_HANDLE_VALUE; + Socket = INVALID_SOCKET; +} + + +/*********************************************************************************************** + * WIC::~WinsockInterfaceClass -- destructor for the WinsockInterfaceClass * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +WinsockInterfaceClass::~WinsockInterfaceClass(void) +{ + Close(); +} + + +/*********************************************************************************************** + * WIC::Close -- Releases any currently in use Winsock resources. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:52PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close(void) +{ + /* + ** If we never initialised the class in the first place then just return + */ + if (!WinsockInitialised) return; + + /* + ** Cancel any outstaning asyncronous events + */ + Stop_Listening(); + + /* + ** Close any open sockets + */ + Close_Socket(); + + /* + ** Call the Winsock cleanup function to say we are finished using Winsock + */ + WSACleanup(); + + WinsockInitialised = false; +} + + + +/*********************************************************************************************** + * WIC::Close_Socket -- Close the communication socket if its open * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:53AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Close_Socket (void) +{ + if ( Socket != INVALID_SOCKET ) { + closesocket (Socket); + Socket = INVALID_SOCKET; + } +} + + + +/*********************************************************************************************** + * WIC::Start_Listening -- Enable callbacks for read/write events on our socket * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:54AM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Start_Listening (void) +{ + /* + ** Enable asynchronous events on the socket + */ + if ( WSAAsyncSelect ( Socket, MainWindow, Protocol_Event_Message(), FD_READ | FD_WRITE) == SOCKET_ERROR ){ + WWDebugString ( "TS: Async select failed.\n" ); + assert (false); + WSACancelAsyncRequest(ASync); + ASync = INVALID_HANDLE_VALUE; + return (false); + } + return (true); +} + + +/*********************************************************************************************** + * WIC::Stop_Listening -- Disable the winsock event callback * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:06PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Stop_Listening (void) +{ + if ( ASync != INVALID_HANDLE_VALUE ) { + WSACancelAsyncRequest ( ASync ); + ASync = INVALID_HANDLE_VALUE; + } +} + + + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our incoming packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_In_Buffers (void) +{ + WinsockBufferType *packet; + + while ( InBuffers.Count() ) { + packet = InBuffers [ 0 ]; + delete packet; + InBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Discard_In_Buffers -- Discard any packets in our outgoing packet holding buffers * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 11:55AM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Discard_Out_Buffers (void) +{ + WinsockBufferType *packet; + + while ( OutBuffers.Count() ) { + packet = OutBuffers [ 0 ]; + delete packet; + OutBuffers.Delete (0); + } +} + + +/*********************************************************************************************** + * WIC::Init -- Initialised Winsock and this class for use. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: true if Winsock is available and was initialised * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 2:54PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Init(void) +{ + short version; + int rc; + + /* + ** Just return true if we are already set up + */ + if (WinsockInitialised) return (true); + + /* + ** Create a buffer much larger than the sizeof (WSADATA) would indicate since Bounds Checker + ** says that a buffer of that size gets overrun. + */ + char *buffer = new char [sizeof (WSADATA) + 1024]; + WSADATA *winsock_info = (WSADATA*) (&buffer[0]); + + /* + ** Initialise socket and event handle to null + */ + Socket =INVALID_SOCKET; + ASync = INVALID_HANDLE_VALUE; + Discard_In_Buffers(); + Discard_Out_Buffers(); + + /* + ** Start WinSock, and fill in our Winsock info structure + */ + version = (WINSOCK_MINOR_VER << 8) | WINSOCK_MAJOR_VER; + rc = WSAStartup(version, winsock_info); + if (rc != 0) { + char out[128]; + sprintf (out, "TS: Winsock failed to initialise - error code %d.\n", GetLastError() ); + OutputDebugString (out); + delete [] buffer; + return (false); + } + + /* + ** Check the Winsock version number + */ + if ((winsock_info->wVersion & 0x00ff) != (version & 0x00ff) || + (winsock_info->wVersion >> 8) != (version >> 8)) { + OutputDebugString ("TS: Winsock version is less than 1.1\n" ); + delete [] buffer; + return (false); + } + + /* + ** Everything is OK so return success + */ + WinsockInitialised = true; + + delete [] buffer; + return (true); + +} + + + + +/*********************************************************************************************** + * WIC::Read -- read any pending input from the communications socket * + * * + * * + * * + * INPUT: ptr to buffer to receive input * + * length of buffer * + * ptr to address to fill with address that packet was sent from * + * length of address buffer * + * * + * OUTPUT: number of bytes transfered to buffer * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * * + * HISTORY: * + * 3/20/96 2:58PM ST : Created * + *=============================================================================================*/ +int WinsockInterfaceClass::Read(void *buffer, int &buffer_len, void *address, int &address_len) +{ + address_len = address_len; + /* + ** Call the message loop in case there are any outstanding winsock READ messages. + */ + Keyboard->Check(); + + /* + ** If there are no available packets then return 0 + */ + if ( InBuffers.Count() == 0 ) return (0); + + /* + ** Get the oldest packet for reading + */ + int packetnum = 0; + WinsockBufferType *packet = InBuffers [packetnum]; + + assert ( buffer_len >= packet->BufferLen ); + assert ( address_len >= sizeof (packet->Address) ); + + /* + ** Copy the data and the address it came from into the supplied buffers. + */ + memcpy ( buffer, packet->Buffer, packet->BufferLen ); + memcpy ( address, packet->Address, sizeof (packet->Address) ); + + /* + ** Return the length of the packet in buffer_len. + */ + buffer_len = packet->BufferLen; + + /* + ** Delete the temporary storage for the packet now that it is being passed to the game. + */ + InBuffers.Delete ( packetnum ); + delete packet; + + return ( buffer_len ); +} + + + + +/*********************************************************************************************** + * WIC::WriteTo -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * address to send data to. * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: The format of the address is dependent on the protocol in use. * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::WriteTo(void *buffer, int buffer_len, void *address) +{ + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + packet->IsBroadcast = false; +// memcpy ( packet->Address, address, sizeof (packet->Address) ); + memcpy ( packet->Address, address, sizeof( IPXAddressClass ) ); // Steve Tall has revised WriteTo due to this bug. + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + + /* + ** Create a temporary holding area for the packet. + */ + WinsockBufferType *packet = new WinsockBufferType; + + /* + ** Copy the packet into the holding buffer. + */ + memcpy ( packet->Buffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); +} + + + + +/*********************************************************************************************** + * WIC::Clear_Socket_Error -- Clear any outstanding erros on the socket * + * * + * * + * * + * INPUT: Socket * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:05PM ST : Created * + *=============================================================================================*/ +void WinsockInterfaceClass::Clear_Socket_Error(SOCKET socket) +{ + unsigned long error_code; + int length = 4; + + getsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, &length); + error_code = 0; + setsockopt (socket, SOL_SOCKET, SO_ERROR, (char*)&error_code, length); +} + + + + + + + +/*********************************************************************************************** + * WIC::Set_Socket_Options -- Sets default socket options for Winsock buffer sizes * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:07PM ST : Created * + *=============================================================================================*/ +bool WinsockInterfaceClass::Set_Socket_Options ( void ) +{ + static int socket_transmit_buffer_size = SOCKET_BUFFER_SIZE; + static int socket_receive_buffer_size = SOCKET_BUFFER_SIZE; + + /* + ** Specify the size of the receive buffer. + */ + int err = setsockopt ( Socket, SOL_SOCKET, SO_RCVBUF, (char*)&socket_receive_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_RCVBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + /* + ** Specify the size of the send buffer. + */ + err = setsockopt ( Socket, SOL_SOCKET, SO_SNDBUF, (char*)&socket_transmit_buffer_size, 4); + if ( err == INVALID_SOCKET ) { + char out[128]; + sprintf (out, "TS: Failed to set IPX socket option SO_SNDBUF - error code %d.\n", GetLastError() ); + OutputDebugString (out); + assert ( err != INVALID_SOCKET ); + } + + return ( true ); +} + + diff --git a/code/redalert/wsproto.h b/code/redalert/wsproto.h new file mode 100644 index 0000000..146e05c --- /dev/null +++ b/code/redalert/wsproto.h @@ -0,0 +1,189 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSProto.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/12/97 5:42p $* + * * + * $Revision:: 4 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPROTO_H +#define WSPROTO_H + +#include "_WSProto.h" + +/* +** Include standard Winsock 1.0 header file. +*/ +#include + +/* +** Misc defines +*/ +#define WINSOCK_MINOR_VER 1 // Version of Winsock +#define WINSOCK_MAJOR_VER 1 // that we require + +//#define WS_RECEIVE_BUFFER_LEN 32768 // Length of our temporary receive buffer. Needs to be more that the max packet size which is about 550 bytes. +//#define SOCKET_BUFFER_SIZE 32768 // Length of winsocks internal buffer. +#define WS_RECEIVE_BUFFER_LEN 1024 // Length of our temporary receive buffer. +#define SOCKET_BUFFER_SIZE 1024*128 // Length of winsocks internal buffer. + +#define PLANET_WESTWOOD_HANDLE_MAX 20 // Max length of a WChat handle + +/* +** Define events for Winsock callbacks +*/ +#define WM_IPXASYNCEVENT (WM_USER + 115) // IPX socket Async event +#define WM_UDPASYNCEVENT (WM_USER + 116) // UDP socket Async event + + +/* +** Enum to identify the protocols supported by the Winsock interface. +*/ +typedef enum tProtocolEnum { + PROTOCOL_NONE, + PROTOCOL_IPX, + PROTOCOL_UDP +} ProtocolEnum; + + + +/* +** +** Class to interface with Winsock. This interface only supports connectionless packet protocols +** like UDP & IPX. Connection orientated or streaming protocols like TCP are not supported by this +** class. +** +*/ +class WinsockInterfaceClass { + + public: + + WinsockInterfaceClass(void); + virtual ~WinsockInterfaceClass(void); + + bool Init(void); + void Close(void); + + + virtual void Close_Socket(void); + virtual int Read(void *buffer, int &buffer_len, void *address, int &address_len); + virtual void WriteTo (void *buffer, int buffer_len, void *address); + virtual void Broadcast (void *buffer, int buffer_len); + virtual void Discard_In_Buffers (void); + virtual void Discard_Out_Buffers (void); + virtual bool Start_Listening (void); + virtual void Stop_Listening (void); + virtual void Clear_Socket_Error(SOCKET socket); + virtual bool Set_Socket_Options ( void ); + virtual void Set_Broadcast_Address ( void * ) {}; + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_NONE); + }; + + virtual int Protocol_Event_Message (void) { + return (0); + }; + + virtual bool Open_Socket ( SOCKET ) { + return (false); + }; + + virtual long Message_Handler(HWND, UINT, UINT, LONG) { + return (1); + } + + + typedef enum ConnectStatusEnum { + CONNECTED_OK = 0, + NOT_CONNECTING, + CONNECTING, + UNABLE_TO_CONNECT_TO_SERVER, + CONTACTING_SERVER, + SERVER_ADDRESS_LOOKUP_FAILED, + RESOLVING_HOST_ADDRESS, + UNABLE_TO_ACCEPT_CLIENT, + UNABLE_TO_CONNECT, + CONNECTION_LOST + } ConnectStatusEnum; + + inline ConnectStatusEnum Get_Connection_Status(void) {return (ConnectStatus);} + + protected: + + /* + ** This struct contains the information needed for each incoming and outgoing packet. + ** It acts as a temporary control for these packets. + */ + typedef struct tWinsockBufferType { + unsigned char Address [64]; // Address. IN_ADDR, IPXAddressClass etc. + int BufferLen; // Length of data in buffer + bool IsBroadcast; // Flag to broadcast this packet + unsigned char Buffer[1024]; // Buffer to store packet in. + } WinsockBufferType; + + /* + ** Array of buffers to temporarily store incoming and outgoing packets. + */ + DynamicVectorClass InBuffers; + DynamicVectorClass OutBuffers; + + + /* + ** Is Winsock present and initialised? + */ + bool WinsockInitialised; + + /* + ** Socket that communications will take place over. + */ + SOCKET Socket; + + /* + ** Async object required for callbacks to our message handler. + */ + HANDLE ASync; + + /* + ** Temporary receive buffer to use when querying Winsock for incoming packets. + */ + unsigned char ReceiveBuffer[WS_RECEIVE_BUFFER_LEN]; + + /* + ** Current connection status. + */ + ConnectStatusEnum ConnectStatus; +}; + + + + + + + +#endif //WSPROTO_H diff --git a/code/redalert/wspudp.cpp b/code/redalert/wspudp.cpp new file mode 100644 index 0000000..85151b9 --- /dev/null +++ b/code/redalert/wspudp.cpp @@ -0,0 +1,428 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPUDP.cpp $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + * * + *---------------------------------------------------------------------------------------------* + * * + * WSProto.CPP WinsockInterfaceClass to provide an interface to Winsock protocols * + * * + *---------------------------------------------------------------------------------------------* + * * + * Functions: * + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "function.h" +#include "internet.h" +#include "WSPUDP.h" + +#include +#include +#include + + +/*********************************************************************************************** + * UDPInterfaceClass::UDPInterfaceClass -- Class constructor. * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:11PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::UDPInterfaceClass (void) : WinsockInterfaceClass() +{} + + + +/*********************************************************************************************** + * UDPIC::~UDPInterfaceClass -- UDPInterface class destructor * + * * + * * + * * + * INPUT: Nothing * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 10/9/97 12:17PM ST : Created * + *=============================================================================================*/ +UDPInterfaceClass::~UDPInterfaceClass (void) +{ + while ( BroadcastAddresses.Count() ) { + delete BroadcastAddresses[0]; + BroadcastAddresses.Delete(0); + } + + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + Close(); +} + + +/*********************************************************************************************** + * UDPInterfaceClass::Set_Broadcast_Address -- Sets the address to send broadcast packets to * + * * + * * + * * + * INPUT: ptr to address in decimal dot format. i.e. xxx.xxx.xxx.xxx * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:12PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Set_Broadcast_Address (void *address) +{ + char* ip_addr = (char*) address; + assert ( strlen (ip_addr) <= strlen ( "xxx.xxx.xxx.xxx" ) ); + + unsigned char *baddr = new unsigned char[4]; + + sscanf ( ip_addr, "%hhu.%hhu.%hhu.%hhu", &baddr[0], &baddr[1], &baddr[2], &baddr[3] ); + BroadcastAddresses.Add (baddr); +} + + + +/*********************************************************************************************** + * UDPInterfaceClass::Open_Socket -- Opens a socket for communications via the UDP protocol * + * * + * * + * * + * INPUT: Socket number to use. Not required for this protocol. * + * * + * OUTPUT: True if socket was opened OK * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 8/5/97 12:13PM ST : Created * + *=============================================================================================*/ +bool UDPInterfaceClass::Open_Socket ( SOCKET ) +{ + LINGER ling; + struct sockaddr_in addr; + + /* + ** If Winsock is not initialised then do it now. + */ + if ( !WinsockInitialised ) { + if ( !Init()) return ( false );; + } + + /* + ** Create our UDP socket + */ + Socket = socket(AF_INET, SOCK_DGRAM, 0); + if (Socket == INVALID_SOCKET) { + return (false); + } + + /* + ** Bind our UDP socket to our UDP port number + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ( (unsigned short) PlanetWestwoodPortNumber); + addr.sin_addr.s_addr = htonl (INADDR_ANY); + + if ( bind (Socket, (LPSOCKADDR)&addr, sizeof(addr) ) == SOCKET_ERROR) { + Close_Socket (); + return (false); + } + + /* + ** Use gethostbyname to find the name of the local host. We will need this to look up + ** the local ip address. + */ + char hostname[128]; + gethostname(hostname, 128); + WWDebugString (hostname); + struct hostent *host_info = gethostbyname ( hostname ); + + /* + ** Clear out any old local addresses from the local address list. + */ + while ( LocalAddresses.Count() ) { + delete LocalAddresses[0]; + LocalAddresses.Delete(0); + } + + + /* + ** Add all local IP addresses to the list. This list will be used to discard any packets that + ** we send to ourselves. + */ + unsigned long **addresses = (unsigned long**) (host_info->h_addr_list); + + for ( ;; ) { + if ( !*addresses ) break; + + unsigned long address = **addresses++; + //address = ntohl (address); + + char temp[128]; + sprintf (temp, "RA95: Found local address: %d.%d.%d.%d\n", address & 0xff, (address & 0xff00) >> 8, (address & 0xff0000) >> 16, (address & 0xff000000) >> 24); + OutputDebugString (temp); + + unsigned char *a = new unsigned char [4]; + * ((unsigned long*) a) = address; + LocalAddresses.Add (a); + } + + /* + ** Set options for the UDP socket + */ + ling.l_onoff = 0; // linger off + ling.l_linger = 0; // timeout in seconds (ie close now) + setsockopt (Socket, SOL_SOCKET, SO_LINGER, (LPSTR)&ling, sizeof(ling)); + + WinsockInterfaceClass::Set_Socket_Options(); + + return (true); + + +} + + + + +/*********************************************************************************************** + * UDPIC::Broadcast -- Send data via the Winsock socket * + * * + * * + * * + * INPUT: ptr to buffer containing data to send * + * length of data to send * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:00PM ST : Created * + *=============================================================================================*/ +void UDPInterfaceClass::Broadcast (void *buffer, int buffer_len) +{ + for ( int i=0 ; iBuffer, buffer, buffer_len ); + packet->BufferLen = buffer_len; + + /* + ** Indicate that this packet should be broadcast. + */ + packet->IsBroadcast = true; + + /* + ** Set up the send address for this packet. + */ + memset (packet->Address, 0, sizeof (packet->Address)); + memcpy (packet->Address+4, BroadcastAddresses[i], 4); + + /* + ** Add it to our out list. + */ + OutBuffers.Add ( packet ); + + /* + ** Send a message to ourselves so that we can initiate a write if Winsock is idle. + */ + SendMessage ( MainWindow, Protocol_Event_Message(), 0, (LONG)FD_WRITE ); + + /* + ** Make sure the message loop gets called. + */ + Keyboard->Check(); + } +} + + + + + +/*********************************************************************************************** + * TMC::Message_Handler -- Message handler function for Winsock related messages * + * * + * * + * * + * INPUT: Windows message handler stuff * + * * + * OUTPUT: Nothing * + * * + * WARNINGS: None * + * * + * HISTORY: * + * 3/20/96 3:05PM ST : Created * + *=============================================================================================*/ +long UDPInterfaceClass::Message_Handler(HWND, UINT message, UINT, LONG lParam) +{ + struct sockaddr_in addr; + int rc; + int addr_len; + WinsockBufferType *packet; + + /* + ** We only handle UDP events. + */ + if ( message != WM_UDPASYNCEVENT ) return (1); + + /* + ** Handle UDP packet events + */ + switch ( WSAGETSELECTEVENT(lParam) ) { + + /* + ** Read event. Winsock has data it would like to give us. + */ + case FD_READ: + /* + ** Clear any outstanding errors on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** Call the Winsock recvfrom function to get the outstanding packet. + */ + addr_len = sizeof(addr); + rc = recvfrom ( Socket, (char*)ReceiveBuffer, sizeof (ReceiveBuffer), 0, (LPSOCKADDR)&addr, &addr_len); + if (rc == SOCKET_ERROR) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** rc is the number of bytes received from Winsock + */ + if ( rc ) { + + /* + ** Make sure this packet didn't come from us. If it did then throw it away. + */ + for ( int i=0 ; iBufferLen = rc; + memcpy ( packet->Buffer, ReceiveBuffer, rc ); + memset ( packet->Address, 0, sizeof (packet->Address) ); + memcpy ( packet->Address+4, &addr.sin_addr.s_addr, 4 ); + InBuffers.Add (packet); + } + return (0); + + + /* + ** Write event. We send ourselves this event when we have more data to send. This + ** event will also occur automatically when a packet has finished being sent. + */ + case FD_WRITE: + /* + ** Clear any outstanding erros on the socket. + */ + rc = WSAGETSELECTERROR(lParam); + if (rc != 0) { + Clear_Socket_Error (Socket); + return (0);; + } + + /* + ** If there are no packets waiting to be sent then bail. + */ + if ( OutBuffers.Count() == 0 ) return (0); + int packetnum = 0; + + /* + ** Get a pointer to the packet. + */ + packet = OutBuffers [ packetnum ]; + + /* + ** Set up the address structure of the outgoing packet + */ + addr.sin_family = AF_INET; + addr.sin_port = (unsigned short) htons ((unsigned short)PlanetWestwoodPortNumber); + memcpy (&addr.sin_addr.s_addr, packet->Address+4, 4); + + /* + ** Send it. + ** If we get a WSAWOULDBLOCK error it means that Winsock is unable to accept the packet + ** at this time. In this case, we clear the socket error and just exit. Winsock will + ** send us another WRITE message when it is ready to receive more data. + */ + rc = sendto ( Socket, (const char*) packet->Buffer, packet->BufferLen, 0, (LPSOCKADDR)&addr, sizeof (addr) ); + + if (rc == SOCKET_ERROR){ + if (WSAGetLastError() != WSAEWOULDBLOCK) { + Clear_Socket_Error (Socket); + return (0); + } + } + + /* + ** Delete the sent packet. + */ + OutBuffers.Delete ( packetnum ); + delete packet; + return(0); + } + + return (0); +} diff --git a/code/redalert/wspudp.h b/code/redalert/wspudp.h new file mode 100644 index 0000000..cc7bb91 --- /dev/null +++ b/code/redalert/wspudp.h @@ -0,0 +1,83 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * $Archive:: /Sun/WSPUDP.h $* + * * + * $Author:: Joe_b $* + * * + * $Modtime:: 8/05/97 6:45p $* + * * + * $Revision:: 3 $* + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WSPUDP_H +#define WSPUDP_H + +#include "WSProto.h" +#include + + + + +/* +** Class to allow access to UDP specific portions of the Winsock interface. +** +*/ +class UDPInterfaceClass : public WinsockInterfaceClass { + + public: + + UDPInterfaceClass (void); + virtual ~UDPInterfaceClass(void); + + virtual long Message_Handler(HWND window, UINT message, UINT wParam, LONG lParam); + virtual bool Open_Socket ( SOCKET socketnum ); + virtual void Set_Broadcast_Address ( void *address ); + virtual void Broadcast (void *buffer, int buffer_len); + + virtual ProtocolEnum Get_Protocol (void) { + return (PROTOCOL_UDP); + }; + + virtual int Protocol_Event_Message (void) { + return (WM_UDPASYNCEVENT); + }; + + + private: + + /* + ** Address to use when broadcasting a packet. + */ + DynamicVectorClass BroadcastAddresses; + + /* + ** List of local addresses. + */ + DynamicVectorClass LocalAddresses; +}; + + + +#endif \ No newline at end of file diff --git a/code/redalert/wwalloc.h b/code/redalert/wwalloc.h new file mode 100644 index 0000000..c96a185 --- /dev/null +++ b/code/redalert/wwalloc.h @@ -0,0 +1,65 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: F:\projects\c&c0\vcs\code\wwalloc.h_v 4.9 07 May 1996 17:14:00 JOE_BOSTIC $ */ + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* +** This should be located in the wwlib32.h file, but is located here for +** test purposes. +*/ +#ifdef __FLAT__ +#define PRIVATE static +#endif + +typedef enum MemoryFlagType { + MEM_NORMAL = 0x0000, // Default memory (normal). + MEM_PUBLIC = 0x0000, // Default memory (normal). + MEM_CHIP = 0x0000, // Graphic & sound buffer memory (Amiga). + MEM_UNUSED = 0x0001, // + MEM_SYSTEM = 0x0002, // Allocate out of system heap (XMS or EMS only). + MEM_RELAXED= 0x0004, // Don't worry about page conservation in EMS. + MEM_TEMP = 0x0008, // Temporary allocation (used by library only). + MEM_CLEAR = 0x0010, // Fill memory with '\0' characters. + MEM_PARA = 0x0020, // Paragraph aligned (IBM only). + MEM_XMS = 0x0040, // XMS memory. + MEM_EMS = 0x0080, // EMS memory (not implemented). + MEM_X = 0x8000 // Here to force this enum to be unsigned sized. +} MemoryFlagType; +MemoryFlagType operator |(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator &(MemoryFlagType, MemoryFlagType); +MemoryFlagType operator ~(MemoryFlagType); + + +/* Prototypes for functions defined in this file */ +void *cdecl Alloc(unsigned long bytes_to_alloc, MemoryFlagType flags); +void cdecl Free(void const *pointer); +void *cdecl Resize_Alloc(void const *original_ptr, unsigned long new_size_in_bytes); +long cdecl Ram_Free(MemoryFlagType flag); +long cdecl Total_Ram_Free(MemoryFlagType flag); +long cdecl Heap_Size(MemoryFlagType flag); + +extern unsigned long cdecl MinRam; // Record of least memory at worst case. +extern unsigned long cdecl MaxRam; // Record of total allocated at worst case. + +#ifdef __cplusplus +} +#endif + diff --git a/code/redalert/wwfile.h b/code/redalert/wwfile.h new file mode 100644 index 0000000..33feded --- /dev/null +++ b/code/redalert/wwfile.h @@ -0,0 +1,93 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/WWFILE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Westwood Library * + * * + * File Name : WWFILE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : August 8, 1994 * + * * + * Last Update : August 8, 1994 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef WWFILE_Hx +#define WWFILE_Hx + +#define YEAR(dt) (((dt & 0xFE000000) >> (9 + 16)) + 1980) +#define MONTH(dt) ((dt & 0x01E00000) >> (5 + 16)) +#define DAY(dt) ((dt & 0x001F0000) >> (0 + 16)) +#define HOUR(dt) ((dt & 0x0000F800) >> 11) +#define MINUTE(dt) ((dt & 0x000007E0) >> 5) +#define SECOND(dt) ((dt & 0x0000001F) << 1) + +#include +#include + +#ifndef READ +#define READ 1 +#endif +#ifndef WRITE +#define WRITE 2 +#endif + +/* +** The "bool" integral type was defined by the C++ committee in +** November of '94. Until the compiler supports this, use the following +** definition. +*/ +#ifndef __BORLANDC__ +#ifndef TRUE_FALSE_DEFINED +#define TRUE_FALSE_DEFINED +enum {false=0,true=1}; +typedef int bool; +#endif +#endif + + +class FileClass +{ + public: + virtual ~FileClass(void) {}; + virtual char const * File_Name(void) const = 0; + virtual char const * Set_Name(char const *filename) = 0; + virtual int Create(void) = 0; + virtual int Delete(void) = 0; + virtual int Is_Available(int forced=false) = 0; + virtual int Is_Open(void) const = 0; + virtual int Open(char const *filename, int rights=READ) = 0; + virtual int Open(int rights=READ) = 0; + virtual long Read(void *buffer, long size) = 0; + virtual long Seek(long pos, int dir=SEEK_CUR) = 0; + virtual long Size(void) = 0; + virtual long Write(void const *buffer, long size) = 0; + virtual void Close(void) = 0; + virtual unsigned long Get_Date_Time(void) {return(0);} + virtual bool Set_Date_Time(unsigned long ) {return(false);} + virtual void Error(int error, int canretry = false, char const * filename=NULL) = 0; + + operator char const * () {return File_Name();}; +}; + +#endif diff --git a/code/redalert/xpipe.cpp b/code/redalert/xpipe.cpp new file mode 100644 index 0000000..7781d5a --- /dev/null +++ b/code/redalert/xpipe.cpp @@ -0,0 +1,163 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XPIPE.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XPIPE.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 5, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * FilePipe::Put -- Submit a block of data to the pipe. * + * FilePipe::End -- End the file pipe handler. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#include "FUNCTION.H" +#include "xpipe.h" +#include +#include + + +//--------------------------------------------------------------------------------------------------------- +// BufferPipe +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferPipe::Put -- Submit data to the buffered pipe segment. * + * * + * The buffered pipe is a pipe terminator. That is, the data never flows onto subsequent * + * pipe chains. The data is stored into the buffer previously submitted to the pipe. * + * If the buffer is full, no more data is output to the buffer. * + * * + * INPUT: source -- Pointer to the data to submit. * + * * + * length -- The number of bytes to be submitted. * + * * + * OUTPUT: Returns with the number of bytes output to the destination buffer. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferPipe::Put(void const * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(((char *)BufferPtr.Get_Buffer()) + Index, source, len); + } + + Index += len; +// Length -= len; +// Buffer = ((char *)Buffer) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FilePipe +//--------------------------------------------------------------------------------------------------------- + +FilePipe::~FilePipe(void) +{ + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + File = NULL; + } +} + + +/*********************************************************************************************** + * FilePipe::End -- End the file pipe handler. * + * * + * This routine is called when there will be no more data sent through the pipe. It is * + * responsible for cleaning up anything it needs to. This is not handled by the * + * destructor, although it serves a similar purpose, because pipe are linked together and * + * the destructor order is not easily controlled. If the destructors for a pipe chain were * + * called out of order, the result might be less than pleasant. * + * * + * INPUT: none * + * * + * OUTPUT: Returns with the number of bytes flushed out the final end of the pipe as a * + * consequence of this routine. * + * * + * WARNINGS: Don't send any more data through the pipe after this routine is called. * + * * + * HISTORY: * + * 07/05/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::End(void) +{ + int total = Pipe::End(); + if (Valid_File() && HasOpened) { + HasOpened = false; + File->Close(); + } + return(total); +} + + +/*********************************************************************************************** + * FilePipe::Put -- Submit a block of data to the pipe. * + * * + * Takes the data block submitted and writes it to the file. If the file was not already * + * open, this routine will open it for write. * + * * + * INPUT: source -- Pointer to the data to submit to the file. * + * * + * length -- The number of bytes to write to the file. * + * * + * OUTPUT: Returns with the number of bytes written to the file. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FilePipe::Put(void const * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + File->Open(WRITE); + } + + return(File->Write(source, slen)); + } + return(0); +} diff --git a/code/redalert/xpipe.h b/code/redalert/xpipe.h new file mode 100644 index 0000000..ff85dcc --- /dev/null +++ b/code/redalert/xpipe.h @@ -0,0 +1,92 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XPIPE.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XPIPE.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +#ifndef XPIPE_H +#define XPIPE_H + +#include "pipe.h" +#include "wwfile.h" +#include "buff.h" + +/* +** This is a simple store-into-buffer pipe terminator. Use it as the final link in a pipe process +** that needs to store the data into a memory buffer. This can only serve as the final +** link in the chain of pipe segments. +*/ +class BufferPipe : public Pipe +{ + public: + BufferPipe(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferPipe(void * buffer, int length) : BufferPtr(buffer, length), Index(0) {} + virtual int Put(void const * source, int slen); + + private: + Buffer BufferPtr; + int Index; + +// void * Buffer; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferPipe(BufferPipe & rvalue); + BufferPipe & operator = (BufferPipe const & pipe); +}; + + +/* +** This is a store-to-file pipe terminator. Use it as the final link in a pipe process that +** needs to store the data to a file. This can only serve as the last link in the chain +** of pipe segments. +*/ +class FilePipe : public Pipe +{ + public: + FilePipe(FileClass * file) : File(file), HasOpened(false) {} + FilePipe(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FilePipe(void); + + virtual int Put(void const * source, int slen); + virtual int End(void); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FilePipe(FilePipe & rvalue); + FilePipe & operator = (FilePipe const & pipe); + +}; + +#endif diff --git a/code/redalert/xstraw.cpp b/code/redalert/xstraw.cpp new file mode 100644 index 0000000..14e3752 --- /dev/null +++ b/code/redalert/xstraw.cpp @@ -0,0 +1,148 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XSTRAW.CPP 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XSTRAW.CPP * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * FileStraw::Get -- Fetch data from the file. * + * FileStraw::~FileStraw -- The destructor for the file straw. * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include "FUNCTION.H" +#include "xstraw.h" +#include +#include + +//--------------------------------------------------------------------------------------------------------- +// BufferStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * BufferStraw::Get -- Fetch data from the straw's buffer holding tank. * + * * + * This routine will copy the requested number of bytes from the buffer holding tank (as * + * set up by the straw's constructor) to the buffer specified. * + * * + * INPUT: source -- Pointer to the buffer to be filled with data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes copied to the buffer. If this is less than * + * requested, then it indicates that the data holding tank buffer is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int BufferStraw::Get(void * source, int slen) +{ + int total = 0; + + if (Is_Valid() && source != NULL && slen > 0) { + int len = slen; + if (BufferPtr.Get_Size() != 0) { + int theoretical_max = BufferPtr.Get_Size() - Index; + len = (slen < theoretical_max) ? slen : theoretical_max; + } + + if (len > 0) { + memmove(source, ((char*)BufferPtr.Get_Buffer()) + Index, len); + } + + Index += len; +// Length -= len; +// BufferPtr = ((char *)BufferPtr) + len; + total += len; + } + return(total); +} + + +//--------------------------------------------------------------------------------------------------------- +// FileStraw +//--------------------------------------------------------------------------------------------------------- + + +/*********************************************************************************************** + * FileStraw::Get -- Fetch data from the file. * + * * + * This routine will read data from the file (as specified in the straw's constructor) into * + * the buffer indicated. * + * * + * INPUT: source -- Pointer to the buffer to hold the data. * + * * + * length -- The number of bytes requested. * + * * + * OUTPUT: Returns with the number of bytes stored into the buffer. If this number is less * + * than the number requested, then this indicates that the file is exhausted. * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +int FileStraw::Get(void * source, int slen) +{ + if (Valid_File() && source != NULL && slen > 0) { + if (!File->Is_Open()) { + HasOpened = true; + if (!File->Is_Available()) return(0); + if (!File->Open(READ)) return(0); + } + + return(File->Read(source, slen)); + } + return(0); +} + + +/*********************************************************************************************** + * FileStraw::~FileStraw -- The destructor for the file straw. * + * * + * This destructor only needs to close the file if it was the one to open it. * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/03/1996 JLB : Created. * + *=============================================================================================*/ +FileStraw::~FileStraw(void) +{ + if (Valid_File() && HasOpened) { + File->Close(); + HasOpened = false; + File = NULL; + } +} diff --git a/code/redalert/xstraw.h b/code/redalert/xstraw.h new file mode 100644 index 0000000..7a3361a --- /dev/null +++ b/code/redalert/xstraw.h @@ -0,0 +1,88 @@ +// +// Copyright 2020 Electronic Arts Inc. +// +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free +// software: you can redistribute it and/or modify it under the terms of +// the GNU General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. + +// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed +// in the hope that it will be useful, but with permitted additional restrictions +// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT +// distributed with this program. You should have received a copy of the +// GNU General Public License along with permitted additional restrictions +// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection + +/* $Header: /CounterStrike/XSTRAW.H 1 3/03/97 10:26a Joe_bostic $ */ +/*********************************************************************************************** + *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** + *********************************************************************************************** + * * + * Project Name : Command & Conquer * + * * + * File Name : XSTRAW.H * + * * + * Programmer : Joe L. Bostic * + * * + * Start Date : 07/04/96 * + * * + * Last Update : July 4, 1996 [JLB] * + * * + *---------------------------------------------------------------------------------------------* + * Functions: * + * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +#ifndef XSTRAW_H +#define XSTRAW_H + +#include "straw.h" +#include "buff.h" +#include "wwfile.h" +#include + +/* +** This class is used to manage a buffer as a data source. Data requests will draw from the +** buffer supplied until the buffer is exhausted. +*/ +class BufferStraw : public Straw +{ + public: + BufferStraw(Buffer const & buffer) : BufferPtr(buffer), Index(0) {} + BufferStraw(void const * buffer, int length) : BufferPtr((void*)buffer, length), Index(0) {} + virtual int Get(void * source, int slen); + + private: + Buffer BufferPtr; + int Index; +// void const * BufferPtr; +// int Length; + + bool Is_Valid(void) {return(BufferPtr.Is_Valid());} + BufferStraw(BufferStraw & rvalue); + BufferStraw & operator = (BufferStraw const & pipe); +}; + +/* +** This class is used to manage a file as a data source. Data requests will draw from the +** file until the file has been completely read. +*/ +class FileStraw : public Straw +{ + public: + FileStraw(FileClass * file) : File(file), HasOpened(false) {} + FileStraw(FileClass & file) : File(&file), HasOpened(false) {} + virtual ~FileStraw(void); + virtual int Get(void * source, int slen); + + private: + FileClass * File; + bool HasOpened; + + bool Valid_File(void) {return(File != NULL);} + FileStraw(FileStraw & rvalue); + FileStraw & operator = (FileStraw const & pipe); +}; + + +#endif diff --git a/imgui.ini b/imgui.ini new file mode 100644 index 0000000..76dc3f9 --- /dev/null +++ b/imgui.ini @@ -0,0 +1,15 @@ +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][RenderWindow] +Pos=0,0 +Size=1024,768 +Collapsed=0 + +[Window][Developer Console] +Pos=0,0 +Size=420,270 +Collapsed=0 + From 913c08ba5eee5f23460b5eb31b5ae2772b92849f Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 21:44:48 +0200 Subject: [PATCH 15/18] Fix casing in git ignore. --- .gitignore | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 4b893ed..1a561b5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,11 +8,11 @@ /fonts /sound /ui -/*.mix -/*.pal -/*.ini /*.exe /*.ilk -/*.dat -/savegame* +/*.MIX +/*.PAL +/*.INI +/*.DAT +/SAVEGAME* From 99c7a550be65f6304a175fda4a1f402b7f900228 Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 22:23:31 +0200 Subject: [PATCH 16/18] User input: casing --- code/CMakeLists.txt | 2 +- code/redalert/function.h | 3 ++- code/redalert/io/userinput.cpp | 4 ++-- code/redalert/io/userinput.h | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 3f575b2..40f868a 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -742,4 +742,4 @@ if (UNIX) set_target_properties(RedAlert PROPERTIES OUTPUT_NAME "RedAlert" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/.." ) endif(UNIX) -target_include_directories(RedAlert PRIVATE ./external/xml/;./external/devil/;./external/;./redalert/win32lib;./external/lua/;./external/imgui;./external/dxsdk/Include;./external/ffmpeg-win32/include;./external/sdl2/include;./win32lib;./external/openal/include) \ No newline at end of file +target_include_directories(RedAlert PRIVATE ./external/xml/;./external/devil/;./external/;./redalert/win32lib;./redalert/io;./external/lua/;./external/imgui;./external/dxsdk/Include;./external/ffmpeg-win32/include;./external/sdl2/include;./win32lib;./external/openal/include) diff --git a/code/redalert/function.h b/code/redalert/function.h index dc10fba..ae1810a 100644 --- a/code/redalert/function.h +++ b/code/redalert/function.h @@ -200,7 +200,8 @@ struct NoInitClass { #endif #include "key.h" -#include "io/UserInput.h" +#include "io/userinput.h" +#include "io/eventqueue.h" #include #include "mpu.h" diff --git a/code/redalert/io/userinput.cpp b/code/redalert/io/userinput.cpp index aadd59c..088f926 100644 --- a/code/redalert/io/userinput.cpp +++ b/code/redalert/io/userinput.cpp @@ -1,5 +1,5 @@ -#include "../FUNCTION.H" -#include "UserInput.h" +#include "../function.h" +#include "userinput.h" extern KeyboardClass * Keyboard; diff --git a/code/redalert/io/userinput.h b/code/redalert/io/userinput.h index d6cc690..314e75b 100644 --- a/code/redalert/io/userinput.h +++ b/code/redalert/io/userinput.h @@ -116,7 +116,8 @@ typedef enum { class UserInputClass { public: - UserInputClass(); + UserInputClass(void); + void UserInputClass::Process_Input( KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags ); InputAction LastAction; From d0440932d94cc8ae3892bd47dcb05b3440839ba6 Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 22:52:37 +0200 Subject: [PATCH 17/18] Sidebar fixes, power fix, input fixes. --- .gitignore | 1 + code/redalert/conquer.cpp | 10 +- code/redalert/externs.h | 1 + code/redalert/gadget.cpp | 156 +++------- code/redalert/globals.cpp | 1 + code/redalert/io/eventqueue.cpp | 4 +- code/redalert/io/userinput.cpp | 300 +++++++++++------- code/redalert/io/userinput.h | 79 +++-- code/redalert/key.cpp | 2 +- code/redalert/options.cpp | 3 +- code/redalert/power.cpp | 99 +++--- code/redalert/sidebar.cpp | 520 +++++++++++++++++--------------- 12 files changed, 628 insertions(+), 548 deletions(-) diff --git a/.gitignore b/.gitignore index 1a561b5..7ea87a2 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /*.DAT /SAVEGAME* +/cache diff --git a/code/redalert/conquer.cpp b/code/redalert/conquer.cpp index 10a55c5..2614ddc 100644 --- a/code/redalert/conquer.cpp +++ b/code/redalert/conquer.cpp @@ -743,7 +743,7 @@ void Keyboard_Process(KeyNumType & input) ** Center the map about the construction yard or construction vehicle ** if one is present. */ - if (key != 0 && key == VK_H) { + if (key != 0 && key == Options.KeyBase) { Unselect_All(); if (PlayerPtr->CurBuildings) { for (index = 0; index < Buildings.Count(); index++) { @@ -875,19 +875,19 @@ void Keyboard_Process(KeyNumType & input) ** Scrolls the tactical map in the direction specified. */ int distance = CELL_LEPTON_W; - if (key != 0 && key == VK_LEFT) { + if (key != 0 && key == KN_LEFT) { Map.Scroll_Map(DIR_W, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_RIGHT) { + if (key != 0 && key == KN_RIGHT) { Map.Scroll_Map(DIR_E, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_UP) { + if (key != 0 && key == KN_UP) { Map.Scroll_Map(DIR_N, distance, true); input = KN_NONE; } - if (key != 0 && key == VK_DOWN) { + if (key != 0 && key == KN_DOWN) { Map.Scroll_Map(DIR_S, distance, true); input = KN_NONE; } diff --git a/code/redalert/externs.h b/code/redalert/externs.h index da7f00a..45eb9b1 100644 --- a/code/redalert/externs.h +++ b/code/redalert/externs.h @@ -162,6 +162,7 @@ extern PKey SlowKey; extern RulesClass Rule; extern KeyboardClass * Keyboard; extern UserInputClass UserInput; +extern EventQueueClass EventQueue; extern RandomStraw CryptRandom; extern RandomClass NonCriticalRandomNumber; extern CarryoverClass * Carryover; diff --git a/code/redalert/gadget.cpp b/code/redalert/gadget.cpp index 71c1d32..6dba2e5 100644 --- a/code/redalert/gadget.cpp +++ b/code/redalert/gadget.cpp @@ -58,31 +58,31 @@ #include -/* -** This records the current gadget the the gadget system is "stuck on". Such a -** gadget will be processed to the exclusion of all others until the mouse button -** is no longer pressed. -*/ -GadgetClass * GadgetClass::StuckOn = 0; + /* + ** This records the current gadget the the gadget system is "stuck on". Such a + ** gadget will be processed to the exclusion of all others until the mouse button + ** is no longer pressed. + */ +GadgetClass* GadgetClass::StuckOn = 0; /* ** This is a copy of a pointer to the last list used by the gadget input system. ** If a change of list is detected, then all gadgets are forced to be redrawn. */ -GadgetClass * GadgetClass::LastList = 0; +GadgetClass* GadgetClass::LastList = 0; /* ** This points to the gadget that is intercepting all keyboard events. */ -GadgetClass * GadgetClass::Focused = 0; +GadgetClass* GadgetClass::Focused = 0; /* ** This points to the current color scheme for drawing all gadgets. */ -static RemapControlType _GreyScheme = {15}; -RemapControlType * GadgetClass::ColorScheme = &_GreyScheme; +static RemapControlType _GreyScheme = { 15 }; +RemapControlType* GadgetClass::ColorScheme = &_GreyScheme; /*********************************************************************************************** * GadgetClass::GadgetClass -- Constructor for gadget object. * @@ -110,7 +110,7 @@ GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) X(x), Y(y), Width(w), Height(h), IsToRepaint(false), IsSticky(sticky), IsDisabled(false), Flags(flags) { if (IsSticky) { - Flags |= LEFTPRESS|LEFTRELEASE; + Flags |= LEFTPRESS | LEFTRELEASE; } } @@ -130,7 +130,7 @@ GadgetClass::GadgetClass(int x, int y, int w, int h, unsigned flags, int sticky) * HISTORY: * * 08/01/1996 JLB : Created. * *=============================================================================================*/ -GadgetClass::GadgetClass(GadgetClass const & gadget) : +GadgetClass::GadgetClass(GadgetClass const& gadget) : X(gadget.X), Y(gadget.Y), Width(gadget.Width), @@ -190,7 +190,7 @@ GadgetClass::~GadgetClass(void) * * * HISTORY: 01/03/1995 MML : Created. * *=========================================================================*/ -int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mousey) +int GadgetClass::Clicked_On(KeyNumType& key, unsigned flags, int mousex, int mousey) { /* ** Set flags to match only those events that occur AND are being looked for. If @@ -212,7 +212,7 @@ int GadgetClass::Clicked_On(KeyNumType & key, unsigned flags, int mousex, int mo */ if (this == StuckOn || (flags & KEYBOARD) || - (flags && (mousex - X) < Width && (mousey - Y) < Height)) { + (flags && (mousex - X) < Width && (mousey - Y) < Height)) { return(Action(flags, key)); } @@ -281,10 +281,10 @@ void GadgetClass::Disable(void) * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ -GadgetClass * GadgetClass::Remove(void) +GadgetClass* GadgetClass::Remove(void) { Clear_Focus(); - return(GadgetClass *)LinkClass::Remove(); + return(GadgetClass*)LinkClass::Remove(); } @@ -303,7 +303,7 @@ GadgetClass * GadgetClass::Remove(void) * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ -GadgetClass * GadgetClass::Get_Next(void) const +GadgetClass* GadgetClass::Get_Next(void) const { return(GadgetClass*)LinkClass::Get_Next(); } @@ -324,7 +324,7 @@ GadgetClass * GadgetClass::Get_Next(void) const * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ -GadgetClass * GadgetClass::Get_Prev(void) const +GadgetClass* GadgetClass::Get_Prev(void) const { return(GadgetClass*)LinkClass::Get_Prev(); } @@ -347,7 +347,7 @@ GadgetClass * GadgetClass::Get_Prev(void) const *=============================================================================================*/ void GadgetClass::Delete_List(void) { - GadgetClass * g = this; + GadgetClass* g = this; /* ** Move to head of the list. @@ -365,7 +365,7 @@ void GadgetClass::Delete_List(void) while (g) { g->Clear_Focus(); - GadgetClass * temp = g; + GadgetClass* temp = g; g = g->Get_Next(); delete temp; } @@ -391,7 +391,7 @@ void GadgetClass::Delete_List(void) * HISTORY: * * 01/15/1995 JLB : Created. * *=============================================================================================*/ -int GadgetClass::Action(unsigned flags, KeyNumType &) +int GadgetClass::Action(unsigned flags, KeyNumType&) { /* ** If any of the event flags are active, then this indicates that something probably @@ -450,7 +450,7 @@ int GadgetClass::Draw_Me(int forced) *=============================================================================================*/ void GadgetClass::Draw_All(bool forced) { - GadgetClass * gadget = this; + GadgetClass* gadget = this; while (gadget != NULL) { gadget->Draw_Me(forced); @@ -470,8 +470,10 @@ void GadgetClass::Draw_All(bool forced) * HISTORY: 01/03/1995 MML : Created. * *=========================================================================*/ KeyNumType GadgetClass::Input(void) -{ - KeyNumType key; +{ + EventMessage eventMessage; + KeyNumType key = KN_NONE; + int flags = 0; int forced = false; /* @@ -488,111 +490,52 @@ KeyNumType GadgetClass::Input(void) /* ** Fetch any pending keyboard input. */ - key = Keyboard->Check(); - if (key != 0) { - key = Keyboard->Get(); + UserInput.Process_Input(key, flags); + + if (EventQueue.Check()) { + eventMessage = EventQueue.Get(); + eventMessage.Key_Flags(key, flags); } - /* - ** For mouse button clicks, the mouse position is actually held in the MouseQ... - ** globals rather than their normal Mouse... globals. This is because we need to - ** know the position of the mouse at the exact instant when the click occurred - ** rather the the mouse position at the time we get around to this function. - */ -// jmarshall - UserInput.Process_Input( g_globalKeyNumType, g_globalKeyFlags); - int flags = g_globalKeyFlags; -// jmarshall end - - /* - ** Set the mouse button state flags. These will be passed to the individual - ** buttons so that they can determine what action to perform (if any). - */ - //if (key) { - // if (key == KN_LMOUSE) { - // flags |= LEFTPRESS; - // } - // if (key == KN_RMOUSE) { - // flags |= RIGHTPRESS; - // } - // if (key == (KN_LMOUSE | KN_RLSE_BIT)) { - // flags |= LEFTRELEASE; - // } - // if (key == (KN_RMOUSE | KN_RLSE_BIT)) { - // flags |= RIGHTRELEASE; - // } - //} - - /* - ** If the mouse wasn't responsible for this key code, then it must be from - ** the keyboard. Flag this fact. - */ - if (key && !flags) { - flags |= KEYBOARD; - } - - /* - ** Mouse button up or down action is ignored if there is a keyboard event. This - ** allows keyboard events to fall through normally even if the mouse is over a - ** gadget that is flagged for LEFTUP or RIGHTUP. - */ -// jmarshall - //if (!key) { - // - // /* - // ** Check for the mouse being held down. We can't use the normal input system - // ** for this, so we must examine the actual current state of the mouse - // ** buttons. As a side note, if we determine that the mouse button isn't being - // ** held down, then we automatically know that it must be up -- set the flag - // ** accordingly. - // */ - // if (Keyboard->Down(KN_LMOUSE)) { - // flags |= LEFTHELD; - // } else { - // flags |= LEFTUP; - // } - // if (Keyboard->Down(KN_RMOUSE)) { - // flags |= RIGHTHELD; - // } else { - // flags |= RIGHTUP; - // } - //} -// jmarshall end - /* ** If "sticky" processing is active, then only process the stuck gadget. */ if (StuckOn) { StuckOn->Draw_Me(false); - GadgetClass * oldstuck = StuckOn; + GadgetClass* oldstuck = StuckOn; StuckOn->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); - if (StuckOn) { + + if (StuckOn) + { StuckOn->Draw_Me(false); - } else { + } + else { oldstuck->Draw_Me(false); } - } else { - + } + else + { /* ** If there is a gadget that has the keyboard focus, then route all keyboard ** events to it. */ - if (Focused && (flags & KEYBOARD)) { + if (Focused && eventMessage.EventType == EventMessageType::EVENT_KEYBOARD) { Focused->Draw_Me(false); Focused->Clicked_On(key, flags, UserInput.Mouse.X, UserInput.Mouse.Y); if (Focused) { Focused->Draw_Me(false); } - } else { + } + else { /* ** Sweep through all the buttons in the chain and pass the current button state ** and keyboard input data to them. These routines will detect whether they should ** perform some action and return a flag to this effect. They also have the option - ** of changing the key value so that an appropriate return value is use for this + ** of changing the key value so that an appropriate return value is used for this ** processing routine. */ - GadgetClass * next_button = this; + GadgetClass* next_button = this; while (next_button != NULL) { /* @@ -621,6 +564,7 @@ KeyNumType GadgetClass::Input(void) } } } + return(key); } @@ -645,14 +589,14 @@ KeyNumType GadgetClass::Input(void) * HISTORY: * * 01/16/1995 JLB : Created. * *=============================================================================================*/ -ControlClass * GadgetClass::Extract_Gadget(unsigned id) +ControlClass* GadgetClass::Extract_Gadget(unsigned id) { - GadgetClass * g = this; + GadgetClass* g = this; if (id != 0) { while (g != NULL) { if (g->Get_ID() == id) { - return((ControlClass *)g); + return((ControlClass*)g); } g = g->Get_Next(); } @@ -801,7 +745,7 @@ bool GadgetClass::Has_Focus(void) *=============================================================================================*/ int GadgetClass::Is_List_To_Redraw(void) { - GadgetClass * gadget = this; + GadgetClass* gadget = this; while (gadget != NULL) { if (gadget->IsToRepaint) { diff --git a/code/redalert/globals.cpp b/code/redalert/globals.cpp index 1c04133..54e06db 100644 --- a/code/redalert/globals.cpp +++ b/code/redalert/globals.cpp @@ -257,6 +257,7 @@ RulesClass Rule; ** user input class pointer. */ UserInputClass UserInput; +EventQueueClass EventQueue; /*************************************************************************** ** All keyboard input is routed through the object pointed to by this diff --git a/code/redalert/io/eventqueue.cpp b/code/redalert/io/eventqueue.cpp index eef7a05..512ec54 100644 --- a/code/redalert/io/eventqueue.cpp +++ b/code/redalert/io/eventqueue.cpp @@ -1,5 +1,5 @@ -#include "../FUNCTION.H" -#include "EventQueue.h" +#include "../function.h" +#include "eventqueue.h" /* Clears the event queue and frees memory diff --git a/code/redalert/io/userinput.cpp b/code/redalert/io/userinput.cpp index 088f926..6c05848 100644 --- a/code/redalert/io/userinput.cpp +++ b/code/redalert/io/userinput.cpp @@ -1,18 +1,60 @@ #include "../function.h" #include "userinput.h" -extern KeyboardClass * Keyboard; +extern KeyboardClass* Keyboard; + +std::map sdl_keyMapping_reverse; + +UserInputClass::UserInputClass() +{ + LastAction = InputAction::INPUT_ACTION_NONE; + + // generate reverse keymapping + for (auto it = sdl_keyMapping.begin(); it != sdl_keyMapping.end(); it++) + { + sdl_keyMapping_reverse[it->second] = it->first; + } +} + +bool UserInputClass::KeyboardState::Key_Down(KeyNumType key) const +{ + auto sdl_Key = sdl_keyMapping_reverse.find(key); + if (sdl_Key != sdl_keyMapping_reverse.end()) + { + const SDL_Scancode scanCode = SDL_GetScancodeFromKey(sdl_Key->second); + const Uint8* keyState = SDL_GetKeyboardState(NULL); + return (keyState[scanCode]); + } + return false; +} + +bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const +{ + // get mouse state, we are only interested in the buttons + const Uint32 MouseState = SDL_GetMouseState(NULL, NULL); + + switch (key) + { + case KN_LMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_LEFT); + break; + case KN_RMOUSE: + return MouseState & SDL_BUTTON(SDL_BUTTON_RIGHT); + break; + } +} void UserInputClass::Process_Input(KeyNumType& key, int& flags) { bool leftMouseProcessed = false; - SDL_GetMouseState(&Mouse.X, &Mouse.Y); + + const Uint32 MouseState = SDL_GetMouseState(&Mouse.X, &Mouse.Y); + + flags = 0; Keyboard->MouseQX = Mouse.X; Keyboard->MouseQY = Mouse.Y; - flags = 0; - static SDL_Event event; memset(&event, 0, sizeof(SDL_Event)); @@ -21,119 +63,161 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) switch (event.type) { - case SDL_SYSWMEVENT: - if (event.syswm.msg->msg.win.msg == WM_DESTROY) - { - Invalidate_Cached_Icons(); - VisiblePage.Un_Init(); - HiddenPage.Un_Init(); - AllSurfaces.Release(); - if (!InDebugger) Reset_Video_Mode(); - - PostQuitMessage(0); - - switch (ReadyToQuit) { - case 1: - ReadyToQuit = 2; - break; - case 0: - ExitProcess(0); - break; - } - } - - break; - - case SDL_MOUSEMOTION: - flags = GadgetClass::LEFTUP; - break; - - case SDL_MOUSEBUTTONDOWN: - if (event.button.button == SDL_BUTTON_LEFT) { - Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_DOWN; - - key = KN_LMOUSE; - flags |= GadgetClass::LEFTPRESS; - - Keyboard->Put_Element(KN_LMOUSE); - - leftMouseProcessed = true; - numFramesLMouse++; - } - else if (event.button.button == SDL_BUTTON_RIGHT) { - Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_DOWN; - - Keyboard->Put_Element(KN_RMOUSE); - - key = KN_RMOUSE; - flags |= GadgetClass::RIGHTPRESS; - - numFramesLMouse = 0; - } - break; - - case SDL_MOUSEBUTTONUP: - if (event.button.button == SDL_BUTTON_LEFT) { - Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_RELEASE; - flags &= GadgetClass::LEFTPRESS; - flags |= GadgetClass::LEFTRELEASE; - numFramesLMouse = 0; - } - else if (event.button.button == SDL_BUTTON_RIGHT) { - Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_RELEASE; - - flags &= GadgetClass::RIGHTPRESS; - flags |= GadgetClass::RIGHTRELEASE; - numFramesLMouse = 0; - } - break; - - case SDL_KEYDOWN: + case SDL_SYSWMEVENT: + if (event.syswm.msg->msg.win.msg == WM_DESTROY) { - if (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) { - renderConsole = !renderConsole; + Invalidate_Cached_Icons(); + VisiblePage.Un_Init(); + HiddenPage.Un_Init(); + AllSurfaces.Release(); + if (!InDebugger) Reset_Video_Mode(); - if (renderConsole) { - ShowCursor(TRUE); - Hide_Mouse(); - } - else { - ShowCursor(FALSE); - Show_Mouse(); - } + PostQuitMessage(0); + + switch (ReadyToQuit) { + case 1: + ReadyToQuit = 2; + break; + case 0: + ExitProcess(0); break; } + } - int keyFlags = 0x00; - if (state[SDL_SCANCODE_RSHIFT] || state[SDL_SCANCODE_LSHIFT]) { - KeyB.Shift = true; - keyFlags |= KN_SHIFT_BIT; - } - if (state[SDL_SCANCODE_RALT] || state[SDL_SCANCODE_LALT]) { - keyFlags |= KN_ALT_BIT; - KeyB.Alt = true; - } - if (state[SDL_SCANCODE_RCTRL] || state[SDL_SCANCODE_LCTRL]) { - keyFlags |= KN_CTRL_BIT; - KeyB.Control = true; - } + break; - // handle match between SDL and internals (non-text keys) - auto element = sdl_keyMapping.find((SDL_KeyCode)event.key.keysym.sym); - if (element != sdl_keyMapping.end()) - { - Keyboard->Put_Element(element->second | keyFlags); - KeyB.ASCII = element->second; - } + /* Mouse is moving */ + case SDL_MOUSEMOTION: + if (!(MouseState & SDL_BUTTON(SDL_BUTTON_LEFT))) + { + flags |= GadgetClass::LEFTUP; + } + break; + + case SDL_MOUSEBUTTONDOWN: + if (event.button.button == SDL_BUTTON_LEFT) { + Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_DOWN; + + key = KN_LMOUSE; + flags |= GadgetClass::LEFTPRESS; + + EventQueue.Put(EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE)); + + Keyboard->Put_Element(KN_LMOUSE); + + leftMouseProcessed = true; + numFramesLMouse++; + + // after first left-mouse down, abort handling any other SDL events + // because game might first need to switch into rubber band mode + if (numFramesLMouse == 1) return; + } + else if (event.button.button == SDL_BUTTON_RIGHT) { + Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_DOWN; + + EventQueue.Put(EventMessage(Mouse.X, Mouse.Y, KN_RMOUSE)); + + Keyboard->Put_Element(KN_RMOUSE); + + key = KN_RMOUSE; + flags |= GadgetClass::RIGHTPRESS; + + numFramesLMouse = 0; + } + break; + + case SDL_MOUSEBUTTONUP: + if (event.button.button == SDL_BUTTON_LEFT) { + Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_RELEASE; + + EventQueue.Put(EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE)); + + flags |= GadgetClass::LEFTRELEASE; + numFramesLMouse = 0; + } + else if (event.button.button == SDL_BUTTON_RIGHT) { + Mouse.Button_Right == MouseButtonState::MOUSE_BUTTON_RELEASE; + + EventQueue.Put(EventMessage(Mouse.X, Mouse.Y, KN_RMOUSE, MouseButtonState::MOUSE_BUTTON_RELEASE)); + + flags |= GadgetClass::RIGHTRELEASE; + numFramesLMouse = 0; + } + break; + + // Keyboard key lifted up + case SDL_KEYUP: + flags |= GadgetClass::KEYBOARD; + + break; + + // Keyboard key pressed down + case SDL_KEYDOWN: + { + if (event.key.keysym.scancode == SDL_SCANCODE_GRAVE) { + renderConsole = !renderConsole; + + if (renderConsole) { + ShowCursor(TRUE); + Hide_Mouse(); + } + else { + ShowCursor(FALSE); + Show_Mouse(); + } break; } - case SDL_TEXTINPUT: - // limit to range 0-255 - if (event.text.text[0] == ( event.text.text[0] & 0xFF ) ) + + flags |= GadgetClass::KEYBOARD; + + int keyFlags = 0x00; + KeyB.Shift = false; + KeyB.Alt = false; + KeyB.Control = false; + KeyB.CapsLock = false; + + if (state[SDL_SCANCODE_RSHIFT] || state[SDL_SCANCODE_LSHIFT]) { + KeyB.Shift = true; + keyFlags |= KN_SHIFT_BIT; + } + if (state[SDL_SCANCODE_RALT] || state[SDL_SCANCODE_LALT]) { + keyFlags |= KN_ALT_BIT; + KeyB.Alt = true; + } + if (state[SDL_SCANCODE_RCTRL] || state[SDL_SCANCODE_LCTRL]) { + keyFlags |= KN_CTRL_BIT; + KeyB.Control = true; + } + if (state[SDL_SCANCODE_CAPSLOCK]) { + keyFlags |= KN_CAPSLOCK_BIT; + KeyB.CapsLock = true; + } + + // handle match between SDL and internals ( non-text keys ) + auto element = sdl_keyMapping.find((SDL_KeyCode)event.key.keysym.sym); + if (element != sdl_keyMapping.end()) + { + //EventQueue.Put( element->second | keyFlags ); + EventQueue.Put(EventMessage((KeyNumType)element->second, element->second, KeyB.Shift, KeyB.Alt, KeyB.Control, KeyB.CapsLock)); + + Keyboard->Put_Element(element->second | keyFlags); + + KeyB.LastKey = element->first; + if (event.text.text[0] == (event.text.text[0] & 0xFF)) { - KeyB.ASCII = (char)event.text.text[0]; + KeyB.ASCII = element->second; } + } + + break; + } + case SDL_TEXTINPUT: + // limit to range 0-255 + if (event.text.text[0] == (event.text.text[0] & 0xFF)) + { + KeyB.ASCII = (char)event.text.text[0]; + } } if (Debug_Map || renderConsole) @@ -145,6 +229,8 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) if (!leftMouseProcessed && numFramesLMouse != 0) { Mouse.Button_Left == MouseButtonState::MOUSE_BUTTON_HOLD; - flags = GadgetClass::LEFTHELD; + flags |= GadgetClass::LEFTHELD; + + EventQueue.Put(EventMessage(Mouse.X, Mouse.Y, KN_LMOUSE, MouseButtonState::MOUSE_BUTTON_HOLD)); } } \ No newline at end of file diff --git a/code/redalert/io/userinput.h b/code/redalert/io/userinput.h index 314e75b..412e29c 100644 --- a/code/redalert/io/userinput.h +++ b/code/redalert/io/userinput.h @@ -115,53 +115,52 @@ typedef enum { class UserInputClass { +public: + UserInputClass(); + void UserInputClass::Process_Input(KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags); + + InputAction LastAction; + + class KeyboardState { public: - UserInputClass(void); + SDL_KeyCode LastKey; + char ASCII; - void UserInputClass::Process_Input( KeyNumType& key = g_globalKeyNumType, int& flags = g_globalKeyFlags ); + bool Shift; + bool Control; + bool Alt; + bool CapsLock; - InputAction LastAction; + bool Key_Down(KeyNumType key) const; - class KeyboardState { - public: - SDL_KeyCode LastKey; - char ASCII; + void ResetState() { + this->Shift = false; + this->Control = false; + this->Alt = false; + } + }; - bool Shift; - bool Control; - bool Alt; - bool CapsLock; + KeyboardState KeyB; - bool Key_Down(KeyNumType key) const; + class MouseState { + public: + int X; + int Y; - void ResetState() { - this->Shift = false; - this->Control = false; - this->Alt = false; - } - }; + MouseButtonState Button_Left; + MouseButtonState Button_Middle; + MouseButtonState Button_Right; - KeyboardState KeyB; + bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const; - class MouseState { - public: - int X; - int Y; + void ResetState() { + this->Button_Left = MouseButtonState::MOUSE_BUTTON_UP; + this->Button_Middle = MouseButtonState::MOUSE_BUTTON_UP; + this->Button_Right = MouseButtonState::MOUSE_BUTTON_UP; + } + }; - MouseButtonState Button_Left; - MouseButtonState Button_Middle; - MouseButtonState Button_Right; - - bool UserInputClass::MouseState::Mouse_Down(KeyNumType key) const; - - void ResetState() { - this->Button_Left = MouseButtonState::MOUSE_BUTTON_UP; - this->Button_Middle = MouseButtonState::MOUSE_BUTTON_UP; - this->Button_Right = MouseButtonState::MOUSE_BUTTON_UP; - } - }; - - MouseState Mouse; - private: - int numFramesLMouse = 0; -}; \ No newline at end of file + MouseState Mouse; +private: + int numFramesLMouse = 0; +}; diff --git a/code/redalert/key.cpp b/code/redalert/key.cpp index 90626c7..4e80790 100644 --- a/code/redalert/key.cpp +++ b/code/redalert/key.cpp @@ -372,7 +372,7 @@ bool WWKeyboardClass::Down(unsigned short key) return g_globalKeyFlags & GadgetClass::RIGHTPRESS; } - return(GetAsyncKeyState(key & 0xFF) == 0 ? false : true); + return UserInput.KeyB.Key_Down((KeyNumType)key); } diff --git a/code/redalert/options.cpp b/code/redalert/options.cpp index cff3732..734ddb1 100644 --- a/code/redalert/options.cpp +++ b/code/redalert/options.cpp @@ -591,6 +591,7 @@ void OptionsClass::Load_Settings(void) SlowPalette = ini.Get_Bool(OPTIONS, "SlowPalette", SlowPalette); IsPaletteScroll = ini.Get_Bool(OPTIONS, "PaletteScroll", IsPaletteScroll); + /* KeyForceMove1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove1", KeyForceMove1); KeyForceMove2 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceMove2", KeyForceMove2); KeyForceAttack1 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyForceAttack1", KeyForceAttack1); @@ -641,7 +642,6 @@ void OptionsClass::Load_Settings(void) KeyTeam9 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam9", KeyTeam9); KeyTeam10 = (KeyNumType)ini.Get_Int(HotkeyName, "KeyTeam10", KeyTeam10); - #ifdef WIN32 KeyForceMove1 = (KeyNumType)(KeyForceMove1 & ~WWKEY_VK_BIT); KeyForceMove2 = (KeyNumType)(KeyForceMove2 & ~WWKEY_VK_BIT); @@ -693,6 +693,7 @@ void OptionsClass::Load_Settings(void) KeyTeam9 = (KeyNumType)(KeyTeam9 & ~WWKEY_VK_BIT); KeyTeam10 = (KeyNumType)(KeyTeam10 & ~WWKEY_VK_BIT); #endif + */ } diff --git a/code/redalert/power.cpp b/code/redalert/power.cpp index b9118ba..8c4edea 100644 --- a/code/redalert/power.cpp +++ b/code/redalert/power.cpp @@ -44,11 +44,11 @@ #include "function.h" -/* -** Points to the shape to use for the "desired" power level indicator. -*/ -void const * PowerClass::PowerShape; -void const * PowerClass::PowerBarShape; + /* + ** Points to the shape to use for the "desired" power level indicator. + */ +void const* PowerClass::PowerShape; +void const* PowerClass::PowerBarShape; PowerClass::PowerButtonClass PowerClass::PowerButton; @@ -137,8 +137,8 @@ void PowerClass::One_Time(void) RadarClass::One_Time(); PowerButton.X = ScreenWidth - (640 - (POWER_X * RESFACTOR)); PowerButton.Y = POWER_Y * RESFACTOR; - PowerButton.Width = (POWER_WIDTH * RESFACTOR)-1; - PowerButton.Height = ScreenHeight - ( POWER_Y * RESFACTOR ) ;// POWER_HEIGHT* RESFACTOR; + PowerButton.Width = (POWER_WIDTH * RESFACTOR) - 1; + PowerButton.Height = ScreenHeight - (POWER_Y * RESFACTOR);// POWER_HEIGHT* RESFACTOR; PowerShape = MFCD::Retrieve("POWER.SHP"); PowerBarShape = MFCD::Retrieve("POWERBAR.SHP"); } @@ -162,13 +162,14 @@ void PowerClass::One_Time(void) *=============================================================================================*/ void PowerClass::Draw_It(bool complete) { - static int _modtable[]={ + static int _modtable[] = { 0, -1, 0, 1, 0, -1, -2, -1, 0, 1, 2, 1 ,0 }; -// jmarshall - render the world before the power bar. + // jmarshall - render the world before the power bar. RadarClass::Draw_It(complete); -// jmarshall end + // jmarshall end + { BStart(BENCH_POWER); @@ -176,7 +177,7 @@ void PowerClass::Draw_It(bool complete) if (Map.IsSidebarActive) { IsToRedraw = false; ShapeFlags_Type flags = SHAPE_NORMAL; - void const * remap = NULL; + void const* remap = NULL; if (FlashTimer > 1 && ((FlashTimer % 3) & 0x01) != 0) { flags = flags | SHAPE_FADING; @@ -190,25 +191,23 @@ void PowerClass::Draw_It(bool complete) int bottomOfBar = (88 * RESFACTOR) + 56; - while(bottomOfBar < PowerButton.Height + 34 ) + while (bottomOfBar < PowerButton.Height + 42) { CC_Draw_Shape(PowerBarShape, 1, ScreenWidth - (640 - (240 * RESFACTOR)), bottomOfBar, WINDOW_MAIN, flags | SHAPE_NORMAL | SHAPE_WIN_REL, remap); bottomOfBar += 8 * RESFACTOR; } - int bottom = ( PowerButton.Y * RESFACTOR ) + PowerButton.Height; + int bottom = bottomOfBar + (28 * RESFACTOR); /* ** Determine how much the power production exceeds or falls short ** of power demands. */ - int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; - int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; + int power_height = (PowerHeight == DesiredPowerHeight) ? PowerHeight + (_modtable[PowerBounce] * PowerDir) : PowerHeight; + int drain_height = (DrainHeight == DesiredDrainHeight) ? DrainHeight + (_modtable[DrainBounce] * DrainDir) : DrainHeight; power_height = Bound(power_height, 0, PowerButton.Height - 2); drain_height = Bound(drain_height, 0, PowerButton.Height - 2); - bottom -= (15 * RESFACTOR); - /* ** Draw the power output graphic on top of the power bar framework. */ @@ -233,23 +232,26 @@ void PowerClass::Draw_It(bool complete) ** ST - 5/2/96 11:23AM */ - power_height = (power_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); - drain_height = (drain_height*(76*RESFACTOR+1)) / (53*RESFACTOR+1); + power_height = (power_height * (76 * RESFACTOR + 1)) / (53 * RESFACTOR + 1); + drain_height = (drain_height * (76 * RESFACTOR + 1)) / (53 * RESFACTOR + 1); //bottom = (175*RESFACTOR)+1; - LogicPage->Fill_Rect(ScreenWidth - (640 - (245*RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (245*RESFACTOR+1)), bottom, color2 ); - LogicPage->Fill_Rect(ScreenWidth - (640 - (246*RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (246*RESFACTOR+1)), bottom, color1 ); + LogicPage->Fill_Rect(ScreenWidth - (640 - (245 * RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (245 * RESFACTOR + 1)), bottom, color2); + LogicPage->Fill_Rect(ScreenWidth - (640 - (246 * RESFACTOR)), bottom - power_height, ScreenWidth - (640 - (246 * RESFACTOR + 1)), bottom, color1); } /* ** Draw the power drain threshold marker. */ - CC_Draw_Shape(PowerShape, 0, ScreenWidth - (640 - (((POWER_X * RESFACTOR)) +RESFACTOR)), bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + if (PlayerPtr->Drain > 0) + { + CC_Draw_Shape(PowerShape, 0, ScreenWidth - (640 - (((POWER_X * RESFACTOR)) + RESFACTOR)), bottom - (drain_height + (2 * RESFACTOR)), WINDOW_MAIN, flags | SHAPE_NORMAL, remap); + } } LogicPage->Unlock(); } BEnd(BENCH_POWER); - } + } } @@ -270,11 +272,11 @@ void PowerClass::Draw_It(bool complete) * 12/20/1994 JLB : Created. * * 12/31/1994 JLB : Uses mouse coordinate parameters. * *=============================================================================================*/ -void PowerClass::AI(KeyNumType &input, int x, int y) +void PowerClass::AI(KeyNumType& input, int x, int y) { if (Map.IsSidebarActive /*IsActive*/) { int olddrain = DrainHeight; - int oldpower = PowerHeight; + int oldpower = PowerHeight; /* @@ -283,13 +285,15 @@ void PowerClass::AI(KeyNumType &input, int x, int y) */ if (PlayerPtr->Power != RecordedPower) { DesiredPowerHeight = Power_Height(PlayerPtr->Power); - RecordedPower = PlayerPtr->Power; - PowerBounce = 12; + RecordedPower = PlayerPtr->Power; + PowerBounce = 12; if (PowerHeight > DesiredPowerHeight) { PowerDir = -1; - } else if (PowerHeight < DesiredPowerHeight) { + } + else if (PowerHeight < DesiredPowerHeight) { PowerDir = 1; - } else { + } + else { PowerBounce = 0; } } @@ -300,13 +304,15 @@ void PowerClass::AI(KeyNumType &input, int x, int y) */ if (PlayerPtr->Drain != RecordedDrain) { DesiredDrainHeight = Power_Height(PlayerPtr->Drain); - RecordedDrain = PlayerPtr->Drain; - DrainBounce = 12; + RecordedDrain = PlayerPtr->Drain; + DrainBounce = 12; if (DrainHeight > DesiredDrainHeight) { DrainDir = -1; - } else if (DrainHeight < DesiredDrainHeight) { + } + else if (DrainHeight < DesiredDrainHeight) { DrainDir = 1; - } else { + } + else { DrainBounce = 0; } } @@ -315,7 +321,8 @@ void PowerClass::AI(KeyNumType &input, int x, int y) IsToRedraw = true; Flag_To_Redraw(false); DrainBounce--; - } else { + } + else { /* ** If we need to move the drain height then do so. */ @@ -328,7 +335,8 @@ void PowerClass::AI(KeyNumType &input, int x, int y) IsToRedraw = true; Flag_To_Redraw(false); PowerBounce--; - } else { + } + else { /* ** If we need to move the power height then do so. */ @@ -345,7 +353,7 @@ void PowerClass::AI(KeyNumType &input, int x, int y) /* ** Flag to redraw if the power bar flash effect has expired. */ -// if (FlashTimer == 1) { + // if (FlashTimer == 1) { if (FlashTimer > 0) { IsToRedraw = true; Flag_To_Redraw(false); @@ -374,7 +382,7 @@ void PowerClass::AI(KeyNumType &input, int x, int y) * HISTORY: * * 06/01/1995 JLB : Created. * *=============================================================================================*/ -void PowerClass::Refresh_Cells(CELL cell, short const * list) +void PowerClass::Refresh_Cells(CELL cell, short const* list) { if (*list == REFRESH_SIDEBAR) { IsToRedraw = true; @@ -398,16 +406,16 @@ void PowerClass::Refresh_Cells(CELL cell, short const * list) *=========================================================================*/ int PowerClass::Power_Height(int value) { - int num = value/ POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's - int retval = 0; // currently there is no power + int num = value / POWER_STEP_LEVEL; // figure out the initial num of DRAIN_VALUE's + int retval = 0; // currently there is no power /* ** Loop through the different hundreds figuring out the fractional piece ** of each. */ - for (int lp = 0; lp < num; lp ++) { - retval = retval + (((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR); - value -= POWER_STEP_LEVEL; + for (int lp = 0; lp < num; lp++) { + retval = retval + (((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR); + value -= POWER_STEP_LEVEL; } /* @@ -417,7 +425,7 @@ int PowerClass::Power_Height(int value) retval = retval + (((((POWER_HEIGHT - 2) - retval) / POWER_STEP_FACTOR) * value) / POWER_STEP_LEVEL); } - retval = Bound(retval, 0, POWER_HEIGHT-2); + retval = Bound(retval, 0, POWER_HEIGHT - 2); return(retval); } @@ -439,7 +447,7 @@ int PowerClass::Power_Height(int value) * HISTORY: * * 08/07/1995 JLB : Created. * *=============================================================================================*/ -int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) +int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType& key) { if (!Map.IsSidebarActive) { return(false); @@ -452,7 +460,8 @@ int PowerClass::PowerButtonClass::Action(unsigned flags, KeyNumType & key) Map.Override_Mouse_Shape(MOUSE_NORMAL); if (PlayerPtr->Power_Fraction() < 1 && PlayerPtr->Power > 0) { Map.Help_Text(TXT_POWER_OUTPUT_LOW, -1, -1, GadgetClass::Get_Color_Scheme()->Color); - } else { + } + else { Map.Help_Text(TXT_POWER_OUTPUT, -1, -1, GadgetClass::Get_Color_Scheme()->Color); } GadgetClass::Action(flags, key); diff --git a/code/redalert/sidebar.cpp b/code/redalert/sidebar.cpp index 2a9912e..06d53d2 100644 --- a/code/redalert/sidebar.cpp +++ b/code/redalert/sidebar.cpp @@ -76,19 +76,19 @@ #include "function.h" -//#undef RESFACTOR -//#define RESFACTOR 1 + //#undef RESFACTOR + //#define RESFACTOR 1 -void * SidebarClass::SidebarShape = NULL; -void * SidebarClass::SidebarMiddleShape = NULL; -void * SidebarClass::SidebarBottomShape = NULL; +void* SidebarClass::SidebarShape = NULL; +void* SidebarClass::SidebarMiddleShape = NULL; +void* SidebarClass::SidebarBottomShape = NULL; /*************************************************************************** ** This holds the translucent table for use with the construction clock ** animation. */ -char SidebarClass::StripClass::ClockTranslucentTable[(1+1)*256]; +char SidebarClass::StripClass::ClockTranslucentTable[(1 + 1) * 256]; /*************************************************************************** @@ -121,9 +121,9 @@ SidebarClass::StripClass::SelectButton[COLUMNS][MAX_VISIBLE]; /* ** Shape data pointers */ -void * SidebarClass::StripClass::LogoShapes = NULL; -void const * SidebarClass::StripClass::ClockShapes; -void const * SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; +void* SidebarClass::StripClass::LogoShapes = NULL; +void const* SidebarClass::StripClass::ClockShapes; +void const* SidebarClass::StripClass::SpecialShapes[SPC_COUNT]; /*********************************************************************************************** @@ -169,16 +169,16 @@ SidebarClass::SidebarClass(void) : * HISTORY: * * 08/06/1996 JLB : Created. * *=============================================================================================*/ -SidebarClass::SidebarClass(NoInitClass const & x) : PowerClass(x) +SidebarClass::SidebarClass(NoInitClass const& x) : PowerClass(x) { /* ** Set up the coordinates for the sidebar strips. These coordinates are for ** the upper left corner. */ -// Column[0].X = COLUMN_ONE_X * RESFACTOR; -// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; -// Column[1].X = COLUMN_TWO_X * RESFACTOR; -// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; + // Column[0].X = COLUMN_ONE_X * RESFACTOR; + // Column[0].Y = COLUMN_ONE_Y * RESFACTOR; + // Column[1].X = COLUMN_TWO_X * RESFACTOR; + // Column[1].Y = COLUMN_TWO_Y * RESFACTOR; } @@ -224,25 +224,25 @@ void SidebarClass::One_Time(void) ** code so that as the sidebar buildable buttons scroll, they get properly ** clipped at the top and bottom edges. */ - WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X+8)) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWX] = ((SIDE_X + 8)) * RESFACTOR; WindowList[WINDOW_SIDEBAR][WINDOWY] = (SIDE_Y + 1 + TOP_HEIGHT) * RESFACTOR; - WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH) * RESFACTOR; + WindowList[WINDOW_SIDEBAR][WINDOWWIDTH] = (SIDE_WIDTH)*RESFACTOR; WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = ScreenHeight; -// WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; + // WindowList[WINDOW_SIDEBAR][WINDOWHEIGHT] = (StripClass::MAX_VISIBLE * StripClass::OBJECT_HEIGHT-1) * RESFACTOR; - /* - ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM - */ - WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1*RESFACTOR; + /* + ** Top of the window seems to be wrong for the new sidebar. ST - 5/2/96 2:49AM + */ + WindowList[WINDOW_SIDEBAR][WINDOWY] -= 1 * RESFACTOR; /* ** Set up the coordinates for the sidebar strips. These coordinates are for ** the upper left corner. */ -// Column[0].X = COLUMN_ONE_X * RESFACTOR; -// Column[0].Y = COLUMN_ONE_Y * RESFACTOR; -// Column[1].X = COLUMN_TWO_X * RESFACTOR; -// Column[1].Y = COLUMN_TWO_Y * RESFACTOR; + // Column[0].X = COLUMN_ONE_X * RESFACTOR; + // Column[0].Y = COLUMN_ONE_Y * RESFACTOR; + // Column[1].X = COLUMN_TWO_X * RESFACTOR; + // Column[1].Y = COLUMN_TWO_Y * RESFACTOR; Column[0].One_Time(0); Column[1].One_Time(1); @@ -307,8 +307,8 @@ void SidebarClass::Init_IO(void) Repair.IsSticky = true; Repair.ID = BUTTON_REPAIR; - Repair.X = ScreenWidth - (640 - (0x1f2/2)*RESFACTOR); - Repair.Y = (0x96/2)*RESFACTOR; + Repair.X = ScreenWidth - (640 - (0x1f2 / 2) * RESFACTOR); + Repair.Y = (0x96 / 2) * RESFACTOR; Repair.IsPressed = false; Repair.IsToggleType = true; Repair.ReflectButtonState = true; @@ -317,7 +317,7 @@ void SidebarClass::Init_IO(void) Upgrade.IsSticky = true; Upgrade.ID = BUTTON_UPGRADE; Upgrade.X = ScreenWidth - (640 - 0x21f); - Upgrade.Y = (0x96/2)*RESFACTOR; + Upgrade.Y = (0x96 / 2) * RESFACTOR; Upgrade.IsPressed = false; Upgrade.IsToggleType = true; Upgrade.ReflectButtonState = true; @@ -325,14 +325,15 @@ void SidebarClass::Init_IO(void) Zoom.IsSticky = true; Zoom.ID = BUTTON_ZOOM; - Zoom.X = ScreenWidth - (640 - ((0x24c/2)*RESFACTOR)); - Zoom.Y = (0x96/2)*RESFACTOR; + Zoom.X = ScreenWidth - (640 - ((0x24c / 2) * RESFACTOR)); + Zoom.Y = (0x96 / 2) * RESFACTOR; Zoom.IsPressed = false; Zoom.Set_Shape(MFCD::Retrieve("MAP.SHP")); if ((IsRadarActive && Is_Zoomable()) || Session.Type != GAME_NORMAL) { Zoom.Enable(); - } else { + } + else { Zoom.Disable(); } Column[0].Init_IO(0); @@ -344,8 +345,8 @@ void SidebarClass::Init_IO(void) if (IsSidebarActive) { IsSidebarActive = false; Activate(1); -// Background.Zap(); -// Add_A_Button(Background); + // Background.Zap(); + // Add_A_Button(Background); } } } @@ -388,7 +389,7 @@ void SidebarClass::Init_Theater(TheaterType theater) *=============================================================================================*/ void SidebarClass::Reload_Sidebar(void) { - static char * sidebarnames[]={ + static char* sidebarnames[] = { "SIDE?NA.SHP", //NATO "SIDE?NA.SHP", "SIDE?US.SHP", //USSR @@ -402,18 +403,18 @@ void SidebarClass::Reload_Sidebar(void) }; int houseloaded = 0; - if(PlayerPtr) { + if (PlayerPtr) { houseloaded = PlayerPtr->ActLike; } /* Don't have write access to the static char array. ST - 5/20/2019 */ #if (0) - char * sidename = sidebarnames[houseloaded]; - *(sidename+4) = '1'; + char* sidename = sidebarnames[houseloaded]; + *(sidename + 4) = '1'; SidebarShape = (void*)MFCD::Retrieve(sidename); - *(sidename+4) = '2'; + *(sidename + 4) = '2'; SidebarMiddleShape = (void*)MFCD::Retrieve(sidename); - *(sidename+4) = '3'; + *(sidename + 4) = '3'; SidebarBottomShape = (void*)MFCD::Retrieve(sidename); #else char sb_name[16]; @@ -506,7 +507,7 @@ bool SidebarClass::Factory_Link(int factory, RTTIType type, int id) * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ -void SidebarClass::Refresh_Cells(CELL cell, short const * list) +void SidebarClass::Refresh_Cells(CELL cell, short const* list) { if (*list == REFRESH_SIDEBAR) { IsToRedraw = true; @@ -544,14 +545,14 @@ bool SidebarClass::Activate_Repair(int control) control = IsRepairActive ? 0 : 1; } switch (control) { - case 1: - IsRepairActive = true; - break; + case 1: + IsRepairActive = true; + break; - default: - case 0: - IsRepairActive = false; - break; + default: + case 0: + IsRepairActive = false; + break; } if (old != IsRepairActive) { Flag_To_Redraw(false); @@ -591,14 +592,14 @@ bool SidebarClass::Activate_Upgrade(int control) control = IsUpgradeActive ? 0 : 1; } switch (control) { - case 1: - IsUpgradeActive = true; - break; + case 1: + IsUpgradeActive = true; + break; - default: - case 0: - IsUpgradeActive = false; - break; + default: + case 0: + IsUpgradeActive = false; + break; } if (old != IsUpgradeActive) { Flag_To_Redraw(false); @@ -637,14 +638,14 @@ bool SidebarClass::Activate_Demolish(int control) control = IsDemolishActive ? 0 : 1; } switch (control) { - case 1: - IsDemolishActive = true; - break; + case 1: + IsDemolishActive = true; + break; - default: - case 0: - IsDemolishActive = false; - break; + default: + case 0: + IsDemolishActive = false; + break; } if (old != IsDemolishActive) { Flag_To_Redraw(false); @@ -759,15 +760,12 @@ bool SidebarClass::Scroll(bool up, int column) *=============================================================================================*/ void SidebarClass::Draw_It(bool complete) { - /* Draw black background */ - GL_FillRect(TBLACK, SIDE_X * RESFACTOR, 8 * RESFACTOR, SIDE_WIDTH * RESFACTOR, ScreenHeight - (8 * RESFACTOR)); - PowerClass::Draw_It(complete); BStart(BENCH_SIDEBAR); if (IsSidebarActive && (IsToRedraw || complete) && !Debug_Map) { - // IsToRedraw = false; + // IsToRedraw = false; if (LogicPage->Lock()) { /* @@ -775,9 +773,9 @@ void SidebarClass::Draw_It(bool complete) */ int shape = complete ? 0 : 1; - CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarShape, 0, SIDE_X * RESFACTOR, 8 * RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); - CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8+80)*RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); + CC_Draw_Shape(SidebarMiddleShape, shape, SIDE_X * RESFACTOR, (8 + 80) * RESFACTOR, WINDOW_MAIN, SHAPE_WIN_REL); CC_Draw_Shape(SidebarBottomShape, shape, SIDE_X * RESFACTOR, ScreenHeight - (80 * RESFACTOR) + 3, WINDOW_MAIN, SHAPE_WIN_REL); @@ -829,7 +827,7 @@ void SidebarClass::Draw_It(bool complete) * 12/31/1994 JLB : Uses mouse coordinate parameters. * * 06/27/1995 JLB : key toggles sidebar. * *=============================================================================================*/ -void SidebarClass::AI(KeyNumType & input, int x, int y) +void SidebarClass::AI(KeyNumType& input, int x, int y) { bool redraw = true; @@ -893,19 +891,20 @@ void SidebarClass::AI(KeyNumType & input, int x, int y) */ if (PlayerPtr->BScan) { Activate_Repair(true); - } else { + } + else { Activate_Repair(false); } - if (input == (BUTTON_REPAIR|KN_BUTTON)) { + if (input == (BUTTON_REPAIR | KN_BUTTON)) { Repair_Mode_Control(-1); } - if (input == (BUTTON_ZOOM|KN_BUTTON)) { + if (input == (BUTTON_ZOOM | KN_BUTTON)) { Zoom_Mode_Control(); } - if (input == (BUTTON_UPGRADE|KN_BUTTON)) { + if (input == (BUTTON_UPGRADE | KN_BUTTON)) { Sell_Mode_Control(-1); } @@ -1004,18 +1003,18 @@ bool SidebarClass::Activate(int control) ** Determine the new state of the sidebar. */ switch (control) { - case -1: - IsSidebarActive = IsSidebarActive == false; - break; + case -1: + IsSidebarActive = IsSidebarActive == false; + break; - case 1: - IsSidebarActive = true; - break; + case 1: + IsSidebarActive = true; + break; - default: - case 0: - IsSidebarActive = false; - break; + default: + case 0: + IsSidebarActive = false; + break; } /* @@ -1029,7 +1028,7 @@ bool SidebarClass::Activate(int control) ** activate it on the left side of the screen. */ if (IsSidebarActive /*&& X*/) { - Set_View_Dimensions( 0, 8 * RESFACTOR, SeenBuff.Get_Width() - ( SIDE_WIDTH * RESFACTOR ) ); + Set_View_Dimensions(0, 8 * RESFACTOR, SeenBuff.Get_Width() - (SIDE_WIDTH * RESFACTOR)); IsToRedraw = true; Help_Text(TXT_NONE); @@ -1047,7 +1046,8 @@ bool SidebarClass::Activate(int control) Add_A_Button(Map.RadarButton); Map.PowerButton.Zap(); Add_A_Button(Map.PowerButton); - } else { + } + else { Help_Text(TXT_NONE); Set_View_Dimensions(0, 0); Remove_A_Button(Repair); @@ -1085,7 +1085,7 @@ bool SidebarClass::Activate(int control) * HISTORY: * * 12/31/1994 JLB : Created. * *=============================================================================================*/ -SidebarClass::StripClass::StripClass(InitClass const & ) : +SidebarClass::StripClass::StripClass(InitClass const&) : X(0), Y(0), ID(0), @@ -1123,11 +1123,11 @@ SidebarClass::StripClass::StripClass(InitClass const & ) : * HISTORY: * * 12/31/1994 JLB : Created. * *=============================================================================================*/ -void SidebarClass::StripClass::One_Time(int ) +void SidebarClass::StripClass::One_Time(int) { // Sidebar height HEIGHT = ScreenHeight; - MaxButtonsVisible = ( HEIGHT - ( ( SIDE_Y + TOP_HEIGHT + OBJECT_HEIGHT ) * RESFACTOR ) ) / ( OBJECT_HEIGHT * RESFACTOR ); + MaxButtonsVisible = (HEIGHT - ((SIDE_Y + TOP_HEIGHT + OBJECT_HEIGHT) * RESFACTOR)) / (OBJECT_HEIGHT * RESFACTOR); /* ** Sidebar is player team specific in Hires @@ -1138,7 +1138,7 @@ void SidebarClass::StripClass::One_Time(int ) char buffer[_MAX_FNAME]; sprintf(buffer, "%sICON", SpecialWeaponFile[lp]); - char fullname[_MAX_FNAME+_MAX_EXT]; + char fullname[_MAX_FNAME + _MAX_EXT]; _makepath(fullname, NULL, NULL, buffer, ".SHP"); SpecialShapes[lp] = MFCD::Retrieve(fullname); } @@ -1160,7 +1160,7 @@ void SidebarClass::StripClass::One_Time(int ) * HISTORY: * * 05/19/1995 JLB : commented * *=============================================================================================*/ -void const * SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) +void const* SidebarClass::StripClass::Get_Special_Cameo(SpecialWeaponType type) { if ((unsigned)type < SPC_COUNT) { return(SpecialShapes[type]); @@ -1222,25 +1222,25 @@ void SidebarClass::StripClass::Init_IO(int id) ID = id; UpButton[ID].IsSticky = true; - UpButton[ID].ID = BUTTON_UP+id; + UpButton[ID].ID = BUTTON_UP + id; UpButton[ID].X = X + (UP_X_OFFSET * RESFACTOR); - UpButton[ID].Y = HEIGHT - ( ( OBJECT_HEIGHT + 7 ) * RESFACTOR ); + UpButton[ID].Y = HEIGHT - ((OBJECT_HEIGHT + 7) * RESFACTOR); UpButton[ID].Set_Shape(MFCD::Retrieve("STRIPUP.SHP")); DownButton[ID].IsSticky = true; - DownButton[ID].ID = BUTTON_DOWN+id; + DownButton[ID].ID = BUTTON_DOWN + id; DownButton[ID].X = X + (DOWN_X_OFFSET * RESFACTOR); - DownButton[ID].Y = HEIGHT - ( (OBJECT_HEIGHT + 7 ) * RESFACTOR ); + DownButton[ID].Y = HEIGHT - ((OBJECT_HEIGHT + 7) * RESFACTOR); DownButton[ID].Set_Shape(MFCD::Retrieve("STRIPDN.SHP")); UpButton[ID].Y += 3; DownButton[ID].Y += 3; for (int index = 0; index < MaxButtonsVisible; index++) { - SelectClass & g = SelectButton[ID][index]; + SelectClass& g = SelectButton[ID][index]; g.ID = BUTTON_SELECT; g.X = X; - g.Y = Y + ((OBJECT_HEIGHT*index) * RESFACTOR); + g.Y = Y + ((OBJECT_HEIGHT * index) * RESFACTOR); g.Width = OBJECT_WIDTH * RESFACTOR; g.Height = OBJECT_HEIGHT * RESFACTOR; g.Set_Owner(*this, index); @@ -1265,28 +1265,28 @@ void SidebarClass::StripClass::Init_Theater(TheaterType theater) Reload_LogoShapes(); - if ( (theater != THEATER_NONE) && (theater != ::LastTheater)) { + if ((theater != THEATER_NONE) && (theater != ::LastTheater)) { static TLucentType const ClockCols[1] = { {GREEN, BLACK, 100, 0} -// {GREEN, LTGREY, 180, 0} + // {GREEN, LTGREY, 180, 0} }; /* ** Make sure that remapping doesn't occur on the colors that cycle. */ PaletteClass pal = OriginalPalette; - memset(&pal[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + memset(&pal[CYCLE_COLOR_START * 3], 0x3f, CYCLE_COLOR_COUNT * 3); Build_Translucent_Table(pal, &ClockCols[0], 1, (void*)ClockTranslucentTable); -// Mem_Copy(GamePalette, OriginalPalette, 768); -// memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); + // Mem_Copy(GamePalette, OriginalPalette, 768); + // memset(&GamePalette[CYCLE_COLOR_START*3], 0x3f, CYCLE_COLOR_COUNT*3); - /* - ** Create the translucent table used for the sidebar. - */ -// Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); -// GamePalette = OriginalPalette; + /* + ** Create the translucent table used for the sidebar. + */ + // Build_Translucent_Table(GamePalette, &ClockCols[0], 1, (void*)ClockTranslucentTable); + // GamePalette = OriginalPalette; Conquer_Build_Fading_Table(GamePalette, &ClockTranslucentTable[256], BLACK, 100); } @@ -1297,7 +1297,7 @@ void SidebarClass::StripClass::Reload_LogoShapes(void) /* ** Load hi-res strip art here since it is player side specific */ - static char * stripnames[]={ + static char* stripnames[] = { "stripna.shp", //Nato "stripna.shp", "stripus.shp", //USSR @@ -1315,13 +1315,13 @@ void SidebarClass::StripClass::Reload_LogoShapes(void) ** Sidebar art is dependent on the side of the player */ - if(PlayerPtr) { + if (PlayerPtr) { houseloaded = PlayerPtr->ActLike; } -// jmarshall + // jmarshall if (houseloaded >= HOUSE_BAD) return; -// jmarshall end + // jmarshall end LogoShapes = (void*)MFCD::Retrieve(stripnames[houseloaded]); } @@ -1444,8 +1444,9 @@ bool SidebarClass::StripClass::Scroll(bool up) if (up) { if (!TopIndex) return(false); Scroller--; - } else { - if ( TopIndex+ MaxButtonsVisible >= BuildableCount ) return(false); + } + else { + if (TopIndex + MaxButtonsVisible >= BuildableCount) return(false); Scroller++; } return(true); @@ -1496,7 +1497,7 @@ void SidebarClass::StripClass::Flag_To_Redraw(void) * 12/31/1994 JLB : Created. * * 12/31/1994 JLB : Uses mouse coordinate parameters. * *=============================================================================================*/ -bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) +bool SidebarClass::StripClass::AI(KeyNumType& input, int, int) { bool redraw = false; @@ -1504,13 +1505,13 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) ** If this is scroll button for this side strip, then scroll the strip as ** indicated. */ - if (input == (UpButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + if (input == (UpButton[ID].ID | KN_BUTTON)) { // && !IsScrolling UpButton[ID].IsPressed = false; if (!Scroll(true)) { Sound_Effect(VOC_SCOLD); } } - if (input == (DownButton[ID].ID|KN_BUTTON)) { // && !IsScrolling + if (input == (DownButton[ID].ID | KN_BUTTON)) { // && !IsScrolling DownButton[ID].IsPressed = false; if (!Scroll(false)) { Sound_Effect(VOC_SCOLD); @@ -1522,9 +1523,10 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) ** logic handler. This might result in up or down scrolling. */ if (!IsScrolling && Scroller) { - if (BuildableCount <= MaxButtonsVisible ) { + if (BuildableCount <= MaxButtonsVisible) { Scroller = 0; - } else { + } + else { /* ** Top of list is moving toward lower ordered entries in the object list. It looks like @@ -1534,7 +1536,8 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) if (Scroller < 0) { if (!TopIndex) { Scroller = 0; - } else { + } + else { Scroller++; IsScrollingDown = false; IsScrolling = true; @@ -1542,10 +1545,12 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) Slid = 0; } - } else { + } + else { if (TopIndex + MaxButtonsVisible >= BuildableCount) { Scroller = 0; - } else { + } + else { Scroller--; Slid = OBJECT_HEIGHT; IsScrollingDown = true; @@ -1566,7 +1571,8 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) Slid = 0; TopIndex++; } - } else { + } + else { Slid += SCROLL_RATE; if (Slid >= OBJECT_HEIGHT) { IsScrolling = false; @@ -1599,7 +1605,7 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) int factoryid = Buildables[index].Factory; if (factoryid != -1) { - FactoryClass * factory = Factories.Raw_Ptr(factoryid); + FactoryClass* factory = Factories.Raw_Ptr(factoryid); if (factory && (factory->Has_Changed() || factory->Is_Blocked())) { redraw = true; @@ -1611,28 +1617,28 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) ** the main exception to the ability to leave the factory under their own ** power. */ - TechnoClass * pending = factory->Get_Object(); + TechnoClass* pending = factory->Get_Object(); if (pending != NULL) { switch (pending->What_Am_I()) { - case RTTI_VESSEL: - case RTTI_UNIT: - case RTTI_AIRCRAFT: - OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); - if (!factory->Is_Blocked()) { - Speak(VOX_UNIT_READY); - } - break; + case RTTI_VESSEL: + case RTTI_UNIT: + case RTTI_AIRCRAFT: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; - case RTTI_BUILDING: - Speak(VOX_CONSTRUCTION); - break; + case RTTI_BUILDING: + Speak(VOX_CONSTRUCTION); + break; - case RTTI_INFANTRY: - OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); - if (!factory->Is_Blocked()) { - Speak(VOX_UNIT_READY); - } - break; + case RTTI_INFANTRY: + OutList.Add(EventClass(EventClass::PLACE, pending->What_Am_I(), -1)); + if (!factory->Is_Blocked()) { + Speak(VOX_UNIT_READY); + } + break; } } } @@ -1673,7 +1679,7 @@ bool SidebarClass::StripClass::AI(KeyNumType & input, int , int ) void SidebarClass::StripClass::Draw_It(bool complete) { if (IsToRedraw || complete) { - // IsToRedraw = false; + // IsToRedraw = false; if (RunningAsDLL) { return; @@ -1688,8 +1694,8 @@ void SidebarClass::StripClass::Draw_It(bool complete) /* ** New sidebar needs to be drawn not filled */ - if ( BuildableCount < MaxButtonsVisible ) { - CC_Draw_Shape( LogoShapes, ID, X+(2*RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL|SHAPE_NORMAL, 0); + if (BuildableCount < MaxButtonsVisible) { + CC_Draw_Shape(LogoShapes, ID, X + (2 * RESFACTOR), Y, WINDOW_MAIN, SHAPE_WIN_REL | SHAPE_NORMAL, 0); } /* @@ -1707,20 +1713,20 @@ void SidebarClass::StripClass::Draw_It(bool complete) bool completed; int stage; bool darken = false; - void const * shapefile = 0; + void const* shapefile = 0; int shapenum = 0; - void const * remapper = 0; - FactoryClass * factory = 0; - int index = i+TopIndex; + void const* remapper = 0; + FactoryClass* factory = 0; + int index = i + TopIndex; int x = X; - int y = Y + (i*OBJECT_HEIGHT * RESFACTOR); + int y = Y + (i * OBJECT_HEIGHT * RESFACTOR); /* ** If the strip is scrolling, then the offset is adjusted accordingly. */ if (IsScrolling) { - y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; -// y -= OBJECT_HEIGHT - Slid; + y -= (OBJECT_HEIGHT - Slid) * RESFACTOR; + // y -= OBJECT_HEIGHT - Slid; } /* @@ -1728,7 +1734,7 @@ void SidebarClass::StripClass::Draw_It(bool complete) ** slot. This shape pointer is used to draw the underlying graphic there. */ if (index < BuildableCount) { - ObjectTypeClass const * obj = NULL; + ObjectTypeClass const* obj = NULL; SpecialWeaponType spc = SPC_NONE; if (Buildables[index].BuildableType != RTTI_SPECIAL) { @@ -1740,7 +1746,7 @@ void SidebarClass::StripClass::Draw_It(bool complete) ** Fetch the remap table that is appropriate for this object ** type. */ - remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const *)obj)->Remap); + remapper = PlayerPtr->Remap_Table(false, ((TechnoTypeClass const*)obj)->Remap); /* ** If there is already a factory producing this kind of object, then all @@ -1759,28 +1765,31 @@ void SidebarClass::StripClass::Draw_It(bool complete) } shapefile = obj->Get_Cameo_Data(); - shapenum = 0; + shapenum = 0; if (Buildables[index].Factory != -1) { - factory = Factories.Raw_Ptr(Buildables[index].Factory); - production = true; - completed = factory->Has_Completed(); - stage = factory->Completion(); - darken = false; - } else { - production = false; -// darken = IsBuilding; + factory = Factories.Raw_Ptr(Buildables[index].Factory); + production = true; + completed = factory->Has_Completed(); + stage = factory->Completion(); + darken = false; + } + else { + production = false; + // darken = IsBuilding; - /* - ** Darken the imagery if a factory of a matching type is - ** already busy. - */ + /* + ** Darken the imagery if a factory of a matching type is + ** already busy. + */ darken = isbusy; } - } else { + } + else { darken = PlayerPtr->Is_Hack_Prevented(Buildables[index].BuildableType, Buildables[index].BuildableID); } - } else { + } + else { spc = SpecialWeaponType(Buildables[index].BuildableID); shapefile = Get_Special_Cameo(spc); @@ -1801,16 +1810,18 @@ void SidebarClass::StripClass::Draw_It(bool complete) remapper = Map.FadingLight; } - } else { - shapefile = LogoShapes; + } + else { + shapefile = LogoShapes; if (!darken) { - shapenum = SB_BLANK; + shapenum = SB_BLANK; } } - } else { - shapefile = LogoShapes; - shapenum = SB_BLANK; - production = false; + } + else { + shapefile = LogoShapes; + shapenum = SB_BLANK; + production = false; } remapper = 0; @@ -1823,10 +1834,10 @@ void SidebarClass::StripClass::Draw_It(bool complete) */ if (shapenum != SB_BLANK || shapefile != LogoShapes) { CC_Draw_Shape(shapefile, shapenum, - x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), - y-WindowList[WINDOW_SIDEBAR][WINDOWY], + x - (WindowList[WINDOW_SIDEBAR][WINDOWX]) + (LEFT_EDGE_OFFSET * RESFACTOR), + y - WindowList[WINDOW_SIDEBAR][WINDOWY], WINDOW_SIDEBAR, - SHAPE_NORMAL|SHAPE_WIN_REL| (remapper ? SHAPE_FADING : SHAPE_NORMAL), + SHAPE_NORMAL | SHAPE_WIN_REL | (remapper ? SHAPE_FADING : SHAPE_NORMAL), remapper); /* @@ -1835,11 +1846,11 @@ void SidebarClass::StripClass::Draw_It(bool complete) */ if (darken) { CC_Draw_Shape(ClockShapes, 0, - x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), - y-WindowList[WINDOW_SIDEBAR][WINDOWY], + x - (WindowList[WINDOW_SIDEBAR][WINDOWX]) + (LEFT_EDGE_OFFSET * RESFACTOR), + y - WindowList[WINDOW_SIDEBAR][WINDOWY], WINDOW_SIDEBAR, - SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, - NULL, ClockTranslucentTable); + SHAPE_NORMAL | SHAPE_WIN_REL | SHAPE_GHOST, + NULL, ClockTranslucentTable); } } @@ -1854,26 +1865,27 @@ void SidebarClass::StripClass::Draw_It(bool complete) ** Display text showing that the object is ready to place. */ CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_READY, - (x-(WindowList[WINDOW_SIDEBAR][WINDOWX]))+(LEFT_EDGE_OFFSET+15) * RESFACTOR, - (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), - WINDOW_SIDEBAR, SHAPE_CENTER); - } else { + (x - (WindowList[WINDOW_SIDEBAR][WINDOWX])) + (LEFT_EDGE_OFFSET + 15) * RESFACTOR, + (y - WindowList[WINDOW_SIDEBAR][WINDOWY]) + (4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); + } + else { - CC_Draw_Shape(ClockShapes, stage+1, - x-(WindowList[WINDOW_SIDEBAR][WINDOWX])+(LEFT_EDGE_OFFSET * RESFACTOR), - y-WindowList[WINDOW_SIDEBAR][WINDOWY], - WINDOW_SIDEBAR, - SHAPE_NORMAL|SHAPE_WIN_REL|SHAPE_GHOST, - NULL, ClockTranslucentTable); + CC_Draw_Shape(ClockShapes, stage + 1, + x - (WindowList[WINDOW_SIDEBAR][WINDOWX]) + (LEFT_EDGE_OFFSET * RESFACTOR), + y - WindowList[WINDOW_SIDEBAR][WINDOWY], + WINDOW_SIDEBAR, + SHAPE_NORMAL | SHAPE_WIN_REL | SHAPE_GHOST, + NULL, ClockTranslucentTable); /* ** Display text showing that the construction is temporarily on hold. */ if (factory && !factory->Is_Building()) { CC_Draw_Shape(ObjectTypeClass::PipShapes, PIP_HOLDING, - (x-(WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET+15) * RESFACTOR), - (y-WindowList[WINDOW_SIDEBAR][WINDOWY])+(4 * RESFACTOR), - WINDOW_SIDEBAR, SHAPE_CENTER); + (x - (WindowList[WINDOW_SIDEBAR][WINDOWX])) + ((LEFT_EDGE_OFFSET + 15) * RESFACTOR), + (y - WindowList[WINDOW_SIDEBAR][WINDOWY]) + (4 * RESFACTOR), + WINDOW_SIDEBAR, SHAPE_CENTER); } } } @@ -1919,13 +1931,15 @@ bool SidebarClass::StripClass::Recalc(void) */ bool redraw = false; for (int index = 0; index < BuildableCount; index++) { - TechnoTypeClass const * tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); + TechnoTypeClass const* tech = Fetch_Techno_Type(Buildables[index].BuildableType, Buildables[index].BuildableID); if (tech != NULL) { ok = tech->Who_Can_Build_Me(true, false, PlayerPtr->Class->House) != NULL; - } else { + } + else { if ((unsigned)Buildables[index].BuildableID < SPC_COUNT) { ok = PlayerPtr->SuperWeapon[Buildables[index].BuildableID].Is_Present(); - } else { + } + else { ok = false; } } @@ -1935,8 +1949,8 @@ bool SidebarClass::StripClass::Recalc(void) /* ** Removes this entry from the list. */ - if (BuildableCount > 1 && index < BuildableCount-1) { - memcpy(&Buildables[index], &Buildables[index+1], sizeof(Buildables[0])*((BuildableCount-index)-1)); + if (BuildableCount > 1 && index < BuildableCount - 1) { + memcpy(&Buildables[index], &Buildables[index + 1], sizeof(Buildables[0]) * ((BuildableCount - index) - 1)); } TopIndex = 0; IsToRedraw = true; @@ -1975,7 +1989,7 @@ bool SidebarClass::StripClass::Recalc(void) * 01/19/1995 JLB : Created. * *=============================================================================================*/ SidebarClass::StripClass::SelectClass::SelectClass(void) : - ControlClass(0, 0, 0, (OBJECT_WIDTH-1) * RESFACTOR, OBJECT_HEIGHT * RESFACTOR, LEFTPRESS|RIGHTPRESS|LEFTUP), + ControlClass(0, 0, 0, (OBJECT_WIDTH - 1)* RESFACTOR, OBJECT_HEIGHT* RESFACTOR, LEFTPRESS | RIGHTPRESS | LEFTUP), Strip(0), Index(0) { @@ -2001,7 +2015,7 @@ SidebarClass::StripClass::SelectClass::SelectClass(void) : * HISTORY: * * 01/19/1995 JLB : Created. * *=============================================================================================*/ -void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int index) +void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass& strip, int index) { Strip = &strip; Index = index; @@ -2028,7 +2042,7 @@ void SidebarClass::StripClass::SelectClass::Set_Owner(StripClass & strip, int in * 01/19/1995 JLB : Created. * * 10/09/1996 JLB : Sonar pulse converted to regular event type. * *=============================================================================================*/ -int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & key) +int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType& key) { if (Strip == NULL) return 0; @@ -2036,9 +2050,9 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k RTTIType otype = Strip->Buildables[index].BuildableType; int oid = Strip->Buildables[index].BuildableID; int fnumber = Strip->Buildables[index].Factory; - RemapControlType * scheme = GadgetClass::Get_Color_Scheme(); + RemapControlType* scheme = GadgetClass::Get_Color_Scheme(); - ObjectTypeClass const * choice = NULL; + ObjectTypeClass const* choice = NULL; SpecialWeaponType spc = SPC_NONE; /* @@ -2048,14 +2062,15 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k ** kind of factory is specified by the "genfactory" value. This can be used to see ** if the factory type is currently busy or not. */ - FactoryClass * factory = PlayerPtr->Fetch_Factory(otype); + FactoryClass* factory = PlayerPtr->Fetch_Factory(otype); Map.Override_Mouse_Shape(MOUSE_NORMAL); if (index < Strip->BuildableCount) { if (otype != RTTI_SPECIAL) { - choice = Fetch_Techno_Type(otype, oid); - } else { + choice = Fetch_Techno_Type(otype, oid); + } + else { spc = SpecialWeaponType(oid); } @@ -2063,9 +2078,10 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k factory = Factories.Raw_Ptr(fnumber); } - } else { - Map.Help_Text(TXT_NONE); - } + } + else { + Map.Help_Text(TXT_NONE); + } if (spc != SPC_NONE) { @@ -2096,16 +2112,19 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k Map.IsTargettingMode = spc; Unselect_All(); Speak(VOX_SELECT_TARGET); - } else { + } + else { OutList.Add(EventClass(EventClass::SPECIAL_PLACE, SPC_SONAR_PULSE, 0)); } - } else { + } + else { PlayerPtr->SuperWeapon[spc].Impatient_Click(); } } } - } else { + } + else { if (choice != NULL) { @@ -2145,7 +2164,8 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k if (!factory->Is_Building()) { Speak(VOX_CANCELED); OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); - } else { + } + else { Speak(VOX_SUSPENDED); OutList.Add(EventClass(EventClass::SUSPEND, otype, oid)); Map.Column[0].IsToRedraw = true; @@ -2175,7 +2195,8 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k */ if (factory->Is_Building()) { Speak(VOX_NO_FACTORY); - } else { + } + else { /* ** If production has completed, then attempt to have the object exit @@ -2183,15 +2204,17 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k */ if (factory->Has_Completed()) { - TechnoClass * pending = factory->Get_Object(); + TechnoClass* pending = factory->Get_Object(); if (!pending && factory->Get_Special_Item()) { Map.IsTargettingMode = SPC_ANY; - } else { - BuildingClass * builder = pending->Who_Can_Build_Me(false, false); + } + else { + BuildingClass* builder = pending->Who_Can_Build_Me(false, false); if (!builder) { OutList.Add(EventClass(EventClass::ABANDON, otype, oid)); Speak(VOX_NO_FACTORY); - } else { + } + else { /* ** If the completed object is a building, then change the @@ -2200,8 +2223,9 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k ** the building is actually placed down. */ if (pending->What_Am_I() == RTTI_BUILDING) { - PlayerPtr->Manual_Place(builder, (BuildingClass *)pending); - } else { + PlayerPtr->Manual_Place(builder, (BuildingClass*)pending); + } + else { /* ** For objects that can leave the factory under their own @@ -2212,18 +2236,21 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k } } } - } else { + } + else { if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { // Eva scolds the player here. - } else { + } + else { /* ** The factory must have been in a suspended state. Resume construction ** normally. */ if (otype == RTTI_INFANTRYTYPE) { Speak(VOX_TRAINING); - } else { + } + else { Speak(VOX_BUILDING); } OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); @@ -2231,11 +2258,13 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k } } - } else { + } + else { if (PlayerPtr->Is_Hack_Prevented(otype, oid)) { // Eva scolds the player here. - } else { + } + else { /* ** If this side strip is already busy with production, then ignore the @@ -2243,14 +2272,16 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k */ if (otype == RTTI_INFANTRYTYPE) { Speak(VOX_TRAINING); - } else { + } + else { Speak(VOX_BUILDING); } OutList.Add(EventClass(EventClass::PRODUCE, Strip->Buildables[index].BuildableType, Strip->Buildables[index].BuildableID)); } } } - } else { + } + else { flags = 0; } } @@ -2277,7 +2308,7 @@ int SidebarClass::StripClass::SelectClass::Action(unsigned flags, KeyNumType & k * HISTORY: * * 03/28/1995 JLB : Created. * *=============================================================================================*/ -int SidebarClass::SBGadgetClass::Action(unsigned , KeyNumType & ) +int SidebarClass::SBGadgetClass::Action(unsigned, KeyNumType&) { Map.Help_Text(TXT_NONE); Map.Override_Mouse_Shape(MOUSE_NORMAL, false); @@ -2374,7 +2405,8 @@ bool SidebarClass::StripClass::Abandon_Production(int factory) Factories.Raw_Ptr(factory)->Abandon(); Buildables[index].Factory = -1; abandon = true; - } else { + } + else { if (Buildables[index].Factory != -1) { noprod = false; } @@ -2424,23 +2456,26 @@ void SidebarClass::Zoom_Mode_Control(void) ** radar spying readout => zoomed */ if (IsRadarActive) { - if (Is_Zoomed() || Session.Type==GAME_NORMAL) { + if (Is_Zoomed() || Session.Type == GAME_NORMAL) { if (Is_Zoomed() || !Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } - } else { + } + else { if (!Spying_On_House() && !Is_Player_Names()) { Player_Names(1); - } else { + } + else { Player_Names(0); if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } } - } else { - if (Session.Type!=GAME_NORMAL) { - Player_Names(Is_Player_Names()==0); + } + else { + if (Session.Type != GAME_NORMAL) { + Player_Names(Is_Player_Names() == 0); } } #else @@ -2451,23 +2486,26 @@ void SidebarClass::Zoom_Mode_Control(void) ** radar spying readout => not zoomed */ if (IsRadarActive) { - if (Session.Type==GAME_NORMAL) { + if (Session.Type == GAME_NORMAL) { if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } - } else { + } + else { if (!Spying_On_House() && !Is_Player_Names()) { Player_Names(1); - } else { + } + else { Player_Names(0); if (!Spy_Next_House()) { Zoom_Mode(Coord_Cell(TacticalCoord)); } } } - } else { - if (Session.Type!=GAME_NORMAL) { - Player_Names(Is_Player_Names()==0); + } + else { + if (Session.Type != GAME_NORMAL) { + Player_Names(Is_Player_Names() == 0); } } #endif From 510bd2ea5739c5b58c7e5895724ce7758bf2b37c Mon Sep 17 00:00:00 2001 From: Ben Lankamp Date: Wed, 1 Jul 2020 23:29:21 +0200 Subject: [PATCH 18/18] Fix for text input --- code/redalert/edit.cpp | 28 +++++++++++++++------------- code/redalert/io/userinput.cpp | 5 +---- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/code/redalert/edit.cpp b/code/redalert/edit.cpp index 1684c36..d1f3a31 100644 --- a/code/redalert/edit.cpp +++ b/code/redalert/edit.cpp @@ -345,10 +345,9 @@ void EditClass::Draw_Text(char const * text) *=============================================================================================*/ bool EditClass::Handle_Key(KeyNumType key, unsigned flags) { - unsigned char testAscii = UserInput.KeyB.ASCII; - //if (key & KN_SHIFT_BIT || key & KN_CAPSLOCK_BIT) testAscii = toupper(testAscii); + unsigned rawKey = key & 0xff; - switch (key & 0xff) { + switch (rawKey) { /* ** Handle the special case of a non-keyboard event. It is possible that this ** key code might be passed to this routine if this routine has been overridden @@ -357,6 +356,7 @@ bool EditClass::Handle_Key(KeyNumType key, unsigned flags) case 0: break; + /* ** If the return key is pressed, then remove the focus from this edit ** gadget but otherwise let the normal gadget processing proceed. This @@ -384,11 +384,13 @@ bool EditClass::Handle_Key(KeyNumType key, unsigned flags) ** if it can legally be added to the edit string and do so if possible. */ default: + unsigned char keyAscii = UserInput.KeyB.ASCII; + if (key & KN_SHIFT_BIT || key & KN_CAPSLOCK_BIT) keyAscii = toupper(keyAscii); /* ** Don't add a character if the length is greater than edit width. */ - if (((int)String_Pixel_Width(String) + (int)Char_Pixel_Width(testAscii) ) >= (Width-2)) { + if (((int)String_Pixel_Width(String) + (int)Char_Pixel_Width(keyAscii) ) >= (Width-2)) { break; } @@ -401,21 +403,21 @@ bool EditClass::Handle_Key(KeyNumType key, unsigned flags) ** Invisible characters are never added to the string. This is ** especially true for spaces at the beginning of the string. */ - if (!isgraph(testAscii) && testAscii != ' ') break; - if (testAscii == ' ' && Length == 0) break; + if (!isgraph(keyAscii) && keyAscii != ' ') break; + if (keyAscii == ' ' && Length == 0) break; /* ** If this is an upper case only edit gadget, then force the alphabetic ** character to upper case. */ - if ((EditFlags & UPPERCASE) && isalpha(testAscii)) { - testAscii = (KeyASCIIType)toupper(testAscii); + if ((EditFlags & UPPERCASE) && isalpha(keyAscii)) { + rawKey = (KeyASCIIType)toupper(keyAscii); } - if ((!(EditFlags & NUMERIC) || !isdigit(testAscii)) && - (!(EditFlags & ALPHA) || !isalpha(testAscii)) && - (!(EditFlags & MISC) || isalnum(testAscii)) && - testAscii != ' ') { + if ((!(EditFlags & NUMERIC) || !isdigit(keyAscii)) && + (!(EditFlags & ALPHA) || !isalpha(keyAscii)) && + (!(EditFlags & MISC) || isalnum(keyAscii)) && + rawKey != ' ') { break; } @@ -425,7 +427,7 @@ bool EditClass::Handle_Key(KeyNumType key, unsigned flags) ** because the event flag has been cleared. This prevents the gadget's ID ** number from being returned just because the gadget has been edited. */ - String[Length++] = testAscii; + String[Length++] = keyAscii; String[Length] = '\0'; Flag_To_Redraw(); diff --git a/code/redalert/io/userinput.cpp b/code/redalert/io/userinput.cpp index 6c05848..1d3ce53 100644 --- a/code/redalert/io/userinput.cpp +++ b/code/redalert/io/userinput.cpp @@ -204,10 +204,7 @@ void UserInputClass::Process_Input(KeyNumType& key, int& flags) Keyboard->Put_Element(element->second | keyFlags); KeyB.LastKey = element->first; - if (event.text.text[0] == (event.text.text[0] & 0xFF)) - { - KeyB.ASCII = element->second; - } + KeyB.ASCII = 0x0; } break;